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 2 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
19 changes: 10 additions & 9 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 @@ -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 @@ -1930,7 +1931,7 @@ export class Parser extends DiagnosticEmitter {
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 @@ -2044,7 +2045,7 @@ 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)) {
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 @@ -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
50 changes: 43 additions & 7 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 Down Expand Up @@ -970,7 +983,10 @@ export class Tokenizer extends DiagnosticEmitter {
break;
}
}
this.token = this.unsafeNext(token == Token.IDENTIFIER, maxCompoundLength);
this.token = this.unsafeNext(
token == Token.IDENTIFIER ? IdentifierHandling.PREFER : IdentifierHandling.DEFAULT,
maxCompoundLength
);
if (this.token == token) {
this.nextToken = -1;
return true;
Expand All @@ -982,6 +998,26 @@ export class Tokenizer extends DiagnosticEmitter {
}
}

/**
* Skip any name token, whether or not it is a keyword.
*/
skipIdentifierName(): bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this become an IdentifierHandling option on skip(), maybe? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense, yeah this copied function is a bit ugly. I guess it felt a bit weird given the special case in skip.

How about this: I get rid of the special case in skip and specify it using an optional param like you suggested. Then, to make the common case the most natural to write, I'll make skipIdentifier as a shorthand for skip(Token.IDENTIFIER, IdentifierHandling.PREFER) and keep skipIdentifierName as a shorthand for skip(Token.IDENTIFIER, IdentifierHandling.ALWAYS). Let me know what you think.

var posBefore = this.pos;
var tokenBefore = this.token;
var tokenPosBefore = this.tokenPos;
var maxCompoundLength = i32.MAX_VALUE;
this.token = this.unsafeNext(IdentifierHandling.ALWAYS, maxCompoundLength);
if (this.token == Token.IDENTIFIER) {
this.nextToken = -1;
return true;
} else {
this.pos = posBefore;
this.token = tokenBefore;
this.tokenPos = tokenPosBefore;
return false;
}
}

mark(): State {
var state: State;
if (reusableState) {
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)
)
)
)