My working unpac space for OCaml projects in development
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix 'in' operator context and 'let'/'await' as identifiers

- Add allow_in context saving/restoring in:
- Array literals
- Object literals
- Parenthesized expressions
- Import() calls
- Conditional expression consequent
- Add in_module flag to properly track module context for await
- Fix 'let' as identifier in non-strict mode for:
- Statement context
- For-loop init context
- Add check for keywords that can be binding names in non-strict mode
- Fix getter/setter ASI in class bodies (newline after get/set)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+99 -18
+99 -18
lib/quickjs/parser/parser.ml
··· 24 24 lexer : Lexer.t; 25 25 mutable current : Token.token; 26 26 mutable strict_mode : bool; 27 + mutable in_module : bool; (* Parsing as module (await is reserved) *) 27 28 mutable in_function : bool; 28 29 mutable in_generator : bool; 29 30 mutable in_async : bool; ··· 39 40 lexer; 40 41 current; 41 42 strict_mode = false; 43 + in_module = false; 42 44 in_function = false; 43 45 in_generator = false; 44 46 in_async = false; ··· 116 118 match kw with 117 119 | Token.Kw_implements | Token.Kw_interface | Token.Kw_package 118 120 | Token.Kw_private | Token.Kw_protected | Token.Kw_public 119 - | Token.Kw_static -> true 121 + | Token.Kw_static | Token.Kw_let -> true 120 122 | _ -> false 121 123 122 124 (** Check if a keyword is a contextual keyword (always usable as identifier) *) ··· 134 136 let loc = current_loc parser in 135 137 advance parser; 136 138 { Ast.name; loc } 137 - (* 'await' can be identifier when not in async function and not in module (strict mode) *) 138 - | Token.Keyword Token.Kw_await when not parser.in_async && not parser.strict_mode -> 139 + (* 'await' can be identifier when not in async function and not in module *) 140 + | Token.Keyword Token.Kw_await when not parser.in_async && not parser.in_module -> 139 141 let loc = current_loc parser in 140 142 advance parser; 141 143 { Ast.name = "await"; loc } ··· 222 224 let key = 223 225 if computed then begin 224 226 advance parser; 227 + (* Inside [], 'in' is allowed as a binary operator *) 228 + let saved_allow_in = parser.allow_in in 229 + parser.allow_in <- true; 225 230 let k = parse_assignment_expression parser in 231 + parser.allow_in <- saved_allow_in; 226 232 expect parser Token.RBracket; 227 233 k 228 234 end else begin ··· 288 294 Ast.mk_pat ~loc (Ast.Pat_identifier { Ast.name; loc }) 289 295 290 296 (* 'await' can be identifier when not in async function and not in module *) 291 - | Token.Keyword Token.Kw_await when not parser.in_async && not parser.strict_mode -> 297 + | Token.Keyword Token.Kw_await when not parser.in_async && not parser.in_module -> 292 298 let loc = current_loc parser in 293 299 advance parser; 294 300 Ast.mk_pat ~loc (Ast.Pat_identifier { Ast.name = "await"; loc }) ··· 413 419 let key = 414 420 if computed then begin 415 421 advance parser; 422 + (* Inside [], 'in' is allowed as a binary operator *) 423 + let saved_allow_in = parser.allow_in in 424 + parser.allow_in <- true; 416 425 let k = parse_assignment_expression parser in 426 + parser.allow_in <- saved_allow_in; 417 427 expect parser Token.RBracket; 418 428 k 419 429 end else begin ··· 476 486 and parse_array_literal parser : Ast.expression = 477 487 let start_loc = current_loc parser in 478 488 expect parser Token.LBracket; 489 + (* Inside array literal, 'in' is allowed as a binary operator *) 490 + let saved_allow_in = parser.allow_in in 491 + parser.allow_in <- true; 479 492 let elements = ref [] in 480 493 while current_token parser <> Token.RBracket && not (is_at_end parser) do 481 494 if current_token parser = Token.Comma then begin ··· 495 508 expect parser Token.Comma 496 509 end 497 510 done; 511 + parser.allow_in <- saved_allow_in; 498 512 let end_loc = current_loc parser in 499 513 expect parser Token.RBracket; 500 514 let loc = Source.mk_loc ~start:start_loc.start ~end_:end_loc.end_ () in ··· 503 517 and parse_object_literal parser : Ast.expression = 504 518 let start_loc = current_loc parser in 505 519 expect parser Token.LBrace; 520 + (* Inside object literal, 'in' is allowed as a binary operator *) 521 + let saved_allow_in = parser.allow_in in 522 + parser.allow_in <- true; 506 523 let properties = ref [] in 507 524 while current_token parser <> Token.RBrace && not (is_at_end parser) do 508 525 let prop = parse_property parser in ··· 510 527 if current_token parser <> Token.RBrace then 511 528 expect parser Token.Comma 512 529 done; 530 + parser.allow_in <- saved_allow_in; 513 531 let end_loc = current_loc parser in 514 532 expect parser Token.RBrace; 515 533 let loc = Source.mk_loc ~start:start_loc.start ~end_:end_loc.end_ () in ··· 578 596 advance parser; 579 597 Ast.mk_expr ~loc (Ast.Identifier { Ast.name; loc }) 580 598 581 - (* 'await' can be identifier when not in async function and not in module (strict mode) *) 582 - | Token.Keyword Token.Kw_await when not parser.in_async && not parser.strict_mode -> 599 + (* 'await' can be identifier when not in async function and not in module *) 600 + | Token.Keyword Token.Kw_await when not parser.in_async && not parser.in_module -> 583 601 advance parser; 584 602 Ast.mk_expr ~loc (Ast.Identifier { Ast.name = "await"; loc }) 585 603 ··· 608 626 609 627 | Token.LParen -> 610 628 advance parser; 629 + (* Inside parentheses, 'in' is allowed as a binary operator *) 630 + let saved_allow_in = parser.allow_in in 631 + parser.allow_in <- true; 611 632 let expr = parse_expression parser in 633 + parser.allow_in <- saved_allow_in; 612 634 expect parser Token.RParen; 613 635 Ast.mk_expr ~loc:expr.Ast.loc (Ast.Paren expr) 614 636 ··· 730 752 | Token.LParen -> 731 753 (* import("module") or import("module", options) *) 732 754 advance parser; 755 + (* Per spec: import(AssignmentExpression[+In]) - 'in' is always allowed *) 756 + let saved_allow_in = parser.allow_in in 757 + parser.allow_in <- true; 733 758 let arg = parse_assignment_expression parser in 734 759 (* Allow optional comma and second argument (import attributes) *) 735 760 if current_token parser = Token.Comma then begin ··· 741 766 if current_token parser = Token.Comma then advance parser 742 767 end 743 768 end; 769 + parser.allow_in <- saved_allow_in; 744 770 expect parser Token.RParen; 745 771 let end_loc = current_loc parser in 746 772 let loc = Source.mk_loc ~start:start_loc.start ~end_:end_loc.end_ () in ··· 867 893 | _ -> false 868 894 in 869 895 let is_generator = is_generator || (current_token parser = Token.Star && (advance parser; true)) in 870 - (* Check for getter/setter *) 896 + (* Check for getter/setter - only if next token is on same line *) 871 897 let kind = 872 898 match current_token parser with 873 899 | Token.Identifier "get" -> 874 900 let next = Lexer.peek parser.lexer in 875 - if next.tok <> Token.LParen then begin 901 + (* get is accessor prefix only if followed by name on same line *) 902 + if next.tok <> Token.LParen && not next.preceded_by_newline then begin 876 903 advance parser; 877 904 Ast.Get_method 878 905 end else Ast.Method 879 906 | Token.Identifier "set" -> 880 907 let next = Lexer.peek parser.lexer in 881 - if next.tok <> Token.LParen then begin 908 + (* set is accessor prefix only if followed by name on same line *) 909 + if next.tok <> Token.LParen && not next.preceded_by_newline then begin 882 910 advance parser; 883 911 Ast.Set_method 884 912 end else Ast.Method ··· 889 917 let key = 890 918 if computed then begin 891 919 advance parser; 920 + (* Inside [], 'in' is allowed as a binary operator *) 921 + let saved_allow_in = parser.allow_in in 922 + parser.allow_in <- true; 892 923 let k = parse_assignment_expression parser in 924 + parser.allow_in <- saved_allow_in; 893 925 expect parser Token.RBracket; 894 926 k 895 927 end else begin ··· 1003 1035 1004 1036 | Token.LBracket -> 1005 1037 advance parser; 1038 + (* Inside [], 'in' is allowed as a binary operator *) 1039 + let saved_allow_in = parser.allow_in in 1040 + parser.allow_in <- true; 1006 1041 let property = parse_expression parser in 1042 + parser.allow_in <- saved_allow_in; 1007 1043 let end_loc = current_loc parser in 1008 1044 expect parser Token.RBracket; 1009 1045 let loc = Source.mk_loc ~start:expr.Ast.loc.start ~end_:end_loc.end_ () in ··· 1185 1221 let argument = parse_unary_expression parser in 1186 1222 let loc = Source.mk_loc ~start:start_loc.start ~end_:argument.Ast.loc.end_ () in 1187 1223 Ast.mk_expr ~loc (Ast.Unary { operator = Ast.Delete; argument }) 1188 - | Token.Keyword Token.Kw_await when parser.in_async || (parser.strict_mode && not parser.in_function) -> 1224 + | Token.Keyword Token.Kw_await when parser.in_async || (parser.in_module && not parser.in_function) -> 1189 1225 (* await is allowed in async functions and at module top level (top-level await in modules) *) 1190 1226 (* In scripts at top level, 'await' is an identifier, not an await expression *) 1191 1227 advance parser; ··· 1283 1319 let test = parse_binary_expression parser 0 in 1284 1320 if current_token parser = Token.Question then begin 1285 1321 advance parser; 1322 + (* Per spec: ConditionalExpression ? AssignmentExpression[+In] : AssignmentExpression[?In] 1323 + The consequent (between ? and :) always allows 'in' operator *) 1324 + let saved_allow_in = parser.allow_in in 1325 + parser.allow_in <- true; 1286 1326 let consequent = parse_assignment_expression parser in 1327 + parser.allow_in <- saved_allow_in; 1287 1328 expect parser Token.Colon; 1288 1329 let alternate = parse_assignment_expression parser in 1289 1330 let loc = Source.mk_loc ~start:test.Ast.loc.start ~end_:alternate.Ast.loc.end_ () in ··· 1652 1693 advance parser; 1653 1694 Some (Ast.For_init_var (parse_variable_declaration parser Ast.Var)) 1654 1695 | Token.Keyword Token.Kw_let -> 1655 - advance parser; 1656 - Some (Ast.For_init_var (parse_variable_declaration parser Ast.Let)) 1696 + (* In non-strict mode, 'let' is only a keyword if followed by [ or binding name 1697 + Otherwise it's an identifier expression *) 1698 + let next = Lexer.peek parser.lexer in 1699 + let is_binding_start = match next.tok with 1700 + | Token.LBracket | Token.LBrace -> true 1701 + | Token.Identifier _ -> true 1702 + (* Keywords that can be binding names in non-strict mode *) 1703 + | Token.Keyword kw when is_strict_reserved kw -> true 1704 + | Token.Keyword Token.Kw_yield -> not parser.in_generator 1705 + | Token.Keyword Token.Kw_await -> not parser.in_async && not parser.in_module 1706 + | _ -> false 1707 + in 1708 + if parser.strict_mode || is_binding_start then begin 1709 + advance parser; 1710 + Some (Ast.For_init_var (parse_variable_declaration parser Ast.Let)) 1711 + end else 1712 + (* 'let' is an identifier in non-strict mode *) 1713 + Some (Ast.For_init_expr (parse_expr_no_in ())) 1657 1714 | Token.Keyword Token.Kw_const -> 1658 1715 advance parser; 1659 1716 Some (Ast.For_init_var (parse_variable_declaration parser Ast.Const)) ··· 1805 1862 Ast.mk_stmt ~loc:decl.var_loc (Ast.Variable decl) 1806 1863 1807 1864 | Token.Keyword Token.Kw_let -> 1808 - advance parser; 1809 - let decl = parse_variable_declaration parser Ast.Let in 1810 - expect_semicolon parser; 1811 - Ast.mk_stmt ~loc:decl.var_loc (Ast.Variable decl) 1865 + (* In non-strict mode, 'let' followed by [ or binding name is a declaration 1866 + Otherwise 'let' might be an identifier expression statement *) 1867 + let next = Lexer.peek parser.lexer in 1868 + let is_binding_start = match next.tok with 1869 + | Token.LBracket | Token.LBrace -> true 1870 + | Token.Identifier _ -> true 1871 + (* Keywords that can be binding names in non-strict mode *) 1872 + | Token.Keyword kw when is_strict_reserved kw -> true 1873 + | Token.Keyword Token.Kw_yield -> not parser.in_generator 1874 + | Token.Keyword Token.Kw_await -> not parser.in_async && not parser.in_module 1875 + | _ -> false 1876 + in 1877 + if parser.strict_mode || is_binding_start then begin 1878 + advance parser; 1879 + let decl = parse_variable_declaration parser Ast.Let in 1880 + expect_semicolon parser; 1881 + Ast.mk_stmt ~loc:decl.var_loc (Ast.Variable decl) 1882 + end else begin 1883 + (* 'let' as identifier in non-strict mode *) 1884 + let expr = parse_expression parser in 1885 + expect_semicolon parser; 1886 + Ast.mk_stmt ~loc:expr.Ast.loc (Ast.Expression expr) 1887 + end 1812 1888 1813 1889 | Token.Keyword Token.Kw_const -> 1814 1890 advance parser; ··· 2401 2477 let directives = ref [] in 2402 2478 let parsing_directives = ref true in 2403 2479 let detected_module = ref is_module in 2404 - (* Modules are always in strict mode *) 2405 - if is_module then parser.strict_mode <- true; 2480 + (* Modules are always in strict mode and have await reserved *) 2481 + if is_module then begin 2482 + parser.strict_mode <- true; 2483 + parser.in_module <- true 2484 + end; 2406 2485 while not (is_at_end parser) do 2407 2486 let item = 2408 2487 match current_token parser with ··· 2417 2496 end else begin 2418 2497 detected_module := true; 2419 2498 parser.strict_mode <- true; (* Modules are always strict mode *) 2499 + parser.in_module <- true; 2420 2500 parsing_directives := false; 2421 2501 Ast.Module_decl (parse_import_declaration parser) 2422 2502 end 2423 2503 | Token.Keyword Token.Kw_export -> 2424 2504 detected_module := true; 2425 2505 parser.strict_mode <- true; (* Modules are always strict mode *) 2506 + parser.in_module <- true; 2426 2507 parsing_directives := false; 2427 2508 Ast.Module_decl (parse_export_declaration parser) 2428 2509 | _ ->