Skip to content

Commit c9b169f

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 c9b169f

File tree

3 files changed

+68
-18
lines changed

3 files changed

+68
-18
lines changed

src/ast.ts

+20-5
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(
@@ -753,9 +757,10 @@ export abstract class Node {
753757
static createWhileStatement(
754758
condition: Expression,
755759
statement: Statement,
760+
label: IdentifierExpression | null,
756761
range: Range
757762
): WhileStatement {
758-
return new WhileStatement(condition, statement, range);
763+
return new WhileStatement(condition, statement, label, range);
759764
}
760765

761766
/** Tests if this node is a literal of the specified kind. */
@@ -1788,6 +1793,8 @@ export class BlockStatement extends Statement {
17881793
constructor(
17891794
/** Contained statements. */
17901795
public statements: Statement[],
1796+
/** Label, if any. */
1797+
public label: IdentifierExpression | null,
17911798
/** Source range. */
17921799
range: Range
17931800
) {
@@ -1858,6 +1865,8 @@ export class DoStatement extends Statement {
18581865
public body: Statement,
18591866
/** Condition when to repeat. */
18601867
public condition: Expression,
1868+
/** Label, if any. */
1869+
public label: IdentifierExpression | null,
18611870
/** Source range. */
18621871
range: Range
18631872
) {
@@ -2022,6 +2031,8 @@ export class ForStatement extends Statement {
20222031
public incrementor: Expression | null,
20232032
/** Body statement being looped over. */
20242033
public body: Statement,
2034+
/** Label, if any. */
2035+
public label: IdentifierExpression | null,
20252036
/** Source range. */
20262037
range: Range
20272038
) {
@@ -2038,6 +2049,8 @@ export class ForOfStatement extends Statement {
20382049
public iterable: Expression,
20392050
/** Body statement being looped over. */
20402051
public body: Statement,
2052+
/** Label, if any. */
2053+
public label: IdentifierExpression | null,
20412054
/** Source range. */
20422055
range: Range
20432056
) {
@@ -2382,6 +2395,8 @@ export class WhileStatement extends Statement {
23822395
public condition: Expression,
23832396
/** Body statement being looped over. */
23842397
public body: Statement,
2398+
/** Label, if any. */
2399+
public label: IdentifierExpression | null,
23852400
/** Source range. */
23862401
range: Range
23872402
) {

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

src/parser.ts

+47-13
Original file line numberDiff line numberDiff line change
@@ -2899,7 +2899,34 @@ 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.For:
2917+
case Token.While:
2918+
case Token.Do:
2919+
case Token.OpenBrace:
2920+
// Do nothing
2921+
break;
2922+
default:
2923+
this.error(DiagnosticCode.A_label_is_not_allowed_here, range);
2924+
}
2925+
} else {
2926+
tn.reset(preIdentifierState);
2927+
}
2928+
}
2929+
29032930
switch (token) {
29042931
case Token.Break: {
29052932
statement = this.parseBreak(tn);
@@ -2914,11 +2941,11 @@ export class Parser extends DiagnosticEmitter {
29142941
break;
29152942
}
29162943
case Token.Do: {
2917-
statement = this.parseDoStatement(tn);
2944+
statement = this.parseDoStatement(tn, label);
29182945
break;
29192946
}
29202947
case Token.For: {
2921-
statement = this.parseForStatement(tn);
2948+
statement = this.parseForStatement(tn, label);
29222949
break;
29232950
}
29242951
case Token.If: {
@@ -2934,7 +2961,7 @@ export class Parser extends DiagnosticEmitter {
29342961
break;
29352962
}
29362963
case Token.OpenBrace: {
2937-
statement = this.parseBlockStatement(tn, topLevel);
2964+
statement = this.parseBlockStatement(tn, topLevel, label);
29382965
break;
29392966
}
29402967
case Token.Return: {
@@ -2967,7 +2994,7 @@ export class Parser extends DiagnosticEmitter {
29672994
break;
29682995
}
29692996
case Token.While: {
2970-
statement = this.parseWhileStatement(tn);
2997+
statement = this.parseWhileStatement(tn, label);
29712998
break;
29722999
}
29733000
case Token.Type: { // also identifier
@@ -2994,7 +3021,8 @@ export class Parser extends DiagnosticEmitter {
29943021

29953022
parseBlockStatement(
29963023
tn: Tokenizer,
2997-
topLevel: bool
3024+
topLevel: bool,
3025+
label: IdentifierExpression | null = null
29983026
): BlockStatement | null {
29993027

30003028
// at '{': Statement* '}' ';'?
@@ -3013,7 +3041,7 @@ export class Parser extends DiagnosticEmitter {
30133041
statements.push(statement);
30143042
}
30153043
}
3016-
let ret = Node.createBlockStatement(statements, tn.range(startPos, tn.pos));
3044+
let ret = Node.createBlockStatement(statements, label, tn.range(startPos, tn.pos));
30173045
if (topLevel) tn.skip(Token.Semicolon);
30183046
return ret;
30193047
}
@@ -3051,7 +3079,8 @@ export class Parser extends DiagnosticEmitter {
30513079
}
30523080

30533081
parseDoStatement(
3054-
tn: Tokenizer
3082+
tn: Tokenizer,
3083+
label: IdentifierExpression | null
30553084
): DoStatement | null {
30563085

30573086
// at 'do': Statement 'while' '(' Expression ')' ';'?
@@ -3067,7 +3096,7 @@ export class Parser extends DiagnosticEmitter {
30673096
if (!condition) return null;
30683097

30693098
if (tn.skip(Token.CloseParen)) {
3070-
let ret = Node.createDoStatement(statement, condition, tn.range(startPos, tn.pos));
3099+
let ret = Node.createDoStatement(statement, condition, label, tn.range(startPos, tn.pos));
30713100
tn.skip(Token.Semicolon);
30723101
return ret;
30733102
} else {
@@ -3106,7 +3135,8 @@ export class Parser extends DiagnosticEmitter {
31063135
}
31073136

31083137
parseForStatement(
3109-
tn: Tokenizer
3138+
tn: Tokenizer,
3139+
label: IdentifierExpression | null
31103140
): Statement | null {
31113141

31123142
// at 'for': '(' Statement? Expression? ';' Expression? ')' Statement
@@ -3139,7 +3169,7 @@ export class Parser extends DiagnosticEmitter {
31393169
);
31403170
return null;
31413171
}
3142-
return this.parseForOfStatement(tn, startPos, initializer);
3172+
return this.parseForOfStatement(tn, startPos, initializer, label);
31433173
}
31443174
if (initializer.kind == NodeKind.Variable) {
31453175
let declarations = (<VariableStatement>initializer).declarations;
@@ -3153,7 +3183,7 @@ export class Parser extends DiagnosticEmitter {
31533183
); // recoverable
31543184
}
31553185
}
3156-
return this.parseForOfStatement(tn, startPos, initializer);
3186+
return this.parseForOfStatement(tn, startPos, initializer, label);
31573187
}
31583188
this.error(
31593189
DiagnosticCode.Identifier_expected,
@@ -3215,6 +3245,7 @@ export class Parser extends DiagnosticEmitter {
32153245
: null,
32163246
incrementor,
32173247
statement,
3248+
label,
32183249
tn.range(startPos, tn.pos)
32193250
);
32203251

@@ -3243,6 +3274,7 @@ export class Parser extends DiagnosticEmitter {
32433274
tn: Tokenizer,
32443275
startPos: i32,
32453276
variable: Statement,
3277+
label: IdentifierExpression | null
32463278
): ForOfStatement | null {
32473279

32483280
// at 'of': Expression ')' Statement
@@ -3265,6 +3297,7 @@ export class Parser extends DiagnosticEmitter {
32653297
variable,
32663298
iterable,
32673299
statement,
3300+
label,
32683301
tn.range(startPos, tn.pos)
32693302
);
32703303
}
@@ -3609,7 +3642,8 @@ export class Parser extends DiagnosticEmitter {
36093642
}
36103643

36113644
parseWhileStatement(
3612-
tn: Tokenizer
3645+
tn: Tokenizer,
3646+
label: IdentifierExpression | null
36133647
): WhileStatement | null {
36143648

36153649
// at 'while': '(' Expression ')' Statement ';'?
@@ -3621,7 +3655,7 @@ export class Parser extends DiagnosticEmitter {
36213655
if (tn.skip(Token.CloseParen)) {
36223656
let statement = this.parseStatement(tn);
36233657
if (!statement) return null;
3624-
let ret = Node.createWhileStatement(expression, statement, tn.range(startPos, tn.pos));
3658+
let ret = Node.createWhileStatement(expression, statement, label, tn.range(startPos, tn.pos));
36253659
tn.skip(Token.Semicolon);
36263660
return ret;
36273661
} else {

0 commit comments

Comments
 (0)