mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 15:12:12 +00:00
Initial function expression parsing
This commit is contained in:
parent
9ef8b162a9
commit
bda6cb9792
2
dist/assemblyscript.js
vendored
2
dist/assemblyscript.js
vendored
File diff suppressed because one or more lines are too long
2
dist/assemblyscript.js.map
vendored
2
dist/assemblyscript.js.map
vendored
File diff suppressed because one or more lines are too long
39
src/ast.ts
39
src/ast.ts
@ -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. */
|
||||
|
@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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("}");
|
||||
}
|
||||
}
|
||||
|
||||
|
194
src/parser.ts
194
src/parser.ts
@ -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,
|
||||
|
16
tests/parser/function-expression.ts
Normal file
16
tests/parser/function-expression.ts
Normal 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;
|
16
tests/parser/function-expression.ts.fixture.ts
Normal file
16
tests/parser/function-expression.ts.fixture.ts
Normal 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;
|
Loading…
x
Reference in New Issue
Block a user