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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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,

View File

@ -0,0 +1,16 @@
var a = function(): void {
;
};
var b = function someName(): void {
;
};
var c = function(a: i32, b: i32): i32 {
;
};
var d = (): void => {
;
};
var e = (a: i32, b: i32): i32 => {
;
};
var f = (a: i32): i32 => a;

View File

@ -0,0 +1,16 @@
var a = function(): void {
;
};
var b = function someName(): void {
;
};
var c = function(a: i32, b: i32): i32 {
;
};
var d = (): void => {
;
};
var e = (a: i32, b: i32): i32 => {
;
};
var f = (a: i32): i32 => a;