mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-23 10:29:01 -06:00
Rudimentary template support, excluding tagging.
This commit is contained in:
parent
61549239d8
commit
518a5d3a48
@ -4103,11 +4103,11 @@ module ts {
|
||||
}
|
||||
return createArrayType(elementType);
|
||||
}
|
||||
|
||||
|
||||
function isNumericName(name: string) {
|
||||
return (name !== "") && !isNaN(<number><any>name);
|
||||
}
|
||||
|
||||
|
||||
function checkObjectLiteral(node: ObjectLiteral, contextualMapper?: TypeMapper): Type {
|
||||
var members = node.symbol.members;
|
||||
var properties: SymbolTable = {};
|
||||
@ -5227,6 +5227,12 @@ module ts {
|
||||
return resultType;
|
||||
}
|
||||
|
||||
function checkTemplateExpression(node: TemplateExpression): void {
|
||||
forEach((<TemplateExpression>node).templateSpans, templateSpan => {
|
||||
checkExpression(templateSpan.expression);
|
||||
});
|
||||
}
|
||||
|
||||
function checkExpressionWithContextualType(node: Expression, contextualType: Type, contextualMapper?: TypeMapper): Type {
|
||||
var saveContextualType = node.contextualType;
|
||||
node.contextualType = contextualType;
|
||||
@ -5280,7 +5286,11 @@ module ts {
|
||||
return booleanType;
|
||||
case SyntaxKind.NumericLiteral:
|
||||
return numberType;
|
||||
case SyntaxKind.TemplateExpression:
|
||||
checkTemplateExpression(<TemplateExpression>node);
|
||||
// fall through
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
return stringType;
|
||||
case SyntaxKind.RegularExpressionLiteral:
|
||||
return globalRegExpType;
|
||||
@ -7229,77 +7239,6 @@ module ts {
|
||||
return node.parent && node.parent.kind === SyntaxKind.TypeReference;
|
||||
}
|
||||
|
||||
function isExpression(node: Node): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ThisKeyword:
|
||||
case SyntaxKind.SuperKeyword:
|
||||
case SyntaxKind.NullKeyword:
|
||||
case SyntaxKind.TrueKeyword:
|
||||
case SyntaxKind.FalseKeyword:
|
||||
case SyntaxKind.RegularExpressionLiteral:
|
||||
case SyntaxKind.ArrayLiteral:
|
||||
case SyntaxKind.ObjectLiteral:
|
||||
case SyntaxKind.PropertyAccess:
|
||||
case SyntaxKind.IndexedAccess:
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.NewExpression:
|
||||
case SyntaxKind.TypeAssertion:
|
||||
case SyntaxKind.ParenExpression:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.PrefixOperator:
|
||||
case SyntaxKind.PostfixOperator:
|
||||
case SyntaxKind.BinaryExpression:
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
case SyntaxKind.OmittedExpression:
|
||||
return true;
|
||||
case SyntaxKind.QualifiedName:
|
||||
while (node.parent.kind === SyntaxKind.QualifiedName) node = node.parent;
|
||||
return node.parent.kind === SyntaxKind.TypeQuery;
|
||||
case SyntaxKind.Identifier:
|
||||
if (node.parent.kind === SyntaxKind.TypeQuery) {
|
||||
return true;
|
||||
}
|
||||
// Fall through
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.StringLiteral:
|
||||
var parent = node.parent;
|
||||
switch (parent.kind) {
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
case SyntaxKind.Parameter:
|
||||
case SyntaxKind.Property:
|
||||
case SyntaxKind.EnumMember:
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
return (<VariableDeclaration>parent).initializer === node;
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
case SyntaxKind.IfStatement:
|
||||
case SyntaxKind.DoStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.ReturnStatement:
|
||||
case SyntaxKind.WithStatement:
|
||||
case SyntaxKind.SwitchStatement:
|
||||
case SyntaxKind.CaseClause:
|
||||
case SyntaxKind.ThrowStatement:
|
||||
case SyntaxKind.SwitchStatement:
|
||||
return (<ExpressionStatement>parent).expression === node;
|
||||
case SyntaxKind.ForStatement:
|
||||
return (<ForStatement>parent).initializer === node ||
|
||||
(<ForStatement>parent).condition === node ||
|
||||
(<ForStatement>parent).iterator === node;
|
||||
case SyntaxKind.ForInStatement:
|
||||
return (<ForInStatement>parent).variable === node ||
|
||||
(<ForInStatement>parent).expression === node;
|
||||
case SyntaxKind.TypeAssertion:
|
||||
return node === (<TypeAssertion>parent).operand;
|
||||
default:
|
||||
if (isExpression(parent)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function isTypeNode(node: Node): boolean {
|
||||
if (SyntaxKind.FirstTypeNode <= node.kind && node.kind <= SyntaxKind.LastTypeNode) {
|
||||
return true;
|
||||
|
||||
@ -5,6 +5,12 @@ module ts {
|
||||
[index: string]: T;
|
||||
}
|
||||
|
||||
export enum Comparison {
|
||||
LessThan = -1,
|
||||
EqualTo = 0,
|
||||
GreaterThan = 1
|
||||
}
|
||||
|
||||
export interface StringSet extends Map<any> { }
|
||||
|
||||
export function forEach<T, U>(array: T[], callback: (element: T) => U): U {
|
||||
@ -79,6 +85,7 @@ module ts {
|
||||
export function concatenate<T>(array1: T[], array2: T[]): T[] {
|
||||
if (!array2 || !array2.length) return array1;
|
||||
if (!array1 || !array1.length) return array2;
|
||||
|
||||
return array1.concat(array2);
|
||||
}
|
||||
|
||||
@ -304,11 +311,11 @@ module ts {
|
||||
};
|
||||
}
|
||||
|
||||
export function compareValues<T>(a: T, b: T): number {
|
||||
if (a === b) return 0;
|
||||
if (a === undefined) return -1;
|
||||
if (b === undefined) return 1;
|
||||
return a < b ? -1 : 1;
|
||||
export function compareValues<T>(a: T, b: T): Comparison {
|
||||
if (a === b) return Comparison.EqualTo;
|
||||
if (a === undefined) return Comparison.LessThan;
|
||||
if (b === undefined) return Comparison.GreaterThan;
|
||||
return a < b ? Comparison.LessThan : Comparison.GreaterThan;
|
||||
}
|
||||
|
||||
function getDiagnosticFilename(diagnostic: Diagnostic): string {
|
||||
@ -333,7 +340,7 @@ module ts {
|
||||
var previousDiagnostic = diagnostics[0];
|
||||
for (var i = 1; i < diagnostics.length; i++) {
|
||||
var currentDiagnostic = diagnostics[i];
|
||||
var isDupe = compareDiagnostics(currentDiagnostic, previousDiagnostic) === 0;
|
||||
var isDupe = compareDiagnostics(currentDiagnostic, previousDiagnostic) === Comparison.EqualTo;
|
||||
if (!isDupe) {
|
||||
newDiagnostics.push(currentDiagnostic);
|
||||
previousDiagnostic = currentDiagnostic;
|
||||
|
||||
@ -122,6 +122,7 @@ module ts {
|
||||
const_must_be_declared_inside_a_block: { code: 1156, category: DiagnosticCategory.Error, key: "const must be declared inside a block." },
|
||||
let_must_be_declared_inside_a_block: { code: 1157, category: DiagnosticCategory.Error, key: "let must be declared inside a block." },
|
||||
Only_var_declarations_can_be_exported: { code: 1158, category: DiagnosticCategory.Error, key: "Only var declarations can be exported." },
|
||||
Invalid_template_literal_expected: { code: 1159, category: DiagnosticCategory.Error, key: "Invalid template literal; expected '}'" },
|
||||
Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." },
|
||||
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." },
|
||||
Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." },
|
||||
|
||||
@ -449,7 +449,7 @@
|
||||
},
|
||||
"An enum member cannot have a numeric name.": {
|
||||
"category": "Error",
|
||||
"code": 1151
|
||||
"code": 1151
|
||||
},
|
||||
"'var', 'let' or 'const' expected.": {
|
||||
"category": "Error",
|
||||
@ -479,6 +479,10 @@
|
||||
"category": "Error",
|
||||
"code": 1158
|
||||
},
|
||||
"Invalid template literal; expected '}'": {
|
||||
"category": "Error",
|
||||
"code": 1159
|
||||
},
|
||||
|
||||
"Duplicate identifier '{0}'.": {
|
||||
"category": "Error",
|
||||
|
||||
@ -792,14 +792,101 @@ module ts {
|
||||
}
|
||||
}
|
||||
|
||||
function emitLiteral(node: LiteralExpression) {
|
||||
var text = getSourceTextOfLocalNode(node);
|
||||
if (node.kind === SyntaxKind.StringLiteral && compilerOptions.sourceMap) {
|
||||
function emitLiteral(node: LiteralExpression): void {
|
||||
var text = getLiteralText();
|
||||
|
||||
if (compilerOptions.sourceMap && (node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind))) {
|
||||
writer.writeLiteral(text);
|
||||
}
|
||||
else {
|
||||
write(text);
|
||||
}
|
||||
|
||||
function getLiteralText() {
|
||||
if (compilerOptions.target < ScriptTarget.ES6 && isTemplateLiteralKind(node.kind)) {
|
||||
return getTemplateLiteralAsStringLiteral(node)
|
||||
}
|
||||
|
||||
return getSourceTextOfLocalNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
function getTemplateLiteralAsStringLiteral(node: LiteralExpression): string {
|
||||
return "\"" + escapeString(node.text) + "\"";
|
||||
}
|
||||
|
||||
function emitTemplateExpression(node: TemplateExpression): void {
|
||||
if (compilerOptions.target >= ScriptTarget.ES6) {
|
||||
forEachChild(node, emitNode);
|
||||
return;
|
||||
}
|
||||
|
||||
var templateNeedsParens = isExpression(node.parent) &&
|
||||
comparePrecedenceToBinaryPlus(node.parent) !== Comparison.LessThan;
|
||||
|
||||
if (templateNeedsParens) {
|
||||
write("(");
|
||||
}
|
||||
|
||||
emitLiteral(node.head);
|
||||
|
||||
forEach(node.templateSpans, templateSpan => {
|
||||
var needsParens = comparePrecedenceToBinaryPlus(templateSpan.expression) !== Comparison.GreaterThan;
|
||||
|
||||
write(" + ");
|
||||
|
||||
if (needsParens) {
|
||||
write("(");
|
||||
}
|
||||
emit(templateSpan.expression);
|
||||
if (needsParens) {
|
||||
write(")");
|
||||
}
|
||||
|
||||
write(" + ")
|
||||
emitLiteral(templateSpan.literal);
|
||||
});
|
||||
|
||||
if (templateNeedsParens) {
|
||||
write(")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the expression has lesser, greater,
|
||||
* or equal precedence to the binary '+' operator
|
||||
*/
|
||||
function comparePrecedenceToBinaryPlus(expression: Expression): Comparison {
|
||||
// All binary expressions have lower precedence than '+' apart from '*', '/', and '%'.
|
||||
// All unary operators have a higher precedence apart from yield.
|
||||
// Arrow functions and conditionals have a lower precedence,
|
||||
// although we convert the former into regular function expressions in ES5 mode,
|
||||
// and in ES6 mode this function won't get called anyway.
|
||||
//
|
||||
// TODO (drosen): Note that we need to account for the upcoming 'yield' and
|
||||
// spread ('...') unary operators that are anticipated for ES6.
|
||||
switch (expression.kind) {
|
||||
case SyntaxKind.BinaryExpression:
|
||||
switch ((<BinaryExpression>expression).operator) {
|
||||
case SyntaxKind.AsteriskToken:
|
||||
case SyntaxKind.SlashToken:
|
||||
case SyntaxKind.PercentToken:
|
||||
return Comparison.GreaterThan;
|
||||
case SyntaxKind.PlusToken:
|
||||
return Comparison.EqualTo;
|
||||
default:
|
||||
return Comparison.LessThan;
|
||||
}
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
return Comparison.LessThan;
|
||||
default:
|
||||
return Comparison.GreaterThan;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function emitTemplateSpan(span: TemplateSpan) {
|
||||
forEachChild(span, emitNode);
|
||||
}
|
||||
|
||||
// This function specifically handles numeric/string literals for enum and accessor 'identifiers'.
|
||||
@ -2091,7 +2178,15 @@ module ts {
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.RegularExpressionLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
case SyntaxKind.TemplateHead:
|
||||
case SyntaxKind.TemplateMiddle:
|
||||
case SyntaxKind.TemplateTail:
|
||||
return emitLiteral(<LiteralExpression>node);
|
||||
case SyntaxKind.TemplateExpression:
|
||||
return emitTemplateExpression(<TemplateExpression>node);
|
||||
case SyntaxKind.TemplateSpan:
|
||||
return emitTemplateSpan(<TemplateSpan>node);
|
||||
case SyntaxKind.QualifiedName:
|
||||
return emitPropertyAccess(<QualifiedName>node);
|
||||
case SyntaxKind.ArrayLiteral:
|
||||
|
||||
@ -344,6 +344,10 @@ module ts {
|
||||
child((<ImportDeclaration>node).externalModuleName);
|
||||
case SyntaxKind.ExportAssignment:
|
||||
return child((<ExportAssignment>node).exportName);
|
||||
case SyntaxKind.TemplateExpression:
|
||||
return child((<TemplateExpression>node).head) || children((<TemplateExpression>node).templateSpans);
|
||||
case SyntaxKind.TemplateSpan:
|
||||
return child((<TemplateSpan>node).expression) || child((<TemplateSpan>node).literal);
|
||||
}
|
||||
}
|
||||
|
||||
@ -448,10 +452,95 @@ module ts {
|
||||
}
|
||||
}
|
||||
|
||||
export function isExpression(node: Node): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ThisKeyword:
|
||||
case SyntaxKind.SuperKeyword:
|
||||
case SyntaxKind.NullKeyword:
|
||||
case SyntaxKind.TrueKeyword:
|
||||
case SyntaxKind.FalseKeyword:
|
||||
case SyntaxKind.RegularExpressionLiteral:
|
||||
case SyntaxKind.ArrayLiteral:
|
||||
case SyntaxKind.ObjectLiteral:
|
||||
case SyntaxKind.PropertyAccess:
|
||||
case SyntaxKind.IndexedAccess:
|
||||
case SyntaxKind.CallExpression:
|
||||
case SyntaxKind.NewExpression:
|
||||
case SyntaxKind.TypeAssertion:
|
||||
case SyntaxKind.ParenExpression:
|
||||
case SyntaxKind.FunctionExpression:
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.PrefixOperator:
|
||||
case SyntaxKind.PostfixOperator:
|
||||
case SyntaxKind.BinaryExpression:
|
||||
case SyntaxKind.ConditionalExpression:
|
||||
case SyntaxKind.TemplateExpression:
|
||||
case SyntaxKind.OmittedExpression:
|
||||
return true;
|
||||
case SyntaxKind.QualifiedName:
|
||||
while (node.parent.kind === SyntaxKind.QualifiedName) node = node.parent;
|
||||
return node.parent.kind === SyntaxKind.TypeQuery;
|
||||
case SyntaxKind.Identifier:
|
||||
if (node.parent.kind === SyntaxKind.TypeQuery) {
|
||||
return true;
|
||||
}
|
||||
// fall through
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
var parent = node.parent;
|
||||
switch (parent.kind) {
|
||||
case SyntaxKind.VariableDeclaration:
|
||||
case SyntaxKind.Parameter:
|
||||
case SyntaxKind.Property:
|
||||
case SyntaxKind.EnumMember:
|
||||
case SyntaxKind.PropertyAssignment:
|
||||
return (<VariableDeclaration>parent).initializer === node;
|
||||
case SyntaxKind.ExpressionStatement:
|
||||
case SyntaxKind.IfStatement:
|
||||
case SyntaxKind.DoStatement:
|
||||
case SyntaxKind.WhileStatement:
|
||||
case SyntaxKind.ReturnStatement:
|
||||
case SyntaxKind.WithStatement:
|
||||
case SyntaxKind.SwitchStatement:
|
||||
case SyntaxKind.CaseClause:
|
||||
case SyntaxKind.ThrowStatement:
|
||||
case SyntaxKind.SwitchStatement:
|
||||
return (<ExpressionStatement>parent).expression === node;
|
||||
case SyntaxKind.ForStatement:
|
||||
return (<ForStatement>parent).initializer === node ||
|
||||
(<ForStatement>parent).condition === node ||
|
||||
(<ForStatement>parent).iterator === node;
|
||||
case SyntaxKind.ForInStatement:
|
||||
return (<ForInStatement>parent).variable === node ||
|
||||
(<ForInStatement>parent).expression === node;
|
||||
case SyntaxKind.TypeAssertion:
|
||||
return node === (<TypeAssertion>parent).operand;
|
||||
default:
|
||||
if (isExpression(parent)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function hasRestParameters(s: SignatureDeclaration): boolean {
|
||||
return s.parameters.length > 0 && (s.parameters[s.parameters.length - 1].flags & NodeFlags.Rest) !== 0;
|
||||
}
|
||||
|
||||
export function isLiteralKind(kind: SyntaxKind): boolean {
|
||||
return SyntaxKind.FirstLiteralToken <= kind && kind <= SyntaxKind.LastLiteralToken;
|
||||
}
|
||||
|
||||
export function isTextualLiteralKind(kind: SyntaxKind): boolean {
|
||||
return kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NoSubstitutionTemplateLiteral;
|
||||
}
|
||||
|
||||
export function isTemplateLiteralKind(kind: SyntaxKind): boolean {
|
||||
return SyntaxKind.FirstTemplateToken <= kind && kind <= SyntaxKind.LastTemplateToken;
|
||||
}
|
||||
|
||||
export function isInAmbientContext(node: Node): boolean {
|
||||
while (node) {
|
||||
if (node.flags & (NodeFlags.Ambient | NodeFlags.DeclarationFile)) return true;
|
||||
@ -460,6 +549,7 @@ module ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
export function isDeclaration(node: Node): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.TypeParameter:
|
||||
@ -875,6 +965,10 @@ module ts {
|
||||
return token = scanner.reScanSlashToken();
|
||||
}
|
||||
|
||||
function reScanTemplateToken(): SyntaxKind {
|
||||
return token = scanner.reScanTemplateToken();
|
||||
}
|
||||
|
||||
function lookAheadHelper<T>(callback: () => T, alwaysResetState: boolean): T {
|
||||
// Keep track of the state we'll need to rollback to if lookahead fails (or if the
|
||||
// caller asked us to always reset our state).
|
||||
@ -1021,7 +1115,9 @@ module ts {
|
||||
}
|
||||
|
||||
function isPropertyName(): boolean {
|
||||
return token >= SyntaxKind.Identifier || token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral;
|
||||
return token >= SyntaxKind.Identifier ||
|
||||
token === SyntaxKind.StringLiteral ||
|
||||
token === SyntaxKind.NumericLiteral;
|
||||
}
|
||||
|
||||
function parsePropertyName(): Identifier {
|
||||
@ -1296,7 +1392,44 @@ module ts {
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseLiteralNode(internName?:boolean): LiteralExpression {
|
||||
function parseTemplateExpression() {
|
||||
var template = <TemplateExpression>createNode(SyntaxKind.TemplateExpression);
|
||||
|
||||
template.head = parseLiteralNode();
|
||||
Debug.assert(template.head.kind === SyntaxKind.TemplateHead, "Template head has wrong token kind");
|
||||
|
||||
var templateSpans: TemplateSpan[] = [];
|
||||
do {
|
||||
templateSpans.push(parseTemplateSpan());
|
||||
}
|
||||
while (templateSpans[templateSpans.length - 1].literal.kind === SyntaxKind.TemplateMiddle)
|
||||
|
||||
template.templateSpans = templateSpans;
|
||||
return finishNode(template);
|
||||
}
|
||||
|
||||
function parseTemplateSpan(): TemplateSpan {
|
||||
var span = <TemplateSpan>createNode(SyntaxKind.TemplateSpan);
|
||||
span.expression = parseExpression(/*noIn*/ false);
|
||||
|
||||
var literal: LiteralExpression;
|
||||
|
||||
if (token === SyntaxKind.CloseBraceToken) {
|
||||
reScanTemplateToken()
|
||||
literal = parseLiteralNode();
|
||||
}
|
||||
else {
|
||||
error(Diagnostics.Invalid_template_literal_expected);
|
||||
literal = <LiteralExpression>createMissingNode();
|
||||
literal.text = "";
|
||||
}
|
||||
|
||||
span.literal = literal;
|
||||
|
||||
return finishNode(span);
|
||||
}
|
||||
|
||||
function parseLiteralNode(internName?: boolean): LiteralExpression {
|
||||
var node = <LiteralExpression>createNode(token);
|
||||
var text = scanner.getTokenValue();
|
||||
node.text = internName ? internIdentifier(text) : text;
|
||||
@ -1308,7 +1441,7 @@ module ts {
|
||||
// Octal literals are not allowed in strict mode or ES5
|
||||
// Note that theoretically the following condition would hold true literals like 009,
|
||||
// which is not octal.But because of how the scanner separates the tokens, we would
|
||||
// never get a token like this.Instead, we would get 00 and 9 as two separate tokens.
|
||||
// never get a token like this. Instead, we would get 00 and 9 as two separate tokens.
|
||||
// We also do not need to check for negatives because any prefix operator would be part of a
|
||||
// parent unary expression.
|
||||
if (node.kind === SyntaxKind.NumericLiteral
|
||||
@ -1327,7 +1460,9 @@ module ts {
|
||||
}
|
||||
|
||||
function parseStringLiteral(): LiteralExpression {
|
||||
if (token === SyntaxKind.StringLiteral) return parseLiteralNode(/*internName:*/ true);
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
return parseLiteralNode(/*internName:*/ true);
|
||||
}
|
||||
error(Diagnostics.String_literal_expected);
|
||||
return <LiteralExpression>createMissingNode();
|
||||
}
|
||||
@ -1755,6 +1890,8 @@ module ts {
|
||||
case SyntaxKind.FalseKeyword:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
case SyntaxKind.TemplateHead:
|
||||
case SyntaxKind.OpenParenToken:
|
||||
case SyntaxKind.OpenBracketToken:
|
||||
case SyntaxKind.OpenBraceToken:
|
||||
@ -1846,8 +1983,8 @@ module ts {
|
||||
}
|
||||
|
||||
// Now see if we might be in cases '2' or '3'.
|
||||
// If the expression was a LHS expression, and we have an assignment operator, then
|
||||
// we're in '2' or '3'. Consume the assignment and return.
|
||||
// If the expression was a LHS expression, and we have an assignment operator, then
|
||||
// we're in '2' or '3'. Consume the assignment and return.
|
||||
if (isLeftHandSideExpression(expr) && isAssignmentOperator()) {
|
||||
if (isInStrictMode && isEvalOrArgumentsIdentifier(expr)) {
|
||||
// ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an
|
||||
@ -1879,6 +2016,8 @@ module ts {
|
||||
case SyntaxKind.RegularExpressionLiteral:
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
case SyntaxKind.TemplateExpression:
|
||||
case SyntaxKind.FalseKeyword:
|
||||
case SyntaxKind.NullKeyword:
|
||||
case SyntaxKind.ThisKeyword:
|
||||
@ -2343,6 +2482,7 @@ module ts {
|
||||
return parseTokenNode();
|
||||
case SyntaxKind.NumericLiteral:
|
||||
case SyntaxKind.StringLiteral:
|
||||
case SyntaxKind.NoSubstitutionTemplateLiteral:
|
||||
return parseLiteralNode();
|
||||
case SyntaxKind.OpenParenToken:
|
||||
return parseParenExpression();
|
||||
@ -2360,6 +2500,9 @@ module ts {
|
||||
return parseLiteralNode();
|
||||
}
|
||||
break;
|
||||
case SyntaxKind.TemplateHead:
|
||||
return parseTemplateExpression();
|
||||
|
||||
default:
|
||||
if (isIdentifier()) {
|
||||
return parseIdentifier();
|
||||
|
||||
@ -24,6 +24,7 @@ module ts {
|
||||
isReservedWord(): boolean;
|
||||
reScanGreaterToken(): SyntaxKind;
|
||||
reScanSlashToken(): SyntaxKind;
|
||||
reScanTemplateToken(): SyntaxKind;
|
||||
scan(): SyntaxKind;
|
||||
setText(text: string): void;
|
||||
setTextPos(textPos: number): void;
|
||||
@ -465,7 +466,7 @@ module ts {
|
||||
var len: number; // Length of text
|
||||
var startPos: number; // Start position of whitespace before current token
|
||||
var tokenPos: number; // Start position of text of current token
|
||||
var token: number;
|
||||
var token: SyntaxKind;
|
||||
var tokenValue: string;
|
||||
var precedingLineBreak: boolean;
|
||||
|
||||
@ -518,10 +519,10 @@ module ts {
|
||||
return +(text.substring(start, pos));
|
||||
}
|
||||
|
||||
function scanHexDigits(count: number, exact?: boolean): number {
|
||||
function scanHexDigits(count: number, useExactCount?: boolean): number {
|
||||
var digits = 0;
|
||||
var value = 0;
|
||||
while (digits < count || !exact) {
|
||||
while (digits < count || !useExactCount) {
|
||||
var ch = text.charCodeAt(pos);
|
||||
if (ch >= CharacterCodes._0 && ch <= CharacterCodes._9) {
|
||||
value = value * 16 + ch - CharacterCodes._0;
|
||||
@ -562,60 +563,7 @@ module ts {
|
||||
}
|
||||
if (ch === CharacterCodes.backslash) {
|
||||
result += text.substring(start, pos);
|
||||
pos++;
|
||||
if (pos >= len) {
|
||||
error(Diagnostics.Unexpected_end_of_text);
|
||||
break;
|
||||
}
|
||||
ch = text.charCodeAt(pos++);
|
||||
switch (ch) {
|
||||
case CharacterCodes._0:
|
||||
result += "\0";
|
||||
break;
|
||||
case CharacterCodes.b:
|
||||
result += "\b";
|
||||
break;
|
||||
case CharacterCodes.t:
|
||||
result += "\t";
|
||||
break;
|
||||
case CharacterCodes.n:
|
||||
result += "\n";
|
||||
break;
|
||||
case CharacterCodes.v:
|
||||
result += "\v";
|
||||
break;
|
||||
case CharacterCodes.f:
|
||||
result += "\f";
|
||||
break;
|
||||
case CharacterCodes.r:
|
||||
result += "\r";
|
||||
break;
|
||||
case CharacterCodes.singleQuote:
|
||||
result += "\'";
|
||||
break;
|
||||
case CharacterCodes.doubleQuote:
|
||||
result += "\"";
|
||||
break;
|
||||
case CharacterCodes.x:
|
||||
case CharacterCodes.u:
|
||||
var ch = scanHexDigits(ch === CharacterCodes.x ? 2 : 4, true);
|
||||
if (ch >= 0) {
|
||||
result += String.fromCharCode(ch);
|
||||
}
|
||||
else {
|
||||
error(Diagnostics.Hexadecimal_digit_expected);
|
||||
}
|
||||
break;
|
||||
case CharacterCodes.carriageReturn:
|
||||
if (pos < len && text.charCodeAt(pos) === CharacterCodes.lineFeed) pos++;
|
||||
break;
|
||||
case CharacterCodes.lineFeed:
|
||||
case CharacterCodes.lineSeparator:
|
||||
case CharacterCodes.paragraphSeparator:
|
||||
break;
|
||||
default:
|
||||
result += String.fromCharCode(ch);
|
||||
}
|
||||
result += scanEscapeSequence();
|
||||
start = pos;
|
||||
continue;
|
||||
}
|
||||
@ -629,13 +577,134 @@ module ts {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current 'tokenValue' and returns a NoSubstitutionTemplateLiteral or
|
||||
* a literal component of a TemplateExpression.
|
||||
*/
|
||||
function scanTemplateAndSetTokenValue(): SyntaxKind {
|
||||
var isStartOfTemplate = text.charCodeAt(pos) === CharacterCodes.backtick;
|
||||
|
||||
pos++;
|
||||
var start = pos;
|
||||
var contents = ""
|
||||
var resultingToken = SyntaxKind.Unknown;
|
||||
|
||||
while (true) {
|
||||
if (pos >= len) {
|
||||
contents += text.substring(start, pos);
|
||||
error(Diagnostics.Unexpected_end_of_text);
|
||||
resultingToken = isStartOfTemplate ? SyntaxKind.NoSubstitutionTemplateLiteral : SyntaxKind.TemplateTail;
|
||||
break;
|
||||
}
|
||||
|
||||
var currChar = text.charCodeAt(pos);
|
||||
|
||||
// '`'
|
||||
if (currChar === CharacterCodes.backtick) {
|
||||
contents += text.substring(start, pos);
|
||||
pos++;
|
||||
resultingToken = isStartOfTemplate ? SyntaxKind.NoSubstitutionTemplateLiteral : SyntaxKind.TemplateTail;
|
||||
break;
|
||||
}
|
||||
|
||||
// '${'
|
||||
if (currChar === CharacterCodes.$ && pos + 1 < len && text.charCodeAt(pos + 1) === CharacterCodes.openBrace) {
|
||||
contents += text.substring(start, pos);
|
||||
pos += 2;
|
||||
resultingToken = isStartOfTemplate ? SyntaxKind.TemplateHead : SyntaxKind.TemplateMiddle;
|
||||
break;
|
||||
}
|
||||
|
||||
// Escape character
|
||||
if (currChar === CharacterCodes.backslash) {
|
||||
contents += text.substring(start, pos);
|
||||
contents += scanEscapeSequence();
|
||||
start = pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Speculated ECMAScript 6 Spec 11.8.6.1:
|
||||
// <CR><LF> and <CR> LineTerminatorSequences are normalized to <LF> for Template Values
|
||||
// An explicit EscapeSequence is needed to include a <CR> or <CR><LF> sequence.
|
||||
if (currChar === CharacterCodes.carriageReturn) {
|
||||
contents += text.substring(start, pos);
|
||||
|
||||
if (pos + 1 < len && text.charCodeAt(pos + 1) === CharacterCodes.lineFeed) {
|
||||
pos++;
|
||||
}
|
||||
pos++;
|
||||
contents += "\n";
|
||||
start = pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
tokenValue = contents;
|
||||
return resultingToken;
|
||||
}
|
||||
|
||||
function scanEscapeSequence(): string {
|
||||
pos++;
|
||||
if (pos >= len) {
|
||||
error(Diagnostics.Unexpected_end_of_text);
|
||||
return "";
|
||||
}
|
||||
var ch = text.charCodeAt(pos++);
|
||||
switch (ch) {
|
||||
case CharacterCodes._0:
|
||||
return "\0";
|
||||
case CharacterCodes.b:
|
||||
return "\b";
|
||||
case CharacterCodes.t:
|
||||
return "\t";
|
||||
case CharacterCodes.n:
|
||||
return "\n";
|
||||
case CharacterCodes.v:
|
||||
return "\v";
|
||||
case CharacterCodes.f:
|
||||
return "\f";
|
||||
case CharacterCodes.r:
|
||||
return "\r";
|
||||
case CharacterCodes.singleQuote:
|
||||
return "\'";
|
||||
case CharacterCodes.doubleQuote:
|
||||
return "\"";
|
||||
case CharacterCodes.x:
|
||||
case CharacterCodes.u:
|
||||
var ch = scanHexDigits(ch === CharacterCodes.x ? 2 : 4, /*useExactCount*/ true);
|
||||
if (ch >= 0) {
|
||||
return String.fromCharCode(ch);
|
||||
}
|
||||
else {
|
||||
error(Diagnostics.Hexadecimal_digit_expected);
|
||||
return ""
|
||||
}
|
||||
|
||||
// when encountering a LineContinuation (i.e. a backslash and a line terminator sequence),
|
||||
// the line terminator is interpreted to be "the empty code unit sequence".
|
||||
case CharacterCodes.carriageReturn:
|
||||
if (pos < len && text.charCodeAt(pos) === CharacterCodes.lineFeed) {
|
||||
pos++;
|
||||
}
|
||||
// fall through
|
||||
case CharacterCodes.lineFeed:
|
||||
case CharacterCodes.lineSeparator:
|
||||
case CharacterCodes.paragraphSeparator:
|
||||
return ""
|
||||
default:
|
||||
return String.fromCharCode(ch);
|
||||
}
|
||||
}
|
||||
|
||||
// Current character is known to be a backslash. Check for Unicode escape of the form '\uXXXX'
|
||||
// and return code point value if valid Unicode escape is found. Otherwise return -1.
|
||||
function peekUnicodeEscape(): number {
|
||||
if (pos + 5 < len && text.charCodeAt(pos + 1) === CharacterCodes.u) {
|
||||
var start = pos;
|
||||
pos += 2;
|
||||
var value = scanHexDigits(4, true);
|
||||
var value = scanHexDigits(4, /*useExactCount*/ true);
|
||||
pos = start;
|
||||
return value;
|
||||
}
|
||||
@ -734,6 +803,8 @@ module ts {
|
||||
case CharacterCodes.singleQuote:
|
||||
tokenValue = scanString();
|
||||
return token = SyntaxKind.StringLiteral;
|
||||
case CharacterCodes.backtick:
|
||||
return token = scanTemplateAndSetTokenValue()
|
||||
case CharacterCodes.percent:
|
||||
if (text.charCodeAt(pos + 1) === CharacterCodes.equals) {
|
||||
return pos += 2, token = SyntaxKind.PercentEqualsToken;
|
||||
@ -851,7 +922,7 @@ module ts {
|
||||
case CharacterCodes._0:
|
||||
if (pos + 2 < len && (text.charCodeAt(pos + 1) === CharacterCodes.X || text.charCodeAt(pos + 1) === CharacterCodes.x)) {
|
||||
pos += 2;
|
||||
var value = scanHexDigits(1, false);
|
||||
var value = scanHexDigits(1, /*useExactCount*/ false);
|
||||
if (value < 0) {
|
||||
error(Diagnostics.Hexadecimal_digit_expected);
|
||||
value = 0;
|
||||
@ -1037,6 +1108,15 @@ module ts {
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unconditionally back up and scan a template expression portion.
|
||||
*/
|
||||
function reScanTemplateToken(): SyntaxKind {
|
||||
Debug.assert("'reScanTemplateToken' should only be called on a '}'");
|
||||
pos = tokenPos;
|
||||
return token = scanTemplateAndSetTokenValue();
|
||||
}
|
||||
|
||||
function tryScan<T>(callback: () => T): T {
|
||||
var savePos = pos;
|
||||
var saveStartPos = startPos;
|
||||
@ -1085,10 +1165,11 @@ module ts {
|
||||
isReservedWord: () => token >= SyntaxKind.FirstReservedWord && token <= SyntaxKind.LastReservedWord,
|
||||
reScanGreaterToken: reScanGreaterToken,
|
||||
reScanSlashToken: reScanSlashToken,
|
||||
reScanTemplateToken: reScanTemplateToken,
|
||||
scan: scan,
|
||||
setText: setText,
|
||||
setTextPos: setTextPos,
|
||||
tryScan: tryScan
|
||||
tryScan: tryScan,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,11 @@ module ts {
|
||||
NumericLiteral,
|
||||
StringLiteral,
|
||||
RegularExpressionLiteral,
|
||||
NoSubstitutionTemplateLiteral,
|
||||
// Pseudo-literals
|
||||
TemplateHead,
|
||||
TemplateMiddle,
|
||||
TemplateTail,
|
||||
// Punctuation
|
||||
OpenBraceToken,
|
||||
CloseBraceToken,
|
||||
@ -170,6 +175,8 @@ module ts {
|
||||
PostfixOperator,
|
||||
BinaryExpression,
|
||||
ConditionalExpression,
|
||||
TemplateExpression,
|
||||
TemplateSpan,
|
||||
OmittedExpression,
|
||||
// Element
|
||||
Block,
|
||||
@ -230,7 +237,11 @@ module ts {
|
||||
FirstToken = EndOfFileToken,
|
||||
LastToken = StringKeyword,
|
||||
FirstTriviaToken = SingleLineCommentTrivia,
|
||||
LastTriviaToken = WhitespaceTrivia
|
||||
LastTriviaToken = WhitespaceTrivia,
|
||||
FirstLiteralToken = NumericLiteral,
|
||||
LastLiteralToken = NoSubstitutionTemplateLiteral,
|
||||
FirstTemplateToken = NoSubstitutionTemplateLiteral,
|
||||
LastTemplateToken = TemplateTail
|
||||
}
|
||||
|
||||
export enum NodeFlags {
|
||||
@ -369,13 +380,23 @@ module ts {
|
||||
body: Node; // Required, whereas the member inherited from FunctionDeclaration is optional
|
||||
}
|
||||
|
||||
// The text property of a LiteralExpression stores the interpreted value of the literal in text form. For a StringLiteral
|
||||
// this means quotes have been removed and escapes have been converted to actual characters. For a NumericLiteral, the
|
||||
// stored value is the toString() representation of the number. For example 1, 1.00, and 1e0 are all stored as just "1".
|
||||
// The text property of a LiteralExpression stores the interpreted value of the literal in text form. For a StringLiteral,
|
||||
// or any literal of a template, this means quotes have been removed and escapes have been converted to actual characters.
|
||||
// For a NumericLiteral, the stored value is the toString() representation of the number. For example 1, 1.00, and 1e0 are all stored as just "1".
|
||||
export interface LiteralExpression extends Expression {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface TemplateExpression extends Expression {
|
||||
head: LiteralExpression;
|
||||
templateSpans: TemplateSpan[]
|
||||
}
|
||||
|
||||
export interface TemplateSpan extends Node {
|
||||
expression: Expression;
|
||||
literal: LiteralExpression;
|
||||
}
|
||||
|
||||
export interface ParenExpression extends Expression {
|
||||
expression: Expression;
|
||||
}
|
||||
@ -1189,6 +1210,7 @@ module ts {
|
||||
asterisk = 0x2A, // *
|
||||
at = 0x40, // @
|
||||
backslash = 0x5C, // \
|
||||
backtick = 0x60, // `
|
||||
bar = 0x7C, // |
|
||||
caret = 0x5E, // ^
|
||||
closeBrace = 0x7D, // }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user