Skip to content

Commit a77d39b

Browse files
Merge pull request #1589 from ivogabe/taggedTemplates
Tagged templates ES3 & 5
2 parents 9c27fb1 + 2b10d39 commit a77d39b

File tree

64 files changed

+695
-531
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+695
-531
lines changed

src/compiler/binder.ts

+1
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ module ts {
377377

378378
function bind(node: Node) {
379379
node.parent = parent;
380+
380381
switch (node.kind) {
381382
case SyntaxKind.TypeParameter:
382383
bindDeclaration(<Declaration>node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes, /*isBlockScopeContainer*/ false);

src/compiler/checker.ts

-5
Original file line numberDiff line numberDiff line change
@@ -6759,11 +6759,6 @@ module ts {
67596759
}
67606760

67616761
function checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type {
6762-
// Grammar checking
6763-
if (languageVersion < ScriptTarget.ES6) {
6764-
grammarErrorOnFirstToken(node.template, Diagnostics.Tagged_templates_are_only_available_when_targeting_ECMAScript_6_and_higher);
6765-
}
6766-
67676762
return getReturnTypeOfSignature(getResolvedSignature(node));
67686763
}
67696764

src/compiler/diagnosticInformationMap.generated.ts

-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,6 @@ module ts {
117117
const_declarations_must_be_initialized: { code: 1155, category: DiagnosticCategory.Error, key: "'const' declarations must be initialized" },
118118
const_declarations_can_only_be_declared_inside_a_block: { code: 1156, category: DiagnosticCategory.Error, key: "'const' declarations can only be declared inside a block." },
119119
let_declarations_can_only_be_declared_inside_a_block: { code: 1157, category: DiagnosticCategory.Error, key: "'let' declarations can only be declared inside a block." },
120-
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." },
121120
Unterminated_template_literal: { code: 1160, category: DiagnosticCategory.Error, key: "Unterminated template literal." },
122121
Unterminated_regular_expression_literal: { code: 1161, category: DiagnosticCategory.Error, key: "Unterminated regular expression literal." },
123122
An_object_member_cannot_be_declared_optional: { code: 1162, category: DiagnosticCategory.Error, key: "An object member cannot be declared optional." },

src/compiler/diagnosticMessages.json

-4
Original file line numberDiff line numberDiff line change
@@ -459,10 +459,6 @@
459459
"category": "Error",
460460
"code": 1157
461461
},
462-
"Tagged templates are only available when targeting ECMAScript 6 and higher.": {
463-
"category": "Error",
464-
"code": 1159
465-
},
466462
"Unterminated template literal.": {
467463
"category": "Error",
468464
"code": 1160

src/compiler/emitter.ts

+80-8
Original file line numberDiff line numberDiff line change
@@ -2072,7 +2072,7 @@ module ts {
20722072
}
20732073
}
20742074

2075-
function emitParenthesized(node: Node, parenthesized: boolean) {
2075+
function emitParenthesizedIf(node: Node, parenthesized: boolean) {
20762076
if (parenthesized) {
20772077
write("(");
20782078
}
@@ -2205,6 +2205,72 @@ module ts {
22052205
function getTemplateLiteralAsStringLiteral(node: LiteralExpression): string {
22062206
return '"' + escapeString(node.text) + '"';
22072207
}
2208+
2209+
function emitDownlevelRawTemplateLiteral(node: LiteralExpression) {
2210+
// Find original source text, since we need to emit the raw strings of the tagged template.
2211+
// The raw strings contain the (escaped) strings of what the user wrote.
2212+
// Examples: `\n` is converted to "\\n", a template string with a newline to "\n".
2213+
var text = getSourceTextOfNodeFromSourceFile(currentSourceFile, node);
2214+
2215+
// text contains the original source, it will also contain quotes ("`"), dolar signs and braces ("${" and "}"),
2216+
// thus we need to remove those characters.
2217+
// First template piece starts with "`", others with "}"
2218+
// Last template piece ends with "`", others with "${"
2219+
var isLast = node.kind === SyntaxKind.NoSubstitutionTemplateLiteral || node.kind === SyntaxKind.TemplateTail;
2220+
text = text.substring(1, text.length - (isLast ? 1 : 2));
2221+
2222+
// Newline normalization:
2223+
// ES6 Spec 11.8.6.1 - Static Semantics of TV's and TRV's
2224+
// <CR><LF> and <CR> LineTerminatorSequences are normalized to <LF> for both TV and TRV.
2225+
text = text.replace(/\r\n?/g, "\n");
2226+
text = escapeString(text);
2227+
2228+
write('"' + text + '"');
2229+
}
2230+
2231+
function emitDownlevelTaggedTemplateArray(node: TaggedTemplateExpression, literalEmitter: (literal: LiteralExpression) => void) {
2232+
write("[");
2233+
if (node.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral) {
2234+
literalEmitter(<LiteralExpression>node.template);
2235+
}
2236+
else {
2237+
literalEmitter((<TemplateExpression>node.template).head);
2238+
forEach((<TemplateExpression>node.template).templateSpans, (child) => {
2239+
write(", ");
2240+
literalEmitter(child.literal);
2241+
});
2242+
}
2243+
write("]");
2244+
}
2245+
2246+
function emitDownlevelTaggedTemplate(node: TaggedTemplateExpression) {
2247+
var tempVariable = createAndRecordTempVariable(node);
2248+
write("(");
2249+
emit(tempVariable);
2250+
write(" = ");
2251+
emitDownlevelTaggedTemplateArray(node, emit);
2252+
write(", ");
2253+
2254+
emit(tempVariable);
2255+
write(".raw = ");
2256+
emitDownlevelTaggedTemplateArray(node, emitDownlevelRawTemplateLiteral);
2257+
write(", ");
2258+
2259+
emitParenthesizedIf(node.tag, needsParenthesisForPropertyAccessOrInvocation(node.tag));
2260+
write("(");
2261+
emit(tempVariable);
2262+
2263+
// Now we emit the expressions
2264+
if (node.template.kind === SyntaxKind.TemplateExpression) {
2265+
forEach((<TemplateExpression>node.template).templateSpans, templateSpan => {
2266+
write(", ");
2267+
var needsParens = templateSpan.expression.kind === SyntaxKind.BinaryExpression
2268+
&& (<BinaryExpression>templateSpan.expression).operatorToken.kind === SyntaxKind.CommaToken;
2269+
emitParenthesizedIf(templateSpan.expression, needsParens);
2270+
});
2271+
}
2272+
write("))");
2273+
}
22082274

22092275
function emitTemplateExpression(node: TemplateExpression): void {
22102276
// In ES6 mode and above, we can simply emit each portion of a template in order, but in
@@ -2249,7 +2315,8 @@ module ts {
22492315
write(" + ");
22502316
}
22512317

2252-
emitParenthesized(templateSpan.expression, needsParens);
2318+
emitParenthesizedIf(templateSpan.expression, needsParens);
2319+
22532320
// Only emit if the literal is non-empty.
22542321
// The binary '+' operator is left-associative, so the first string concatenation
22552322
// with the head will force the result up to this point to be a string.
@@ -2479,7 +2546,7 @@ module ts {
24792546
emit((<SpreadElementExpression>node).expression);
24802547
}
24812548

2482-
function needsParenthesisForPropertyAccess(node: Expression) {
2549+
function needsParenthesisForPropertyAccessOrInvocation(node: Expression) {
24832550
switch (node.kind) {
24842551
case SyntaxKind.Identifier:
24852552
case SyntaxKind.ArrayLiteralExpression:
@@ -2509,7 +2576,7 @@ module ts {
25092576
var e = elements[pos];
25102577
if (e.kind === SyntaxKind.SpreadElementExpression) {
25112578
e = (<SpreadElementExpression>e).expression;
2512-
emitParenthesized(e, /*parenthesized*/ group === 0 && needsParenthesisForPropertyAccess(e));
2579+
emitParenthesizedIf(e, /*parenthesized*/ group === 0 && needsParenthesisForPropertyAccessOrInvocation(e));
25132580
pos++;
25142581
}
25152582
else {
@@ -2985,9 +3052,14 @@ module ts {
29853052
}
29863053

29873054
function emitTaggedTemplateExpression(node: TaggedTemplateExpression): void {
2988-
emit(node.tag);
2989-
write(" ");
2990-
emit(node.template);
3055+
if (compilerOptions.target >= ScriptTarget.ES6) {
3056+
emit(node.tag);
3057+
write(" ");
3058+
emit(node.template);
3059+
}
3060+
else {
3061+
emitDownlevelTaggedTemplate(node);
3062+
}
29913063
}
29923064

29933065
function emitParenExpression(node: ParenthesizedExpression) {
@@ -3157,7 +3229,7 @@ module ts {
31573229
}
31583230

31593231
function emitExpressionStatement(node: ExpressionStatement) {
3160-
emitParenthesized(node.expression, /*parenthesized*/ node.expression.kind === SyntaxKind.ArrowFunction);
3232+
emitParenthesizedIf(node.expression, /*parenthesized*/ node.expression.kind === SyntaxKind.ArrowFunction);
31613233
write(";");
31623234
}
31633235

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//// [taggedTemplateStringsHexadecimalEscapes.ts]
2+
function f(...args: any[]) {
3+
}
4+
5+
f `\x0D${ "Interrupted CRLF" }\x0A`;
6+
7+
//// [taggedTemplateStringsHexadecimalEscapes.js]
8+
function f() {
9+
var args = [];
10+
for (var _i = 0; _i < arguments.length; _i++) {
11+
args[_i - 0] = arguments[_i];
12+
}
13+
}
14+
(_a = ["\r", "\n"], _a.raw = ["\\x0D", "\\x0A"], f(_a, "Interrupted CRLF"));
15+
var _a;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== tests/cases/compiler/taggedTemplateStringsHexadecimalEscapes.ts ===
2+
function f(...args: any[]) {
3+
>f : (...args: any[]) => void
4+
>args : any[]
5+
}
6+
7+
f `\x0D${ "Interrupted CRLF" }\x0A`;
8+
>f : (...args: any[]) => void
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//// [taggedTemplateStringsHexadecimalEscapesES6.ts]
2+
function f(...args: any[]) {
3+
}
4+
5+
f `\x0D${ "Interrupted CRLF" }\x0A`;
6+
7+
//// [taggedTemplateStringsHexadecimalEscapesES6.js]
8+
function f(...args) {
9+
}
10+
f `\x0D${"Interrupted CRLF"}\x0A`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== tests/cases/compiler/taggedTemplateStringsHexadecimalEscapesES6.ts ===
2+
function f(...args: any[]) {
3+
>f : (...args: any[]) => void
4+
>args : any[]
5+
}
6+
7+
f `\x0D${ "Interrupted CRLF" }\x0A`;
8+
>f : (...args: any[]) => void
9+

tests/baselines/reference/taggedTemplateStringsPlainCharactersThatArePartsOfEscapes01.errors.txt

-13
This file was deleted.

tests/baselines/reference/taggedTemplateStringsPlainCharactersThatArePartsOfEscapes01.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ function f() {
1414
x[_i - 0] = arguments[_i];
1515
}
1616
}
17-
f "0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n";
17+
(_a = ["0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n"], _a.raw = ["0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n"], f(_a));
18+
var _a;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
=== tests/cases/conformance/es6/templates/taggedTemplateStringsPlainCharactersThatArePartsOfEscapes01.ts ===
2+
3+
4+
function f(...x: any[]) {
5+
>f : (...x: any[]) => void
6+
>x : any[]
7+
8+
}
9+
10+
f `0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2028 2029 0085 t v f b r n`
11+
>f : (...x: any[]) => void
12+

0 commit comments

Comments
 (0)