Merge pull request #1589 from ivogabe/taggedTemplates

Tagged templates ES3 & 5
This commit is contained in:
Daniel Rosenwasser
2015-02-26 10:29:40 -08:00
64 changed files with 695 additions and 531 deletions

View File

@@ -377,6 +377,7 @@ module ts {
function bind(node: Node) {
node.parent = parent;
switch (node.kind) {
case SyntaxKind.TypeParameter:
bindDeclaration(<Declaration>node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes, /*isBlockScopeContainer*/ false);

View File

@@ -6759,11 +6759,6 @@ module ts {
}
function checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type {
// Grammar checking
if (languageVersion < ScriptTarget.ES6) {
grammarErrorOnFirstToken(node.template, Diagnostics.Tagged_templates_are_only_available_when_targeting_ECMAScript_6_and_higher);
}
return getReturnTypeOfSignature(getResolvedSignature(node));
}

View File

@@ -117,7 +117,6 @@ module ts {
const_declarations_must_be_initialized: { code: 1155, category: DiagnosticCategory.Error, key: "'const' declarations must be initialized" },
const_declarations_can_only_be_declared_inside_a_block: { code: 1156, category: DiagnosticCategory.Error, key: "'const' declarations can only be declared inside a block." },
let_declarations_can_only_be_declared_inside_a_block: { code: 1157, category: DiagnosticCategory.Error, key: "'let' declarations can only be declared inside a block." },
Tagged_templates_are_only_available_when_targeting_ECMAScript_6_and_higher: { code: 1159, category: DiagnosticCategory.Error, key: "Tagged templates are only available when targeting ECMAScript 6 and higher." },
Unterminated_template_literal: { code: 1160, category: DiagnosticCategory.Error, key: "Unterminated template literal." },
Unterminated_regular_expression_literal: { code: 1161, category: DiagnosticCategory.Error, key: "Unterminated regular expression literal." },
An_object_member_cannot_be_declared_optional: { code: 1162, category: DiagnosticCategory.Error, key: "An object member cannot be declared optional." },

View File

@@ -459,10 +459,6 @@
"category": "Error",
"code": 1157
},
"Tagged templates are only available when targeting ECMAScript 6 and higher.": {
"category": "Error",
"code": 1159
},
"Unterminated template literal.": {
"category": "Error",
"code": 1160

View File

@@ -2072,7 +2072,7 @@ module ts {
}
}
function emitParenthesized(node: Node, parenthesized: boolean) {
function emitParenthesizedIf(node: Node, parenthesized: boolean) {
if (parenthesized) {
write("(");
}
@@ -2205,6 +2205,72 @@ module ts {
function getTemplateLiteralAsStringLiteral(node: LiteralExpression): string {
return '"' + escapeString(node.text) + '"';
}
function emitDownlevelRawTemplateLiteral(node: LiteralExpression) {
// Find original source text, since we need to emit the raw strings of the tagged template.
// The raw strings contain the (escaped) strings of what the user wrote.
// Examples: `\n` is converted to "\\n", a template string with a newline to "\n".
var text = getSourceTextOfNodeFromSourceFile(currentSourceFile, node);
// text contains the original source, it will also contain quotes ("`"), dolar signs and braces ("${" and "}"),
// thus we need to remove those characters.
// First template piece starts with "`", others with "}"
// Last template piece ends with "`", others with "${"
var isLast = node.kind === SyntaxKind.NoSubstitutionTemplateLiteral || node.kind === SyntaxKind.TemplateTail;
text = text.substring(1, text.length - (isLast ? 1 : 2));
// Newline normalization:
// ES6 Spec 11.8.6.1 - Static Semantics of TV's and TRV's
// <CR><LF> and <CR> LineTerminatorSequences are normalized to <LF> for both TV and TRV.
text = text.replace(/\r\n?/g, "\n");
text = escapeString(text);
write('"' + text + '"');
}
function emitDownlevelTaggedTemplateArray(node: TaggedTemplateExpression, literalEmitter: (literal: LiteralExpression) => void) {
write("[");
if (node.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral) {
literalEmitter(<LiteralExpression>node.template);
}
else {
literalEmitter((<TemplateExpression>node.template).head);
forEach((<TemplateExpression>node.template).templateSpans, (child) => {
write(", ");
literalEmitter(child.literal);
});
}
write("]");
}
function emitDownlevelTaggedTemplate(node: TaggedTemplateExpression) {
var tempVariable = createAndRecordTempVariable(node);
write("(");
emit(tempVariable);
write(" = ");
emitDownlevelTaggedTemplateArray(node, emit);
write(", ");
emit(tempVariable);
write(".raw = ");
emitDownlevelTaggedTemplateArray(node, emitDownlevelRawTemplateLiteral);
write(", ");
emitParenthesizedIf(node.tag, needsParenthesisForPropertyAccessOrInvocation(node.tag));
write("(");
emit(tempVariable);
// Now we emit the expressions
if (node.template.kind === SyntaxKind.TemplateExpression) {
forEach((<TemplateExpression>node.template).templateSpans, templateSpan => {
write(", ");
var needsParens = templateSpan.expression.kind === SyntaxKind.BinaryExpression
&& (<BinaryExpression>templateSpan.expression).operatorToken.kind === SyntaxKind.CommaToken;
emitParenthesizedIf(templateSpan.expression, needsParens);
});
}
write("))");
}
function emitTemplateExpression(node: TemplateExpression): void {
// In ES6 mode and above, we can simply emit each portion of a template in order, but in
@@ -2249,7 +2315,8 @@ module ts {
write(" + ");
}
emitParenthesized(templateSpan.expression, needsParens);
emitParenthesizedIf(templateSpan.expression, needsParens);
// Only emit if the literal is non-empty.
// The binary '+' operator is left-associative, so the first string concatenation
// with the head will force the result up to this point to be a string.
@@ -2479,7 +2546,7 @@ module ts {
emit((<SpreadElementExpression>node).expression);
}
function needsParenthesisForPropertyAccess(node: Expression) {
function needsParenthesisForPropertyAccessOrInvocation(node: Expression) {
switch (node.kind) {
case SyntaxKind.Identifier:
case SyntaxKind.ArrayLiteralExpression:
@@ -2509,7 +2576,7 @@ module ts {
var e = elements[pos];
if (e.kind === SyntaxKind.SpreadElementExpression) {
e = (<SpreadElementExpression>e).expression;
emitParenthesized(e, /*parenthesized*/ group === 0 && needsParenthesisForPropertyAccess(e));
emitParenthesizedIf(e, /*parenthesized*/ group === 0 && needsParenthesisForPropertyAccessOrInvocation(e));
pos++;
}
else {
@@ -2985,9 +3052,14 @@ module ts {
}
function emitTaggedTemplateExpression(node: TaggedTemplateExpression): void {
emit(node.tag);
write(" ");
emit(node.template);
if (compilerOptions.target >= ScriptTarget.ES6) {
emit(node.tag);
write(" ");
emit(node.template);
}
else {
emitDownlevelTaggedTemplate(node);
}
}
function emitParenExpression(node: ParenthesizedExpression) {
@@ -3157,7 +3229,7 @@ module ts {
}
function emitExpressionStatement(node: ExpressionStatement) {
emitParenthesized(node.expression, /*parenthesized*/ node.expression.kind === SyntaxKind.ArrowFunction);
emitParenthesizedIf(node.expression, /*parenthesized*/ node.expression.kind === SyntaxKind.ArrowFunction);
write(";");
}