Skip to content

Commit c9029f4

Browse files
committed
Add support for labeled statements to the parser/AST
This is a prerequisite for supporting labeled breaks/continues. Clearly unusable labels, such as `x: let foo = 1;` report an error by default, similar to TS's behavior.
1 parent 40850fe commit c9029f4

File tree

3 files changed

+77
-22
lines changed

3 files changed

+77
-22
lines changed

Diff for: src/ast.ts

+24-6
Original file line numberDiff line numberDiff line change
@@ -440,9 +440,10 @@ export abstract class Node {
440440

441441
static createBlockStatement(
442442
statements: Statement[],
443+
label: IdentifierExpression | null,
443444
range: Range
444445
): BlockStatement {
445-
return new BlockStatement(statements, range);
446+
return new BlockStatement(statements, label, range);
446447
}
447448

448449
static createBreakStatement(
@@ -475,9 +476,10 @@ export abstract class Node {
475476
static createDoStatement(
476477
body: Statement,
477478
condition: Expression,
479+
label: IdentifierExpression | null,
478480
range: Range
479481
): DoStatement {
480-
return new DoStatement(body, condition, range);
482+
return new DoStatement(body, condition, label, range);
481483
}
482484

483485
static createEmptyStatement(
@@ -607,18 +609,20 @@ export abstract class Node {
607609
condition: Expression | null,
608610
incrementor: Expression | null,
609611
body: Statement,
612+
label: IdentifierExpression | null,
610613
range: Range
611614
): ForStatement {
612-
return new ForStatement(initializer, condition, incrementor, body, range);
615+
return new ForStatement(initializer, condition, incrementor, body, label, range);
613616
}
614617

615618
static createForOfStatement(
616619
variable: Statement,
617620
iterable: Expression,
618621
body: Statement,
622+
label: IdentifierExpression | null,
619623
range: Range
620624
): ForOfStatement {
621-
return new ForOfStatement(variable, iterable, body, range);
625+
return new ForOfStatement(variable, iterable, body, label, range);
622626
}
623627

624628
static createFunctionDeclaration(
@@ -675,9 +679,10 @@ export abstract class Node {
675679
static createSwitchStatement(
676680
condition: Expression,
677681
cases: SwitchCase[],
682+
label: IdentifierExpression | null,
678683
range: Range
679684
): SwitchStatement {
680-
return new SwitchStatement(condition, cases, range);
685+
return new SwitchStatement(condition, cases, label, range);
681686
}
682687

683688
static createSwitchCase(
@@ -753,9 +758,10 @@ export abstract class Node {
753758
static createWhileStatement(
754759
condition: Expression,
755760
statement: Statement,
761+
label: IdentifierExpression | null,
756762
range: Range
757763
): WhileStatement {
758-
return new WhileStatement(condition, statement, range);
764+
return new WhileStatement(condition, statement, label, range);
759765
}
760766

761767
/** Tests if this node is a literal of the specified kind. */
@@ -1788,6 +1794,8 @@ export class BlockStatement extends Statement {
17881794
constructor(
17891795
/** Contained statements. */
17901796
public statements: Statement[],
1797+
/** Label, if any. */
1798+
public label: IdentifierExpression | null,
17911799
/** Source range. */
17921800
range: Range
17931801
) {
@@ -1858,6 +1866,8 @@ export class DoStatement extends Statement {
18581866
public body: Statement,
18591867
/** Condition when to repeat. */
18601868
public condition: Expression,
1869+
/** Label, if any. */
1870+
public label: IdentifierExpression | null,
18611871
/** Source range. */
18621872
range: Range
18631873
) {
@@ -2022,6 +2032,8 @@ export class ForStatement extends Statement {
20222032
public incrementor: Expression | null,
20232033
/** Body statement being looped over. */
20242034
public body: Statement,
2035+
/** Label, if any. */
2036+
public label: IdentifierExpression | null,
20252037
/** Source range. */
20262038
range: Range
20272039
) {
@@ -2038,6 +2050,8 @@ export class ForOfStatement extends Statement {
20382050
public iterable: Expression,
20392051
/** Body statement being looped over. */
20402052
public body: Statement,
2053+
/** Label, if any. */
2054+
public label: IdentifierExpression | null,
20412055
/** Source range. */
20422056
range: Range
20432057
) {
@@ -2258,6 +2272,8 @@ export class SwitchStatement extends Statement {
22582272
public condition: Expression,
22592273
/** Contained cases. */
22602274
public cases: SwitchCase[],
2275+
/** Label, if any. */
2276+
public label: IdentifierExpression | null,
22612277
/** Source range. */
22622278
range: Range
22632279
) {
@@ -2382,6 +2398,8 @@ export class WhileStatement extends Statement {
23822398
public condition: Expression,
23832399
/** Body statement being looped over. */
23842400
public body: Statement,
2401+
/** Label, if any. */
2402+
public label: IdentifierExpression | null,
23852403
/** Source range. */
23862404
range: Range
23872405
) {

Diff for: src/diagnosticMessages.json

+1
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@
125125
"A class may only extend another class.": 1311,
126126
"A parameter property cannot be declared using a rest parameter.": 1317,
127127
"A default export can only be used in a module.": 1319,
128+
"A label is not allowed here.": 1344,
128129
"An expression of type '{0}' cannot be tested for truthiness.": 1345,
129130
"An identifier or keyword cannot immediately follow a numeric literal.": 1351,
130131

Diff for: src/parser.ts

+52-16
Original file line numberDiff line numberDiff line change
@@ -2899,7 +2899,35 @@ export class Parser extends DiagnosticEmitter {
28992899

29002900
let state = tn.mark();
29012901
let token = tn.next();
2902+
let label: IdentifierExpression | null = null;
29022903
let statement: Statement | null = null;
2904+
2905+
// Detect labeled statements
2906+
if (token == Token.Identifier) {
2907+
const preIdentifierState = tn.mark();
2908+
const identifier = tn.readIdentifier();
2909+
const range = tn.range();
2910+
2911+
if (tn.skip(Token.Colon)) {
2912+
label = Node.createIdentifierExpression(identifier, range);
2913+
token = tn.next();
2914+
2915+
switch (token) {
2916+
case Token.Do:
2917+
case Token.For:
2918+
case Token.OpenBrace:
2919+
case Token.Switch:
2920+
case Token.While:
2921+
// Do nothing
2922+
break;
2923+
default:
2924+
this.error(DiagnosticCode.A_label_is_not_allowed_here, range);
2925+
}
2926+
} else {
2927+
tn.reset(preIdentifierState);
2928+
}
2929+
}
2930+
29032931
switch (token) {
29042932
case Token.Break: {
29052933
statement = this.parseBreak(tn);
@@ -2914,11 +2942,11 @@ export class Parser extends DiagnosticEmitter {
29142942
break;
29152943
}
29162944
case Token.Do: {
2917-
statement = this.parseDoStatement(tn);
2945+
statement = this.parseDoStatement(tn, label);
29182946
break;
29192947
}
29202948
case Token.For: {
2921-
statement = this.parseForStatement(tn);
2949+
statement = this.parseForStatement(tn, label);
29222950
break;
29232951
}
29242952
case Token.If: {
@@ -2934,7 +2962,7 @@ export class Parser extends DiagnosticEmitter {
29342962
break;
29352963
}
29362964
case Token.OpenBrace: {
2937-
statement = this.parseBlockStatement(tn, topLevel);
2965+
statement = this.parseBlockStatement(tn, topLevel, label);
29382966
break;
29392967
}
29402968
case Token.Return: {
@@ -2951,7 +2979,7 @@ export class Parser extends DiagnosticEmitter {
29512979
return Node.createEmptyStatement(tn.range(tn.tokenPos));
29522980
}
29532981
case Token.Switch: {
2954-
statement = this.parseSwitchStatement(tn);
2982+
statement = this.parseSwitchStatement(tn, label);
29552983
break;
29562984
}
29572985
case Token.Throw: {
@@ -2967,7 +2995,7 @@ export class Parser extends DiagnosticEmitter {
29672995
break;
29682996
}
29692997
case Token.While: {
2970-
statement = this.parseWhileStatement(tn);
2998+
statement = this.parseWhileStatement(tn, label);
29712999
break;
29723000
}
29733001
case Token.Type: { // also identifier
@@ -2994,7 +3022,8 @@ export class Parser extends DiagnosticEmitter {
29943022

29953023
parseBlockStatement(
29963024
tn: Tokenizer,
2997-
topLevel: bool
3025+
topLevel: bool,
3026+
label: IdentifierExpression | null = null
29983027
): BlockStatement | null {
29993028

30003029
// at '{': Statement* '}' ';'?
@@ -3013,7 +3042,7 @@ export class Parser extends DiagnosticEmitter {
30133042
statements.push(statement);
30143043
}
30153044
}
3016-
let ret = Node.createBlockStatement(statements, tn.range(startPos, tn.pos));
3045+
let ret = Node.createBlockStatement(statements, label, tn.range(startPos, tn.pos));
30173046
if (topLevel) tn.skip(Token.Semicolon);
30183047
return ret;
30193048
}
@@ -3051,7 +3080,8 @@ export class Parser extends DiagnosticEmitter {
30513080
}
30523081

30533082
parseDoStatement(
3054-
tn: Tokenizer
3083+
tn: Tokenizer,
3084+
label: IdentifierExpression | null
30553085
): DoStatement | null {
30563086

30573087
// at 'do': Statement 'while' '(' Expression ')' ';'?
@@ -3067,7 +3097,7 @@ export class Parser extends DiagnosticEmitter {
30673097
if (!condition) return null;
30683098

30693099
if (tn.skip(Token.CloseParen)) {
3070-
let ret = Node.createDoStatement(statement, condition, tn.range(startPos, tn.pos));
3100+
let ret = Node.createDoStatement(statement, condition, label, tn.range(startPos, tn.pos));
30713101
tn.skip(Token.Semicolon);
30723102
return ret;
30733103
} else {
@@ -3106,7 +3136,8 @@ export class Parser extends DiagnosticEmitter {
31063136
}
31073137

31083138
parseForStatement(
3109-
tn: Tokenizer
3139+
tn: Tokenizer,
3140+
label: IdentifierExpression | null
31103141
): Statement | null {
31113142

31123143
// at 'for': '(' Statement? Expression? ';' Expression? ')' Statement
@@ -3139,7 +3170,7 @@ export class Parser extends DiagnosticEmitter {
31393170
);
31403171
return null;
31413172
}
3142-
return this.parseForOfStatement(tn, startPos, initializer);
3173+
return this.parseForOfStatement(tn, startPos, initializer, label);
31433174
}
31443175
if (initializer.kind == NodeKind.Variable) {
31453176
let declarations = (<VariableStatement>initializer).declarations;
@@ -3153,7 +3184,7 @@ export class Parser extends DiagnosticEmitter {
31533184
); // recoverable
31543185
}
31553186
}
3156-
return this.parseForOfStatement(tn, startPos, initializer);
3187+
return this.parseForOfStatement(tn, startPos, initializer, label);
31573188
}
31583189
this.error(
31593190
DiagnosticCode.Identifier_expected,
@@ -3215,6 +3246,7 @@ export class Parser extends DiagnosticEmitter {
32153246
: null,
32163247
incrementor,
32173248
statement,
3249+
label,
32183250
tn.range(startPos, tn.pos)
32193251
);
32203252

@@ -3243,6 +3275,7 @@ export class Parser extends DiagnosticEmitter {
32433275
tn: Tokenizer,
32443276
startPos: i32,
32453277
variable: Statement,
3278+
label: IdentifierExpression | null
32463279
): ForOfStatement | null {
32473280

32483281
// at 'of': Expression ')' Statement
@@ -3265,6 +3298,7 @@ export class Parser extends DiagnosticEmitter {
32653298
variable,
32663299
iterable,
32673300
statement,
3301+
label,
32683302
tn.range(startPos, tn.pos)
32693303
);
32703304
}
@@ -3309,7 +3343,8 @@ export class Parser extends DiagnosticEmitter {
33093343
}
33103344

33113345
parseSwitchStatement(
3312-
tn: Tokenizer
3346+
tn: Tokenizer,
3347+
label: IdentifierExpression | null
33133348
): SwitchStatement | null {
33143349

33153350
// at 'switch': '(' Expression ')' '{' SwitchCase* '}' ';'?
@@ -3326,7 +3361,7 @@ export class Parser extends DiagnosticEmitter {
33263361
if (!switchCase) return null;
33273362
switchCases.push(switchCase);
33283363
}
3329-
let ret = Node.createSwitchStatement(condition, switchCases, tn.range(startPos, tn.pos));
3364+
let ret = Node.createSwitchStatement(condition, switchCases, label, tn.range(startPos, tn.pos));
33303365
tn.skip(Token.Semicolon);
33313366
return ret;
33323367
} else {
@@ -3609,7 +3644,8 @@ export class Parser extends DiagnosticEmitter {
36093644
}
36103645

36113646
parseWhileStatement(
3612-
tn: Tokenizer
3647+
tn: Tokenizer,
3648+
label: IdentifierExpression | null
36133649
): WhileStatement | null {
36143650

36153651
// at 'while': '(' Expression ')' Statement ';'?
@@ -3621,7 +3657,7 @@ export class Parser extends DiagnosticEmitter {
36213657
if (tn.skip(Token.CloseParen)) {
36223658
let statement = this.parseStatement(tn);
36233659
if (!statement) return null;
3624-
let ret = Node.createWhileStatement(expression, statement, tn.range(startPos, tn.pos));
3660+
let ret = Node.createWhileStatement(expression, statement, label, tn.range(startPos, tn.pos));
36253661
tn.skip(Token.Semicolon);
36263662
return ret;
36273663
} else {

0 commit comments

Comments
 (0)