Skip to content

Allow keywords in named import/export syntax #107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 28 additions & 27 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import {
Tokenizer,
Token,
Range,
CommentHandler
CommentHandler,
IdentifierHandling
} from "./tokenizer";

import {
Expand Down Expand Up @@ -579,7 +580,7 @@ export class Parser extends DiagnosticEmitter {
this.tryParseSignatureIsSignature = false;
return null;
}
} else if (tn.skip(Token.IDENTIFIER)) {
} else if (tn.skipIdentifier()) {
let name = Node.createIdentifierExpression(tn.readIdentifier(), tn.range(tn.tokenPos, tn.pos));
if (tn.skip(Token.QUESTION)) {
isSignature = true;
Expand Down Expand Up @@ -678,11 +679,11 @@ export class Parser extends DiagnosticEmitter {
// at '@': Identifier ('.' Identifier)* '(' Arguments

var startPos = tn.tokenPos;
if (tn.skip(Token.IDENTIFIER)) {
if (tn.skipIdentifier()) {
let name = tn.readIdentifier();
let expression: Expression = Node.createIdentifierExpression(name, tn.range(startPos, tn.pos));
while (tn.skip(Token.DOT)) {
if (tn.skip(Token.IDENTIFIER)) {
if (tn.skipIdentifier()) {
name = tn.readIdentifier();
expression = Node.createPropertyAccessExpression(
expression,
Expand Down Expand Up @@ -744,7 +745,7 @@ export class Parser extends DiagnosticEmitter {

// before: Identifier (':' Type)? ('=' Expression)?

if (!tn.skip(Token.IDENTIFIER)) {
if (!tn.skipIdentifier()) {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
Expand Down Expand Up @@ -853,7 +854,7 @@ export class Parser extends DiagnosticEmitter {

// before: Identifier ('=' Expression)?

if (!tn.skip(Token.IDENTIFIER)) {
if (!tn.skipIdentifier()) {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
Expand Down Expand Up @@ -1081,7 +1082,7 @@ export class Parser extends DiagnosticEmitter {
}
isRest = true;
}
if (tn.skip(Token.IDENTIFIER)) {
if (tn.skipIdentifier()) {
if (!isRest) startRange = tn.range();
let identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
let type: CommonTypeNode | null = null;
Expand Down Expand Up @@ -1154,7 +1155,7 @@ export class Parser extends DiagnosticEmitter {
// '{' Statement* '}'
// ';'?

if (!tn.skip(Token.IDENTIFIER)) {
if (!tn.skipIdentifier()) {
this.error(
DiagnosticCode.Identifier_expected,
tn.range(tn.pos)
Expand Down Expand Up @@ -1281,7 +1282,7 @@ export class Parser extends DiagnosticEmitter {
// Statement

if (tn.token == Token.FUNCTION) {
if (tn.skip(Token.IDENTIFIER)) {
if (tn.skipIdentifier()) {
name = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
} else { // empty name
name = Node.createEmptyIdentifierExpression(tn.range(tn.pos));
Expand Down Expand Up @@ -1397,7 +1398,7 @@ export class Parser extends DiagnosticEmitter {

var isInterface = tn.token == Token.INTERFACE;

if (!tn.skip(Token.IDENTIFIER)) {
if (!tn.skipIdentifier()) {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
Expand Down Expand Up @@ -1565,7 +1566,7 @@ export class Parser extends DiagnosticEmitter {
var setStart: i32 = 0;
var setEnd: i32 = 0;
if (tn.skip(Token.GET)) {
if (tn.peek(true, true) == Token.IDENTIFIER && !tn.nextTokenOnNewLine) {
if (tn.peek(true, IdentifierHandling.PREFER) == Token.IDENTIFIER && !tn.nextTokenOnNewLine) {
flags |= CommonFlags.GET;
isGetter = true;
setStart = tn.tokenPos;
Expand All @@ -1580,7 +1581,7 @@ export class Parser extends DiagnosticEmitter {
tn.reset(state);
}
} else if (tn.skip(Token.SET)) {
if (tn.peek(true, true) == Token.IDENTIFIER && !tn.nextTokenOnNewLine) {
if (tn.peek(true, IdentifierHandling.PREFER) == Token.IDENTIFIER && !tn.nextTokenOnNewLine) {
flags |= CommonFlags.SET | CommonFlags.SET;
isSetter = true;
setStart = tn.tokenPos;
Expand Down Expand Up @@ -1617,7 +1618,7 @@ export class Parser extends DiagnosticEmitter {
}
}

if (!isConstructor && !tn.skip(Token.IDENTIFIER)) {
if (!isConstructor && !tn.skipIdentifier()) {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
Expand Down Expand Up @@ -1833,7 +1834,7 @@ export class Parser extends DiagnosticEmitter {

// at 'namespace': Identifier '{' (Variable | Function)* '}'

if (tn.skip(Token.IDENTIFIER)) {
if (tn.skipIdentifier()) {
let identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
if (tn.skip(Token.OPENBRACE)) {
let members = new Array<Statement>();
Expand Down Expand Up @@ -1926,11 +1927,11 @@ export class Parser extends DiagnosticEmitter {

// before: Identifier ('as' Identifier)?

if (tn.skip(Token.IDENTIFIER)) {
if (tn.skipIdentifier()) {
let identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
let asIdentifier: IdentifierExpression | null = null;
if (tn.skip(Token.AS)) {
if (tn.skip(Token.IDENTIFIER)) {
if (tn.skipIdentifierName()) {
asIdentifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
} else {
this.error(
Expand Down Expand Up @@ -1986,7 +1987,7 @@ export class Parser extends DiagnosticEmitter {
}
} else if (tn.skip(Token.ASTERISK)) {
if (tn.skip(Token.AS)) {
if (tn.skip(Token.IDENTIFIER)) {
if (tn.skipIdentifier()) {
namespaceName = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
} else {
this.error(
Expand Down Expand Up @@ -2044,11 +2045,11 @@ export class Parser extends DiagnosticEmitter {

// before: Identifier ('as' Identifier)?

if (tn.skip(Token.IDENTIFIER)) {
if (tn.skipIdentifierName()) {
let identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
let asIdentifier: IdentifierExpression | null = null;
if (tn.skip(Token.AS)) {
if (tn.skip(Token.IDENTIFIER)) {
if (tn.skipIdentifier()) {
asIdentifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
} else {
this.error(
Expand Down Expand Up @@ -2081,10 +2082,10 @@ export class Parser extends DiagnosticEmitter {

// at 'export' 'import': Identifier ('=' Identifier)? ';'?

if (tn.skip(Token.IDENTIFIER)) {
if (tn.skipIdentifier()) {
let asIdentifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
if (tn.skip(Token.EQUALS)) {
if (tn.skip(Token.IDENTIFIER)) {
if (tn.skipIdentifier()) {
let identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
let ret = Node.createExportImportStatement(identifier, asIdentifier, tn.range(startPos, tn.pos));
tn.skip(Token.SEMICOLON);
Expand Down Expand Up @@ -2242,7 +2243,7 @@ export class Parser extends DiagnosticEmitter {

var identifier: IdentifierExpression | null = null;
if (tn.peek(true) == Token.IDENTIFIER && !tn.nextTokenOnNewLine) {
tn.next(true);
tn.next(IdentifierHandling.PREFER);
identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
}
var ret = Node.createBreakStatement(identifier, tn.range());
Expand All @@ -2258,7 +2259,7 @@ export class Parser extends DiagnosticEmitter {

var identifier: IdentifierExpression | null = null;
if (tn.peek(true) == Token.IDENTIFIER && !tn.nextTokenOnNewLine) {
tn.next(true);
tn.next(IdentifierHandling.PREFER);
identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
}
var ret = Node.createContinueStatement(identifier, tn.range());
Expand Down Expand Up @@ -2578,7 +2579,7 @@ export class Parser extends DiagnosticEmitter {
);
return null;
}
if (!tn.skip(Token.IDENTIFIER)) {
if (!tn.skipIdentifier()) {
this.error(
DiagnosticCode.Identifier_expected,
tn.range()
Expand Down Expand Up @@ -2656,7 +2657,7 @@ export class Parser extends DiagnosticEmitter {

// at 'type': Identifier ('<' TypeParameters '>')? '=' Type ';'?

if (tn.skip(Token.IDENTIFIER)) {
if (tn.skipIdentifier()) {
let name = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
let typeParameters: TypeParameterNode[] | null = null;
if (tn.skip(Token.LESSTHAN)) {
Expand Down Expand Up @@ -2744,7 +2745,7 @@ export class Parser extends DiagnosticEmitter {
tn: Tokenizer
): Expression | null {

var token = tn.next(true);
var token = tn.next(IdentifierHandling.PREFER);
var startPos = tn.tokenPos;
var expr: Expression | null = null;

Expand Down Expand Up @@ -2821,7 +2822,7 @@ export class Parser extends DiagnosticEmitter {
let state = tn.mark();
let again = true;
do {
switch (tn.next(true)) {
switch (tn.next(IdentifierHandling.PREFER)) {

// function expression
case Token.DOT_DOT_DOT: {
Expand Down
37 changes: 29 additions & 8 deletions src/tokenizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,12 @@ export enum Token {
ENDOFFILE
}

export enum IdentifierHandling {
DEFAULT,
PREFER,
ALWAYS
}

export function tokenFromKeyword(text: string): Token {
switch (text.length && text.charCodeAt(0)) {
case CharCode.a: {
Expand Down Expand Up @@ -523,12 +529,15 @@ export class Tokenizer extends DiagnosticEmitter {
}
}

next(preferIdentifier: bool = false): Token {
next(identifierHandling: IdentifierHandling = IdentifierHandling.DEFAULT): Token {
this.nextToken = -1;
return this.token = this.unsafeNext(preferIdentifier);
return this.token = this.unsafeNext(identifierHandling);
}

private unsafeNext(preferIdentifier: bool = false, maxTokenLength: i32 = i32.MAX_VALUE): Token {
private unsafeNext(
identifierHandling: IdentifierHandling = IdentifierHandling.DEFAULT,
maxTokenLength: i32 = i32.MAX_VALUE
): Token {
var text = this.source.text;
while (this.pos < this.end) {
this.tokenPos = this.pos;
Expand Down Expand Up @@ -908,7 +917,11 @@ export class Tokenizer extends DiagnosticEmitter {
let keywordToken = tokenFromKeyword(keywordText);
if (
keywordToken != Token.INVALID &&
!(preferIdentifier && tokenIsAlsoIdentifier(keywordToken))
identifierHandling !== IdentifierHandling.ALWAYS &&
!(
identifierHandling === IdentifierHandling.PREFER &&
tokenIsAlsoIdentifier(keywordToken)
)
) {
return keywordToken;
}
Expand All @@ -933,15 +946,15 @@ export class Tokenizer extends DiagnosticEmitter {

peek(
checkOnNewLine: bool = false,
preferIdentifier: bool = false,
identifierHandling: IdentifierHandling = IdentifierHandling.DEFAULT,
maxCompoundLength: i32 = i32.MAX_VALUE
): Token {
var text = this.source.text;
if (this.nextToken < 0) {
let posBefore = this.pos;
let tokenBefore = this.token;
let tokenPosBefore = this.tokenPos;
this.nextToken = this.unsafeNext(preferIdentifier, maxCompoundLength);
this.nextToken = this.unsafeNext(identifierHandling, maxCompoundLength);
this.nextTokenPos = this.tokenPos;
if (checkOnNewLine) {
this.nextTokenOnNewLine = false;
Expand All @@ -959,7 +972,15 @@ export class Tokenizer extends DiagnosticEmitter {
return this.nextToken;
}

skip(token: Token): bool {
skipIdentifier(): bool {
return this.skip(Token.IDENTIFIER, IdentifierHandling.PREFER);
}

skipIdentifierName(): bool {
return this.skip(Token.IDENTIFIER, IdentifierHandling.ALWAYS);
}

skip(token: Token, identifierHandling: IdentifierHandling = IdentifierHandling.DEFAULT): bool {
var posBefore = this.pos;
var tokenBefore = this.token;
var tokenPosBefore = this.tokenPos;
Expand All @@ -970,7 +991,7 @@ export class Tokenizer extends DiagnosticEmitter {
break;
}
}
this.token = this.unsafeNext(token == Token.IDENTIFIER, maxCompoundLength);
this.token = this.unsafeNext(identifierHandling, maxCompoundLength);
if (this.token == token) {
this.nextToken = -1;
return true;
Expand Down
9 changes: 9 additions & 0 deletions tests/compiler/named-export-default.optimized.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(module
(type $i (func (result i32)))
(memory $0 1)
(export "default" (func $named-export-default/get3))
(export "memory" (memory $0))
(func $named-export-default/get3 (; 0 ;) (type $i) (result i32)
(i32.const 3)
)
)
5 changes: 5 additions & 0 deletions tests/compiler/named-export-default.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function get3(): i32 {
return 3;
}

export {get3 as default};
12 changes: 12 additions & 0 deletions tests/compiler/named-export-default.untouched.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
(module
(type $i (func (result i32)))
(global $HEAP_BASE i32 (i32.const 4))
(memory $0 1)
(export "default" (func $named-export-default/get3))
(export "memory" (memory $0))
(func $named-export-default/get3 (; 0 ;) (type $i) (result i32)
(return
(i32.const 3)
)
)
)
12 changes: 12 additions & 0 deletions tests/compiler/named-import-default.optimized.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
(module
(type $i (func (result i32)))
(memory $0 1)
(export "getValue" (func $named-import-default/getValue))
(export "memory" (memory $0))
(func $named-export-default/get3 (; 0 ;) (type $i) (result i32)
(i32.const 3)
)
(func $named-import-default/getValue (; 1 ;) (type $i) (result i32)
(call $named-export-default/get3)
)
)
7 changes: 7 additions & 0 deletions tests/compiler/named-import-default.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import {
default as get3
} from "./named-export-default";

export function getValue(): i32 {
return get3();
}
17 changes: 17 additions & 0 deletions tests/compiler/named-import-default.untouched.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
(module
(type $i (func (result i32)))
(global $HEAP_BASE i32 (i32.const 4))
(memory $0 1)
(export "getValue" (func $named-import-default/getValue))
(export "memory" (memory $0))
(func $named-export-default/get3 (; 0 ;) (type $i) (result i32)
(return
(i32.const 3)
)
)
(func $named-import-default/getValue (; 1 ;) (type $i) (result i32)
(return
(call $named-export-default/get3)
)
)
)