diff --git a/handrolled_parser/core.mbt b/handrolled_parser/core.mbt index 283a3c97..e3653f0d 100644 --- a/handrolled_parser/core.mbt +++ b/handrolled_parser/core.mbt @@ -121,6 +121,11 @@ fn State::loc_start_with(state : Self, start : Position) -> Location { { start, end: state.parsed_position } } +///| +fn State::empty_loc(state : Self) -> Location { + { start: state.parsed_position, end: state.parsed_position } +} + ///| fn State::peek_location(state : Self) -> Location { let (_, start, end) = state.peek() diff --git a/handrolled_parser/parse_expr_test.mbt b/handrolled_parser/parse_expr_test.mbt index 15e141df..c035eb0a 100644 --- a/handrolled_parser/parse_expr_test.mbt +++ b/handrolled_parser/parse_expr_test.mbt @@ -290,7 +290,7 @@ test { "loc": { "file": "", "start": { "line": 2, "column": 4 }, - "end": { "line": 2, "column": 8 }, + "end": { "line": 4, "column": 5 }, }, "children": { "pattern": { @@ -318,7 +318,7 @@ test { "loc": { "file": "", "start": { "line": 2, "column": 9 }, - "end": { "line": 2, "column": 8 }, + "end": { "line": 4, "column": 5 }, }, "children": { "expr": { @@ -432,7 +432,7 @@ test { "loc": { "file": "", "start": { "line": 2, "column": 9 }, - "end": { "line": 2, "column": 8 }, + "end": { "line": 4, "column": 5 }, }, "children": {}, }, diff --git a/handrolled_parser/parser.mbt b/handrolled_parser/parser.mbt index b93cfc08..d5d1a65c 100644 --- a/handrolled_parser/parser.mbt +++ b/handrolled_parser/parser.mbt @@ -443,7 +443,10 @@ fn State::parse_fun_decl( ) |> @list.from_array }) - let params_loc = self.loc_start_with(param_loc_start) + let params_loc = match decl_params { + Some(_) => self.loc_start_with(param_loc_start) + None => self.empty_loc() + } let (return_type, error_type) = self.parse_func_return_type() let loc = self.loc_start_with(spos) { @@ -4259,8 +4262,8 @@ fn State::parse_simple_expr( self.parse_semi_trailing_record() COLON | COMMA | RBRACE => self.parse_record_expr(type_name=None) _ => { - let loc = self.loc_start_with(spos) let expr = self.parse_block_expr() + let loc = self.loc_start_with(spos) Group(expr~, group=Brace, loc~) } } @@ -4276,8 +4279,8 @@ fn State::parse_simple_expr( | BYTES(_) | STRING(_) if self.peek_token(nth=2) is COLON => self.parse_map_expr() _ => { - let loc = self.loc_start_with(spos) let expr = self.parse_block_expr() + let loc = self.loc_start_with(spos) Group(expr~, group=Brace, loc~) } } diff --git a/test/sync_test/loc_regression_test.mbt b/test/sync_test/loc_regression_test.mbt new file mode 100644 index 00000000..d131422e --- /dev/null +++ b/test/sync_test/loc_regression_test.mbt @@ -0,0 +1,60 @@ +///| +let loc_regression_source = + #| let global: Int = { + #| 1 + #| } + #| + #| fn init { + #| let _ = global + #| } + +///| +fn parse_loc_regression( + parser : @parser.Parser, +) -> (@syntax.Impls, @basic.Location, @basic.Location, Json) raise Error { + let (impls, diagnostics) = @parser.parse_string( + loc_regression_source, + name="loc_regression.mbt", + parser~, + ) + if !diagnostics.is_empty() { + fail(diagnostics.to_string()) + } + let mut brace_group_loc = None + let mut params_loc = None + for impl_ in impls { + match impl_ { + TopLetDef(expr=Group(group=Brace, loc~, ..), ..) => + brace_group_loc = Some(loc) + TopFuncDef(fun_decl~, ..) => params_loc = Some(fun_decl.params_loc) + _ => () + } + } + let json = @basic.show_loc.protect(@basic.String, fn() { + impls.map(impl_ => impl_.json_repr()).to_json() + }) + match (brace_group_loc, params_loc) { + (Some(brace_group_loc), Some(params_loc)) => + (impls, brace_group_loc, params_loc, json) + _ => fail("failed to find loc regression fixtures in parsed AST") + } +} + +///| +test "loc parity for brace group and empty fn params" { + let (_, hand_group_loc, hand_params_loc, hand_json) = parse_loc_regression( + @parser.Handrolled, + ) + let (_, yacc_group_loc, yacc_params_loc, yacc_json) = parse_loc_regression( + @parser.MoonYacc, + ) + inspect(hand_group_loc, content="loc_regression.mbt-1:20-3:3") + inspect(hand_params_loc, content="loc_regression.mbt-5:9-5:9") + inspect(yacc_group_loc, content="loc_regression.mbt-1:20-3:3") + inspect(yacc_params_loc, content="loc_regression.mbt-5:9-5:9") + if hand_json != yacc_json { + fail( + "loc-sensitive AST mismatch between handrolled and yacc parsers.\nhandrolled:\n\{hand_json.stringify(indent=2)}\n\nyacc:\n\{yacc_json.stringify(indent=2)}", + ) + } +}