Initial function expression parsing

This commit is contained in:
dcodeIO
2018-02-27 00:30:04 +01:00
parent 9ef8b162a9
commit bda6cb9792
8 changed files with 306 additions and 51 deletions

View File

@ -34,6 +34,8 @@ export enum NodeKind {
COMMA,
ELEMENTACCESS,
FALSE,
FUNCTION,
FUNCTIONARROW,
LITERAL,
NEW,
NULL,
@ -225,6 +227,18 @@ export abstract class Node {
return expr;
}
static createFunctionExpression(
declaration: FunctionDeclaration,
isArrow: bool = false
): FunctionExpression {
var expr = isArrow
? new FunctionArrowExpression()
: new FunctionExpression();
expr.range = declaration.range;
expr.declaration = declaration;
return expr;
}
static createIntegerLiteralExpression(
value: I64,
range: Range
@ -710,7 +724,7 @@ export abstract class Node {
typeParameters: TypeParameter[],
parameters: Parameter[],
returnType: TypeNode | null,
statements: Statement[] | null,
body: Statement | null,
modifiers: Modifier[] | null,
decorators: Decorator[] | null,
range: Range
@ -721,7 +735,7 @@ export abstract class Node {
stmt.typeParameters = typeParameters; setParent(typeParameters, stmt);
stmt.parameters = parameters; setParent(parameters, stmt);
stmt.returnType = returnType; if (returnType) returnType.parent = stmt;
stmt.statements = statements; if (statements) setParent(statements, stmt);
stmt.body = body; if (body) body.parent = stmt;
stmt.modifiers = modifiers; if (modifiers) setParent(modifiers, stmt);
stmt.decorators = decorators; if (decorators) setParent(decorators, stmt);
return stmt;
@ -732,7 +746,7 @@ export abstract class Node {
typeParameters: TypeParameter[],
parameters: Parameter[],
returnType: TypeNode | null,
statements: Statement[] | null,
body: Statement | null,
modifiers: Modifier[] | null,
decorators: Decorator[] | null,
range: Range
@ -743,7 +757,7 @@ export abstract class Node {
stmt.typeParameters = typeParameters; setParent(typeParameters, stmt);
stmt.parameters = parameters; setParent(parameters, stmt);
stmt.returnType = returnType; if (returnType) returnType.parent = stmt;
stmt.statements = statements; if (statements) setParent(statements, stmt);
stmt.body = body; if (body) body.parent = stmt;
stmt.modifiers = modifiers; if (modifiers) setParent(modifiers, stmt);
stmt.decorators = decorators; if (decorators) setParent(decorators, stmt);
return stmt;
@ -1033,6 +1047,19 @@ export class FloatLiteralExpression extends LiteralExpression {
value: f64;
}
/** Represents a function expression using the 'function' keyword. */
export class FunctionExpression extends Expression {
kind = NodeKind.FUNCTION;
/** Inline function declaration. */
declaration: FunctionDeclaration;
}
/** Represents an arrow function expression. */
export class FunctionArrowExpression extends FunctionExpression {
kind = NodeKind.FUNCTIONARROW;
}
/** Represents an integer literal expression. */
export class IntegerLiteralExpression extends LiteralExpression {
literalKind = LiteralKind.INTEGER;
@ -1468,8 +1495,8 @@ export class FunctionDeclaration extends DeclarationStatement {
parameters: Parameter[];
/** Return type. */
returnType: TypeNode | null;
/** Contained statements. */
statements: Statement[] | null;
/** Body statement. Usually a block. */
body: Statement | null;
}
/** Represents an `if` statement. */

View File

@ -729,14 +729,14 @@ export class Compiler extends DiagnosticEmitter {
var declaration = instance.prototype.declaration;
if (instance.is(ElementFlags.DECLARED)) {
if (declaration.statements) {
if (declaration.body) {
this.error(
DiagnosticCode.An_implementation_cannot_be_declared_in_ambient_contexts,
declaration.name.range
);
return false;
}
} else if (!declaration.statements) {
} else if (!declaration.body) {
this.error(
DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration,
declaration.name.range
@ -748,12 +748,12 @@ export class Compiler extends DiagnosticEmitter {
instance.set(ElementFlags.COMPILED);
// compile statements
var stmts: ExpressionRef[] | null = null;
var stmt: ExpressionRef = 0;
if (!instance.is(ElementFlags.DECLARED)) {
var previousFunction = this.currentFunction;
this.currentFunction = instance;
var statements = assert(declaration.statements, "implementation expected");
stmts = this.compileStatements(statements);
var body = assert(declaration.body, "implementation expected");
stmt = this.compileStatement(body);
// make sure the top-level branch or all child branches return
var allBranchesReturn = this.currentFunction.flow.finalize();
if (instance.returnType != Type.void && !allBranchesReturn) {
@ -810,7 +810,7 @@ export class Compiler extends DiagnosticEmitter {
instance.internalName,
typeRef,
typesToNativeTypes(instance.additionalLocals),
this.module.createBlock(null, <ExpressionRef[]>stmts, NativeType.None)
assert(stmt)
);
}

View File

@ -25,6 +25,7 @@ import {
CallExpression,
CommaExpression,
ElementAccessExpression,
FunctionExpression,
NewExpression,
ParenthesizedExpression,
PropertyAccessExpression,
@ -124,6 +125,11 @@ export function serializeNode(node: Node, sb: string[]): void {
serializeElementAccessExpression(<ElementAccessExpression>node, sb);
break;
case NodeKind.FUNCTION:
case NodeKind.FUNCTIONARROW:
serializeFunctionExpression(<FunctionExpression>node, sb);
break;
case NodeKind.LITERAL:
serializeLiteralExpression(<LiteralExpression>node, sb);
break;
@ -412,6 +418,21 @@ export function serializeElementAccessExpression(node: ElementAccessExpression,
sb.push("]");
}
export function serializeFunctionExpression(node: FunctionExpression, sb: string[]): void {
var declaration = node.declaration;
var isArrow = node.kind == NodeKind.FUNCTIONARROW;
if (!isArrow) {
if (declaration.name.text.length) {
sb.push("function ");
} else {
sb.push("function");
}
} else {
assert(declaration.name.text.length == 0);
}
serializeFunctionCommon(declaration, sb, isArrow);
}
export function serializeLiteralExpression(node: LiteralExpression, sb: string[]): void {
switch (node.literalKind) {
@ -608,15 +629,19 @@ export function serializeStatement(node: Statement, sb: string[]): void {
function serializeTerminatedStatement(statement: Statement, sb: string[]): void {
serializeStatement(statement, sb);
if (sb.length) { // i.e. leading EmptyStatement
if (
!sb.length || // leading EmptyStatement
statement.kind == NodeKind.VARIABLE || // potentially assigns a FunctionExpression
statement.kind == NodeKind.EXPRESSION // potentially assigns a FunctionExpression
) {
sb.push(";\n");
} else {
var last = sb[sb.length - 1];
if (last.length && last.charCodeAt(last.length - 1) == CharCode.CLOSEBRACE) {
sb.push("\n");
} else {
sb.push(";\n");
}
} else {
sb.push(";\n");
}
}
@ -840,11 +865,15 @@ export function serializeFunctionDeclaration(node: FunctionDeclaration, sb: stri
sb.push(" ");
}
}
sb.push("function ");
if (node.name.text.length) {
sb.push("function ");
} else {
sb.push("function");
}
serializeFunctionCommon(node, sb);
}
function serializeFunctionCommon(node: FunctionDeclaration, sb: string[]): void {
function serializeFunctionCommon(node: FunctionDeclaration, sb: string[], isArrow: bool = false): void {
var i: i32, k: i32;
serializeIdentifierExpression(node.name, sb);
if (k = node.typeParameters.length) {
@ -864,18 +893,33 @@ function serializeFunctionCommon(node: FunctionDeclaration, sb: string[]): void
serializeParameter(node.parameters[i], sb);
}
}
if (node.returnType && !hasModifier(ModifierKind.SET, node.modifiers)) {
sb.push("): ");
serializeTypeNode(node.returnType, sb);
} else {
sb.push(")");
}
if (node.statements) {
sb.push(" {\n");
for (i = 0, k = node.statements.length; i < k; ++i) {
serializeTerminatedStatement(node.statements[i], sb);
if (isArrow) {
if (node.body) {
if (node.returnType) {
sb.push("): ");
serializeTypeNode(node.returnType, sb);
}
sb.push(" => ");
serializeStatement(<Statement>node.body, sb);
} else {
if (node.returnType) {
sb.push(" => ");
serializeTypeNode(node.returnType, sb);
} else {
sb.push(" => void");
}
}
} else {
if (node.returnType && !hasModifier(ModifierKind.SET, node.modifiers)) {
sb.push("): ");
serializeTypeNode(node.returnType, sb);
} else {
sb.push(")");
}
if (node.body) {
sb.push(" ");
serializeStatement(node.body, sb);
}
sb.push("}");
}
}

View File

@ -79,7 +79,8 @@ import {
addModifier,
getModifier,
hasModifier,
setReusableModifiers
setReusableModifiers,
FunctionExpression
} from "./ast";
@ -776,7 +777,8 @@ export class Parser extends DiagnosticEmitter {
}
parseParameter(
tn: Tokenizer
tn: Tokenizer,
suppressErrors: bool = false
): Parameter | null {
// before: '...'? Identifier '?'? (':' Type)? ('=' Expression)?
@ -927,10 +929,8 @@ export class Parser extends DiagnosticEmitter {
var isDeclare = hasModifier(ModifierKind.DECLARE, modifiers);
var statements: Statement[] | null = null;
var body: Statement | null = null;
if (tn.skip(Token.OPENBRACE)) {
statements = new Array();
if (isDeclare) {
this.error(
DiagnosticCode.An_implementation_cannot_be_declared_in_ambient_contexts,
@ -938,12 +938,8 @@ export class Parser extends DiagnosticEmitter {
); // recoverable
}
while (!tn.skip(Token.CLOSEBRACE)) {
var statement = this.parseStatement(tn);
if (!statement) return null;
statements.push(<Statement>statement);
}
body = this.parseBlockStatement(tn, false);
if (!body) return null;
} else if (!isDeclare) {
this.error(
DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration,
@ -956,7 +952,7 @@ export class Parser extends DiagnosticEmitter {
typeParameters,
<Parameter[]>parameters,
returnType,
statements,
body,
modifiers,
decorators,
tn.range(startPos, tn.pos)
@ -965,6 +961,102 @@ export class Parser extends DiagnosticEmitter {
return ret;
}
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,
[],
parameters,
returnType,
body,
null,
null,
tn.range(startPos, tn.pos)
);
return Node.createFunctionExpression(declaration, isArrow);
}
parseClass(
tn: Tokenizer,
modifiers: Modifier[] | null,
@ -1184,7 +1276,7 @@ export class Parser extends DiagnosticEmitter {
); // recoverable
}
var statements: Statement[] | null = null;
var body: Statement | null = null;
if (tn.skip(Token.OPENBRACE)) {
if (parentIsDeclare) {
this.error(
@ -1192,12 +1284,8 @@ export class Parser extends DiagnosticEmitter {
tn.range()
); // recoverable
}
statements = new Array();
while (!tn.skip(Token.CLOSEBRACE)) {
var statement = this.parseStatement(tn);
if (!statement) return null;
statements.push(<Statement>statement);
}
body = this.parseBlockStatement(tn, false);
if (!body) return null;
} else if (!parentIsDeclare) {
this.error(
DiagnosticCode.Function_implementation_is_missing_or_not_immediately_following_the_declaration,
@ -1210,7 +1298,7 @@ export class Parser extends DiagnosticEmitter {
typeParameters,
parameters,
returnType,
statements,
body,
modifiers,
decorators,
tn.range(startPos, tn.pos)
@ -2212,7 +2300,68 @@ export class Parser extends DiagnosticEmitter {
switch (token) {
// ParenthesizedExpression
// FunctionExpression
case Token.OPENPAREN:
// 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
expr = this.parseExpression(tn);
if (!expr) return null;
if (!tn.skip(Token.CLOSEPAREN)) {
@ -2287,7 +2436,7 @@ export class Parser extends DiagnosticEmitter {
case Token.INTEGERLITERAL:
return Node.createIntegerLiteralExpression(tn.readInteger(), tn.range(startPos, tn.pos));
case Token.FLOATLITERAL:
case Token.FLOATLITERAL:
return Node.createFloatLiteralExpression(tn.readFloat(), tn.range(startPos, tn.pos));
// RegexpLiteralExpression
@ -2307,6 +2456,9 @@ export class Parser extends DiagnosticEmitter {
tn.range(startPos, tn.pos)
);
case Token.FUNCTION:
return this.parseFunctionExpression(tn);
default:
this.error(
DiagnosticCode.Expression_expected,