Skip to content

Commit 518a5d3

Browse files
Rudimentary template support, excluding tagging.
1 parent 6154923 commit 518a5d3

8 files changed

+445
-153
lines changed

src/compiler/checker.ts

+12-73
Original file line numberDiff line numberDiff line change
@@ -4103,11 +4103,11 @@ module ts {
41034103
}
41044104
return createArrayType(elementType);
41054105
}
4106-
4106+
41074107
function isNumericName(name: string) {
41084108
return (name !== "") && !isNaN(<number><any>name);
41094109
}
4110-
4110+
41114111
function checkObjectLiteral(node: ObjectLiteral, contextualMapper?: TypeMapper): Type {
41124112
var members = node.symbol.members;
41134113
var properties: SymbolTable = {};
@@ -5227,6 +5227,12 @@ module ts {
52275227
return resultType;
52285228
}
52295229

5230+
function checkTemplateExpression(node: TemplateExpression): void {
5231+
forEach((<TemplateExpression>node).templateSpans, templateSpan => {
5232+
checkExpression(templateSpan.expression);
5233+
});
5234+
}
5235+
52305236
function checkExpressionWithContextualType(node: Expression, contextualType: Type, contextualMapper?: TypeMapper): Type {
52315237
var saveContextualType = node.contextualType;
52325238
node.contextualType = contextualType;
@@ -5280,7 +5286,11 @@ module ts {
52805286
return booleanType;
52815287
case SyntaxKind.NumericLiteral:
52825288
return numberType;
5289+
case SyntaxKind.TemplateExpression:
5290+
checkTemplateExpression(<TemplateExpression>node);
5291+
// fall through
52835292
case SyntaxKind.StringLiteral:
5293+
case SyntaxKind.NoSubstitutionTemplateLiteral:
52845294
return stringType;
52855295
case SyntaxKind.RegularExpressionLiteral:
52865296
return globalRegExpType;
@@ -7229,77 +7239,6 @@ module ts {
72297239
return node.parent && node.parent.kind === SyntaxKind.TypeReference;
72307240
}
72317241

7232-
function isExpression(node: Node): boolean {
7233-
switch (node.kind) {
7234-
case SyntaxKind.ThisKeyword:
7235-
case SyntaxKind.SuperKeyword:
7236-
case SyntaxKind.NullKeyword:
7237-
case SyntaxKind.TrueKeyword:
7238-
case SyntaxKind.FalseKeyword:
7239-
case SyntaxKind.RegularExpressionLiteral:
7240-
case SyntaxKind.ArrayLiteral:
7241-
case SyntaxKind.ObjectLiteral:
7242-
case SyntaxKind.PropertyAccess:
7243-
case SyntaxKind.IndexedAccess:
7244-
case SyntaxKind.CallExpression:
7245-
case SyntaxKind.NewExpression:
7246-
case SyntaxKind.TypeAssertion:
7247-
case SyntaxKind.ParenExpression:
7248-
case SyntaxKind.FunctionExpression:
7249-
case SyntaxKind.ArrowFunction:
7250-
case SyntaxKind.PrefixOperator:
7251-
case SyntaxKind.PostfixOperator:
7252-
case SyntaxKind.BinaryExpression:
7253-
case SyntaxKind.ConditionalExpression:
7254-
case SyntaxKind.OmittedExpression:
7255-
return true;
7256-
case SyntaxKind.QualifiedName:
7257-
while (node.parent.kind === SyntaxKind.QualifiedName) node = node.parent;
7258-
return node.parent.kind === SyntaxKind.TypeQuery;
7259-
case SyntaxKind.Identifier:
7260-
if (node.parent.kind === SyntaxKind.TypeQuery) {
7261-
return true;
7262-
}
7263-
// Fall through
7264-
case SyntaxKind.NumericLiteral:
7265-
case SyntaxKind.StringLiteral:
7266-
var parent = node.parent;
7267-
switch (parent.kind) {
7268-
case SyntaxKind.VariableDeclaration:
7269-
case SyntaxKind.Parameter:
7270-
case SyntaxKind.Property:
7271-
case SyntaxKind.EnumMember:
7272-
case SyntaxKind.PropertyAssignment:
7273-
return (<VariableDeclaration>parent).initializer === node;
7274-
case SyntaxKind.ExpressionStatement:
7275-
case SyntaxKind.IfStatement:
7276-
case SyntaxKind.DoStatement:
7277-
case SyntaxKind.WhileStatement:
7278-
case SyntaxKind.ReturnStatement:
7279-
case SyntaxKind.WithStatement:
7280-
case SyntaxKind.SwitchStatement:
7281-
case SyntaxKind.CaseClause:
7282-
case SyntaxKind.ThrowStatement:
7283-
case SyntaxKind.SwitchStatement:
7284-
return (<ExpressionStatement>parent).expression === node;
7285-
case SyntaxKind.ForStatement:
7286-
return (<ForStatement>parent).initializer === node ||
7287-
(<ForStatement>parent).condition === node ||
7288-
(<ForStatement>parent).iterator === node;
7289-
case SyntaxKind.ForInStatement:
7290-
return (<ForInStatement>parent).variable === node ||
7291-
(<ForInStatement>parent).expression === node;
7292-
case SyntaxKind.TypeAssertion:
7293-
return node === (<TypeAssertion>parent).operand;
7294-
default:
7295-
if (isExpression(parent)) {
7296-
return true;
7297-
}
7298-
}
7299-
}
7300-
return false;
7301-
}
7302-
73037242
function isTypeNode(node: Node): boolean {
73047243
if (SyntaxKind.FirstTypeNode <= node.kind && node.kind <= SyntaxKind.LastTypeNode) {
73057244
return true;

src/compiler/core.ts

+13-6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ module ts {
55
[index: string]: T;
66
}
77

8+
export enum Comparison {
9+
LessThan = -1,
10+
EqualTo = 0,
11+
GreaterThan = 1
12+
}
13+
814
export interface StringSet extends Map<any> { }
915

1016
export function forEach<T, U>(array: T[], callback: (element: T) => U): U {
@@ -79,6 +85,7 @@ module ts {
7985
export function concatenate<T>(array1: T[], array2: T[]): T[] {
8086
if (!array2 || !array2.length) return array1;
8187
if (!array1 || !array1.length) return array2;
88+
8289
return array1.concat(array2);
8390
}
8491

@@ -304,11 +311,11 @@ module ts {
304311
};
305312
}
306313

307-
export function compareValues<T>(a: T, b: T): number {
308-
if (a === b) return 0;
309-
if (a === undefined) return -1;
310-
if (b === undefined) return 1;
311-
return a < b ? -1 : 1;
314+
export function compareValues<T>(a: T, b: T): Comparison {
315+
if (a === b) return Comparison.EqualTo;
316+
if (a === undefined) return Comparison.LessThan;
317+
if (b === undefined) return Comparison.GreaterThan;
318+
return a < b ? Comparison.LessThan : Comparison.GreaterThan;
312319
}
313320

314321
function getDiagnosticFilename(diagnostic: Diagnostic): string {
@@ -333,7 +340,7 @@ module ts {
333340
var previousDiagnostic = diagnostics[0];
334341
for (var i = 1; i < diagnostics.length; i++) {
335342
var currentDiagnostic = diagnostics[i];
336-
var isDupe = compareDiagnostics(currentDiagnostic, previousDiagnostic) === 0;
343+
var isDupe = compareDiagnostics(currentDiagnostic, previousDiagnostic) === Comparison.EqualTo;
337344
if (!isDupe) {
338345
newDiagnostics.push(currentDiagnostic);
339346
previousDiagnostic = currentDiagnostic;

src/compiler/diagnosticInformationMap.generated.ts

+1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ module ts {
122122
const_must_be_declared_inside_a_block: { code: 1156, category: DiagnosticCategory.Error, key: "const must be declared inside a block." },
123123
let_must_be_declared_inside_a_block: { code: 1157, category: DiagnosticCategory.Error, key: "let must be declared inside a block." },
124124
Only_var_declarations_can_be_exported: { code: 1158, category: DiagnosticCategory.Error, key: "Only var declarations can be exported." },
125+
Invalid_template_literal_expected: { code: 1159, category: DiagnosticCategory.Error, key: "Invalid template literal; expected '}'" },
125126
Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." },
126127
Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." },
127128
Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." },

src/compiler/diagnosticMessages.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@
449449
},
450450
"An enum member cannot have a numeric name.": {
451451
"category": "Error",
452-
"code": 1151
452+
"code": 1151
453453
},
454454
"'var', 'let' or 'const' expected.": {
455455
"category": "Error",
@@ -479,6 +479,10 @@
479479
"category": "Error",
480480
"code": 1158
481481
},
482+
"Invalid template literal; expected '}'": {
483+
"category": "Error",
484+
"code": 1159
485+
},
482486

483487
"Duplicate identifier '{0}'.": {
484488
"category": "Error",

src/compiler/emitter.ts

+98-3
Original file line numberDiff line numberDiff line change
@@ -792,14 +792,101 @@ module ts {
792792
}
793793
}
794794

795-
function emitLiteral(node: LiteralExpression) {
796-
var text = getSourceTextOfLocalNode(node);
797-
if (node.kind === SyntaxKind.StringLiteral && compilerOptions.sourceMap) {
795+
function emitLiteral(node: LiteralExpression): void {
796+
var text = getLiteralText();
797+
798+
if (compilerOptions.sourceMap && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) {
798799
writer.writeLiteral(text);
799800
}
800801
else {
801802
write(text);
802803
}
804+
805+
function getLiteralText() {
806+
if (compilerOptions.target < ScriptTarget.ES6 && isTemplateLiteralKind(node.kind)) {
807+
return getTemplateLiteralAsStringLiteral(node)
808+
}
809+
810+
return getSourceTextOfLocalNode(node);
811+
}
812+
}
813+
814+
function getTemplateLiteralAsStringLiteral(node: LiteralExpression): string {
815+
return "\"" + escapeString(node.text) + "\"";
816+
}
817+
818+
function emitTemplateExpression(node: TemplateExpression): void {
819+
if (compilerOptions.target >= ScriptTarget.ES6) {
820+
forEachChild(node, emitNode);
821+
return;
822+
}
823+
824+
var templateNeedsParens = isExpression(node.parent) &&
825+
comparePrecedenceToBinaryPlus(node.parent) !== Comparison.LessThan;
826+
827+
if (templateNeedsParens) {
828+
write("(");
829+
}
830+
831+
emitLiteral(node.head);
832+
833+
forEach(node.templateSpans, templateSpan => {
834+
var needsParens = comparePrecedenceToBinaryPlus(templateSpan.expression) !== Comparison.GreaterThan;
835+
836+
write(" + ");
837+
838+
if (needsParens) {
839+
write("(");
840+
}
841+
emit(templateSpan.expression);
842+
if (needsParens) {
843+
write(")");
844+
}
845+
846+
write(" + ")
847+
emitLiteral(templateSpan.literal);
848+
});
849+
850+
if (templateNeedsParens) {
851+
write(")");
852+
}
853+
854+
/**
855+
* Returns whether the expression has lesser, greater,
856+
* or equal precedence to the binary '+' operator
857+
*/
858+
function comparePrecedenceToBinaryPlus(expression: Expression): Comparison {
859+
// All binary expressions have lower precedence than '+' apart from '*', '/', and '%'.
860+
// All unary operators have a higher precedence apart from yield.
861+
// Arrow functions and conditionals have a lower precedence,
862+
// although we convert the former into regular function expressions in ES5 mode,
863+
// and in ES6 mode this function won't get called anyway.
864+
//
865+
// TODO (drosen): Note that we need to account for the upcoming 'yield' and
866+
// spread ('...') unary operators that are anticipated for ES6.
867+
switch (expression.kind) {
868+
case SyntaxKind.BinaryExpression:
869+
switch ((<BinaryExpression>expression).operator) {
870+
case SyntaxKind.AsteriskToken:
871+
case SyntaxKind.SlashToken:
872+
case SyntaxKind.PercentToken:
873+
return Comparison.GreaterThan;
874+
case SyntaxKind.PlusToken:
875+
return Comparison.EqualTo;
876+
default:
877+
return Comparison.LessThan;
878+
}
879+
case SyntaxKind.ConditionalExpression:
880+
return Comparison.LessThan;
881+
default:
882+
return Comparison.GreaterThan;
883+
}
884+
}
885+
886+
}
887+
888+
function emitTemplateSpan(span: TemplateSpan) {
889+
forEachChild(span, emitNode);
803890
}
804891

805892
// This function specifically handles numeric/string literals for enum and accessor 'identifiers'.
@@ -2091,7 +2178,15 @@ module ts {
20912178
case SyntaxKind.NumericLiteral:
20922179
case SyntaxKind.StringLiteral:
20932180
case SyntaxKind.RegularExpressionLiteral:
2181+
case SyntaxKind.NoSubstitutionTemplateLiteral:
2182+
case SyntaxKind.TemplateHead:
2183+
case SyntaxKind.TemplateMiddle:
2184+
case SyntaxKind.TemplateTail:
20942185
return emitLiteral(<LiteralExpression>node);
2186+
case SyntaxKind.TemplateExpression:
2187+
return emitTemplateExpression(<TemplateExpression>node);
2188+
case SyntaxKind.TemplateSpan:
2189+
return emitTemplateSpan(<TemplateSpan>node);
20952190
case SyntaxKind.QualifiedName:
20962191
return emitPropertyAccess(<QualifiedName>node);
20972192
case SyntaxKind.ArrayLiteral:

0 commit comments

Comments
 (0)