assemblyscript/src/parser.ts

3085 lines
84 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,
CommonTypeNode,
2017-09-28 13:08:25 +02:00
TypeNode,
SignatureNode,
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,
DecoratorNode,
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,
ModifierNode,
2017-09-28 13:08:25 +02:00
ModifierKind,
2017-12-13 23:24:13 +01:00
NamespaceDeclaration,
ParameterNode,
ParameterKind,
2017-09-28 13:08:25 +02:00
ReturnStatement,
SwitchCase,
SwitchStatement,
ThrowStatement,
TryStatement,
TypeDeclaration,
TypeParameterNode,
2017-09-28 13:08:25 +02:00
VariableStatement,
VariableDeclaration,
VoidStatement,
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: DecoratorNode[] | 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: ModifierNode[] | 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
// parse the statement
var statement: Statement | null = null;
var modifier: ModifierNode | null;
2018-02-25 00:13:39 +01:00
// handle declarations
switch (tn.peek()) {
case Token.CONST: {
tn.next();
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;
} else {
statement = this.parseVariable(tn, modifiers, decorators);
decorators = null;
}
break;
}
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: {
tn.next();
statement = this.parseVariable(tn, modifiers, decorators);
decorators = null;
break;
}
case Token.ENUM: {
tn.next();
statement = this.parseEnum(tn, modifiers, decorators);
decorators = null;
break;
}
case Token.FUNCTION: {
tn.next();
statement = this.parseFunction(tn, modifiers, decorators);
decorators = null;
break;
}
case Token.ABSTRACT: {
tn.next();
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
}
case Token.CLASS: {
tn.next();
statement = this.parseClass(tn, modifiers, decorators);
decorators = null;
break;
}
case Token.NAMESPACE: {
tn.next();
statement = this.parseNamespace(tn, modifiers, decorators);
decorators = null;
break;
}
case Token.IMPORT: {
tn.next();
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: {
tn.next();
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)) {
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);
}
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 {
var backlog = this.backlog;
return backlog.length ? 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
): CommonTypeNode | null {
2018-02-25 00:13:39 +01:00
// 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: CommonTypeNode;
2017-09-28 13:08:25 +02:00
// '(' ...
if (token == Token.OPENPAREN) {
// '(' FunctionSignature ')' '|' 'null'?
let isNullableSignature = tn.skip(Token.OPENPAREN);
// FunctionSignature?
let signature = this.tryParseSignature(tn);
if (signature) {
if (isNullableSignature) {
if (!tn.skip(Token.CLOSEPAREN)) {
this.error(
DiagnosticCode._0_expected,
tn.range(), ")"
);
return null;
}
if (!tn.skip(Token.BAR)) {
this.error(
DiagnosticCode._0_expected,
tn.range(), "|"
);
return null;
}
if (!tn.skip(Token.NULL)) {
this.error(
DiagnosticCode._0_expected,
tn.range(), "null"
);
}
signature.isNullable = true;
}
return signature;
} else if (isNullableSignature || this.tryParseSignatureIsSignature) {
this.error(
DiagnosticCode.Unexpected_token,
tn.range()
);
return null;
}
// Type (',' Type)* ')'
if (acceptParenthesized) {
let innerType = this.parseType(tn, false, suppressErrors);
if (!innerType) return null;
if (!tn.skip(Token.CLOSEPAREN)) {
if (!suppressErrors) {
this.error(
DiagnosticCode._0_expected,
tn.range(tn.pos), "}"
);
}
return null;
2018-02-25 00:13:39 +01:00
}
type = innerType;
type.range.start = startPos;
type.range.end = tn.pos;
} else {
this.error(
DiagnosticCode.Unexpected_token,
tn.range()
);
2017-09-28 13:08:25 +02:00
return null;
}
// 'this'
2017-09-28 13:08:25 +02:00
} 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
// '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
// StringLiteral
2017-09-28 13:08:25 +02:00
} 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
// Identifier
2017-09-28 13:08:25 +02:00
} 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;
}
// Indicates whether tryParseSignature determined that it is handling a Signature
private tryParseSignatureIsSignature: bool = false;
/** Parses a function signature, as used in type declarations. */
tryParseSignature(
tn: Tokenizer
): SignatureNode | null {
// at '(': ('...'? Identifier '?'? ':' Type (',' '...'? Identifier '?'? ':' Type)* )? ')' '=>' Type
var state = tn.mark();
var startPos = tn.tokenPos;
var parameters: ParameterNode[] | null = null;
var thisType: TypeNode | null = null;
var isSignature: bool = false;
if (tn.skip(Token.CLOSEPAREN)) {
isSignature = true;
tn.discard(state);
parameters = [];
} else {
isSignature = false; // not yet known
do {
var kind = ParameterKind.DEFAULT;
if (tn.skip(Token.DOT_DOT_DOT)) {
isSignature = true;
tn.discard(state);
kind = ParameterKind.REST;
}
if (tn.skip(Token.THIS)) {
if (tn.skip(Token.COLON)) {
isSignature = true;
tn.discard(state);
let t = this.parseType(tn, false);
if (!t) return null;
if (t.kind != NodeKind.TYPE) {
this.error(
DiagnosticCode.Operation_not_supported,
t.range
);
this.tryParseSignatureIsSignature = true;
return null;
}
thisType = <TypeNode>t;
} else {
tn.reset(state);
this.tryParseSignatureIsSignature = false;
return null;
}
} else if (tn.skip(Token.IDENTIFIER)) {
var name = Node.createIdentifierExpression(tn.readIdentifier(), tn.range(tn.tokenPos, tn.pos));
if (tn.skip(Token.QUESTION)) {
isSignature = true;
tn.discard(state);
if (kind == ParameterKind.REST) {
this.error(
DiagnosticCode.A_rest_parameter_cannot_be_optional,
tn.range()
); // recoverable
} else {
kind = ParameterKind.OPTIONAL;
}
}
if (tn.skip(Token.COLON)) {
isSignature = true;
tn.discard(state);
var type = this.parseType(tn); // not suppressing errors because known
if (!type) {
this.tryParseSignatureIsSignature = isSignature;
return null;
}
var param = new ParameterNode();
param.parameterKind = kind;
param.name = name;
param.type = type;
if (!parameters) parameters = [ param ];
else parameters.push(param);
} else {
if (isSignature) {
this.error(
DiagnosticCode.Type_expected,
tn.range()
); // recoverable
}
}
} else {
if (isSignature) {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
);
} else {
tn.reset(state);
}
this.tryParseSignatureIsSignature = isSignature;
return null;
}
} while (tn.skip(Token.COMMA));
if (!tn.skip(Token.CLOSEPAREN)) {
this.error(
DiagnosticCode._0_expected,
tn.range(), ")"
);
this.tryParseSignatureIsSignature = isSignature;
return null;
}
}
var returnType: CommonTypeNode | null;
if (tn.skip(Token.EQUALS_GREATERTHAN)) {
isSignature = true;
tn.discard(state);
returnType = this.parseType(tn);
if (!returnType) {
this.tryParseSignatureIsSignature = isSignature;
return null;
}
} else {
if (isSignature) {
this.error(
DiagnosticCode._0_expected,
tn.range(), "=>"
);
} else {
tn.reset(state);
}
this.tryParseSignatureIsSignature = isSignature;
return null;
}
this.tryParseSignatureIsSignature = true;
return Node.createSignature(
parameters || [],
returnType,
thisType,
false,
tn.range(startPos, tn.pos)
);
}
2017-09-28 13:08:25 +02:00
// statements
2018-02-25 00:13:39 +01:00
parseDecorator(
tn: Tokenizer
): DecoratorNode | null {
2018-02-25 00:13:39 +01:00
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: ModifierNode[] | null,
decorators: DecoratorNode[] | null
2018-02-25 00:13:39 +01:00
): 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: ModifierNode[] | null,
parentDecorators: DecoratorNode[] | null
2018-02-25 00:13:39 +01:00
): 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: CommonTypeNode | 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: ModifierNode[] | null,
decorators: DecoratorNode[] | null
2018-02-25 00:13:39 +01:00
): 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
): TypeParameterNode[] | null {
2018-02-25 00:13:39 +01:00
2017-09-28 13:08:25 +02:00
// at '<': TypeParameter (',' TypeParameter)* '>'
2018-02-25 00:13:39 +01:00
var typeParameters = new Array<TypeParameterNode>();
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;
typeParameters.push(<TypeParameterNode>typeParameter);
2017-09-28 13:08:25 +02:00
} 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
): TypeParameterNode | null {
2018-02-25 00:13:39 +01:00
// 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)) {
let t = this.parseType(tn);
if (!t) return null;
if (t.kind != NodeKind.TYPE) {
this.error(
DiagnosticCode.Operation_not_supported,
t.range
);
return null;
}
extendsType = <TypeNode>t;
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
): ParameterNode[] | null {
2018-02-25 00:13:39 +01:00
2017-09-28 13:08:25 +02:00
// at '(': (Parameter (',' Parameter)*)? ')'
2018-02-25 00:13:39 +01:00
var parameters = new Array<ParameterNode>();
var seenRest: ParameterNode | 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
): ParameterNode | null {
2018-02-25 00:13:39 +01:00
// 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: CommonTypeNode | 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: ModifierNode[] | null,
decorators: DecoratorNode[] | null
2018-02-25 00:13:39 +01:00
): 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
var name = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
var signatureStart: i32 = -1;
2018-02-25 00:13:39 +01:00
var typeParameters: TypeParameterNode[] | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.LESSTHAN)) {
signatureStart = tn.tokenPos;
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
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
if (signatureStart < 0) {
signatureStart = tn.tokenPos;
}
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,
name.range
2018-02-25 00:13:39 +01:00
); // recoverable
}
if (parameters.length && parameters[0].initializer) {
this.error(
DiagnosticCode.A_set_accessor_parameter_cannot_have_an_initializer,
name.range
2018-02-25 00:13:39 +01:00
); // 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,
name.range
2018-02-25 00:13:39 +01:00
); // recoverable
}
var returnType: CommonTypeNode | 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;
}
2018-02-25 00:13:39 +01:00
if (!returnType) {
returnType = Node.createOmittedType(
2018-02-25 00:13:39 +01:00
tn.range(tn.pos)
);
if (!isSetter) {
this.error(
DiagnosticCode.Type_expected,
returnType.range
); // recoverable
}
2017-12-14 11:55:35 +01:00
}
2018-02-25 00:13:39 +01:00
var signature = Node.createSignature(
parameters,
returnType,
null,
false,
tn.range(signatureStart, tn.pos)
);
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(
name,
2018-02-25 00:13:39 +01:00
typeParameters,
signature,
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 name: IdentifierExpression;
2018-02-27 00:30:04 +01:00
var isArrow = false;
// either at 'function':
// Identifier?
// '(' Parameters (':' Type)?
// Statement
if (tn.token == Token.FUNCTION) {
if (tn.skip(Token.IDENTIFIER)) {
name = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
2018-02-27 00:30:04 +01:00
} else { // empty name
name = Node.createEmptyIdentifierExpression(tn.range(tn.pos));
2018-02-27 00:30:04 +01:00
}
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);
name = Node.createEmptyIdentifierExpression(tn.range(tn.tokenPos));
2018-02-27 00:30:04 +01:00
}
// TODO: type parameters? doesn't seem worth it.
var signatureStart = tn.pos;
2018-02-27 00:30:04 +01:00
var parameters = this.parseParameters(tn);
if (!parameters) return null;
return this.parseFunctionExpressionCommon(tn, name, parameters, isArrow, startPos, signatureStart);
2018-02-27 00:30:04 +01:00
}
private parseFunctionExpressionCommon(
tn: Tokenizer,
name: IdentifierExpression,
parameters: ParameterNode[],
2018-02-27 00:30:04 +01:00
isArrow: bool,
startPos: i32 = -1,
signatureStart: i32 = -1
2018-02-27 00:30:04 +01:00
): FunctionExpression | null {
if (startPos < 0) startPos = name.range.start;
if (signatureStart < 0) signatureStart = startPos;
2018-02-27 00:30:04 +01:00
var returnType: CommonTypeNode | null = null;
2018-02-27 00:30:04 +01:00
if (tn.skip(Token.COLON)) {
returnType = this.parseType(tn);
if (!returnType) return null;
} else {
returnType = Node.createOmittedType(tn.range(tn.pos));
this.error(
DiagnosticCode.Type_expected,
returnType.range
); // recoverable
2018-02-27 00:30:04 +01:00
}
if (isArrow) {
if (!tn.skip(Token.EQUALS_GREATERTHAN)) {
this.error(
DiagnosticCode._0_expected,
tn.range(tn.pos), "=>"
);
return null;
}
}
var signature = Node.createSignature(
parameters,
returnType,
null,
false,
tn.range(signatureStart, tn.pos)
);
2018-02-27 00:30:04 +01:00
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(
name,
null,
signature,
2018-02-27 00:30:04 +01:00
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: ModifierNode[] | null,
decorators: DecoratorNode[] | null
2018-02-25 00:13:39 +01:00
): 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: TypeParameterNode[] | 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)) {
let t = this.parseType(tn);
if (!t) return null;
if (t.kind != NodeKind.TYPE) {
this.error(
DiagnosticCode.Operation_not_supported,
t.range
);
return null;
}
extendsType = <TypeNode>t;
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<DecoratorNode>();
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(<DecoratorNode>decorator);
2017-10-02 12:52:15 +02:00
}
var modifiers: ModifierNode[] | 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
var state = 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(state);
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(state);
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 name = 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: TypeParameterNode[] | 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 signatureStart = tn.tokenPos;
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,
name.range
2018-02-25 00:13:39 +01:00
);
}
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,
name.range
2018-02-25 00:13:39 +01:00
);
}
if (parameters.length && parameters[0].initializer) {
this.error(
DiagnosticCode.A_set_accessor_parameter_cannot_have_an_initializer,
name.range
2018-02-25 00:13:39 +01:00
);
}
2017-12-14 11:55:35 +01:00
}
2018-02-25 00:13:39 +01:00
var returnType: CommonTypeNode | null = null;
2017-09-28 13:08:25 +02:00
if (tn.skip(Token.COLON)) {
if (name.kind == NodeKind.CONSTRUCTOR) {
2018-02-25 00:13:39 +01:00
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()
);
}
returnType = this.parseType(tn, name.kind == NodeKind.CONSTRUCTOR || isSetter);
2018-02-25 00:13:39 +01:00
if (!returnType) return null;
} else {
returnType = Node.createOmittedType(tn.range(tn.pos));
if (!isSetter && name.kind != NodeKind.CONSTRUCTOR) {
this.error(
DiagnosticCode.Type_expected,
returnType.range
); // recoverable
}
2018-02-25 00:13:39 +01:00
}
var signature = Node.createSignature(
parameters,
returnType,
null,
false,
tn.range(signatureStart, tn.pos)
);
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(
name,
2018-02-25 00:13:39 +01:00
typeParameters,
signature,
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,
name.range
2018-02-25 00:13:39 +01:00
);
} 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,
name.range
2018-02-25 00:13:39 +01:00
);
2017-09-28 13:08:25 +02:00
// field: (':' Type)? ('=' Expression)? ';'?
} else {
var modifier: ModifierNode | 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: CommonTypeNode | 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(
name,
2018-02-25 00:13:39 +01:00
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: ModifierNode[] | null,
decorators: DecoratorNode[] | null
2018-02-25 00:13:39 +01:00
): 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: ModifierNode[] | null
2018-02-25 00:13:39 +01:00
): 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
var state = tn.mark();
var token = tn.next();
2017-09-28 13:08:25 +02:00
switch (token) {
case Token.BREAK: {
return this.parseBreak(tn);
}
case Token.CONST: {
2018-02-25 00:13:39 +01:00
return this.parseVariable(tn, [
Node.createModifier(ModifierKind.CONST, tn.range())
], null);
}
case Token.CONTINUE: {
return this.parseContinue(tn);
}
case Token.DO: {
2017-09-28 13:08:25 +02:00
return this.parseDoStatement(tn);
}
case Token.FOR: {
2017-09-28 13:08:25 +02:00
return this.parseForStatement(tn);
}
case Token.IF: {
2017-09-28 13:08:25 +02:00
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);
}
case Token.VAR: {
return this.parseVariable(tn, null, null);
}
case Token.OPENBRACE: {
2017-09-28 13:08:25 +02:00
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));
}
case Token.SWITCH: {
2017-09-28 13:08:25 +02:00
return this.parseSwitchStatement(tn);
}
case Token.THROW: {
2017-09-28 13:08:25 +02:00
return this.parseThrowStatement(tn);
}
case Token.TRY: {
2017-09-28 13:08:25 +02:00
return this.parseTryStatement(tn);
}
case Token.TYPE: {
2018-03-12 17:44:09 +01:00
return this.parseTypeDeclaration(tn);
}
case Token.VOID: {
return this.parseVoidStatement(tn);
}
case Token.WHILE: {
2017-09-28 13:08:25 +02:00
return this.parseWhileStatement(tn);
}
default: {
tn.reset(state);
2017-09-28 13:08:25 +02:00
return this.parseExpressionStatement(tn);
}
2017-09-28 13:08:25 +02:00
}
}
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: ModifierNode[] | null = null,
decorators: DecoratorNode[] | null = null
2018-02-25 00:13:39 +01:00
): TypeDeclaration | null {
2018-03-12 17:44:09 +01:00
// at 'type': Identifier ('<' TypeParameters '>')? '=' 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());
2018-03-12 17:44:09 +01:00
var typeParameters: TypeParameterNode[] | null = null;
if (tn.skip(Token.LESSTHAN)) {
typeParameters = this.parseTypeParameters(tn);
if (!typeParameters) return null;
}
if (tn.skip(Token.EQUALS)) {
var type = this.parseType(tn);
2018-02-25 00:13:39 +01:00
if (!type) return null;
2018-03-12 17:44:09 +01:00
var ret = Node.createTypeDeclaration(
name,
typeParameters,
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;
}
parseVoidStatement(
tn: Tokenizer
): VoidStatement | null {
// at 'void': Expression ';'?
var startPos = tn.tokenPos;
var expression = this.parseExpression(tn, Precedence.GROUPING);
if (!expression) return null;
var ret = Node.createVoidStatement(expression, tn.range(startPos, tn.pos));
tn.skip(Token.SEMICOLON);
return ret;
}
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;
var ret = Node.createWhileStatement(expression, 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
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.createEmptyIdentifierExpression(tn.range(startPos)),
2018-02-27 00:30:04 +01:00
[],
true
);
}
var state = tn.mark();
2018-02-27 00:30:04 +01:00
var again = true;
do {
switch (tn.next(true)) {
// function expression
case Token.DOT_DOT_DOT: {
tn.reset(state);
2018-02-27 00:30:04 +01:00
return this.parseFunctionExpression(tn);
}
2018-02-27 00:30:04 +01:00
// can be both
case Token.IDENTIFIER: {
2018-02-27 00:30:04 +01:00
tn.readIdentifier();
switch (tn.next()) {
// if we got here, check for arrow
case Token.CLOSEPAREN: {
2018-02-27 00:30:04 +01:00
if (!tn.skip(Token.EQUALS_GREATERTHAN)) {
again = false;
break;
}
// fall-through
}
2018-02-27 00:30:04 +01:00
// function expression
case Token.QUESTION: // optional parameter
case Token.COLON: { // type annotation
tn.reset(state);
2018-02-27 00:30:04 +01:00
return this.parseFunctionExpression(tn);
}
2018-02-27 00:30:04 +01:00
// can be both
case Token.COMMA: {
2018-02-27 00:30:04 +01:00
break; // continue
}
2018-02-27 00:30:04 +01:00
// parenthesized expression
// case Token.EQUALS: // missing type annotation for simplicity
default: {
2018-02-27 00:30:04 +01:00
again = false;
break;
}
2018-02-27 00:30:04 +01:00
}
break;
}
2018-02-27 00:30:04 +01:00
// parenthesized expression
default: {
2018-02-27 00:30:04 +01:00
again = false;
break;
}
2018-02-27 00:30:04 +01:00
}
} while (again);
tn.reset(state);
2018-02-27 00:30:04 +01:00
// 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
case Token.OPENBRACKET: {
var elementExpressions = new Array<Expression | null>();
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)
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)
);
}
case Token.IDENTIFIER: {
2018-01-05 01:55:59 +01:00
return Node.createIdentifierExpression(tn.readIdentifier(), tn.range(startPos, tn.pos));
}
case Token.THIS: {
2018-01-05 01:55:59 +01:00
return Node.createThisExpression(tn.range(startPos, tn.pos));
}
case Token.CONSTRUCTOR: {
2018-01-05 01:55:59 +01:00
return Node.createConstructorExpression(tn.range(startPos, tn.pos));
}
case Token.SUPER: {
2018-01-05 01:55:59 +01:00
return Node.createSuperExpression(tn.range(startPos, tn.pos));
}
case Token.STRINGLITERAL: {
2018-01-05 01:55:59 +01:00
return Node.createStringLiteralExpression(tn.readString(), tn.range(startPos, tn.pos));
}
case Token.INTEGERLITERAL: {
2018-01-05 01:55:59 +01:00
return Node.createIntegerLiteralExpression(tn.readInteger(), tn.range(startPos, tn.pos));
}
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
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)
);
}
case Token.FUNCTION: {
2018-02-27 00:30:04 +01:00
return this.parseFunctionExpression(tn);
}
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;
}
2017-09-28 13:08:25 +02:00
}
}
2018-02-25 00:13:39 +01:00
tryParseTypeArgumentsBeforeArguments(
tn: Tokenizer
): CommonTypeNode[] | null {
2018-02-25 00:13:39 +01:00
// at '<': Type (',' Type)* '>' '('
2017-09-28 13:08:25 +02:00
var state = tn.mark();
2018-02-25 00:13:39 +01:00
if (!tn.skip(Token.LESSTHAN)) return null;
var typeArguments = new Array<CommonTypeNode>();
2017-09-28 13:08:25 +02:00
do {
var type = this.parseType(tn, true, true);
if (!type) {
tn.reset(state);
2017-09-28 13:08:25 +02:00
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(state);
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 with type arguments
var typeArguments: CommonTypeNode[] | null;
while (
// there might be better ways to distinguish a LESSTHAN from a CALL with type arguments
(typeArguments = this.tryParseTypeArgumentsBeforeArguments(tn)) ||
tn.skip(Token.OPENPAREN)
) {
let 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: {
2018-01-29 22:36:07 +01:00
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;
}
2018-01-29 22:36:07 +01:00
// ElementAccessExpression
case Token.OPENBRACKET: {
2018-01-29 22:36:07 +01:00
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-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;
}
2018-01-29 22:36:07 +01:00
// TernaryExpression
case Token.QUESTION: {
2018-01-29 22:36:07 +01:00
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;
}
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;
2017-09-28 13:08:25 +02:00
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-09-28 13:08:25 +02:00
}
}
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;
2017-09-28 13:08:25 +02:00
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;
2017-09-28 13:08:25 +02:00
case Token.EQUALS_EQUALS:
case Token.EXCLAMATION_EQUALS:
case Token.EQUALS_EQUALS_EQUALS:
case Token.EXCLAMATION_EQUALS_EQUALS: return Precedence.EQUALITY;
2017-09-28 13:08:25 +02:00
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;
2017-09-28 13:08:25 +02:00
case Token.LESSTHAN_LESSTHAN:
case Token.GREATERTHAN_GREATERTHAN:
case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN: return Precedence.SHIFT;
2017-09-28 13:08:25 +02:00
case Token.PLUS:
case Token.MINUS: return Precedence.ADDITIVE;
2017-09-28 13:08:25 +02:00
case Token.ASTERISK:
case Token.SLASH:
case Token.PERCENT: return Precedence.MULTIPLICATIVE;
case Token.ASTERISK_ASTERISK: return Precedence.EXPONENTIATED;
2017-09-28 13:08:25 +02:00
case Token.PLUS_PLUS:
case Token.MINUS_MINUS: return Precedence.UNARY_POSTFIX;
2017-09-28 13:08:25 +02:00
case Token.DOT:
case Token.NEW:
case Token.OPENBRACKET: return Precedence.MEMBERACCESS;
default: return Precedence.INVALID;
2017-09-28 13:08:25 +02:00
}
}
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;
2017-09-28 13:08:25 +02:00
}
}