@@ -107,6 +107,7 @@ const ParseError = error{
107
107
/// Invalid expression or pattern inside '(...)'
108
108
InvalidExpressionInsideParens ,
109
109
// TODO: remove these two errors
110
+ JsxExpectedTagName ,
110
111
JsxNotImplemented ,
111
112
TypeScriptNotImplemented ,
112
113
// The parser instance has already been used
@@ -155,15 +156,19 @@ pub const SourceType = enum { script, module };
155
156
pub const Config = struct {
156
157
/// Whether we're parsing a script or a module.
157
158
source_type : SourceType = .module ,
158
- /// Enable strict mode by default
159
+ /// Enable strict mode in the entire file
159
160
strict_mode : bool = false ,
160
161
/// Enable JSX support
161
162
jsx : bool = false ,
162
163
/// Enable typescript support
163
164
typescript : bool = false ,
164
165
/// Add a special ast node for expressions wrapped in '()'.
165
- /// Parses `(a)` as `parenthesized_expression { identifier }` instead of { identifier }
166
+ /// Parses `(a)` as `parenthesized_expression { identifier }` instead of ' { identifier }'
166
167
preserve_parens : bool = true ,
168
+
169
+ pub fn isTsx (self : Config ) bool {
170
+ return self .jsx and self .typescript ;
171
+ }
167
172
};
168
173
169
174
allocator : std.mem.Allocator ,
@@ -596,21 +601,22 @@ fn defaultImportSpecifier(self: *Self) Error!Node.Index {
596
601
}
597
602
598
603
fn importSpecifier (self : * Self ) Error ! Node.Index {
599
- var name_token : TokenWithId = undefined ; // assigned in the 'blk' block
600
- const name = blk : {
604
+ const name , const name_token = blk : {
601
605
if (self .isAtToken (.string_literal )) {
602
- name_token = try self .next ();
603
- break :blk try self .stringLiteralFromToken (name_token .id );
606
+ const name_tok = try self .next ();
607
+ break :blk .{ try self .stringLiteralFromToken (name_tok .id ), name_tok } ;
604
608
}
605
609
606
- name_token = try self .expectIdOrKeyword ();
610
+ const name_tok = try self .expectIdOrKeyword ();
607
611
608
612
// `import {foo as bar }` // <- `foo` is an identifier node
609
613
// `import { foo }` // <- `foo` is a BINDING identifier node
610
- break : blk try if (self .isAtToken (.kw_as ))
611
- self .identifier (name_token .id )
614
+ const name_node = try if (self .isAtToken (.kw_as ))
615
+ self .identifier (name_tok .id )
612
616
else
613
- self .bindingIdentifier (name_token .id );
617
+ self .bindingIdentifier (name_tok .id );
618
+
619
+ break :blk .{ name_node , name_tok };
614
620
};
615
621
616
622
if (self .isAtToken (.kw_as ) or ! self .isIdentifier (name_token .token .tag )) {
@@ -637,6 +643,8 @@ fn importSpecifier(self: *Self) Error!Node.Index {
637
643
);
638
644
}
639
645
646
+ /// A string literal or an identifier reference.
647
+ /// https://262.ecma-international.org/15.0/index.html#prod-ModuleExportName
640
648
fn moduleExportName (self : * Self ) Error ! Node.Index {
641
649
if (self .isAtToken (.string_literal ))
642
650
return self .stringLiteral ();
@@ -964,6 +972,9 @@ fn labeledOrExpressionStatement(self: *Self) Error!Node.Index {
964
972
return self .expressionStatement ();
965
973
}
966
974
975
+ /// https://262.ecma-international.org/15.0/index.html#prod-LabelledStatement
976
+ /// LabeledStatement:
977
+ /// LabelIdentifier ':' LabelledItem
967
978
fn labeledStatement (self : * Self ) Error ! Node.Index {
968
979
const label = try self .next ();
969
980
_ = try self .expectToken (.@":" );
@@ -988,6 +999,10 @@ fn labeledStatement(self: *Self) Error!Node.Index {
988
999
);
989
1000
}
990
1001
1002
+ /// https://262.ecma-international.org/15.0/index.html#prod-LabelledItem
1003
+ /// LabelledItem:
1004
+ /// Statement
1005
+ /// FunctionDeclaration
991
1006
fn labeledItem (self : * Self ) Error ! Node.Index {
992
1007
if (self .isAtToken (.kw_function )) {
993
1008
const lookahead = try self .lookAhead ();
@@ -1498,7 +1513,7 @@ fn forLoopIterator(self: *Self) Error!struct { ForLoopKind, ast.Node.Index } {
1498
1513
// for (var/let/const x = 0; x < 10; x++)
1499
1514
// for (var/let/const x in y)
1500
1515
// for (var/let/const x of y)
1501
- const maybe_decl_kw = blk : {
1516
+ const maybe_vardecl_keyword = blk : {
1502
1517
const curr = self .current .token .tag ;
1503
1518
if (curr == .kw_var or curr == .kw_const ) {
1504
1519
break :blk try self .next ();
@@ -1512,7 +1527,7 @@ fn forLoopIterator(self: *Self) Error!struct { ForLoopKind, ast.Node.Index } {
1512
1527
break :blk null ;
1513
1528
};
1514
1529
1515
- if (maybe_decl_kw ) | decl_kw | {
1530
+ if (maybe_vardecl_keyword ) | decl_kw | {
1516
1531
var loop_kind = ForLoopKind .basic ;
1517
1532
const iterator = try self .completeVarDeclLoopIterator (
1518
1533
lpar .id ,
@@ -1976,6 +1991,11 @@ fn catchClause(self: *Self) Error!Node.Index {
1976
1991
}, catch_kw .id , self .nodeSpan (body ).end );
1977
1992
}
1978
1993
1994
+ /// https://262.ecma-international.org/15.0/index.html#prod-CatchParameter
1995
+ ///
1996
+ /// CatchParameter:
1997
+ /// BindingIdentifier
1998
+ /// BindingPattern
1979
1999
fn catchParameter (self : * Self ) Error ! ? Node.Index {
1980
2000
if (! self .isAtToken (.@"(" )) return null ;
1981
2001
_ = try self .nextToken ();
@@ -2033,12 +2053,8 @@ fn caseBlock(self: *Self) Error!ast.SubRange {
2033
2053
.kw_case = > break :blk try self .caseClause (),
2034
2054
.kw_default = > {
2035
2055
if (saw_default ) {
2036
- try self .emitDiagnostic (
2037
- self .current .token .startCoord (self .source ),
2038
- "Multiple 'default' clauses are not allowed in a switch statement" ,
2039
- .{},
2040
- );
2041
- return Error .MultipleDefaults ;
2056
+ // TODO: emit this error and keep going. No need to stop parsing.
2057
+ return self .errorOnToken (self .current .token , ParseError .MultipleDefaults );
2042
2058
}
2043
2059
2044
2060
saw_default = true ;
@@ -2521,6 +2537,7 @@ fn errorMessage(err: ParseError) []const u8 {
2521
2537
ParseError .ExpectedImportSpecifier = > "Expected an import specifier, '*', or file path" ,
2522
2538
ParseError .InvalidExpressionInsideParens = > "Invalid expression or pattern inside (...)" ,
2523
2539
ParseError .RestElementNotLastInParams = > "Rest element must be the last item in a function parameter list" ,
2540
+ ParseError .JsxExpectedTagName = > "Expected a tag name after '<' in JSX code" ,
2524
2541
};
2525
2542
}
2526
2543
@@ -2804,15 +2821,35 @@ fn expect2(self: *Self, tag1: Token.Tag, tag2: Token.Tag) Error!TokenWithId {
2804
2821
return Error .UnexpectedToken ;
2805
2822
}
2806
2823
2807
- /// Consume the next token from the lexer, skipping all comments.
2824
+ /// Consume the next token from the lexer, skipping all comments and whitespaces.
2825
+ /// Returns the CURRENT token which was consumed in the previous call to 'next',
2826
+ /// and advances the pointer to the next token.
2808
2827
fn next (self : * Self ) Error ! TokenWithId {
2809
2828
var next_token = try self .tokenizer .next ();
2810
- while (next_token .tag == .comment or
2811
- next_token .tag == .whitespace ) : (next_token = try self .tokenizer .next ())
2812
- {
2829
+ while (next_token .tag == .comment or next_token .tag == .whitespace ) {
2830
+ try self .saveToken (next_token );
2831
+ next_token = try self .tokenizer .next ();
2832
+ }
2833
+
2834
+ return self .advanceToToken (next_token );
2835
+ }
2836
+
2837
+ /// Consume the next JSX token from the lexer, skipping all comments and whitespaces.
2838
+ ///
2839
+ /// [is_inside_jsx_tags]: Whether we are currently inside JSX tags (i.e. between '<' and '>').
2840
+ fn nextJsx (self : * Self , is_inside_jsx_tags : bool ) Error ! TokenWithId {
2841
+ var next_token = try self .tokenizer .nextJsx (is_inside_jsx_tags );
2842
+ while (next_token .tag == .comment or next_token .tag == .whitespace ) {
2813
2843
try self .saveToken (next_token );
2844
+ next_token = try self .tokenizer .nextJsx (is_inside_jsx_tags );
2814
2845
}
2815
2846
2847
+ return self .advanceToToken (next_token );
2848
+ }
2849
+
2850
+ /// Set [next_token] as the current token, and update `self.current`, and `self.prev_token_line`
2851
+ /// Returns the newly saved token along with its ID.
2852
+ fn advanceToToken (self : * Self , next_token : Token ) error {OutOfMemory }! TokenWithId {
2816
2853
try self .saveToken (next_token );
2817
2854
2818
2855
const current = self .current .token ;
@@ -2821,6 +2858,7 @@ fn next(self: *Self) Error!TokenWithId {
2821
2858
self .current .token = next_token ;
2822
2859
self .current .id = @enumFromInt (self .tokens .items .len - 1 );
2823
2860
self .prev_token_line = current .line ;
2861
+
2824
2862
return TokenWithId { .token = current , .id = current_id };
2825
2863
}
2826
2864
@@ -2847,7 +2885,7 @@ inline fn nextToken(self: *Self) Error!Token {
2847
2885
/// without consuming it, or advancing in the source.
2848
2886
///
2849
2887
/// NOTE: The token returned by this function should only be used for
2850
- /// inspection, and never added to the AST.
2888
+ /// inspection, and should not be added to the AST.
2851
2889
fn lookAhead (self : * Self ) Error ! Token {
2852
2890
const line = self .tokenizer .line ;
2853
2891
const index = self .tokenizer .index ;
@@ -2862,7 +2900,7 @@ fn lookAhead(self: *Self) Error!Token {
2862
2900
return next_token ;
2863
2901
}
2864
2902
2865
- inline fn peek (self : * Self ) Token {
2903
+ fn peek (self : * Self ) Token {
2866
2904
return self .current .token ;
2867
2905
}
2868
2906
@@ -4288,7 +4326,10 @@ fn primaryExpression(self: *Self) Error!Node.Index {
4288
4326
.@"(" = > return self .groupingExprOrArrowParameters (& token ),
4289
4327
.kw_async = > return self .asyncExpression (& token ),
4290
4328
.kw_function = > return self .functionExpression (token .id , .{}),
4291
- else = > {},
4329
+ .@"<" = > {},
4330
+ else = > {
4331
+ // TODO: JSX
4332
+ },
4292
4333
}
4293
4334
4294
4335
if (self .isKeywordIdentifier (token .token .tag )) {
@@ -4312,6 +4353,25 @@ fn primaryExpression(self: *Self) Error!Node.Index {
4312
4353
return Error .UnexpectedToken ;
4313
4354
}
4314
4355
4356
+ fn jsxExpression (self : * Self ) Error ! void {
4357
+ assert (self .current .token .tag == .@"<" );
4358
+ _ = try self .nextJsx (true );
4359
+
4360
+ if (self .isAtToken (.@">" )) {
4361
+ // TODO: implement jsx fragment
4362
+ unreachable ;
4363
+ }
4364
+
4365
+ const jsx_identifier = try self .nextJsx (true );
4366
+ if (jsx_identifier .token .tag != .jsx_identifier ) {
4367
+ return self .errorOnToken (jsx_identifier .token , ParseError .JsxExpectedTagName );
4368
+ }
4369
+ }
4370
+
4371
+ fn jsxFragment (_ : * Self ) Error ! void {}
4372
+ fn jsxElement (_ : * Self ) Error ! void {}
4373
+ fn jsxClosingElement (_ : * Self ) Error ! void {}
4374
+
4315
4375
/// Check whether a numeric literal is valid in strict mode.
4316
4376
/// Decimals and legacy octal literals are not allowed (e.g: 01, 09, 023127, etc.).
4317
4377
fn isValidStrictModeNumber (self : * const Self , token : * const Token ) bool {
0 commit comments