@@ -24,9 +24,15 @@ const Ident = base.Ident;
24
24
const MAX_PARSE_DIAGNOSTICS : usize = 1_000 ;
25
25
const MAX_PARSE_STACK_SIZE : usize = 10_000 ;
26
26
27
- /// Temporary helper to create a Region from start and end positions
28
- /// TODO: Update all callers to compute proper end positions
27
+ /// Helper to create a Region from start and end positions
29
28
fn makeRegion (start : Position , end : Position ) Region {
29
+ // In debug mode, catch bugs where end is before start
30
+ if (std .debug .runtime_safety ) {
31
+ if (end .offset < start .offset ) {
32
+ std .debug .print ("makeRegion error: end.offset ({}) < start.offset ({})\n " , .{ end .offset , start .offset });
33
+ std .debug .assert (end .offset >= start .offset );
34
+ }
35
+ }
30
36
return Region { .start = start , .end = end };
31
37
}
32
38
@@ -564,7 +570,7 @@ fn parseModuleApply(self: *Parser) Error!Node.Idx {
564
570
close_paren_region = self .currentRegion ();
565
571
self .advance ();
566
572
} else {
567
- // Use last position as approximation
573
+ // Use current position for missing close paren
568
574
close_paren_region = makeRegion (self .currentPosition (), self .currentPosition ());
569
575
}
570
576
@@ -689,12 +695,15 @@ pub fn pushMalformed(self: *Parser, tag: AST.Diagnostic.Tag, start_pos: Position
689
695
}
690
696
} else start_pos ;
691
697
692
- const end_pos = self .currentPosition ();
698
+ var end_pos = self .currentPosition ();
693
699
694
700
if (self .peek () != .EndOfFile ) {
695
701
self .advance ();
702
+ end_pos = self .currentPosition ();
696
703
}
697
704
705
+ std .debug .assert (end_pos .offset >= actual_start_pos .offset );
706
+
698
707
// Only add diagnostics if we haven't hit the limit
699
708
if (self .diagnostics .items .len < MAX_PARSE_DIAGNOSTICS ) {
700
709
try self .pushDiagnostic (tag , actual_start_pos , end_pos );
@@ -860,6 +869,7 @@ fn parseAppHeader(self: *Parser, start_pos: Position) Error!AST.Header {
860
869
self .advance (); // consume 'platform' keyword
861
870
862
871
// Parse provides list after platform keyword
872
+ const provides_start = self .currentPosition ();
863
873
self .expect (.OpenSquare ) catch {
864
874
try self .pushDiagnostic (.header_expected_open_square , field_start , self .currentPosition ());
865
875
continue ;
@@ -872,8 +882,7 @@ fn parseAppHeader(self: *Parser, start_pos: Position) Error!AST.Header {
872
882
};
873
883
874
884
// Create the provides list node (use block as a container for the exposed items)
875
- // The region for the provides list spans from the current position to the last exposed item
876
- const provides_start = self .currentPosition ();
885
+ // The region for the provides list spans from the start to the last exposed item
877
886
const provides_region = if (provides_idx != @as (collections .NodeSlices (Node .Idx ).Idx , @enumFromInt (0 ))) blk : {
878
887
// Get the region of the provides list (should have items)
879
888
var iter = self .ast .node_slices .nodes (& provides_idx );
@@ -885,9 +894,15 @@ fn parseAppHeader(self: *Parser, start_pos: Position) Error!AST.Header {
885
894
const last_region = self .ast .nodes .fieldItem (.region , @as (collections .SafeMultiList (Node ).Idx , @enumFromInt (@intFromEnum (last ))));
886
895
break :blk makeRegion (provides_start , last_region .end );
887
896
} else {
888
- break :blk makeRegion (provides_start , provides_start );
897
+ // Empty list, use current position for end
898
+ const end_pos = self .currentPosition ();
899
+ break :blk makeRegion (provides_start , end_pos );
889
900
}
890
- } else makeRegion (provides_start , provides_start );
901
+ } else blk : {
902
+ // No items, use current position for end
903
+ const end_pos = self .currentPosition ();
904
+ break :blk makeRegion (provides_start , end_pos );
905
+ };
891
906
const provides_node = try self .ast .appendNode (self .gpa , provides_region , .block , .{ .nodes = provides_idx });
892
907
893
908
// Create the platform binop: string_value platform provides_node
@@ -1850,7 +1865,7 @@ fn parseListLiteral(self: *Parser) Error!Node.Idx {
1850
1865
self .advance ();
1851
1866
} else {
1852
1867
try self .pushDiagnostic (.expected_expr_close_square_or_comma , start_pos , self .currentPosition ());
1853
- // Use the current position as an approximation for error recovery
1868
+ // Use current position for missing close bracket
1854
1869
close_bracket_region = makeRegion (self .currentPosition (), self .currentPosition ());
1855
1870
}
1856
1871
@@ -2183,10 +2198,10 @@ fn parseMatch(self: *Parser) Error!Node.Idx {
2183
2198
const branch_node = self .ast .nodes .get (branch_idx );
2184
2199
2185
2200
if (branch_node .tag != .binop_thick_arrow ) {
2186
- // Not a branch - restore position and break
2187
- // Actually we can 't easily restore position, so just break
2188
- // This means we might have consumed a token that's not part of the match
2189
- // TODO: Better error recovery here
2201
+ // Not a branch - we've parsed something that's not a valid match branch
2202
+ // Since we don 't backtrack, treat it as a malformed branch and report error
2203
+ // Then break out of branch parsing
2204
+ _ = try self . pushMalformed ( .expr_unexpected_token , branch_node . region . start );
2190
2205
break ;
2191
2206
}
2192
2207
@@ -2387,12 +2402,12 @@ pub fn parseTypeAnno(self: *Parser) Error!Node.Idx {
2387
2402
}
2388
2403
2389
2404
// Operator precedence
2390
- const BindingPower = struct {
2405
+ pub const BindingPower = struct {
2391
2406
left : u8 ,
2392
2407
right : u8 ,
2393
2408
};
2394
2409
2395
- fn getBindingPower (tag : Token.Tag ) BindingPower {
2410
+ pub fn getBindingPower (tag : Token.Tag ) BindingPower {
2396
2411
return switch (tag ) {
2397
2412
.OpBar = > .{ .left = 10 , .right = 11 },
2398
2413
.OpOr = > .{ .left = 20 , .right = 21 },
@@ -2412,7 +2427,7 @@ fn getBindingPower(tag: Token.Tag) BindingPower {
2412
2427
};
2413
2428
}
2414
2429
2415
- fn tokenToBinOpTag (tag : Token.Tag ) ? Node.Tag {
2430
+ pub fn tokenToBinOpTag (tag : Token.Tag ) ? Node.Tag {
2416
2431
return switch (tag ) {
2417
2432
.OpAssign = > .binop_equals ,
2418
2433
.OpEquals = > .binop_double_equals ,
0 commit comments