See More

/// /// module ts { let nodeConstructors = new Array Node>(SyntaxKind.Count); /* @internal */ export let parseTime = 0; export function getNodeConstructor(kind: SyntaxKind): new () => Node { return nodeConstructors[kind] || (nodeConstructors[kind] = objectAllocator.getNodeConstructor(kind)); } export function createNode(kind: SyntaxKind): Node { return new (getNodeConstructor(kind))(); } function visitNode(cbNode: (node: Node) => T, node: Node): T { if (node) { return cbNode(node); } } function visitNodeArray(cbNodes: (nodes: Node[]) => T, nodes: Node[]) { if (nodes) { return cbNodes(nodes); } } function visitEachNode(cbNode: (node: Node) => T, nodes: Node[]) { if (nodes) { for (let node of nodes) { let result = cbNode(node); if (result) { return result; } } } } // Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes // stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise, // embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns // a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned. export function forEachChild(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T { if (!node) { return; } // The visitXXX functions could be written as local functions that close over the cbNode and cbNodeArray // callback parameters, but that causes a closure allocation for each invocation with noticeable effects // on performance. let visitNodes: (cb: (node: Node | Node[]) => T, nodes: Node[]) => T = cbNodeArray ? visitNodeArray : visitEachNode; let cbNodes = cbNodeArray || cbNode; switch (node.kind) { case SyntaxKind.QualifiedName: return visitNode(cbNode, (node).left) || visitNode(cbNode, (node).right); case SyntaxKind.TypeParameter: return visitNode(cbNode, (node).name) || visitNode(cbNode, (node).constraint) || visitNode(cbNode, (node).expression); case SyntaxKind.Parameter: case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: case SyntaxKind.PropertyAssignment: case SyntaxKind.ShorthandPropertyAssignment: case SyntaxKind.VariableDeclaration: case SyntaxKind.BindingElement: return visitNodes(cbNodes, node.decorators) || visitNodes(cbNodes, node.modifiers) || visitNode(cbNode, (node).propertyName) || visitNode(cbNode, (node).dotDotDotToken) || visitNode(cbNode, (node).name) || visitNode(cbNode, (node).questionToken) || visitNode(cbNode, (node).type) || visitNode(cbNode, (node).initializer); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: return visitNodes(cbNodes, node.decorators) || visitNodes(cbNodes, node.modifiers) || visitNodes(cbNodes, (node).typeParameters) || visitNodes(cbNodes, (node).parameters) || visitNode(cbNode, (node).type); case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: case SyntaxKind.Constructor: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.FunctionExpression: case SyntaxKind.FunctionDeclaration: case SyntaxKind.ArrowFunction: return visitNodes(cbNodes, node.decorators) || visitNodes(cbNodes, node.modifiers) || visitNode(cbNode, (node).asteriskToken) || visitNode(cbNode, (node).name) || visitNode(cbNode, (node).questionToken) || visitNodes(cbNodes, (node).typeParameters) || visitNodes(cbNodes, (node).parameters) || visitNode(cbNode, (node).type) || visitNode(cbNode, (node).equalsGreaterThanToken) || visitNode(cbNode, (node).body); case SyntaxKind.TypeReference: return visitNode(cbNode, (node).typeName) || visitNodes(cbNodes, (node).typeArguments); case SyntaxKind.TypeQuery: return visitNode(cbNode, (node).exprName); case SyntaxKind.TypeLiteral: return visitNodes(cbNodes, (node).members); case SyntaxKind.ArrayType: return visitNode(cbNode, (node).elementType); case SyntaxKind.TupleType: return visitNodes(cbNodes, (node).elementTypes); case SyntaxKind.UnionType: return visitNodes(cbNodes, (node).types); case SyntaxKind.ParenthesizedType: return visitNode(cbNode, (node).type); case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ArrayBindingPattern: return visitNodes(cbNodes, (node).elements); case SyntaxKind.ArrayLiteralExpression: return visitNodes(cbNodes, (node).elements); case SyntaxKind.ObjectLiteralExpression: return visitNodes(cbNodes, (node).properties); case SyntaxKind.PropertyAccessExpression: return visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).dotToken) || visitNode(cbNode, (node).name); case SyntaxKind.ElementAccessExpression: return visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).argumentExpression); case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: return visitNode(cbNode, (node).expression) || visitNodes(cbNodes, (node).typeArguments) || visitNodes(cbNodes, (node).arguments); case SyntaxKind.TaggedTemplateExpression: return visitNode(cbNode, (node).tag) || visitNode(cbNode, (node).template); case SyntaxKind.TypeAssertionExpression: return visitNode(cbNode, (node).type) || visitNode(cbNode, (node).expression); case SyntaxKind.ParenthesizedExpression: return visitNode(cbNode, (node).expression); case SyntaxKind.DeleteExpression: return visitNode(cbNode, (node).expression); case SyntaxKind.TypeOfExpression: return visitNode(cbNode, (node).expression); case SyntaxKind.VoidExpression: return visitNode(cbNode, (node).expression); case SyntaxKind.PrefixUnaryExpression: return visitNode(cbNode, (node).operand); case SyntaxKind.YieldExpression: return visitNode(cbNode, (node).asteriskToken) || visitNode(cbNode, (node).expression); case SyntaxKind.PostfixUnaryExpression: return visitNode(cbNode, (node).operand); case SyntaxKind.BinaryExpression: return visitNode(cbNode, (node).left) || visitNode(cbNode, (node).operatorToken) || visitNode(cbNode, (node).right); case SyntaxKind.ConditionalExpression: return visitNode(cbNode, (node).condition) || visitNode(cbNode, (node).questionToken) || visitNode(cbNode, (node).whenTrue) || visitNode(cbNode, (node).colonToken) || visitNode(cbNode, (node).whenFalse); case SyntaxKind.SpreadElementExpression: return visitNode(cbNode, (node).expression); case SyntaxKind.Block: case SyntaxKind.ModuleBlock: return visitNodes(cbNodes, (node).statements); case SyntaxKind.SourceFile: return visitNodes(cbNodes, (node).statements) || visitNode(cbNode, (node).endOfFileToken); case SyntaxKind.VariableStatement: return visitNodes(cbNodes, node.decorators) || visitNodes(cbNodes, node.modifiers) || visitNode(cbNode, (node).declarationList); case SyntaxKind.VariableDeclarationList: return visitNodes(cbNodes, (node).declarations); case SyntaxKind.ExpressionStatement: return visitNode(cbNode, (node).expression); case SyntaxKind.IfStatement: return visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).thenStatement) || visitNode(cbNode, (node).elseStatement); case SyntaxKind.DoStatement: return visitNode(cbNode, (node).statement) || visitNode(cbNode, (node).expression); case SyntaxKind.WhileStatement: return visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).statement); case SyntaxKind.ForStatement: return visitNode(cbNode, (node).initializer) || visitNode(cbNode, (node).condition) || visitNode(cbNode, (node).incrementor) || visitNode(cbNode, (node).statement); case SyntaxKind.ForInStatement: return visitNode(cbNode, (node).initializer) || visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).statement); case SyntaxKind.ForOfStatement: return visitNode(cbNode, (node).initializer) || visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).statement); case SyntaxKind.ContinueStatement: case SyntaxKind.BreakStatement: return visitNode(cbNode, (node).label); case SyntaxKind.ReturnStatement: return visitNode(cbNode, (node).expression); case SyntaxKind.WithStatement: return visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).statement); case SyntaxKind.SwitchStatement: return visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).caseBlock); case SyntaxKind.CaseBlock: return visitNodes(cbNodes, (node).clauses); case SyntaxKind.CaseClause: return visitNode(cbNode, (node).expression) || visitNodes(cbNodes, (node).statements); case SyntaxKind.DefaultClause: return visitNodes(cbNodes, (node).statements); case SyntaxKind.LabeledStatement: return visitNode(cbNode, (node).label) || visitNode(cbNode, (node).statement); case SyntaxKind.ThrowStatement: return visitNode(cbNode, (node).expression); case SyntaxKind.TryStatement: return visitNode(cbNode, (node).tryBlock) || visitNode(cbNode, (node).catchClause) || visitNode(cbNode, (node).finallyBlock); case SyntaxKind.CatchClause: return visitNode(cbNode, (node).variableDeclaration) || visitNode(cbNode, (node).block); case SyntaxKind.Decorator: return visitNode(cbNode, (node).expression); case SyntaxKind.ClassDeclaration: case SyntaxKind.ClassExpression: return visitNodes(cbNodes, node.decorators) || visitNodes(cbNodes, node.modifiers) || visitNode(cbNode, (node).name) || visitNodes(cbNodes, (node).typeParameters) || visitNodes(cbNodes, (node).heritageClauses) || visitNodes(cbNodes, (node).members); case SyntaxKind.InterfaceDeclaration: return visitNodes(cbNodes, node.decorators) || visitNodes(cbNodes, node.modifiers) || visitNode(cbNode, (node).name) || visitNodes(cbNodes, (node).typeParameters) || visitNodes(cbNodes, (node).heritageClauses) || visitNodes(cbNodes, (node).members); case SyntaxKind.TypeAliasDeclaration: return visitNodes(cbNodes, node.decorators) || visitNodes(cbNodes, node.modifiers) || visitNode(cbNode, (node).name) || visitNode(cbNode, (node).type); case SyntaxKind.EnumDeclaration: return visitNodes(cbNodes, node.decorators) || visitNodes(cbNodes, node.modifiers) || visitNode(cbNode, (node).name) || visitNodes(cbNodes, (node).members); case SyntaxKind.EnumMember: return visitNode(cbNode, (node).name) || visitNode(cbNode, (node).initializer); case SyntaxKind.ModuleDeclaration: return visitNodes(cbNodes, node.decorators) || visitNodes(cbNodes, node.modifiers) || visitNode(cbNode, (node).name) || visitNode(cbNode, (node).body); case SyntaxKind.ImportEqualsDeclaration: return visitNodes(cbNodes, node.decorators) || visitNodes(cbNodes, node.modifiers) || visitNode(cbNode, (node).name) || visitNode(cbNode, (node).moduleReference); case SyntaxKind.ImportDeclaration: return visitNodes(cbNodes, node.decorators) || visitNodes(cbNodes, node.modifiers) || visitNode(cbNode, (node).importClause) || visitNode(cbNode, (node).moduleSpecifier); case SyntaxKind.ImportClause: return visitNode(cbNode, (node).name) || visitNode(cbNode, (node).namedBindings); case SyntaxKind.NamespaceImport: return visitNode(cbNode, (node).name); case SyntaxKind.NamedImports: case SyntaxKind.NamedExports: return visitNodes(cbNodes, (node).elements); case SyntaxKind.ExportDeclaration: return visitNodes(cbNodes, node.decorators) || visitNodes(cbNodes, node.modifiers) || visitNode(cbNode, (node).exportClause) || visitNode(cbNode, (node).moduleSpecifier); case SyntaxKind.ImportSpecifier: case SyntaxKind.ExportSpecifier: return visitNode(cbNode, (node).propertyName) || visitNode(cbNode, (node).name); case SyntaxKind.ExportAssignment: return visitNodes(cbNodes, node.decorators) || visitNodes(cbNodes, node.modifiers) || visitNode(cbNode, (node).expression); case SyntaxKind.TemplateExpression: return visitNode(cbNode, (node).head) || visitNodes(cbNodes, (node).templateSpans); case SyntaxKind.TemplateSpan: return visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).literal); case SyntaxKind.ComputedPropertyName: return visitNode(cbNode, (node).expression); case SyntaxKind.HeritageClause: return visitNodes(cbNodes, (node).types); case SyntaxKind.ExpressionWithTypeArguments: return visitNode(cbNode, (node).expression) || visitNodes(cbNodes, (node).typeArguments); case SyntaxKind.ExternalModuleReference: return visitNode(cbNode, (node).expression); case SyntaxKind.MissingDeclaration: return visitNodes(cbNodes, node.decorators); } } export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false): SourceFile { let start = new Date().getTime(); let result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes); parseTime += new Date().getTime() - start; return result; } // Produces a new SourceFile for the 'newText' provided. The 'textChangeRange' parameter // indicates what changed between the 'text' that this SourceFile has and the 'newText'. // The SourceFile will be created with the compiler attempting to reuse as many nodes from // this file as possible. // // Note: this function mutates nodes from this SourceFile. That means any existing nodes // from this SourceFile that are being held onto may change as a result (including // becoming detached from any SourceFile). It is recommended that this SourceFile not // be used once 'update' is called on it. export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile { return IncrementalParser.updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks); } // Implement the parser as a singleton module. We do this for perf reasons because creating // parser instances can actually be expensive enough to impact us on projects with many source // files. module Parser { // Share a single scanner across all calls to parse a source file. This helps speed things // up by avoiding the cost of creating/compiling scanners over and over again. const scanner = createScanner(ScriptTarget.Latest, /*skipTrivia:*/ true); const disallowInAndDecoratorContext = ParserContextFlags.DisallowIn | ParserContextFlags.Decorator; let sourceFile: SourceFile; let syntaxCursor: IncrementalParser.SyntaxCursor; let token: SyntaxKind; let sourceText: string; let nodeCount: number; let identifiers: Map; let identifierCount: number; let parsingContext: ParsingContext; // Flags that dictate what parsing context we're in. For example: // Whether or not we are in strict parsing mode. All that changes in strict parsing mode is // that some tokens that would be considered identifiers may be considered keywords. // // When adding more parser context flags, consider which is the more common case that the // flag will be in. This should be the 'false' state for that flag. The reason for this is // that we don't store data in our nodes unless the value is in the *non-default* state. So, // for example, more often than code 'allows-in' (or doesn't 'disallow-in'). We opt for // 'disallow-in' set to 'false'. Otherwise, if we had 'allowsIn' set to 'true', then almost // all nodes would need extra state on them to store this info. // // Note: 'allowIn' and 'allowYield' track 1:1 with the [in] and [yield] concepts in the ES6 // grammar specification. // // An important thing about these context concepts. By default they are effectively inherited // while parsing through every grammar production. i.e. if you don't change them, then when // you parse a sub-production, it will have the same context values as the parent production. // This is great most of the time. After all, consider all the 'expression' grammar productions // and how nearly all of them pass along the 'in' and 'yield' context values: // // EqualityExpression[In, Yield] : // RelationalExpression[?In, ?Yield] // EqualityExpression[?In, ?Yield] == RelationalExpression[?In, ?Yield] // EqualityExpression[?In, ?Yield] != RelationalExpression[?In, ?Yield] // EqualityExpression[?In, ?Yield] === RelationalExpression[?In, ?Yield] // EqualityExpression[?In, ?Yield] !== RelationalExpression[?In, ?Yield] // // Where you have to be careful is then understanding what the points are in the grammar // where the values are *not* passed along. For example: // // SingleNameBinding[Yield,GeneratorParameter] // [+GeneratorParameter]BindingIdentifier[Yield] Initializer[In]opt // [~GeneratorParameter]BindingIdentifier[?Yield]Initializer[In, ?Yield]opt // // Here this is saying that if the GeneratorParameter context flag is set, that we should // explicitly set the 'yield' context flag to false before calling into the BindingIdentifier // and we should explicitly unset the 'yield' context flag before calling into the Initializer. // production. Conversely, if the GeneratorParameter context flag is not set, then we // should leave the 'yield' context flag alone. // // Getting this all correct is tricky and requires careful reading of the grammar to // understand when these values should be changed versus when they should be inherited. // // Note: it should not be necessary to save/restore these flags during speculative/lookahead // parsing. These context flags are naturally stored and restored through normal recursive // descent parsing and unwinding. let contextFlags: ParserContextFlags = 0; // Whether or not we've had a parse error since creating the last AST node. If we have // encountered an error, it will be stored on the next AST node we create. Parse errors // can be broken down into three categories: // // 1) An error that occurred during scanning. For example, an unterminated literal, or a // character that was completely not understood. // // 2) A token was expected, but was not present. This type of error is commonly produced // by the 'parseExpected' function. // // 3) A token was present that no parsing function was able to consume. This type of error // only occurs in the 'abortParsingListOrMoveToNextToken' function when the parser // decides to skip the token. // // In all of these cases, we want to mark the next node as having had an error before it. // With this mark, we can know in incremental settings if this node can be reused, or if // we have to reparse it. If we don't keep this information around, we may just reuse the // node. in that event we would then not produce the same errors as we did before, causing // significant confusion problems. // // Note: it is necessary that this value be saved/restored during speculative/lookahead // parsing. During lookahead parsing, we will often create a node. That node will have // this value attached, and then this value will be set back to 'false'. If we decide to // rewind, we must get back to the same value we had prior to the lookahead. // // Note: any errors at the end of the file that do not precede a regular node, should get // attached to the EOF token. let parseErrorBeforeNextFinishedNode: boolean = false; export function parseSourceFile(fileName: string, _sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, setParentNodes?: boolean): SourceFile { sourceText = _sourceText; syntaxCursor = _syntaxCursor; parsingContext = 0; identifiers = {}; identifierCount = 0; nodeCount = 0; contextFlags = 0; parseErrorBeforeNextFinishedNode = false; createSourceFile(fileName, languageVersion); // Initialize and prime the scanner before parsing the source elements. scanner.setText(sourceText); scanner.setOnError(scanError); scanner.setScriptTarget(languageVersion); token = nextToken(); processReferenceComments(sourceFile); sourceFile.statements = parseList(ParsingContext.SourceElements, /*checkForStrictMode*/ true, parseSourceElement); Debug.assert(token === SyntaxKind.EndOfFileToken); sourceFile.endOfFileToken = parseTokenNode(); setExternalModuleIndicator(sourceFile); sourceFile.nodeCount = nodeCount; sourceFile.identifierCount = identifierCount; sourceFile.identifiers = identifiers; if (setParentNodes) { fixupParentReferences(sourceFile); } syntaxCursor = undefined; // Clear out the text the scanner is pointing at, so it doesn't keep anything alive unnecessarily. scanner.setText(""); scanner.setOnError(undefined); let result = sourceFile; // Clear any data. We don't want to accidently hold onto it for too long. sourceFile = undefined; identifiers = undefined; syntaxCursor = undefined; sourceText = undefined; return result; } function fixupParentReferences(sourceFile: SourceFile) { // normally parent references are set during binding. However, for clients that only need // a syntax tree, and no semantic features, then the binding process is an unnecessary // overhead. This functions allows us to set all the parents, without all the expense of // binding. let parent: Node = sourceFile; forEachChild(sourceFile, visitNode); return; function visitNode(n: Node): void { // walk down setting parents that differ from the parent we think it should be. This // allows us to quickly bail out of setting parents for subtrees during incremental // parsing if (n.parent !== parent) { n.parent = parent; let saveParent = parent; parent = n; forEachChild(n, visitNode); parent = saveParent; } } } function createSourceFile(fileName: string, languageVersion: ScriptTarget) { sourceFile = createNode(SyntaxKind.SourceFile, /*pos*/ 0); sourceFile.pos = 0; sourceFile.end = sourceText.length; sourceFile.text = sourceText; sourceFile.parseDiagnostics = []; sourceFile.bindDiagnostics = []; sourceFile.languageVersion = languageVersion; sourceFile.fileName = normalizePath(fileName); sourceFile.flags = fileExtensionIs(sourceFile.fileName, ".d.ts") ? NodeFlags.DeclarationFile : 0; } function setContextFlag(val: Boolean, flag: ParserContextFlags) { if (val) { contextFlags |= flag; } else { contextFlags &= ~flag; } } function setStrictModeContext(val: boolean) { setContextFlag(val, ParserContextFlags.StrictMode); } function setDisallowInContext(val: boolean) { setContextFlag(val, ParserContextFlags.DisallowIn); } function setYieldContext(val: boolean) { setContextFlag(val, ParserContextFlags.Yield); } function setGeneratorParameterContext(val: boolean) { setContextFlag(val, ParserContextFlags.GeneratorParameter); } function setDecoratorContext(val: boolean) { setContextFlag(val, ParserContextFlags.Decorator); } function doOutsideOfContext(flags: ParserContextFlags, func: () => T): T { let currentContextFlags = contextFlags & flags; if (currentContextFlags) { setContextFlag(false, currentContextFlags); let result = func(); setContextFlag(true, currentContextFlags); return result; } // no need to do anything special as we are not in any of the requested contexts return func(); } function allowInAnd(func: () => T): T { if (contextFlags & ParserContextFlags.DisallowIn) { setDisallowInContext(false); let result = func(); setDisallowInContext(true); return result; } // no need to do anything special if 'in' is already allowed. return func(); } function disallowInAnd(func: () => T): T { if (contextFlags & ParserContextFlags.DisallowIn) { // no need to do anything special if 'in' is already disallowed. return func(); } setDisallowInContext(true); let result = func(); setDisallowInContext(false); return result; } function doInYieldContext(func: () => T): T { if (contextFlags & ParserContextFlags.Yield) { // no need to do anything special if we're already in the [Yield] context. return func(); } setYieldContext(true); let result = func(); setYieldContext(false); return result; } function doOutsideOfYieldContext(func: () => T): T { if (contextFlags & ParserContextFlags.Yield) { setYieldContext(false); let result = func(); setYieldContext(true); return result; } // no need to do anything special if we're not in the [Yield] context. return func(); } function doInDecoratorContext(func: () => T): T { if (contextFlags & ParserContextFlags.Decorator) { // no need to do anything special if we're already in the [Decorator] context. return func(); } setDecoratorContext(true); let result = func(); setDecoratorContext(false); return result; } function inYieldContext() { return (contextFlags & ParserContextFlags.Yield) !== 0; } function inStrictModeContext() { return (contextFlags & ParserContextFlags.StrictMode) !== 0; } function inGeneratorParameterContext() { return (contextFlags & ParserContextFlags.GeneratorParameter) !== 0; } function inDisallowInContext() { return (contextFlags & ParserContextFlags.DisallowIn) !== 0; } function inDecoratorContext() { return (contextFlags & ParserContextFlags.Decorator) !== 0; } function parseErrorAtCurrentToken(message: DiagnosticMessage, arg0?: any): void { let start = scanner.getTokenPos(); let length = scanner.getTextPos() - start; parseErrorAtPosition(start, length, message, arg0); } function parseErrorAtPosition(start: number, length: number, message: DiagnosticMessage, arg0?: any): void { // Don't report another error if it would just be at the same position as the last error. let lastError = lastOrUndefined(sourceFile.parseDiagnostics); if (!lastError || start !== lastError.start) { sourceFile.parseDiagnostics.push(createFileDiagnostic(sourceFile, start, length, message, arg0)); } // Mark that we've encountered an error. We'll set an appropriate bit on the next // node we finish so that it can't be reused incrementally. parseErrorBeforeNextFinishedNode = true; } function scanError(message: DiagnosticMessage, length?: number) { let pos = scanner.getTextPos(); parseErrorAtPosition(pos, length || 0, message); } function getNodePos(): number { return scanner.getStartPos(); } function getNodeEnd(): number { return scanner.getStartPos(); } function nextToken(): SyntaxKind { return token = scanner.scan(); } function getTokenPos(pos: number): number { return skipTrivia(sourceText, pos); } function reScanGreaterToken(): SyntaxKind { return token = scanner.reScanGreaterToken(); } function reScanSlashToken(): SyntaxKind { return token = scanner.reScanSlashToken(); } function reScanTemplateToken(): SyntaxKind { return token = scanner.reScanTemplateToken(); } function speculationHelper(callback: () => T, isLookAhead: 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). let saveToken = token; let saveParseDiagnosticsLength = sourceFile.parseDiagnostics.length; let saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode; // Note: it is not actually necessary to save/restore the context flags here. That's // because the saving/restorating of these flags happens naturally through the recursive // descent nature of our parser. However, we still store this here just so we can // assert that that invariant holds. let saveContextFlags = contextFlags; // If we're only looking ahead, then tell the scanner to only lookahead as well. // Otherwise, if we're actually speculatively parsing, then tell the scanner to do the // same. let result = isLookAhead ? scanner.lookAhead(callback) : scanner.tryScan(callback); Debug.assert(saveContextFlags === contextFlags); // If our callback returned something 'falsy' or we're just looking ahead, // then unconditionally restore us to where we were. if (!result || isLookAhead) { token = saveToken; sourceFile.parseDiagnostics.length = saveParseDiagnosticsLength; parseErrorBeforeNextFinishedNode = saveParseErrorBeforeNextFinishedNode; } return result; } // Invokes the provided callback then unconditionally restores the parser to the state it // was in immediately prior to invoking the callback. The result of invoking the callback // is returned from this function. function lookAhead(callback: () => T): T { return speculationHelper(callback, /*isLookAhead:*/ true); } // Invokes the provided callback. If the callback returns something falsy, then it restores // the parser to the state it was in immediately prior to invoking the callback. If the // callback returns something truthy, then the parser state is not rolled back. The result // of invoking the callback is returned from this function. function tryParse(callback: () => T): T { return speculationHelper(callback, /*isLookAhead:*/ false); } // Ignore strict mode flag because we will report an error in type checker instead. function isIdentifier(): boolean { if (token === SyntaxKind.Identifier) { return true; } // If we have a 'yield' keyword, and we're in the [yield] context, then 'yield' is // considered a keyword and is not an identifier. if (token === SyntaxKind.YieldKeyword && inYieldContext()) { return false; } return token > SyntaxKind.LastReservedWord; } function parseExpected(kind: SyntaxKind, diagnosticMessage?: DiagnosticMessage): boolean { if (token === kind) { nextToken(); return true; } // Report specific message if provided with one. Otherwise, report generic fallback message. if (diagnosticMessage) { parseErrorAtCurrentToken(diagnosticMessage); } else { parseErrorAtCurrentToken(Diagnostics._0_expected, tokenToString(kind)); } return false; } function parseOptional(t: SyntaxKind): boolean { if (token === t) { nextToken(); return true; } return false; } function parseOptionalToken(t: SyntaxKind): Node { if (token === t) { return parseTokenNode(); } return undefined; } function parseExpectedToken(t: SyntaxKind, reportAtCurrentPosition: boolean, diagnosticMessage: DiagnosticMessage, arg0?: any): Node { return parseOptionalToken(t) || createMissingNode(t, reportAtCurrentPosition, diagnosticMessage, arg0); } function parseTokenNode(): T { let node = createNode(token); nextToken(); return finishNode(node); } function canParseSemicolon() { // If there's a real semicolon, then we can always parse it out. if (token === SyntaxKind.SemicolonToken) { return true; } // We can parse out an optional semicolon in ASI cases in the following cases. return token === SyntaxKind.CloseBraceToken || token === SyntaxKind.EndOfFileToken || scanner.hasPrecedingLineBreak(); } function parseSemicolon(): boolean { if (canParseSemicolon()) { if (token === SyntaxKind.SemicolonToken) { // consume the semicolon if it was explicitly provided. nextToken(); } return true; } else { return parseExpected(SyntaxKind.SemicolonToken); } } function createNode(kind: SyntaxKind, pos?: number): Node { nodeCount++; let node = new (nodeConstructors[kind] || (nodeConstructors[kind] = objectAllocator.getNodeConstructor(kind)))(); if (!(pos >= 0)) { pos = scanner.getStartPos(); } node.pos = pos; node.end = pos; return node; } function finishNode(node: T): T { node.end = scanner.getStartPos(); if (contextFlags) { node.parserContextFlags = contextFlags; } // Keep track on the node if we encountered an error while parsing it. If we did, then // we cannot reuse the node incrementally. Once we've marked this node, clear out the // flag so that we don't mark any subsequent nodes. if (parseErrorBeforeNextFinishedNode) { parseErrorBeforeNextFinishedNode = false; node.parserContextFlags |= ParserContextFlags.ThisNodeHasError; } return node; } function createMissingNode(kind: SyntaxKind, reportAtCurrentPosition: boolean, diagnosticMessage: DiagnosticMessage, arg0?: any): Node { if (reportAtCurrentPosition) { parseErrorAtPosition(scanner.getStartPos(), 0, diagnosticMessage, arg0); } else { parseErrorAtCurrentToken(diagnosticMessage, arg0); } let result = createNode(kind, scanner.getStartPos()); (result).text = ""; return finishNode(result); } function internIdentifier(text: string): string { text = escapeIdentifier(text); return hasProperty(identifiers, text) ? identifiers[text] : (identifiers[text] = text); } // An identifier that starts with two underscores has an extra underscore character prepended to it to avoid issues // with magic property names like '__proto__'. The 'identifiers' object is used to share a single string instance for // each identifier in order to reduce memory consumption. function createIdentifier(isIdentifier: boolean, diagnosticMessage?: DiagnosticMessage): Identifier { identifierCount++; if (isIdentifier) { let node = createNode(SyntaxKind.Identifier); // Store original token kind if it is not just an Identifier so we can report appropriate error later in type checker if (token !== SyntaxKind.Identifier) { node.originalKeywordKind = token; } node.text = internIdentifier(scanner.getTokenValue()); nextToken(); return finishNode(node); } return createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition:*/ false, diagnosticMessage || Diagnostics.Identifier_expected); } function parseIdentifier(diagnosticMessage?: DiagnosticMessage): Identifier { return createIdentifier(isIdentifier(), diagnosticMessage); } function parseIdentifierName(): Identifier { return createIdentifier(isIdentifierOrKeyword()); } function isLiteralPropertyName(): boolean { return isIdentifierOrKeyword() || token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral; } function parsePropertyName(): DeclarationName { if (token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral) { return parseLiteralNode(/*internName:*/ true); } if (token === SyntaxKind.OpenBracketToken) { return parseComputedPropertyName(); } return parseIdentifierName(); } function parseComputedPropertyName(): ComputedPropertyName { // PropertyName[Yield,GeneratorParameter] : // LiteralPropertyName // [+GeneratorParameter] ComputedPropertyName // [~GeneratorParameter] ComputedPropertyName[?Yield] // // ComputedPropertyName[Yield] : // [ AssignmentExpression[In, ?Yield] ] // let node = createNode(SyntaxKind.ComputedPropertyName); parseExpected(SyntaxKind.OpenBracketToken); // We parse any expression (including a comma expression). But the grammar // says that only an assignment expression is allowed, so the grammar checker // will error if it sees a comma expression. let yieldContext = inYieldContext(); if (inGeneratorParameterContext()) { setYieldContext(false); } node.expression = allowInAnd(parseExpression); if (inGeneratorParameterContext()) { setYieldContext(yieldContext); } parseExpected(SyntaxKind.CloseBracketToken); return finishNode(node); } function parseContextualModifier(t: SyntaxKind): boolean { return token === t && tryParse(nextTokenCanFollowModifier); } function nextTokenCanFollowModifier() { if (token === SyntaxKind.ConstKeyword) { // 'const' is only a modifier if followed by 'enum'. return nextToken() === SyntaxKind.EnumKeyword; } if (token === SyntaxKind.ExportKeyword) { nextToken(); if (token === SyntaxKind.DefaultKeyword) { return lookAhead(nextTokenIsClassOrFunction); } return token !== SyntaxKind.AsteriskToken && token !== SyntaxKind.OpenBraceToken && canFollowModifier(); } if (token === SyntaxKind.DefaultKeyword) { return nextTokenIsClassOrFunction(); } nextToken(); return canFollowModifier(); } function parseAnyContextualModifier(): boolean { return isModifier(token) && tryParse(nextTokenCanFollowModifier); } function canFollowModifier(): boolean { return token === SyntaxKind.OpenBracketToken || token === SyntaxKind.OpenBraceToken || token === SyntaxKind.AsteriskToken || isLiteralPropertyName(); } function nextTokenIsClassOrFunction(): boolean { nextToken(); return token === SyntaxKind.ClassKeyword || token === SyntaxKind.FunctionKeyword; } // True if positioned at the start of a list element function isListElement(parsingContext: ParsingContext, inErrorRecovery: boolean): boolean { let node = currentNode(parsingContext); if (node) { return true; } switch (parsingContext) { case ParsingContext.SourceElements: case ParsingContext.ModuleElements: return isSourceElement(inErrorRecovery); case ParsingContext.BlockStatements: case ParsingContext.SwitchClauseStatements: return isStartOfStatement(inErrorRecovery); case ParsingContext.SwitchClauses: return token === SyntaxKind.CaseKeyword || token === SyntaxKind.DefaultKeyword; case ParsingContext.TypeMembers: return isStartOfTypeMember(); case ParsingContext.ClassMembers: // We allow semicolons as class elements (as specified by ES6) as long as we're // not in error recovery. If we're in error recovery, we don't want an errant // semicolon to be treated as a class member (since they're almost always used // for statements. return lookAhead(isClassMemberStart) || (token === SyntaxKind.SemicolonToken && !inErrorRecovery); case ParsingContext.EnumMembers: // Include open bracket computed properties. This technically also lets in indexers, // which would be a candidate for improved error reporting. return token === SyntaxKind.OpenBracketToken || isLiteralPropertyName(); case ParsingContext.ObjectLiteralMembers: return token === SyntaxKind.OpenBracketToken || token === SyntaxKind.AsteriskToken || isLiteralPropertyName(); case ParsingContext.ObjectBindingElements: return isLiteralPropertyName(); case ParsingContext.HeritageClauseElement: // If we see { } then only consume it as an expression if it is followed by , or { // That way we won't consume the body of a class in its heritage clause. if (token === SyntaxKind.OpenBraceToken) { return lookAhead(isValidHeritageClauseObjectLiteral); } if (!inErrorRecovery) { return isStartOfLeftHandSideExpression() && !isHeritageClauseExtendsOrImplementsKeyword(); } else { // If we're in error recovery we tighten up what we're willing to match. // That way we don't treat something like "this" as a valid heritage clause // element during recovery. return isIdentifier() && !isHeritageClauseExtendsOrImplementsKeyword(); } case ParsingContext.VariableDeclarations: return isIdentifierOrPattern(); case ParsingContext.ArrayBindingElements: return token === SyntaxKind.CommaToken || token === SyntaxKind.DotDotDotToken || isIdentifierOrPattern(); case ParsingContext.TypeParameters: return isIdentifier(); case ParsingContext.ArgumentExpressions: case ParsingContext.ArrayLiteralMembers: return token === SyntaxKind.CommaToken || token === SyntaxKind.DotDotDotToken || isStartOfExpression(); case ParsingContext.Parameters: return isStartOfParameter(); case ParsingContext.TypeArguments: case ParsingContext.TupleElementTypes: return token === SyntaxKind.CommaToken || isStartOfType(); case ParsingContext.HeritageClauses: return isHeritageClause(); case ParsingContext.ImportOrExportSpecifiers: return isIdentifierOrKeyword(); } Debug.fail("Non-exhaustive case in 'isListElement'."); } function isValidHeritageClauseObjectLiteral() { Debug.assert(token === SyntaxKind.OpenBraceToken); if (nextToken() === SyntaxKind.CloseBraceToken) { // if we see "extends {}" then only treat the {} as what we're extending (and not // the class body) if we have: // // extends {} { // extends {}, // extends {} extends // extends {} implements let next = nextToken(); return next === SyntaxKind.CommaToken || next === SyntaxKind.OpenBraceToken || next === SyntaxKind.ExtendsKeyword || next === SyntaxKind.ImplementsKeyword; } return true; } function nextTokenIsIdentifier() { nextToken(); return isIdentifier(); } function isHeritageClauseExtendsOrImplementsKeyword(): boolean { if (token === SyntaxKind.ImplementsKeyword || token === SyntaxKind.ExtendsKeyword) { return lookAhead(nextTokenIsStartOfExpression); } return false; } function nextTokenIsStartOfExpression() { nextToken(); return isStartOfExpression(); } // True if positioned at a list terminator function isListTerminator(kind: ParsingContext): boolean { if (token === SyntaxKind.EndOfFileToken) { // Being at the end of the file ends all lists. return true; } switch (kind) { case ParsingContext.ModuleElements: case ParsingContext.BlockStatements: case ParsingContext.SwitchClauses: case ParsingContext.TypeMembers: case ParsingContext.ClassMembers: case ParsingContext.EnumMembers: case ParsingContext.ObjectLiteralMembers: case ParsingContext.ObjectBindingElements: case ParsingContext.ImportOrExportSpecifiers: return token === SyntaxKind.CloseBraceToken; case ParsingContext.SwitchClauseStatements: return token === SyntaxKind.CloseBraceToken || token === SyntaxKind.CaseKeyword || token === SyntaxKind.DefaultKeyword; case ParsingContext.HeritageClauseElement: return token === SyntaxKind.OpenBraceToken || token === SyntaxKind.ExtendsKeyword || token === SyntaxKind.ImplementsKeyword; case ParsingContext.VariableDeclarations: return isVariableDeclaratorListTerminator(); case ParsingContext.TypeParameters: // Tokens other than '>' are here for better error recovery return token === SyntaxKind.GreaterThanToken || token === SyntaxKind.OpenParenToken || token === SyntaxKind.OpenBraceToken || token === SyntaxKind.ExtendsKeyword || token === SyntaxKind.ImplementsKeyword; case ParsingContext.ArgumentExpressions: // Tokens other than ')' are here for better error recovery return token === SyntaxKind.CloseParenToken || token === SyntaxKind.SemicolonToken; case ParsingContext.ArrayLiteralMembers: case ParsingContext.TupleElementTypes: case ParsingContext.ArrayBindingElements: return token === SyntaxKind.CloseBracketToken; case ParsingContext.Parameters: // Tokens other than ')' and ']' (the latter for index signatures) are here for better error recovery return token === SyntaxKind.CloseParenToken || token === SyntaxKind.CloseBracketToken /*|| token === SyntaxKind.OpenBraceToken*/; case ParsingContext.TypeArguments: // Tokens other than '>' are here for better error recovery return token === SyntaxKind.GreaterThanToken || token === SyntaxKind.OpenParenToken; case ParsingContext.HeritageClauses: return token === SyntaxKind.OpenBraceToken || token === SyntaxKind.CloseBraceToken; } } function isVariableDeclaratorListTerminator(): boolean { // If we can consume a semicolon (either explicitly, or with ASI), then consider us done // with parsing the list of variable declarators. if (canParseSemicolon()) { return true; } // in the case where we're parsing the variable declarator of a 'for-in' statement, we // are done if we see an 'in' keyword in front of us. Same with for-of if (isInOrOfKeyword(token)) { return true; } // ERROR RECOVERY TWEAK: // For better error recovery, if we see an '=>' then we just stop immediately. We've got an // arrow function here and it's going to be very unlikely that we'll resynchronize and get // another variable declaration. if (token === SyntaxKind.EqualsGreaterThanToken) { return true; } // Keep trying to parse out variable declarators. return false; } // True if positioned at element or terminator of the current list or any enclosing list function isInSomeParsingContext(): boolean { for (let kind = 0; kind < ParsingContext.Count; kind++) { if (parsingContext & (1 << kind)) { if (isListElement(kind, /* inErrorRecovery */ true) || isListTerminator(kind)) { return true; } } } return false; } // Parses a list of elements function parseList(kind: ParsingContext, checkForStrictMode: boolean, parseElement: () => T): NodeArray { let saveParsingContext = parsingContext; parsingContext |= 1 << kind; let result = >[]; result.pos = getNodePos(); let savedStrictModeContext = inStrictModeContext(); while (!isListTerminator(kind)) { if (isListElement(kind, /* inErrorRecovery */ false)) { let element = parseListElement(kind, parseElement); result.push(element); // test elements only if we are not already in strict mode if (checkForStrictMode && !inStrictModeContext()) { if (isPrologueDirective(element)) { if (isUseStrictPrologueDirective(sourceFile, element)) { setStrictModeContext(true); checkForStrictMode = false; } } else { checkForStrictMode = false; } } continue; } if (abortParsingListOrMoveToNextToken(kind)) { break; } } setStrictModeContext(savedStrictModeContext); result.end = getNodeEnd(); parsingContext = saveParsingContext; return result; } /// Should be called only on prologue directives (isPrologueDirective(node) should be true) function isUseStrictPrologueDirective(sourceFile: SourceFile, node: Node): boolean { Debug.assert(isPrologueDirective(node)); let nodeText = getSourceTextOfNodeFromSourceFile(sourceFile, (node).expression); // Note: the node text must be exactly "use strict" or 'use strict'. It is not ok for the // string to contain unicode escapes (as per ES5). return nodeText === '"use strict"' || nodeText === "'use strict'"; } function parseListElement(parsingContext: ParsingContext, parseElement: () => T): T { let node = currentNode(parsingContext); if (node) { return consumeNode(node); } return parseElement(); } function currentNode(parsingContext: ParsingContext): Node { // If there is an outstanding parse error that we've encountered, but not attached to // some node, then we cannot get a node from the old source tree. This is because we // want to mark the next node we encounter as being unusable. // // Note: This may be too conservative. Perhaps we could reuse the node and set the bit // on it (or its leftmost child) as having the error. For now though, being conservative // is nice and likely won't ever affect perf. if (parseErrorBeforeNextFinishedNode) { return undefined; } if (!syntaxCursor) { // if we don't have a cursor, we could never return a node from the old tree. return undefined; } let node = syntaxCursor.currentNode(scanner.getStartPos()); // Can't reuse a missing node. if (nodeIsMissing(node)) { return undefined; } // Can't reuse a node that intersected the change range. if (node.intersectsChange) { return undefined; } // Can't reuse a node that contains a parse error. This is necessary so that we // produce the same set of errors again. if (containsParseError(node)) { return undefined; } // We can only reuse a node if it was parsed under the same strict mode that we're // currently in. i.e. if we originally parsed a node in non-strict mode, but then // the user added 'using strict' at the top of the file, then we can't use that node // again as the presense of strict mode may cause us to parse the tokens in the file // differetly. // // Note: we *can* reuse tokens when the strict mode changes. That's because tokens // are unaffected by strict mode. It's just the parser will decide what to do with it // differently depending on what mode it is in. // // This also applies to all our other context flags as well. let nodeContextFlags = node.parserContextFlags & ParserContextFlags.ParserGeneratedFlags; if (nodeContextFlags !== contextFlags) { return undefined; } // Ok, we have a node that looks like it could be reused. Now verify that it is valid // in the currest list parsing context that we're currently at. if (!canReuseNode(node, parsingContext)) { return undefined; } return node; } function consumeNode(node: Node) { // Move the scanner so it is after the node we just consumed. scanner.setTextPos(node.end); nextToken(); return node; } function canReuseNode(node: Node, parsingContext: ParsingContext): boolean { switch (parsingContext) { case ParsingContext.ModuleElements: return isReusableModuleElement(node); case ParsingContext.ClassMembers: return isReusableClassMember(node); case ParsingContext.SwitchClauses: return isReusableSwitchClause(node); case ParsingContext.BlockStatements: case ParsingContext.SwitchClauseStatements: return isReusableStatement(node); case ParsingContext.EnumMembers: return isReusableEnumMember(node); case ParsingContext.TypeMembers: return isReusableTypeMember(node); case ParsingContext.VariableDeclarations: return isReusableVariableDeclaration(node); case ParsingContext.Parameters: return isReusableParameter(node); // Any other lists we do not care about reusing nodes in. But feel free to add if // you can do so safely. Danger areas involve nodes that may involve speculative // parsing. If speculative parsing is involved with the node, then the range the // parser reached while looking ahead might be in the edited range (see the example // in canReuseVariableDeclaratorNode for a good case of this). case ParsingContext.HeritageClauses: // This would probably be safe to reuse. There is no speculative parsing with // heritage clauses. case ParsingContext.TypeParameters: // This would probably be safe to reuse. There is no speculative parsing with // type parameters. Note that that's because type *parameters* only occur in // unambiguous *type* contexts. While type *arguments* occur in very ambiguous // *expression* contexts. case ParsingContext.TupleElementTypes: // This would probably be safe to reuse. There is no speculative parsing with // tuple types. // Technically, type argument list types are probably safe to reuse. While // speculative parsing is involved with them (since type argument lists are only // produced from speculative parsing a < as a type argument list), we only have // the types because speculative parsing succeeded. Thus, the lookahead never // went past the end of the list and rewound. case ParsingContext.TypeArguments: // Note: these are almost certainly not safe to ever reuse. Expressions commonly // need a large amount of lookahead, and we should not reuse them as they may // have actually intersected the edit. case ParsingContext.ArgumentExpressions: // This is not safe to reuse for the same reason as the 'AssignmentExpression' // cases. i.e. a property assignment may end with an expression, and thus might // have lookahead far beyond it's old node. case ParsingContext.ObjectLiteralMembers: // This is probably not safe to reuse. There can be speculative parsing with // type names in a heritage clause. There can be generic names in the type // name list, and there can be left hand side expressions (which can have type // arguments.) case ParsingContext.HeritageClauseElement: } return false; } function isReusableModuleElement(node: Node) { if (node) { switch (node.kind) { case SyntaxKind.ImportDeclaration: case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.ExportDeclaration: case SyntaxKind.ExportAssignment: case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.ModuleDeclaration: case SyntaxKind.EnumDeclaration: return true; } return isReusableStatement(node); } return false; } function isReusableClassMember(node: Node) { if (node) { switch (node.kind) { case SyntaxKind.Constructor: case SyntaxKind.IndexSignature: case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.PropertyDeclaration: case SyntaxKind.SemicolonClassElement: return true; } } return false; } function isReusableSwitchClause(node: Node) { if (node) { switch (node.kind) { case SyntaxKind.CaseClause: case SyntaxKind.DefaultClause: return true; } } return false; } function isReusableStatement(node: Node) { if (node) { switch (node.kind) { case SyntaxKind.FunctionDeclaration: case SyntaxKind.VariableStatement: case SyntaxKind.Block: case SyntaxKind.IfStatement: case SyntaxKind.ExpressionStatement: case SyntaxKind.ThrowStatement: case SyntaxKind.ReturnStatement: case SyntaxKind.SwitchStatement: case SyntaxKind.BreakStatement: case SyntaxKind.ContinueStatement: case SyntaxKind.ForInStatement: case SyntaxKind.ForOfStatement: case SyntaxKind.ForStatement: case SyntaxKind.WhileStatement: case SyntaxKind.WithStatement: case SyntaxKind.EmptyStatement: case SyntaxKind.TryStatement: case SyntaxKind.LabeledStatement: case SyntaxKind.DoStatement: case SyntaxKind.DebuggerStatement: return true; } } return false; } function isReusableEnumMember(node: Node) { return node.kind === SyntaxKind.EnumMember; } function isReusableTypeMember(node: Node) { if (node) { switch (node.kind) { case SyntaxKind.ConstructSignature: case SyntaxKind.MethodSignature: case SyntaxKind.IndexSignature: case SyntaxKind.PropertySignature: case SyntaxKind.CallSignature: return true; } } return false; } function isReusableVariableDeclaration(node: Node) { if (node.kind !== SyntaxKind.VariableDeclaration) { return false; } // Very subtle incremental parsing bug. Consider the following code: // // let v = new List < A, B // // This is actually legal code. It's a list of variable declarators "v = new List() // // then we have a problem. "v = new Listnode; return variableDeclarator.initializer === undefined; } function isReusableParameter(node: Node) { if (node.kind !== SyntaxKind.Parameter) { return false; } // See the comment in isReusableVariableDeclaration for why we do this. let parameter = node; return parameter.initializer === undefined; } // Returns true if we should abort parsing. function abortParsingListOrMoveToNextToken(kind: ParsingContext) { parseErrorAtCurrentToken(parsingContextErrors(kind)); if (isInSomeParsingContext()) { return true; } nextToken(); return false; } function parsingContextErrors(context: ParsingContext): DiagnosticMessage { switch (context) { case ParsingContext.SourceElements: return Diagnostics.Declaration_or_statement_expected; case ParsingContext.ModuleElements: return Diagnostics.Declaration_or_statement_expected; case ParsingContext.BlockStatements: return Diagnostics.Statement_expected; case ParsingContext.SwitchClauses: return Diagnostics.case_or_default_expected; case ParsingContext.SwitchClauseStatements: return Diagnostics.Statement_expected; case ParsingContext.TypeMembers: return Diagnostics.Property_or_signature_expected; case ParsingContext.ClassMembers: return Diagnostics.Unexpected_token_A_constructor_method_accessor_or_property_was_expected; case ParsingContext.EnumMembers: return Diagnostics.Enum_member_expected; case ParsingContext.HeritageClauseElement: return Diagnostics.Expression_expected; case ParsingContext.VariableDeclarations: return Diagnostics.Variable_declaration_expected; case ParsingContext.ObjectBindingElements: return Diagnostics.Property_destructuring_pattern_expected; case ParsingContext.ArrayBindingElements: return Diagnostics.Array_element_destructuring_pattern_expected; case ParsingContext.ArgumentExpressions: return Diagnostics.Argument_expression_expected; case ParsingContext.ObjectLiteralMembers: return Diagnostics.Property_assignment_expected; case ParsingContext.ArrayLiteralMembers: return Diagnostics.Expression_or_comma_expected; case ParsingContext.Parameters: return Diagnostics.Parameter_declaration_expected; case ParsingContext.TypeParameters: return Diagnostics.Type_parameter_declaration_expected; case ParsingContext.TypeArguments: return Diagnostics.Type_argument_expected; case ParsingContext.TupleElementTypes: return Diagnostics.Type_expected; case ParsingContext.HeritageClauses: return Diagnostics.Unexpected_token_expected; case ParsingContext.ImportOrExportSpecifiers: return Diagnostics.Identifier_expected; } }; // Parses a comma-delimited list of elements function parseDelimitedList(kind: ParsingContext, parseElement: () => T, considerSemicolonAsDelimeter?: boolean): NodeArray { let saveParsingContext = parsingContext; parsingContext |= 1 << kind; let result = >[]; result.pos = getNodePos(); let commaStart = -1; // Meaning the previous token was not a comma while (true) { if (isListElement(kind, /* inErrorRecovery */ false)) { result.push(parseListElement(kind, parseElement)); commaStart = scanner.getTokenPos(); if (parseOptional(SyntaxKind.CommaToken)) { continue; } commaStart = -1; // Back to the state where the last token was not a comma if (isListTerminator(kind)) { break; } // We didn't get a comma, and the list wasn't terminated, explicitly parse // out a comma so we give a good error message. parseExpected(SyntaxKind.CommaToken); // If the token was a semicolon, and the caller allows that, then skip it and // continue. This ensures we get back on track and don't result in tons of // parse errors. For example, this can happen when people do things like use // a semicolon to delimit object literal members. Note: we'll have already // reported an error when we called parseExpected above. if (considerSemicolonAsDelimeter && token === SyntaxKind.SemicolonToken && !scanner.hasPrecedingLineBreak()) { nextToken(); } continue; } if (isListTerminator(kind)) { break; } if (abortParsingListOrMoveToNextToken(kind)) { break; } } // Recording the trailing comma is deliberately done after the previous // loop, and not just if we see a list terminator. This is because the list // may have ended incorrectly, but it is still important to know if there // was a trailing comma. // Check if the last token was a comma. if (commaStart >= 0) { // Always preserve a trailing comma by marking it on the NodeArray result.hasTrailingComma = true; } result.end = getNodeEnd(); parsingContext = saveParsingContext; return result; } function createMissingList(): NodeArray { let pos = getNodePos(); let result = >[]; result.pos = pos; result.end = pos; return result; } function parseBracketedList(kind: ParsingContext, parseElement: () => T, open: SyntaxKind, close: SyntaxKind): NodeArray { if (parseExpected(open)) { let result = parseDelimitedList(kind, parseElement); parseExpected(close); return result; } return createMissingList(); } // The allowReservedWords parameter controls whether reserved words are permitted after the first dot function parseEntityName(allowReservedWords: boolean, diagnosticMessage?: DiagnosticMessage): EntityName { let entity: EntityName = parseIdentifier(diagnosticMessage); while (parseOptional(SyntaxKind.DotToken)) { let node = createNode(SyntaxKind.QualifiedName, entity.pos); node.left = entity; node.right = parseRightSideOfDot(allowReservedWords); entity = finishNode(node); } return entity; } function parseRightSideOfDot(allowIdentifierNames: boolean): Identifier { // Technically a keyword is valid here as all keywords are identifier names. // However, often we'll encounter this in error situations when the keyword // is actually starting another valid construct. // // So, we check for the following specific case: // // name. // keyword identifierNameOrKeyword // // Note: the newlines are important here. For example, if that above code // were rewritten into: // // name.keyword // identifierNameOrKeyword // // Then we would consider it valid. That's because ASI would take effect and // the code would be implicitly: "name.keyword; identifierNameOrKeyword". // In the first case though, ASI will not take effect because there is not a // line terminator after the keyword. if (scanner.hasPrecedingLineBreak() && scanner.isReservedWord()) { let matchesPattern = lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine); if (matchesPattern) { // Report that we need an identifier. However, report it right after the dot, // and not on the next token. This is because the next token might actually // be an identifier and the error woudl be quite confusing. return createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentToken:*/ true, Diagnostics.Identifier_expected); } } return allowIdentifierNames ? parseIdentifierName() : parseIdentifier(); } function parseTemplateExpression(): TemplateExpression { let template = createNode(SyntaxKind.TemplateExpression); template.head = parseLiteralNode(); Debug.assert(template.head.kind === SyntaxKind.TemplateHead, "Template head has wrong token kind"); let templateSpans = >[]; templateSpans.pos = getNodePos(); do { templateSpans.push(parseTemplateSpan()); } while (lastOrUndefined(templateSpans).literal.kind === SyntaxKind.TemplateMiddle) templateSpans.end = getNodeEnd(); template.templateSpans = templateSpans; return finishNode(template); } function parseTemplateSpan(): TemplateSpan { let span = createNode(SyntaxKind.TemplateSpan); span.expression = allowInAnd(parseExpression); let literal: LiteralExpression; if (token === SyntaxKind.CloseBraceToken) { reScanTemplateToken() literal = parseLiteralNode(); } else { literal = parseExpectedToken(SyntaxKind.TemplateTail, /*reportAtCurrentPosition:*/ false, Diagnostics._0_expected, tokenToString(SyntaxKind.CloseBraceToken)); } span.literal = literal; return finishNode(span); } function parseLiteralNode(internName?: boolean): LiteralExpression { let node = createNode(token); let text = scanner.getTokenValue(); node.text = internName ? internIdentifier(text) : text; if (scanner.hasExtendedUnicodeEscape()) { node.hasExtendedUnicodeEscape = true; } if (scanner.isUnterminated()) { node.isUnterminated = true; } let tokenPos = scanner.getTokenPos(); nextToken(); finishNode(node); // 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. // 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 && sourceText.charCodeAt(tokenPos) === CharacterCodes._0 && isOctalDigit(sourceText.charCodeAt(tokenPos + 1))) { node.flags |= NodeFlags.OctalLiteral; } return node; } // TYPES function parseTypeReference(): TypeReferenceNode { let node = createNode(SyntaxKind.TypeReference); node.typeName = parseEntityName(/*allowReservedWords*/ false, Diagnostics.Type_expected); if (!scanner.hasPrecedingLineBreak() && token === SyntaxKind.LessThanToken) { node.typeArguments = parseBracketedList(ParsingContext.TypeArguments, parseType, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken); } return finishNode(node); } function parseTypeQuery(): TypeQueryNode { let node = createNode(SyntaxKind.TypeQuery); parseExpected(SyntaxKind.TypeOfKeyword); node.exprName = parseEntityName(/*allowReservedWords*/ true); return finishNode(node); } function parseTypeParameter(): TypeParameterDeclaration { let node = createNode(SyntaxKind.TypeParameter); node.name = parseIdentifier(); if (parseOptional(SyntaxKind.ExtendsKeyword)) { // It's not uncommon for people to write improper constraints to a generic. If the // user writes a constraint that is an expression and not an actual type, then parse // it out as an expression (so we can recover well), but report that a type is needed // instead. if (isStartOfType() || !isStartOfExpression()) { node.constraint = parseType(); } else { // It was not a type, and it looked like an expression. Parse out an expression // here so we recover well. Note: it is important that we call parseUnaryExpression // and not parseExpression here. If the user has: // // // // We do *not* want to consume the > as we're consuming the expression for "". node.expression = parseUnaryExpressionOrHigher(); } } return finishNode(node); } function parseTypeParameters(): NodeArray { if (token === SyntaxKind.LessThanToken) { return parseBracketedList(ParsingContext.TypeParameters, parseTypeParameter, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken); } } function parseParameterType(): TypeNode { if (parseOptional(SyntaxKind.ColonToken)) { return token === SyntaxKind.StringLiteral ? parseLiteralNode(/*internName:*/ true) : parseType(); } return undefined; } function isStartOfParameter(): boolean { return token === SyntaxKind.DotDotDotToken || isIdentifierOrPattern() || isModifier(token) || token === SyntaxKind.AtToken; } function setModifiers(node: Node, modifiers: ModifiersArray) { if (modifiers) { node.flags |= modifiers.flags; node.modifiers = modifiers; } } function parseParameter(): ParameterDeclaration { let node = createNode(SyntaxKind.Parameter); node.decorators = parseDecorators(); setModifiers(node, parseModifiers()); node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); // SingleNameBinding[Yield,GeneratorParameter] : See 13.2.3 // [+GeneratorParameter]BindingIdentifier[Yield]Initializer[In]opt // [~GeneratorParameter]BindingIdentifier[?Yield]Initializer[In, ?Yield]opt node.name = inGeneratorParameterContext() ? doInYieldContext(parseIdentifierOrPattern) : parseIdentifierOrPattern(); if (getFullWidth(node.name) === 0 && node.flags === 0 && isModifier(token)) { // in cases like // 'use strict' // function foo(static) // isParameter('static') === true, because of isModifier('static') // however 'static' is not a legal identifier in a strict mode. // so result of this function will be ParameterDeclaration (flags = 0, name = missing, type = undefined, initializer = undefined) // and current token will not change => parsing of the enclosing parameter list will last till the end of time (or OOM) // to avoid this we'll advance cursor to the next token. nextToken(); } node.questionToken = parseOptionalToken(SyntaxKind.QuestionToken); node.type = parseParameterType(); node.initializer = inGeneratorParameterContext() ? doOutsideOfYieldContext(parseParameterInitializer) : parseParameterInitializer(); // Do not check for initializers in an ambient context for parameters. This is not // a grammar error because the grammar allows arbitrary call signatures in // an ambient context. // It is actually not necessary for this to be an error at all. The reason is that // function/constructor implementations are syntactically disallowed in ambient // contexts. In addition, parameter initializers are semantically disallowed in // overload signatures. So parameter initializers are transitively disallowed in // ambient contexts. return finishNode(node); } function parseParameterInitializer() { return parseInitializer(/*inParameter*/ true); } function fillSignature( returnToken: SyntaxKind, yieldAndGeneratorParameterContext: boolean, requireCompleteParameterList: boolean, signature: SignatureDeclaration): void { let returnTokenRequired = returnToken === SyntaxKind.EqualsGreaterThanToken; signature.typeParameters = parseTypeParameters(); signature.parameters = parseParameterList(yieldAndGeneratorParameterContext, requireCompleteParameterList); if (returnTokenRequired) { parseExpected(returnToken); signature.type = parseType(); } else if (parseOptional(returnToken)) { signature.type = parseType(); } } // Note: after careful analysis of the grammar, it does not appear to be possible to // have 'Yield' And 'GeneratorParameter' not in sync. i.e. any production calling // this FormalParameters production either always sets both to true, or always sets // both to false. As such we only have a single parameter to represent both. function parseParameterList(yieldAndGeneratorParameterContext: boolean, requireCompleteParameterList: boolean) { // FormalParameters[Yield,GeneratorParameter] : // ... // // FormalParameter[Yield,GeneratorParameter] : // BindingElement[?Yield, ?GeneratorParameter] // // BindingElement[Yield, GeneratorParameter ] : See 13.2.3 // SingleNameBinding[?Yield, ?GeneratorParameter] // [+GeneratorParameter]BindingPattern[?Yield, GeneratorParameter]Initializer[In]opt // [~GeneratorParameter]BindingPattern[?Yield]Initializer[In, ?Yield]opt // // SingleNameBinding[Yield, GeneratorParameter] : See 13.2.3 // [+GeneratorParameter]BindingIdentifier[Yield]Initializer[In]opt // [~GeneratorParameter]BindingIdentifier[?Yield]Initializer[In, ?Yield]opt if (parseExpected(SyntaxKind.OpenParenToken)) { let savedYieldContext = inYieldContext(); let savedGeneratorParameterContext = inGeneratorParameterContext(); setYieldContext(yieldAndGeneratorParameterContext); setGeneratorParameterContext(yieldAndGeneratorParameterContext); let result = parseDelimitedList(ParsingContext.Parameters, parseParameter); setYieldContext(savedYieldContext); setGeneratorParameterContext(savedGeneratorParameterContext); if (!parseExpected(SyntaxKind.CloseParenToken) && requireCompleteParameterList) { // Caller insisted that we had to end with a ) We didn't. So just return // undefined here. return undefined; } return result; } // We didn't even have an open paren. If the caller requires a complete parameter list, // we definitely can't provide that. However, if they're ok with an incomplete one, // then just return an empty set of parameters. return requireCompleteParameterList ? undefined : createMissingList(); } function parseTypeMemberSemicolon() { // We allow type members to be separated by commas or (possibly ASI) semicolons. // First check if it was a comma. If so, we're done with the member. if (parseOptional(SyntaxKind.CommaToken)) { return; } // Didn't have a comma. We must have a (possible ASI) semicolon. parseSemicolon(); } function parseSignatureMember(kind: SyntaxKind): SignatureDeclaration { let node = createNode(kind); if (kind === SyntaxKind.ConstructSignature) { parseExpected(SyntaxKind.NewKeyword); } fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ false, node); parseTypeMemberSemicolon(); return finishNode(node); } function isIndexSignature(): boolean { if (token !== SyntaxKind.OpenBracketToken) { return false; } return lookAhead(isUnambiguouslyIndexSignature); } function isUnambiguouslyIndexSignature() { // The only allowed sequence is: // // [id: // // However, for error recovery, we also check the following cases: // // [... // [id, // [id?, // [id?: // [id?] // [public id // [private id // [protected id // [] // nextToken(); if (token === SyntaxKind.DotDotDotToken || token === SyntaxKind.CloseBracketToken) { return true; } if (isModifier(token)) { nextToken(); if (isIdentifier()) { return true; } } else if (!isIdentifier()) { return false; } else { // Skip the identifier nextToken(); } // A colon signifies a well formed indexer // A comma should be a badly formed indexer because comma expressions are not allowed // in computed properties. if (token === SyntaxKind.ColonToken || token === SyntaxKind.CommaToken) { return true; } // Question mark could be an indexer with an optional property, // or it could be a conditional expression in a computed property. if (token !== SyntaxKind.QuestionToken) { return false; } // If any of the following tokens are after the question mark, it cannot // be a conditional expression, so treat it as an indexer. nextToken(); return token === SyntaxKind.ColonToken || token === SyntaxKind.CommaToken || token === SyntaxKind.CloseBracketToken; } function parseIndexSignatureDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): IndexSignatureDeclaration { let node = createNode(SyntaxKind.IndexSignature, fullStart); node.decorators = decorators; setModifiers(node, modifiers); node.parameters = parseBracketedList(ParsingContext.Parameters, parseParameter, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken); node.type = parseTypeAnnotation(); parseTypeMemberSemicolon(); return finishNode(node) } function parsePropertyOrMethodSignature(): Declaration { let fullStart = scanner.getStartPos(); let name = parsePropertyName(); let questionToken = parseOptionalToken(SyntaxKind.QuestionToken); if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) { let method = createNode(SyntaxKind.MethodSignature, fullStart); method.name = name; method.questionToken = questionToken; // Method signatues don't exist in expression contexts. So they have neither // [Yield] nor [GeneratorParameter] fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ false, method); parseTypeMemberSemicolon(); return finishNode(method); } else { let property = createNode(SyntaxKind.PropertySignature, fullStart); property.name = name; property.questionToken = questionToken; property.type = parseTypeAnnotation(); parseTypeMemberSemicolon(); return finishNode(property); } } function isStartOfTypeMember(): boolean { switch (token) { case SyntaxKind.OpenParenToken: case SyntaxKind.LessThanToken: case SyntaxKind.OpenBracketToken: // Both for indexers and computed properties return true; default: if (isModifier(token)) { let result = lookAhead(isStartOfIndexSignatureDeclaration); if (result) { return result; } } return isLiteralPropertyName() && lookAhead(isTypeMemberWithLiteralPropertyName); } } function isStartOfIndexSignatureDeclaration() { while (isModifier(token)) { nextToken(); } return isIndexSignature(); } function isTypeMemberWithLiteralPropertyName() { nextToken(); return token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken || token === SyntaxKind.QuestionToken || token === SyntaxKind.ColonToken || canParseSemicolon(); } function parseTypeMember(): Declaration { switch (token) { case SyntaxKind.OpenParenToken: case SyntaxKind.LessThanToken: return parseSignatureMember(SyntaxKind.CallSignature); case SyntaxKind.OpenBracketToken: // Indexer or computed property return isIndexSignature() ? parseIndexSignatureDeclaration(scanner.getStartPos(), /*decorators*/ undefined, /*modifiers:*/ undefined) : parsePropertyOrMethodSignature(); case SyntaxKind.NewKeyword: if (lookAhead(isStartOfConstructSignature)) { return parseSignatureMember(SyntaxKind.ConstructSignature); } // fall through. case SyntaxKind.StringLiteral: case SyntaxKind.NumericLiteral: return parsePropertyOrMethodSignature(); default: // Index declaration as allowed as a type member. But as per the grammar, // they also allow modifiers. So we have to check for an index declaration // that might be following modifiers. This ensures that things work properly // when incrementally parsing as the parser will produce the Index declaration // if it has the same text regardless of whether it is inside a class or an // object type. if (isModifier(token)) { let result = tryParse(parseIndexSignatureWithModifiers); if (result) { return result; } } if (isIdentifierOrKeyword()) { return parsePropertyOrMethodSignature(); } } } function parseIndexSignatureWithModifiers() { let fullStart = scanner.getStartPos(); let decorators = parseDecorators(); let modifiers = parseModifiers(); return isIndexSignature() ? parseIndexSignatureDeclaration(fullStart, decorators, modifiers) : undefined; } function isStartOfConstructSignature() { nextToken(); return token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken; } function parseTypeLiteral(): TypeLiteralNode { let node = createNode(SyntaxKind.TypeLiteral); node.members = parseObjectTypeMembers(); return finishNode(node); } function parseObjectTypeMembers(): NodeArray { let members: NodeArray; if (parseExpected(SyntaxKind.OpenBraceToken)) { members = parseList(ParsingContext.TypeMembers, /*checkForStrictMode*/ false, parseTypeMember); parseExpected(SyntaxKind.CloseBraceToken); } else { members = createMissingList(); } return members; } function parseTupleType(): TupleTypeNode { let node = createNode(SyntaxKind.TupleType); node.elementTypes = parseBracketedList(ParsingContext.TupleElementTypes, parseType, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken); return finishNode(node); } function parseParenthesizedType(): ParenthesizedTypeNode { let node = createNode(SyntaxKind.ParenthesizedType); parseExpected(SyntaxKind.OpenParenToken); node.type = parseType(); parseExpected(SyntaxKind.CloseParenToken); return finishNode(node); } function parseFunctionOrConstructorType(kind: SyntaxKind): FunctionOrConstructorTypeNode { let node = createNode(kind); if (kind === SyntaxKind.ConstructorType) { parseExpected(SyntaxKind.NewKeyword); } fillSignature(SyntaxKind.EqualsGreaterThanToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ false, node); return finishNode(node); } function parseKeywordAndNoDot(): TypeNode { let node = parseTokenNode(); return token === SyntaxKind.DotToken ? undefined : node; } function parseNonArrayType(): TypeNode { switch (token) { case SyntaxKind.AnyKeyword: case SyntaxKind.StringKeyword: case SyntaxKind.NumberKeyword: case SyntaxKind.BooleanKeyword: case SyntaxKind.SymbolKeyword: // If these are followed by a dot, then parse these out as a dotted type reference instead. let node = tryParse(parseKeywordAndNoDot); return node || parseTypeReference(); case SyntaxKind.VoidKeyword: return parseTokenNode(); case SyntaxKind.TypeOfKeyword: return parseTypeQuery(); case SyntaxKind.OpenBraceToken: return parseTypeLiteral(); case SyntaxKind.OpenBracketToken: return parseTupleType(); case SyntaxKind.OpenParenToken: return parseParenthesizedType(); default: return parseTypeReference(); } } function isStartOfType(): boolean { switch (token) { case SyntaxKind.AnyKeyword: case SyntaxKind.StringKeyword: case SyntaxKind.NumberKeyword: case SyntaxKind.BooleanKeyword: case SyntaxKind.SymbolKeyword: case SyntaxKind.VoidKeyword: case SyntaxKind.TypeOfKeyword: case SyntaxKind.OpenBraceToken: case SyntaxKind.OpenBracketToken: case SyntaxKind.LessThanToken: case SyntaxKind.NewKeyword: return true; case SyntaxKind.OpenParenToken: // Only consider '(' the start of a type if followed by ')', '...', an identifier, a modifier, // or something that starts a type. We don't want to consider things like '(1)' a type. return lookAhead(isStartOfParenthesizedOrFunctionType); default: return isIdentifier(); } } function isStartOfParenthesizedOrFunctionType() { nextToken(); return token === SyntaxKind.CloseParenToken || isStartOfParameter() || isStartOfType(); } function parseArrayTypeOrHigher(): TypeNode { let type = parseNonArrayType(); while (!scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) { parseExpected(SyntaxKind.CloseBracketToken); let node = createNode(SyntaxKind.ArrayType, type.pos); node.elementType = type; type = finishNode(node); } return type; } function parseUnionTypeOrHigher(): TypeNode { let type = parseArrayTypeOrHigher(); if (token === SyntaxKind.BarToken) { let types = >[type]; types.pos = type.pos; while (parseOptional(SyntaxKind.BarToken)) { types.push(parseArrayTypeOrHigher()); } types.end = getNodeEnd(); let node = createNode(SyntaxKind.UnionType, type.pos); node.types = types; type = finishNode(node); } return type; } function isStartOfFunctionType(): boolean { if (token === SyntaxKind.LessThanToken) { return true; } return token === SyntaxKind.OpenParenToken && lookAhead(isUnambiguouslyStartOfFunctionType); } function isUnambiguouslyStartOfFunctionType() { nextToken(); if (token === SyntaxKind.CloseParenToken || token === SyntaxKind.DotDotDotToken) { // ( ) // ( ... return true; } if (isIdentifier() || isModifier(token)) { nextToken(); if (token === SyntaxKind.ColonToken || token === SyntaxKind.CommaToken || token === SyntaxKind.QuestionToken || token === SyntaxKind.EqualsToken || isIdentifier() || isModifier(token)) { // ( id : // ( id , // ( id ? // ( id = // ( modifier id return true; } if (token === SyntaxKind.CloseParenToken) { nextToken(); if (token === SyntaxKind.EqualsGreaterThanToken) { // ( id ) => return true; } } } return false; } function parseType(): TypeNode { // The rules about 'yield' only apply to actual code/expression contexts. They don't // apply to 'type' contexts. So we disable these parameters here before moving on. let savedYieldContext = inYieldContext(); let savedGeneratorParameterContext = inGeneratorParameterContext(); setYieldContext(false); setGeneratorParameterContext(false); let result = parseTypeWorker(); setYieldContext(savedYieldContext); setGeneratorParameterContext(savedGeneratorParameterContext); return result; } function parseTypeWorker(): TypeNode { if (isStartOfFunctionType()) { return parseFunctionOrConstructorType(SyntaxKind.FunctionType); } if (token === SyntaxKind.NewKeyword) { return parseFunctionOrConstructorType(SyntaxKind.ConstructorType); } return parseUnionTypeOrHigher(); } function parseTypeAnnotation(): TypeNode { return parseOptional(SyntaxKind.ColonToken) ? parseType() : undefined; } // EXPRESSIONS function isStartOfLeftHandSideExpression(): boolean { switch (token) { case SyntaxKind.ThisKeyword: case SyntaxKind.SuperKeyword: case SyntaxKind.NullKeyword: case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: case SyntaxKind.NumericLiteral: case SyntaxKind.StringLiteral: case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.TemplateHead: case SyntaxKind.OpenParenToken: case SyntaxKind.OpenBracketToken: case SyntaxKind.OpenBraceToken: case SyntaxKind.FunctionKeyword: case SyntaxKind.ClassKeyword: case SyntaxKind.NewKeyword: case SyntaxKind.SlashToken: case SyntaxKind.SlashEqualsToken: case SyntaxKind.Identifier: return true; default: return isIdentifier(); } } function isStartOfExpression(): boolean { if (isStartOfLeftHandSideExpression()) { return true; } switch (token) { case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: case SyntaxKind.TildeToken: case SyntaxKind.ExclamationToken: case SyntaxKind.DeleteKeyword: case SyntaxKind.TypeOfKeyword: case SyntaxKind.VoidKeyword: case SyntaxKind.PlusPlusToken: case SyntaxKind.MinusMinusToken: case SyntaxKind.LessThanToken: case SyntaxKind.YieldKeyword: // Yield always starts an expression. Either it is an identifier (in which case // it is definitely an expression). Or it's a keyword (either because we're in // a generator, or in strict mode (or both)) and it started a yield expression. return true; default: // Error tolerance. If we see the start of some binary operator, we consider // that the start of an expression. That way we'll parse out a missing identifier, // give a good message about an identifier being missing, and then consume the // rest of the binary expression. if (isBinaryOperator()) { return true; } return isIdentifier(); } } function isStartOfExpressionStatement(): boolean { // As per the grammar, none of '{' or 'function' or 'class' can start an expression statement. return token !== SyntaxKind.OpenBraceToken && token !== SyntaxKind.FunctionKeyword && token !== SyntaxKind.ClassKeyword && token !== SyntaxKind.AtToken && isStartOfExpression(); } function parseExpression(): Expression { // Expression[in]: // AssignmentExpression[in] // Expression[in] , AssignmentExpression[in] // clear the decorator context when parsing Expression, as it should be unambiguous when parsing a decorator let saveDecoratorContext = inDecoratorContext(); if (saveDecoratorContext) { setDecoratorContext(false); } let expr = parseAssignmentExpressionOrHigher(); let operatorToken: Node; while ((operatorToken = parseOptionalToken(SyntaxKind.CommaToken))) { expr = makeBinaryExpression(expr, operatorToken, parseAssignmentExpressionOrHigher()); } if (saveDecoratorContext) { setDecoratorContext(true); } return expr; } function parseInitializer(inParameter: boolean): Expression { if (token !== SyntaxKind.EqualsToken) { // It's not uncommon during typing for the user to miss writing the '=' token. Check if // there is no newline after the last token and if we're on an expression. If so, parse // this as an equals-value clause with a missing equals. // NOTE: There are two places where we allow equals-value clauses. The first is in a // variable declarator. The second is with a parameter. For variable declarators // it's more likely that a { would be a allowed (as an object literal). While this // is also allowed for parameters, the risk is that we consume the { as an object // literal when it really will be for the block following the parameter. if (scanner.hasPrecedingLineBreak() || (inParameter && token === SyntaxKind.OpenBraceToken) || !isStartOfExpression()) { // preceding line break, open brace in a parameter (likely a function body) or current token is not an expression - // do not try to parse initializer return undefined; } } // Initializer[In, Yield] : // = AssignmentExpression[?In, ?Yield] parseExpected(SyntaxKind.EqualsToken); return parseAssignmentExpressionOrHigher(); } function parseAssignmentExpressionOrHigher(): Expression { // AssignmentExpression[in,yield]: // 1) ConditionalExpression[?in,?yield] // 2) LeftHandSideExpression = AssignmentExpression[?in,?yield] // 3) LeftHandSideExpression AssignmentOperator AssignmentExpression[?in,?yield] // 4) ArrowFunctionExpression[?in,?yield] // 5) [+Yield] YieldExpression[?In] // // Note: for ease of implementation we treat productions '2' and '3' as the same thing. // (i.e. they're both BinaryExpressions with an assignment operator in it). // First, do the simple check if we have a YieldExpression (production '5'). if (isYieldExpression()) { return parseYieldExpression(); } // Then, check if we have an arrow function (production '4') that starts with a parenthesized // parameter list. If we do, we must *not* recurse for productions 1, 2 or 3. An ArrowFunction is // not a LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done // with AssignmentExpression if we see one. let arrowExpression = tryParseParenthesizedArrowFunctionExpression(); if (arrowExpression) { return arrowExpression; } // Now try to see if we're in production '1', '2' or '3'. A conditional expression can // start with a LogicalOrExpression, while the assignment productions can only start with // LeftHandSideExpressions. // // So, first, we try to just parse out a BinaryExpression. If we get something that is a // LeftHandSide or higher, then we can try to parse out the assignment expression part. // Otherwise, we try to parse out the conditional expression bit. We want to allow any // binary expression here, so we pass in the 'lowest' precedence here so that it matches // and consumes anything. let expr = parseBinaryExpressionOrHigher(/*precedence:*/ 0); // To avoid a look-ahead, we did not handle the case of an arrow function with a single un-parenthesized // parameter ('x => ...') above. We handle it here by checking if the parsed expression was a single // identifier and the current token is an arrow. if (expr.kind === SyntaxKind.Identifier && token === SyntaxKind.EqualsGreaterThanToken) { return parseSimpleArrowFunctionExpression(expr); } // 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. // // Note: we call reScanGreaterToken so that we get an appropriately merged token // for cases like > > = becoming >>= if (isLeftHandSideExpression(expr) && isAssignmentOperator(reScanGreaterToken())) { return makeBinaryExpression(expr, parseTokenNode(), parseAssignmentExpressionOrHigher()); } // It wasn't an assignment or a lambda. This is a conditional expression: return parseConditionalExpressionRest(expr); } function isYieldExpression(): boolean { if (token === SyntaxKind.YieldKeyword) { // If we have a 'yield' keyword, and htis is a context where yield expressions are // allowed, then definitely parse out a yield expression. if (inYieldContext()) { return true; } if (inStrictModeContext()) { // If we're in strict mode, then 'yield' is a keyword, could only ever start // a yield expression. return true; } // We're in a context where 'yield expr' is not allowed. However, if we can // definitely tell that the user was trying to parse a 'yield expr' and not // just a normal expr that start with a 'yield' identifier, then parse out // a 'yield expr'. We can then report an error later that they are only // allowed in generator expressions. // // for example, if we see 'yield(foo)', then we'll have to treat that as an // invocation expression of something called 'yield'. However, if we have // 'yield foo' then that is not legal as a normal expression, so we can // definitely recognize this as a yield expression. // // for now we just check if the next token is an identifier. More heuristics // can be added here later as necessary. We just need to make sure that we // don't accidently consume something legal. return lookAhead(nextTokenIsIdentifierOnSameLine); } return false; } function nextTokenIsIdentifierOnSameLine() { nextToken(); return !scanner.hasPrecedingLineBreak() && isIdentifier() } function nextTokenIsIdentifierOrStartOfDestructuringOnTheSameLine() { nextToken(); return !scanner.hasPrecedingLineBreak() && (isIdentifier() || token === SyntaxKind.OpenBraceToken || token === SyntaxKind.OpenBracketToken); } function parseYieldExpression(): YieldExpression { let node = createNode(SyntaxKind.YieldExpression); // YieldExpression[In] : // yield // yield [no LineTerminator here] [Lexical goal InputElementRegExp]AssignmentExpression[?In, Yield] // yield [no LineTerminator here] * [Lexical goal InputElementRegExp]AssignmentExpression[?In, Yield] nextToken(); if (!scanner.hasPrecedingLineBreak() && (token === SyntaxKind.AsteriskToken || isStartOfExpression())) { node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); node.expression = parseAssignmentExpressionOrHigher(); return finishNode(node); } else { // if the next token is not on the same line as yield. or we don't have an '*' or // the start of an expressin, then this is just a simple "yield" expression. return finishNode(node); } } function parseSimpleArrowFunctionExpression(identifier: Identifier): Expression { Debug.assert(token === SyntaxKind.EqualsGreaterThanToken, "parseSimpleArrowFunctionExpression should only have been called if we had a =>"); let node = createNode(SyntaxKind.ArrowFunction, identifier.pos); let parameter = createNode(SyntaxKind.Parameter, identifier.pos); parameter.name = identifier; finishNode(parameter); node.parameters = >[parameter]; node.parameters.pos = parameter.pos; node.parameters.end = parameter.end; node.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken, false, Diagnostics._0_expected, "=>"); node.body = parseArrowFunctionExpressionBody(); return finishNode(node); } function tryParseParenthesizedArrowFunctionExpression(): Expression { let triState = isParenthesizedArrowFunctionExpression(); if (triState === Tristate.False) { // It's definitely not a parenthesized arrow function expression. return undefined; } // If we definitely have an arrow function, then we can just parse one, not requiring a // following => or { token. Otherwise, we *might* have an arrow function. Try to parse // it out, but don't allow any ambiguity, and return 'undefined' if this could be an // expression instead. let arrowFunction = triState === Tristate.True ? parseParenthesizedArrowFunctionExpressionHead(/*allowAmbiguity:*/ true) : tryParse(parsePossibleParenthesizedArrowFunctionExpressionHead); if (!arrowFunction) { // Didn't appear to actually be a parenthesized arrow function. Just bail out. return undefined; } // If we have an arrow, then try to parse the body. Even if not, try to parse if we // have an opening brace, just in case we're in an error state. var lastToken = token; arrowFunction.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken, /*reportAtCurrentPosition:*/false, Diagnostics._0_expected, "=>"); arrowFunction.body = (lastToken === SyntaxKind.EqualsGreaterThanToken || lastToken === SyntaxKind.OpenBraceToken) ? parseArrowFunctionExpressionBody() : parseIdentifier(); return finishNode(arrowFunction); } // True -> We definitely expect a parenthesized arrow function here. // False -> There *cannot* be a parenthesized arrow function here. // Unknown -> There *might* be a parenthesized arrow function here. // Speculatively look ahead to be sure, and rollback if not. function isParenthesizedArrowFunctionExpression(): Tristate { if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) { return lookAhead(isParenthesizedArrowFunctionExpressionWorker); } if (token === SyntaxKind.EqualsGreaterThanToken) { // ERROR RECOVERY TWEAK: // If we see a standalone => try to parse it as an arrow function expression as that's // likely what the user intended to write. return Tristate.True; } // Definitely not a parenthesized arrow function. return Tristate.False; } function isParenthesizedArrowFunctionExpressionWorker() { let first = token; let second = nextToken(); if (first === SyntaxKind.OpenParenToken) { if (second === SyntaxKind.CloseParenToken) { // Simple cases: "() =>", "(): ", and "() {". // This is an arrow function with no parameters. // The last one is not actually an arrow function, // but this is probably what the user intended. let third = nextToken(); switch (third) { case SyntaxKind.EqualsGreaterThanToken: case SyntaxKind.ColonToken: case SyntaxKind.OpenBraceToken: return Tristate.True; default: return Tristate.False; } } // If encounter "([" or "({", this could be the start of a binding pattern. // Examples: // ([ x ]) => { } // ({ x }) => { } // ([ x ]) // ({ x }) if (second === SyntaxKind.OpenBracketToken || second === SyntaxKind.OpenBraceToken) { return Tristate.Unknown; } // Simple case: "(..." // This is an arrow function with a rest parameter. if (second === SyntaxKind.DotDotDotToken) { return Tristate.True; } // If we had "(" followed by something that's not an identifier, // then this definitely doesn't look like a lambda. // Note: we could be a little more lenient and allow // "(public" or "(private". These would not ever actually be allowed, // but we could provide a good error message instead of bailing out. if (!isIdentifier()) { return Tristate.False; } // If we have something like "(a:", then we must have a // type-annotated parameter in an arrow function expression. if (nextToken() === SyntaxKind.ColonToken) { return Tristate.True; } // This *could* be a parenthesized arrow function. // Return Unknown to let the caller know. return Tristate.Unknown; } else { Debug.assert(first === SyntaxKind.LessThanToken); // If we have "<" not followed by an identifier, // then this definitely is not an arrow function. if (!isIdentifier()) { return Tristate.False; } // This *could* be a parenthesized arrow function. return Tristate.Unknown; } } function parsePossibleParenthesizedArrowFunctionExpressionHead(): ArrowFunction { return parseParenthesizedArrowFunctionExpressionHead(/*allowAmbiguity:*/ false); } function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction { let node = createNode(SyntaxKind.ArrowFunction); // Arrow functions are never generators. // // If we're speculatively parsing a signature for a parenthesized arrow function, then // we have to have a complete parameter list. Otherwise we might see something like // a => (b => c) // And think that "(b =>" was actually a parenthesized arrow function with a missing // close paren. fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ !allowAmbiguity, node); // If we couldn't get parameters, we definitely could not parse out an arrow function. if (!node.parameters) { return undefined; } // Parsing a signature isn't enough. // Parenthesized arrow signatures often look like other valid expressions. // For instance: // - "(x = 10)" is an assignment expression parsed as a signature with a default parameter value. // - "(x,y)" is a comma expression parsed as a signature with two parameters. // - "a ? (b): c" will have "(b):" parsed as a signature with a return type annotation. // // So we need just a bit of lookahead to ensure that it can only be a signature. if (!allowAmbiguity && token !== SyntaxKind.EqualsGreaterThanToken && token !== SyntaxKind.OpenBraceToken) { // Returning undefined here will cause our caller to rewind to where we started from. return undefined; } return node; } function parseArrowFunctionExpressionBody(): Block | Expression { if (token === SyntaxKind.OpenBraceToken) { return parseFunctionBlock(/*allowYield:*/ false, /* ignoreMissingOpenBrace */ false); } if (isStartOfStatement(/*inErrorRecovery:*/ true) && !isStartOfExpressionStatement() && token !== SyntaxKind.FunctionKeyword && token !== SyntaxKind.ClassKeyword) { // Check if we got a plain statement (i.e. no expression-statements, no function/class expressions/declarations) // // Here we try to recover from a potential error situation in the case where the // user meant to supply a block. For example, if the user wrote: // // a => // let v = 0; // } // // they may be missing an open brace. Check to see if that's the case so we can // try to recover better. If we don't do this, then the next close curly we see may end // up preemptively closing the containing construct. // // Note: even when 'ignoreMissingOpenBrace' is passed as true, parseBody will still error. return parseFunctionBlock(/*allowYield:*/ false, /* ignoreMissingOpenBrace */ true); } return parseAssignmentExpressionOrHigher(); } function parseConditionalExpressionRest(leftOperand: Expression): Expression { // Note: we are passed in an expression which was produced from parseBinaryExpressionOrHigher. let questionToken = parseOptionalToken(SyntaxKind.QuestionToken); if (!questionToken) { return leftOperand; } // Note: we explicitly 'allowIn' in the whenTrue part of the condition expression, and // we do not that for the 'whenFalse' part. let node = createNode(SyntaxKind.ConditionalExpression, leftOperand.pos); node.condition = leftOperand; node.questionToken = questionToken; node.whenTrue = doOutsideOfContext(disallowInAndDecoratorContext, parseAssignmentExpressionOrHigher); node.colonToken = parseExpectedToken(SyntaxKind.ColonToken, /*reportAtCurrentPosition:*/ false, Diagnostics._0_expected, tokenToString(SyntaxKind.ColonToken)); node.whenFalse = parseAssignmentExpressionOrHigher(); return finishNode(node); } function parseBinaryExpressionOrHigher(precedence: number): Expression { let leftOperand = parseUnaryExpressionOrHigher(); return parseBinaryExpressionRest(precedence, leftOperand); } function isInOrOfKeyword(t: SyntaxKind) { return t === SyntaxKind.InKeyword || t === SyntaxKind.OfKeyword; } function parseBinaryExpressionRest(precedence: number, leftOperand: Expression): Expression { while (true) { // We either have a binary operator here, or we're finished. We call // reScanGreaterToken so that we merge token sequences like > and = into >= reScanGreaterToken(); let newPrecedence = getBinaryOperatorPrecedence(); // Check the precedence to see if we should "take" this operator if (newPrecedence <= precedence) { break; } if (token === SyntaxKind.InKeyword && inDisallowInContext()) { break; } leftOperand = makeBinaryExpression(leftOperand, parseTokenNode(), parseBinaryExpressionOrHigher(newPrecedence)); } return leftOperand; } function isBinaryOperator() { if (inDisallowInContext() && token === SyntaxKind.InKeyword) { return false; } return getBinaryOperatorPrecedence() > 0; } function getBinaryOperatorPrecedence(): number { switch (token) { case SyntaxKind.BarBarToken: return 1; case SyntaxKind.AmpersandAmpersandToken: return 2; case SyntaxKind.BarToken: return 3; case SyntaxKind.CaretToken: return 4; case SyntaxKind.AmpersandToken: return 5; case SyntaxKind.EqualsEqualsToken: case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsEqualsToken: case SyntaxKind.ExclamationEqualsEqualsToken: return 6; case SyntaxKind.LessThanToken: case SyntaxKind.GreaterThanToken: case SyntaxKind.LessThanEqualsToken: case SyntaxKind.GreaterThanEqualsToken: case SyntaxKind.InstanceOfKeyword: case SyntaxKind.InKeyword: return 7; case SyntaxKind.LessThanLessThanToken: case SyntaxKind.GreaterThanGreaterThanToken: case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: return 8; case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: return 9; case SyntaxKind.AsteriskToken: case SyntaxKind.SlashToken: case SyntaxKind.PercentToken: return 10; } // -1 is lower than all other precedences. Returning it will cause binary expression // parsing to stop. return -1; } function makeBinaryExpression(left: Expression, operatorToken: Node, right: Expression): BinaryExpression { let node = createNode(SyntaxKind.BinaryExpression, left.pos); node.left = left; node.operatorToken = operatorToken; node.right = right; return finishNode(node); } function parsePrefixUnaryExpression() { let node = createNode(SyntaxKind.PrefixUnaryExpression); node.operator = token; nextToken(); node.operand = parseUnaryExpressionOrHigher(); return finishNode(node); } function parseDeleteExpression() { let node = createNode(SyntaxKind.DeleteExpression); nextToken(); node.expression = parseUnaryExpressionOrHigher(); return finishNode(node); } function parseTypeOfExpression() { let node = createNode(SyntaxKind.TypeOfExpression); nextToken(); node.expression = parseUnaryExpressionOrHigher(); return finishNode(node); } function parseVoidExpression() { let node = createNode(SyntaxKind.VoidExpression); nextToken(); node.expression = parseUnaryExpressionOrHigher(); return finishNode(node); } function parseUnaryExpressionOrHigher(): UnaryExpression { switch (token) { case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: case SyntaxKind.TildeToken: case SyntaxKind.ExclamationToken: case SyntaxKind.PlusPlusToken: case SyntaxKind.MinusMinusToken: return parsePrefixUnaryExpression(); case SyntaxKind.DeleteKeyword: return parseDeleteExpression(); case SyntaxKind.TypeOfKeyword: return parseTypeOfExpression(); case SyntaxKind.VoidKeyword: return parseVoidExpression(); case SyntaxKind.LessThanToken: return parseTypeAssertion(); default: return parsePostfixExpressionOrHigher(); } } function parsePostfixExpressionOrHigher(): PostfixExpression { let expression = parseLeftHandSideExpressionOrHigher(); Debug.assert(isLeftHandSideExpression(expression)); if ((token === SyntaxKind.PlusPlusToken || token === SyntaxKind.MinusMinusToken) && !scanner.hasPrecedingLineBreak()) { let node = createNode(SyntaxKind.PostfixUnaryExpression, expression.pos); node.operand = expression; node.operator = token; nextToken(); return finishNode(node); } return expression; } function parseLeftHandSideExpressionOrHigher(): LeftHandSideExpression { // Original Ecma: // LeftHandSideExpression: See 11.2 // NewExpression // CallExpression // // Our simplification: // // LeftHandSideExpression: See 11.2 // MemberExpression // CallExpression // // See comment in parseMemberExpressionOrHigher on how we replaced NewExpression with // MemberExpression to make our lives easier. // // to best understand the below code, it's important to see how CallExpression expands // out into its own productions: // // CallExpression: // MemberExpression Arguments // CallExpression Arguments // CallExpression[Expression] // CallExpression.IdentifierName // super ( ArgumentListopt ) // super.IdentifierName // // Because of the recursion in these calls, we need to bottom out first. There are two // bottom out states we can run into. Either we see 'super' which must start either of // the last two CallExpression productions. Or we have a MemberExpression which either // completes the LeftHandSideExpression, or starts the beginning of the first four // CallExpression productions. let expression = token === SyntaxKind.SuperKeyword ? parseSuperExpression() : parseMemberExpressionOrHigher(); // Now, we *may* be complete. However, we might have consumed the start of a // CallExpression. As such, we need to consume the rest of it here to be complete. return parseCallExpressionRest(expression); } function parseMemberExpressionOrHigher(): MemberExpression { // Note: to make our lives simpler, we decompose the the NewExpression productions and // place ObjectCreationExpression and FunctionExpression into PrimaryExpression. // like so: // // PrimaryExpression : See 11.1 // this // Identifier // Literal // ArrayLiteral // ObjectLiteral // (Expression) // FunctionExpression // new MemberExpression Arguments? // // MemberExpression : See 11.2 // PrimaryExpression // MemberExpression[Expression] // MemberExpression.IdentifierName // // CallExpression : See 11.2 // MemberExpression // CallExpression Arguments // CallExpression[Expression] // CallExpression.IdentifierName // // Technically this is ambiguous. i.e. CallExpression defines: // // CallExpression: // CallExpression Arguments // // If you see: "new Foo()" // // Then that could be treated as a single ObjectCreationExpression, or it could be // treated as the invocation of "new Foo". We disambiguate that in code (to match // the original grammar) by making sure that if we see an ObjectCreationExpression // we always consume arguments if they are there. So we treat "new Foo()" as an // object creation only, and not at all as an invocation) Another way to think // about this is that for every "new" that we see, we will consume an argument list if // it is there as part of the *associated* object creation node. Any additional // argument lists we see, will become invocation expressions. // // Because there are no other places in the grammar now that refer to FunctionExpression // or ObjectCreationExpression, it is safe to push down into the PrimaryExpression // production. // // Because CallExpression and MemberExpression are left recursive, we need to bottom out // of the recursion immediately. So we parse out a primary expression to start with. let expression = parsePrimaryExpression(); return parseMemberExpressionRest(expression); } function parseSuperExpression(): MemberExpression { let expression = parseTokenNode(); if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.DotToken) { return expression; } // If we have seen "super" it must be followed by '(' or '.'. // If it wasn't then just try to parse out a '.' and report an error. let node = createNode(SyntaxKind.PropertyAccessExpression, expression.pos); node.expression = expression; node.dotToken = parseExpectedToken(SyntaxKind.DotToken, /*reportAtCurrentPosition:*/ false, Diagnostics.super_must_be_followed_by_an_argument_list_or_member_access); node.name = parseRightSideOfDot(/*allowIdentifierNames:*/ true); return finishNode(node); } function parseTypeAssertion(): TypeAssertion { let node = createNode(SyntaxKind.TypeAssertionExpression); parseExpected(SyntaxKind.LessThanToken); node.type = parseType(); parseExpected(SyntaxKind.GreaterThanToken); node.expression = parseUnaryExpressionOrHigher(); return finishNode(node); } function parseMemberExpressionRest(expression: LeftHandSideExpression): MemberExpression { while (true) { let dotToken = parseOptionalToken(SyntaxKind.DotToken); if (dotToken) { let propertyAccess = createNode(SyntaxKind.PropertyAccessExpression, expression.pos); propertyAccess.expression = expression; propertyAccess.dotToken = dotToken; propertyAccess.name = parseRightSideOfDot(/*allowIdentifierNames:*/ true); expression = finishNode(propertyAccess); continue; } // when in the [Decorator] context, we do not parse ElementAccess as it could be part of a ComputedPropertyName if (!inDecoratorContext() && parseOptional(SyntaxKind.OpenBracketToken)) { let indexedAccess = createNode(SyntaxKind.ElementAccessExpression, expression.pos); indexedAccess.expression = expression; // It's not uncommon for a user to write: "new Type[]". // Check for that common pattern and report a better error message. if (token !== SyntaxKind.CloseBracketToken) { indexedAccess.argumentExpression = allowInAnd(parseExpression); if (indexedAccess.argumentExpression.kind === SyntaxKind.StringLiteral || indexedAccess.argumentExpression.kind === SyntaxKind.NumericLiteral) { let literal = indexedAccess.argumentExpression; literal.text = internIdentifier(literal.text); } } parseExpected(SyntaxKind.CloseBracketToken); expression = finishNode(indexedAccess); continue; } if (token === SyntaxKind.NoSubstitutionTemplateLiteral || token === SyntaxKind.TemplateHead) { let tagExpression = createNode(SyntaxKind.TaggedTemplateExpression, expression.pos); tagExpression.tag = expression; tagExpression.template = token === SyntaxKind.NoSubstitutionTemplateLiteral ? parseLiteralNode() : parseTemplateExpression(); expression = finishNode(tagExpression); continue; } return expression; } } function parseCallExpressionRest(expression: LeftHandSideExpression): LeftHandSideExpression { while (true) { expression = parseMemberExpressionRest(expression); if (token === SyntaxKind.LessThanToken) { // See if this is the start of a generic invocation. If so, consume it and // keep checking for postfix expressions. Otherwise, it's just a '<' that's // part of an arithmetic expression. Break out so we consume it higher in the // stack. let typeArguments = tryParse(parseTypeArgumentsInExpression); if (!typeArguments) { return expression; } let callExpr = createNode(SyntaxKind.CallExpression, expression.pos); callExpr.expression = expression; callExpr.typeArguments = typeArguments; callExpr.arguments = parseArgumentList(); expression = finishNode(callExpr); continue; } else if (token === SyntaxKind.OpenParenToken) { let callExpr = createNode(SyntaxKind.CallExpression, expression.pos); callExpr.expression = expression; callExpr.arguments = parseArgumentList(); expression = finishNode(callExpr); continue; } return expression; } } function parseArgumentList() { parseExpected(SyntaxKind.OpenParenToken); let result = parseDelimitedList(ParsingContext.ArgumentExpressions, parseArgumentExpression); parseExpected(SyntaxKind.CloseParenToken); return result; } function parseTypeArgumentsInExpression() { if (!parseOptional(SyntaxKind.LessThanToken)) { return undefined; } let typeArguments = parseDelimitedList(ParsingContext.TypeArguments, parseType); if (!parseExpected(SyntaxKind.GreaterThanToken)) { // If it doesn't have the closing > then it's definitely not an type argument list. return undefined; } // If we have a '<', then only parse this as a arugment list if the type arguments // are complete and we have an open paren. if we don't, rewind and return nothing. return typeArguments && canFollowTypeArgumentsInExpression() ? typeArguments : undefined; } function canFollowTypeArgumentsInExpression(): boolean { switch (token) { case SyntaxKind.OpenParenToken: // foo( // this case are the only case where this token can legally follow a type argument // list. So we definitely want to treat this as a type arg list. case SyntaxKind.DotToken: // foo. case SyntaxKind.CloseParenToken: // foo) case SyntaxKind.CloseBracketToken: // foo] case SyntaxKind.ColonToken: // foo: case SyntaxKind.SemicolonToken: // foo; case SyntaxKind.QuestionToken: // foo? case SyntaxKind.EqualsEqualsToken: // foo == case SyntaxKind.EqualsEqualsEqualsToken: // foo === case SyntaxKind.ExclamationEqualsToken: // foo != case SyntaxKind.ExclamationEqualsEqualsToken: // foo !== case SyntaxKind.AmpersandAmpersandToken: // foo && case SyntaxKind.BarBarToken: // foo || case SyntaxKind.CaretToken: // foo ^ case SyntaxKind.AmpersandToken: // foo & case SyntaxKind.BarToken: // foo | case SyntaxKind.CloseBraceToken: // foo } case SyntaxKind.EndOfFileToken: // foo