///
///
///
///
module ts {
var nodeConstructors = new Array Node>(SyntaxKind.Count);
export function getNodeConstructor(kind: SyntaxKind): new () => Node {
return nodeConstructors[kind] || (nodeConstructors[kind] = objectAllocator.getNodeConstructor(kind));
}
function createRootNode(kind: SyntaxKind, pos: number, end: number, flags: NodeFlags): Node {
var node = new (getNodeConstructor(kind))();
node.pos = pos;
node.end = end;
node.flags = flags;
return node;
}
var moduleExtensions = [".d.ts", ".ts", ".js"];
interface ReferenceComments {
referencedFiles: FileReference[];
amdDependencies: string[];
}
export function getModuleNameFromFilename(filename: string) {
for (var i = 0; i < moduleExtensions.length; i++) {
var ext = moduleExtensions[i];
var len = filename.length - ext.length;
if (len > 0 && filename.substr(len) === ext) return filename.substr(0, len);
}
return filename;
}
export function getSourceFileOfNode(node: Node): SourceFile {
while (node && node.kind !== SyntaxKind.SourceFile) node = node.parent;
return node;
}
// This is a useful function for debugging purposes.
export function nodePosToString(node: Node): string {
var file = getSourceFileOfNode(node);
var loc = file.getLineAndCharacterFromPosition(node.pos);
return file.filename + "(" + loc.line + "," + loc.character + ")";
}
export function getStartPosOfNode(node: Node): number {
return node.pos;
}
export function getTokenPosOfNode(node: Node): number {
return skipTrivia(getSourceFileOfNode(node).text, node.pos);
}
export function getSourceTextOfNodeFromSourceText(sourceText: string, node: Node): string {
return sourceText.substring(skipTrivia(sourceText, node.pos), node.end);
}
export function getSourceTextOfNode(node: Node): string {
var text = getSourceFileOfNode(node).text;
return text.substring(skipTrivia(text, node.pos), node.end);
}
// Add an extra underscore to identifiers that start with two underscores to avoid issues with magic names like '__proto__'
export function escapeIdentifier(identifier: string): string {
return identifier.length >= 2 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ ? "_" + identifier : identifier;
}
// Remove extra underscore from escaped identifier
export function unescapeIdentifier(identifier: string): string {
return identifier.length >= 3 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ && identifier.charCodeAt(2) === CharacterCodes._ ? identifier.substr(1) : identifier;
}
// Return display name of an identifier
export function identifierToString(identifier: Identifier) {
return identifier.kind === SyntaxKind.Missing ? "(Missing)" : getSourceTextOfNode(identifier);
}
export function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): Diagnostic {
node = getErrorSpanForNode(node);
var file = getSourceFileOfNode(node);
var start = skipTrivia(file.text, node.pos);
var length = node.end - start;
return createFileDiagnostic(file, start, length, message, arg0, arg1, arg2);
}
export function createDiagnosticForNodeFromMessageChain(node: Node, messageChain: DiagnosticMessageChain): Diagnostic {
node = getErrorSpanForNode(node);
var file = getSourceFileOfNode(node);
var start = skipTrivia(file.text, node.pos);
var length = node.end - start;
return flattenDiagnosticChain(file, start, length, messageChain);
}
export function getErrorSpanForNode(node: Node): Node {
var errorSpan: Node;
switch (node.kind) {
// This list is a work in progress. Add missing node kinds to improve their error
// spans.
case SyntaxKind.VariableDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.EnumMember:
errorSpan = (node).name;
break;
}
// We now have the ideal error span, but it may be a node that is optional and absent
// (e.g. the name of a function expression), in which case errorSpan will be undefined.
// Alternatively, it might be required and missing (e.g. the name of a module), in which
// case its pos will equal its end (length 0). In either of these cases, we should fall
// back to the original node that the error was issued on.
return errorSpan && errorSpan.pos < errorSpan.end ? errorSpan : node;
}
export function isExternalModule(file: SourceFile): boolean {
return file.externalModuleIndicator !== undefined;
}
export function isPrologueDirective(node: Node): boolean {
return node.kind === SyntaxKind.ExpressionStatement && (node).expression.kind === SyntaxKind.StringLiteral;
}
function isEvalOrArgumentsIdentifier(node: Node): boolean {
return node.kind === SyntaxKind.Identifier &&
(node).text &&
((node).text === "eval" || (node).text === "arguments")
}
/// Should be called only on prologue directives (isPrologueDirective(node) should be true)
function isUseStrictPrologueDirective(node: Node): boolean {
Debug.assert(isPrologueDirective(node));
return ((node).expression).text === "use strict";
}
// 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, cbNodes?: (nodes: Node[]) => T): T {
function child(node: Node): T {
if (node) return cbNode(node);
}
function children(nodes: Node[]) {
if (nodes) {
if (cbNodes) return cbNodes(nodes);
var result: T;
for (var i = 0, len = nodes.length; i < len; i++) {
if (result = cbNode(nodes[i])) break;
}
return result;
}
}
if (!node) return;
switch (node.kind) {
case SyntaxKind.QualifiedName:
return child((node).left) ||
child((node).right);
case SyntaxKind.TypeParameter:
return child((node).name) ||
child((node).constraint);
case SyntaxKind.Parameter:
return child((node).name) ||
child((node).type) ||
child((node).initializer);
case SyntaxKind.Property:
case SyntaxKind.PropertyAssignment:
return child((node).name) ||
child((node).type) ||
child((node).initializer);
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
return children((node).typeParameters) ||
children((node).parameters) ||
child((node).type);
case SyntaxKind.Method:
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ArrowFunction:
return child((node).name) ||
children((node).typeParameters) ||
children((node).parameters) ||
child((node).type) ||
child((node).body);
case SyntaxKind.TypeReference:
return child((node).typeName) ||
children((node).typeArguments);
case SyntaxKind.TypeQuery:
return child((node).exprName);
case SyntaxKind.TypeLiteral:
return children((node).members);
case SyntaxKind.ArrayType:
return child((node).elementType);
case SyntaxKind.ArrayLiteral:
return children((node).elements);
case SyntaxKind.ObjectLiteral:
return children((node).properties);
case SyntaxKind.PropertyAccess:
return child((node).left) ||
child((node).right);
case SyntaxKind.IndexedAccess:
return child((node).object) ||
child((node).index);
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return child((node).func) ||
children((node).typeArguments) ||
children((node).arguments);
case SyntaxKind.TypeAssertion:
return child((node).type) ||
child((node).operand);
case SyntaxKind.ParenExpression:
return child((node).expression);
case SyntaxKind.PrefixOperator:
case SyntaxKind.PostfixOperator:
return child((node).operand);
case SyntaxKind.BinaryExpression:
return child((node).left) ||
child((node).right);
case SyntaxKind.ConditionalExpression:
return child((node).condition) ||
child((node).whenTrue) ||
child((node).whenFalse);
case SyntaxKind.Block:
case SyntaxKind.TryBlock:
case SyntaxKind.FinallyBlock:
case SyntaxKind.FunctionBlock:
case SyntaxKind.ModuleBlock:
case SyntaxKind.SourceFile:
return children((node).statements);
case SyntaxKind.VariableStatement:
return children((node).declarations);
case SyntaxKind.ExpressionStatement:
return child((node).expression);
case SyntaxKind.IfStatement:
return child((node).expression) ||
child((node).thenStatement) ||
child((node).elseStatement);
case SyntaxKind.DoStatement:
return child((node).statement) ||
child((node).expression);
case SyntaxKind.WhileStatement:
return child((node).expression) ||
child((node).statement);
case SyntaxKind.ForStatement:
return children((node).declarations) ||
child((node).initializer) ||
child((node).condition) ||
child((node).iterator) ||
child((node).statement);
case SyntaxKind.ForInStatement:
return child((node).declaration) ||
child((node).variable) ||
child((node).expression) ||
child((node).statement);
case SyntaxKind.ContinueStatement:
case SyntaxKind.BreakStatement:
return child((node).label);
case SyntaxKind.ReturnStatement:
return child((node).expression);
case SyntaxKind.WithStatement:
return child((node).expression) ||
child((node).statement);
case SyntaxKind.SwitchStatement:
return child((node).expression) ||
children((node).clauses);
case SyntaxKind.CaseClause:
case SyntaxKind.DefaultClause:
return child((node).expression) ||
children((node).statements);
case SyntaxKind.LabelledStatement:
return child((node).label) ||
child((node).statement);
case SyntaxKind.ThrowStatement:
return child((node).expression);
case SyntaxKind.TryStatement:
return child((node).tryBlock) ||
child((node).catchBlock) ||
child((node).finallyBlock);
case SyntaxKind.CatchBlock:
return child((node).variable) ||
children((node).statements);
case SyntaxKind.VariableDeclaration:
return child((node).name) ||
child((node).type) ||
child((node).initializer);
case SyntaxKind.ClassDeclaration:
return child((node).name) ||
children((node).typeParameters) ||
child((node).baseType) ||
children((node).implementedTypes) ||
children((node).members);
case SyntaxKind.InterfaceDeclaration:
return child((node).name) ||
children((node).typeParameters) ||
children((node).baseTypes) ||
children((node).members);
case SyntaxKind.EnumDeclaration:
return child((node).name) ||
children((node).members);
case SyntaxKind.EnumMember:
return child((node).name) ||
child((node).initializer);
case SyntaxKind.ModuleDeclaration:
return child((node).name) ||
child((node).body);
case SyntaxKind.ImportDeclaration:
return child((node).name) ||
child((node).entityName) ||
child((node).externalModuleName);
case SyntaxKind.ExportAssignment:
return child((node).exportName);
}
}
export function hasRestParameters(s: SignatureDeclaration): boolean {
return s.parameters.length > 0 && (s.parameters[s.parameters.length - 1].flags & NodeFlags.Rest) !== 0;
}
enum ParsingContext {
SourceElements, // Elements in source file
ModuleElements, // Elements in module declaration
BlockStatements, // Statements in block
SwitchClauses, // Clauses in switch statement
SwitchClauseStatements, // Statements in switch clause
TypeMembers, // Members in interface or type literal
ClassMembers, // Members in class declaration
EnumMembers, // Members in enum declaration
BaseTypeReferences, // Type references in extends or implements clause
VariableDeclarations, // Variable declarations in variable statement
ArgumentExpressions, // Expressions in argument list
ObjectLiteralMembers, // Members in object literal
ArrayLiteralMembers, // Members in array literal
Parameters, // Parameters in parameter list
TypeParameters, // Type parameters in type parameter list
TypeArguments, // Type arguments in type argument list
Count // Number of parsing contexts
}
enum Tristate {
False,
True,
Unknown
}
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.BaseTypeReferences: return Diagnostics.Type_reference_expected;
case ParsingContext.VariableDeclarations: return Diagnostics.Variable_declaration_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;
}
};
enum LookAheadMode {
NotLookingAhead,
NoErrorYet,
Error
}
enum ModifierContext {
SourceElements, // Top level elements in a source file
ModuleElements, // Elements in module declaration
ClassMembers, // Members in class declaration
Parameters, // Parameters in parameter list
}
enum TrailingCommaBehavior {
Disallow,
Allow,
Preserve
}
// Tracks whether we nested (directly or indirectly) in a certain control block.
// Used for validating break and continue statements.
enum ControlBlockContext {
NotNested,
Nested,
CrossingFunctionBoundary
}
interface LabelledStatementInfo {
addLabel(label: Identifier): void;
pushCurrentLabelSet(isIterationStatement: boolean): void;
pushFunctionBoundary(): void;
pop(): void;
nodeIsNestedInLabel(label: Identifier, requireIterationStatement: boolean, stopAtFunctionBoundary: boolean): ControlBlockContext;
}
export function createSourceFile(filename: string, sourceText: string, languageVersion: ScriptTarget): SourceFile {
var file: SourceFile;
var scanner: Scanner;
var token: SyntaxKind;
var parsingContext: ParsingContext;
var commentRanges: TextRange[];
var identifiers: Map = {};
var identifierCount = 0;
var nodeCount = 0;
var lineStarts: number[];
var isInStrictMode = false;
var lookAheadMode = LookAheadMode.NotLookingAhead;
var inAmbientContext = false;
var inFunctionBody = false;
var inSwitchStatement = ControlBlockContext.NotNested;
var inIterationStatement = ControlBlockContext.NotNested;
// The following is a state machine that tracks what labels are in our current parsing
// context. So if we are parsing a node that is nested (arbitrarily deeply) in a label,
// it will be tracked in this data structure. It is used for checking break/continue
// statements, and checking for duplicate labels.
var labelledStatementInfo: LabelledStatementInfo = (() => {
// These are initialized on demand because labels are rare, so it is usually
// not even necessary to allocate these.
var functionBoundarySentinel: StringSet;
var currentLabelSet: StringSet;
var labelSetStack: StringSet[];
var isIterationStack: boolean[];
function addLabel(label: Identifier): void {
if (!currentLabelSet) {
currentLabelSet = {};
}
currentLabelSet[label.text] = true;
}
function pushCurrentLabelSet(isIterationStatement: boolean): void {
if (!labelSetStack && !isIterationStack) {
labelSetStack = [];
isIterationStack = [];
}
Debug.assert(currentLabelSet !== undefined);
labelSetStack.push(currentLabelSet);
isIterationStack.push(isIterationStatement);
currentLabelSet = undefined;
}
function pushFunctionBoundary(): void {
if (!functionBoundarySentinel) {
functionBoundarySentinel = {};
if (!labelSetStack && !isIterationStack) {
labelSetStack = [];
isIterationStack = [];
}
}
Debug.assert(currentLabelSet === undefined);
labelSetStack.push(functionBoundarySentinel);
// It does not matter what we push here, since we will never ask if a function boundary
// is an iteration statement
isIterationStack.push(false);
}
function pop(): void {
// Assert that we are in a "pushed" state
Debug.assert(labelSetStack.length && isIterationStack.length && currentLabelSet === undefined);
labelSetStack.pop();
isIterationStack.pop();
}
function nodeIsNestedInLabel(label: Identifier, requireIterationStatement: boolean, stopAtFunctionBoundary: boolean): ControlBlockContext {
if (!requireIterationStatement && currentLabelSet && hasProperty(currentLabelSet, label.text)) {
return ControlBlockContext.Nested;
}
if (!labelSetStack) {
return ControlBlockContext.NotNested;
}
// We want to start searching for the label at the lowest point in the tree,
// and climb up from there. So we start at the end of the labelSetStack array.
var crossedFunctionBoundary = false;
for (var i = labelSetStack.length - 1; i >= 0; i--) {
var labelSet = labelSetStack[i];
// Not allowed to cross function boundaries, so stop if we encounter one
if (labelSet === functionBoundarySentinel) {
if (stopAtFunctionBoundary) {
break;
}
else {
crossedFunctionBoundary = true;
continue;
}
}
// If we require an iteration statement, only search in the current
// statement if it is an iteration statement
if (requireIterationStatement && isIterationStack[i] === false) {
continue;
}
if (hasProperty(labelSet, label.text)) {
return crossedFunctionBoundary ? ControlBlockContext.CrossingFunctionBoundary : ControlBlockContext.Nested;
}
}
// This is a bit of a misnomer. If the caller passed true for stopAtFunctionBoundary,
// there actually may be an enclosing label across a function boundary, but we will
// just return NotNested
return ControlBlockContext.NotNested;
}
return {
addLabel: addLabel,
pushCurrentLabelSet: pushCurrentLabelSet,
pushFunctionBoundary: pushFunctionBoundary,
pop: pop,
nodeIsNestedInLabel: nodeIsNestedInLabel,
};
})();
function getLineAndCharacterlFromSourcePosition(position: number) {
if (!lineStarts) {
lineStarts = getLineStarts(sourceText);
}
return getLineAndCharacterOfPosition(lineStarts, position);
}
function error(message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void {
var start = scanner.getTokenPos();
var length = scanner.getTextPos() - start;
errorAtPos(start, length, message, arg0, arg1, arg2);
}
// This is just like createDiagnosticForNode except that it uses the current file
// being parsed instead of the file containing the node. This is because during
// parse, the nodes do not have parent pointers to get to the file.
//
// It is very intentional that we are not checking or changing the lookAheadMode value
// here. 'grammarErrorOnNode' is called when we are doing extra grammar checks and not
// when we are doing the actual parsing to determine what the user wrote. In other
// words, this function is called once we have already parsed the node, and are just
// applying some stricter checks on that node.
function grammarErrorOnNode(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void {
var span = getErrorSpanForNode(node);
var start = skipTrivia(file.text, span.pos);
var length = span.end - start;
file.syntacticErrors.push(createFileDiagnostic(file, start, length, message, arg0, arg1, arg2));
}
function reportInvalidUseInStrictMode(node: Identifier): void {
// identifierToString cannot be used here since it uses backreference to 'parent' that is not yet set
var name = sourceText.substring(skipTrivia(sourceText, node.pos), node.end);
grammarErrorOnNode(node, Diagnostics.Invalid_use_of_0_in_strict_mode, name);
}
function grammarErrorAtPos(start: number, length: number, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void {
file.syntacticErrors.push(createFileDiagnostic(file, start, length, message, arg0, arg1, arg2));
}
function errorAtPos(start: number, length: number, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void {
var lastErrorPos = file.syntacticErrors.length
? file.syntacticErrors[file.syntacticErrors.length - 1].start
: -1;
if (start !== lastErrorPos) {
file.syntacticErrors.push(createFileDiagnostic(file, start, length, message, arg0, arg1, arg2));
}
if (lookAheadMode === LookAheadMode.NoErrorYet) {
lookAheadMode = LookAheadMode.Error;
}
}
function scanError(message: DiagnosticMessage) {
var pos = scanner.getTextPos();
errorAtPos(pos, 0, message);
}
function onComment(pos: number, end: number) {
if (commentRanges) commentRanges.push({ pos: pos, end: end });
}
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 lookAheadHelper(callback: () => T, alwaysResetState: boolean): T {
// Keep track of the state we'll need to rollback to if lookahead fails (or if the
// caller asked us to always reset our state).
var saveToken = token;
var saveSyntacticErrorsLength = file.syntacticErrors.length;
// Keep track of the current look ahead mode (this matters if we have nested
// speculative parsing).
var saveLookAheadMode = lookAheadMode;
// Mark that we're in speculative parsing and then try to parse out whatever code
// the callback wants.
lookAheadMode = LookAheadMode.NoErrorYet;
var result = callback();
// If we switched from 1 to to -1 then a parse error occurred during the callback.
// If that's the case, then we want to act as if we never got any result at all.
Debug.assert(lookAheadMode === LookAheadMode.Error || lookAheadMode === LookAheadMode.NoErrorYet);
if (lookAheadMode === LookAheadMode.Error) {
result = undefined;
}
// Now restore as appropriate.
lookAheadMode = saveLookAheadMode;
if (!result || alwaysResetState) {
token = saveToken;
file.syntacticErrors.length = saveSyntacticErrorsLength;
}
return result;
}
function lookAhead(callback: () => T): T {
var result: T;
scanner.tryScan(() => {
result = lookAheadHelper(callback, /*alwaysResetState:*/ true);
// Returning false here indicates to the scanner that it should always jump
// back to where it started. This makes sense as 'lookahead' acts as if
// neither the parser nor scanner was affected by the operation.
//
// Note: the rewinding of the parser state is already handled in lookAheadHelper
// (because we passed 'true' for alwaysResetState).
return false;
});
return result;
}
function tryParse(callback: () => T): T {
return scanner.tryScan(() => lookAheadHelper(callback, /*alwaysResetState:*/ false));
}
function isIdentifier(): boolean {
return token === SyntaxKind.Identifier || (isInStrictMode ? token > SyntaxKind.LastFutureReservedWord : token > SyntaxKind.LastReservedWord);
}
function parseExpected(t: SyntaxKind): boolean {
if (token === t) {
nextToken();
return true;
}
error(Diagnostics._0_expected, tokenToString(t));
return false;
}
function parseOptional(t: SyntaxKind): boolean {
if (token === t) {
nextToken();
return true;
}
return false;
}
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(): void {
if (canParseSemicolon()) {
if (token === SyntaxKind.SemicolonToken) {
// consume the semicolon if it was explicitly provided.
nextToken();
}
}
else {
error(Diagnostics._0_expected, ";");
}
}
function createNode(kind: SyntaxKind, pos?: number): Node {
nodeCount++;
var 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();
return node;
}
function createMissingNode(): Node {
return createNode(SyntaxKind.Missing);
}
// 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): Identifier {
identifierCount++;
if (isIdentifier) {
var node = createNode(SyntaxKind.Identifier);
var text = escapeIdentifier(scanner.getTokenValue());
node.text = hasProperty(identifiers, text) ? identifiers[text] : (identifiers[text] = text);
nextToken();
return finishNode(node);
}
error(Diagnostics.Identifier_expected);
return createMissingNode();
}
function parseIdentifier(): Identifier {
return createIdentifier(isIdentifier());
}
function parseIdentifierName(): Identifier {
return createIdentifier(token >= SyntaxKind.Identifier);
}
function isPropertyName(): boolean {
return token >= SyntaxKind.Identifier || token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral;
}
function parsePropertyName(): Identifier {
if (token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral) {
return parsePrimaryExpression();
}
return parseIdentifierName();
}
function isKeyword(token: SyntaxKind): boolean {
return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword;
}
function isModifier(token: SyntaxKind): boolean {
switch (token) {
case SyntaxKind.PublicKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.StaticKeyword:
case SyntaxKind.ExportKeyword:
case SyntaxKind.DeclareKeyword:
return true;
}
return false;
}
function parseContextualModifier(t: SyntaxKind): boolean {
return token === t && tryParse(() => {
nextToken();
return token === SyntaxKind.OpenBracketToken || isPropertyName();
});
}
function parseAnyContextualModifier(): boolean {
return isModifier(token) && tryParse(() => {
nextToken();
return token === SyntaxKind.OpenBracketToken || isPropertyName();
});
}
// True if positioned at the start of a list element
function isListElement(kind: ParsingContext, inErrorRecovery: boolean): boolean {
switch (kind) {
case ParsingContext.SourceElements:
case ParsingContext.ModuleElements:
return isSourceElement(inErrorRecovery);
case ParsingContext.BlockStatements:
case ParsingContext.SwitchClauseStatements:
return isStatement(inErrorRecovery);
case ParsingContext.SwitchClauses:
return token === SyntaxKind.CaseKeyword || token === SyntaxKind.DefaultKeyword;
case ParsingContext.TypeMembers:
return isTypeMember();
case ParsingContext.ClassMembers:
return lookAhead(isClassMemberStart);
case ParsingContext.EnumMembers:
case ParsingContext.ObjectLiteralMembers:
return isPropertyName();
case ParsingContext.BaseTypeReferences:
return isIdentifier() && ((token !== SyntaxKind.ExtendsKeyword && token !== SyntaxKind.ImplementsKeyword) || !lookAhead(() => (nextToken(), isIdentifier())));
case ParsingContext.VariableDeclarations:
case ParsingContext.TypeParameters:
return isIdentifier();
case ParsingContext.ArgumentExpressions:
return isExpression();
case ParsingContext.ArrayLiteralMembers:
return token === SyntaxKind.CommaToken || isExpression();
case ParsingContext.Parameters:
return isParameter();
case ParsingContext.TypeArguments:
return isType();
}
Debug.fail("Non-exhaustive case in 'isListElement'.");
}
// 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:
return token === SyntaxKind.CloseBraceToken;
case ParsingContext.SwitchClauseStatements:
return token === SyntaxKind.CloseBraceToken || token === SyntaxKind.CaseKeyword || token === SyntaxKind.DefaultKeyword;
case ParsingContext.BaseTypeReferences:
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:
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;
}
}
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.
if (token === SyntaxKind.InKeyword) {
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 (var 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 {
var saveParsingContext = parsingContext;
parsingContext |= 1 << kind;
var result = >[];
result.pos = getNodePos();
var saveIsInStrictMode = isInStrictMode;
while (!isListTerminator(kind)) {
if (isListElement(kind, /* inErrorRecovery */ false)) {
var element = parseElement();
result.push(element);
// test elements only if we are not already in strict mode
if (!isInStrictMode && checkForStrictMode) {
if (isPrologueDirective(element)) {
if (isUseStrictPrologueDirective(element)) {
isInStrictMode = true;
checkForStrictMode = false;
}
}
else {
checkForStrictMode = false;
}
}
}
else {
error(parsingContextErrors(kind));
if (isInSomeParsingContext()) {
break;
}
nextToken();
}
}
isInStrictMode = saveIsInStrictMode;
result.end = getNodeEnd();
parsingContext = saveParsingContext;
return result;
}
// Parses a comma-delimited list of elements
function parseDelimitedList(kind: ParsingContext, parseElement: () => T, trailingCommaBehavior: TrailingCommaBehavior): NodeArray {
var saveParsingContext = parsingContext;
parsingContext |= 1 << kind;
var result = >[];
result.pos = getNodePos();
// Keep track of how many errors we had before the list started. If we don't see any new
// errors resulting from the list being malformed, we are free to complain about a trailing comma.
var errorCountBeforeParsingList = file.syntacticErrors.length;
var commaStart = -1; // Meaning the previous token was not a comma
while (true) {
if (isListElement(kind, /* inErrorRecovery */ false)) {
result.push(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;
}
error(Diagnostics._0_expected, ",");
}
else if (isListTerminator(kind)) {
// Check if the last token was a comma.
if (commaStart >= 0) {
if (trailingCommaBehavior === TrailingCommaBehavior.Disallow) {
if (file.syntacticErrors.length === errorCountBeforeParsingList) {
// Report a grammar error so we don't affect lookahead
grammarErrorAtPos(commaStart, scanner.getStartPos() - commaStart, Diagnostics.Trailing_comma_not_allowed);
}
}
else if (trailingCommaBehavior === TrailingCommaBehavior.Preserve) {
result.push(createNode(SyntaxKind.OmittedExpression));
}
}
break;
}
else {
error(parsingContextErrors(kind));
if (isInSomeParsingContext()) {
break;
}
nextToken();
}
}
result.end = getNodeEnd();
parsingContext = saveParsingContext;
return result;
}
function createMissingList(): NodeArray {
var pos = getNodePos();
var result = >[];
result.pos = pos;
result.end = pos;
return result;
}
function createNodeArray(node: T): NodeArray {
var result = >[node];
result.pos = node.pos;
result.end = node.end;
return result;
}
function parseBracketedList(kind: ParsingContext, parseElement: () => T, startToken: SyntaxKind, endToken: SyntaxKind): NodeArray {
if (parseExpected(startToken)) {
var result = parseDelimitedList(kind, parseElement, TrailingCommaBehavior.Disallow);
parseExpected(endToken);
return result;
}
return createMissingList();
}
// The allowReservedWords parameter controls whether reserved words are permitted after the first dot
function parseEntityName(allowReservedWords: boolean): EntityName {
var entity: EntityName = parseIdentifier();
while (parseOptional(SyntaxKind.DotToken)) {
var node = createNode(SyntaxKind.QualifiedName, entity.pos);
node.left = entity;
node.right = allowReservedWords ? parseIdentifierName() : parseIdentifier();
entity = finishNode(node);
}
return entity;
}
function parseTokenNode(): Node {
var node = createNode(token);
nextToken();
return finishNode(node);
}
function parseLiteralNode(): LiteralExpression {
var node = createNode(token);
node.text = scanner.getTokenValue();
nextToken();
return finishNode(node);
}
function parseStringLiteral(): LiteralExpression {
if (token === SyntaxKind.StringLiteral) return parseLiteralNode();
error(Diagnostics.String_literal_expected);
return createMissingNode();
}
// TYPES
function parseTypeReference(): TypeReferenceNode {
var node = createNode(SyntaxKind.TypeReference);
node.typeName = parseEntityName(/*allowReservedWords*/ false);
if (!scanner.hasPrecedingLineBreak() && token === SyntaxKind.LessThanToken) {
node.typeArguments = parseTypeArguments();
}
return finishNode(node);
}
function parseTypeQuery(): TypeQueryNode {
var node = createNode(SyntaxKind.TypeQuery);
parseExpected(SyntaxKind.TypeOfKeyword);
node.exprName = parseEntityName(/*allowReservedWords*/ true);
return finishNode(node);
}
function parseTypeParameter(): TypeParameterDeclaration {
var 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 (isType() || !isExpression()) {
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 "".
var expr = parseUnaryExpression();
grammarErrorOnNode(expr, Diagnostics.Type_expected);
}
}
return finishNode(node);
}
function parseTypeParameters(): NodeArray {
if (token === SyntaxKind.LessThanToken) {
var pos = getNodePos();
var result = parseBracketedList(ParsingContext.TypeParameters, parseTypeParameter, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken);
if (!result.length) {
var start = getTokenPos(pos);
var length = getNodePos() - start;
errorAtPos(start, length, Diagnostics.Type_parameter_list_cannot_be_empty);
}
return result;
}
}
function parseParameterType(): TypeNode {
return parseOptional(SyntaxKind.ColonToken) ? token === SyntaxKind.StringLiteral ? parseStringLiteral() : parseType() : undefined;
}
function isParameter(): boolean {
return token === SyntaxKind.DotDotDotToken || isIdentifier() || isModifier(token);
}
function parseParameter(flags: NodeFlags = 0): ParameterDeclaration {
var node = createNode(SyntaxKind.Parameter);
node.flags |= parseAndCheckModifiers(ModifierContext.Parameters);
if (parseOptional(SyntaxKind.DotDotDotToken)) {
node.flags |= NodeFlags.Rest;
}
node.name = parseIdentifier();
if (node.name.kind === SyntaxKind.Missing && 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();
}
if (parseOptional(SyntaxKind.QuestionToken)) {
node.flags |= NodeFlags.QuestionMark;
}
node.type = parseParameterType();
node.initializer = parseInitializer(/*inParameter*/ true);
// 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 parseSignature(kind: SyntaxKind, returnToken: SyntaxKind): ParsedSignature {
if (kind === SyntaxKind.ConstructSignature) {
parseExpected(SyntaxKind.NewKeyword);
}
var typeParameters = parseTypeParameters();
var parameters = parseParameterList(SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken);
checkParameterList(parameters);
var type = parseOptional(returnToken) ? parseType() : undefined;
return {
typeParameters: typeParameters,
parameters: parameters,
type: type
};
}
// Because we use this for index signatures as well, we sometimes use
// parentheses, and sometimes use brackets.
function parseParameterList(startDelimiter: SyntaxKind, endDelimiter: SyntaxKind) {
return parseBracketedList(ParsingContext.Parameters, parseParameter, startDelimiter, endDelimiter);
}
function checkParameterList(parameters: NodeArray): void {
var seenOptionalParameter = false;
var parameterCount = parameters.length;
for (var i = 0; i < parameterCount; i++) {
var parameter = parameters[i];
// It is a SyntaxError if the Identifier "eval" or the Identifier "arguments" occurs as the
// Identifier in a PropertySetParameterList of a PropertyAssignment that is contained in strict code
// or if its FunctionBody is strict code(11.1.5).
// It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a
// strict mode FunctionDeclaration or FunctionExpression(13.1)
if (isInStrictMode && isEvalOrArgumentsIdentifier(parameter.name)) {
reportInvalidUseInStrictMode(parameter.name);
return;
}
else if (parameter.flags & NodeFlags.Rest) {
if (i !== (parameterCount - 1)) {
grammarErrorOnNode(parameter.name, Diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list);
return;
}
if (parameter.flags & NodeFlags.QuestionMark) {
grammarErrorOnNode(parameter.name, Diagnostics.A_rest_parameter_cannot_be_optional);
return;
}
if (parameter.initializer) {
grammarErrorOnNode(parameter.name, Diagnostics.A_rest_parameter_cannot_have_an_initializer);
return;
}
}
else if (parameter.flags & NodeFlags.QuestionMark || parameter.initializer) {
seenOptionalParameter = true;
if (parameter.flags & NodeFlags.QuestionMark && parameter.initializer) {
grammarErrorOnNode(parameter.name, Diagnostics.Parameter_cannot_have_question_mark_and_initializer);
return;
}
}
else {
if (seenOptionalParameter) {
grammarErrorOnNode(parameter.name, Diagnostics.A_required_parameter_cannot_follow_an_optional_parameter);
return;
}
}
}
}
function parseSignatureMember(kind: SyntaxKind, returnToken: SyntaxKind): SignatureDeclaration {
var node = createNode(kind);
var sig = parseSignature(kind, returnToken);
node.typeParameters = sig.typeParameters;
node.parameters = sig.parameters;
node.type = sig.type;
parseSemicolon();
return finishNode(node);
}
function parseIndexSignatureMember(): SignatureDeclaration {
var node = createNode(SyntaxKind.IndexSignature);
var errorCountBeforeIndexSignature = file.syntacticErrors.length;
var indexerStart = scanner.getTokenPos();
node.parameters = parseParameterList(SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken);
var indexerLength = scanner.getStartPos() - indexerStart;
node.type = parseTypeAnnotation();
parseSemicolon();
if (file.syntacticErrors.length === errorCountBeforeIndexSignature) {
checkIndexSignature(node, indexerStart, indexerLength);
}
return finishNode(node);
}
function checkIndexSignature(node: SignatureDeclaration, indexerStart: number, indexerLength: number): void {
var parameter = node.parameters[0];
if (node.parameters.length !== 1) {
var arityDiagnostic = Diagnostics.An_index_signature_must_have_exactly_one_parameter
if (parameter) {
grammarErrorOnNode(parameter.name, arityDiagnostic);
}
else {
grammarErrorAtPos(indexerStart, indexerLength, arityDiagnostic);
}
return;
}
else if (parameter.flags & NodeFlags.Rest) {
grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_cannot_have_a_rest_parameter);
return;
}
else if (parameter.flags & NodeFlags.Modifier) {
grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_accessibility_modifier);
return;
}
else if (parameter.flags & NodeFlags.QuestionMark) {
grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_a_question_mark);
return;
}
else if (parameter.initializer) {
grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_initializer);
return;
}
else if (!parameter.type) {
grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_must_have_a_type_annotation);
return;
}
else if (parameter.type.kind !== SyntaxKind.StringKeyword &&
parameter.type.kind !== SyntaxKind.NumberKeyword) {
grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_type_must_be_string_or_number);
return;
}
else if (!node.type) {
grammarErrorAtPos(indexerStart, indexerLength, Diagnostics.An_index_signature_must_have_a_type_annotation);
return;
}
}
function parsePropertyOrMethod(): Declaration {
var node = createNode(SyntaxKind.Unknown);
node.name = parsePropertyName();
if (parseOptional(SyntaxKind.QuestionToken)) {
node.flags |= NodeFlags.QuestionMark;
}
if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) {
node.kind = SyntaxKind.Method;
var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken);
(node).typeParameters = sig.typeParameters;
(node).parameters = sig.parameters;
(node).type = sig.type;
}
else {
node.kind = SyntaxKind.Property;
(node).type = parseTypeAnnotation();
}
parseSemicolon();
return finishNode(node);
}
function isTypeMember(): boolean {
switch (token) {
case SyntaxKind.OpenParenToken:
case SyntaxKind.LessThanToken:
case SyntaxKind.OpenBracketToken:
return true;
default:
return isPropertyName() && lookAhead(() => nextToken() === 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, SyntaxKind.ColonToken);
case SyntaxKind.OpenBracketToken:
return parseIndexSignatureMember();
case SyntaxKind.NewKeyword:
if (lookAhead(() => nextToken() === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken)) {
return parseSignatureMember(SyntaxKind.ConstructSignature, SyntaxKind.ColonToken);
}
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
return parsePropertyOrMethod();
default:
if (token >= SyntaxKind.Identifier) {
return parsePropertyOrMethod();
}
}
}
function parseTypeLiteral(): TypeLiteralNode {
var node = createNode(SyntaxKind.TypeLiteral);
if (parseExpected(SyntaxKind.OpenBraceToken)) {
node.members = parseList(ParsingContext.TypeMembers, /*checkForStrictMode*/ false, parseTypeMember);
parseExpected(SyntaxKind.CloseBraceToken);
}
else {
node.members = createMissingList();
}
return finishNode(node);
}
function parseFunctionType(signatureKind: SyntaxKind): TypeLiteralNode {
var node = createNode(SyntaxKind.TypeLiteral);
var member = createNode(signatureKind);
var sig = parseSignature(signatureKind, SyntaxKind.EqualsGreaterThanToken);
member.typeParameters = sig.typeParameters;
member.parameters = sig.parameters;
member.type = sig.type;
finishNode(member);
node.members = createNodeArray(member);
return finishNode(node);
}
function parseKeywordAndNoDot(): Node {
var 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.VoidKeyword:
var node = tryParse(parseKeywordAndNoDot);
return node || parseTypeReference();
case SyntaxKind.TypeOfKeyword:
return parseTypeQuery();
case SyntaxKind.OpenBraceToken:
return parseTypeLiteral();
case SyntaxKind.OpenParenToken:
case SyntaxKind.LessThanToken:
return parseFunctionType(SyntaxKind.CallSignature);
case SyntaxKind.NewKeyword:
return parseFunctionType(SyntaxKind.ConstructSignature);
default:
if (isIdentifier()) {
return parseTypeReference();
}
}
error(Diagnostics.Type_expected);
return createMissingNode();
}
function isType(): boolean {
switch (token) {
case SyntaxKind.AnyKeyword:
case SyntaxKind.StringKeyword:
case SyntaxKind.NumberKeyword:
case SyntaxKind.BooleanKeyword:
case SyntaxKind.VoidKeyword:
case SyntaxKind.TypeOfKeyword:
case SyntaxKind.OpenBraceToken:
case SyntaxKind.LessThanToken:
case SyntaxKind.NewKeyword:
return true;
case SyntaxKind.OpenParenToken:
// Only consider an ( as the start of a type if we have () or (id
// We don't want to consider things like (1) as a function type.
return lookAhead(() => {
nextToken();
return token === SyntaxKind.CloseParenToken || isParameter();
});
default:
return isIdentifier();
}
}
function parseType(): TypeNode {
var type = parseNonArrayType();
while (type && !scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) {
parseExpected(SyntaxKind.CloseBracketToken);
var node = createNode(SyntaxKind.ArrayType, type.pos);
node.elementType = type;
type = finishNode(node);
}
return type;
}
function parseTypeAnnotation(): TypeNode {
return parseOptional(SyntaxKind.ColonToken) ? parseType() : undefined;
}
// EXPRESSIONS
function isExpression(): 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.OpenParenToken:
case SyntaxKind.OpenBracketToken:
case SyntaxKind.OpenBraceToken:
case SyntaxKind.FunctionKeyword:
case SyntaxKind.NewKeyword:
case SyntaxKind.SlashToken:
case SyntaxKind.SlashEqualsToken:
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.Identifier:
return true;
default:
return isIdentifier();
}
}
function isExpressionStatement(): boolean {
// As per the grammar, neither '{' nor 'function' can start an expression statement.
return token !== SyntaxKind.OpenBraceToken && token !== SyntaxKind.FunctionKeyword && isExpression();
}
function parseExpression(noIn?: boolean): Expression {
var expr = parseAssignmentExpression(noIn);
while (parseOptional(SyntaxKind.CommaToken)) {
expr = makeBinaryExpression(expr, SyntaxKind.CommaToken, parseAssignmentExpression(noIn));
}
return expr;
}
function parseInitializer(inParameter: boolean, noIn?: 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) || !isExpression()) {
// 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;
}
}
parseExpected(SyntaxKind.EqualsToken);
return parseAssignmentExpression(noIn);
}
function parseAssignmentExpression(noIn?: boolean): Expression {
// Augmented by TypeScript:
//
// AssignmentExpression[in]:
// 1) ConditionalExpression[in]
// 2) LeftHandSideExpression = AssignmentExpression[in]
// 3) LeftHandSideExpression AssignmentOperator AssignmentExpression[in]
// 4) ArrowFunctionExpression <-- added by TypeScript
//
// 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, check if we have production '4' (an arrow function). Note that if we do, we
// must *not* recurse for productsion 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.
var arrowExpression = tryParseArrowFunctionExpression();
if (arrowExpression) {
return arrowExpression;
}
// Now try to handle the rest of the cases. First, see if we can parse out up to and
// including a conditional expression.
var expr = parseConditionalExpression(noIn);
// Now see if we might be in cases '2' or '3'.
// If the expression was a LHS expression, and we have an assignment operator, then
// we're in '2' or '3'. Consume the assignment and return.
if (isLeftHandSideExpression(expr) && isAssignmentOperator()) {
if (isInStrictMode && isEvalOrArgumentsIdentifier(expr)) {
// ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an
// Assignment operator(11.13) or of a PostfixExpression(11.3)
reportInvalidUseInStrictMode(expr);
}
var operator = token;
nextToken();
return makeBinaryExpression(expr, operator, parseAssignmentExpression(noIn));
}
// otherwise this was production '1'. Return whatever we parsed so far.
return expr;
}
function isLeftHandSideExpression(expr: Expression): boolean {
if (expr) {
switch (expr.kind) {
case SyntaxKind.PropertyAccess:
case SyntaxKind.IndexedAccess:
case SyntaxKind.NewExpression:
case SyntaxKind.CallExpression:
case SyntaxKind.ArrayLiteral:
case SyntaxKind.ParenExpression:
case SyntaxKind.ObjectLiteral:
case SyntaxKind.FunctionExpression:
case SyntaxKind.Identifier:
case SyntaxKind.Missing:
case SyntaxKind.RegularExpressionLiteral:
case SyntaxKind.NumericLiteral:
case SyntaxKind.StringLiteral:
case SyntaxKind.FalseKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.ThisKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.SuperKeyword:
return true;
}
}
return false;
}
function tryParseArrowFunctionExpression(): Expression {
return isSimpleArrowFunctionExpression()
? parseSimpleArrowFunctionExpression()
: tryParseParenthesizedArrowFunctionExpression();
}
function isSimpleArrowFunctionExpression(): boolean {
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 whatthe user intended to write.
return true;
}
if (token === SyntaxKind.Identifier) {
// if we see: a =>
// then this is clearly an arrow function expression.
return lookAhead(() => {
return nextToken() === SyntaxKind.EqualsGreaterThanToken;
});
}
// Definitely not a simple arrow function expression.
return false;
}
function parseSimpleArrowFunctionExpression(): Expression {
Debug.assert(token === SyntaxKind.Identifier || token === SyntaxKind.EqualsGreaterThanToken);
// Get the identifier for the simple arrow. If one isn't there then we'll report a useful
// message that it is missing.
var identifier = parseIdentifier();
Debug.assert(token === SyntaxKind.EqualsGreaterThanToken, "parseSimpleArrowFunctionExpression should only have been called if we had a =>");
parseExpected(SyntaxKind.EqualsGreaterThanToken);
var parameter = createNode(SyntaxKind.Parameter, identifier.pos);
parameter.name = identifier;
finishNode(parameter);
var signature = { parameters: [parameter] };
return parseArrowExpressionTail(identifier.pos, signature, /*noIn:*/ false);
}
function tryParseParenthesizedArrowFunctionExpression(): Expression {
var pos = getNodePos();
// Indicates whether we are certain that we should parse an arrow expression.
var triState = isParenthesizedArrowFunctionExpression();
if (triState === Tristate.False) {
return undefined;
}
if (triState === Tristate.True) {
var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken);
// 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.
if (parseExpected(SyntaxKind.EqualsGreaterThanToken) || token === SyntaxKind.OpenBraceToken) {
return parseArrowExpressionTail(pos, sig, /* noIn: */ false);
}
else {
// If not, we're probably better off bailing out and returning a bogus function expression.
return makeFunctionExpression(SyntaxKind.ArrowFunction, pos, /* name */ undefined, sig, createMissingNode());
}
}
// *Maybe* we had an arrow function and we need to try to parse it out,
// rolling back and trying other parses if we fail.
var sig = tryParseSignatureIfArrowOrBraceFollows();
if (sig) {
parseExpected(SyntaxKind.EqualsGreaterThanToken);
return parseArrowExpressionTail(pos, sig, /*noIn:*/ false);
}
else {
return undefined;
}
}
// 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(() => {
var first = token;
var 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.
var third = nextToken();
switch (third) {
case SyntaxKind.EqualsGreaterThanToken:
case SyntaxKind.ColonToken:
case SyntaxKind.OpenBraceToken:
return Tristate.True;
default:
return Tristate.False;
}
}
// 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;
}
});
}
// Definitely not a parenthesized arrow function.
return Tristate.False;
}
function tryParseSignatureIfArrowOrBraceFollows(): ParsedSignature {
return tryParse(() => {
var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken);
// 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 (token === SyntaxKind.EqualsGreaterThanToken || token === SyntaxKind.OpenBraceToken) {
return sig;
}
return undefined;
});
}
function parseArrowExpressionTail(pos: number, sig: ParsedSignature, noIn: boolean): FunctionExpression {
var body: Node;
if (token === SyntaxKind.OpenBraceToken) {
body = parseBody(/* ignoreMissingOpenBrace */ false)
}
else if (isStatement(/* inErrorRecovery */ true) && !isExpressionStatement() && token !== SyntaxKind.FunctionKeyword) {
// Check if we got a plain statement (i.e. no expression-statements, no functions 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 =>
// var 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.
body = parseBody(/* ignoreMissingOpenBrace */ true);
}
else {
body = parseAssignmentExpression(noIn);
}
return makeFunctionExpression(SyntaxKind.ArrowFunction, pos, /* name */ undefined, sig, body);
}
function isAssignmentOperator(): boolean {
return token >= SyntaxKind.FirstAssignment && token <= SyntaxKind.LastAssignment;
}
function parseConditionalExpression(noIn?: boolean): Expression {
var expr = parseBinaryExpression(noIn);
while (parseOptional(SyntaxKind.QuestionToken)) {
var node = createNode(SyntaxKind.ConditionalExpression, expr.pos);
node.condition = expr;
node.whenTrue = parseAssignmentExpression(false);
parseExpected(SyntaxKind.ColonToken);
node.whenFalse = parseAssignmentExpression(noIn);
expr = finishNode(node);
}
return expr;
}
function parseBinaryExpression(noIn?: boolean): Expression {
return parseBinaryOperators(parseUnaryExpression(), 0, noIn);
}
function parseBinaryOperators(expr: Expression, minPrecedence: number, noIn?: boolean): Expression {
while (true) {
reScanGreaterToken();
var precedence = getOperatorPrecedence();
if (precedence && precedence > minPrecedence && (!noIn || token !== SyntaxKind.InKeyword)) {
var operator = token;
nextToken();
expr = makeBinaryExpression(expr, operator, parseBinaryOperators(parseUnaryExpression(), precedence, noIn));
continue;
}
return expr;
}
}
function getOperatorPrecedence(): 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;
}
return undefined;
}
function makeBinaryExpression(left: Expression, operator: SyntaxKind, right: Expression): BinaryExpression {
var node = createNode(SyntaxKind.BinaryExpression, left.pos);
node.left = left;
node.operator = operator;
node.right = right;
return finishNode(node);
}
function parseUnaryExpression(): Expression {
var pos = getNodePos();
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:
var operator = token;
nextToken();
var operand = parseUnaryExpression();
if (isInStrictMode) {
// The identifier eval or arguments may not appear as the LeftHandSideExpression of an
// Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression
// operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator
if ((token === SyntaxKind.PlusPlusToken || token === SyntaxKind.MinusMinusToken) && isEvalOrArgumentsIdentifier(operand)) {
reportInvalidUseInStrictMode(operand);
}
else if (token === SyntaxKind.DeleteKeyword && operand.kind === SyntaxKind.Identifier) {
// When a delete operator occurs within strict mode code, a SyntaxError is thrown if its
// UnaryExpression is a direct reference to a variable, function argument, or function name
grammarErrorOnNode(operand, Diagnostics.delete_cannot_be_called_on_an_identifier_in_strict_mode);
}
}
return makeUnaryExpression(SyntaxKind.PrefixOperator, pos, operator, operand);
case SyntaxKind.LessThanToken:
return parseTypeAssertion();
}
var primaryExpression = parsePrimaryExpression();
// TS 1.0 spec (2014): 4.8
// CallExpression: ( Modified )
// super ( ArgumentListopt )
// super . IdentifierName
var illegalUsageOfSuperKeyword =
primaryExpression.kind === SyntaxKind.SuperKeyword && token !== SyntaxKind.OpenParenToken && token !== SyntaxKind.DotToken;
if (illegalUsageOfSuperKeyword) {
error(Diagnostics.super_must_be_followed_by_argument_list_or_member_access);
}
var expr = parseCallAndAccess(primaryExpression, /* inNewExpression */ false);
Debug.assert(isLeftHandSideExpression(expr));
if ((token === SyntaxKind.PlusPlusToken || token === SyntaxKind.MinusMinusToken) && !scanner.hasPrecedingLineBreak()) {
// The identifier eval or arguments may not appear as the LeftHandSideExpression of an
// Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression
// operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator.
if (isInStrictMode && isEvalOrArgumentsIdentifier(expr)) {
reportInvalidUseInStrictMode(expr);
}
var operator = token;
nextToken();
expr = makeUnaryExpression(SyntaxKind.PostfixOperator, expr.pos, operator, expr);
}
return expr;
}
function parseTypeAssertion(): TypeAssertion {
var node = createNode(SyntaxKind.TypeAssertion);
parseExpected(SyntaxKind.LessThanToken);
node.type = parseType();
parseExpected(SyntaxKind.GreaterThanToken);
node.operand = parseUnaryExpression();
return finishNode(node);
}
function makeUnaryExpression(kind: SyntaxKind, pos: number, operator: SyntaxKind, operand: Expression): UnaryExpression {
var node = createNode(kind, pos);
node.operator = operator;
node.operand = operand;
return finishNode(node);
}
function parseCallAndAccess(expr: Expression, inNewExpression: boolean): Expression {
while (true) {
if (parseOptional(SyntaxKind.DotToken)) {
var propertyAccess = createNode(SyntaxKind.PropertyAccess, expr.pos);
propertyAccess.left = expr;
propertyAccess.right = parseIdentifierName();
expr = finishNode(propertyAccess);
continue;
}
var bracketStart = scanner.getTokenPos();
if (parseOptional(SyntaxKind.OpenBracketToken)) {
var indexedAccess = createNode(SyntaxKind.IndexedAccess, expr.pos);
indexedAccess.object = expr;
// It's not uncommon for a user to write: "new Type[]". Check for that common pattern
// and report a better error message.
if (inNewExpression && parseOptional(SyntaxKind.CloseBracketToken)) {
indexedAccess.index = createMissingNode();
grammarErrorAtPos(bracketStart, scanner.getStartPos() - bracketStart, Diagnostics.new_T_cannot_be_used_to_create_an_array_Use_new_Array_T_instead);
}
// Otherwise parse the indexed access normally.
else {
indexedAccess.index = parseExpression();
parseExpected(SyntaxKind.CloseBracketToken);
}
expr = finishNode(indexedAccess);
continue;
}
// Try to parse a Call Expression unless we are in a New Expression.
// If we are parsing a New Expression, then parentheses are optional,
// and is taken care of by the 'parseNewExpression' caller.
if ((token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) && !inNewExpression) {
var callExpr = createNode(SyntaxKind.CallExpression, expr.pos);
callExpr.func = expr;
if (token === SyntaxKind.LessThanToken) {
if (!(callExpr.typeArguments = tryParse(parseTypeArgumentsAndOpenParen))) return expr;
}
else {
parseExpected(SyntaxKind.OpenParenToken);
}
callExpr.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, parseAssignmentExpression, TrailingCommaBehavior.Disallow);
parseExpected(SyntaxKind.CloseParenToken);
expr = finishNode(callExpr);
continue;
}
return expr;
}
}
function parseTypeArgumentsAndOpenParen(): NodeArray {
var result = parseTypeArguments();
parseExpected(SyntaxKind.OpenParenToken);
return result;
}
function parseTypeArguments(): NodeArray {
var typeArgumentListStart = scanner.getTokenPos();
var errorCountBeforeTypeParameterList = file.syntacticErrors.length;
var result = parseBracketedList(ParsingContext.TypeArguments, parseType, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken);
if (!result.length && file.syntacticErrors.length === errorCountBeforeTypeParameterList) {
grammarErrorAtPos(typeArgumentListStart, scanner.getStartPos() - typeArgumentListStart, Diagnostics.Type_argument_list_cannot_be_empty);
}
return result;
}
function parsePrimaryExpression(): Expression {
switch (token) {
case SyntaxKind.ThisKeyword:
case SyntaxKind.SuperKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
return parseTokenNode();
case SyntaxKind.NumericLiteral:
case SyntaxKind.StringLiteral:
return parseLiteralNode();
case SyntaxKind.OpenParenToken:
return parseParenExpression();
case SyntaxKind.OpenBracketToken:
return parseArrayLiteral();
case SyntaxKind.OpenBraceToken:
return parseObjectLiteral();
case SyntaxKind.FunctionKeyword:
return parseFunctionExpression();
case SyntaxKind.NewKeyword:
return parseNewExpression();
case SyntaxKind.SlashToken:
case SyntaxKind.SlashEqualsToken:
if (reScanSlashToken() === SyntaxKind.RegularExpressionLiteral) {
return parseLiteralNode();
}
break;
default:
if (isIdentifier()) {
return parseIdentifier();
}
}
error(Diagnostics.Expression_expected);
return createMissingNode();
}
function parseParenExpression(): ParenExpression {
var node = createNode(SyntaxKind.ParenExpression);
parseExpected(SyntaxKind.OpenParenToken);
node.expression = parseExpression();
parseExpected(SyntaxKind.CloseParenToken);
return finishNode(node);
}
function parseArrayLiteralElement(): Expression {
return token === SyntaxKind.CommaToken ? createNode(SyntaxKind.OmittedExpression) : parseAssignmentExpression();
}
function parseArrayLiteral(): ArrayLiteral {
var node = createNode(SyntaxKind.ArrayLiteral);
parseExpected(SyntaxKind.OpenBracketToken);
if (scanner.hasPrecedingLineBreak()) node.flags |= NodeFlags.MultiLine;
node.elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers, parseArrayLiteralElement, TrailingCommaBehavior.Preserve);
parseExpected(SyntaxKind.CloseBracketToken);
return finishNode(node);
}
function parsePropertyAssignment(): PropertyDeclaration {
var node = createNode(SyntaxKind.PropertyAssignment);
node.name = parsePropertyName();
if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) {
var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken);
var body = parseBody(/* ignoreMissingOpenBrace */ false);
// do not propagate property name as name for function expression
// for scenarios like
// var x = 1;
// var y = { x() { } }
// otherwise this will bring y.x into the scope of x which is incorrect
node.initializer = makeFunctionExpression(SyntaxKind.FunctionExpression, node.pos, undefined, sig, body);
}
else {
parseExpected(SyntaxKind.ColonToken);
node.initializer = parseAssignmentExpression(false);
}
return finishNode(node);
}
function parseObjectLiteralMember(): Node {
var initialPos = getNodePos();
var initialToken = token;
if (parseContextualModifier(SyntaxKind.GetKeyword) || parseContextualModifier(SyntaxKind.SetKeyword)) {
var kind = initialToken === SyntaxKind.GetKeyword ? SyntaxKind.GetAccessor : SyntaxKind.SetAccessor;
return parseAndCheckMemberAccessorDeclaration(kind, initialPos, 0);
}
return parsePropertyAssignment();
}
function parseObjectLiteral(): ObjectLiteral {
var node = createNode(SyntaxKind.ObjectLiteral);
parseExpected(SyntaxKind.OpenBraceToken);
if (scanner.hasPrecedingLineBreak()) node.flags |= NodeFlags.MultiLine;
node.properties = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralMember, TrailingCommaBehavior.Preserve);
parseExpected(SyntaxKind.CloseBraceToken);
var seen: Map = {};
var Property = 1;
var GetAccessor = 2;
var SetAccesor = 4;
var GetOrSetAccessor = GetAccessor | SetAccesor;
forEach(node.properties, (p: Declaration) => {
if (p.kind === SyntaxKind.OmittedExpression) {
return;
}
// ECMA-262 11.1.5 Object Initialiser
// If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true
// a.This production is contained in strict code and IsDataDescriptor(previous) is true and
// IsDataDescriptor(propId.descriptor) is true.
// b.IsDataDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true.
// c.IsAccessorDescriptor(previous) is true and IsDataDescriptor(propId.descriptor) is true.
// d.IsAccessorDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true
// and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields
var currentKind: number;
if (p.kind === SyntaxKind.PropertyAssignment) {
currentKind = Property;
}
else if (p.kind === SyntaxKind.GetAccessor) {
currentKind = GetAccessor;
}
else if (p.kind === SyntaxKind.SetAccessor) {
currentKind = SetAccesor;
}
else {
Debug.fail("Unexpected syntax kind:" + SyntaxKind[p.kind]);
}
if (!hasProperty(seen, p.name.text)) {
seen[p.name.text] = currentKind;
}
else {
var existingKind = seen[p.name.text];
if (currentKind === Property && existingKind === Property) {
if (isInStrictMode) {
grammarErrorOnNode(p.name, Diagnostics.An_object_literal_cannot_have_multiple_properties_with_the_same_name_in_strict_mode);
}
}
else if ((currentKind & GetOrSetAccessor) && (existingKind & GetOrSetAccessor)) {
if (existingKind !== GetOrSetAccessor && currentKind !== existingKind) {
seen[p.name.text] = currentKind | existingKind;
}
else {
grammarErrorOnNode(p.name, Diagnostics.An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name);
}
}
else {
grammarErrorOnNode(p.name, Diagnostics.An_object_literal_cannot_have_property_and_accessor_with_the_same_name);
}
}
});
return finishNode(node);
}
function parseFunctionExpression(): FunctionExpression {
var pos = getNodePos();
parseExpected(SyntaxKind.FunctionKeyword);
var name = isIdentifier() ? parseIdentifier() : undefined;
var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken);
var body = parseBody(/* ignoreMissingOpenBrace */ false);
if (name && isInStrictMode && isEvalOrArgumentsIdentifier(name)) {
// It is a SyntaxError to use within strict mode code the identifiers eval or arguments as the
// Identifier of a FunctionDeclaration or FunctionExpression or as a formal parameter name(13.1)
reportInvalidUseInStrictMode(name);
}
return makeFunctionExpression(SyntaxKind.FunctionExpression, pos, name, sig, body);
}
function makeFunctionExpression(kind: SyntaxKind, pos: number, name: Identifier, sig: ParsedSignature, body: Node): FunctionExpression {
var node = createNode(kind, pos);
node.name = name;
node.typeParameters = sig.typeParameters;
node.parameters = sig.parameters;
node.type = sig.type;
node.body = body;
return finishNode(node);
}
function parseNewExpression(): NewExpression {
var node = createNode(SyntaxKind.NewExpression);
parseExpected(SyntaxKind.NewKeyword);
node.func = parseCallAndAccess(parsePrimaryExpression(), /* inNewExpression */ true);
if (parseOptional(SyntaxKind.OpenParenToken) || token === SyntaxKind.LessThanToken && (node.typeArguments = tryParse(parseTypeArgumentsAndOpenParen))) {
node.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, parseAssignmentExpression, TrailingCommaBehavior.Disallow);
parseExpected(SyntaxKind.CloseParenToken);
}
return finishNode(node);
}
// STATEMENTS
function parseBlock(ignoreMissingOpenBrace: boolean, checkForStrictMode: boolean): Block {
var node = createNode(SyntaxKind.Block);
if (parseExpected(SyntaxKind.OpenBraceToken) || ignoreMissingOpenBrace) {
node.statements = parseList(ParsingContext.BlockStatements,checkForStrictMode, parseStatement);
parseExpected(SyntaxKind.CloseBraceToken);
}
else {
node.statements = createMissingList();
}
return finishNode(node);
}
function parseBody(ignoreMissingOpenBrace: boolean): Block {
var saveInFunctionBody = inFunctionBody;
var saveInSwitchStatement = inSwitchStatement;
var saveInIterationStatement = inIterationStatement;
inFunctionBody = true;
if (inSwitchStatement === ControlBlockContext.Nested) {
inSwitchStatement = ControlBlockContext.CrossingFunctionBoundary;
}
if (inIterationStatement === ControlBlockContext.Nested) {
inIterationStatement = ControlBlockContext.CrossingFunctionBoundary;
}
labelledStatementInfo.pushFunctionBoundary();
var block = parseBlock(ignoreMissingOpenBrace, /*checkForStrictMode*/ true);
block.kind = SyntaxKind.FunctionBlock;
labelledStatementInfo.pop();
inFunctionBody = saveInFunctionBody;
inSwitchStatement = saveInSwitchStatement;
inIterationStatement = saveInIterationStatement;
return block;
}
function parseEmptyStatement(): Statement {
var node = createNode(SyntaxKind.EmptyStatement);
parseExpected(SyntaxKind.SemicolonToken);
return finishNode(node);
}
function parseIfStatement(): IfStatement {
var node = createNode(SyntaxKind.IfStatement);
parseExpected(SyntaxKind.IfKeyword);
parseExpected(SyntaxKind.OpenParenToken);
node.expression = parseExpression();
parseExpected(SyntaxKind.CloseParenToken);
node.thenStatement = parseStatement();
node.elseStatement = parseOptional(SyntaxKind.ElseKeyword) ? parseStatement() : undefined;
return finishNode(node);
}
function parseDoStatement(): DoStatement {
var node = createNode(SyntaxKind.DoStatement);
parseExpected(SyntaxKind.DoKeyword);
var saveInIterationStatement = inIterationStatement;
inIterationStatement = ControlBlockContext.Nested;
node.statement = parseStatement();
inIterationStatement = saveInIterationStatement;
parseExpected(SyntaxKind.WhileKeyword);
parseExpected(SyntaxKind.OpenParenToken);
node.expression = parseExpression();
parseExpected(SyntaxKind.CloseParenToken);
// From: https://mail.mozilla.org/pipermail/es-discuss/2011-August/016188.html
// 157 min --- All allen at wirfs-brock.com CONF --- "do{;}while(false)false" prohibited in
// spec but allowed in consensus reality. Approved -- this is the de-facto standard whereby
// do;while(0)x will have a semicolon inserted before x.
parseOptional(SyntaxKind.SemicolonToken);
return finishNode(node);
}
function parseWhileStatement(): WhileStatement {
var node = createNode(SyntaxKind.WhileStatement);
parseExpected(SyntaxKind.WhileKeyword);
parseExpected(SyntaxKind.OpenParenToken);
node.expression = parseExpression();
parseExpected(SyntaxKind.CloseParenToken);
var saveInIterationStatement = inIterationStatement;
inIterationStatement = ControlBlockContext.Nested;
node.statement = parseStatement();
inIterationStatement = saveInIterationStatement;
return finishNode(node);
}
function parseForOrForInStatement(): Statement {
var pos = getNodePos();
parseExpected(SyntaxKind.ForKeyword);
parseExpected(SyntaxKind.OpenParenToken);
if (token !== SyntaxKind.SemicolonToken) {
if (parseOptional(SyntaxKind.VarKeyword)) {
var declarations = parseVariableDeclarationList(0, true);
if (!declarations.length) {
error(Diagnostics.Variable_declaration_list_cannot_be_empty);
}
}
else {
var varOrInit = parseExpression(true);
}
}
var forOrForInStatement: IterationStatement;
if (parseOptional(SyntaxKind.InKeyword)) {
var forInStatement = createNode(SyntaxKind.ForInStatement, pos);
if (declarations) {
if (declarations.length > 1) {
error(Diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement);
}
forInStatement.declaration = declarations[0];
}
else {
forInStatement.variable = varOrInit;
}
forInStatement.expression = parseExpression();
parseExpected(SyntaxKind.CloseParenToken);
forOrForInStatement = forInStatement;
}
else {
var forStatement = createNode(SyntaxKind.ForStatement, pos);
if (declarations) forStatement.declarations = declarations;
if (varOrInit) forStatement.initializer = varOrInit;
parseExpected(SyntaxKind.SemicolonToken);
if (token !== SyntaxKind.SemicolonToken && token !== SyntaxKind.CloseParenToken) {
forStatement.condition = parseExpression();
}
parseExpected(SyntaxKind.SemicolonToken);
if (token !== SyntaxKind.CloseParenToken) {
forStatement.iterator = parseExpression();
}
parseExpected(SyntaxKind.CloseParenToken);
forOrForInStatement = forStatement;
}
var saveInIterationStatement = inIterationStatement;
inIterationStatement = ControlBlockContext.Nested;
forOrForInStatement.statement = parseStatement();
inIterationStatement = saveInIterationStatement;
return finishNode(forOrForInStatement);
}
function parseBreakOrContinueStatement(kind: SyntaxKind): BreakOrContinueStatement {
var node = createNode(kind);
var errorCountBeforeStatement = file.syntacticErrors.length;
var keywordStart = scanner.getTokenPos();
var keywordLength = scanner.getTextPos() - keywordStart;
parseExpected(kind === SyntaxKind.BreakStatement ? SyntaxKind.BreakKeyword : SyntaxKind.ContinueKeyword);
if (!canParseSemicolon()) node.label = parseIdentifier();
parseSemicolon();
finishNode(node);
// In an ambient context, we will already give an error for having a statement.
if (!inAmbientContext && errorCountBeforeStatement === file.syntacticErrors.length) {
if (node.label) {
checkBreakOrContinueStatementWithLabel(node);
}
else {
checkBareBreakOrContinueStatement(node);
}
}
return node;
}
function checkBareBreakOrContinueStatement(node: BreakOrContinueStatement): void {
if (node.kind === SyntaxKind.BreakStatement) {
if (inIterationStatement === ControlBlockContext.Nested
|| inSwitchStatement === ControlBlockContext.Nested) {
return;
}
else if (inIterationStatement === ControlBlockContext.NotNested
&& inSwitchStatement === ControlBlockContext.NotNested) {
grammarErrorOnNode(node, Diagnostics.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement);
return;
}
// Fall through
}
else if (node.kind === SyntaxKind.ContinueStatement) {
if (inIterationStatement === ControlBlockContext.Nested) {
return;
}
else if (inIterationStatement === ControlBlockContext.NotNested) {
grammarErrorOnNode(node, Diagnostics.A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement);
return;
}
// Fall through
}
else {
Debug.fail("checkAnonymousBreakOrContinueStatement");
}
Debug.assert(inIterationStatement === ControlBlockContext.CrossingFunctionBoundary
|| inSwitchStatement === ControlBlockContext.CrossingFunctionBoundary);
grammarErrorOnNode(node, Diagnostics.Jump_target_cannot_cross_function_boundary);
}
function checkBreakOrContinueStatementWithLabel(node: BreakOrContinueStatement): void {
// For error specificity, if the label is not found, we want to distinguish whether it is because
// it crossed a function boundary or it was simply not found. To do this, we pass false for
// stopAtFunctionBoundary.
var nodeIsNestedInLabel = labelledStatementInfo.nodeIsNestedInLabel(node.label,
/*requireIterationStatement*/ node.kind === SyntaxKind.ContinueStatement,
/*stopAtFunctionBoundary*/ false);
if (nodeIsNestedInLabel === ControlBlockContext.Nested) {
return;
}
if (nodeIsNestedInLabel === ControlBlockContext.CrossingFunctionBoundary) {
grammarErrorOnNode(node, Diagnostics.Jump_target_cannot_cross_function_boundary);
return;
}
// It is NotNested
if (node.kind === SyntaxKind.ContinueStatement) {
grammarErrorOnNode(node, Diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement);
}
else if (node.kind === SyntaxKind.BreakStatement) {
grammarErrorOnNode(node, Diagnostics.A_break_statement_can_only_jump_to_a_label_of_an_enclosing_statement);
}
else {
Debug.fail("checkBreakOrContinueStatementWithLabel");
}
}
function parseReturnStatement(): ReturnStatement {
var node = createNode(SyntaxKind.ReturnStatement);
var errorCountBeforeReturnStatement = file.syntacticErrors.length;
var returnTokenStart = scanner.getTokenPos();
var returnTokenLength = scanner.getTextPos() - returnTokenStart;
parseExpected(SyntaxKind.ReturnKeyword);
if (!canParseSemicolon()) node.expression = parseExpression();
parseSemicolon();
// In an ambient context, we will already give an error for having a statement.
if (!inFunctionBody && !inAmbientContext && errorCountBeforeReturnStatement === file.syntacticErrors.length) {
grammarErrorAtPos(returnTokenStart, returnTokenLength, Diagnostics.A_return_statement_can_only_be_used_within_a_function_body);
}
return finishNode(node);
}
function parseWithStatement(): WithStatement {
var node = createNode(SyntaxKind.WithStatement);
var startPos = scanner.getTokenPos();
parseExpected(SyntaxKind.WithKeyword);
var endPos = scanner.getStartPos();
parseExpected(SyntaxKind.OpenParenToken);
node.expression = parseExpression();
parseExpected(SyntaxKind.CloseParenToken);
node.statement = parseStatement();
node = finishNode(node);
if (isInStrictMode) {
// Strict mode code may not include a WithStatement. The occurrence of a WithStatement in such
// a context is an
grammarErrorAtPos(startPos, endPos - startPos, Diagnostics.with_statements_are_not_allowed_in_strict_mode)
}
return node;
}
function parseCaseClause(): CaseOrDefaultClause {
var node = createNode(SyntaxKind.CaseClause);
parseExpected(SyntaxKind.CaseKeyword);
node.expression = parseExpression();
parseExpected(SyntaxKind.ColonToken);
node.statements = parseList(ParsingContext.SwitchClauseStatements, /*checkForStrictMode*/ false, parseStatement);
return finishNode(node);
}
function parseDefaultClause(): CaseOrDefaultClause {
var node = createNode(SyntaxKind.DefaultClause);
parseExpected(SyntaxKind.DefaultKeyword);
parseExpected(SyntaxKind.ColonToken);
node.statements = parseList(ParsingContext.SwitchClauseStatements, /*checkForStrictMode*/ false, parseStatement);
return finishNode(node);
}
function parseCaseOrDefaultClause(): CaseOrDefaultClause {
return token === SyntaxKind.CaseKeyword ? parseCaseClause() : parseDefaultClause();
}
function parseSwitchStatement(): SwitchStatement {
var node = createNode(SyntaxKind.SwitchStatement);
parseExpected(SyntaxKind.SwitchKeyword);
parseExpected(SyntaxKind.OpenParenToken);
node.expression = parseExpression();
parseExpected(SyntaxKind.CloseParenToken);
parseExpected(SyntaxKind.OpenBraceToken);
var saveInSwitchStatement = inSwitchStatement;
inSwitchStatement = ControlBlockContext.Nested;
node.clauses = parseList(ParsingContext.SwitchClauses, /*checkForStrictMode*/ false, parseCaseOrDefaultClause);
inSwitchStatement = saveInSwitchStatement;
parseExpected(SyntaxKind.CloseBraceToken);
// Error on duplicate 'default' clauses.
var defaultClauses: CaseOrDefaultClause[] = filter(node.clauses, clause => clause.kind === SyntaxKind.DefaultClause);
for (var i = 1, n = defaultClauses.length; i < n; i++) {
var clause = defaultClauses[i];
var start = skipTrivia(file.text, clause.pos);
var end = clause.statements.length > 0 ? clause.statements[0].pos : clause.end;
grammarErrorAtPos(start, end - start, Diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement);
}
return finishNode(node);
}
function parseThrowStatement(): ThrowStatement {
var node = createNode(SyntaxKind.ThrowStatement);
parseExpected(SyntaxKind.ThrowKeyword);
if (scanner.hasPrecedingLineBreak()) {
error(Diagnostics.Line_break_not_permitted_here);
}
node.expression = parseExpression();
parseSemicolon();
return finishNode(node);
}
// TODO: Review for error recovery
function parseTryStatement(): TryStatement {
var node = createNode(SyntaxKind.TryStatement);
node.tryBlock = parseTokenAndBlock(SyntaxKind.TryKeyword, SyntaxKind.TryBlock);
if (token === SyntaxKind.CatchKeyword) {
node.catchBlock = parseCatchBlock();
}
if (token === SyntaxKind.FinallyKeyword) {
node.finallyBlock = parseTokenAndBlock(SyntaxKind.FinallyKeyword, SyntaxKind.FinallyBlock);
}
if (!(node.catchBlock || node.finallyBlock)) {
error(Diagnostics.catch_or_finally_expected);
}
return finishNode(node);
}
function parseTokenAndBlock(token: SyntaxKind, kind: SyntaxKind): Block {
var pos = getNodePos();
parseExpected(token);
var result = parseBlock(/* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false);
result.kind = kind;
result.pos = pos;
return result;
}
function parseCatchBlock(): CatchBlock {
var pos = getNodePos();
parseExpected(SyntaxKind.CatchKeyword);
parseExpected(SyntaxKind.OpenParenToken);
var variable = parseIdentifier();
var typeAnnotationColonStart = scanner.getTokenPos();
var typeAnnotationColonLength = scanner.getTextPos() - typeAnnotationColonStart;
var typeAnnotation = parseTypeAnnotation();
parseExpected(SyntaxKind.CloseParenToken);
var result = parseBlock(/* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false);
result.kind = SyntaxKind.CatchBlock;
result.pos = pos;
result.variable = variable;
if (typeAnnotation) {
errorAtPos(typeAnnotationColonStart, typeAnnotationColonLength, Diagnostics.Catch_clause_parameter_cannot_have_a_type_annotation);
}
if (isInStrictMode && isEvalOrArgumentsIdentifier(variable)) {
// It is a SyntaxError if a TryStatement with a Catch occurs within strict code and the Identifier of the
// Catch production is eval or arguments
reportInvalidUseInStrictMode(variable);
}
return result;
}
function parseDebuggerStatement(): Statement {
var node = createNode(SyntaxKind.DebuggerStatement);
parseExpected(SyntaxKind.DebuggerKeyword);
parseSemicolon();
return finishNode(node);
}
function isIterationStatementStart(): boolean {
return token === SyntaxKind.WhileKeyword || token === SyntaxKind.DoKeyword || token === SyntaxKind.ForKeyword;
}
function parseStatementWithLabelSet(): Statement {
labelledStatementInfo.pushCurrentLabelSet(isIterationStatementStart());
var statement = parseStatement();
labelledStatementInfo.pop();
return statement;
}
function isLabel(): boolean {
return isIdentifier() && lookAhead(() => nextToken() === SyntaxKind.ColonToken);
}
function parseLabelledStatement(): LabelledStatement {
var node = createNode(SyntaxKind.LabelledStatement);
node.label = parseIdentifier();
parseExpected(SyntaxKind.ColonToken);
if (labelledStatementInfo.nodeIsNestedInLabel(node.label, /*requireIterationStatement*/ false, /*stopAtFunctionBoundary*/ true)) {
grammarErrorOnNode(node.label, Diagnostics.Duplicate_label_0, getSourceTextOfNodeFromSourceText(sourceText, node.label));
}
labelledStatementInfo.addLabel(node.label);
// We only want to call parseStatementWithLabelSet when the label set is complete
// Therefore, keep parsing labels until we know we're done.
node.statement = isLabel() ? parseLabelledStatement() : parseStatementWithLabelSet();
return finishNode(node);
}
function parseExpressionStatement(): ExpressionStatement {
var node = createNode(SyntaxKind.ExpressionStatement);
node.expression = parseExpression();
parseSemicolon();
return finishNode(node);
}
function isStatement(inErrorRecovery: boolean): boolean {
switch (token) {
case SyntaxKind.SemicolonToken:
// If we're in error recovery, then we don't want to treat ';' as an empty statement.
// The problem is that ';' can show up in far too many contexts, and if we see one
// and assume it's a statement, then we may bail out innapropriately from whatever
// we're parsing. For example, if we have a semicolon in the middle of a class, then
// we really don't want to assume the class is over and we're on a statement in the
// outer module. We just want to consume and move on.
return !inErrorRecovery;
case SyntaxKind.OpenBraceToken:
case SyntaxKind.VarKeyword:
case SyntaxKind.FunctionKeyword:
case SyntaxKind.IfKeyword:
case SyntaxKind.DoKeyword:
case SyntaxKind.WhileKeyword:
case SyntaxKind.ForKeyword:
case SyntaxKind.ContinueKeyword:
case SyntaxKind.BreakKeyword:
case SyntaxKind.ReturnKeyword:
case SyntaxKind.WithKeyword:
case SyntaxKind.SwitchKeyword:
case SyntaxKind.ThrowKeyword:
case SyntaxKind.TryKeyword:
case SyntaxKind.DebuggerKeyword:
return true;
case SyntaxKind.InterfaceKeyword:
case SyntaxKind.ClassKeyword:
case SyntaxKind.ModuleKeyword:
case SyntaxKind.EnumKeyword:
// When followed by an identifier, these do not start a statement but might
// instead be following declarations
if (isDeclaration()) return false;
case SyntaxKind.PublicKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.StaticKeyword:
// When followed by an identifier or keyword, these do not start a statement but
// might instead be following type members
if (lookAhead(() => nextToken() >= SyntaxKind.Identifier)) return false;
default:
return isExpression();
}
}
function parseStatement(): Statement {
switch (token) {
case SyntaxKind.OpenBraceToken:
return parseBlock(/* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false);
case SyntaxKind.VarKeyword:
return parseVariableStatement();
case SyntaxKind.FunctionKeyword:
return parseFunctionDeclaration();
case SyntaxKind.SemicolonToken:
return parseEmptyStatement();
case SyntaxKind.IfKeyword:
return parseIfStatement();
case SyntaxKind.DoKeyword:
return parseDoStatement();
case SyntaxKind.WhileKeyword:
return parseWhileStatement();
case SyntaxKind.ForKeyword:
return parseForOrForInStatement();
case SyntaxKind.ContinueKeyword:
return parseBreakOrContinueStatement(SyntaxKind.ContinueStatement);
case SyntaxKind.BreakKeyword:
return parseBreakOrContinueStatement(SyntaxKind.BreakStatement);
case SyntaxKind.ReturnKeyword:
return parseReturnStatement();
case SyntaxKind.WithKeyword:
return parseWithStatement();
case SyntaxKind.SwitchKeyword:
return parseSwitchStatement();
case SyntaxKind.ThrowKeyword:
return parseThrowStatement();
case SyntaxKind.TryKeyword:
return parseTryStatement();
case SyntaxKind.DebuggerKeyword:
return parseDebuggerStatement();
default:
if (isLabel()) {
return parseLabelledStatement();
}
return parseExpressionStatement();
}
}
function parseStatementOrFunction(): Statement {
return token === SyntaxKind.FunctionKeyword ? parseFunctionDeclaration() : parseStatement();
}
function parseAndCheckFunctionBody(isConstructor: boolean): Block {
var initialPosition = scanner.getTokenPos();
var errorCountBeforeBody = file.syntacticErrors.length;
if (token === SyntaxKind.OpenBraceToken) {
var body = parseBody(/* ignoreMissingOpenBrace */ false);
if (body && inAmbientContext && file.syntacticErrors.length === errorCountBeforeBody) {
var diagnostic = isConstructor ? Diagnostics.A_constructor_implementation_cannot_be_declared_in_an_ambient_context : Diagnostics.A_function_implementation_cannot_be_declared_in_an_ambient_context;
grammarErrorAtPos(initialPosition, 1, diagnostic);
}
return body;
}
if (canParseSemicolon()) {
parseSemicolon();
return undefined;
}
error(Diagnostics.Block_or_expected); // block or ';' expected
}
// DECLARATIONS
function parseVariableDeclaration(flags: NodeFlags, noIn?: boolean): VariableDeclaration {
var node = createNode(SyntaxKind.VariableDeclaration);
node.flags = flags;
var errorCountBeforeVariableDeclaration = file.syntacticErrors.length;
node.name = parseIdentifier();
node.type = parseTypeAnnotation();
// Issue any initializer-related errors on the equals token
var initializerStart = scanner.getTokenPos();
var initializerFirstTokenLength = scanner.getTextPos() - initializerStart;
node.initializer = parseInitializer(/*inParameter*/ false, noIn);
if (inAmbientContext && node.initializer && errorCountBeforeVariableDeclaration === file.syntacticErrors.length) {
grammarErrorAtPos(initializerStart, initializerFirstTokenLength, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
}
if (isInStrictMode && isEvalOrArgumentsIdentifier(node.name)) {
// It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code
// and its Identifier is eval or arguments
reportInvalidUseInStrictMode(node.name);
}
return finishNode(node);
}
function parseVariableDeclarationList(flags: NodeFlags, noIn?: boolean): NodeArray {
return parseDelimitedList(ParsingContext.VariableDeclarations, () => parseVariableDeclaration(flags, noIn), TrailingCommaBehavior.Disallow);
}
function parseVariableStatement(pos?: number, flags?: NodeFlags): VariableStatement {
var node = createNode(SyntaxKind.VariableStatement, pos);
if (flags) node.flags = flags;
var errorCountBeforeVarStatement = file.syntacticErrors.length;
parseExpected(SyntaxKind.VarKeyword);
node.declarations = parseVariableDeclarationList(flags, /*noIn*/false);
parseSemicolon();
if (!node.declarations.length && file.syntacticErrors.length === errorCountBeforeVarStatement) {
grammarErrorOnNode(node, Diagnostics.Variable_declaration_list_cannot_be_empty);
}
return finishNode(node);
}
function parseFunctionDeclaration(pos?: number, flags?: NodeFlags): FunctionDeclaration {
var node = createNode(SyntaxKind.FunctionDeclaration, pos);
if (flags) node.flags = flags;
parseExpected(SyntaxKind.FunctionKeyword);
node.name = parseIdentifier();
var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken);
node.typeParameters = sig.typeParameters;
node.parameters = sig.parameters;
node.type = sig.type;
node.body = parseAndCheckFunctionBody(/*isConstructor*/ false);
if (isInStrictMode && isEvalOrArgumentsIdentifier(node.name)) {
// It is a SyntaxError to use within strict mode code the identifiers eval or arguments as the
// Identifier of a FunctionDeclaration or FunctionExpression or as a formal parameter name(13.1)
reportInvalidUseInStrictMode(node.name);
}
return finishNode(node);
}
function parseConstructorDeclaration(pos: number, flags: NodeFlags): ConstructorDeclaration {
var node = createNode(SyntaxKind.Constructor, pos);
node.flags = flags;
parseExpected(SyntaxKind.ConstructorKeyword);
var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken);
node.typeParameters = sig.typeParameters;
node.parameters = sig.parameters;
node.type = sig.type;
node.body = parseAndCheckFunctionBody(/*isConstructor*/ true);
if (node.typeParameters) {
grammarErrorAtPos(node.typeParameters.pos, node.typeParameters.end - node.typeParameters.pos, Diagnostics.Type_parameters_cannot_appear_on_a_constructor_declaration);
}
if (node.type) {
grammarErrorOnNode(node.type, Diagnostics.Type_annotation_cannot_appear_on_a_constructor_declaration);
}
return finishNode(node);
}
function parsePropertyMemberDeclaration(pos: number, flags: NodeFlags): Declaration {
var errorCountBeforePropertyDeclaration = file.syntacticErrors.length;
var name = parsePropertyName();
var questionStart = scanner.getTokenPos();
if (parseOptional(SyntaxKind.QuestionToken)) {
errorAtPos(questionStart, scanner.getStartPos() - questionStart, Diagnostics.A_class_member_cannot_be_declared_optional);
}
if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) {
var method = createNode(SyntaxKind.Method, pos);
method.flags = flags;
method.name = name;
var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken);
method.typeParameters = sig.typeParameters;
method.parameters = sig.parameters;
method.type = sig.type;
method.body = parseAndCheckFunctionBody(/*isConstructor*/ false);
return finishNode(method);
}
else {
var property = createNode(SyntaxKind.Property, pos);
property.flags = flags;
property.name = name;
property.type = parseTypeAnnotation();
var initializerStart = scanner.getTokenPos();
var initializerFirstTokenLength = scanner.getTextPos() - initializerStart;
property.initializer = parseInitializer(/*inParameter*/ false);
parseSemicolon();
if (inAmbientContext && property.initializer && errorCountBeforePropertyDeclaration === file.syntacticErrors.length) {
grammarErrorAtPos(initializerStart, initializerFirstTokenLength, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
}
return finishNode(property);
}
}
function parseAndCheckMemberAccessorDeclaration(kind: SyntaxKind, pos: number, flags: NodeFlags): MethodDeclaration {
var errorCountBeforeAccessor = file.syntacticErrors.length;
var accessor = parseMemberAccessorDeclaration(kind, pos, flags);
if (errorCountBeforeAccessor === file.syntacticErrors.length) {
if (languageVersion < ScriptTarget.ES5) {
grammarErrorOnNode(accessor.name, Diagnostics.Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher);
}
else if (inAmbientContext) {
grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_be_declared_in_an_ambient_context);
}
else if (accessor.typeParameters) {
grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_have_type_parameters);
}
else if (kind === SyntaxKind.GetAccessor && accessor.parameters.length) {
grammarErrorOnNode(accessor.name, Diagnostics.A_get_accessor_cannot_have_parameters);
}
else if (kind === SyntaxKind.SetAccessor) {
if (accessor.type) {
grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_cannot_have_a_return_type_annotation);
}
else if (accessor.parameters.length !== 1) {
grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_must_have_exactly_one_parameter);
}
else {
var parameter = accessor.parameters[0];
if (parameter.flags & NodeFlags.Rest) {
grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_cannot_have_rest_parameter);
}
else if (parameter.flags & NodeFlags.Modifier) {
grammarErrorOnNode(accessor.name, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation);
}
else if (parameter.flags & NodeFlags.QuestionMark) {
grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_cannot_have_an_optional_parameter);
}
else if (parameter.initializer) {
grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_parameter_cannot_have_an_initializer);
}
}
}
}
return accessor;
}
function parseMemberAccessorDeclaration(kind: SyntaxKind, pos: number, flags: NodeFlags): MethodDeclaration {
var node = createNode(kind, pos);
node.flags = flags;
node.name = parsePropertyName();
var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken);
node.typeParameters = sig.typeParameters;
node.parameters = sig.parameters;
node.type = sig.type;
node.body = parseBody(/* ignoreMissingOpenBrace */ false);
return finishNode(node);
}
function isClassMemberStart(): boolean {
var idToken: SyntaxKind;
// Eat up all modifiers, but hold on to the last one in case it is actually an identifier.
while (isModifier(token)) {
idToken = token;
nextToken();
}
// Try to get the first property-like token following all modifiers.
// This can either be an identifier or the 'get' or 'set' keywords.
if (isPropertyName()) {
idToken = token;
nextToken();
}
// Index signatures are class members; we can parse.
if (token === SyntaxKind.OpenBracketToken) {
return true;
}
// If we were able to get any potential identifier...
if (idToken !== undefined) {
// If we have a non-keyword identifier, or if we have an accessor, then it's safe to parse.
if (!isKeyword(idToken) || idToken === SyntaxKind.SetKeyword || idToken === SyntaxKind.GetKeyword) {
return true;
}
// If it *is* a keyword, but not an accessor, check a little farther along
// to see if it should actually be parsed as a class member.
switch (token) {
case SyntaxKind.OpenParenToken: // Method declaration
case SyntaxKind.LessThanToken: // Generic Method declaration
case SyntaxKind.ColonToken: // Type Annotation for declaration
case SyntaxKind.EqualsToken: // Initializer for declaration
case SyntaxKind.QuestionToken: // Not valid, but permitted so that it gets caught later on.
return true;
default:
// Covers
// - Semicolons (declaration termination)
// - Closing braces (end-of-class, must be declaration)
// - End-of-files (not valid, but permitted so that it gets caught later on)
// - Line-breaks (enabling *automatic semicolon insertion*)
return canParseSemicolon();
}
}
return false;
}
function parseAndCheckModifiers(context: ModifierContext): number {
var flags = 0;
var lastStaticModifierStart: number;
var lastStaticModifierLength: number;
var lastDeclareModifierStart: number;
var lastDeclareModifierLength: number
var lastPrivateModifierStart: number;
var lastPrivateModifierLength: number;
while (true) {
var modifierStart = scanner.getTokenPos();
var modifierToken = token;
// Try to parse the modifier
if (!parseAnyContextualModifier()) break;
var modifierLength = scanner.getStartPos() - modifierStart;
switch (modifierToken) {
case SyntaxKind.PublicKeyword:
if (flags & NodeFlags.Private || flags & NodeFlags.Public) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics.Accessibility_modifier_already_seen);
}
else if (flags & NodeFlags.Static) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics._0_modifier_must_precede_1_modifier, "public", "static");
}
else if (context === ModifierContext.ModuleElements || context === ModifierContext.SourceElements) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics._0_modifier_cannot_appear_on_a_module_element, "public");
}
flags |= NodeFlags.Public;
break;
case SyntaxKind.PrivateKeyword:
if (flags & NodeFlags.Private || flags & NodeFlags.Public) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics.Accessibility_modifier_already_seen);
}
else if (flags & NodeFlags.Static) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics._0_modifier_must_precede_1_modifier, "private", "static");
}
else if (context === ModifierContext.ModuleElements || context === ModifierContext.SourceElements) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics._0_modifier_cannot_appear_on_a_module_element, "private");
}
lastPrivateModifierStart = modifierStart;
lastPrivateModifierLength = modifierLength;
flags |= NodeFlags.Private;
break;
case SyntaxKind.StaticKeyword:
if (flags & NodeFlags.Static) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics._0_modifier_already_seen, "static");
}
else if (context === ModifierContext.ModuleElements || context === ModifierContext.SourceElements) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics._0_modifier_cannot_appear_on_a_module_element, "static");
}
else if (context === ModifierContext.Parameters) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "static");
}
lastStaticModifierStart = modifierStart;
lastStaticModifierLength = modifierLength;
flags |= NodeFlags.Static;
break;
case SyntaxKind.ExportKeyword:
if (flags & NodeFlags.Export) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics._0_modifier_already_seen, "export");
}
else if (flags & NodeFlags.Ambient) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics._0_modifier_must_precede_1_modifier, "export", "declare");
}
else if (context === ModifierContext.ClassMembers) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "export");
}
else if (context === ModifierContext.Parameters) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "export");
}
flags |= NodeFlags.Export;
break;
case SyntaxKind.DeclareKeyword:
if (flags & NodeFlags.Ambient) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics._0_modifier_already_seen, "declare");
}
else if (context === ModifierContext.ClassMembers) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "declare");
}
else if (context === ModifierContext.Parameters) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "declare");
}
else if (inAmbientContext && context === ModifierContext.ModuleElements) {
grammarErrorAtPos(modifierStart, modifierLength, Diagnostics.A_declare_modifier_cannot_be_used_in_an_already_ambient_context);
}
lastDeclareModifierStart = modifierStart;
lastDeclareModifierLength = modifierLength;
flags |= NodeFlags.Ambient;
break;
}
}
if (token === SyntaxKind.ConstructorKeyword && flags & NodeFlags.Static) {
grammarErrorAtPos(lastStaticModifierStart, lastStaticModifierLength, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "static");
}
else if (token === SyntaxKind.ConstructorKeyword && flags & NodeFlags.Private) {
grammarErrorAtPos(lastPrivateModifierStart, lastPrivateModifierLength, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "private");
}
else if (token === SyntaxKind.ImportKeyword) {
if (flags & NodeFlags.Ambient) {
grammarErrorAtPos(lastDeclareModifierStart, lastDeclareModifierLength, Diagnostics.A_declare_modifier_cannot_be_used_with_an_import_declaration, "declare");
}
}
else if (token === SyntaxKind.InterfaceKeyword) {
if (flags & NodeFlags.Ambient) {
grammarErrorAtPos(lastDeclareModifierStart, lastDeclareModifierLength, Diagnostics.A_declare_modifier_cannot_be_used_with_an_interface_declaration, "declare");
}
}
else if (token !== SyntaxKind.ExportKeyword && !(flags & NodeFlags.Ambient) && inAmbientContext && context === ModifierContext.SourceElements) {
// A declare modifier is required for any top level .d.ts declaration except export=, interfaces and imports:
// categories:
//
// DeclarationElement:
// ExportAssignment
// export_opt InterfaceDeclaration
// export_opt ImportDeclaration
// export_opt ExternalImportDeclaration
// export_opt AmbientDeclaration
//
var declarationStart = scanner.getTokenPos();
var declarationFirstTokenLength = scanner.getTextPos() - declarationStart;
grammarErrorAtPos(declarationStart, declarationFirstTokenLength, Diagnostics.A_declare_modifier_is_required_for_a_top_level_declaration_in_a_d_ts_file);
}
return flags;
}
function parseClassMemberDeclaration(): Declaration {
var pos = getNodePos();
var flags = parseAndCheckModifiers(ModifierContext.ClassMembers);
if (parseContextualModifier(SyntaxKind.GetKeyword)) {
return parseAndCheckMemberAccessorDeclaration(SyntaxKind.GetAccessor, pos, flags);
}
if (parseContextualModifier(SyntaxKind.SetKeyword)) {
return parseAndCheckMemberAccessorDeclaration(SyntaxKind.SetAccessor, pos, flags);
}
if (token === SyntaxKind.ConstructorKeyword) {
return parseConstructorDeclaration(pos, flags);
}
if (token >= SyntaxKind.Identifier || token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral) {
return parsePropertyMemberDeclaration(pos, flags);
}
if (token === SyntaxKind.OpenBracketToken) {
if (flags) {
var start = getTokenPos(pos);
var length = getNodePos() - start;
errorAtPos(start, length, Diagnostics.Modifiers_not_permitted_on_index_signature_members);
}
return parseIndexSignatureMember();
}
// 'isClassMemberStart' should have hinted not to attempt parsing.
Debug.fail("Should not have attempted to parse class member declaration.");
}
function parseClassDeclaration(pos: number, flags: NodeFlags): ClassDeclaration {
var node = createNode(SyntaxKind.ClassDeclaration, pos);
node.flags = flags;
var errorCountBeforeClassDeclaration = file.syntacticErrors.length;
parseExpected(SyntaxKind.ClassKeyword);
node.name = parseIdentifier();
node.typeParameters = parseTypeParameters();
// TODO(jfreeman): Parse arbitrary sequence of heritage clauses and error for order and duplicates
node.baseType = parseOptional(SyntaxKind.ExtendsKeyword) ? parseTypeReference() : undefined;
var implementsKeywordStart = scanner.getTokenPos();
var implementsKeywordLength: number;
if (parseOptional(SyntaxKind.ImplementsKeyword)) {
implementsKeywordLength = scanner.getStartPos() - implementsKeywordStart;
node.implementedTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, parseTypeReference, TrailingCommaBehavior.Disallow);
}
var errorCountBeforeClassBody = file.syntacticErrors.length;
if (parseExpected(SyntaxKind.OpenBraceToken)) {
node.members = parseList(ParsingContext.ClassMembers, /*checkForStrictMode*/ false, parseClassMemberDeclaration);
parseExpected(SyntaxKind.CloseBraceToken);
}
else {
node.members = createMissingList();
}
if (node.implementedTypes && !node.implementedTypes.length && errorCountBeforeClassBody === errorCountBeforeClassDeclaration) {
grammarErrorAtPos(implementsKeywordStart, implementsKeywordLength, Diagnostics._0_list_cannot_be_empty, "implements");
}
return finishNode(node);
}
function parseInterfaceDeclaration(pos: number, flags: NodeFlags): InterfaceDeclaration {
var node = createNode(SyntaxKind.InterfaceDeclaration, pos);
node.flags = flags;
var errorCountBeforeInterfaceDeclaration = file.syntacticErrors.length;
parseExpected(SyntaxKind.InterfaceKeyword);
node.name = parseIdentifier();
node.typeParameters = parseTypeParameters();
// TODO(jfreeman): Parse arbitrary sequence of heritage clauses and error for order and duplicates
var extendsKeywordStart = scanner.getTokenPos();
var extendsKeywordLength: number;
if (parseOptional(SyntaxKind.ExtendsKeyword)) {
extendsKeywordLength = scanner.getStartPos() - extendsKeywordStart;
node.baseTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, parseTypeReference, TrailingCommaBehavior.Disallow);
}
var errorCountBeforeInterfaceBody = file.syntacticErrors.length;
node.members = parseTypeLiteral().members;
if (node.baseTypes && !node.baseTypes.length && errorCountBeforeInterfaceBody === errorCountBeforeInterfaceDeclaration) {
grammarErrorAtPos(extendsKeywordStart, extendsKeywordLength, Diagnostics._0_list_cannot_be_empty, "extends");
}
return finishNode(node);
}
function parseAndCheckEnumDeclaration(pos: number, flags: NodeFlags): EnumDeclaration {
function isIntegerLiteral(expression: Expression): boolean {
function isInteger(literalExpression: LiteralExpression): boolean {
// Allows for scientific notation since literalExpression.text was formed by
// coercing a number to a string. Sometimes this coersion can yield a string
// in scientific notation.
// We also don't need special logic for hex because a hex integer is converted
// to decimal when it is coerced.
return /^[0-9]+([eE]\+?[0-9]+)?$/.test(literalExpression.text);
}
if (expression.kind === SyntaxKind.PrefixOperator) {
var unaryExpression = expression;
if (unaryExpression.operator === SyntaxKind.PlusToken || unaryExpression.operator === SyntaxKind.MinusToken) {
expression = unaryExpression.operand;
}
}
if (expression.kind === SyntaxKind.NumericLiteral) {
return isInteger(expression);
}
return false;
}
var inConstantEnumMemberSection = true;
// In an ambient declaration, the grammar only allows integer literals as initializers.
// In a nonambient declaration, the grammar allows uninitialized members only in a
// ConstantEnumMemberSection, which starts at the beginning of an enum declaration
// or any time an integer literal initializer is encountered.
function parseAndCheckEnumMember(): EnumMember {
var node = createNode(SyntaxKind.EnumMember);
var errorCountBeforeEnumMember = file.syntacticErrors.length;
node.name = parsePropertyName();
node.initializer = parseInitializer(/*inParameter*/ false);
if (inAmbientContext) {
if (node.initializer && !isIntegerLiteral(node.initializer) && errorCountBeforeEnumMember === file.syntacticErrors.length) {
grammarErrorOnNode(node.name, Diagnostics.Ambient_enum_elements_can_only_have_integer_literal_initializers);
}
}
else if (node.initializer) {
inConstantEnumMemberSection = isIntegerLiteral(node.initializer);
}
else if (!inConstantEnumMemberSection && errorCountBeforeEnumMember === file.syntacticErrors.length) {
grammarErrorOnNode(node.name, Diagnostics.Enum_member_must_have_initializer);
}
return finishNode(node);
}
var node = createNode(SyntaxKind.EnumDeclaration, pos);
node.flags = flags;
parseExpected(SyntaxKind.EnumKeyword);
node.name = parseIdentifier();
if (parseExpected(SyntaxKind.OpenBraceToken)) {
node.members = parseDelimitedList(ParsingContext.EnumMembers, parseAndCheckEnumMember, TrailingCommaBehavior.Allow);
parseExpected(SyntaxKind.CloseBraceToken);
}
else {
node.members = createMissingList();
}
return finishNode(node);
}
function parseModuleBody(): Block {
var node = createNode(SyntaxKind.ModuleBlock);
if (parseExpected(SyntaxKind.OpenBraceToken)) {
node.statements = parseList(ParsingContext.ModuleElements, /*checkForStrictMode*/ false, parseModuleElement);
parseExpected(SyntaxKind.CloseBraceToken);
}
else {
node.statements = createMissingList();
}
return finishNode(node);
}
function parseInternalModuleTail(pos: number, flags: NodeFlags): ModuleDeclaration {
var node = createNode(SyntaxKind.ModuleDeclaration, pos);
node.flags = flags;
node.name = parseIdentifier();
if (parseOptional(SyntaxKind.DotToken)) {
node.body = parseInternalModuleTail(getNodePos(), NodeFlags.Export);
}
else {
node.body = parseModuleBody();
forEach((node.body).statements, s => {
if (s.kind === SyntaxKind.ExportAssignment) {
// Export assignments are not allowed in an internal module
grammarErrorOnNode(s, Diagnostics.An_export_assignment_cannot_be_used_in_an_internal_module);
}
else if (s.kind === SyntaxKind.ImportDeclaration && (s).externalModuleName) {
grammarErrorOnNode(s, Diagnostics.Import_declarations_in_an_internal_module_cannot_reference_an_external_module)
}
});
}
return finishNode(node);
}
function parseAmbientExternalModuleDeclaration(pos: number, flags: NodeFlags): ModuleDeclaration {
var node = createNode(SyntaxKind.ModuleDeclaration, pos);
node.flags = flags;
node.name = parseStringLiteral();
if (!inAmbientContext) {
var errorCount = file.syntacticErrors.length;
// Only report this error if we have not already errored about a missing declare modifier,
// which would have been at or after pos
if (!errorCount || file.syntacticErrors[errorCount - 1].start < getTokenPos(pos)) {
grammarErrorOnNode(node.name, Diagnostics.Only_ambient_modules_can_use_quoted_names);
}
}
// For error recovery, just in case the user forgot the declare modifier on this ambient
// external module, treat it as ambient anyway.
var saveInAmbientContext = inAmbientContext;
inAmbientContext = true;
node.body = parseModuleBody();
inAmbientContext = saveInAmbientContext;
return finishNode(node);
}
function parseModuleDeclaration(pos: number, flags: NodeFlags): ModuleDeclaration {
parseExpected(SyntaxKind.ModuleKeyword);
return token === SyntaxKind.StringLiteral ? parseAmbientExternalModuleDeclaration(pos, flags) : parseInternalModuleTail(pos, flags);
}
function parseImportDeclaration(pos: number, flags: NodeFlags): ImportDeclaration {
var node = createNode(SyntaxKind.ImportDeclaration, pos);
node.flags = flags;
parseExpected(SyntaxKind.ImportKeyword);
node.name = parseIdentifier();
parseExpected(SyntaxKind.EqualsToken);
var entityName = parseEntityName(/*allowReservedWords*/ false);
if (entityName.kind === SyntaxKind.Identifier && (entityName).text === "require" && parseOptional(SyntaxKind.OpenParenToken)) {
node.externalModuleName = parseStringLiteral();
parseExpected(SyntaxKind.CloseParenToken);
}
else {
node.entityName = entityName;
}
parseSemicolon();
return finishNode(node);
}
function parseExportAssignmentTail(pos: number): ExportAssignment {
var node = createNode(SyntaxKind.ExportAssignment, pos);
node.exportName = parseIdentifier();
parseSemicolon();
return finishNode(node);
}
function isDeclaration() {
switch (token) {
case SyntaxKind.VarKeyword:
case SyntaxKind.FunctionKeyword:
return true;
case SyntaxKind.ClassKeyword:
case SyntaxKind.InterfaceKeyword:
case SyntaxKind.EnumKeyword:
case SyntaxKind.ImportKeyword:
// Not true keywords so ensure an identifier follows
return lookAhead(() => nextToken() >= SyntaxKind.Identifier);
case SyntaxKind.ModuleKeyword:
// Not a true keyword so ensure an identifier or string literal follows
return lookAhead(() => nextToken() >= SyntaxKind.Identifier || token === SyntaxKind.StringLiteral);
case SyntaxKind.ExportKeyword:
// Check for export assignment or modifier on source element
return lookAhead(() => nextToken() === SyntaxKind.EqualsToken || isDeclaration());
case SyntaxKind.DeclareKeyword:
case SyntaxKind.PublicKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.StaticKeyword:
// Check for modifier on source element
return lookAhead(() => { nextToken(); return isDeclaration(); });
}
}
function parseDeclaration(modifierContext: ModifierContext): Statement {
var pos = getNodePos();
var errorCountBeforeModifiers = file.syntacticErrors.length;
var flags = parseAndCheckModifiers(modifierContext);
if (token === SyntaxKind.ExportKeyword) {
var modifiersEnd = scanner.getStartPos();
nextToken();
if (parseOptional(SyntaxKind.EqualsToken)) {
var exportAssignmentTail = parseExportAssignmentTail(pos);
if (flags !== 0 && errorCountBeforeModifiers === file.syntacticErrors.length) {
var modifiersStart = skipTrivia(sourceText, pos);
grammarErrorAtPos(modifiersStart, modifiersEnd - modifiersStart, Diagnostics.An_export_assignment_cannot_have_modifiers);
}
return exportAssignmentTail;
}
}
var saveInAmbientContext = inAmbientContext;
if (flags & NodeFlags.Ambient) {
inAmbientContext = true;
}
var result: Declaration;
switch (token) {
case SyntaxKind.VarKeyword:
result = parseVariableStatement(pos, flags);
break;
case SyntaxKind.FunctionKeyword:
result = parseFunctionDeclaration(pos, flags);
break;
case SyntaxKind.ClassKeyword:
result = parseClassDeclaration(pos, flags);
break;
case SyntaxKind.InterfaceKeyword:
result = parseInterfaceDeclaration(pos, flags);
break;
case SyntaxKind.EnumKeyword:
result = parseAndCheckEnumDeclaration(pos, flags);
break;
case SyntaxKind.ModuleKeyword:
result = parseModuleDeclaration(pos, flags);
break;
case SyntaxKind.ImportKeyword:
result = parseImportDeclaration(pos, flags);
break;
default:
error(Diagnostics.Declaration_expected);
}
inAmbientContext = saveInAmbientContext;
return result;
}
function isSourceElement(inErrorRecovery: boolean): boolean {
return isDeclaration() || isStatement(inErrorRecovery);
}
function parseSourceElement() {
return parseSourceElementOrModuleElement(ModifierContext.SourceElements);
}
function parseModuleElement() {
return parseSourceElementOrModuleElement(ModifierContext.ModuleElements);
}
function parseSourceElementOrModuleElement(modifierContext: ModifierContext): Statement {
if (isDeclaration()) {
return parseDeclaration(modifierContext);
}
var statementStart = scanner.getTokenPos();
var statementFirstTokenLength = scanner.getTextPos() - statementStart;
var errorCountBeforeStatement = file.syntacticErrors.length;
var statement = parseStatement();
if (inAmbientContext && file.syntacticErrors.length === errorCountBeforeStatement) {
grammarErrorAtPos(statementStart, statementFirstTokenLength, Diagnostics.Statements_are_not_allowed_in_ambient_contexts);
}
return statement;
}
function processReferenceComments(): ReferenceComments {
var referencedFiles: FileReference[] = [];
var amdDependencies: string[] = [];
commentRanges = [];
token = scanner.scan();
for (var i = 0; i < commentRanges.length; i++) {
var range = commentRanges[i];
var comment = sourceText.substring(range.pos, range.end);
var simpleReferenceRegEx = /^\/\/\/\s*/gim;
if (isNoDefaultLibRegEx.exec(comment)) {
file.hasNoDefaultLib = true;
}
else {
var fullReferenceRegEx = /^(\/\/\/\s*/;
var matchResult = fullReferenceRegEx.exec(comment);
if (!matchResult) {
var start = range.pos;
var length = range.end - start;
errorAtPos(start, length, Diagnostics.Invalid_reference_comment);
}
else {
referencedFiles.push({
pos: range.pos,
end: range.end,
filename: matchResult[3]
});
}
}
}
else {
var amdDependencyRegEx = /^\/\/\/\s*
node.flags & NodeFlags.Export
|| node.kind === SyntaxKind.ImportDeclaration && (node).externalModuleName
|| node.kind === SyntaxKind.ExportAssignment
? node
: undefined);
}
scanner = createScanner(languageVersion, sourceText, scanError, onComment);
var rootNodeFlags: NodeFlags = 0;
if (fileExtensionIs(filename, ".d.ts")) {
rootNodeFlags = NodeFlags.DeclarationFile;
inAmbientContext = true;
}
file = createRootNode(SyntaxKind.SourceFile, 0, sourceText.length, rootNodeFlags);
file.filename = normalizePath(filename);
file.text = sourceText;
file.getLineAndCharacterFromPosition = getLineAndCharacterlFromSourcePosition;
file.syntacticErrors = [];
file.semanticErrors = [];
var referenceComments = processReferenceComments();
file.referencedFiles = referenceComments.referencedFiles;
file.amdDependencies = referenceComments.amdDependencies;
file.statements = parseList(ParsingContext.SourceElements, /*checkForStrictMode*/ true, parseSourceElement);
file.externalModuleIndicator = getExternalModuleIndicator();
file.nodeCount = nodeCount;
file.identifierCount = identifierCount;
return file;
}
export function createProgram(rootNames: string[], options: CompilerOptions, host: CompilerHost): Program {
var program: Program;
var files: SourceFile[] = [];
var filesByName: Map = {};
var errors: Diagnostic[] = [];
var seenNoDefaultLib = options.noLib;
var commonSourceDirectory: string;
forEach(rootNames, name => processRootFile(name, false));
if (!seenNoDefaultLib) processRootFile(host.getDefaultLibFilename(), true);
verifyCompilerOptions();
errors.sort(compareDiagnostics);
program = {
getSourceFile: getSourceFile,
getSourceFiles: () => files,
getCompilerOptions: () => options,
getCompilerHost: () => host,
getDiagnostics: getDiagnostics,
getGlobalDiagnostics: getGlobalDiagnostics,
getTypeChecker: () => createTypeChecker(program),
getCommonSourceDirectory: () => commonSourceDirectory,
};
return program;
function getSourceFile(filename: string) {
filename = host.getCanonicalFileName(filename);
return hasProperty(filesByName, filename) ? filesByName[filename] : undefined;
}
function getDiagnostics(sourceFile?: SourceFile): Diagnostic[] {
return sourceFile ? filter(errors, e => e.file === sourceFile) : errors;
}
function getGlobalDiagnostics(): Diagnostic[] {
return filter(errors, e => !e.file);
}
function addExtension(filename: string, extension: string): string {
return getBaseFilename(filename).indexOf(".") >= 0 ? filename : filename + extension;
}
function processRootFile(filename: string, isDefaultLib: boolean) {
processSourceFile(normalizePath(addExtension(filename, ".ts")), isDefaultLib);
}
function processSourceFile(filename: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) {
if (refEnd !== undefined && refPos !== undefined) {
var start = refPos;
var length = refEnd - refPos;
}
if (!fileExtensionIs(filename, ".ts")) {
errors.push(createFileDiagnostic(refFile, start, length, Diagnostics.File_0_must_have_extension_ts_or_d_ts, filename));
}
else if (!findSourceFile(filename, isDefaultLib, refFile, refPos, refEnd)) {
errors.push(createFileDiagnostic(refFile, start, length, Diagnostics.File_0_not_found, filename));
}
}
// Get source file from normalized filename
function findSourceFile(filename: string, isDefaultLib: boolean, refFile?: SourceFile, refStart?: number, refLength?: number): SourceFile {
// Look through existing source files to see if we've encountered it.
var canonicalName = host.getCanonicalFileName(filename);
var file = getSourceFile(filename);
if (file) {
if (host.useCaseSensitiveFileNames() && canonicalName !== file.filename) {
errors.push(createFileDiagnostic(refFile, refStart, refLength,
Diagnostics.Filename_0_differs_from_already_included_filename_1_only_in_casing, filename, file.filename));
}
}
else {
// If we haven't, read the file.
file = host.getSourceFile(filename, options.target, hostErrorMessage => {
errors.push(createFileDiagnostic(refFile, refStart, refLength,
Diagnostics.Cannot_read_file_0_Colon_1, filename, hostErrorMessage));
});
if (file) {
filesByName[host.getCanonicalFileName(filename)] = file;
seenNoDefaultLib = seenNoDefaultLib || file.hasNoDefaultLib;
if (!options.noResolve) {
var basePath = getDirectoryPath(filename);
processReferencedFiles(file, basePath);
processImportedModules(file, basePath);
}
if (isDefaultLib) {
files.unshift(file);
}
else {
files.push(file);
}
forEach(file.syntacticErrors, e => {
errors.push(e);
});
}
}
return file;
}
function processReferencedFiles(file: SourceFile, basePath: string) {
forEach(file.referencedFiles, ref => {
processSourceFile(normalizePath(combinePaths(basePath, ref.filename)), false, file, ref.pos, ref.end);
});
}
function processImportedModules(file: SourceFile, basePath: string) {
forEach(file.statements, node => {
if (node.kind === SyntaxKind.ImportDeclaration && (node).externalModuleName) {
var nameLiteral = (node).externalModuleName
var moduleName = nameLiteral.text;
if (moduleName) {
var searchPath = basePath;
while (true) {
var searchName = normalizePath(combinePaths(searchPath, moduleName));
if (findModuleSourceFile(searchName + ".ts", nameLiteral) || findModuleSourceFile(searchName + ".d.ts", nameLiteral)) break;
var parentPath = getDirectoryPath(searchPath);
if (parentPath === searchPath) break;
searchPath = parentPath;
}
}
}
else if (node.kind === SyntaxKind.ModuleDeclaration && (node).name.kind === SyntaxKind.StringLiteral && (node.flags & NodeFlags.Ambient || file.flags & NodeFlags.DeclarationFile)) {
// TypeScript 1.0 spec (April 2014): 12.1.6
// An AmbientExternalModuleDeclaration declares an external module.
// This type of declaration is permitted only in the global module.
// The StringLiteral must specify a top - level external module name.
// Relative external module names are not permitted
forEachChild((node).body, node => {
if (node.kind === SyntaxKind.ImportDeclaration && (node).externalModuleName) {
var nameLiteral = (node).externalModuleName;
var moduleName = nameLiteral.text;
if (moduleName) {
// TypeScript 1.0 spec (April 2014): 12.1.6
// An ExternalImportDeclaration in anAmbientExternalModuleDeclaration may reference other external modules
// only through top - level external module names. Relative external module names are not permitted.
var searchName = normalizePath(combinePaths(basePath, moduleName));
var tsFile = findModuleSourceFile(searchName + ".ts", nameLiteral);
if (!tsFile) {
findModuleSourceFile(searchName + ".d.ts", nameLiteral);
}
}
}
})
}
});
function findModuleSourceFile(filename: string, nameLiteral: LiteralExpression) {
return findSourceFile(filename, /* isDefaultLib */ false, file, nameLiteral.pos, nameLiteral.end - nameLiteral.pos);
}
}
function verifyCompilerOptions() {
if (!options.sourceMap && (options.mapRoot || options.sourceRoot)) {
// Error to specify --mapRoot or --sourceRoot without mapSourceFiles
if (options.mapRoot) {
errors.push(createCompilerDiagnostic(Diagnostics.Option_mapRoot_cannot_be_specified_without_specifying_sourcemap_option));
}
if (options.sourceRoot) {
errors.push(createCompilerDiagnostic(Diagnostics.Option_sourceRoot_cannot_be_specified_without_specifying_sourcemap_option));
}
return;
}
var firstExternalModule = forEach(files, f => isExternalModule(f) ? f : undefined);
if (firstExternalModule && options.module === ModuleKind.None) {
// We cannot use createDiagnosticFromNode because nodes do not have parents yet
var externalModuleErrorSpan = getErrorSpanForNode(firstExternalModule.externalModuleIndicator);
var errorStart = skipTrivia(firstExternalModule.text, externalModuleErrorSpan.pos);
var errorLength = externalModuleErrorSpan.end - errorStart;
errors.push(createFileDiagnostic(firstExternalModule, errorStart, errorLength, Diagnostics.Cannot_compile_external_modules_unless_the_module_flag_is_provided));
}
// there has to be common source directory if user specified --outdir || --sourcRoot
// if user specified --mapRoot, there needs to be common source directory if there would be multiple files being emitted
if (options.outDir || // there is --outDir specified
options.sourceRoot || // there is --sourceRoot specified
(options.mapRoot && // there is --mapRoot Specified and there would be multiple js files generated
(!options.out || firstExternalModule !== undefined))) {
var commonPathComponents: string[];
forEach(files, sourceFile => {
// Each file contributes into common source file path
if (!(sourceFile.flags & NodeFlags.DeclarationFile)
&& !fileExtensionIs(sourceFile.filename, ".js")) {
var sourcePathCompoments = getNormalizedPathComponents(sourceFile.filename, host.getCurrentDirectory());
sourcePathCompoments.pop(); // FileName is not part of directory
if (commonPathComponents) {
for (var i = 0; i < Math.min(commonPathComponents.length, sourcePathCompoments.length); i++) {
if (commonPathComponents[i] !== sourcePathCompoments[i]) {
if (i === 0) {
errors.push(createCompilerDiagnostic(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files));
return;
}
// New common path found that is 0 -> i-1
commonPathComponents.length = i;
break;
}
}
// If the fileComponent path completely matched and less than already found update the length
if (sourcePathCompoments.length < commonPathComponents.length) {
commonPathComponents.length = sourcePathCompoments.length;
}
}
else {
// first file
commonPathComponents = sourcePathCompoments;
}
}
});
commonSourceDirectory = getNormalizedPathFromPathCompoments(commonPathComponents);
if (commonSourceDirectory) {
// Make sure directory path ends with directory separator so this string can directly
// used to replace with "" to get the relative path of the source file and the relative path doesnt
// start with / making it rooted path
commonSourceDirectory += directorySeparator;
}
}
}
}
}