From ccb447875f616f74cfd78aaa6efa7da69a2c5514 Mon Sep 17 00:00:00 2001 From: dcode Date: Tue, 19 Nov 2019 07:44:33 +0100 Subject: [PATCH 1/4] Fix 'new' parsing precedence & allow omitting parenthesis --- src/parser.ts | 7 +- tests/compiler/new.json | 5 + tests/compiler/new.optimized.wat | 108 ++++++++++++++ tests/compiler/new.ts | 16 ++ tests/compiler/new.untouched.wat | 249 +++++++++++++++++++++++++++++++ 5 files changed, 384 insertions(+), 1 deletion(-) create mode 100644 tests/compiler/new.json create mode 100644 tests/compiler/new.optimized.wat create mode 100644 tests/compiler/new.ts create mode 100644 tests/compiler/new.untouched.wat diff --git a/src/parser.ts b/src/parser.ts index 55003b2aae..d98a317ddd 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -3266,7 +3266,7 @@ export class Parser extends DiagnosticEmitter { // NewExpression if (token == Token.NEW) { - operand = this.parseExpression(tn, Precedence.CALL); + operand = this.parseExpression(tn, Precedence.GROUPING); if (!operand) return null; if (operand.kind == NodeKind.CALL) { return Node.createNewExpression( @@ -3275,6 +3275,11 @@ export class Parser extends DiagnosticEmitter { (operand).arguments, tn.range(startPos, tn.pos) ); + } else if (operand.kind == NodeKind.IDENTIFIER) { // new Class; + return Node.createNewExpression( + operand, null, [], + tn.range(startPos, tn.pos) + ); } else { this.error( DiagnosticCode.This_expression_is_not_constructable, diff --git a/tests/compiler/new.json b/tests/compiler/new.json new file mode 100644 index 0000000000..b1da366ff4 --- /dev/null +++ b/tests/compiler/new.json @@ -0,0 +1,5 @@ +{ + "asc_flags": [ + "--runtime none" + ] +} \ No newline at end of file diff --git a/tests/compiler/new.optimized.wat b/tests/compiler/new.optimized.wat new file mode 100644 index 0000000000..05dc3b8df7 --- /dev/null +++ b/tests/compiler/new.optimized.wat @@ -0,0 +1,108 @@ +(module + (type $FUNCSIG$vi (func (param i32))) + (type $FUNCSIG$v (func)) + (type $FUNCSIG$ii (func (param i32) (result i32))) + (memory $0 0) + (global $new/ref (mut i32) (i32.const 0)) + (global $~lib/rt/stub/startOffset (mut i32) (i32.const 0)) + (global $~lib/rt/stub/offset (mut i32) (i32.const 0)) + (global $new/gen (mut i32) (i32.const 0)) + (export "memory" (memory $0)) + (start $start) + (func $~lib/rt/stub/maybeGrowMemory (; 0 ;) (type $FUNCSIG$vi) (param $0 i32) + (local $1 i32) + (local $2 i32) + local.get $0 + memory.size + local.tee $2 + i32.const 16 + i32.shl + local.tee $1 + i32.gt_u + if + local.get $2 + local.get $0 + local.get $1 + i32.sub + i32.const 65535 + i32.add + i32.const -65536 + i32.and + i32.const 16 + i32.shr_u + local.tee $1 + local.get $2 + local.get $1 + i32.gt_s + select + memory.grow + i32.const 0 + i32.lt_s + if + local.get $1 + memory.grow + i32.const 0 + i32.lt_s + if + unreachable + end + end + end + local.get $0 + global.set $~lib/rt/stub/offset + ) + (func $~lib/rt/stub/__alloc (; 1 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + (local $1 i32) + (local $2 i32) + global.get $~lib/rt/stub/offset + i32.const 16 + i32.add + local.tee $2 + i32.const 16 + i32.add + call $~lib/rt/stub/maybeGrowMemory + local.get $2 + i32.const 16 + i32.sub + local.tee $1 + i32.const 16 + i32.store + local.get $1 + i32.const -1 + i32.store offset=4 + local.get $1 + local.get $0 + i32.store offset=8 + local.get $1 + i32.const 0 + i32.store offset=12 + local.get $2 + ) + (func $start:new (; 2 ;) (type $FUNCSIG$v) + i32.const 16 + global.set $~lib/rt/stub/startOffset + i32.const 16 + global.set $~lib/rt/stub/offset + i32.const 3 + call $~lib/rt/stub/__alloc + global.set $new/ref + i32.const 3 + call $~lib/rt/stub/__alloc + global.set $new/ref + i32.const 3 + call $~lib/rt/stub/__alloc + global.set $new/ref + i32.const 4 + call $~lib/rt/stub/__alloc + global.set $new/gen + i32.const 4 + call $~lib/rt/stub/__alloc + global.set $new/gen + ) + (func $start (; 3 ;) (type $FUNCSIG$v) + call $start:new + ) + (func $null (; 4 ;) (type $FUNCSIG$v) + nop + ) +) diff --git a/tests/compiler/new.ts b/tests/compiler/new.ts new file mode 100644 index 0000000000..7ebad6011c --- /dev/null +++ b/tests/compiler/new.ts @@ -0,0 +1,16 @@ +class Ref { + get ref(): Ref { return this; } +} + +var ref: Ref; +ref = new Ref(); +ref = new Ref; +ref = new Ref().ref; + +class Gen { + get gen(): Gen { return this; } +} + +var gen: Gen; +gen = new Gen(); +gen = new Gen().gen; diff --git a/tests/compiler/new.untouched.wat b/tests/compiler/new.untouched.wat new file mode 100644 index 0000000000..9093cb57c8 --- /dev/null +++ b/tests/compiler/new.untouched.wat @@ -0,0 +1,249 @@ +(module + (type $FUNCSIG$iii (func (param i32 i32) (result i32))) + (type $FUNCSIG$vi (func (param i32))) + (type $FUNCSIG$ii (func (param i32) (result i32))) + (type $FUNCSIG$v (func)) + (memory $0 0) + (table $0 1 funcref) + (elem (i32.const 0) $null) + (global $new/ref (mut i32) (i32.const 0)) + (global $~lib/rt/stub/startOffset (mut i32) (i32.const 0)) + (global $~lib/rt/stub/offset (mut i32) (i32.const 0)) + (global $new/gen (mut i32) (i32.const 0)) + (global $~lib/heap/__heap_base i32 (i32.const 8)) + (export "memory" (memory $0)) + (start $start) + (func $~lib/rt/stub/maybeGrowMemory (; 0 ;) (type $FUNCSIG$vi) (param $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + memory.size + local.set $1 + local.get $1 + i32.const 16 + i32.shl + local.set $2 + local.get $0 + local.get $2 + i32.gt_u + if + local.get $0 + local.get $2 + i32.sub + i32.const 65535 + i32.add + i32.const 65535 + i32.const -1 + i32.xor + i32.and + i32.const 16 + i32.shr_u + local.set $3 + local.get $1 + local.tee $4 + local.get $3 + local.tee $5 + local.get $4 + local.get $5 + i32.gt_s + select + local.set $4 + local.get $4 + memory.grow + i32.const 0 + i32.lt_s + if + local.get $3 + memory.grow + i32.const 0 + i32.lt_s + if + unreachable + end + end + end + local.get $0 + global.set $~lib/rt/stub/offset + ) + (func $~lib/rt/stub/__alloc (; 1 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local $6 i32) + local.get $0 + i32.const 1073741808 + i32.gt_u + if + unreachable + end + global.get $~lib/rt/stub/offset + i32.const 16 + i32.add + local.set $2 + local.get $0 + i32.const 15 + i32.add + i32.const 15 + i32.const -1 + i32.xor + i32.and + local.tee $3 + i32.const 16 + local.tee $4 + local.get $3 + local.get $4 + i32.gt_u + select + local.set $5 + local.get $2 + local.get $5 + i32.add + call $~lib/rt/stub/maybeGrowMemory + local.get $2 + i32.const 16 + i32.sub + local.set $6 + local.get $6 + local.get $5 + i32.store + local.get $6 + i32.const -1 + i32.store offset=4 + local.get $6 + local.get $1 + i32.store offset=8 + local.get $6 + local.get $0 + i32.store offset=12 + local.get $2 + ) + (func $~lib/rt/stub/__retain (; 2 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + ) + (func $new/Ref#constructor (; 3 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 0 + i32.const 3 + call $~lib/rt/stub/__alloc + call $~lib/rt/stub/__retain + local.set $0 + end + local.get $0 + ) + (func $~lib/rt/stub/__release (; 4 ;) (type $FUNCSIG$vi) (param $0 i32) + nop + ) + (func $new/Ref#get:ref (; 5 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + call $~lib/rt/stub/__retain + ) + (func $new/Gen#constructor (; 6 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 0 + i32.const 4 + call $~lib/rt/stub/__alloc + call $~lib/rt/stub/__retain + local.set $0 + end + local.get $0 + ) + (func $new/Gen#get:gen (; 7 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + call $~lib/rt/stub/__retain + ) + (func $start:new (; 8 ;) (type $FUNCSIG$v) + (local $0 i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + global.get $~lib/heap/__heap_base + i32.const 15 + i32.add + i32.const 15 + i32.const -1 + i32.xor + i32.and + global.set $~lib/rt/stub/startOffset + global.get $~lib/rt/stub/startOffset + global.set $~lib/rt/stub/offset + i32.const 0 + call $new/Ref#constructor + local.set $0 + global.get $new/ref + call $~lib/rt/stub/__release + local.get $0 + global.set $new/ref + i32.const 0 + call $new/Ref#constructor + local.set $0 + global.get $new/ref + call $~lib/rt/stub/__release + local.get $0 + global.set $new/ref + i32.const 0 + call $new/Ref#constructor + local.tee $0 + call $new/Ref#get:ref + local.tee $1 + local.tee $2 + global.get $new/ref + local.tee $3 + i32.ne + if + local.get $2 + call $~lib/rt/stub/__retain + drop + local.get $3 + call $~lib/rt/stub/__release + end + local.get $2 + global.set $new/ref + i32.const 0 + call $new/Gen#constructor + local.set $3 + global.get $new/gen + call $~lib/rt/stub/__release + local.get $3 + global.set $new/gen + i32.const 0 + call $new/Gen#constructor + local.tee $3 + call $new/Gen#get:gen + local.tee $2 + local.tee $4 + global.get $new/gen + local.tee $5 + i32.ne + if + local.get $4 + call $~lib/rt/stub/__retain + drop + local.get $5 + call $~lib/rt/stub/__release + end + local.get $4 + global.set $new/gen + local.get $0 + call $~lib/rt/stub/__release + local.get $1 + call $~lib/rt/stub/__release + local.get $2 + call $~lib/rt/stub/__release + local.get $3 + call $~lib/rt/stub/__release + ) + (func $start (; 9 ;) (type $FUNCSIG$v) + call $start:new + ) + (func $null (; 10 ;) (type $FUNCSIG$v) + ) +) From b445992f6387be7002c7433eb0d9d1a611f12a90 Mon Sep 17 00:00:00 2001 From: dcode Date: Wed, 20 Nov 2019 11:01:59 +0100 Subject: [PATCH 2/4] more + cleanup --- src/ast.ts | 56 +++--- src/compiler.ts | 7 +- src/extra/ast.ts | 11 +- src/parser.ts | 310 +++++++++++++++---------------- src/resolver.ts | 2 +- tests/compiler/new.optimized.wat | 10 + tests/compiler/new.ts | 11 ++ tests/compiler/new.untouched.wat | 61 +++++- 8 files changed, 272 insertions(+), 196 deletions(-) diff --git a/src/ast.ts b/src/ast.ts index ee33029cee..fc2f137cbc 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -114,29 +114,6 @@ export function nodeIsConstantValue(kind: NodeKind): bool { return false; } -/** Checks if a node might be callable. */ -export function nodeIsCallable(kind: NodeKind): bool { - switch (kind) { - case NodeKind.IDENTIFIER: - case NodeKind.ASSERTION: // if kind=NONNULL - case NodeKind.CALL: - case NodeKind.ELEMENTACCESS: - case NodeKind.PARENTHESIZED: - case NodeKind.PROPERTYACCESS: - case NodeKind.SUPER: return true; - } - return false; -} - -/** Checks if a node might be callable with generic arguments. */ -export function nodeIsGenericCallable(kind: NodeKind): bool { - switch (kind) { - case NodeKind.IDENTIFIER: - case NodeKind.PROPERTYACCESS: return true; - } - return false; -} - /** Base class of all nodes. */ export abstract class Node { @@ -428,14 +405,14 @@ export abstract class Node { } static createNewExpression( - expression: Expression, + typeName: TypeName, typeArgs: TypeNode[] | null, args: Expression[], range: Range ): NewExpression { var expr = new NewExpression(); expr.range = range; - expr.expression = expression; + expr.typeName = typeName; expr.typeArguments = typeArgs; expr.arguments = args; return expr; @@ -1489,8 +1466,35 @@ export class IntegerLiteralExpression extends LiteralExpression { } /** Represents a `new` expression. Like a call but with its own kind. */ -export class NewExpression extends CallExpression { +export class NewExpression extends Expression { kind = NodeKind.NEW; + + /** Type being constructed. */ + typeName: TypeName; + /** Provided type arguments. */ + typeArguments: TypeNode[] | null; + /** Provided arguments. */ + arguments: Expression[]; + + /** Gets the type arguments range for reporting. */ + get typeArgumentsRange(): Range { + var typeArguments = this.typeArguments; + var numTypeArguments: i32; + if (typeArguments && (numTypeArguments = typeArguments.length)) { + return Range.join(typeArguments[0].range, typeArguments[numTypeArguments - 1].range); + } + return this.typeName.range; + } + + /** Gets the arguments range for reporting. */ + get argumentsRange(): Range { + var args = this.arguments; + var numArguments = args.length; + if (numArguments) { + return Range.join(args[0].range, args[numArguments - 1].range); + } + return this.typeName.range; + } } /** Represents a `null` expression. */ diff --git a/src/compiler.ts b/src/compiler.ts index 863c5ca940..4b06119560 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -7807,15 +7807,12 @@ export class Compiler extends DiagnosticEmitter { var flow = this.currentFlow; // obtain the class being instantiated - var target = this.resolver.lookupExpression( // reports - expression.expression, - flow - ); + var target = this.resolver.resolveTypeName(expression.typeName, flow.actualFunction); if (!target) return module.unreachable(); if (target.kind != ElementKind.CLASS_PROTOTYPE) { this.error( DiagnosticCode.This_expression_is_not_constructable, - expression.expression.range + expression.typeName.range ); return this.module.unreachable(); } diff --git a/src/extra/ast.ts b/src/extra/ast.ts index 384473196f..da80807295 100644 --- a/src/extra/ast.ts +++ b/src/extra/ast.ts @@ -543,9 +543,12 @@ export class ASTBuilder { } visitCallExpression(node: CallExpression): void { - var sb = this.sb; this.visitNode(node.expression); - var typeArguments = node.typeArguments; + this.visitArguments(node.typeArguments, node.arguments); + } + + private visitArguments(typeArguments: TypeNode[] | null, args: Expression[]): void { + var sb = this.sb; if (typeArguments) { let numTypeArguments = typeArguments.length; if (numTypeArguments) { @@ -560,7 +563,6 @@ export class ASTBuilder { } else { sb.push("("); } - var args = node.arguments; var numArgs = args.length; if (numArgs) { this.visitNode(args[0]); @@ -757,7 +759,8 @@ export class ASTBuilder { visitNewExpression(node: NewExpression): void { this.sb.push("new "); - this.visitCallExpression(node); + this.visitTypeName(node.typeName); + this.visitArguments(node.typeArguments, node.arguments); } visitParenthesizedExpression(node: ParenthesizedExpression): void { diff --git a/src/parser.ts b/src/parser.ts index d98a317ddd..f2913fc6eb 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -37,6 +37,7 @@ import { Source, SourceKind, TypeNode, + TypeName, NamedTypeNode, FunctionTypeNode, ArrowKind, @@ -84,9 +85,7 @@ import { VoidStatement, WhileStatement, - mangleInternalPath, - nodeIsCallable, - nodeIsGenericCallable + mangleInternalPath } from "./ast"; /** Parser interface. */ @@ -400,6 +399,30 @@ export class Parser extends DiagnosticEmitter { return this.program; } + /** Parses a type name. */ + parseTypeName( + tn: Tokenizer + ): TypeName | null { + // at: Identifier ('.' Identifier)* + + var first = Node.createSimpleTypeName(tn.readIdentifier(), tn.range()); + var current = first; + while (tn.skip(Token.DOT)) { + if (tn.skip(Token.IDENTIFIER)) { + let next = Node.createSimpleTypeName(tn.readIdentifier(), tn.range()); + current.next = next; + current = next; + } else { + this.error( + DiagnosticCode.Identifier_expected, + tn.range(tn.pos) + ); + return null; + } + } + return first; + } + /** Parses a type. */ parseType( tn: Tokenizer, @@ -504,26 +527,11 @@ export class Parser extends DiagnosticEmitter { // Identifier } else if (token == Token.IDENTIFIER) { - let first = Node.createSimpleTypeName(tn.readIdentifier(), tn.range()); - let current = first; + let name = this.parseTypeName(tn); + if (!name) return null; let parameters: NamedTypeNode[] | null = null; let nullable = false; - // Identifier ('.' Identifier)+ - while (tn.skip(Token.DOT)) { - if (tn.skip(Token.IDENTIFIER)) { - let next = Node.createSimpleTypeName(tn.readIdentifier(), tn.range()); - current.next = next; - current = next; - } else { - this.error( - DiagnosticCode.Identifier_expected, - tn.range(tn.pos) - ); - return null; - } - } - // Name if (tn.skip(Token.LESSTHAN)) { do { @@ -556,7 +564,7 @@ export class Parser extends DiagnosticEmitter { return null; } } - type = Node.createNamedType(first, parameters || [], nullable, tn.range(startPos, tn.pos)); + type = Node.createNamedType(name, parameters || [], nullable, tn.range(startPos, tn.pos)); } else { if (!suppressErrors) { this.error( @@ -3250,73 +3258,87 @@ export class Parser extends DiagnosticEmitter { } // expressions - // see: http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm#climbing parseExpressionStart( tn: Tokenizer ): Expression | null { - var token = tn.next(IdentifierHandling.PREFER); var startPos = tn.tokenPos; - var precedence = determinePrecedenceStart(token); - if (precedence != Precedence.NONE) { - let operand: Expression | null; + switch (token) { - // TODO: SpreadExpression, YieldExpression (currently become unsupported UnaryPrefixExpressions) + // TODO: SpreadExpression, YieldExpression + case Token.DOT_DOT_DOT: + case Token.YIELD: // fallthrough to unsupported UnaryPrefixExpression - // NewExpression - if (token == Token.NEW) { - operand = this.parseExpression(tn, Precedence.GROUPING); + // UnaryPrefixExpression + case Token.EXCLAMATION: + case Token.TILDE: + case Token.PLUS: + case Token.MINUS: + case Token.TYPEOF: + case Token.VOID: + case Token.DELETE: { + let operand = this.parseExpression(tn, Precedence.UNARY_PREFIX); if (!operand) return null; - if (operand.kind == NodeKind.CALL) { - return Node.createNewExpression( - (operand).expression, - (operand).typeArguments, - (operand).arguments, - tn.range(startPos, tn.pos) - ); - } else if (operand.kind == NodeKind.IDENTIFIER) { // new Class; - return Node.createNewExpression( - operand, null, [], - tn.range(startPos, tn.pos) - ); - } else { - this.error( - DiagnosticCode.This_expression_is_not_constructable, - operand.range - ); - } - return null; - } else { - operand = this.parseExpression(tn, precedence); + return Node.createUnaryPrefixExpression(token, operand, tn.range(startPos, tn.pos)); + } + case Token.PLUS_PLUS: + case Token.MINUS_MINUS: { + let operand = this.parseExpression(tn, Precedence.UNARY_PREFIX); if (!operand) return null; + switch (operand.kind) { + case NodeKind.IDENTIFIER: + case NodeKind.ELEMENTACCESS: + case NodeKind.PROPERTYACCESS: break; + default: { + 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)); } - // UnaryPrefixExpression - if (token == Token.PLUS_PLUS || token == Token.MINUS_MINUS) { - if ( - operand.kind != NodeKind.IDENTIFIER && - operand.kind != NodeKind.ELEMENTACCESS && - operand.kind != NodeKind.PROPERTYACCESS - ) { + // NewExpression + case Token.NEW: { + if (!tn.skipIdentifier()) { this.error( - DiagnosticCode.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access, - operand.range + DiagnosticCode.Identifier_expected, + tn.range() ); + return null; + } + let typeName = this.parseTypeName(tn); + if (!typeName) return null; + let typeArguments: TypeNode[] | null = null; + let arguments_: Expression[] | null = null; + if ( + tn.skip(Token.OPENPAREN) + || + (typeArguments = this.tryParseTypeArgumentsBeforeArguments(tn)) !== null + ) { + arguments_ = this.parseArguments(tn); + if (!arguments_) return null; + } else { + arguments_ = []; // new Type; } + return Node.createNewExpression( + typeName, + typeArguments, + arguments_, + tn.range(startPos, tn.pos) + ); } - return Node.createUnaryPrefixExpression(token, operand, tn.range(startPos, tn.pos)); - } - - var expr: Expression | null = null; - switch (token) { + // Special IdentifierExpression case Token.NULL: return Node.createNullExpression(tn.range()); case Token.TRUE: return Node.createTrueExpression(tn.range()); case Token.FALSE: return Node.createFalseExpression(tn.range()); + case Token.THIS: return Node.createThisExpression(tn.range()); + case Token.CONSTRUCTOR: return Node.createConstructorExpression(tn.range()); - // ParenthesizedExpression - // FunctionExpression + // ParenthesizedExpression or FunctionExpression case Token.OPENPAREN: { // determine whether this is a function expression @@ -3394,8 +3416,8 @@ export class Parser extends DiagnosticEmitter { tn.reset(state); // parse parenthesized - expr = this.parseExpression(tn); - if (!expr) return null; + let inner = this.parseExpression(tn); + if (!inner) return null; if (!tn.skip(Token.CLOSEPAREN)) { this.error( DiagnosticCode._0_expected, @@ -3403,12 +3425,14 @@ export class Parser extends DiagnosticEmitter { ); return null; } - return Node.createParenthesizedExpression(expr, tn.range(startPos, tn.pos)); + inner = Node.createParenthesizedExpression(inner, tn.range(startPos, tn.pos)); + return this.maybeParseCallExpression(tn, inner); } // ArrayLiteralExpression case Token.OPENBRACKET: { let elementExpressions = new Array(); while (!tn.skip(Token.CLOSEBRACKET)) { + let expr: Expression | null; if (tn.peek() == Token.COMMA) { expr = null; // omitted } else { @@ -3489,7 +3513,7 @@ export class Parser extends DiagnosticEmitter { ); return null; } - expr = this.parseExpression(tn, Precedence.CALL); + let expr = this.parseExpression(tn, Precedence.CALL); if (!expr) return null; return Node.createAssertionExpression( AssertionKind.PREFIX, @@ -3519,13 +3543,7 @@ export class Parser extends DiagnosticEmitter { startPos ); } - return identifier; - } - case Token.THIS: { - return Node.createThisExpression(tn.range(startPos, tn.pos)); - } - case Token.CONSTRUCTOR: { - return Node.createConstructorExpression(tn.range(startPos, tn.pos)); + return this.maybeParseCallExpression(tn, identifier, true); } case Token.SUPER: { if (tn.peek() != Token.DOT && tn.nextToken != Token.OPENPAREN) { @@ -3534,7 +3552,8 @@ export class Parser extends DiagnosticEmitter { tn.range() ); } - return Node.createSuperExpression(tn.range(startPos, tn.pos)); + let expr = Node.createSuperExpression(tn.range(startPos, tn.pos)); + return this.maybeParseCallExpression(tn, expr); } case Token.STRINGLITERAL: { return Node.createStringLiteralExpression(tn.readString(), tn.range(startPos, tn.pos)); @@ -3563,7 +3582,9 @@ export class Parser extends DiagnosticEmitter { ); } case Token.FUNCTION: { - return this.parseFunctionExpression(tn); + let expr = this.parseFunctionExpression(tn); + if (!expr) return null; + return this.maybeParseCallExpression(tn, expr); } case Token.CLASS: { return this.parseClassExpression(tn); @@ -3644,20 +3665,19 @@ export class Parser extends DiagnosticEmitter { precedence: Precedence = Precedence.COMMA ): Expression | null { assert(precedence != Precedence.NONE); - var expr = this.parseExpressionStart(tn); if (!expr) return null; - expr = this.maybeParseCallExpression(tn, expr); // simple call like on an Identifier - var startPos = expr.range.start; - var token: Token; - var next: Expression | null = null; + + // precedence climbing + // see: http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm#climbing var nextPrecedence: Precedence; while ( - (nextPrecedence = determinePrecedence(token = tn.peek())) >= precedence - ) { // precedence climbing - tn.next(); + (nextPrecedence = determinePrecedence(tn.peek())) >= precedence + ) { + let token = tn.next(); switch (token) { + // AssertionExpression case Token.AS: { let toType = this.parseType(tn); // reports @@ -3677,6 +3697,7 @@ export class Parser extends DiagnosticEmitter { null, tn.range(startPos, tn.pos) ); + expr = this.maybeParseCallExpression(tn, expr); break; } // InstanceOfExpression @@ -3692,7 +3713,7 @@ export class Parser extends DiagnosticEmitter { } // ElementAccessExpression case Token.OPENBRACKET: { - next = this.parseExpression(tn); // reports + let next = this.parseExpression(tn); // reports if (!next) return null; if (!tn.skip(Token.CLOSEBRACKET)) { this.error( @@ -3706,6 +3727,7 @@ export class Parser extends DiagnosticEmitter { next, tn.range(startPos, tn.pos) ); + expr = this.maybeParseCallExpression(tn, expr); break; } // UnaryPostfixExpression @@ -3763,27 +3785,19 @@ export class Parser extends DiagnosticEmitter { expr = Node.createCommaExpression(commaExprs, tn.range(startPos, tn.pos)); break; } - default: { - - // PropertyAccessExpression - if (token == Token.DOT) { - if (tn.skipIdentifier()) { - next = Node.createIdentifierExpression(tn.readIdentifier(), tn.range()); - } else { - next = this.parseExpression(tn, - isRightAssociative(token) - ? nextPrecedence - : nextPrecedence + 1 - ); - if (!next) return null; - } - if (next.kind == NodeKind.IDENTIFIER) { // expr '.' Identifier - expr = Node.createPropertyAccessExpression( - expr, - next, - tn.range(startPos, tn.pos) - ); - } else if (next.kind == NodeKind.CALL) { // expr '.' CallExpression + // PropertyAccessExpression + case Token.DOT: { + if (tn.skipIdentifier()) { // expr '.' Identifier + let next = Node.createIdentifierExpression(tn.readIdentifier(), tn.range()); + expr = Node.createPropertyAccessExpression( + expr, + next, + tn.range(startPos, tn.pos) + ); + } else { + let next = this.parseExpression(tn, nextPrecedence + 1); + if (!next) return null; + if (next.kind == NodeKind.CALL) { // expr '.' CallExpression expr = this.joinPropertyCall(tn, startPos, expr, next); if (!expr) return null; } else { @@ -3793,21 +3807,22 @@ export class Parser extends DiagnosticEmitter { ); return null; } - - // BinaryExpression - } else { - next = this.parseExpression(tn, - isRightAssociative(token) - ? nextPrecedence - : nextPrecedence + 1 - ); - if (!next) return null; - expr = Node.createBinaryExpression(token, expr, next, tn.range(startPos, tn.pos)); } + expr = this.maybeParseCallExpression(tn, expr, true); + break; + } + // BinaryExpression + default: { + let next = this.parseExpression(tn, + isRightAssociative(token) + ? nextPrecedence + : nextPrecedence + 1 + ); + if (!next) return null; + expr = Node.createBinaryExpression(token, expr, next, tn.range(startPos, tn.pos)); break; } } - expr = this.maybeParseCallExpression(tn, expr); // compound call like on an ElementAccess } return expr; } @@ -3848,24 +3863,24 @@ export class Parser extends DiagnosticEmitter { private maybeParseCallExpression( tn: Tokenizer, - expr: Expression + expr: Expression, + potentiallyGeneric: bool = false ): Expression { - if (nodeIsCallable(expr.kind)) { - let typeArguments: TypeNode[] | null = null; - while ( - tn.skip(Token.OPENPAREN) - || - nodeIsGenericCallable(expr.kind) && (typeArguments = this.tryParseTypeArgumentsBeforeArguments(tn)) !== null - ) { - let args = this.parseArguments(tn); - if (!args) break; - expr = Node.createCallExpression( // is again callable - expr, - typeArguments, - args, - tn.range(expr.range.start, tn.pos) - ); - } + var typeArguments: TypeNode[] | null = null; + while ( + tn.skip(Token.OPENPAREN) + || + potentiallyGeneric && (typeArguments = this.tryParseTypeArgumentsBeforeArguments(tn)) !== null + ) { + let args = this.parseArguments(tn); + if (!args) break; + expr = Node.createCallExpression( // is again callable + expr, + typeArguments, + args, + tn.range(expr.range.start, tn.pos) + ); + potentiallyGeneric = false; } return expr; } @@ -3980,25 +3995,6 @@ export const enum Precedence { GROUPING } -/** Determines the precedence of a starting token. */ -function determinePrecedenceStart(kind: Token): Precedence { - switch (kind) { - case Token.DOT_DOT_DOT: return Precedence.SPREAD; - case Token.YIELD: return Precedence.YIELD; - case Token.EXCLAMATION: - case Token.TILDE: - case Token.PLUS: - case Token.MINUS: - case Token.PLUS_PLUS: - case Token.MINUS_MINUS: - case Token.TYPEOF: - case Token.VOID: - case Token.DELETE: return Precedence.UNARY_PREFIX; - case Token.NEW: return Precedence.MEMBERACCESS; - } - return Precedence.NONE; -} - /** Determines the precende of a non-starting token. */ function determinePrecedence(kind: Token): Precedence { switch (kind) { diff --git a/src/resolver.ts b/src/resolver.ts index 3ef1d2228a..cc1fe23c9c 100644 --- a/src/resolver.ts +++ b/src/resolver.ts @@ -2414,7 +2414,7 @@ export class Resolver extends DiagnosticEmitter { /** How to proceed with eventual diagnostics. */ reportMode: ReportMode = ReportMode.REPORT ): Element | null { - var element = this.lookupExpression(node.expression, ctxFlow, ctxType, reportMode); + var element = this.resolveTypeName(node.typeName, ctxFlow.actualFunction, reportMode); if (!element) return null; if (element.kind == ElementKind.CLASS_PROTOTYPE) { return this.resolveClassInclTypeArguments( diff --git a/tests/compiler/new.optimized.wat b/tests/compiler/new.optimized.wat index 05dc3b8df7..4b11d9201e 100644 --- a/tests/compiler/new.optimized.wat +++ b/tests/compiler/new.optimized.wat @@ -7,6 +7,7 @@ (global $~lib/rt/stub/startOffset (mut i32) (i32.const 0)) (global $~lib/rt/stub/offset (mut i32) (i32.const 0)) (global $new/gen (mut i32) (i32.const 0)) + (global $new/ref2 (mut i32) (i32.const 0)) (export "memory" (memory $0)) (start $start) (func $~lib/rt/stub/maybeGrowMemory (; 0 ;) (type $FUNCSIG$vi) (param $0 i32) @@ -98,6 +99,15 @@ i32.const 4 call $~lib/rt/stub/__alloc global.set $new/gen + i32.const 5 + call $~lib/rt/stub/__alloc + global.set $new/ref2 + i32.const 5 + call $~lib/rt/stub/__alloc + global.set $new/ref2 + i32.const 5 + call $~lib/rt/stub/__alloc + global.set $new/ref2 ) (func $start (; 3 ;) (type $FUNCSIG$v) call $start:new diff --git a/tests/compiler/new.ts b/tests/compiler/new.ts index 7ebad6011c..87827c0da5 100644 --- a/tests/compiler/new.ts +++ b/tests/compiler/new.ts @@ -14,3 +14,14 @@ class Gen { var gen: Gen; gen = new Gen(); gen = new Gen().gen; + +namespace ns { + export class Ref { + get ref(): Ref { return this; } + } +} + +var ref2: ns.Ref; +ref2 = new ns.Ref(); +ref2 = new ns.Ref; +ref2 = new ns.Ref().ref; diff --git a/tests/compiler/new.untouched.wat b/tests/compiler/new.untouched.wat index 9093cb57c8..e765ab471e 100644 --- a/tests/compiler/new.untouched.wat +++ b/tests/compiler/new.untouched.wat @@ -10,6 +10,7 @@ (global $~lib/rt/stub/startOffset (mut i32) (i32.const 0)) (global $~lib/rt/stub/offset (mut i32) (i32.const 0)) (global $new/gen (mut i32) (i32.const 0)) + (global $new/ref2 (mut i32) (i32.const 0)) (global $~lib/heap/__heap_base i32 (i32.const 8)) (export "memory" (memory $0)) (start $start) @@ -158,13 +159,31 @@ local.get $0 call $~lib/rt/stub/__retain ) - (func $start:new (; 8 ;) (type $FUNCSIG$v) + (func $new/ns.Ref#constructor (; 8 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + i32.eqz + if + i32.const 0 + i32.const 5 + call $~lib/rt/stub/__alloc + call $~lib/rt/stub/__retain + local.set $0 + end + local.get $0 + ) + (func $new/ns.Ref#get:ref (; 9 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32) + local.get $0 + call $~lib/rt/stub/__retain + ) + (func $start:new (; 10 ;) (type $FUNCSIG$v) (local $0 i32) (local $1 i32) (local $2 i32) (local $3 i32) (local $4 i32) (local $5 i32) + (local $6 i32) + (local $7 i32) global.get $~lib/heap/__heap_base i32.const 15 i32.add @@ -232,6 +251,38 @@ end local.get $4 global.set $new/gen + i32.const 0 + call $new/ns.Ref#constructor + local.set $5 + global.get $new/ref2 + call $~lib/rt/stub/__release + local.get $5 + global.set $new/ref2 + i32.const 0 + call $new/ns.Ref#constructor + local.set $4 + global.get $new/ref2 + call $~lib/rt/stub/__release + local.get $4 + global.set $new/ref2 + i32.const 0 + call $new/ns.Ref#constructor + local.tee $4 + call $new/ns.Ref#get:ref + local.tee $5 + local.tee $6 + global.get $new/ref2 + local.tee $7 + i32.ne + if + local.get $6 + call $~lib/rt/stub/__retain + drop + local.get $7 + call $~lib/rt/stub/__release + end + local.get $6 + global.set $new/ref2 local.get $0 call $~lib/rt/stub/__release local.get $1 @@ -240,10 +291,14 @@ call $~lib/rt/stub/__release local.get $3 call $~lib/rt/stub/__release + local.get $4 + call $~lib/rt/stub/__release + local.get $5 + call $~lib/rt/stub/__release ) - (func $start (; 9 ;) (type $FUNCSIG$v) + (func $start (; 11 ;) (type $FUNCSIG$v) call $start:new ) - (func $null (; 10 ;) (type $FUNCSIG$v) + (func $null (; 12 ;) (type $FUNCSIG$v) ) ) From f6fb0fef4bb6c3784060fae738d8101f36ee9864 Mon Sep 17 00:00:00 2001 From: dcode Date: Wed, 20 Nov 2019 16:06:09 +0100 Subject: [PATCH 3/4] more --- src/ast.ts | 4 +-- src/parser.ts | 84 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 53 insertions(+), 35 deletions(-) diff --git a/src/ast.ts b/src/ast.ts index fc2f137cbc..2980df67b7 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -1643,12 +1643,10 @@ export class Source extends Node { statements: Statement[]; /** Full source text. */ text: string; - /** Tokenizer reference. */ - tokenizer: Tokenizer | null = null; /** Source map index. */ debugInfoIndex: i32 = -1; /** Re-exported sources. */ - exportPaths: Set | null = null; + exportPaths: string[] | null = null; /** Constructs a new source node. */ constructor(normalizedPath: string, text: string, kind: SourceKind) { diff --git a/src/parser.ts b/src/parser.ts index f2913fc6eb..ca689452f3 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -148,7 +148,6 @@ export class Parser extends DiagnosticEmitter { // tokenize and parse var tn = new Tokenizer(source, program.diagnostics); tn.onComment = this.onComment; - source.tokenizer = tn; var statements = source.statements; while (!tn.skip(Token.ENDOFFILE)) { let statement = this.parseTopLevelStatement(tn, null); @@ -399,10 +398,13 @@ export class Parser extends DiagnosticEmitter { return this.program; } + // types + /** Parses a type name. */ parseTypeName( tn: Tokenizer ): TypeName | null { + // at: Identifier ('.' Identifier)* var first = Node.createSimpleTypeName(tn.readIdentifier(), tn.range()); @@ -430,6 +432,8 @@ export class Parser extends DiagnosticEmitter { suppressErrors: bool = false ): TypeNode | null { + // before: Type + // NOTE: this parses our limited subset var token = tn.next(); var startPos = tn.tokenPos; @@ -806,7 +810,7 @@ export class Parser extends DiagnosticEmitter { let name = tn.readIdentifier(); let expression: Expression = Node.createIdentifierExpression(name, tn.range(startPos, tn.pos)); while (tn.skip(Token.DOT)) { - if (tn.skipIdentifier()) { + if (tn.skipIdentifier(IdentifierHandling.PREFER)) { name = tn.readIdentifier(); expression = Node.createPropertyAccessExpression( expression, @@ -2365,8 +2369,9 @@ export class Parser extends DiagnosticEmitter { let ret = Node.createExportStatement(null, path, isDeclare, tn.range(startPos, tn.pos)); let internalPath = assert(ret.internalPath); let source = tn.source; - if (!source.exportPaths) source.exportPaths = new Set(); - source.exportPaths.add(internalPath); + let exportPaths = source.exportPaths; + if (!exportPaths) source.exportPaths = [ internalPath ]; + else if (!exportPaths.includes(internalPath)) exportPaths.push(internalPath); if (!this.seenlog.has(internalPath)) { this.dependees.set(internalPath, this.currentSource); this.backlog.push(internalPath); @@ -3811,17 +3816,54 @@ export class Parser extends DiagnosticEmitter { expr = this.maybeParseCallExpression(tn, expr, true); break; } + // BinaryExpression (right associative) + 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.ASTERISK_ASTERISK: { + let next = this.parseExpression(tn, nextPrecedence); + if (!next) return null; + expr = Node.createBinaryExpression(token, expr, next, tn.range(startPos, tn.pos)); + break; + } // BinaryExpression - default: { - let next = this.parseExpression(tn, - isRightAssociative(token) - ? nextPrecedence - : nextPrecedence + 1 - ); + case Token.LESSTHAN: + case Token.GREATERTHAN: + case Token.LESSTHAN_EQUALS: + case Token.GREATERTHAN_EQUALS: + case Token.EQUALS_EQUALS: + case Token.EQUALS_EQUALS_EQUALS: + case Token.EXCLAMATION_EQUALS_EQUALS: + case Token.EXCLAMATION_EQUALS: + case Token.PLUS: + case Token.MINUS: + case Token.ASTERISK: + case Token.SLASH: + case Token.PERCENT: + case Token.LESSTHAN_LESSTHAN: + case Token.GREATERTHAN_GREATERTHAN: + case Token.GREATERTHAN_GREATERTHAN_GREATERTHAN: + case Token.AMPERSAND: + case Token.BAR: + case Token.CARET: + case Token.AMPERSAND_AMPERSAND: + case Token.BAR_BAR: { + let next = this.parseExpression(tn, nextPrecedence + 1); if (!next) return null; expr = Node.createBinaryExpression(token, expr, next, tn.range(startPos, tn.pos)); break; } + default: assert(false); // filtered by determinePrecedence } } return expr; @@ -4047,25 +4089,3 @@ function determinePrecedence(kind: Token): Precedence { } return Precedence.NONE; } - -/** Determines whether a non-starting token is right associative. */ -function isRightAssociative(kind: Token): bool { - 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; - } -} From c36560e2c7491c5403473d3ba00d52dfc8a57666 Mon Sep 17 00:00:00 2001 From: dcode Date: Wed, 20 Nov 2019 16:12:24 +0100 Subject: [PATCH 4/4] Update test --- tests/compiler/new.untouched.wat | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/compiler/new.untouched.wat b/tests/compiler/new.untouched.wat index e765ab471e..96deeb822d 100644 --- a/tests/compiler/new.untouched.wat +++ b/tests/compiler/new.untouched.wat @@ -220,7 +220,7 @@ if local.get $2 call $~lib/rt/stub/__retain - drop + local.set $2 local.get $3 call $~lib/rt/stub/__release end @@ -245,7 +245,7 @@ if local.get $4 call $~lib/rt/stub/__retain - drop + local.set $4 local.get $5 call $~lib/rt/stub/__release end @@ -277,7 +277,7 @@ if local.get $6 call $~lib/rt/stub/__retain - drop + local.set $6 local.get $7 call $~lib/rt/stub/__release end