assemblyscript/src/parser.ts

2842 lines
76 KiB
TypeScript
Raw Normal View History

2017-09-28 13:08:25 +02:00
/*
2018-02-25 00:13:39 +01:00
This is a custom parser specifically written for the AssemblyScript subset. It accepts some of the
most common TypeScript-only patterns that it knows an appropriate error message for but, though it
uses TypeScript's codes for diagnostics, doesn't ultimately aim at full compatibility.
2017-09-28 13:08:25 +02:00
*/
import {
Program,
2018-02-17 11:09:22 +01:00
LIBRARY_PREFIX,
PATH_DELIMITER
2017-12-24 03:19:47 +01:00
} from "./program";
2017-09-28 13:08:25 +02:00
2017-12-24 03:19:47 +01:00
import {
Tokenizer,
Token,
Range
} from "./tokenizer";
import {
DiagnosticCode,
DiagnosticEmitter
} from "./diagnostics";
import {
normalize as normalizePath
} from "./util/path";
import {
2018-02-25 00:13:39 +01:00
Node,
2017-09-28 13:08:25 +02:00
NodeKind,
2017-10-02 12:52:15 +02:00
Source,
SourceKind,
2017-09-28 13:08:25 +02:00
TypeNode,
Expression,
2017-09-28 13:08:25 +02:00
AssertionKind,
2017-12-13 23:24:13 +01:00
CallExpression,
2017-09-28 13:08:25 +02:00
IdentifierExpression,
StringLiteralExpression,
Statement,
2017-09-28 13:08:25 +02:00
BlockStatement,
BreakStatement,
2017-09-28 13:08:25 +02:00
ClassDeclaration,
ContinueStatement,
2017-12-13 23:24:13 +01:00
DeclarationStatement,
Decorator,
2017-09-28 13:08:25 +02:00
DoStatement,
EnumDeclaration,
EnumValueDeclaration,
ExportImportStatement,
ExportMember,
ExportStatement,
ExpressionStatement,
ForStatement,
FunctionExpression,
2017-09-28 13:08:25 +02:00
FunctionDeclaration,
IfStatement,
ImportDeclaration,
ImportStatement,
Modifier,
ModifierKind,
2017-12-13 23:24:13 +01:00
NamespaceDeclaration,
2017-09-28 13:08:25 +02:00
Parameter,
ParameterKind,
2017-09-28 13:08:25 +02:00
ReturnStatement,
SwitchCase,
SwitchStatement,
ThrowStatement,
TryStatement,
TypeDeclaration,
2017-09-28 13:08:25 +02:00
TypeParameter,
VariableStatement,
VariableDeclaration,
WhileStatement,
2017-12-18 03:46:36 +01:00
addModifier,
getModifier,
hasModifier,
setReusableModifiers
2018-02-25 00:13:39 +01:00
2017-09-28 13:08:25 +02:00
} from "./ast";
2017-12-18 03:46:36 +01:00
/** Parser interface. */
2017-09-28 13:08:25 +02:00
export class Parser extends DiagnosticEmitter {
2017-12-18 03:46:36 +01:00
/** Program being created. */
2017-09-28 13:08:25 +02:00
program: Program;
2017-12-18 03:46:36 +01:00
/** Log of source file names to be requested. */
2017-09-28 13:08:25 +02:00
backlog: string[] = new Array();
2017-12-18 03:46:36 +01:00
/** Log of source file names already processed. */
seenlog: Set<string> = new Set();
2017-09-28 13:08:25 +02:00
2017-12-18 03:46:36 +01:00
/** Constructs a new parser. */
2017-09-28 13:08:25 +02:00
constructor() {
super();
this.program = new Program(this.diagnostics);
}
2017-12-18 03:46:36 +01:00
/** Parses a file and adds its definitions to the program. */
2018-02-25 00:13:39 +01:00
parseFile(
text: string,
path: string,
isEntry: bool
): void {
// check if already parsed
var normalizedPath = normalizePath(path);
2018-02-25 00:13:39 +01:00
for (var i = 0, k = this.program.sources.length; i < k; ++i) {
if (this.program.sources[i].normalizedPath == normalizedPath) return;
}
2017-10-02 12:52:15 +02:00
this.seenlog.add(normalizedPath);
2018-02-25 00:13:39 +01:00
// create the source element
var source = new Source(
normalizedPath,
text,
isEntry
? SourceKind.ENTRY
: path.startsWith(LIBRARY_PREFIX) && path.indexOf(PATH_DELIMITER, LIBRARY_PREFIX.length) < 0
? SourceKind.LIBRARY
: SourceKind.DEFAULT
);
2017-09-28 13:08:25 +02:00
this.program.sources.push(source);
2018-02-25 00:13:39 +01:00
// tokenize and parse
var tn = new Tokenizer(source, this.program.diagnostics);
2017-09-28 13:08:25 +02:00
source.tokenizer = tn;
while (!tn.skip(Token.ENDOFFILE)) {
var statement = this.parseTopLevelStatement(tn);
if (statement) {
statement.parent = source;
source.statements.push(statement);
}
}
2018-02-25 00:13:39 +01:00
tn.finish();
}
2017-09-28 13:08:25 +02:00
2018-02-25 00:13:39 +01:00
/** Parses a top-level statement. */
parseTopLevelStatement(
tn: Tokenizer,
isNamespaceMember: bool = false
): Statement | null {
2017-10-02 12:52:15 +02:00
2018-02-25 00:13:39 +01:00
// check decorators
var decorators: Decorator[] | null = null;
while (tn.skip(Token.AT)) {
var decorator = this.parseDecorator(tn);
2018-02-25 00:13:39 +01:00
if (!decorator) break;
if (!decorators) decorators = [];
decorators.push(decorator);
}
2017-09-28 13:08:25 +02:00
2018-02-25 00:13:39 +01:00
// check modifiers
var modifiers: Modifier[] | null = null;
2018-02-25 00:13:39 +01:00
if (tn.skip(Token.EXPORT)) {
2017-12-16 17:54:53 +01:00
modifiers = addModifier(Node.createModifier(ModifierKind.EXPORT, tn.range()), modifiers);
2018-02-25 00:13:39 +01:00
}
if (tn.skip(Token.DECLARE)) {
2017-12-16 17:54:53 +01:00
modifiers = addModifier(Node.createModifier(ModifierKind.DECLARE, tn.range()), modifiers);
tn.peek(true);
2018-02-25 00:13:39 +01:00
if (tn.nextTokenOnNewLine) {
this.error(
DiagnosticCode.Line_break_not_permitted_here,
tn.range(tn.pos)
); // recoverable, compatibility
}
}
2017-09-28 13:08:25 +02:00
2018-02-25 00:13:39 +01:00
// remember where we took off
tn.mark();
2017-09-28 13:08:25 +02:00
2018-02-25 00:13:39 +01:00
// parse the statement
var statement: Statement | null = null;
var modifier: Modifier | null;
2018-02-25 00:13:39 +01:00
// handle declarations
switch (tn.next()) {
2017-09-28 13:08:25 +02:00
case Token.CONST:
2017-12-16 17:54:53 +01:00
modifiers = addModifier(Node.createModifier(ModifierKind.CONST, tn.range()), modifiers);
if (tn.skip(Token.ENUM)) {
statement = this.parseEnum(tn, modifiers, decorators);
2017-09-28 13:08:25 +02:00
break;
}
statement = this.parseVariable(tn, modifiers, decorators);
decorators = null;
break;
2017-09-28 13:08:25 +02:00
case Token.LET:
modifiers = addModifier(Node.createModifier(ModifierKind.LET, tn.range()), modifiers);
2018-02-25 00:13:39 +01:00
// fall-through
case Token.VAR:
statement = this.parseVariable(tn, modifiers, decorators);
decorators = null;
break;
2017-09-28 13:08:25 +02:00
case Token.ENUM:
statement = this.parseEnum(tn, modifiers, decorators);
decorators = null;
break;
2017-09-28 13:08:25 +02:00
case Token.FUNCTION:
statement = this.parseFunction(tn, modifiers, decorators);
decorators = null;
break;
2017-09-28 13:08:25 +02:00
case Token.ABSTRACT:
if (!tn.skip(Token.CLASS)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(tn.pos), "class"
);
2017-09-28 13:08:25 +02:00
break;
}
2018-02-25 00:13:39 +01:00
modifiers = addModifier(
Node.createModifier(ModifierKind.ABSTRACT, tn.range()), modifiers
);
// fall through
2017-09-28 13:08:25 +02:00
case Token.CLASS:
statement = this.parseClass(tn, modifiers, decorators);
decorators = null;
break;
2017-09-28 13:08:25 +02:00
case Token.NAMESPACE:
statement = this.parseNamespace(tn, modifiers, decorators);
decorators = null;
break;
2017-09-28 13:08:25 +02:00
case Token.IMPORT:
2017-12-18 03:46:36 +01:00
if (modifier = getModifier(ModifierKind.EXPORT, modifiers)) {
statement = this.parseExportImport(tn, modifier.range);
2018-02-25 00:13:39 +01:00
} else {
statement = this.parseImport(tn);
2018-02-25 00:13:39 +01:00
}
if (modifiers) setReusableModifiers(modifiers);
break;
case Token.TYPE:
statement = this.parseTypeDeclaration(tn, modifiers, decorators);
decorators = null;
break;
default:
2018-02-25 00:13:39 +01:00
// handle plain exports
if (hasModifier(ModifierKind.EXPORT, modifiers)) {
tn.reset();
statement = this.parseExport(tn, modifiers); // TODO: why exactly does this have modifiers again? 'declare'?
2018-02-25 00:13:39 +01:00
// handle non-declaration statements
} else {
if (modifiers) {
2018-02-25 00:13:39 +01:00
if (modifier = getModifier(ModifierKind.DECLARE, modifiers)) {
this.error(
DiagnosticCode._0_modifier_cannot_be_used_here,
modifier.range, "declare"
); // recoverable
}
2017-12-18 03:46:36 +01:00
setReusableModifiers(modifiers);
}
tn.reset();
2018-02-25 00:13:39 +01:00
if (!isNamespaceMember) {
statement = this.parseStatement(tn, true);
2018-02-25 00:13:39 +01:00
}
}
break;
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
// check for decorators that weren't consumed
if (decorators) {
for (var i = 0, k = decorators.length; i < k; ++i) {
this.error(
DiagnosticCode.Decorators_are_not_valid_here,
decorators[i].range
);
}
}
return statement;
2017-09-28 13:08:25 +02:00
}
2017-12-18 03:46:36 +01:00
/** Obtains the next file to parse. */
2017-09-28 13:08:25 +02:00
nextFile(): string | null {
return this.backlog.length ? this.backlog.shift() : null;
2017-09-28 13:08:25 +02:00
}
2017-12-18 03:46:36 +01:00
/** Finishes parsing and returns the program. */
2017-09-28 13:08:25 +02:00
finish(): Program {
2018-02-25 00:13:39 +01:00
if (this.backlog.length) throw new Error("backlog is not empty");
this.backlog = [];
this.seenlog.clear();
2017-09-28 13:08:25 +02:00
return this.program;
}
2018-02-25 00:13:39 +01:00
/** Parses a type. */
parseType(
tn: Tokenizer,
acceptParenthesized: bool = true,
suppressErrors: bool = false
): TypeNode | null {
// NOTE: this parses our limited subset
var token = tn.next();
var startPos = tn.tokenPos;
2017-09-28 13:08:25 +02:00
// void
2018-02-25 00:13:39 +01:00
if (token == Token.VOID) {
return Node.createType(
Node.createIdentifierExpression("void", tn.range()), [], false, tn.range(startPos, tn.pos)
);
}
2017-09-28 13:08:25 +02:00
var type: TypeNode;
2017-09-28 13:08:25 +02:00
// ( ... )
if (acceptParenthesized && token == Token.OPENPAREN) {
var innerType = this.parseType(tn, false, suppressErrors);
2018-02-25 00:13:39 +01:00
if (!innerType) return null;
2017-09-28 13:08:25 +02:00
if (!tn.skip(Token.CLOSEPAREN)) {
2018-02-25 00:13:39 +01:00
if (!suppressErrors) {
this.error(
DiagnosticCode._0_expected,
tn.range(tn.pos), "}"
);
}
2017-09-28 13:08:25 +02:00
return null;
}
type = innerType;
type.range.start = startPos;
type.range.end = tn.pos;
// this
} else if (token == Token.THIS) {
2018-02-25 00:13:39 +01:00
type = Node.createType(
Node.createThisExpression(tn.range()), [], false, tn.range(startPos, tn.pos)
);
2017-09-28 13:08:25 +02:00
2017-10-07 14:29:43 +02:00
// true
2017-09-28 13:08:25 +02:00
} else if (token == Token.TRUE || token == Token.FALSE) {
2018-02-25 00:13:39 +01:00
type = Node.createType(
Node.createIdentifierExpression("bool", tn.range()), [], false, tn.range(startPos, tn.pos)
);
2017-09-28 13:08:25 +02:00
// string literal
} else if (token == Token.STRINGLITERAL) {
tn.readString();
2018-02-25 00:13:39 +01:00
type = Node.createType(
Node.createIdentifierExpression("string", tn.range()), [], false, tn.range(startPos, tn.pos)
);
2017-09-28 13:08:25 +02:00
// Name
} else if (token == Token.IDENTIFIER) {
2018-01-05 01:55:59 +01:00
var identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
var parameters = new Array<TypeNode>();
var nullable = false;
2017-09-28 13:08:25 +02:00
// Name<T>
if (tn.skip(Token.LESSTHAN)) {
do {
var parameter = this.parseType(tn, true, suppressErrors);
2018-02-25 00:13:39 +01:00
if (!parameter) return null;
2017-09-28 13:08:25 +02:00
parameters.push(<TypeNode>parameter);
} while (tn.skip(Token.COMMA));
if (!tn.skip(Token.GREATERTHAN)) {
2018-02-25 00:13:39 +01:00
if (!suppressErrors) {
this.error(
DiagnosticCode._0_expected,
tn.range(tn.pos), ">"
);
}
2017-09-28 13:08:25 +02:00
return null;
}
}
// ... | null
if (tn.skip(Token.BAR)) {
if (tn.skip(Token.NULL)) {
nullable = true;
} else {
2018-02-25 00:13:39 +01:00
if (!suppressErrors) {
this.error(
DiagnosticCode._0_expected,
tn.range(tn.pos), "null"
);
}
2017-09-28 13:08:25 +02:00
return null;
}
}
2017-12-16 17:54:53 +01:00
type = Node.createType(identifier, parameters, nullable, tn.range(startPos, tn.pos));
2017-09-28 13:08:25 +02:00
} else {
2018-02-25 00:13:39 +01:00
if (!suppressErrors) {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
}
2017-09-28 13:08:25 +02:00
return null;
}
// ... [][]
while (tn.skip(Token.OPENBRACKET)) {
var bracketStart = tn.tokenPos;
2017-09-28 13:08:25 +02:00
if (!tn.skip(Token.CLOSEBRACKET)) {
2018-02-25 00:13:39 +01:00
if (!suppressErrors) {
this.error(
DiagnosticCode._0_expected,
tn.range(), "]"
);
}
2017-09-28 13:08:25 +02:00
return null;
}
var bracketRange = tn.range(bracketStart, tn.pos);
2017-09-28 13:08:25 +02:00
// ...[] | null
nullable = false;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.BAR)) {
if (tn.skip(Token.NULL)) {
nullable = true;
} else {
2018-02-25 00:13:39 +01:00
if (!suppressErrors) {
this.error(
DiagnosticCode._0_expected,
tn.range(), "null"
);
}
2017-09-28 13:08:25 +02:00
return null;
}
}
2018-02-25 00:13:39 +01:00
type = Node.createType(
Node.createIdentifierExpression("Array", bracketRange),
[ type ],
nullable,
tn.range(startPos, tn.pos)
);
if (nullable) break;
2017-09-28 13:08:25 +02:00
}
return type;
}
// statements
2018-02-25 00:13:39 +01:00
parseDecorator(
tn: Tokenizer
): Decorator | null {
2017-10-02 12:52:15 +02:00
// at '@': Identifier ('.' Identifier)* '(' Arguments
2018-02-25 00:13:39 +01:00
var startPos = tn.tokenPos;
2017-10-02 12:52:15 +02:00
if (tn.skip(Token.IDENTIFIER)) {
var name = tn.readIdentifier();
2018-01-05 01:55:59 +01:00
var expression: Expression = Node.createIdentifierExpression(name, tn.range(startPos, tn.pos));
2017-10-02 12:52:15 +02:00
while (tn.skip(Token.DOT)) {
if (tn.skip(Token.IDENTIFIER)) {
name = tn.readIdentifier();
2018-02-25 00:13:39 +01:00
expression = Node.createPropertyAccessExpression(
expression,
Node.createIdentifierExpression(name, tn.range()),
tn.range(startPos, tn.pos)
);
2017-10-02 12:52:15 +02:00
} else {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
2017-10-02 12:52:15 +02:00
return null;
}
}
var args: Expression[] | null;
2017-10-02 12:52:15 +02:00
if (tn.skip(Token.OPENPAREN)) {
args = this.parseArguments(tn);
2018-02-25 00:13:39 +01:00
if (args) {
return Node.createDecorator(expression, args, tn.range(startPos, tn.pos));
2018-02-25 00:13:39 +01:00
}
} else {
return Node.createDecorator(expression, null, tn.range(startPos, tn.pos));
2018-02-25 00:13:39 +01:00
}
} else {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
}
2017-10-02 12:52:15 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseVariable(
tn: Tokenizer,
modifiers: Modifier[] | null,
decorators: Decorator[] | null
): VariableStatement | null {
2017-09-28 13:08:25 +02:00
// at ('const' | 'let' | 'var'): VariableDeclaration (',' VariableDeclaration)* ';'?
2018-02-25 00:13:39 +01:00
var startPos = modifiers && modifiers.length ? modifiers[0].range.start : tn.tokenPos;
var members = new Array<VariableDeclaration>();
var isDeclare = hasModifier(ModifierKind.DECLARE, modifiers);
2017-09-28 13:08:25 +02:00
do {
var member = this.parseVariableDeclaration(tn, isDeclare, modifiers, decorators);
2018-02-25 00:13:39 +01:00
if (!member) return null;
2017-09-28 13:08:25 +02:00
members.push(<VariableDeclaration>member);
} while (tn.skip(Token.COMMA));
2018-01-05 01:55:59 +01:00
var ret = Node.createVariableStatement(members, modifiers, decorators, tn.range(startPos, tn.pos));
2017-09-28 13:08:25 +02:00
tn.skip(Token.SEMICOLON);
return ret;
}
2018-02-25 00:13:39 +01:00
parseVariableDeclaration(
tn: Tokenizer,
isDeclare: bool = false,
parentModifiers: Modifier[] | null,
parentDecorators: Decorator[] | null
): VariableDeclaration | null {
// before: Identifier (':' Type)? ('=' Expression)?
2017-09-28 13:08:25 +02:00
if (!tn.skip(Token.IDENTIFIER)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
2017-09-28 13:08:25 +02:00
return null;
}
2018-01-05 01:55:59 +01:00
var identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
2017-09-28 13:08:25 +02:00
var type: TypeNode | null = null;
2018-02-25 00:13:39 +01:00
if (tn.skip(Token.COLON)) {
2017-09-28 13:08:25 +02:00
type = this.parseType(tn);
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
var initializer: Expression | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.EQUALS)) {
2018-02-25 00:13:39 +01:00
if (isDeclare) {
this.error(
DiagnosticCode.Initializers_are_not_allowed_in_ambient_contexts,
tn.range()
); // recoverable
}
initializer = this.parseExpression(tn, Precedence.COMMA + 1);
2018-02-25 00:13:39 +01:00
if (!initializer) return null;
2017-12-14 11:55:35 +01:00
} else {
2017-12-27 22:38:32 +01:00
if (hasModifier(ModifierKind.CONST, parentModifiers)) {
2018-02-25 00:13:39 +01:00
if (!hasModifier(ModifierKind.DECLARE, parentModifiers)) {
this.error(
DiagnosticCode._const_declarations_must_be_initialized,
identifier.range
);
}
} else if (!type) { // neither type nor initializer
this.error(
DiagnosticCode.Type_expected,
tn.range(tn.pos)
); // recoverable
}
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
return Node.createVariableDeclaration(
identifier,
type,
initializer,
parentModifiers,
parentDecorators,
Range.join(identifier.range, tn.range())
);
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
parseEnum(
tn: Tokenizer,
modifiers: Modifier[] | null,
decorators: Decorator[] | null
): EnumDeclaration | null {
2017-09-28 13:08:25 +02:00
// at 'enum': Identifier '{' (EnumValueDeclaration (',' EnumValueDeclaration )*)? '}' ';'?
2018-02-25 00:13:39 +01:00
var startPos = modifiers && modifiers.length ? modifiers[0].range.start : tn.tokenPos;
2017-09-28 13:08:25 +02:00
if (tn.next() != Token.IDENTIFIER) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
2017-09-28 13:08:25 +02:00
return null;
}
2018-01-05 01:55:59 +01:00
var identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
2017-09-28 13:08:25 +02:00
if (tn.next() != Token.OPENBRACE) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), "{"
);
2017-09-28 13:08:25 +02:00
return null;
}
var members = new Array<EnumValueDeclaration>();
2017-09-28 13:08:25 +02:00
if (!tn.skip(Token.CLOSEBRACE)) {
do {
var member = this.parseEnumValue(tn);
2018-02-25 00:13:39 +01:00
if (!member) return null;
2017-09-28 13:08:25 +02:00
members.push(<EnumValueDeclaration>member);
} while (tn.skip(Token.COMMA));
if (!tn.skip(Token.CLOSEBRACE)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), "}"
);
2017-09-28 13:08:25 +02:00
return null;
}
}
2018-02-25 00:13:39 +01:00
var ret = Node.createEnumDeclaration(
identifier,
members,
modifiers,
decorators,
tn.range(startPos, tn.pos)
);
2017-09-28 13:08:25 +02:00
tn.skip(Token.SEMICOLON);
return ret;
}
2018-02-25 00:13:39 +01:00
parseEnumValue(
tn: Tokenizer
): EnumValueDeclaration | null {
// before: Identifier ('=' Expression)?
2017-09-28 13:08:25 +02:00
if (!tn.skip(Token.IDENTIFIER)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
2017-09-28 13:08:25 +02:00
return null;
}
2018-01-05 01:55:59 +01:00
var identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
var value: Expression | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.EQUALS)) {
value = this.parseExpression(tn, Precedence.COMMA + 1);
2018-02-25 00:13:39 +01:00
if (!value) return null;
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
return Node.createEnumValueDeclaration(
identifier,
value,
Range.join(identifier.range, tn.range())
);
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
parseReturn(
tn: Tokenizer
): ReturnStatement | null {
2017-09-28 13:08:25 +02:00
// at 'return': Expression | (';' | '}' | ...'\n')
2018-02-25 00:13:39 +01:00
var expr: Expression | null = null;
2018-02-25 00:13:39 +01:00
if (
tn.peek(true) != Token.SEMICOLON &&
tn.nextToken != Token.CLOSEBRACE &&
!tn.nextTokenOnNewLine
) {
if (!(expr = this.parseExpression(tn))) return null;
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
2018-01-05 01:55:59 +01:00
var ret = Node.createReturnStatement(expr, tn.range());
2017-09-28 13:08:25 +02:00
tn.skip(Token.SEMICOLON);
return ret;
}
2018-02-25 00:13:39 +01:00
parseTypeParameters(
tn: Tokenizer
): TypeParameter[] | null {
2017-09-28 13:08:25 +02:00
// at '<': TypeParameter (',' TypeParameter)* '>'
2018-02-25 00:13:39 +01:00
var typeParameters = new Array<TypeParameter>();
2017-09-28 13:08:25 +02:00
if (!tn.skip(Token.GREATERTHAN)) {
do {
var typeParameter = this.parseTypeParameter(tn);
2018-02-25 00:13:39 +01:00
if (!typeParameter) return null;
2017-09-28 13:08:25 +02:00
typeParameters.push(<TypeParameter>typeParameter);
} while (tn.skip(Token.COMMA));
if (!tn.skip(Token.GREATERTHAN)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), ">"
);
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode.Type_parameter_list_cannot_be_empty,
tn.range()
); // recoverable
}
2017-09-28 13:08:25 +02:00
return typeParameters;
}
2018-02-25 00:13:39 +01:00
parseTypeParameter(
tn: Tokenizer
): TypeParameter | null {
// before: Identifier ('extends' Type)?
2017-09-28 13:08:25 +02:00
if (tn.next() == Token.IDENTIFIER) {
2018-02-25 00:13:39 +01:00
var identifier = Node.createIdentifierExpression(
tn.readIdentifier(),
tn.range()
);
var extendsType: TypeNode | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.EXTENDS)) {
2018-02-25 00:13:39 +01:00
if (!(extendsType = this.parseType(tn))) return null;
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
return Node.createTypeParameter(
identifier,
extendsType,
Range.join(identifier.range, tn.range())
);
} else {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
}
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseParameters(
tn: Tokenizer
): Parameter[] | null {
2017-09-28 13:08:25 +02:00
// at '(': (Parameter (',' Parameter)*)? ')'
2018-02-25 00:13:39 +01:00
var parameters = new Array<Parameter>();
var seenRest: Parameter | null = null;
var seenOptional = false;
var reportedRest = false;
2018-02-25 00:13:39 +01:00
2017-09-28 13:08:25 +02:00
if (tn.peek() != Token.CLOSEPAREN) {
do {
var param = this.parseParameter(tn);
2018-02-25 00:13:39 +01:00
if (!param) return null;
if (seenRest && !reportedRest) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.A_rest_parameter_must_be_last_in_a_parameter_list,
seenRest.name.range
);
reportedRest = true;
}
switch (param.parameterKind) {
default:
2018-02-25 00:13:39 +01:00
if (seenOptional) {
this.error(
DiagnosticCode.A_required_parameter_cannot_follow_an_optional_parameter,
param.name.range
);
}
break;
case ParameterKind.OPTIONAL:
seenOptional = true;
break;
case ParameterKind.REST:
seenRest = param;
break;
}
parameters.push(param);
2017-09-28 13:08:25 +02:00
} while (tn.skip(Token.COMMA));
}
2018-02-25 00:13:39 +01:00
if (!tn.skip(Token.CLOSEPAREN)) {
this.error(
DiagnosticCode._0_expected,
tn.range(), ")"
);
return null;
}
return parameters;
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
parseParameter(
2018-02-27 00:30:04 +01:00
tn: Tokenizer,
suppressErrors: bool = false
2018-02-25 00:13:39 +01:00
): Parameter | null {
// before: '...'? Identifier '?'? (':' Type)? ('=' Expression)?
var isRest = false;
var isOptional = false;
var startRange: Range | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.DOT_DOT_DOT)) {
isRest = true;
2017-09-28 13:08:25 +02:00
startRange = tn.range();
}
if (tn.skip(Token.IDENTIFIER)) {
2018-02-25 00:13:39 +01:00
if (!isRest) startRange = tn.range();
2018-01-05 01:55:59 +01:00
var identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
var type: TypeNode | null = null;
if (isOptional = tn.skip(Token.QUESTION)) {
2018-02-25 00:13:39 +01:00
if (isRest) {
this.error(
DiagnosticCode.A_rest_parameter_cannot_be_optional,
identifier.range
);
}
}
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.COLON)) {
type = this.parseType(tn);
2018-02-25 00:13:39 +01:00
if (!type) return null;
2017-09-28 13:08:25 +02:00
}
var initializer: Expression | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.EQUALS)) {
2018-02-25 00:13:39 +01:00
if (isRest) {
this.error(
DiagnosticCode.A_rest_parameter_cannot_have_an_initializer,
identifier.range
);
}
if (isOptional) {
this.error(
DiagnosticCode.Parameter_cannot_have_question_mark_and_initializer,
identifier.range
);
} else {
isOptional = true;
2018-02-25 00:13:39 +01:00
}
2018-01-14 21:17:43 +01:00
initializer = this.parseExpression(tn, Precedence.COMMA + 1);
2018-02-25 00:13:39 +01:00
if (!initializer) return null;
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
return Node.createParameter(
identifier,
type,
initializer,
isRest
? ParameterKind.REST
: isOptional
? ParameterKind.OPTIONAL
: ParameterKind.DEFAULT,
Range.join(<Range>startRange, tn.range())
);
} else {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
}
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseFunction(
tn: Tokenizer,
modifiers: Modifier[] | null,
decorators: Decorator[] | null
): FunctionDeclaration | null {
// at 'function':
// Identifier
// ('<' TypeParameters)?
// '(' Parameters (':' Type)?
// '{' Statement* '}'
// ';'?
var startPos = modifiers && modifiers.length ? modifiers[0].range.start : tn.tokenPos;
2017-10-02 12:52:15 +02:00
2017-09-28 13:08:25 +02:00
if (!tn.skip(Token.IDENTIFIER)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Identifier_expected,
tn.range(tn.pos)
);
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
2018-01-05 01:55:59 +01:00
var identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
2018-02-25 00:13:39 +01:00
var typeParameters: TypeParameter[] | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.LESSTHAN)) {
typeParameters = this.parseTypeParameters(tn);
2018-02-25 00:13:39 +01:00
if (!typeParameters) return null;
}
2017-09-28 13:08:25 +02:00
if (!tn.skip(Token.OPENPAREN)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(tn.pos), "("
);
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
var parameters = this.parseParameters(tn);
2018-02-25 00:13:39 +01:00
if (!parameters) return null;
var isSetter = hasModifier(ModifierKind.SET, modifiers);
2017-12-14 11:55:35 +01:00
if (isSetter) {
2018-02-25 00:13:39 +01:00
if (parameters.length != 1) {
this.error(
DiagnosticCode.A_set_accessor_must_have_exactly_one_parameter,
identifier.range
); // recoverable
}
if (parameters.length && parameters[0].initializer) {
this.error(
DiagnosticCode.A_set_accessor_parameter_cannot_have_an_initializer,
identifier.range
); // recoverable
}
2017-12-14 11:55:35 +01:00
}
2018-02-25 00:13:39 +01:00
var isGetter = hasModifier(ModifierKind.GET, modifiers);
2018-02-25 00:13:39 +01:00
if (isGetter && parameters.length) {
this.error(
DiagnosticCode.A_get_accessor_cannot_have_parameters,
identifier.range
); // recoverable
}
var returnType: TypeNode | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.COLON)) {
2017-12-14 11:55:35 +01:00
returnType = this.parseType(tn, isSetter);
2018-02-25 00:13:39 +01:00
if (!returnType) return null;
} else if (!isSetter) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Type_expected,
tn.range(tn.pos)
); // recoverable
2017-12-14 11:55:35 +01:00
}
2018-02-25 00:13:39 +01:00
var isDeclare = hasModifier(ModifierKind.DECLARE, modifiers);
2018-02-25 00:13:39 +01:00
2018-02-27 00:30:04 +01:00
var body: Statement | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.OPENBRACE)) {
2018-02-25 00:13:39 +01:00
if (isDeclare) {
this.error(
DiagnosticCode.An_implementation_cannot_be_declared_in_ambient_contexts,
tn.range()
); // recoverable
}
2018-02-27 00:30:04 +01:00
body = this.parseBlockStatement(tn, false);
if (!body) return null;
2018-02-25 00:13:39 +01:00
} else if (!isDeclare) {
this.error(
DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration,
tn.range(tn.pos)
);
}
var ret = Node.createFunctionDeclaration(
identifier,
typeParameters,
<Parameter[]>parameters,
returnType,
2018-02-27 00:30:04 +01:00
body,
2018-02-25 00:13:39 +01:00
modifiers,
decorators,
tn.range(startPos, tn.pos)
);
2017-09-28 13:08:25 +02:00
tn.skip(Token.SEMICOLON);
return ret;
}
2018-02-27 00:30:04 +01:00
parseFunctionExpression(tn: Tokenizer): FunctionExpression | null {
var startPos = tn.tokenPos;
var identifier: IdentifierExpression;
var isArrow = false;
// either at 'function':
// Identifier?
// '(' Parameters (':' Type)?
// Statement
if (tn.token == Token.FUNCTION) {
isArrow = false;
if (tn.skip(Token.IDENTIFIER)) {
identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
} else { // empty name
identifier = Node.createIdentifierExpression("", tn.range(tn.pos, tn.pos));
}
if (!tn.skip(Token.OPENPAREN)) {
this.error(
DiagnosticCode._0_expected,
tn.range(tn.pos), "("
);
return null;
}
// or at '(' of arrow function:
// Parameters (':' Type)?
// Statement
} else {
isArrow = true;
assert(tn.token == Token.OPENPAREN);
identifier = Node.createIdentifierExpression("", tn.range(tn.tokenPos, tn.tokenPos));
}
// TODO: type parameters? doesn't seem worth it.
var parameters = this.parseParameters(tn);
if (!parameters) return null;
return this.parseFunctionExpressionCommon(tn, identifier, parameters, isArrow, startPos);
}
private parseFunctionExpressionCommon(
tn: Tokenizer,
identifier: IdentifierExpression,
parameters: Parameter[],
isArrow: bool,
startPos: i32 = -1
): FunctionExpression | null {
if (startPos < 0) startPos = identifier.range.start;
var returnType: TypeNode | null = null;
if (tn.skip(Token.COLON)) {
returnType = this.parseType(tn);
if (!returnType) return null;
}
if (isArrow) {
if (!tn.skip(Token.EQUALS_GREATERTHAN)) {
this.error(
DiagnosticCode._0_expected,
tn.range(tn.pos), "=>"
);
return null;
}
}
var body: Statement | null;
if (isArrow) {
body = this.parseStatement(tn, false);
} else {
if (!tn.skip(Token.OPENBRACE)) {
this.error(
DiagnosticCode._0_expected,
tn.range(tn.pos), "{"
);
return null;
}
body = this.parseBlockStatement(tn, false);
}
if (!body) return null;
var declaration = Node.createFunctionDeclaration(
identifier,
null,
2018-02-27 00:30:04 +01:00
parameters,
returnType,
body,
null,
null,
tn.range(startPos, tn.pos)
);
return Node.createFunctionExpression(declaration, isArrow);
}
2018-02-25 00:13:39 +01:00
parseClass(
tn: Tokenizer,
modifiers: Modifier[] | null,
decorators: Decorator[] | null
): ClassDeclaration | null {
// at 'class':
// Identifier
// ('<' TypeParameters)?
// ('extends' Type)?
// ('implements' Type (',' Type)*)?
// '{' ClassMember* '}'
var startPos = decorators && decorators.length
? decorators[0].range.start
: modifiers && modifiers.length
2017-10-02 12:52:15 +02:00
? modifiers[0].range.start
: tn.tokenPos;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.IDENTIFIER)) {
2018-02-25 00:13:39 +01:00
var identifier = Node.createIdentifierExpression(
tn.readIdentifier(),
tn.range()
);
var typeParameters: TypeParameter[] | null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.LESSTHAN)) {
typeParameters = this.parseTypeParameters(tn);
2018-02-25 00:13:39 +01:00
if (!typeParameters) return null;
} else {
typeParameters = [];
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
var extendsType: TypeNode | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.EXTENDS)) {
extendsType = this.parseType(tn);
2018-02-25 00:13:39 +01:00
if (!extendsType) return null;
2017-09-28 13:08:25 +02:00
}
var implementsTypes = new Array<TypeNode>();
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.IMPLEMENTS)) {
do {
var type = this.parseType(tn);
2018-02-25 00:13:39 +01:00
if (!type) return null;
2017-09-28 13:08:25 +02:00
implementsTypes.push(<TypeNode>type);
} while (tn.skip(Token.COMMA));
}
if (tn.skip(Token.OPENBRACE)) {
2018-02-25 00:13:39 +01:00
var members = new Array<DeclarationStatement>();
2017-09-28 13:08:25 +02:00
if (!tn.skip(Token.CLOSEBRACE)) {
var isDeclare = hasModifier(ModifierKind.DECLARE, modifiers);
2017-09-28 13:08:25 +02:00
do {
var member = this.parseClassMember(tn, isDeclare);
2018-02-25 00:13:39 +01:00
if (!member) return null;
2017-09-28 13:08:25 +02:00
members.push(<DeclarationStatement>member);
} while (!tn.skip(Token.CLOSEBRACE));
}
2018-02-25 00:13:39 +01:00
return Node.createClassDeclaration(
identifier,
typeParameters,
extendsType,
implementsTypes,
members,
modifiers,
decorators,
tn.range(startPos, tn.pos)
);
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), "{"
);
}
} else {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
}
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseClassMember(
tn: Tokenizer,
parentIsDeclare: bool
): DeclarationStatement | null {
// before:
// ('public' | 'private' | 'protected')?
// ('static' | 'abstract')?
// ('get' | 'set')?
// Identifier ...
var startPos = tn.pos;
2017-09-28 13:08:25 +02:00
var decorators = new Array<Decorator>();
2017-10-02 12:52:15 +02:00
while (tn.skip(Token.AT)) {
var decorator = this.parseDecorator(tn);
2018-02-25 00:13:39 +01:00
if (!decorator) break;
decorators.push(<Decorator>decorator);
2017-10-02 12:52:15 +02:00
}
var modifiers: Modifier[] | null = null;
2017-09-28 13:08:25 +02:00
2018-02-25 00:13:39 +01:00
if (tn.skip(Token.PUBLIC)) {
2017-12-16 17:54:53 +01:00
modifiers = addModifier(Node.createModifier(ModifierKind.PUBLIC, tn.range()), modifiers);
2018-02-25 00:13:39 +01:00
} else if (tn.skip(Token.PRIVATE)) {
2017-12-16 17:54:53 +01:00
modifiers = addModifier(Node.createModifier(ModifierKind.PRIVATE, tn.range()), modifiers);
2018-02-25 00:13:39 +01:00
} else if (tn.skip(Token.PROTECTED)) {
2017-12-16 17:54:53 +01:00
modifiers = addModifier(Node.createModifier(ModifierKind.PROTECTED, tn.range()), modifiers);
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
2018-02-25 00:13:39 +01:00
if (tn.skip(Token.STATIC)) {
2017-12-16 17:54:53 +01:00
modifiers = addModifier(Node.createModifier(ModifierKind.STATIC, tn.range()), modifiers);
2018-02-25 00:13:39 +01:00
} else if (tn.skip(Token.ABSTRACT)) {
2017-12-16 17:54:53 +01:00
modifiers = addModifier(Node.createModifier(ModifierKind.ABSTRACT, tn.range()), modifiers);
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
2018-02-25 00:13:39 +01:00
if (tn.skip(Token.READONLY)) {
2017-12-16 17:54:53 +01:00
modifiers = addModifier(Node.createModifier(ModifierKind.READONLY, tn.range()), modifiers);
2018-02-25 00:13:39 +01:00
}
2017-12-16 02:27:39 +01:00
// check if accessor: ('get' | 'set') ^\n Identifier
tn.mark();
2018-02-25 00:13:39 +01:00
var isGetter = false;
var isSetter = false;
2018-02-25 00:13:39 +01:00
if (isGetter = tn.skip(Token.GET)) {
2018-02-25 00:13:39 +01:00
if (tn.peek(true, true) == Token.IDENTIFIER && !tn.nextTokenOnNewLine) {
modifiers = addModifier(Node.createModifier(ModifierKind.GET, tn.range()), modifiers);
2018-02-25 00:13:39 +01:00
} else {
tn.reset();
isGetter = false;
}
2018-02-25 00:13:39 +01:00
} else if (isSetter = tn.skip(Token.SET)) { // can't be both
2018-02-25 00:13:39 +01:00
if (tn.peek(true, true) == Token.IDENTIFIER && !tn.nextTokenOnNewLine) {
modifiers = addModifier(Node.createModifier(ModifierKind.SET, tn.range()), modifiers);
2018-02-25 00:13:39 +01:00
} else {
tn.reset();
isSetter = false;
}
}
2017-09-28 13:08:25 +02:00
var isConstructor = tn.skip(Token.CONSTRUCTOR);
if (isConstructor || tn.skip(Token.IDENTIFIER)) {
2018-02-25 00:13:39 +01:00
var identifier = isConstructor
2018-01-05 01:55:59 +01:00
? Node.createConstructorExpression(tn.range())
: Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
2018-02-25 00:13:39 +01:00
var typeParameters: TypeParameter[] | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.LESSTHAN)) {
2018-02-25 00:13:39 +01:00
if (isConstructor) {
this.error(
DiagnosticCode.Type_parameters_cannot_appear_on_a_constructor_declaration,
tn.range()
); // recoverable
}
2017-09-28 13:08:25 +02:00
typeParameters = this.parseTypeParameters(tn);
2018-02-25 00:13:39 +01:00
if (!typeParameters) return null;
}
2017-09-28 13:08:25 +02:00
// method: '(' Parameters (':' Type)? '{' Statement* '}' ';'?
if (tn.skip(Token.OPENPAREN)) {
var parameters = this.parseParameters(tn);
2018-02-25 00:13:39 +01:00
if (!parameters) return null;
if (isGetter && parameters.length) {
this.error(
DiagnosticCode.A_get_accessor_cannot_have_parameters,
identifier.range
);
}
2017-12-14 11:55:35 +01:00
if (isSetter) {
2018-02-25 00:13:39 +01:00
if (parameters.length != 1) {
this.error(
DiagnosticCode.A_set_accessor_must_have_exactly_one_parameter,
identifier.range
);
}
if (parameters.length && parameters[0].initializer) {
this.error(
DiagnosticCode.A_set_accessor_parameter_cannot_have_an_initializer,
identifier.range
);
}
2017-12-14 11:55:35 +01:00
}
2018-02-25 00:13:39 +01:00
var returnType: TypeNode | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.COLON)) {
2018-02-25 00:13:39 +01:00
if (identifier.kind == NodeKind.CONSTRUCTOR) {
this.error(
DiagnosticCode.Type_annotation_cannot_appear_on_a_constructor_declaration,
tn.range()
);
} else if (isSetter) {
this.error(
DiagnosticCode.A_set_accessor_cannot_have_a_return_type_annotation,
tn.range()
);
}
2017-12-16 02:27:39 +01:00
returnType = this.parseType(tn, identifier.kind == NodeKind.CONSTRUCTOR || isSetter);
2018-02-25 00:13:39 +01:00
if (!returnType) return null;
} else if (!isSetter && identifier.kind != NodeKind.CONSTRUCTOR) {
this.error(
DiagnosticCode.Type_expected,
tn.range()
); // recoverable
}
2018-02-27 00:30:04 +01:00
var body: Statement | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.OPENBRACE)) {
2018-02-25 00:13:39 +01:00
if (parentIsDeclare) {
this.error(
DiagnosticCode.An_implementation_cannot_be_declared_in_ambient_contexts,
tn.range()
); // recoverable
}
2018-02-27 00:30:04 +01:00
body = this.parseBlockStatement(tn, false);
if (!body) return null;
2018-02-25 00:13:39 +01:00
} else if (!parentIsDeclare) {
this.error(
DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration,
tn.range()
); // recoverable
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
var retMethod = Node.createMethodDeclaration(
identifier,
typeParameters,
parameters,
returnType,
2018-02-27 00:30:04 +01:00
body,
2018-02-25 00:13:39 +01:00
modifiers,
decorators,
tn.range(startPos, tn.pos)
);
2017-09-28 13:08:25 +02:00
tn.skip(Token.SEMICOLON);
return retMethod;
2017-09-28 13:08:25 +02:00
} else if (isConstructor) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Constructor_implementation_is_missing,
identifier.range
);
} else if (isGetter || isSetter) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration,
identifier.range
);
2017-09-28 13:08:25 +02:00
// field: (':' Type)? ('=' Expression)? ';'?
} else {
var modifier: Modifier | null;
2018-02-25 00:13:39 +01:00
if (modifier = getModifier(ModifierKind.ABSTRACT, modifiers)) {
this.error(
DiagnosticCode._0_modifier_cannot_be_used_here,
modifier.range, "abstract"
); // recoverable
}
if (modifier = getModifier(ModifierKind.GET, modifiers)) {
this.error(
DiagnosticCode._0_modifier_cannot_be_used_here,
modifier.range, "get"
); // recoverable
}
if (modifier = getModifier(ModifierKind.SET, modifiers)) {
this.error(
DiagnosticCode._0_modifier_cannot_be_used_here,
modifier.range, "set"
); // recoverable
}
var type: TypeNode | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.COLON)) {
type = this.parseType(tn);
2018-02-25 00:13:39 +01:00
if (!type) return null;
} else {
this.error(
DiagnosticCode.Type_expected,
tn.range()
); // recoverable
}
var initializer: Expression | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.EQUALS)) {
initializer = this.parseExpression(tn);
2018-02-25 00:13:39 +01:00
if (!initializer) return null;
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
var retField = Node.createFieldDeclaration(
identifier,
type,
initializer,
modifiers,
decorators,
tn.range(startPos, tn.pos)
);
2017-09-28 13:08:25 +02:00
tn.skip(Token.SEMICOLON);
return retField;
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
}
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseNamespace(
tn: Tokenizer,
modifiers: Modifier[] | null,
decorators: Decorator[] | null
): NamespaceDeclaration | null {
// at 'namespace': Identifier '{' (Variable | Function)* '}'
2018-02-25 00:13:39 +01:00
var startPos = modifiers && modifiers.length ? modifiers[0].range.start : tn.tokenPos;
if (tn.skip(Token.IDENTIFIER)) {
2018-01-05 01:55:59 +01:00
var identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
if (tn.skip(Token.OPENBRACE)) {
var members = new Array<Statement>();
while (!tn.skip(Token.CLOSEBRACE)) {
var member = this.parseTopLevelStatement(tn, true);
2018-02-25 00:13:39 +01:00
if (!member) return null;
members.push(member);
}
2018-02-25 00:13:39 +01:00
var ret = Node.createNamespaceDeclaration(
identifier,
members,
modifiers,
decorators,
tn.range(startPos, tn.pos)
);
tn.skip(Token.SEMICOLON);
return ret;
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), "{"
);
}
} else {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
}
return null;
}
2018-02-25 00:13:39 +01:00
parseExport(
tn: Tokenizer,
modifiers: Modifier[] | null
): ExportStatement | null {
2017-09-28 13:08:25 +02:00
// at 'export': '{' ExportMember (',' ExportMember)* }' ('from' StringLiteral)? ';'?
2018-02-25 00:13:39 +01:00
var startPos = modifiers && modifiers.length ? modifiers[0].range.start : tn.tokenPos;
2018-02-25 00:13:39 +01:00
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.OPENBRACE)) {
var members = new Array<ExportMember>();
2017-09-28 13:08:25 +02:00
if (!tn.skip(Token.CLOSEBRACE)) {
do {
var member = this.parseExportMember(tn);
2018-02-25 00:13:39 +01:00
if (!member) return null;
2017-09-28 13:08:25 +02:00
members.push(member);
} while (tn.skip(Token.COMMA));
if (!tn.skip(Token.CLOSEBRACE)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), "}"
);
2017-09-28 13:08:25 +02:00
return null;
}
}
var path: StringLiteralExpression | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.FROM)) {
2018-02-25 00:13:39 +01:00
if (tn.skip(Token.STRINGLITERAL)) {
2018-01-05 01:55:59 +01:00
path = Node.createStringLiteralExpression(tn.readString(), tn.range());
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode.String_literal_expected,
tn.range()
);
2017-09-28 13:08:25 +02:00
return null;
}
}
2018-01-05 01:55:59 +01:00
var ret = Node.createExportStatement(members, path, modifiers, tn.range(startPos, tn.pos));
2017-10-02 12:52:15 +02:00
if (ret.normalizedPath && !this.seenlog.has(<string>ret.normalizedPath)) {
this.backlog.push(<string>ret.normalizedPath);
this.seenlog.add(<string>ret.normalizedPath);
}
2017-09-28 13:08:25 +02:00
tn.skip(Token.SEMICOLON);
return ret;
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), "{"
);
}
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseExportMember(
tn: Tokenizer
): ExportMember | null {
// before: Identifier ('as' Identifier)?
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.IDENTIFIER)) {
2018-01-05 01:55:59 +01:00
var identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
var asIdentifier: IdentifierExpression | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.AS)) {
2018-02-25 00:13:39 +01:00
if (tn.skip(Token.IDENTIFIER)) {
2018-01-05 01:55:59 +01:00
asIdentifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
2017-09-28 13:08:25 +02:00
return null;
}
}
2018-02-25 00:13:39 +01:00
return Node.createExportMember(
identifier,
asIdentifier,
asIdentifier
? Range.join(identifier.range, asIdentifier.range)
: identifier.range
);
} else {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
}
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseImport(
tn: Tokenizer
): ImportStatement | null {
// at 'import':
// ('{' (ImportMember (',' ImportMember)* '}') | ('*' 'as' Identifier)?
// 'from' StringLiteral ';'?
var startPos = tn.tokenPos;
var members: ImportDeclaration[] | null = null;
var namespaceName: IdentifierExpression | null = null;
var skipFrom = false;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.OPENBRACE)) {
members = new Array();
2017-09-28 13:08:25 +02:00
if (!tn.skip(Token.CLOSEBRACE)) {
do {
var member = this.parseImportDeclaration(tn);
2018-02-25 00:13:39 +01:00
if (!member) return null;
2017-09-28 13:08:25 +02:00
members.push(member);
} while (tn.skip(Token.COMMA));
if (!tn.skip(Token.CLOSEBRACE)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), "}"
);
2017-09-28 13:08:25 +02:00
return null;
}
}
} else if (tn.skip(Token.ASTERISK)) {
if (tn.skip(Token.AS)) {
if (tn.skip(Token.IDENTIFIER)) {
2018-01-05 01:55:59 +01:00
namespaceName = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
} else {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
return null;
}
} else {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), "as"
);
return null;
}
2018-02-25 00:13:39 +01:00
} else {
skipFrom = true;
2018-02-25 00:13:39 +01:00
}
if (skipFrom || tn.skip(Token.FROM)) {
if (tn.skip(Token.STRINGLITERAL)) {
2018-01-05 01:55:59 +01:00
var path = Node.createStringLiteralExpression(tn.readString(), tn.range());
var ret: ImportStatement;
if (namespaceName) {
assert(!members);
2018-01-05 01:55:59 +01:00
ret = Node.createImportStatementWithWildcard(namespaceName, path, tn.range(startPos, tn.pos));
} else {
ret = Node.createImportStatement(members, path, tn.range(startPos, tn.pos));
}
if (!this.seenlog.has(ret.normalizedPath)) {
this.backlog.push(ret.normalizedPath);
this.seenlog.add(ret.normalizedPath);
}
tn.skip(Token.SEMICOLON);
return ret;
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode.String_literal_expected,
tn.range()
);
}
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), "from"
);
}
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseImportDeclaration(
tn: Tokenizer
): ImportDeclaration | null {
// before: Identifier ('as' Identifier)?
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.IDENTIFIER)) {
2018-01-05 01:55:59 +01:00
var identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
var asIdentifier: IdentifierExpression | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.AS)) {
2018-02-25 00:13:39 +01:00
if (tn.skip(Token.IDENTIFIER)) {
2018-01-05 01:55:59 +01:00
asIdentifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
2017-09-28 13:08:25 +02:00
return null;
}
}
2018-02-25 00:13:39 +01:00
return Node.createImportDeclaration(
identifier,
asIdentifier,
asIdentifier
? Range.join(identifier.range, asIdentifier.range)
: identifier.range
);
} else {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
}
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseExportImport(
tn: Tokenizer,
startRange: Range
): ExportImportStatement | null {
2017-09-28 13:08:25 +02:00
// at 'export' 'import': Identifier ('=' Identifier)? ';'?
2018-02-25 00:13:39 +01:00
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.IDENTIFIER)) {
2018-01-05 01:55:59 +01:00
var asIdentifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.EQUALS)) {
if (tn.skip(Token.IDENTIFIER)) {
2018-01-05 01:55:59 +01:00
var identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
var ret = Node.createExportImportStatement(identifier, asIdentifier, Range.join(startRange, tn.range()));
2017-09-28 13:08:25 +02:00
tn.skip(Token.SEMICOLON);
return ret;
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
}
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), "="
);
}
} else {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
}
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseStatement(
tn: Tokenizer,
topLevel: bool = false
): Statement | null {
2017-09-28 13:08:25 +02:00
// at previous token
2018-02-25 00:13:39 +01:00
2017-09-28 13:08:25 +02:00
tn.mark();
var token = tn.next();
2017-09-28 13:08:25 +02:00
switch (token) {
case Token.BREAK:
return this.parseBreak(tn);
2017-09-28 13:08:25 +02:00
case Token.CONST:
2018-02-25 00:13:39 +01:00
return this.parseVariable(tn, [
Node.createModifier(ModifierKind.CONST, tn.range())
], null);
2017-09-28 13:08:25 +02:00
case Token.CONTINUE:
return this.parseContinue(tn);
2017-09-28 13:08:25 +02:00
case Token.DO:
return this.parseDoStatement(tn);
case Token.FOR:
return this.parseForStatement(tn);
case Token.IF:
return this.parseIfStatement(tn);
case Token.LET:
2018-02-25 00:13:39 +01:00
return this.parseVariable(tn, [
Node.createModifier(ModifierKind.LET, tn.range())
], null);
2017-09-28 13:08:25 +02:00
case Token.VAR:
return this.parseVariable(tn, null, null);
2017-09-28 13:08:25 +02:00
case Token.OPENBRACE:
return this.parseBlockStatement(tn, topLevel);
case Token.RETURN:
2018-02-25 00:13:39 +01:00
if (topLevel) {
this.error(
DiagnosticCode.A_return_statement_can_only_be_used_within_a_function_body,
tn.range()
); // recoverable
}
2017-09-28 13:08:25 +02:00
return this.parseReturn(tn);
case Token.SEMICOLON:
2018-01-05 01:55:59 +01:00
return Node.createEmptyStatement(tn.range(tn.tokenPos));
2017-09-28 13:08:25 +02:00
case Token.SWITCH:
return this.parseSwitchStatement(tn);
case Token.THROW:
return this.parseThrowStatement(tn);
case Token.TRY:
return this.parseTryStatement(tn);
case Token.TYPE:
return this.parseTypeDeclaration(tn, null);
2017-09-28 13:08:25 +02:00
case Token.WHILE:
return this.parseWhileStatement(tn);
default:
tn.reset();
return this.parseExpressionStatement(tn);
}
}
2018-02-25 00:13:39 +01:00
parseBlockStatement(
tn: Tokenizer,
topLevel: bool
): BlockStatement | null {
2017-09-28 13:08:25 +02:00
// at '{': Statement* '}' ';'?
2018-02-25 00:13:39 +01:00
var startPos = tn.tokenPos;
var statements = new Array<Statement>();
2017-09-28 13:08:25 +02:00
while (!tn.skip(Token.CLOSEBRACE)) {
var statement = this.parseStatement(tn, topLevel);
2018-02-25 00:13:39 +01:00
if (!statement) return null;
2017-09-28 13:08:25 +02:00
statements.push(statement);
}
2018-01-05 01:55:59 +01:00
var ret = Node.createBlockStatement(statements, tn.range(startPos, tn.pos));
2017-09-28 13:08:25 +02:00
tn.skip(Token.SEMICOLON);
return ret;
}
2018-02-25 00:13:39 +01:00
parseBreak(
tn: Tokenizer
): BreakStatement | null {
// at 'break': Identifier? ';'?
2018-02-25 00:13:39 +01:00
var identifier: IdentifierExpression | null = null;
if (tn.peek(true) == Token.IDENTIFIER && !tn.nextTokenOnNewLine) {
tn.next(true);
2018-01-05 01:55:59 +01:00
identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
}
2018-01-05 01:55:59 +01:00
var ret = Node.createBreakStatement(identifier, tn.range());
tn.skip(Token.SEMICOLON);
return ret;
}
2018-02-25 00:13:39 +01:00
parseContinue(
tn: Tokenizer
): ContinueStatement | null {
// at 'continue': Identifier? ';'?
2018-02-25 00:13:39 +01:00
var identifier: IdentifierExpression | null = null;
if (tn.peek(true) == Token.IDENTIFIER && !tn.nextTokenOnNewLine) {
tn.next(true);
2018-01-05 01:55:59 +01:00
identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
}
2018-01-05 01:55:59 +01:00
var ret = Node.createContinueStatement(identifier, tn.range());
tn.skip(Token.SEMICOLON);
return ret;
}
2018-02-25 00:13:39 +01:00
parseDoStatement(
tn: Tokenizer
): DoStatement | null {
2017-09-28 13:08:25 +02:00
// at 'do': Statement 'while' '(' Expression ')' ';'?
2018-02-25 00:13:39 +01:00
var startPos = tn.tokenPos;
var statement = this.parseStatement(tn);
2018-02-25 00:13:39 +01:00
if (!statement) return null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.WHILE)) {
2018-02-25 00:13:39 +01:00
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.OPENPAREN)) {
var condition = this.parseExpression(tn);
2018-02-25 00:13:39 +01:00
if (!condition) return null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.CLOSEPAREN)) {
2018-01-05 01:55:59 +01:00
var ret = Node.createDoStatement(<Statement>statement, <Expression>condition, tn.range(startPos, tn.pos));
2017-09-28 13:08:25 +02:00
tn.skip(Token.SEMICOLON);
return ret;
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), ")"
);
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), "("
);
}
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), "while"
);
}
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseExpressionStatement(
tn: Tokenizer
): ExpressionStatement | null {
2017-10-07 14:29:43 +02:00
// at previous token
2018-02-25 00:13:39 +01:00
var expr = this.parseExpression(tn);
2018-02-25 00:13:39 +01:00
if (!expr) return null;
2018-01-05 01:55:59 +01:00
var ret = Node.createExpressionStatement(expr);
2017-09-28 13:08:25 +02:00
tn.skip(Token.SEMICOLON);
return ret;
}
2018-02-25 00:13:39 +01:00
parseForStatement(
tn: Tokenizer
): ForStatement | null {
2017-09-28 13:08:25 +02:00
// at 'for': '(' Statement? Expression? ';' Expression? ')' Statement
2018-02-25 00:13:39 +01:00
var startPos = tn.tokenPos;
2018-02-25 00:13:39 +01:00
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.OPENPAREN)) {
2018-02-25 00:13:39 +01:00
var initializer: Statement | null = null;
2018-02-25 00:13:39 +01:00
2017-11-29 00:18:14 +01:00
if (tn.skip(Token.LET) || tn.skip(Token.CONST) || tn.skip(Token.VAR)) {
initializer = this.parseVariable(tn, null, null);
2018-02-25 00:13:39 +01:00
2017-11-29 00:18:14 +01:00
} else if (!tn.skip(Token.SEMICOLON)) {
initializer = this.parseExpressionStatement(tn);
2018-02-25 00:13:39 +01:00
if (!initializer) return null;
2017-11-29 00:18:14 +01:00
}
2018-02-25 00:13:39 +01:00
2017-11-29 00:18:14 +01:00
if (tn.token == Token.SEMICOLON) {
var condition: ExpressionStatement | null = null;
2017-11-29 00:18:14 +01:00
if (!tn.skip(Token.SEMICOLON)) {
condition = this.parseExpressionStatement(tn);
2018-02-25 00:13:39 +01:00
if (!condition) return null;
2017-11-29 00:18:14 +01:00
}
2018-02-25 00:13:39 +01:00
2017-11-29 00:18:14 +01:00
if (tn.token == Token.SEMICOLON) {
var incrementor: Expression | null = null;
2017-11-29 00:18:14 +01:00
if (!tn.skip(Token.CLOSEPAREN)) {
incrementor = this.parseExpression(tn);
2018-02-25 00:13:39 +01:00
if (!incrementor) return null;
2017-11-29 00:18:14 +01:00
if (!tn.skip(Token.CLOSEPAREN)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), ")"
);
2017-11-29 00:18:14 +01:00
return null;
}
}
2018-02-25 00:13:39 +01:00
var statement = this.parseStatement(tn);
2018-02-25 00:13:39 +01:00
if (!statement) return null;
return Node.createForStatement(
initializer,
condition
? condition.expression
: null,
incrementor,
statement,
tn.range(startPos, tn.pos)
);
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), ";"
);
}
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), ";"
);
}
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), "("
);
}
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseIfStatement(
tn: Tokenizer
): IfStatement | null {
2017-09-28 13:08:25 +02:00
// at 'if': '(' Expression ')' Statement ('else' Statement)?
2018-02-25 00:13:39 +01:00
var startPos = tn.tokenPos;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.OPENPAREN)) {
var condition = this.parseExpression(tn);
2018-02-25 00:13:39 +01:00
if (!condition) return null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.CLOSEPAREN)) {
var statement = this.parseStatement(tn);
2018-02-25 00:13:39 +01:00
if (!statement) return null;
var elseStatement: Statement | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.ELSE)) {
elseStatement = this.parseStatement(tn);
2018-02-25 00:13:39 +01:00
if (!elseStatement) return null;
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
return Node.createIfStatement(
condition,
statement,
elseStatement,
tn.range(startPos, tn.pos)
);
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), ")"
);
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), "("
);
}
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseSwitchStatement(
tn: Tokenizer
): SwitchStatement | null {
2017-09-28 13:08:25 +02:00
// at 'switch': '(' Expression ')' '{' SwitchCase* '}' ';'?
2018-02-25 00:13:39 +01:00
var startPos = tn.tokenPos;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.OPENPAREN)) {
var condition = this.parseExpression(tn);
2018-02-25 00:13:39 +01:00
if (!condition) return null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.CLOSEPAREN)) {
if (tn.skip(Token.OPENBRACE)) {
var cases = new Array<SwitchCase>();
2017-09-28 13:08:25 +02:00
while (!tn.skip(Token.CLOSEBRACE)) {
var case_ = this.parseSwitchCase(tn);
2018-02-25 00:13:39 +01:00
if (!case_) return null;
2017-09-28 13:08:25 +02:00
cases.push(<SwitchCase>case_);
}
2018-01-05 01:55:59 +01:00
var ret = Node.createSwitchStatement(condition, cases, tn.range(startPos, tn.pos));
2017-09-28 13:08:25 +02:00
tn.skip(Token.SEMICOLON);
return ret;
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), "{"
);
}
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), ")"
);
}
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), "("
);
}
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseSwitchCase(
tn: Tokenizer
): SwitchCase | null {
var startPos = tn.tokenPos;
var statements: Statement[],
statement: Statement | null;
2017-09-28 13:08:25 +02:00
// 'case' Expression ':' Statement*
2018-02-25 00:13:39 +01:00
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.CASE)) {
var label = this.parseExpression(tn);
2018-02-25 00:13:39 +01:00
if (!label) return null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.COLON)) {
statements = new Array<Statement>();
2017-09-28 13:08:25 +02:00
while (tn.peek() != Token.CASE && tn.nextToken != Token.DEFAULT && tn.nextToken != Token.CLOSEBRACE) {
statement = this.parseStatement(tn);
2018-02-25 00:13:39 +01:00
if (!statement) return null;
statements.push(statement);
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
return Node.createSwitchCase(label, statements, tn.range(startPos, tn.pos));
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), ":"
);
}
2017-09-28 13:08:25 +02:00
// 'default' ':' Statement*
2018-02-25 00:13:39 +01:00
2017-12-02 20:58:39 +01:00
} else if (tn.skip(Token.DEFAULT)) {
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.COLON)) {
statements = new Array<Statement>();
2017-09-28 13:08:25 +02:00
while (tn.peek() != Token.CASE && tn.nextToken != Token.DEFAULT && tn.nextToken != Token.CLOSEBRACE) {
statement = this.parseStatement(tn);
2018-02-25 00:13:39 +01:00
if (!statement) return null;
statements.push(statement);
2017-09-28 13:08:25 +02:00
}
2017-12-16 17:54:53 +01:00
return Node.createSwitchCase(null, statements, tn.range(startPos, tn.pos));
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), ":"
);
}
} else {
this.error(
DiagnosticCode._case_or_default_expected,
tn.range()
);
}
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseThrowStatement(
tn: Tokenizer
): ThrowStatement | null {
2017-09-28 13:08:25 +02:00
// at 'throw': Expression ';'?
2018-02-25 00:13:39 +01:00
var startPos = tn.tokenPos;
var expression = this.parseExpression(tn);
2018-02-25 00:13:39 +01:00
if (!expression) return null;
2018-01-05 01:55:59 +01:00
var ret = Node.createThrowStatement(<Expression>expression, tn.range(startPos, tn.pos));
2017-09-28 13:08:25 +02:00
tn.skip(Token.SEMICOLON);
return ret;
}
2018-02-25 00:13:39 +01:00
parseTryStatement(
tn: Tokenizer
): TryStatement | null {
// at 'try':
// '{' Statement* '}'
// ('catch' '(' VariableMember ')' '{' Statement* '}')?
// ('finally' '{' Statement* '}'? ';'?
var startPos = tn.tokenPos;
var stmt: Statement | null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.OPENBRACE)) {
var statements = new Array<Statement>();
2017-09-28 13:08:25 +02:00
while (!tn.skip(Token.CLOSEBRACE)) {
stmt = this.parseStatement(tn);
2018-02-25 00:13:39 +01:00
if (!stmt) return null;
2017-09-28 13:08:25 +02:00
statements.push(<Statement>stmt);
}
var catchVariable: IdentifierExpression | null = null;
var catchStatements: Statement[] | null = null;
var finallyStatements: Statement[] | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.CATCH)) {
2017-10-07 14:29:43 +02:00
if (!tn.skip(Token.OPENPAREN)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), "("
);
2017-10-07 14:29:43 +02:00
return null;
}
if (!tn.skip(Token.IDENTIFIER)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
2017-10-07 14:29:43 +02:00
return null;
}
2018-01-05 01:55:59 +01:00
catchVariable = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
2017-10-07 14:29:43 +02:00
if (!tn.skip(Token.CLOSEPAREN)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), ")"
);
2017-10-07 14:29:43 +02:00
return null;
}
if (!tn.skip(Token.OPENBRACE)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), "{"
);
2017-10-07 14:29:43 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
catchStatements = [];
2017-10-07 14:29:43 +02:00
while (!tn.skip(Token.CLOSEBRACE)) {
2018-02-25 00:13:39 +01:00
stmt = this.parseStatement(tn);
if (!stmt) return null;
2017-10-07 14:29:43 +02:00
catchStatements.push(<Statement>stmt);
}
}
if (tn.skip(Token.FINALLY)) {
if (!tn.skip(Token.OPENBRACE)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), "{"
);
2017-10-07 14:29:43 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
finallyStatements = [];
2017-10-07 14:29:43 +02:00
while (!tn.skip(Token.CLOSEBRACE)) {
stmt = this.parseStatement(tn);
2018-02-25 00:13:39 +01:00
if (!stmt) return null;
2017-10-07 14:29:43 +02:00
finallyStatements.push(<Statement>stmt);
}
}
if (!(catchStatements || finallyStatements)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), "catch"
);
2017-10-07 14:29:43 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
var ret = Node.createTryStatement(
statements,
catchVariable,
catchStatements,
finallyStatements,
tn.range(startPos, tn.pos)
);
2017-10-07 14:29:43 +02:00
tn.skip(Token.SEMICOLON);
return ret;
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), "{"
);
}
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
parseTypeDeclaration(
tn: Tokenizer,
modifiers: Modifier[] | null = null,
decorators: Decorator[] | null = null
): TypeDeclaration | null {
// at 'type': Identifier '=' Type ';'?
2018-02-25 00:13:39 +01:00
var startPos = decorators && decorators.length ? decorators[0].range.start
: modifiers && modifiers.length ? modifiers[0].range.start
: tn.tokenPos;
if (tn.skip(Token.IDENTIFIER)) {
2018-01-05 01:55:59 +01:00
var name = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
if (tn.skip(Token.EQUALS)) {
var type = this.parseType(tn);
2018-02-25 00:13:39 +01:00
if (!type) return null;
var ret = Node.createTypeDeclaration(name, type, modifiers, decorators, tn.range(startPos, tn.pos));
tn.skip(Token.SEMICOLON);
return ret;
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), "="
);
}
} else {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
}
return null;
}
2018-02-25 00:13:39 +01:00
parseWhileStatement(
tn: Tokenizer
): WhileStatement | null {
2017-09-28 13:08:25 +02:00
// at 'while': '(' Expression ')' Statement ';'?
2018-02-25 00:13:39 +01:00
var startPos = tn.tokenPos;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.OPENPAREN)) {
var expression = this.parseExpression(tn);
2018-02-25 00:13:39 +01:00
if (!expression) return null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.CLOSEPAREN)) {
var statement = this.parseStatement(tn);
2018-02-25 00:13:39 +01:00
if (!statement) return null;
2018-01-05 01:55:59 +01:00
var ret = Node.createWhileStatement(<Expression>expression, <Statement>statement, tn.range(startPos, tn.pos));
2017-09-28 13:08:25 +02:00
tn.skip(Token.SEMICOLON);
return ret;
2018-02-25 00:13:39 +01:00
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), ")"
);
}
} else {
this.error(
DiagnosticCode._0_expected,
tn.range(), "("
);
}
2017-09-28 13:08:25 +02:00
return null;
}
// expressions
// see: http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm#climbing
2018-02-25 00:13:39 +01:00
parseExpressionStart(
tn: Tokenizer
): Expression | null {
var token = tn.next(true);
var startPos = tn.tokenPos;
var expr: Expression | null = null;
2017-09-28 13:08:25 +02:00
2018-02-25 00:13:39 +01:00
if (token == Token.NULL) {
2018-01-05 01:55:59 +01:00
return Node.createNullExpression(tn.range());
2018-02-25 00:13:39 +01:00
}
if (token == Token.TRUE) {
2018-01-05 01:55:59 +01:00
return Node.createTrueExpression(tn.range());
2018-02-25 00:13:39 +01:00
}
if (token == Token.FALSE) {
2018-01-05 01:55:59 +01:00
return Node.createFalseExpression(tn.range());
2018-02-25 00:13:39 +01:00
}
2017-09-28 13:08:25 +02:00
var p = determinePrecedenceStart(token);
2017-09-28 13:08:25 +02:00
if (p != Precedence.INVALID) {
var operand: Expression | null;
2017-09-28 13:08:25 +02:00
// TODO: SpreadExpression, YieldExpression (currently become unsupported UnaryPrefixExpressions)
// NewExpression
if (token == Token.NEW) {
2017-12-16 02:27:39 +01:00
operand = this.parseExpression(tn, Precedence.CALL);
2018-02-25 00:13:39 +01:00
if (!operand) return null;
if (operand.kind == NodeKind.CALL) {
return Node.createNewExpression(
(<CallExpression>operand).expression,
(<CallExpression>operand).typeArguments,
(<CallExpression>operand).arguments,
tn.range(startPos, tn.pos)
);
} else {
this.error(
DiagnosticCode.Operation_not_supported,
tn.range()
);
}
2017-12-16 02:27:39 +01:00
return null;
2018-01-07 18:15:21 +01:00
} else {
2017-12-16 02:27:39 +01:00
operand = this.parseExpression(tn, p);
2018-02-25 00:13:39 +01:00
if (!operand) return null;
2018-01-07 18:15:21 +01:00
}
2017-09-28 13:08:25 +02:00
// UnaryPrefixExpression
2018-02-25 00:13:39 +01:00
if (token == Token.PLUS_PLUS || token == Token.MINUS_MINUS) {
if (
operand.kind != NodeKind.IDENTIFIER &&
operand.kind != NodeKind.ELEMENTACCESS &&
operand.kind != NodeKind.PROPERTYACCESS
) {
this.error(
DiagnosticCode.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access,
operand.range
);
}
}
return Node.createUnaryPrefixExpression(token, operand, tn.range(startPos, tn.pos));
2017-09-28 13:08:25 +02:00
}
switch (token) {
// ParenthesizedExpression
2018-02-27 00:30:04 +01:00
// FunctionExpression
2018-02-25 00:13:39 +01:00
case Token.OPENPAREN:
2018-02-27 00:30:04 +01:00
// determine whether this is a function expression
if (tn.skip(Token.CLOSEPAREN)) { // must be a function expression (fast route)
return this.parseFunctionExpressionCommon(
tn,
Node.createIdentifierExpression("", tn.range(startPos, startPos)),
[],
true
);
}
tn.mark();
var again = true;
do {
switch (tn.next(true)) {
// function expression
case Token.DOT_DOT_DOT:
tn.reset();
return this.parseFunctionExpression(tn);
// can be both
case Token.IDENTIFIER:
tn.readIdentifier();
switch (tn.next()) {
// if we got here, check for arrow
case Token.CLOSEPAREN:
if (!tn.skip(Token.EQUALS_GREATERTHAN)) {
again = false;
break;
}
// fall-through
// function expression
case Token.QUESTION: // optional parameter
case Token.COLON: // type annotation
tn.reset();
return this.parseFunctionExpression(tn);
// can be both
case Token.COMMA:
break; // continue
// parenthesized expression
// case Token.EQUALS: // missing type annotation for simplicity
default:
again = false;
break;
}
break;
// parenthesized expression
default:
again = false;
break;
}
} while (again);
tn.reset();
// parse parenthesized
2017-11-29 00:18:14 +01:00
expr = this.parseExpression(tn);
2018-02-25 00:13:39 +01:00
if (!expr) return null;
2017-09-28 13:08:25 +02:00
if (!tn.skip(Token.CLOSEPAREN)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), ")"
);
2017-09-28 13:08:25 +02:00
return null;
}
2018-01-05 01:55:59 +01:00
return Node.createParenthesizedExpression(expr, tn.range(startPos, tn.pos));
2017-09-28 13:08:25 +02:00
// ArrayLiteralExpression
2018-02-25 00:13:39 +01:00
case Token.OPENBRACKET:
var elementExpressions = new Array<Expression | null>();
2018-02-25 00:13:39 +01:00
2017-09-28 13:08:25 +02:00
if (!tn.skip(Token.CLOSEBRACKET)) {
do {
2018-02-25 00:13:39 +01:00
if (tn.peek() == Token.COMMA) {
2017-09-28 13:08:25 +02:00
expr = null; // omitted
2018-02-25 00:13:39 +01:00
} else {
2017-09-28 13:08:25 +02:00
expr = this.parseExpression(tn, Precedence.COMMA + 1);
2018-02-25 00:13:39 +01:00
if (!expr) return null;
2017-09-28 13:08:25 +02:00
}
elementExpressions.push(expr);
2018-02-25 00:13:39 +01:00
if (tn.peek() == Token.CLOSEBRACKET) break;
2017-09-28 13:08:25 +02:00
} while (tn.skip(Token.COMMA));
if (!tn.skip(Token.CLOSEBRACKET)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), "]"
);
2017-09-28 13:08:25 +02:00
return null;
}
}
2018-01-05 01:55:59 +01:00
return Node.createArrayLiteralExpression(elementExpressions, tn.range(startPos, tn.pos));
2017-09-28 13:08:25 +02:00
// AssertionExpression (unary prefix)
2018-02-25 00:13:39 +01:00
case Token.LESSTHAN:
var toType = this.parseType(tn);
2018-02-25 00:13:39 +01:00
if (!toType) return null;
2017-09-28 13:08:25 +02:00
if (!tn.skip(Token.GREATERTHAN)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), ">"
);
2017-09-28 13:08:25 +02:00
return null;
}
2017-12-16 02:27:39 +01:00
expr = this.parseExpression(tn, Precedence.CALL);
2018-02-25 00:13:39 +01:00
if (!expr) return null;
return Node.createAssertionExpression(
AssertionKind.PREFIX,
expr,
toType,
tn.range(startPos, tn.pos)
);
2017-09-28 13:08:25 +02:00
2017-10-07 14:29:43 +02:00
case Token.IDENTIFIER:
2018-01-05 01:55:59 +01:00
return Node.createIdentifierExpression(tn.readIdentifier(), tn.range(startPos, tn.pos));
2017-10-07 14:29:43 +02:00
2017-12-16 02:27:39 +01:00
case Token.THIS:
2018-01-05 01:55:59 +01:00
return Node.createThisExpression(tn.range(startPos, tn.pos));
2017-12-16 02:27:39 +01:00
case Token.CONSTRUCTOR:
2018-01-05 01:55:59 +01:00
return Node.createConstructorExpression(tn.range(startPos, tn.pos));
2017-12-16 02:27:39 +01:00
case Token.SUPER:
2018-01-05 01:55:59 +01:00
return Node.createSuperExpression(tn.range(startPos, tn.pos));
2017-12-16 02:27:39 +01:00
2017-09-28 13:08:25 +02:00
case Token.STRINGLITERAL:
2018-01-05 01:55:59 +01:00
return Node.createStringLiteralExpression(tn.readString(), tn.range(startPos, tn.pos));
2017-09-28 13:08:25 +02:00
case Token.INTEGERLITERAL:
2018-01-05 01:55:59 +01:00
return Node.createIntegerLiteralExpression(tn.readInteger(), tn.range(startPos, tn.pos));
2017-09-28 13:08:25 +02:00
2018-02-27 00:30:04 +01:00
case Token.FLOATLITERAL:
2018-01-05 01:55:59 +01:00
return Node.createFloatLiteralExpression(tn.readFloat(), tn.range(startPos, tn.pos));
2017-09-28 13:08:25 +02:00
// RegexpLiteralExpression
2018-01-07 18:15:21 +01:00
// note that this also continues on invalid ones so the surrounding AST remains intact
2018-01-02 05:02:05 +01:00
case Token.SLASH:
2018-01-07 18:15:21 +01:00
var regexpPattern = tn.readRegexpPattern(); // also reports
2018-01-02 05:02:05 +01:00
if (!tn.skip(Token.SLASH)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), "/"
);
2018-01-02 05:02:05 +01:00
return null;
}
2018-02-25 00:13:39 +01:00
return Node.createRegexpLiteralExpression(
regexpPattern,
tn.readRegexpFlags(), // also reports
tn.range(startPos, tn.pos)
);
2017-09-28 13:08:25 +02:00
2018-02-27 00:30:04 +01:00
case Token.FUNCTION:
return this.parseFunctionExpression(tn);
2017-09-28 13:08:25 +02:00
default:
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Expression_expected,
tn.range()
);
2017-09-28 13:08:25 +02:00
return null;
}
}
2018-02-25 00:13:39 +01:00
tryParseTypeArgumentsBeforeArguments(
tn: Tokenizer
): TypeNode[] | null {
// at '<': Type (',' Type)* '>' '('
2017-09-28 13:08:25 +02:00
2018-02-25 00:13:39 +01:00
tn.mark();
if (!tn.skip(Token.LESSTHAN)) return null;
var typeArguments = new Array<TypeNode>();
2017-09-28 13:08:25 +02:00
do {
var type = this.parseType(tn, true, true);
if (!type) {
2017-09-28 13:08:25 +02:00
tn.reset();
return null;
}
typeArguments.push(type);
2017-09-28 13:08:25 +02:00
} while (tn.skip(Token.COMMA));
2018-02-25 00:13:39 +01:00
if (tn.skip(Token.GREATERTHAN) && tn.skip(Token.OPENPAREN)) {
return typeArguments;
2018-02-25 00:13:39 +01:00
}
tn.reset();
return null;
2017-09-28 13:08:25 +02:00
}
2018-02-25 00:13:39 +01:00
parseArguments(
tn: Tokenizer
): Expression[] | null {
2017-09-28 13:08:25 +02:00
// at '(': (Expression (',' Expression)*)? ')'
2018-02-25 00:13:39 +01:00
var args = new Array<Expression>();
2017-09-28 13:08:25 +02:00
if (!tn.skip(Token.CLOSEPAREN)) {
do {
var expr = this.parseExpression(tn, Precedence.COMMA + 1);
2018-02-25 00:13:39 +01:00
if (!expr) return null;
args.push(expr);
2017-09-28 13:08:25 +02:00
} while (tn.skip(Token.COMMA));
if (!tn.skip(Token.CLOSEPAREN)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), ")"
);
2017-09-28 13:08:25 +02:00
return null;
}
}
return args;
}
2018-02-25 00:13:39 +01:00
parseExpression(
tn: Tokenizer,
precedence: Precedence = 0
): Expression | null {
2017-09-28 13:08:25 +02:00
2018-02-25 00:13:39 +01:00
var expr = this.parseExpressionStart(tn);
if (!expr) return null;
var startPos = expr.range.start;
2017-09-28 13:08:25 +02:00
// CallExpression
var typeArguments = this.tryParseTypeArgumentsBeforeArguments(tn); // skips '(' on success
// there might be better ways to distinguish a LESSTHAN from a CALL with type arguments
2017-09-28 13:08:25 +02:00
if (typeArguments || tn.skip(Token.OPENPAREN)) {
var args = this.parseArguments(tn);
2018-02-25 00:13:39 +01:00
if (!args) return null;
2018-01-05 01:55:59 +01:00
expr = Node.createCallExpression(expr, typeArguments, args, tn.range(startPos, tn.pos));
2017-09-28 13:08:25 +02:00
}
var token: Token;
var next: Expression | null = null;
var nextPrecedence: Precedence;
2018-02-25 00:13:39 +01:00
while (
(nextPrecedence = determinePrecedence(token = tn.peek())) >= precedence
) { // precedence climbing
2017-09-28 13:08:25 +02:00
tn.next();
2018-01-29 22:36:07 +01:00
switch (token) {
// AssertionExpression
case Token.AS:
var toType = this.parseType(tn);
2018-02-25 00:13:39 +01:00
if (!toType) return null;
expr = Node.createAssertionExpression(
AssertionKind.AS,
expr,
toType,
tn.range(startPos, tn.pos)
);
2018-01-29 22:36:07 +01:00
break;
2017-09-28 13:08:25 +02:00
2018-01-29 22:36:07 +01:00
// ElementAccessExpression
case Token.OPENBRACKET:
next = this.parseExpression(tn);
2018-02-25 00:13:39 +01:00
if (!next) return null;
2018-01-29 22:36:07 +01:00
if (!tn.skip(Token.CLOSEBRACKET)) {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode._0_expected,
tn.range(), "]"
);
2018-01-29 22:36:07 +01:00
return null;
}
2018-02-25 00:13:39 +01:00
expr = Node.createElementAccessExpression(
expr,
next,
tn.range(startPos, tn.pos)
);
2018-01-29 22:36:07 +01:00
break;
2018-01-05 01:55:59 +01:00
2018-01-29 22:36:07 +01:00
// UnaryPostfixExpression
case Token.PLUS_PLUS:
case Token.MINUS_MINUS:
2018-02-25 00:13:39 +01:00
if (
expr.kind != NodeKind.IDENTIFIER &&
expr.kind != NodeKind.ELEMENTACCESS &&
expr.kind != NodeKind.PROPERTYACCESS
) {
this.error(
DiagnosticCode.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access,
expr.range
);
}
expr = Node.createUnaryPostfixExpression(
token,
expr,
tn.range(startPos, tn.pos)
);
2018-01-29 22:36:07 +01:00
break;
2017-09-28 13:08:25 +02:00
2018-01-29 22:36:07 +01:00
// TernaryExpression
case Token.QUESTION:
var ifThen = this.parseExpression(tn);
2018-02-25 00:13:39 +01:00
if (!ifThen) return null;
if (!tn.skip(Token.COLON)) {
this.error(
DiagnosticCode._0_expected,
tn.range(), ":"
);
2017-09-28 13:08:25 +02:00
return null;
}
2018-02-25 00:13:39 +01:00
var ifElse = this.parseExpression(tn);
if (!ifElse) return null;
expr = Node.createTernaryExpression(
expr,
ifThen,
ifElse,
tn.range(startPos, tn.pos)
);
2018-01-29 22:36:07 +01:00
break;
2017-09-28 13:08:25 +02:00
2018-01-29 22:36:07 +01:00
// CommaExpression
case Token.COMMA:
2018-02-25 00:13:39 +01:00
var commaExprs: Expression[] = [ expr ];
2018-01-29 22:36:07 +01:00
do {
expr = this.parseExpression(tn, Precedence.COMMA + 1);
2018-02-25 00:13:39 +01:00
if (!expr) return null;
2018-01-29 22:36:07 +01:00
commaExprs.push(expr);
} while (tn.skip(Token.COMMA));
expr = Node.createCommaExpression(commaExprs, tn.range(startPos, tn.pos));
break;
default:
2018-02-25 00:13:39 +01:00
next = this.parseExpression(tn,
isRightAssociative(token)
? nextPrecedence
: nextPrecedence + 1
);
if (!next) return null;
2018-01-29 22:36:07 +01:00
// PropertyAccessExpression
if (token == Token.DOT) {
2018-02-25 00:13:39 +01:00
if (next.kind == NodeKind.IDENTIFIER) {
expr = Node.createPropertyAccessExpression(
expr,
<IdentifierExpression>next,
tn.range(startPos, tn.pos)
);
} else if (next.kind == NodeKind.CALL) { // join
2018-01-29 22:36:07 +01:00
var propertyCall = <CallExpression>next;
if (propertyCall.expression.kind == NodeKind.IDENTIFIER) {
2018-02-25 00:13:39 +01:00
propertyCall.expression = Node.createPropertyAccessExpression(
expr,
<IdentifierExpression>propertyCall.expression,
tn.range(startPos, tn.pos)
);
2018-01-29 22:36:07 +01:00
} else {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Identifier_expected,
propertyCall.expression.range
);
2018-01-29 22:36:07 +01:00
return null;
}
expr = propertyCall;
} else {
2018-02-25 00:13:39 +01:00
this.error(
DiagnosticCode.Identifier_expected,
next.range
);
2018-01-29 22:36:07 +01:00
return null;
}
// BinaryExpression
2018-02-25 00:13:39 +01:00
} else {
expr = Node.createBinaryExpression(token, expr, next, tn.range(startPos, tn.pos));
}
2018-01-29 22:36:07 +01:00
break;
2017-09-28 13:08:25 +02:00
}
}
return expr;
}
}
/** Operator precedence from least to largest. */
export const enum Precedence {
2017-09-28 13:08:25 +02:00
COMMA,
SPREAD,
YIELD,
ASSIGNMENT,
CONDITIONAL,
LOGICAL_OR,
LOGICAL_AND,
BITWISE_OR,
BITWISE_XOR,
BITWISE_AND,
EQUALITY,
RELATIONAL,
SHIFT,
ADDITIVE,
MULTIPLICATIVE,
EXPONENTIATED,
UNARY_PREFIX,
UNARY_POSTFIX,
CALL,
MEMBERACCESS,
GROUPING,
INVALID = -1
}
2017-12-18 03:46:36 +01:00
/** Determines the precedence of a starting token. */
function determinePrecedenceStart(kind: Token): i32 {
2017-09-28 13:08:25 +02:00
switch (kind) {
case Token.DOT_DOT_DOT:
return Precedence.SPREAD;
case Token.YIELD:
return Precedence.YIELD;
case Token.EXCLAMATION:
case Token.TILDE:
case Token.PLUS:
case Token.MINUS:
case Token.PLUS_PLUS:
case Token.MINUS_MINUS:
case Token.TYPEOF:
case Token.VOID:
case Token.DELETE:
return Precedence.UNARY_PREFIX;
case Token.NEW:
return Precedence.MEMBERACCESS;
default:
return Precedence.INVALID;
}
}
2017-12-18 03:46:36 +01:00
/** Determines the precende of a non-starting token. */
function determinePrecedence(kind: Token): i32 {
2017-09-28 13:08:25 +02:00
switch (kind) {
case Token.COMMA:
return Precedence.COMMA;
case Token.EQUALS:
case Token.PLUS_EQUALS:
case Token.MINUS_EQUALS:
case Token.ASTERISK_ASTERISK_EQUALS:
case Token.ASTERISK_EQUALS:
case Token.SLASH_EQUALS:
case Token.PERCENT_EQUALS:
case Token.LESSTHAN_LESSTHAN_EQUALS:
case Token.GREATERTHAN_GREATERTHAN_EQUALS:
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS:
case Token.AMPERSAND_EQUALS:
case Token.CARET_EQUALS:
case Token.BAR_EQUALS:
return Precedence.ASSIGNMENT;
case Token.QUESTION:
return Precedence.CONDITIONAL;
case Token.BAR_BAR:
return Precedence.LOGICAL_OR;
case Token.AMPERSAND_AMPERSAND:
return Precedence.LOGICAL_AND;
case Token.BAR:
return Precedence.BITWISE_OR;
case Token.CARET:
return Precedence.BITWISE_XOR;
case Token.AMPERSAND:
return Precedence.BITWISE_AND;
case Token.EQUALS_EQUALS:
case Token.EXCLAMATION_EQUALS:
case Token.EQUALS_EQUALS_EQUALS:
case Token.EXCLAMATION_EQUALS_EQUALS:
return Precedence.EQUALITY;
case Token.AS:
case Token.IN:
case Token.INSTANCEOF:
case Token.LESSTHAN:
case Token.GREATERTHAN:
case Token.LESSTHAN_EQUALS:
case Token.GREATERTHAN_EQUALS:
return Precedence.RELATIONAL;
case Token.LESSTHAN_LESSTHAN:
case Token.GREATERTHAN_GREATERTHAN:
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN:
return Precedence.SHIFT;
case Token.PLUS:
case Token.MINUS:
return Precedence.ADDITIVE;
case Token.ASTERISK:
case Token.SLASH:
case Token.PERCENT:
return Precedence.MULTIPLICATIVE;
case Token.ASTERISK_ASTERISK:
return Precedence.EXPONENTIATED;
case Token.PLUS_PLUS:
case Token.MINUS_MINUS:
return Precedence.UNARY_POSTFIX;
case Token.DOT:
case Token.NEW:
2018-01-29 22:36:07 +01:00
case Token.OPENBRACKET:
2017-09-28 13:08:25 +02:00
return Precedence.MEMBERACCESS;
default:
return Precedence.INVALID;
}
}
2017-12-18 03:46:36 +01:00
/** Determines whether a non-starting token is right associative. */
function isRightAssociative(kind: Token): bool {
2017-09-28 13:08:25 +02:00
switch (kind) {
case Token.EQUALS:
case Token.PLUS_EQUALS:
case Token.MINUS_EQUALS:
case Token.ASTERISK_ASTERISK_EQUALS:
case Token.ASTERISK_EQUALS:
case Token.SLASH_EQUALS:
case Token.PERCENT_EQUALS:
case Token.LESSTHAN_LESSTHAN_EQUALS:
case Token.GREATERTHAN_GREATERTHAN_EQUALS:
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN_EQUALS:
case Token.AMPERSAND_EQUALS:
case Token.CARET_EQUALS:
case Token.BAR_EQUALS:
case Token.QUESTION:
case Token.ASTERISK_ASTERISK:
return true;
default:
return false;
}
}