@tishlang/tish-format 1.0.12 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/Cargo.toml +51 -0
  2. package/LICENSE +13 -0
  3. package/bin/tish-format +0 -0
  4. package/crates/js_to_tish/Cargo.toml +11 -0
  5. package/crates/js_to_tish/README.md +18 -0
  6. package/crates/js_to_tish/src/error.rs +55 -0
  7. package/crates/js_to_tish/src/lib.rs +11 -0
  8. package/crates/js_to_tish/src/span_util.rs +35 -0
  9. package/crates/js_to_tish/src/transform/expr.rs +611 -0
  10. package/crates/js_to_tish/src/transform/stmt.rs +503 -0
  11. package/crates/js_to_tish/src/transform.rs +60 -0
  12. package/crates/tish/Cargo.toml +62 -0
  13. package/crates/tish/build.rs +21 -0
  14. package/crates/tish/src/cargo_native_registry.rs +32 -0
  15. package/crates/tish/src/cli_help.rs +576 -0
  16. package/crates/tish/src/main.rs +853 -0
  17. package/crates/tish/src/repl_completion.rs +199 -0
  18. package/crates/tish/tests/cargo_example_compile.rs +67 -0
  19. package/crates/tish/tests/error_source_location.rs +36 -0
  20. package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
  21. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
  22. package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
  23. package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
  24. package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -0
  25. package/crates/tish/tests/fixtures/runtime_error_location.tish +5 -0
  26. package/crates/tish/tests/fixtures/trycatch_runtime_errors.tish +15 -0
  27. package/crates/tish/tests/fixtures/tty_capability.tish +9 -0
  28. package/crates/tish/tests/integration_test.rs +1406 -0
  29. package/crates/tish/tests/run_optimize_stdout_parity.rs +50 -0
  30. package/crates/tish/tests/shortcircuit.rs +65 -0
  31. package/crates/tish/tests/trycatch_runtime_errors.rs +45 -0
  32. package/crates/tish/tests/tty_capability.rs +43 -0
  33. package/crates/tish_ast/Cargo.toml +9 -0
  34. package/crates/tish_ast/src/ast.rs +649 -0
  35. package/crates/tish_ast/src/lib.rs +5 -0
  36. package/crates/tish_build_utils/Cargo.toml +11 -0
  37. package/crates/tish_build_utils/src/lib.rs +577 -0
  38. package/crates/tish_builtins/Cargo.toml +22 -0
  39. package/crates/tish_builtins/src/array.rs +803 -0
  40. package/crates/tish_builtins/src/collections.rs +481 -0
  41. package/crates/tish_builtins/src/construct.rs +199 -0
  42. package/crates/tish_builtins/src/date.rs +538 -0
  43. package/crates/tish_builtins/src/globals.rs +293 -0
  44. package/crates/tish_builtins/src/helpers.rs +35 -0
  45. package/crates/tish_builtins/src/iterator.rs +129 -0
  46. package/crates/tish_builtins/src/lib.rs +21 -0
  47. package/crates/tish_builtins/src/math.rs +89 -0
  48. package/crates/tish_builtins/src/number.rs +96 -0
  49. package/crates/tish_builtins/src/object.rs +36 -0
  50. package/crates/tish_builtins/src/string.rs +646 -0
  51. package/crates/tish_builtins/src/symbol.rs +83 -0
  52. package/crates/tish_builtins/src/typedarrays.rs +298 -0
  53. package/crates/tish_bytecode/Cargo.toml +17 -0
  54. package/crates/tish_bytecode/src/chunk.rs +164 -0
  55. package/crates/tish_bytecode/src/compiler.rs +2604 -0
  56. package/crates/tish_bytecode/src/encoding.rs +102 -0
  57. package/crates/tish_bytecode/src/lib.rs +20 -0
  58. package/crates/tish_bytecode/src/opcode.rs +185 -0
  59. package/crates/tish_bytecode/src/peephole.rs +189 -0
  60. package/crates/tish_bytecode/src/serialize.rs +193 -0
  61. package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
  62. package/crates/tish_bytecode/tests/constant_folding.rs +84 -0
  63. package/crates/tish_bytecode/tests/sort_optimization.rs +31 -0
  64. package/crates/tish_compile/Cargo.toml +27 -0
  65. package/crates/tish_compile/src/check.rs +774 -0
  66. package/crates/tish_compile/src/codegen.rs +7317 -0
  67. package/crates/tish_compile/src/infer.rs +1681 -0
  68. package/crates/tish_compile/src/lib.rs +206 -0
  69. package/crates/tish_compile/src/resolve.rs +1951 -0
  70. package/crates/tish_compile/src/types.rs +605 -0
  71. package/crates/tish_compile_js/Cargo.toml +18 -0
  72. package/crates/tish_compile_js/examples/jsx_vdom_smoke.tish +8 -0
  73. package/crates/tish_compile_js/src/codegen.rs +938 -0
  74. package/crates/tish_compile_js/src/error.rs +20 -0
  75. package/crates/tish_compile_js/src/lib.rs +26 -0
  76. package/crates/tish_compile_js/src/tests_jsx.rs +414 -0
  77. package/crates/tish_compiler_wasm/Cargo.toml +21 -0
  78. package/crates/tish_compiler_wasm/src/lib.rs +57 -0
  79. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +473 -0
  80. package/crates/tish_core/Cargo.toml +32 -0
  81. package/crates/tish_core/src/console_style.rs +170 -0
  82. package/crates/tish_core/src/json.rs +430 -0
  83. package/crates/tish_core/src/lib.rs +20 -0
  84. package/crates/tish_core/src/macros.rs +36 -0
  85. package/crates/tish_core/src/shape.rs +85 -0
  86. package/crates/tish_core/src/uri.rs +118 -0
  87. package/crates/tish_core/src/value.rs +1350 -0
  88. package/crates/tish_core/src/vmref.rs +183 -0
  89. package/crates/tish_cranelift/Cargo.toml +19 -0
  90. package/crates/tish_cranelift/src/lib.rs +43 -0
  91. package/crates/tish_cranelift/src/link.rs +130 -0
  92. package/crates/tish_cranelift/src/lower.rs +85 -0
  93. package/crates/tish_cranelift_runtime/Cargo.toml +26 -0
  94. package/crates/tish_cranelift_runtime/src/lib.rs +45 -0
  95. package/crates/tish_eval/Cargo.toml +51 -0
  96. package/crates/tish_eval/src/eval.rs +4265 -0
  97. package/crates/tish_eval/src/http.rs +191 -0
  98. package/crates/tish_eval/src/lib.rs +99 -0
  99. package/crates/tish_eval/src/natives.rs +551 -0
  100. package/crates/tish_eval/src/promise.rs +179 -0
  101. package/crates/tish_eval/src/regex.rs +299 -0
  102. package/crates/tish_eval/src/timers.rs +120 -0
  103. package/crates/tish_eval/src/value.rs +336 -0
  104. package/crates/tish_eval/src/value_convert.rs +117 -0
  105. package/crates/tish_ffi/Cargo.toml +26 -0
  106. package/crates/tish_ffi/src/lib.rs +518 -0
  107. package/crates/tish_ffi/tests/fixtures/testmod/Cargo.toml +18 -0
  108. package/crates/tish_ffi/tests/fixtures/testmod/src/lib.rs +46 -0
  109. package/crates/tish_ffi/tests/loader.rs +65 -0
  110. package/crates/tish_fmt/Cargo.toml +16 -0
  111. package/crates/tish_fmt/src/bin/tish-fmt.rs +41 -0
  112. package/crates/tish_fmt/src/lib.rs +2157 -0
  113. package/crates/tish_jsx_web/Cargo.toml +9 -0
  114. package/crates/tish_jsx_web/README.md +5 -0
  115. package/crates/tish_jsx_web/src/lib.rs +2 -0
  116. package/crates/tish_lexer/Cargo.toml +9 -0
  117. package/crates/tish_lexer/src/lib.rs +1104 -0
  118. package/crates/tish_lexer/src/token.rs +170 -0
  119. package/crates/tish_lint/Cargo.toml +18 -0
  120. package/crates/tish_lint/src/bin/tish-lint.rs +195 -0
  121. package/crates/tish_lint/src/lib.rs +281 -0
  122. package/crates/tish_llvm/Cargo.toml +13 -0
  123. package/crates/tish_llvm/src/lib.rs +115 -0
  124. package/crates/tish_lsp/Cargo.toml +25 -0
  125. package/crates/tish_lsp/README.md +26 -0
  126. package/crates/tish_lsp/src/builtin_goto.rs +362 -0
  127. package/crates/tish_lsp/src/import_goto.rs +564 -0
  128. package/crates/tish_lsp/src/main.rs +1459 -0
  129. package/crates/tish_native/Cargo.toml +16 -0
  130. package/crates/tish_native/src/build.rs +481 -0
  131. package/crates/tish_native/src/config.rs +48 -0
  132. package/crates/tish_native/src/lib.rs +416 -0
  133. package/crates/tish_opt/Cargo.toml +13 -0
  134. package/crates/tish_opt/src/lib.rs +1046 -0
  135. package/crates/tish_parser/Cargo.toml +11 -0
  136. package/crates/tish_parser/src/lib.rs +386 -0
  137. package/crates/tish_parser/src/parser.rs +2726 -0
  138. package/crates/tish_pg/Cargo.toml +34 -0
  139. package/crates/tish_pg/README.md +38 -0
  140. package/crates/tish_pg/src/error.rs +52 -0
  141. package/crates/tish_pg/src/lib.rs +955 -0
  142. package/crates/tish_resolve/Cargo.toml +13 -0
  143. package/crates/tish_resolve/src/lib.rs +3601 -0
  144. package/crates/tish_resolve/src/pos.rs +141 -0
  145. package/crates/tish_runtime/Cargo.toml +100 -0
  146. package/crates/tish_runtime/src/http.rs +1347 -0
  147. package/crates/tish_runtime/src/http_fetch.rs +492 -0
  148. package/crates/tish_runtime/src/http_hyper.rs +441 -0
  149. package/crates/tish_runtime/src/http_prefork.rs +189 -0
  150. package/crates/tish_runtime/src/lib.rs +1447 -0
  151. package/crates/tish_runtime/src/native_promise.rs +15 -0
  152. package/crates/tish_runtime/src/promise.rs +558 -0
  153. package/crates/tish_runtime/src/promise_io.rs +38 -0
  154. package/crates/tish_runtime/src/timers.rs +172 -0
  155. package/crates/tish_runtime/src/tty.rs +226 -0
  156. package/crates/tish_runtime/src/ws.rs +778 -0
  157. package/crates/tish_runtime/tests/fetch_readable_stream.rs +102 -0
  158. package/crates/tish_ui/Cargo.toml +17 -0
  159. package/crates/tish_ui/src/jsx.rs +692 -0
  160. package/crates/tish_ui/src/lib.rs +20 -0
  161. package/crates/tish_ui/src/runtime/hooks.rs +573 -0
  162. package/crates/tish_ui/src/runtime/mod.rs +183 -0
  163. package/crates/tish_vm/Cargo.toml +60 -0
  164. package/crates/tish_vm/src/jit.rs +1050 -0
  165. package/crates/tish_vm/src/lib.rs +41 -0
  166. package/crates/tish_vm/src/vm.rs +3536 -0
  167. package/crates/tish_vm/tests/concurrent_shared_state.rs +140 -0
  168. package/crates/tish_vm/tests/fixtures/or_string_cmd.tish +2 -0
  169. package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -0
  170. package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +150 -0
  171. package/crates/tish_wasm/Cargo.toml +15 -0
  172. package/crates/tish_wasm/src/lib.rs +428 -0
  173. package/crates/tish_wasm_runtime/Cargo.toml +37 -0
  174. package/crates/tish_wasm_runtime/src/gpu.rs +429 -0
  175. package/crates/tish_wasm_runtime/src/lib.rs +42 -0
  176. package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
  177. package/crates/tishlang_cargo_bindgen/src/classify.rs +261 -0
  178. package/crates/tishlang_cargo_bindgen/src/discover.rs +125 -0
  179. package/crates/tishlang_cargo_bindgen/src/infer.rs +382 -0
  180. package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
  181. package/crates/tishlang_cargo_bindgen/src/main.rs +167 -0
  182. package/crates/tishlang_cargo_bindgen/src/metadata.rs +117 -0
  183. package/justfile +276 -0
  184. package/package.json +2 -2
  185. package/platform/darwin-arm64/tish-fmt +0 -0
  186. package/platform/darwin-x64/tish-fmt +0 -0
  187. package/platform/linux-arm64/tish-fmt +0 -0
  188. package/platform/linux-x64/tish-fmt +0 -0
  189. package/platform/win32-x64/tish-fmt.exe +0 -0
@@ -0,0 +1,2726 @@
1
+ //! Recursive descent parser for Tish.
2
+
3
+ use std::collections::{HashMap, HashSet};
4
+ use std::sync::Arc;
5
+
6
+ /// Macro to generate single-operator binary parsing functions.
7
+ /// Reduces boilerplate for Or, And, BitOr, BitXor, BitAnd parsers.
8
+ macro_rules! binary_single_op {
9
+ ($name:ident, $next:ident, $token:ident, $op:expr) => {
10
+ fn $name(&mut self) -> Result<Expr, String> {
11
+ let mut left = self.$next()?;
12
+ while matches!(self.peek_kind(), Some(TokenKind::$token)) {
13
+ self.advance();
14
+ let right = self.$next()?;
15
+ let start = expr_span(&left).start;
16
+ let end = expr_span(&right).end;
17
+ left = Expr::Binary {
18
+ left: Box::new(left),
19
+ op: $op,
20
+ right: Box::new(right),
21
+ span: Span { start, end },
22
+ };
23
+ }
24
+ Ok(left)
25
+ }
26
+ };
27
+ }
28
+
29
+ /// Macro for multi-operator binary parsing with a match block.
30
+ macro_rules! binary_multi_op {
31
+ ($name:ident, $next:ident, $( $token:ident => $op:expr ),+ $(,)?) => {
32
+ fn $name(&mut self) -> Result<Expr, String> {
33
+ let mut left = self.$next()?;
34
+ loop {
35
+ let op = match self.peek_kind() {
36
+ $( Some(TokenKind::$token) => $op, )+
37
+ _ => break,
38
+ };
39
+ self.advance();
40
+ let right = self.$next()?;
41
+ let start = expr_span(&left).start;
42
+ let end = expr_span(&right).end;
43
+ left = Expr::Binary {
44
+ left: Box::new(left),
45
+ op,
46
+ right: Box::new(right),
47
+ span: Span { start, end },
48
+ };
49
+ }
50
+ Ok(left)
51
+ }
52
+ };
53
+ }
54
+
55
+ use tishlang_ast::{
56
+ ArrayElement, ArrowBody, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern,
57
+ DestructProp, ExportDeclaration, Expr, FunParam, ImportSpecifier, JsxAttrValue, JsxChild,
58
+ JsxProp, Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Span, Statement,
59
+ TypeAnnotation, TypeLiteral, TypedParam, UnaryOp,
60
+ };
61
+ use tishlang_lexer::{Token, TokenKind};
62
+
63
+ /// Mangle a generic instantiation `base<args>` into a Rust-ident-safe alias name (`Box__number`).
64
+ fn mangle_generic(base: &str, args: &[TypeAnnotation]) -> String {
65
+ let parts: Vec<String> = args.iter().map(mangle_type).collect();
66
+ format!("{}__{}", base, parts.join("_"))
67
+ }
68
+
69
+ fn mangle_type(t: &TypeAnnotation) -> String {
70
+ match t {
71
+ TypeAnnotation::Simple(s) => s.chars().map(|c| if c.is_alphanumeric() { c } else { '_' }).collect(),
72
+ TypeAnnotation::Array(inner) => format!("{}Arr", mangle_type(inner)),
73
+ TypeAnnotation::Tuple(es) => {
74
+ format!("Tup{}", es.iter().map(mangle_type).collect::<Vec<_>>().join(""))
75
+ }
76
+ TypeAnnotation::Object(_) => "Obj".to_string(),
77
+ TypeAnnotation::Union(_) => "Un".to_string(),
78
+ TypeAnnotation::Intersection(_) => "Is".to_string(),
79
+ TypeAnnotation::Function { .. } => "Fn".to_string(),
80
+ TypeAnnotation::Literal(_) => "Lit".to_string(),
81
+ }
82
+ }
83
+
84
+ /// Substitute generic type parameters with their concrete arguments throughout a type body.
85
+ fn subst_type(t: &TypeAnnotation, map: &HashMap<&str, &TypeAnnotation>) -> TypeAnnotation {
86
+ match t {
87
+ TypeAnnotation::Simple(s) => map
88
+ .get(s.as_ref())
89
+ .map(|rep| (*rep).clone())
90
+ .unwrap_or_else(|| t.clone()),
91
+ TypeAnnotation::Array(inner) => TypeAnnotation::Array(Box::new(subst_type(inner, map))),
92
+ TypeAnnotation::Object(fields) => TypeAnnotation::Object(
93
+ fields.iter().map(|(k, v)| (k.clone(), subst_type(v, map))).collect(),
94
+ ),
95
+ TypeAnnotation::Tuple(es) => {
96
+ TypeAnnotation::Tuple(es.iter().map(|e| subst_type(e, map)).collect())
97
+ }
98
+ TypeAnnotation::Union(es) => {
99
+ TypeAnnotation::Union(es.iter().map(|e| subst_type(e, map)).collect())
100
+ }
101
+ TypeAnnotation::Intersection(es) => {
102
+ TypeAnnotation::Intersection(es.iter().map(|e| subst_type(e, map)).collect())
103
+ }
104
+ TypeAnnotation::Function { params, returns } => TypeAnnotation::Function {
105
+ params: params.iter().map(|p| subst_type(p, map)).collect(),
106
+ returns: Box::new(subst_type(returns, map)),
107
+ },
108
+ TypeAnnotation::Literal(_) => t.clone(),
109
+ }
110
+ }
111
+
112
+ pub struct Parser<'a> {
113
+ tokens: &'a [Token],
114
+ pos: usize,
115
+ /// Outstanding `>` owed to enclosing generic-arg lists after a `>>` (`Shr`) token was split,
116
+ /// so nested `Array<Array<T>>` closes correctly (tish lexes `>>` as one token).
117
+ gt_debt: u32,
118
+ /// Generic `type`/`interface` decls (`type Box<T> = …`) → (type-param names, body), for
119
+ /// monomorphizing a reference `Box<number>` into a concrete native struct.
120
+ generic_aliases: HashMap<String, (Vec<Arc<str>>, TypeAnnotation)>,
121
+ /// Synthetic specialized aliases (e.g. `type Box__number = { value: number }`) generated for
122
+ /// each distinct `Generic<Args>` reference; appended to the program at the end.
123
+ generic_specializations: Vec<Statement>,
124
+ /// Names of specializations already generated (dedup).
125
+ generic_done: HashSet<String>,
126
+ }
127
+
128
+ impl<'a> Parser<'a> {
129
+ pub fn new(tokens: &'a [Token]) -> Self {
130
+ Self {
131
+ tokens,
132
+ pos: 0,
133
+ gt_debt: 0,
134
+ generic_aliases: HashMap::new(),
135
+ generic_specializations: Vec::new(),
136
+ generic_done: HashSet::new(),
137
+ }
138
+ }
139
+
140
+ /// Close one generic-arg `>`: use a previously-split `>>` debt, consume a `>`, or split a
141
+ /// `>>` (consuming it and owing one `>` to the parent). Returns false if there's no closer.
142
+ fn try_close_angle(&mut self) -> bool {
143
+ if self.gt_debt > 0 {
144
+ self.gt_debt -= 1;
145
+ return true;
146
+ }
147
+ match self.peek_kind() {
148
+ Some(TokenKind::Gt) => {
149
+ self.advance();
150
+ true
151
+ }
152
+ Some(TokenKind::Shr) => {
153
+ self.advance();
154
+ self.gt_debt += 1;
155
+ true
156
+ }
157
+ Some(TokenKind::UShr) => {
158
+ // `>>>` closing three generic args, e.g. `Foo<Bar<Baz<T>>>`.
159
+ self.advance();
160
+ self.gt_debt += 2;
161
+ true
162
+ }
163
+ _ => false,
164
+ }
165
+ }
166
+
167
+ fn peek(&self) -> Option<&Token> {
168
+ self.tokens.get(self.pos)
169
+ }
170
+
171
+ fn peek_kind(&self) -> Option<TokenKind> {
172
+ self.peek().map(|t| t.kind)
173
+ }
174
+
175
+ fn advance(&mut self) -> Option<&Token> {
176
+ let t = self.tokens.get(self.pos);
177
+ if t.is_some() {
178
+ self.pos += 1;
179
+ }
180
+ t
181
+ }
182
+
183
+ fn expect(&mut self, kind: TokenKind) -> Result<&Token, String> {
184
+ let t = self
185
+ .advance()
186
+ .ok_or_else(|| format!("Expected {:?}, got EOF", kind))?;
187
+ if t.kind == kind {
188
+ Ok(t)
189
+ } else {
190
+ Err(format!(
191
+ "Expected {:?}, got {:?} at {:?}",
192
+ kind, t.kind, t.span
193
+ ))
194
+ }
195
+ }
196
+
197
+ /// After `.` / `?.`, allow contextual keywords as member names. In JS any `IdentifierName`
198
+ /// (including reserved words) is a valid property name — `arr.of`, `x.as`, `o.in`, `o.type` —
199
+ /// but the lexer emits dedicated keyword tokens for these, so they must be accepted explicitly
200
+ /// here. (`TypedArray.of` is the motivating case.) See `docs/js-emit-philosophy.md`.
201
+ fn expect_ident_or_type_member_name(&mut self) -> Result<&Token, String> {
202
+ match self.peek_kind() {
203
+ Some(TokenKind::Ident) => self.expect(TokenKind::Ident),
204
+ Some(TokenKind::Type) => self.expect(TokenKind::Type),
205
+ Some(TokenKind::Of) => self.expect(TokenKind::Of),
206
+ Some(TokenKind::As) => self.expect(TokenKind::As),
207
+ Some(TokenKind::In) => self.expect(TokenKind::In),
208
+ // `delete` is a keyword (the operator) but also a valid property name —
209
+ // `Set`/`Map` expose `.delete(...)`. Issue #40.
210
+ Some(TokenKind::Delete) => self.expect(TokenKind::Delete),
211
+ other => Err(format!(
212
+ "Expected property name after `.` or `?.`, got {:?}",
213
+ other
214
+ )),
215
+ }
216
+ }
217
+
218
+ /// Accept a plain identifier, or the contextual keywords `fn` / `type` used as an
219
+ /// identifier. JS treats `fn` as an ordinary identifier and `type` as only contextually
220
+ /// reserved, so both are valid binding / parameter names and value references (issue #55).
221
+ /// All three carry their spelling in `.literal`. Statement-leading `fn` / `type`
222
+ /// (function declarations / type aliases) are matched earlier in `parse_statement`, so this
223
+ /// only fires in identifier positions.
224
+ fn expect_identifier_name(&mut self) -> Result<&Token, String> {
225
+ let t = self
226
+ .advance()
227
+ .ok_or_else(|| "Expected identifier, got EOF".to_string())?;
228
+ if matches!(
229
+ t.kind,
230
+ TokenKind::Ident | TokenKind::Fn | TokenKind::Type
231
+ ) {
232
+ Ok(t)
233
+ } else {
234
+ Err(format!("Expected Ident, got {:?} at {:?}", t.kind, t.span))
235
+ }
236
+ }
237
+
238
+ fn span_end(&self, start: (usize, usize)) -> Span {
239
+ let end = self.peek().map(|t| t.span.start).unwrap_or(start);
240
+ Span { start, end }
241
+ }
242
+
243
+ pub fn parse_program(&mut self) -> Result<Program, String> {
244
+ let mut statements = Vec::with_capacity(8);
245
+ while self.peek_kind().is_some() {
246
+ if matches!(self.peek_kind(), Some(TokenKind::Dedent)) {
247
+ self.advance();
248
+ continue;
249
+ }
250
+ statements.push(self.parse_statement()?);
251
+ }
252
+ // Prepend the synthetic monomorphized aliases (`type Box__number = …`) so they're declared
253
+ // before any use, for alias resolution + native struct emission.
254
+ if !self.generic_specializations.is_empty() {
255
+ let mut out = std::mem::take(&mut self.generic_specializations);
256
+ out.append(&mut statements);
257
+ statements = out;
258
+ }
259
+ Ok(Program { statements })
260
+ }
261
+
262
+ fn parse_statement(&mut self) -> Result<Statement, String> {
263
+ let kind = self.peek_kind().ok_or("Unexpected EOF")?;
264
+ let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
265
+
266
+ let stmt = match kind {
267
+ TokenKind::LBrace | TokenKind::Indent => self.parse_block()?,
268
+ TokenKind::Let => self.parse_var_decl(true)?,
269
+ TokenKind::Const => self.parse_var_decl(false)?,
270
+ TokenKind::Async => {
271
+ self.advance(); // consume 'async'
272
+ self.parse_fun_decl(true)? // parse_fun_decl expects 'fn' next
273
+ }
274
+ TokenKind::Fn => self.parse_fun_decl(false)?,
275
+ TokenKind::If => self.parse_if()?,
276
+ TokenKind::While => self.parse_while()?,
277
+ TokenKind::For => self.parse_for()?,
278
+ TokenKind::Return => self.parse_return()?,
279
+ TokenKind::Switch => self.parse_switch()?,
280
+ TokenKind::Do => self.parse_do_while()?,
281
+ TokenKind::Throw => self.parse_throw()?,
282
+ TokenKind::Try => self.parse_try()?,
283
+ TokenKind::Break => {
284
+ self.advance();
285
+ let span_end = self.peek().map(|t| t.span.end).unwrap_or(span_start);
286
+ Statement::Break {
287
+ span: Span {
288
+ start: span_start,
289
+ end: span_end,
290
+ },
291
+ }
292
+ }
293
+ TokenKind::Continue => {
294
+ self.advance();
295
+ let span_end = self.peek().map(|t| t.span.end).unwrap_or(span_start);
296
+ Statement::Continue {
297
+ span: Span {
298
+ start: span_start,
299
+ end: span_end,
300
+ },
301
+ }
302
+ }
303
+ TokenKind::Import => self.parse_import()?,
304
+ TokenKind::Export => self.parse_export()?,
305
+ TokenKind::Type => self.parse_type_alias()?,
306
+ TokenKind::Declare => self.parse_declare()?,
307
+ TokenKind::Interface => self.parse_interface()?,
308
+ _ => {
309
+ let expr = self.parse_expr()?;
310
+ let span_end = expr.span().end;
311
+ Statement::ExprStmt {
312
+ expr,
313
+ span: Span {
314
+ start: span_start,
315
+ end: span_end,
316
+ },
317
+ }
318
+ }
319
+ };
320
+
321
+ // Optional semicolon
322
+ if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
323
+ self.advance();
324
+ }
325
+
326
+ Ok(stmt)
327
+ }
328
+
329
+ fn parse_block_or_statement(&mut self) -> Result<Statement, String> {
330
+ if matches!(self.peek_kind(), Some(TokenKind::LBrace))
331
+ || matches!(self.peek_kind(), Some(TokenKind::Indent))
332
+ {
333
+ return self.parse_block();
334
+ }
335
+ self.parse_statement()
336
+ }
337
+
338
+ fn parse_block(&mut self) -> Result<Statement, String> {
339
+ let span_start = self.peek().ok_or("Unexpected EOF")?.span.start;
340
+
341
+ let opened_with_brace = matches!(self.peek_kind(), Some(TokenKind::LBrace));
342
+ if opened_with_brace {
343
+ self.advance(); // {
344
+ // After `{`, the lexer often emits `Indent` for the first indented line of the body.
345
+ // `parse_statement` treats a leading `Indent` as starting a *nested* indent-block, so
346
+ // without consuming this token we get `Block { Block { let ... } ; ... }` and the first
347
+ // `let`/`const` is scoped too narrowly (JS ReferenceError). This indent is layout for
348
+ // *this* brace block, not an inner block.
349
+ if matches!(self.peek_kind(), Some(TokenKind::Indent)) {
350
+ self.advance();
351
+ }
352
+ } else if matches!(self.peek_kind(), Some(TokenKind::Indent)) {
353
+ self.advance(); // Indent
354
+ }
355
+
356
+ let mut statements = Vec::with_capacity(4);
357
+ loop {
358
+ if matches!(self.peek_kind(), Some(TokenKind::RBrace))
359
+ || matches!(self.peek_kind(), Some(TokenKind::Dedent))
360
+ || self.peek_kind().is_none()
361
+ {
362
+ break;
363
+ }
364
+ statements.push(self.parse_statement()?);
365
+ }
366
+
367
+ if matches!(
368
+ self.peek_kind(),
369
+ Some(TokenKind::RBrace | TokenKind::Dedent)
370
+ ) {
371
+ self.advance();
372
+ }
373
+
374
+ let peek_end = self.peek().map(|x| x.span.end);
375
+ let last_end = statements.last().map(|s| s.span().end);
376
+ let end = match (peek_end, last_end) {
377
+ (Some(p), Some(l)) => {
378
+ if p.0 > l.0 || (p.0 == l.0 && p.1 > l.1) {
379
+ p
380
+ } else {
381
+ l
382
+ }
383
+ }
384
+ (Some(p), None) => p,
385
+ (None, Some(l)) => l,
386
+ (None, None) => span_start,
387
+ };
388
+
389
+ Ok(Statement::Block {
390
+ statements,
391
+ span: Span {
392
+ start: span_start,
393
+ end,
394
+ },
395
+ })
396
+ }
397
+
398
+ fn parse_var_decl(&mut self, mutable: bool) -> Result<Statement, String> {
399
+ let span_start = if mutable {
400
+ self.expect(TokenKind::Let)?.span.start
401
+ } else {
402
+ self.expect(TokenKind::Const)?.span.start
403
+ };
404
+
405
+ // First declarator keeps the `let`/`const`-anchored span (single-decl is
406
+ // the overwhelmingly common case and stays byte-identical to before).
407
+ let first = self.parse_one_declarator(mutable, span_start)?;
408
+ if !matches!(self.peek_kind(), Some(TokenKind::Comma)) {
409
+ return Ok(first);
410
+ }
411
+
412
+ // Comma-separated declarators: `let a = 1, b = 2, c`. Each lowers to its
413
+ // own VarDecl inside a transparent `Multi` group (no new scope), so it
414
+ // composes anywhere a single statement is expected (incl. `for` init).
415
+ let mut statements = vec![first];
416
+ while matches!(self.peek_kind(), Some(TokenKind::Comma)) {
417
+ self.advance(); // consume ','
418
+ let decl_start = self.peek().map(|t| t.span.start).unwrap_or(span_start);
419
+ statements.push(self.parse_one_declarator(mutable, decl_start)?);
420
+ }
421
+ Ok(Statement::Multi {
422
+ statements,
423
+ span: self.span_end(span_start),
424
+ })
425
+ }
426
+
427
+ /// Parse one declarator — `ident[: Type] [= init]` or `pattern = init` — into
428
+ /// its own statement. The leading `let`/`const` keyword is consumed by the
429
+ /// caller; `span_start` anchors this declarator's span.
430
+ fn parse_one_declarator(
431
+ &mut self,
432
+ mutable: bool,
433
+ span_start: (usize, usize),
434
+ ) -> Result<Statement, String> {
435
+ // Destructuring pattern declarator.
436
+ if matches!(
437
+ self.peek_kind(),
438
+ Some(TokenKind::LBracket) | Some(TokenKind::LBrace)
439
+ ) {
440
+ let pattern = self.parse_destruct_pattern()?;
441
+ self.expect(TokenKind::Assign)?;
442
+ let init = self.parse_expr()?;
443
+ return Ok(Statement::VarDeclDestructure {
444
+ pattern,
445
+ mutable,
446
+ init,
447
+ span: self.span_end(span_start),
448
+ });
449
+ }
450
+
451
+ let name_tok = self.expect_identifier_name()?;
452
+ let name_span = Span {
453
+ start: name_tok.span.start,
454
+ end: name_tok.span.end,
455
+ };
456
+ let name = name_tok.literal.clone().ok_or("Expected identifier")?;
457
+
458
+ // Optional type annotation: `: Type`
459
+ let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
460
+ self.advance(); // consume :
461
+ Some(self.parse_type_annotation()?)
462
+ } else {
463
+ None
464
+ };
465
+
466
+ let init = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
467
+ self.advance();
468
+ Some(self.parse_expr()?)
469
+ } else {
470
+ None
471
+ };
472
+ Ok(Statement::VarDecl {
473
+ name,
474
+ name_span,
475
+ mutable,
476
+ type_ann,
477
+ init,
478
+ span: self.span_end(span_start),
479
+ })
480
+ }
481
+
482
+ fn parse_destruct_pattern(&mut self) -> Result<DestructPattern, String> {
483
+ match self.peek_kind() {
484
+ Some(TokenKind::LBracket) => self.parse_array_destruct_pattern(),
485
+ Some(TokenKind::LBrace) => self.parse_object_destruct_pattern(),
486
+ _ => Err("Expected destructuring pattern".to_string()),
487
+ }
488
+ }
489
+
490
+ fn parse_array_destruct_pattern(&mut self) -> Result<DestructPattern, String> {
491
+ self.expect(TokenKind::LBracket)?;
492
+ let mut elements = Vec::new();
493
+
494
+ while !matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
495
+ // Handle holes (elision): [a, , b]
496
+ if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
497
+ elements.push(None);
498
+ self.advance();
499
+ continue;
500
+ }
501
+
502
+ // Rest element: ...rest
503
+ if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
504
+ self.advance();
505
+ let name_tok = self.expect(TokenKind::Ident)?;
506
+ let name_span = Span {
507
+ start: name_tok.span.start,
508
+ end: name_tok.span.end,
509
+ };
510
+ let name = name_tok.literal.clone().ok_or("Expected identifier")?;
511
+ elements.push(Some(DestructElement::Rest(name, name_span)));
512
+ break;
513
+ }
514
+
515
+ // Nested pattern or identifier
516
+ let elem = match self.peek_kind() {
517
+ Some(TokenKind::LBracket) | Some(TokenKind::LBrace) => {
518
+ let nested = self.parse_destruct_pattern()?;
519
+ DestructElement::Pattern(Box::new(nested))
520
+ }
521
+ Some(TokenKind::Ident) => {
522
+ let name_tok = self.advance().ok_or("Unexpected EOF")?;
523
+ let name_span = Span {
524
+ start: name_tok.span.start,
525
+ end: name_tok.span.end,
526
+ };
527
+ let name = name_tok.literal.clone().ok_or("Expected identifier")?;
528
+ DestructElement::Ident(name, name_span)
529
+ }
530
+ _ => return Err("Expected identifier or pattern in destructuring".to_string()),
531
+ };
532
+ elements.push(Some(elem));
533
+
534
+ if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
535
+ self.advance();
536
+ } else {
537
+ break;
538
+ }
539
+ }
540
+
541
+ self.expect(TokenKind::RBracket)?;
542
+ Ok(DestructPattern::Array(elements))
543
+ }
544
+
545
+ fn parse_object_destruct_pattern(&mut self) -> Result<DestructPattern, String> {
546
+ self.expect(TokenKind::LBrace)?;
547
+ let mut props = Vec::new();
548
+
549
+ while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
550
+ let key_tok = self.expect(TokenKind::Ident)?;
551
+ let key_span = Span {
552
+ start: key_tok.span.start,
553
+ end: key_tok.span.end,
554
+ };
555
+ let key = key_tok.literal.clone().ok_or("Expected identifier")?;
556
+
557
+ let value = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
558
+ self.advance();
559
+ // Could be renamed binding or nested pattern
560
+ match self.peek_kind() {
561
+ Some(TokenKind::LBracket) | Some(TokenKind::LBrace) => {
562
+ let nested = self.parse_destruct_pattern()?;
563
+ DestructElement::Pattern(Box::new(nested))
564
+ }
565
+ Some(TokenKind::Ident) => {
566
+ let name_tok = self.advance().ok_or("Unexpected EOF")?;
567
+ let name_span = Span {
568
+ start: name_tok.span.start,
569
+ end: name_tok.span.end,
570
+ };
571
+ let name = name_tok.literal.clone().ok_or("Expected identifier")?;
572
+ DestructElement::Ident(name, name_span)
573
+ }
574
+ _ => return Err("Expected identifier or pattern after ':'".to_string()),
575
+ }
576
+ } else {
577
+ // Shorthand: { key } is equivalent to { key: key }
578
+ DestructElement::Ident(key.clone(), key_span)
579
+ };
580
+
581
+ props.push(DestructProp { key, value });
582
+
583
+ if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
584
+ self.advance();
585
+ } else {
586
+ break;
587
+ }
588
+ }
589
+
590
+ self.expect(TokenKind::RBrace)?;
591
+ Ok(DestructPattern::Object(props))
592
+ }
593
+
594
+ /// One formal parameter: `name`, `name: T`, `name = expr`, or a destructuring pattern.
595
+ fn parse_fun_param(&mut self) -> Result<FunParam, String> {
596
+ if matches!(
597
+ self.peek_kind(),
598
+ Some(TokenKind::LBracket | TokenKind::LBrace)
599
+ ) {
600
+ let pattern = self.parse_destruct_pattern()?;
601
+ let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
602
+ self.advance();
603
+ Some(self.parse_type_annotation()?)
604
+ } else {
605
+ None
606
+ };
607
+ let default = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
608
+ self.advance();
609
+ Some(self.parse_expr()?)
610
+ } else {
611
+ None
612
+ };
613
+ return Ok(FunParam::Destructure {
614
+ pattern,
615
+ type_ann,
616
+ default,
617
+ });
618
+ }
619
+ let param_tok = self.expect_identifier_name()?;
620
+ let name_span = Span {
621
+ start: param_tok.span.start,
622
+ end: param_tok.span.end,
623
+ };
624
+ let param_name = param_tok.literal.clone().ok_or("Expected param name")?;
625
+ let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
626
+ self.advance();
627
+ Some(self.parse_type_annotation()?)
628
+ } else {
629
+ None
630
+ };
631
+ let default = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
632
+ self.advance();
633
+ Some(self.parse_expr()?)
634
+ } else {
635
+ None
636
+ };
637
+ Ok(FunParam::Simple(TypedParam {
638
+ name: param_name,
639
+ name_span,
640
+ type_ann,
641
+ default,
642
+ }))
643
+ }
644
+
645
+ /// Parse a generic type-parameter list `<T, U, …>` on a `fn` / `type` declaration, returning the
646
+ /// parameter names. On functions the names are ignored (generic fns run gradually/boxed); on
647
+ /// `type`/`interface` they drive struct monomorphization (`Box<number>` → a native struct).
648
+ fn parse_type_params(&mut self) -> Result<Vec<Arc<str>>, String> {
649
+ let mut params = Vec::new();
650
+ if matches!(self.peek_kind(), Some(TokenKind::Lt)) {
651
+ self.advance(); // <
652
+ while !matches!(self.peek_kind(), Some(TokenKind::Gt)) {
653
+ let tok = self.expect(TokenKind::Ident)?;
654
+ if let Some(n) = &tok.literal {
655
+ params.push(Arc::from(n.as_ref()));
656
+ }
657
+ if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
658
+ self.advance();
659
+ } else {
660
+ break;
661
+ }
662
+ }
663
+ self.expect(TokenKind::Gt)?; // >
664
+ }
665
+ Ok(params)
666
+ }
667
+
668
+ /// Specialize a generic struct reference `base<args>` into a concrete synthetic alias name
669
+ /// (e.g. `Box__number`), emitting `type Box__number = { value: number }` once per instantiation
670
+ /// so it lowers to a native struct. Returns `None` if `base` isn't a known generic alias or the
671
+ /// arity mismatches (caller then falls back to erasing the args).
672
+ fn monomorphize_generic(&mut self, base: &str, args: &[TypeAnnotation]) -> Option<String> {
673
+ let (params, body) = self.generic_aliases.get(base)?.clone();
674
+ if params.len() != args.len() {
675
+ return None;
676
+ }
677
+ let spec_name = mangle_generic(base, args);
678
+ if self.generic_done.insert(spec_name.clone()) {
679
+ let subst: HashMap<&str, &TypeAnnotation> =
680
+ params.iter().map(|p| p.as_ref()).zip(args.iter()).collect();
681
+ let concrete = subst_type(&body, &subst);
682
+ let z = Span {
683
+ start: (0, 0),
684
+ end: (0, 0),
685
+ };
686
+ self.generic_specializations.push(Statement::TypeAlias {
687
+ name: Arc::from(spec_name.as_str()),
688
+ name_span: z,
689
+ ty: concrete,
690
+ span: z,
691
+ });
692
+ }
693
+ Some(spec_name)
694
+ }
695
+
696
+ /// Parse a generic type-argument list `<T, U, …>` on a type reference (`Array<number>`,
697
+ /// `Map<string, number>`). Nested `Array<Array<T>>` works because tish has no `>>` token.
698
+ fn parse_type_args(&mut self) -> Result<Vec<TypeAnnotation>, String> {
699
+ self.expect(TokenKind::Lt)?; // <
700
+ let mut args = Vec::new();
701
+ if !self.try_close_angle() {
702
+ loop {
703
+ args.push(self.parse_type_annotation()?);
704
+ if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
705
+ self.advance();
706
+ continue;
707
+ }
708
+ break;
709
+ }
710
+ if !self.try_close_angle() {
711
+ return Err("expected `>` to close type arguments".to_string());
712
+ }
713
+ }
714
+ Ok(args)
715
+ }
716
+
717
+ /// Parse a type annotation (number, string, T[], T?, {a: T}, A | B, A & B, Array<T>, etc.)
718
+ fn parse_type_annotation(&mut self) -> Result<TypeAnnotation, String> {
719
+ let base = self.parse_type_intersection()?;
720
+
721
+ // Union: T | U | ... (binds looser than `&`)
722
+ if matches!(self.peek_kind(), Some(TokenKind::BitOr)) {
723
+ let mut types = vec![base];
724
+ while matches!(self.peek_kind(), Some(TokenKind::BitOr)) {
725
+ self.advance(); // |
726
+ types.push(self.parse_type_intersection()?);
727
+ }
728
+ return Ok(TypeAnnotation::Union(types));
729
+ }
730
+
731
+ Ok(base)
732
+ }
733
+
734
+ /// `A & B & …` intersection (binds tighter than `|`).
735
+ fn parse_type_intersection(&mut self) -> Result<TypeAnnotation, String> {
736
+ let base = self.parse_type_postfix()?;
737
+ if matches!(self.peek_kind(), Some(TokenKind::BitAnd)) {
738
+ let mut types = vec![base];
739
+ while matches!(self.peek_kind(), Some(TokenKind::BitAnd)) {
740
+ self.advance(); // &
741
+ types.push(self.parse_type_postfix()?);
742
+ }
743
+ return Ok(TypeAnnotation::Intersection(types));
744
+ }
745
+ Ok(base)
746
+ }
747
+
748
+ /// A primary type plus any postfix `[]` (array) and `?` (optional, `T? === T | null`),
749
+ /// chained: `T[]`, `T[][]`, `T?`, `T?[]`, …
750
+ fn parse_type_postfix(&mut self) -> Result<TypeAnnotation, String> {
751
+ let mut t = self.parse_type_primary()?;
752
+ loop {
753
+ match self.peek_kind() {
754
+ Some(TokenKind::LBracket) => {
755
+ self.advance(); // [
756
+ self.expect(TokenKind::RBracket)?; // ]
757
+ t = TypeAnnotation::Array(Box::new(t));
758
+ }
759
+ Some(TokenKind::Question) => {
760
+ self.advance(); // ?
761
+ t = TypeAnnotation::Union(vec![t, TypeAnnotation::Simple("null".into())]);
762
+ }
763
+ _ => break,
764
+ }
765
+ }
766
+ Ok(t)
767
+ }
768
+
769
+ /// Parse a primary type (identifier, object, or function type)
770
+ fn parse_type_primary(&mut self) -> Result<TypeAnnotation, String> {
771
+ match self.peek_kind() {
772
+ Some(TokenKind::Ident) => {
773
+ let tok = self.advance().ok_or("Expected type name")?;
774
+ let name = tok.literal.clone().ok_or("Expected type name")?;
775
+ // Generic reference `Name<Args>`: `Array<T>` desugars to the native `T[]`; other
776
+ // generic refs erase their args (the base name resolves to its alias, whose type
777
+ // params already act as unknown -> `Value`).
778
+ if matches!(self.peek_kind(), Some(TokenKind::Lt)) {
779
+ let args = self.parse_type_args()?;
780
+ if name.as_ref() == "Array" && args.len() == 1 {
781
+ return Ok(TypeAnnotation::Array(Box::new(
782
+ args.into_iter().next().unwrap(),
783
+ )));
784
+ }
785
+ // Monomorphize a generic struct ref `Box<number>` into a synthetic concrete
786
+ // alias `Box__number` (a native struct). Falls back to erasing the args when
787
+ // `name` isn't a known generic alias (e.g. forward reference) or arity mismatches.
788
+ if let Some(spec) = self.monomorphize_generic(name.as_ref(), &args) {
789
+ return Ok(TypeAnnotation::Simple(Arc::from(spec.as_str())));
790
+ }
791
+ return Ok(TypeAnnotation::Simple(name));
792
+ }
793
+ Ok(TypeAnnotation::Simple(name))
794
+ }
795
+ Some(TokenKind::Type | TokenKind::Declare) => {
796
+ let tok = self.advance().ok_or("Expected type name")?;
797
+ let name = tok.literal.clone().ok_or("Expected type name")?;
798
+ Ok(TypeAnnotation::Simple(name))
799
+ }
800
+ // Handle keywords that can be type names
801
+ Some(TokenKind::Null) => {
802
+ self.advance();
803
+ Ok(TypeAnnotation::Simple("null".into()))
804
+ }
805
+ Some(TokenKind::Void) => {
806
+ self.advance();
807
+ Ok(TypeAnnotation::Simple("void".into()))
808
+ }
809
+ Some(TokenKind::LBrace) => {
810
+ // Object type: { key: Type, ... }
811
+ self.advance(); // {
812
+ let mut props = Vec::new();
813
+ while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
814
+ // `for` is a keyword but a common method name (`Symbol.for`); allow it here.
815
+ let key: Arc<str> = match self.peek_kind() {
816
+ Some(TokenKind::Ident) => {
817
+ let tok = self.expect(TokenKind::Ident)?;
818
+ Arc::from(
819
+ tok.literal
820
+ .as_deref()
821
+ .ok_or("Expected property name")?,
822
+ )
823
+ }
824
+ Some(TokenKind::For) => {
825
+ self.advance();
826
+ Arc::from("for")
827
+ }
828
+ _ => {
829
+ return Err(format!(
830
+ "Expected Ident or `for` as object type property name, got {:?}",
831
+ self.peek_kind()
832
+ ));
833
+ }
834
+ };
835
+ self.expect(TokenKind::Colon)?;
836
+ let typ = self.parse_type_annotation()?;
837
+ props.push((key, typ));
838
+ if !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
839
+ // Accept `,` or `;` between items (TypeScript-style
840
+ // semicolons are common in interface/object type
841
+ // declarations); also tolerate a trailing separator.
842
+ if matches!(
843
+ self.peek_kind(),
844
+ Some(TokenKind::Comma) | Some(TokenKind::Semicolon)
845
+ ) {
846
+ self.advance();
847
+ }
848
+ }
849
+ }
850
+ self.expect(TokenKind::RBrace)?;
851
+ Ok(TypeAnnotation::Object(props))
852
+ }
853
+ Some(TokenKind::LParen) => {
854
+ // Function type: (T1, T2) => R
855
+ self.advance(); // (
856
+ let mut params = Vec::new();
857
+ while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
858
+ params.push(self.parse_type_annotation()?);
859
+ if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
860
+ self.expect(TokenKind::Comma)?;
861
+ }
862
+ }
863
+ self.expect(TokenKind::RParen)?;
864
+ self.expect(TokenKind::Arrow)?;
865
+ let returns = self.parse_type_annotation()?;
866
+ Ok(TypeAnnotation::Function {
867
+ params,
868
+ returns: Box::new(returns),
869
+ })
870
+ }
871
+ // Tuple type: [T1, T2, ...]
872
+ Some(TokenKind::LBracket) => {
873
+ self.advance(); // [
874
+ let mut elems = Vec::new();
875
+ while !matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
876
+ elems.push(self.parse_type_annotation()?);
877
+ if !matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
878
+ self.expect(TokenKind::Comma)?;
879
+ }
880
+ }
881
+ self.expect(TokenKind::RBracket)?;
882
+ Ok(TypeAnnotation::Tuple(elems))
883
+ }
884
+ // Literal types: "foo", 42, true, false
885
+ Some(TokenKind::String) => {
886
+ let tok = self.advance().ok_or("Expected string literal type")?;
887
+ let s = tok.literal.clone().ok_or("Expected string literal")?;
888
+ Ok(TypeAnnotation::Literal(TypeLiteral::Str(s)))
889
+ }
890
+ Some(TokenKind::Number) => {
891
+ let tok = self.advance().ok_or("Expected number literal type")?;
892
+ let s = tok.literal.as_ref().ok_or("Expected number literal")?;
893
+ let n: f64 = s.parse().map_err(|_| format!("Invalid number: {}", s))?;
894
+ Ok(TypeAnnotation::Literal(TypeLiteral::Num(n)))
895
+ }
896
+ Some(TokenKind::True) => {
897
+ self.advance();
898
+ Ok(TypeAnnotation::Literal(TypeLiteral::Bool(true)))
899
+ }
900
+ Some(TokenKind::False) => {
901
+ self.advance();
902
+ Ok(TypeAnnotation::Literal(TypeLiteral::Bool(false)))
903
+ }
904
+ _ => Err("Expected type annotation".to_string()),
905
+ }
906
+ }
907
+
908
+ fn parse_fun_decl(&mut self, async_: bool) -> Result<Statement, String> {
909
+ let span_start = self.expect(TokenKind::Fn)?.span.start;
910
+ let name_tok = self.expect(TokenKind::Ident)?;
911
+ let name_span = Span {
912
+ start: name_tok.span.start,
913
+ end: name_tok.span.end,
914
+ };
915
+ let name = name_tok.literal.clone().ok_or("Expected function name")?;
916
+ self.parse_type_params()?; // generic `fn f<T, U>(…)` — fn type params run gradually (boxed)
917
+ self.expect(TokenKind::LParen)?;
918
+ let mut params = Vec::with_capacity(4);
919
+ let mut rest_param = None;
920
+ while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
921
+ if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
922
+ self.advance();
923
+ let rest_tok = self.expect(TokenKind::Ident)?;
924
+ let rest_name_span = Span {
925
+ start: rest_tok.span.start,
926
+ end: rest_tok.span.end,
927
+ };
928
+ let param_name = rest_tok.literal.clone().ok_or("Expected rest param name")?;
929
+ // Optional type annotation for rest param
930
+ let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
931
+ self.advance();
932
+ Some(self.parse_type_annotation()?)
933
+ } else {
934
+ None
935
+ };
936
+ rest_param = Some(TypedParam {
937
+ name: param_name,
938
+ name_span: rest_name_span,
939
+ type_ann,
940
+ default: None,
941
+ });
942
+ if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
943
+ return Err("Rest parameter must be last".to_string());
944
+ }
945
+ break;
946
+ }
947
+ params.push(self.parse_fun_param()?);
948
+ if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
949
+ self.expect(TokenKind::Comma)?;
950
+ }
951
+ }
952
+ self.expect(TokenKind::RParen)?;
953
+
954
+ // Optional return type: `: Type`
955
+ let return_type = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
956
+ self.advance();
957
+ Some(self.parse_type_annotation()?)
958
+ } else {
959
+ None
960
+ };
961
+
962
+ let body = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
963
+ self.advance(); // =
964
+ let expr = self.parse_expr()?;
965
+ let span = expr.span();
966
+ Box::new(Statement::Block {
967
+ statements: vec![Statement::Return {
968
+ value: Some(expr),
969
+ span,
970
+ }],
971
+ span,
972
+ })
973
+ } else {
974
+ Box::new(self.parse_block()?)
975
+ };
976
+
977
+ // Span must cover the whole declaration through the body. `peek().start` alone can sit on
978
+ // the opening `{` (same as `span_start` at EOF) or otherwise truncate before inner spans.
979
+ let peek_start = self.peek().map(|t| t.span.start).unwrap_or(span_start);
980
+ let body_end = body.as_ref().span().end;
981
+ let end = if peek_start.0 > body_end.0
982
+ || (peek_start.0 == body_end.0 && peek_start.1 > body_end.1)
983
+ {
984
+ peek_start
985
+ } else {
986
+ body_end
987
+ };
988
+
989
+ Ok(Statement::FunDecl {
990
+ async_,
991
+ name,
992
+ name_span,
993
+ params,
994
+ rest_param,
995
+ return_type,
996
+ body,
997
+ span: Span {
998
+ start: span_start,
999
+ end,
1000
+ },
1001
+ })
1002
+ }
1003
+
1004
+ fn parse_type_alias(&mut self) -> Result<Statement, String> {
1005
+ let span_start = self.expect(TokenKind::Type)?.span.start;
1006
+ let name_tok = self.expect(TokenKind::Ident)?;
1007
+ let name_span = Span {
1008
+ start: name_tok.span.start,
1009
+ end: name_tok.span.end,
1010
+ };
1011
+ let name = name_tok.literal.clone().ok_or("Expected type alias name")?;
1012
+ let type_params = self.parse_type_params()?;
1013
+ self.expect(TokenKind::Assign)?;
1014
+ let ty = self.parse_type_annotation()?;
1015
+ if !type_params.is_empty() {
1016
+ self.generic_aliases
1017
+ .insert(name.to_string(), (type_params, ty.clone()));
1018
+ }
1019
+ Ok(Statement::TypeAlias {
1020
+ name,
1021
+ name_span,
1022
+ ty,
1023
+ span: self.span_end(span_start),
1024
+ })
1025
+ }
1026
+
1027
+ /// `interface Name { k: T, ... }` — desugared to `type Name = { ... }` so the checker
1028
+ /// (structural matching) and codegen (native `TishStruct_*`) treat it exactly like an
1029
+ /// object-type alias. (`extends` is not yet supported.)
1030
+ fn parse_interface(&mut self) -> Result<Statement, String> {
1031
+ let span_start = self.expect(TokenKind::Interface)?.span.start;
1032
+ let name_tok = self.expect(TokenKind::Ident)?;
1033
+ let name_span = Span {
1034
+ start: name_tok.span.start,
1035
+ end: name_tok.span.end,
1036
+ };
1037
+ let name = name_tok.literal.clone().ok_or("Expected interface name")?;
1038
+ let type_params = self.parse_type_params()?;
1039
+
1040
+ // `extends Parent1, Parent2` — desugar to an intersection of the parents with the body, so
1041
+ // the inherited fields participate in structural checking + native struct emission.
1042
+ let mut parents: Vec<TypeAnnotation> = Vec::new();
1043
+ if matches!(self.peek_kind(), Some(TokenKind::Ident))
1044
+ && self.peek().and_then(|t| t.literal.as_deref()) == Some("extends")
1045
+ {
1046
+ self.advance(); // extends
1047
+ loop {
1048
+ parents.push(self.parse_type_postfix()?);
1049
+ if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
1050
+ self.advance();
1051
+ continue;
1052
+ }
1053
+ break;
1054
+ }
1055
+ }
1056
+
1057
+ let body = self.parse_type_annotation()?;
1058
+ let ty = if parents.is_empty() {
1059
+ body
1060
+ } else {
1061
+ parents.push(body);
1062
+ TypeAnnotation::Intersection(parents)
1063
+ };
1064
+ if !type_params.is_empty() {
1065
+ self.generic_aliases
1066
+ .insert(name.to_string(), (type_params, ty.clone()));
1067
+ }
1068
+ Ok(Statement::TypeAlias {
1069
+ name,
1070
+ name_span,
1071
+ ty,
1072
+ span: self.span_end(span_start),
1073
+ })
1074
+ }
1075
+
1076
+ fn parse_declare(&mut self) -> Result<Statement, String> {
1077
+ let span_start = self.expect(TokenKind::Declare)?.span.start;
1078
+ let async_ = if matches!(self.peek_kind(), Some(TokenKind::Async)) {
1079
+ self.advance();
1080
+ true
1081
+ } else {
1082
+ false
1083
+ };
1084
+ if matches!(self.peek_kind(), Some(TokenKind::Fn)) {
1085
+ return self.parse_declare_fun(span_start, async_);
1086
+ }
1087
+ let const_ = match self.peek_kind() {
1088
+ Some(TokenKind::Let) => {
1089
+ self.advance();
1090
+ false
1091
+ }
1092
+ Some(TokenKind::Const) => {
1093
+ self.advance();
1094
+ true
1095
+ }
1096
+ _ => {
1097
+ return Err(
1098
+ "Expected `let`, `const`, `async fn`, or `fn` after `declare`".to_string(),
1099
+ );
1100
+ }
1101
+ };
1102
+ let name_tok = self.expect(TokenKind::Ident)?;
1103
+ let name_span = Span {
1104
+ start: name_tok.span.start,
1105
+ end: name_tok.span.end,
1106
+ };
1107
+ let name = name_tok.literal.clone().ok_or("Expected identifier")?;
1108
+ let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
1109
+ self.advance();
1110
+ Some(self.parse_type_annotation()?)
1111
+ } else {
1112
+ None
1113
+ };
1114
+ if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
1115
+ return Err("`declare` cannot have an initializer".to_string());
1116
+ }
1117
+ Ok(Statement::DeclareVar {
1118
+ name,
1119
+ name_span,
1120
+ type_ann,
1121
+ const_,
1122
+ span: self.span_end(span_start),
1123
+ })
1124
+ }
1125
+
1126
+ fn parse_declare_fun(
1127
+ &mut self,
1128
+ span_start: (usize, usize),
1129
+ async_: bool,
1130
+ ) -> Result<Statement, String> {
1131
+ self.expect(TokenKind::Fn)?;
1132
+ let name_tok = self.expect(TokenKind::Ident)?;
1133
+ let name_span = Span {
1134
+ start: name_tok.span.start,
1135
+ end: name_tok.span.end,
1136
+ };
1137
+ let name = name_tok.literal.clone().ok_or("Expected function name")?;
1138
+ self.parse_type_params()?; // generic `fn f<T, U>(…)` — fn type params run gradually (boxed)
1139
+ self.expect(TokenKind::LParen)?;
1140
+ let mut params = Vec::with_capacity(4);
1141
+ let mut rest_param = None;
1142
+ while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
1143
+ if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
1144
+ self.advance();
1145
+ let rest_tok = self.expect(TokenKind::Ident)?;
1146
+ let rest_name_span = Span {
1147
+ start: rest_tok.span.start,
1148
+ end: rest_tok.span.end,
1149
+ };
1150
+ let param_name = rest_tok.literal.clone().ok_or("Expected rest param name")?;
1151
+ let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
1152
+ self.advance();
1153
+ Some(self.parse_type_annotation()?)
1154
+ } else {
1155
+ None
1156
+ };
1157
+ rest_param = Some(TypedParam {
1158
+ name: param_name,
1159
+ name_span: rest_name_span,
1160
+ type_ann,
1161
+ default: None,
1162
+ });
1163
+ if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
1164
+ return Err("Rest parameter must be last".to_string());
1165
+ }
1166
+ break;
1167
+ }
1168
+ params.push(self.parse_fun_param()?);
1169
+ if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
1170
+ self.expect(TokenKind::Comma)?;
1171
+ }
1172
+ }
1173
+ self.expect(TokenKind::RParen)?;
1174
+ let return_type = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
1175
+ self.advance();
1176
+ Some(self.parse_type_annotation()?)
1177
+ } else {
1178
+ None
1179
+ };
1180
+ if matches!(
1181
+ self.peek_kind(),
1182
+ Some(TokenKind::Assign | TokenKind::LBrace | TokenKind::Indent)
1183
+ ) {
1184
+ return Err("`declare function` must not have a body".to_string());
1185
+ }
1186
+ if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
1187
+ self.advance();
1188
+ }
1189
+ Ok(Statement::DeclareFun {
1190
+ async_,
1191
+ name,
1192
+ name_span,
1193
+ params,
1194
+ rest_param,
1195
+ return_type,
1196
+ span: self.span_end(span_start),
1197
+ })
1198
+ }
1199
+
1200
+ fn parse_if(&mut self) -> Result<Statement, String> {
1201
+ let span_start = self.expect(TokenKind::If)?.span.start;
1202
+ self.expect(TokenKind::LParen)?;
1203
+ let cond = self.parse_expr()?;
1204
+ self.expect(TokenKind::RParen)?;
1205
+ let then_branch = Box::new(self.parse_block_or_statement()?);
1206
+ let else_branch = if matches!(self.peek_kind(), Some(TokenKind::Else)) {
1207
+ self.advance();
1208
+ Some(Box::new(self.parse_block_or_statement()?))
1209
+ } else {
1210
+ None
1211
+ };
1212
+ Ok(Statement::If {
1213
+ cond,
1214
+ then_branch,
1215
+ else_branch,
1216
+ span: self.span_end(span_start),
1217
+ })
1218
+ }
1219
+
1220
+ fn parse_while(&mut self) -> Result<Statement, String> {
1221
+ let span_start = self.expect(TokenKind::While)?.span.start;
1222
+ self.expect(TokenKind::LParen)?;
1223
+ let cond = self.parse_expr()?;
1224
+ self.expect(TokenKind::RParen)?;
1225
+ let body = Box::new(self.parse_block_or_statement()?);
1226
+ Ok(Statement::While {
1227
+ cond,
1228
+ body,
1229
+ span: self.span_end(span_start),
1230
+ })
1231
+ }
1232
+
1233
+ fn parse_for(&mut self) -> Result<Statement, String> {
1234
+ let span_start = self.expect(TokenKind::For)?.span.start;
1235
+ self.expect(TokenKind::LParen)?;
1236
+ let init = if matches!(self.peek_kind(), Some(TokenKind::Let | TokenKind::Const)) {
1237
+ let mutable = matches!(self.peek_kind(), Some(TokenKind::Let));
1238
+ let var_span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
1239
+ self.advance();
1240
+ let for_name_tok = self.expect(TokenKind::Ident)?;
1241
+ let name_span = Span {
1242
+ start: for_name_tok.span.start,
1243
+ end: for_name_tok.span.end,
1244
+ };
1245
+ let name = for_name_tok.literal.clone().ok_or("Expected identifier")?;
1246
+ if matches!(self.peek_kind(), Some(TokenKind::Of)) {
1247
+ self.advance();
1248
+ let iterable = self.parse_expr()?;
1249
+ self.expect(TokenKind::RParen)?;
1250
+ let body = Box::new(self.parse_block_or_statement()?);
1251
+ return Ok(Statement::ForOf {
1252
+ name,
1253
+ name_span,
1254
+ iterable,
1255
+ body,
1256
+ span: self.span_end(span_start),
1257
+ });
1258
+ }
1259
+ let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
1260
+ self.advance();
1261
+ Some(self.parse_type_annotation()?)
1262
+ } else {
1263
+ None
1264
+ };
1265
+ let init_expr = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
1266
+ self.advance();
1267
+ Some(self.parse_expr()?)
1268
+ } else {
1269
+ None
1270
+ };
1271
+ let first = Statement::VarDecl {
1272
+ name,
1273
+ name_span,
1274
+ mutable,
1275
+ type_ann,
1276
+ init: init_expr,
1277
+ span: self.span_end(var_span_start),
1278
+ };
1279
+ // Comma-separated for-init declarators: `for (let i = 0, n = len; ...)`.
1280
+ let decl = if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
1281
+ let mut statements = vec![first];
1282
+ while matches!(self.peek_kind(), Some(TokenKind::Comma)) {
1283
+ self.advance();
1284
+ let decl_start = self.peek().map(|t| t.span.start).unwrap_or(var_span_start);
1285
+ statements.push(self.parse_one_declarator(mutable, decl_start)?);
1286
+ }
1287
+ Statement::Multi {
1288
+ statements,
1289
+ span: self.span_end(var_span_start),
1290
+ }
1291
+ } else {
1292
+ first
1293
+ };
1294
+ if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
1295
+ self.advance();
1296
+ }
1297
+ Some(Box::new(decl))
1298
+ } else if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
1299
+ None
1300
+ } else {
1301
+ let st = self.peek().ok_or("Unexpected EOF in for init")?;
1302
+ let span_start = st.span.start;
1303
+ Some(Box::new(Statement::ExprStmt {
1304
+ expr: self.parse_expr()?,
1305
+ span: Span {
1306
+ start: span_start,
1307
+ end: self.peek().map(|x| x.span.end).unwrap_or(span_start),
1308
+ },
1309
+ }))
1310
+ };
1311
+ if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
1312
+ self.advance();
1313
+ }
1314
+ let cond = if matches!(
1315
+ self.peek_kind(),
1316
+ Some(TokenKind::Semicolon | TokenKind::RParen)
1317
+ ) {
1318
+ None
1319
+ } else {
1320
+ let c = self.parse_expr()?;
1321
+ self.expect(TokenKind::Semicolon)?;
1322
+ Some(c)
1323
+ };
1324
+ // `for (init; ; update)` — when the condition is empty we matched `;` above but did not
1325
+ // consume it; skip it so `update` / `)` parse correctly (e.g. `for (;;)`).
1326
+ if cond.is_none() && matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
1327
+ self.advance();
1328
+ }
1329
+ let update = if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
1330
+ None
1331
+ } else {
1332
+ let u = self.parse_expr()?;
1333
+ Some(u)
1334
+ };
1335
+ self.expect(TokenKind::RParen)?;
1336
+ let body = Box::new(self.parse_block_or_statement()?);
1337
+ Ok(Statement::For {
1338
+ init,
1339
+ cond,
1340
+ update,
1341
+ body,
1342
+ span: self.span_end(span_start),
1343
+ })
1344
+ }
1345
+
1346
+ fn parse_return(&mut self) -> Result<Statement, String> {
1347
+ let span_start = self.expect(TokenKind::Return)?.span.start;
1348
+ // JS restricted production: `return [no LineTerminator here] Expression`.
1349
+ // A line break between `return` and its argument inserts a semicolon, so
1350
+ // `return` alone is `return null`. The lexer emits no token for a same-indent
1351
+ // line break (only Indent/Dedent on level *changes*), so a braceless
1352
+ // `if (c) return` followed by a statement on the next line would otherwise
1353
+ // swallow that statement as the return value (#96). Detect the line break by
1354
+ // comparing the next token's line to the `return` keyword's line.
1355
+ let next_on_new_line = self
1356
+ .peek()
1357
+ .map(|t| t.span.start.0 > span_start.0)
1358
+ .unwrap_or(true);
1359
+ let value = if next_on_new_line
1360
+ || matches!(
1361
+ self.peek_kind(),
1362
+ Some(TokenKind::Semicolon | TokenKind::Dedent | TokenKind::RBrace)
1363
+ )
1364
+ || self.peek_kind().is_none()
1365
+ {
1366
+ None
1367
+ } else {
1368
+ Some(self.parse_expr()?)
1369
+ };
1370
+ Ok(Statement::Return {
1371
+ value,
1372
+ span: self.span_end(span_start),
1373
+ })
1374
+ }
1375
+
1376
+ fn parse_switch(&mut self) -> Result<Statement, String> {
1377
+ let span_start = self.expect(TokenKind::Switch)?.span.start;
1378
+ self.expect(TokenKind::LParen)?;
1379
+ let expr = self.parse_expr()?;
1380
+ self.expect(TokenKind::RParen)?;
1381
+ self.expect(TokenKind::LBrace)?;
1382
+ let mut cases = Vec::new();
1383
+ let mut default_body = None;
1384
+ loop {
1385
+ if matches!(self.peek_kind(), Some(TokenKind::Case)) {
1386
+ self.advance();
1387
+ let case_expr = self.parse_expr()?;
1388
+ self.expect(TokenKind::Colon)?;
1389
+ let mut body = Vec::new();
1390
+ while !matches!(self.peek_kind(), Some(TokenKind::Case))
1391
+ && !matches!(self.peek_kind(), Some(TokenKind::Default))
1392
+ && !matches!(self.peek_kind(), Some(TokenKind::RBrace))
1393
+ && self.peek_kind().is_some()
1394
+ {
1395
+ body.push(self.parse_statement()?);
1396
+ }
1397
+ cases.push((Some(case_expr), body));
1398
+ } else if matches!(self.peek_kind(), Some(TokenKind::Default)) {
1399
+ self.advance();
1400
+ self.expect(TokenKind::Colon)?;
1401
+ let mut body = Vec::new();
1402
+ while !matches!(self.peek_kind(), Some(TokenKind::RBrace))
1403
+ && self.peek_kind().is_some()
1404
+ {
1405
+ body.push(self.parse_statement()?);
1406
+ }
1407
+ default_body = Some(body);
1408
+ break;
1409
+ } else if matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
1410
+ break;
1411
+ } else {
1412
+ return Err("Expected case or default in switch".to_string());
1413
+ }
1414
+ }
1415
+ self.expect(TokenKind::RBrace)?;
1416
+ Ok(Statement::Switch {
1417
+ expr,
1418
+ cases,
1419
+ default_body,
1420
+ span: self.span_end(span_start),
1421
+ })
1422
+ }
1423
+
1424
+ fn parse_do_while(&mut self) -> Result<Statement, String> {
1425
+ let span_start = self.expect(TokenKind::Do)?.span.start;
1426
+ let body = Box::new(self.parse_block_or_statement()?);
1427
+ self.expect(TokenKind::While)?;
1428
+ self.expect(TokenKind::LParen)?;
1429
+ let cond = self.parse_expr()?;
1430
+ self.expect(TokenKind::RParen)?;
1431
+ Ok(Statement::DoWhile {
1432
+ body,
1433
+ cond,
1434
+ span: self.span_end(span_start),
1435
+ })
1436
+ }
1437
+
1438
+ fn parse_throw(&mut self) -> Result<Statement, String> {
1439
+ let span_start = self.expect(TokenKind::Throw)?.span.start;
1440
+ let value = self.parse_expr()?;
1441
+ Ok(Statement::Throw {
1442
+ value,
1443
+ span: self.span_end(span_start),
1444
+ })
1445
+ }
1446
+
1447
+ fn parse_try(&mut self) -> Result<Statement, String> {
1448
+ let span_start = self.expect(TokenKind::Try)?.span.start;
1449
+ let body = Box::new(self.parse_block_or_statement()?);
1450
+
1451
+ let mut catch_param = None;
1452
+ let mut catch_param_span = None;
1453
+ let mut catch_body = None;
1454
+ let mut finally_body = None;
1455
+
1456
+ if matches!(self.peek_kind(), Some(TokenKind::Catch)) {
1457
+ self.advance();
1458
+ self.expect(TokenKind::LParen)?;
1459
+ let catch_tok = self.expect(TokenKind::Ident)?;
1460
+ catch_param_span = Some(Span {
1461
+ start: catch_tok.span.start,
1462
+ end: catch_tok.span.end,
1463
+ });
1464
+ catch_param = catch_tok.literal.clone();
1465
+ self.expect(TokenKind::RParen)?;
1466
+ catch_body = Some(Box::new(self.parse_block_or_statement()?));
1467
+ }
1468
+
1469
+ if matches!(self.peek_kind(), Some(TokenKind::Finally)) {
1470
+ self.advance();
1471
+ finally_body = Some(Box::new(self.parse_block_or_statement()?));
1472
+ }
1473
+
1474
+ if catch_body.is_none() && finally_body.is_none() {
1475
+ return Err("try statement requires catch or finally".to_string());
1476
+ }
1477
+
1478
+ Ok(Statement::Try {
1479
+ body,
1480
+ catch_param,
1481
+ catch_param_span,
1482
+ catch_body,
1483
+ finally_body,
1484
+ span: self.span_end(span_start),
1485
+ })
1486
+ }
1487
+
1488
+ fn parse_import(&mut self) -> Result<Statement, String> {
1489
+ let span_start = self.expect(TokenKind::Import)?.span.start;
1490
+ let specifiers = if matches!(self.peek_kind(), Some(TokenKind::LBrace)) {
1491
+ // Named: import { a, b as c } from "..."
1492
+ self.advance();
1493
+ let mut specs = Vec::new();
1494
+ while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
1495
+ let name_tok = self.expect(TokenKind::Ident)?;
1496
+ let name_span = Span {
1497
+ start: name_tok.span.start,
1498
+ end: name_tok.span.end,
1499
+ };
1500
+ let name = name_tok
1501
+ .literal
1502
+ .clone()
1503
+ .ok_or("Expected identifier in import")?;
1504
+ // `as` is a dedicated keyword token (also used for casts); in import-specifier
1505
+ // position it introduces a rename: `{ foo as bar }`.
1506
+ let (alias, alias_span) = if matches!(self.peek_kind(), Some(TokenKind::As)) {
1507
+ self.advance(); // consume 'as'
1508
+ let alias_tok = self.expect(TokenKind::Ident)?;
1509
+ let asp = Span {
1510
+ start: alias_tok.span.start,
1511
+ end: alias_tok.span.end,
1512
+ };
1513
+ (
1514
+ Some(
1515
+ alias_tok
1516
+ .literal
1517
+ .clone()
1518
+ .ok_or("Expected alias after 'as'")?,
1519
+ ),
1520
+ Some(asp),
1521
+ )
1522
+ } else {
1523
+ (None, None)
1524
+ };
1525
+ specs.push(ImportSpecifier::Named {
1526
+ name,
1527
+ name_span,
1528
+ alias,
1529
+ alias_span,
1530
+ });
1531
+ if !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
1532
+ self.expect(TokenKind::Comma)?;
1533
+ }
1534
+ }
1535
+ self.expect(TokenKind::RBrace)?;
1536
+ specs
1537
+ } else if matches!(self.peek_kind(), Some(TokenKind::Star)) {
1538
+ // Namespace: import * as M from "..."
1539
+ self.advance();
1540
+ self.expect(TokenKind::As)?;
1541
+ let alias_tok = self.expect(TokenKind::Ident)?;
1542
+ let name_span = Span {
1543
+ start: alias_tok.span.start,
1544
+ end: alias_tok.span.end,
1545
+ };
1546
+ let alias = alias_tok
1547
+ .literal
1548
+ .clone()
1549
+ .ok_or("Expected identifier after 'as'")?;
1550
+ vec![ImportSpecifier::Namespace {
1551
+ name: alias,
1552
+ name_span,
1553
+ }]
1554
+ } else if matches!(self.peek_kind(), Some(TokenKind::Ident)) {
1555
+ // Default: import X from "..."
1556
+ let def_tok = self.expect(TokenKind::Ident)?;
1557
+ let name_span = Span {
1558
+ start: def_tok.span.start,
1559
+ end: def_tok.span.end,
1560
+ };
1561
+ let name = def_tok.literal.clone().ok_or("Expected identifier")?;
1562
+ vec![ImportSpecifier::Default { name, name_span }]
1563
+ } else {
1564
+ return Err("Expected { }, * as name, or default import".to_string());
1565
+ };
1566
+ let from_tok = self.expect(TokenKind::Ident)?;
1567
+ if from_tok.literal.as_deref() != Some("from") {
1568
+ return Err("Expected 'from' in import statement".to_string());
1569
+ }
1570
+ let from = self
1571
+ .expect(TokenKind::String)?
1572
+ .literal
1573
+ .clone()
1574
+ .ok_or("Expected string path in import")?;
1575
+ Ok(Statement::Import {
1576
+ specifiers,
1577
+ from,
1578
+ span: self.span_end(span_start),
1579
+ })
1580
+ }
1581
+
1582
+ fn parse_export(&mut self) -> Result<Statement, String> {
1583
+ let span_start = self.expect(TokenKind::Export)?.span.start;
1584
+ let declaration = if matches!(self.peek_kind(), Some(TokenKind::Default)) {
1585
+ self.advance();
1586
+ let expr = self.parse_expr()?;
1587
+ ExportDeclaration::Default(expr)
1588
+ } else if matches!(self.peek_kind(), Some(TokenKind::Type)) {
1589
+ ExportDeclaration::Named(Box::new(self.parse_type_alias()?))
1590
+ } else if matches!(self.peek_kind(), Some(TokenKind::Declare)) {
1591
+ ExportDeclaration::Named(Box::new(self.parse_declare()?))
1592
+ } else if matches!(self.peek_kind(), Some(TokenKind::Const)) {
1593
+ ExportDeclaration::Named(Box::new(self.parse_var_decl(false)?))
1594
+ } else if matches!(self.peek_kind(), Some(TokenKind::Let)) {
1595
+ ExportDeclaration::Named(Box::new(self.parse_var_decl(true)?))
1596
+ } else if matches!(self.peek_kind(), Some(TokenKind::Async))
1597
+ || matches!(self.peek_kind(), Some(TokenKind::Fn))
1598
+ {
1599
+ let async_ = matches!(self.peek_kind(), Some(TokenKind::Async));
1600
+ if async_ {
1601
+ self.advance();
1602
+ }
1603
+ ExportDeclaration::Named(Box::new(self.parse_fun_decl(async_)?))
1604
+ } else {
1605
+ return Err(
1606
+ "Expected 'default', 'type', 'declare', 'const', 'let', or 'fn' after export"
1607
+ .to_string(),
1608
+ );
1609
+ };
1610
+ Ok(Statement::Export {
1611
+ declaration: Box::new(declaration),
1612
+ span: self.span_end(span_start),
1613
+ })
1614
+ }
1615
+
1616
+ fn parse_expr(&mut self) -> Result<Expr, String> {
1617
+ self.parse_assign()
1618
+ }
1619
+
1620
+ fn parse_assign(&mut self) -> Result<Expr, String> {
1621
+ let left = self.parse_conditional()?;
1622
+
1623
+ // Check for simple assignment
1624
+ if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
1625
+ let start = left.span().start;
1626
+
1627
+ // Variable assignment: x = val
1628
+ if let Expr::Ident { name, .. } = &left {
1629
+ let name = Arc::clone(name);
1630
+ self.advance(); // =
1631
+ let value = self.parse_assign()?;
1632
+ let end = value.span().end;
1633
+ return Ok(Expr::Assign {
1634
+ name,
1635
+ value: Box::new(value),
1636
+ span: Span { start, end },
1637
+ });
1638
+ }
1639
+
1640
+ // Member assignment: obj.prop = val
1641
+ if let Expr::Member {
1642
+ object,
1643
+ prop: MemberProp::Name {
1644
+ name: prop_name, ..
1645
+ },
1646
+ ..
1647
+ } = &left
1648
+ {
1649
+ let object = Box::clone(object);
1650
+ let prop = Arc::clone(prop_name);
1651
+ self.advance(); // =
1652
+ let value = self.parse_assign()?;
1653
+ let end = value.span().end;
1654
+ return Ok(Expr::MemberAssign {
1655
+ object,
1656
+ prop,
1657
+ value: Box::new(value),
1658
+ span: Span { start, end },
1659
+ });
1660
+ }
1661
+
1662
+ // Index assignment: arr[idx] = val or obj[key] = val
1663
+ if let Expr::Index { object, index, .. } = &left {
1664
+ let object = Box::clone(object);
1665
+ let index = Box::clone(index);
1666
+ self.advance(); // =
1667
+ let value = self.parse_assign()?;
1668
+ let end = value.span().end;
1669
+ return Ok(Expr::IndexAssign {
1670
+ object,
1671
+ index,
1672
+ value: Box::new(value),
1673
+ span: Span { start, end },
1674
+ });
1675
+ }
1676
+ }
1677
+
1678
+ // Check for compound assignment (+=, -=, *=, /=, %=)
1679
+ let compound_op = match self.peek_kind() {
1680
+ Some(TokenKind::PlusAssign) => Some(CompoundOp::Add),
1681
+ Some(TokenKind::MinusAssign) => Some(CompoundOp::Sub),
1682
+ Some(TokenKind::StarAssign) => Some(CompoundOp::Mul),
1683
+ Some(TokenKind::SlashAssign) => Some(CompoundOp::Div),
1684
+ Some(TokenKind::PercentAssign) => Some(CompoundOp::Mod),
1685
+ _ => None,
1686
+ };
1687
+
1688
+ if let Some(op) = compound_op {
1689
+ if let Expr::Ident { name, span } = &left {
1690
+ let name = Arc::clone(name);
1691
+ let start = span.start;
1692
+ self.advance(); // consume the compound operator
1693
+ let value = self.parse_assign()?;
1694
+ let end = value.span().end;
1695
+ return Ok(Expr::CompoundAssign {
1696
+ name,
1697
+ op,
1698
+ value: Box::new(value),
1699
+ span: Span { start, end },
1700
+ });
1701
+ }
1702
+ }
1703
+
1704
+ // Check for logical assignment (&&=, ||=, ??=)
1705
+ let logical_op = match self.peek_kind() {
1706
+ Some(TokenKind::AndAndAssign) => Some(LogicalAssignOp::AndAnd),
1707
+ Some(TokenKind::OrOrAssign) => Some(LogicalAssignOp::OrOr),
1708
+ Some(TokenKind::NullishAssign) => Some(LogicalAssignOp::Nullish),
1709
+ _ => None,
1710
+ };
1711
+
1712
+ if let Some(op) = logical_op {
1713
+ if let Expr::Ident { name, span } = &left {
1714
+ let name = Arc::clone(name);
1715
+ let start = span.start;
1716
+ self.advance(); // consume the logical assign operator
1717
+ let value = self.parse_assign()?;
1718
+ let end = value.span().end;
1719
+ return Ok(Expr::LogicalAssign {
1720
+ name,
1721
+ op,
1722
+ value: Box::new(value),
1723
+ span: Span { start, end },
1724
+ });
1725
+ }
1726
+ }
1727
+
1728
+ Ok(left)
1729
+ }
1730
+
1731
+ fn parse_conditional(&mut self) -> Result<Expr, String> {
1732
+ let cond = self.parse_nullish_coalesce()?;
1733
+ if matches!(self.peek_kind(), Some(TokenKind::Question)) {
1734
+ let start = cond.span().start;
1735
+ self.advance(); // ?
1736
+ let then_branch = self.parse_expr()?;
1737
+ self.expect(TokenKind::Colon)?;
1738
+ let else_branch = self.parse_conditional()?; // right-associative
1739
+ let end = else_branch.span().end;
1740
+ Ok(Expr::Conditional {
1741
+ cond: Box::new(cond),
1742
+ then_branch: Box::new(then_branch),
1743
+ else_branch: Box::new(else_branch),
1744
+ span: Span { start, end },
1745
+ })
1746
+ } else {
1747
+ Ok(cond)
1748
+ }
1749
+ }
1750
+
1751
+ fn parse_nullish_coalesce(&mut self) -> Result<Expr, String> {
1752
+ let mut left = self.parse_or()?;
1753
+ while matches!(self.peek_kind(), Some(TokenKind::NullishCoalesce)) {
1754
+ self.advance();
1755
+ let right = self.parse_or()?;
1756
+ let start = left.span().start;
1757
+ let end = right.span().end;
1758
+ left = Expr::NullishCoalesce {
1759
+ left: Box::new(left),
1760
+ right: Box::new(right),
1761
+ span: Span { start, end },
1762
+ };
1763
+ }
1764
+ Ok(left)
1765
+ }
1766
+
1767
+ // Binary operators generated by macros to reduce duplication
1768
+ binary_single_op!(parse_or, parse_and, Or, BinOp::Or);
1769
+ binary_single_op!(parse_and, parse_bit_or, And, BinOp::And);
1770
+ binary_single_op!(parse_bit_or, parse_bit_xor, BitOr, BinOp::BitOr);
1771
+ binary_single_op!(parse_bit_xor, parse_bit_and, BitXor, BinOp::BitXor);
1772
+ binary_single_op!(parse_bit_and, parse_shift, BitAnd, BinOp::BitAnd);
1773
+
1774
+ binary_multi_op!(parse_shift, parse_equality, Shl => BinOp::Shl, Shr => BinOp::Shr, UShr => BinOp::UShr);
1775
+ binary_multi_op!(parse_equality, parse_comparison,
1776
+ StrictEq => BinOp::StrictEq, StrictNe => BinOp::StrictNe,
1777
+ Eq => BinOp::Eq, Ne => BinOp::Ne);
1778
+ binary_multi_op!(parse_comparison, parse_term,
1779
+ Lt => BinOp::Lt, Le => BinOp::Le, Gt => BinOp::Gt, Ge => BinOp::Ge, In => BinOp::In);
1780
+ binary_multi_op!(parse_term, parse_factor, Plus => BinOp::Add, Minus => BinOp::Sub);
1781
+ binary_multi_op!(parse_factor, parse_pow, Star => BinOp::Mul, Slash => BinOp::Div, Percent => BinOp::Mod);
1782
+
1783
+ fn parse_pow(&mut self) -> Result<Expr, String> {
1784
+ let left = self.parse_unary()?;
1785
+ if matches!(self.peek_kind(), Some(TokenKind::StarStar)) {
1786
+ self.advance();
1787
+ let right = self.parse_pow()?; // right-associative
1788
+ let start = left.span().start;
1789
+ let end = right.span().end;
1790
+ return Ok(Expr::Binary {
1791
+ left: Box::new(left),
1792
+ op: BinOp::Pow,
1793
+ right: Box::new(right),
1794
+ span: Span { start, end },
1795
+ });
1796
+ }
1797
+ Ok(left)
1798
+ }
1799
+
1800
+ fn parse_unary(&mut self) -> Result<Expr, String> {
1801
+ // Handle prefix ++/-- (consolidated)
1802
+ if let Some(is_inc) = match self.peek_kind() {
1803
+ Some(TokenKind::PlusPlus) => Some(true),
1804
+ Some(TokenKind::MinusMinus) => Some(false),
1805
+ _ => None,
1806
+ } {
1807
+ let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
1808
+ self.advance();
1809
+ let operand = self.parse_unary()?;
1810
+ if let Expr::Ident { name, span } = &operand {
1811
+ let name = Arc::clone(name);
1812
+ let span = Span {
1813
+ start: span_start,
1814
+ end: span.end,
1815
+ };
1816
+ return Ok(if is_inc {
1817
+ Expr::PrefixInc { name, span }
1818
+ } else {
1819
+ Expr::PrefixDec { name, span }
1820
+ });
1821
+ }
1822
+ return Err(format!(
1823
+ "Prefix {} requires an identifier",
1824
+ if is_inc { "++" } else { "--" }
1825
+ ));
1826
+ }
1827
+ let op = match self.peek_kind() {
1828
+ Some(TokenKind::Not) => UnaryOp::Not,
1829
+ Some(TokenKind::Minus) => UnaryOp::Neg,
1830
+ Some(TokenKind::Plus) => UnaryOp::Pos,
1831
+ Some(TokenKind::BitNot) => UnaryOp::BitNot,
1832
+ Some(TokenKind::Void) => UnaryOp::Void,
1833
+ Some(TokenKind::TypeOf) => {
1834
+ let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
1835
+ self.advance();
1836
+ let operand = self.parse_unary()?;
1837
+ let end = operand.span().end;
1838
+ return Ok(Expr::TypeOf {
1839
+ operand: Box::new(operand),
1840
+ span: Span {
1841
+ start: span_start,
1842
+ end,
1843
+ },
1844
+ });
1845
+ }
1846
+ Some(TokenKind::Delete) => {
1847
+ let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
1848
+ self.advance();
1849
+ let target = self.parse_unary()?;
1850
+ let end = target.span().end;
1851
+ return Ok(Expr::Delete {
1852
+ target: Box::new(target),
1853
+ span: Span {
1854
+ start: span_start,
1855
+ end,
1856
+ },
1857
+ });
1858
+ }
1859
+ Some(TokenKind::Await) => {
1860
+ let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
1861
+ self.advance();
1862
+ let operand = self.parse_unary()?;
1863
+ let end = operand.span().end;
1864
+ return Ok(Expr::Await {
1865
+ operand: Box::new(operand),
1866
+ span: Span {
1867
+ start: span_start,
1868
+ end,
1869
+ },
1870
+ });
1871
+ }
1872
+ _ => return self.parse_postfix(),
1873
+ };
1874
+ let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
1875
+ self.advance();
1876
+ let operand = self.parse_unary()?;
1877
+ let end = operand.span().end;
1878
+ Ok(Expr::Unary {
1879
+ op,
1880
+ operand: Box::new(operand),
1881
+ span: Span {
1882
+ start: span_start,
1883
+ end,
1884
+ },
1885
+ })
1886
+ }
1887
+
1888
+ /// Member chain (`.`, `?.`, `[]`) without consuming a call `(...)`.
1889
+ fn parse_member_expression_no_call(&mut self) -> Result<Expr, String> {
1890
+ let mut expr = self.parse_primary()?;
1891
+ while let Some(kind) = self.peek_kind() {
1892
+ match kind {
1893
+ TokenKind::Dot | TokenKind::OptionalChain => {
1894
+ let optional = kind == TokenKind::OptionalChain;
1895
+ self.advance();
1896
+ let prop_tok = self.expect_ident_or_type_member_name()?;
1897
+ let prop = prop_tok.literal.clone().ok_or("Expected property name")?;
1898
+ let prop_span = Span {
1899
+ start: prop_tok.span.start,
1900
+ end: prop_tok.span.end,
1901
+ };
1902
+ let start = expr.span().start;
1903
+ let end = self.peek().map(|x| x.span.start).unwrap_or(start);
1904
+ expr = Expr::Member {
1905
+ object: Box::new(expr),
1906
+ prop: MemberProp::Name {
1907
+ name: prop,
1908
+ span: prop_span,
1909
+ },
1910
+ optional,
1911
+ span: Span { start, end },
1912
+ };
1913
+ }
1914
+ TokenKind::LBracket => {
1915
+ self.advance();
1916
+ let index = self.parse_expr()?;
1917
+ self.expect(TokenKind::RBracket)?;
1918
+ let start = expr.span().start;
1919
+ let end = self.peek().map(|x| x.span.start).unwrap_or(start);
1920
+ expr = Expr::Index {
1921
+ object: Box::new(expr),
1922
+ index: Box::new(index),
1923
+ optional: false,
1924
+ span: Span { start, end },
1925
+ };
1926
+ }
1927
+ _ => break,
1928
+ }
1929
+ }
1930
+ Ok(expr)
1931
+ }
1932
+
1933
+ /// ECMAScript `NewExpression`: `new` chains, then member expression without call, optional `(...)`.
1934
+ fn parse_new_expression(&mut self) -> Result<Expr, String> {
1935
+ if matches!(self.peek_kind(), Some(TokenKind::New)) {
1936
+ let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
1937
+ self.advance();
1938
+ let callee = Box::new(self.parse_new_expression()?);
1939
+ let args = if matches!(self.peek_kind(), Some(TokenKind::LParen)) {
1940
+ self.advance();
1941
+ let mut args = Vec::new();
1942
+ while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
1943
+ if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
1944
+ self.advance();
1945
+ let arg_expr = self.parse_expr()?;
1946
+ args.push(CallArg::Spread(arg_expr));
1947
+ } else {
1948
+ let arg_expr = self.parse_expr()?;
1949
+ args.push(CallArg::Expr(arg_expr));
1950
+ }
1951
+ if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
1952
+ self.expect(TokenKind::Comma)?;
1953
+ }
1954
+ }
1955
+ self.expect(TokenKind::RParen)?;
1956
+ args
1957
+ } else {
1958
+ Vec::new()
1959
+ };
1960
+ let end = self
1961
+ .peek()
1962
+ .map(|x| x.span.start)
1963
+ .unwrap_or(callee.as_ref().span().end);
1964
+ Ok(Expr::New {
1965
+ callee,
1966
+ args,
1967
+ span: Span {
1968
+ start: span_start,
1969
+ end,
1970
+ },
1971
+ })
1972
+ } else {
1973
+ self.parse_member_expression_no_call()
1974
+ }
1975
+ }
1976
+
1977
+ fn parse_postfix(&mut self) -> Result<Expr, String> {
1978
+ let mut expr = if matches!(self.peek_kind(), Some(TokenKind::New)) {
1979
+ self.parse_new_expression()?
1980
+ } else {
1981
+ self.parse_primary()?
1982
+ };
1983
+ while let Some(kind) = self.peek_kind() {
1984
+ match kind {
1985
+ // `expr as Type` — a type assertion. Gradual + erased: consume the type and keep the
1986
+ // expression unchanged (no runtime effect; the checker is already lenient on what it
1987
+ // can't prove, so the assertion's only job — silencing a strict error — is moot).
1988
+ TokenKind::As => {
1989
+ self.advance(); // as
1990
+ self.parse_type_annotation()?;
1991
+ }
1992
+ TokenKind::LParen => {
1993
+ self.advance();
1994
+ let mut args = Vec::new();
1995
+ while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
1996
+ if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
1997
+ self.advance();
1998
+ let arg_expr = self.parse_expr()?;
1999
+ args.push(CallArg::Spread(arg_expr));
2000
+ } else {
2001
+ let arg_expr = self.parse_expr()?;
2002
+ args.push(CallArg::Expr(arg_expr));
2003
+ }
2004
+ if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
2005
+ self.expect(TokenKind::Comma)?;
2006
+ }
2007
+ }
2008
+ self.expect(TokenKind::RParen)?;
2009
+ let start = expr.span().start;
2010
+ let end = self.peek().map(|x| x.span.start).unwrap_or(start);
2011
+ expr = Expr::Call {
2012
+ callee: Box::new(expr),
2013
+ args,
2014
+ span: Span { start, end },
2015
+ };
2016
+ }
2017
+ TokenKind::Dot | TokenKind::OptionalChain => {
2018
+ let optional = kind == TokenKind::OptionalChain;
2019
+ self.advance();
2020
+ let prop_tok = self.expect_ident_or_type_member_name()?;
2021
+ let prop = prop_tok.literal.clone().ok_or("Expected property name")?;
2022
+ let prop_span = Span {
2023
+ start: prop_tok.span.start,
2024
+ end: prop_tok.span.end,
2025
+ };
2026
+ let start = expr.span().start;
2027
+ let end = self.peek().map(|x| x.span.start).unwrap_or(start);
2028
+ expr = Expr::Member {
2029
+ object: Box::new(expr),
2030
+ prop: MemberProp::Name {
2031
+ name: prop,
2032
+ span: prop_span,
2033
+ },
2034
+ optional,
2035
+ span: Span { start, end },
2036
+ };
2037
+ }
2038
+ TokenKind::LBracket => {
2039
+ self.advance();
2040
+ let index = self.parse_expr()?;
2041
+ self.expect(TokenKind::RBracket)?;
2042
+ let start = expr.span().start;
2043
+ let end = self.peek().map(|x| x.span.start).unwrap_or(start);
2044
+ expr = Expr::Index {
2045
+ object: Box::new(expr),
2046
+ index: Box::new(index),
2047
+ optional: false,
2048
+ span: Span { start, end },
2049
+ };
2050
+ }
2051
+ TokenKind::PlusPlus | TokenKind::MinusMinus => {
2052
+ if let Expr::Ident {
2053
+ name,
2054
+ span: ident_span,
2055
+ } = &expr
2056
+ {
2057
+ let name = Arc::clone(name);
2058
+ let is_inc = kind == TokenKind::PlusPlus;
2059
+ let tok = self.advance().ok_or("Unexpected EOF")?;
2060
+ let span = Span {
2061
+ start: ident_span.start,
2062
+ end: tok.span.end,
2063
+ };
2064
+ expr = if is_inc {
2065
+ Expr::PostfixInc { name, span }
2066
+ } else {
2067
+ Expr::PostfixDec { name, span }
2068
+ };
2069
+ } else {
2070
+ break;
2071
+ }
2072
+ }
2073
+ TokenKind::Question => {
2074
+ // Ternary is parsed in parse_conditional for correct precedence
2075
+ break;
2076
+ }
2077
+ _ => break,
2078
+ }
2079
+ }
2080
+ Ok(expr)
2081
+ }
2082
+
2083
+ fn parse_primary(&mut self) -> Result<Expr, String> {
2084
+ let t = self.advance().ok_or("Unexpected EOF")?;
2085
+ let span = Span {
2086
+ start: t.span.start,
2087
+ end: t.span.end,
2088
+ };
2089
+ match t.kind {
2090
+ TokenKind::Number => {
2091
+ let s = t.literal.as_ref().ok_or("Expected number")?;
2092
+ let n: f64 = s.parse().map_err(|_| format!("Invalid number: {}", s))?;
2093
+ Ok(Expr::Literal {
2094
+ value: Literal::Number(n),
2095
+ span,
2096
+ })
2097
+ }
2098
+ TokenKind::String => {
2099
+ let s = t.literal.clone().ok_or("Expected string")?;
2100
+ Ok(Expr::Literal {
2101
+ value: Literal::String(s),
2102
+ span,
2103
+ })
2104
+ }
2105
+ TokenKind::True => Ok(Expr::Literal {
2106
+ value: Literal::Bool(true),
2107
+ span,
2108
+ }),
2109
+ TokenKind::False => Ok(Expr::Literal {
2110
+ value: Literal::Bool(false),
2111
+ span,
2112
+ }),
2113
+ TokenKind::Null => Ok(Expr::Literal {
2114
+ value: Literal::Null,
2115
+ span,
2116
+ }),
2117
+ // `fn` / `type` as a value reference or single-param arrow head (issue #55).
2118
+ // `fn`/`type` function-expressions don't exist and statement-leading decls are
2119
+ // handled in parse_statement, so treating them as identifiers here is additive.
2120
+ TokenKind::Ident | TokenKind::Fn | TokenKind::Type => {
2121
+ let name = t.literal.clone().ok_or("Expected ident")?;
2122
+ // Check if this is a single-param arrow function: x => ...
2123
+ if matches!(self.peek_kind(), Some(TokenKind::Arrow)) {
2124
+ self.advance(); // consume =>
2125
+ let body = self.parse_arrow_body()?;
2126
+ let end = self.previous_span_end();
2127
+ return Ok(Expr::ArrowFunction {
2128
+ params: vec![FunParam::Simple(TypedParam {
2129
+ name: name.clone(),
2130
+ name_span: span,
2131
+ type_ann: None,
2132
+ default: None,
2133
+ })],
2134
+ body,
2135
+ span: Span {
2136
+ start: span.start,
2137
+ end,
2138
+ },
2139
+ });
2140
+ }
2141
+ Ok(Expr::Ident { name, span })
2142
+ }
2143
+ TokenKind::LParen => {
2144
+ // Check if this is an arrow function: (params) => ...
2145
+ if let Some(arrow_fn) = self.try_parse_arrow_function(&span)? {
2146
+ return Ok(arrow_fn);
2147
+ }
2148
+ // Otherwise it's a grouping expression
2149
+ let expr = self.parse_expr()?;
2150
+ self.expect(TokenKind::RParen)?;
2151
+ Ok(expr)
2152
+ }
2153
+ TokenKind::LBracket => {
2154
+ let mut elements = Vec::new();
2155
+ while !matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
2156
+ if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
2157
+ self.advance();
2158
+ let expr = self.parse_expr()?;
2159
+ elements.push(ArrayElement::Spread(expr));
2160
+ } else {
2161
+ let expr = self.parse_expr()?;
2162
+ elements.push(ArrayElement::Expr(expr));
2163
+ }
2164
+ if !matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
2165
+ self.expect(TokenKind::Comma)?;
2166
+ }
2167
+ }
2168
+ self.expect(TokenKind::RBracket)?;
2169
+ Ok(Expr::Array {
2170
+ elements,
2171
+ span: Span {
2172
+ start: span.start,
2173
+ end: self.peek().map(|x| x.span.end).unwrap_or(span.end),
2174
+ },
2175
+ })
2176
+ }
2177
+ TokenKind::Lt => {
2178
+ // JSX: <Tag or <>
2179
+ match self.peek_kind() {
2180
+ Some(TokenKind::Ident) => self.parse_jsx_element(span.start),
2181
+ Some(TokenKind::Gt) => self.parse_jsx_fragment(span.start),
2182
+ _ => Err(format!(
2183
+ "Invalid JSX: expected tag name or <> after <, got {:?}",
2184
+ self.peek_kind()
2185
+ )),
2186
+ }
2187
+ }
2188
+ TokenKind::LBrace => {
2189
+ let mut props = Vec::new();
2190
+ while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
2191
+ if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
2192
+ self.advance();
2193
+ let expr = self.parse_expr()?;
2194
+ props.push(ObjectProp::Spread(expr));
2195
+ } else {
2196
+ let key_tok = self.advance().ok_or("Expected object key")?;
2197
+ let (key, key_span, is_ident_key) = match key_tok.kind {
2198
+ TokenKind::Ident | TokenKind::Type | TokenKind::Declare => {
2199
+ let k = key_tok.literal.clone().ok_or("Expected key")?;
2200
+ let sp = Span {
2201
+ start: key_tok.span.start,
2202
+ end: key_tok.span.end,
2203
+ };
2204
+ (k, sp, true)
2205
+ }
2206
+ TokenKind::String => {
2207
+ let k = key_tok.literal.clone().ok_or("Expected string key")?;
2208
+ let sp = Span {
2209
+ start: key_tok.span.start,
2210
+ end: key_tok.span.end,
2211
+ };
2212
+ (k, sp, false)
2213
+ }
2214
+ _ => {
2215
+ return Err(format!(
2216
+ "Expected object key (ident or string), got {:?}",
2217
+ key_tok.kind
2218
+ ))
2219
+ }
2220
+ };
2221
+ let value = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
2222
+ self.expect(TokenKind::Colon)?;
2223
+ self.parse_expr()?
2224
+ } else {
2225
+ // ES6 shorthand: { key } => { key: key } (ident only, not string keys)
2226
+ if is_ident_key {
2227
+ Expr::Ident {
2228
+ name: key.clone(),
2229
+ span: key_span,
2230
+ }
2231
+ } else {
2232
+ return Err("String key in object literal requires explicit value (key: value)".to_string());
2233
+ }
2234
+ };
2235
+ props.push(ObjectProp::KeyValue(key, value));
2236
+ }
2237
+ if !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
2238
+ self.expect(TokenKind::Comma)?;
2239
+ }
2240
+ }
2241
+ self.expect(TokenKind::RBrace)?;
2242
+ Ok(Expr::Object {
2243
+ props,
2244
+ span: Span {
2245
+ start: span.start,
2246
+ end: self.peek().map(|x| x.span.end).unwrap_or(span.end),
2247
+ },
2248
+ })
2249
+ }
2250
+ TokenKind::TemplateNoSub => {
2251
+ // Simple template literal without interpolation
2252
+ Ok(Expr::TemplateLiteral {
2253
+ quasis: vec![t.literal.clone().unwrap_or_default()],
2254
+ exprs: vec![],
2255
+ span,
2256
+ })
2257
+ }
2258
+ TokenKind::TemplateHead => {
2259
+ // Template literal with interpolation: `text${
2260
+ let mut quasis = vec![t.literal.clone().unwrap_or_default()];
2261
+ let mut exprs = Vec::new();
2262
+
2263
+ loop {
2264
+ // Parse the expression inside ${}
2265
+ let expr = self.parse_expr()?;
2266
+ exprs.push(expr);
2267
+
2268
+ // Next token should be TemplateMiddle or TemplateTail
2269
+ let next = self.advance().ok_or("Unexpected EOF in template literal")?;
2270
+ match next.kind {
2271
+ TokenKind::TemplateTail => {
2272
+ quasis.push(next.literal.clone().unwrap_or_default());
2273
+ let end = self.previous_span_end();
2274
+ return Ok(Expr::TemplateLiteral {
2275
+ quasis,
2276
+ exprs,
2277
+ span: Span {
2278
+ start: span.start,
2279
+ end,
2280
+ },
2281
+ });
2282
+ }
2283
+ TokenKind::TemplateMiddle => {
2284
+ quasis.push(next.literal.clone().unwrap_or_default());
2285
+ // Continue parsing more expressions
2286
+ }
2287
+ _ => {
2288
+ return Err(format!(
2289
+ "Expected template continuation, got {:?}",
2290
+ next.kind
2291
+ ))
2292
+ }
2293
+ }
2294
+ }
2295
+ }
2296
+ // Include the token span (matching `expect`'s `at {:?}` convention) so error consumers
2297
+ // — notably the LSP's `parse_error_pos` — can place the diagnostic at the real location
2298
+ // instead of falling back to (0, 0) / top-of-file.
2299
+ _ => Err(format!("Unexpected token: {:?} at {:?}", t.kind, t.span)),
2300
+ }
2301
+ }
2302
+
2303
+ /// Try to parse an arrow function starting with '(' already consumed.
2304
+ /// Returns Some(Expr) if successful, None if it's not an arrow function.
2305
+ fn try_parse_arrow_function(&mut self, start_span: &Span) -> Result<Option<Expr>, String> {
2306
+ // Save position for backtracking
2307
+ let saved_pos = self.pos;
2308
+
2309
+ // Try to parse as arrow function params
2310
+ let mut params = Vec::new();
2311
+ let mut is_arrow = false;
2312
+
2313
+ // Check for empty params: () => ...
2314
+ if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
2315
+ self.advance(); // consume )
2316
+ if matches!(self.peek_kind(), Some(TokenKind::Arrow)) {
2317
+ self.advance(); // consume =>
2318
+ is_arrow = true;
2319
+ }
2320
+ } else {
2321
+ // Try to parse params: (x, y), ({ a }), ([a, b]), with optional types/defaults
2322
+ let mut params_ok = true;
2323
+ loop {
2324
+ if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
2325
+ break;
2326
+ }
2327
+ match self.parse_fun_param() {
2328
+ Ok(param) => params.push(param),
2329
+ Err(_) => {
2330
+ params_ok = false;
2331
+ break;
2332
+ }
2333
+ }
2334
+ if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
2335
+ self.advance();
2336
+ } else {
2337
+ break;
2338
+ }
2339
+ }
2340
+ if params_ok && matches!(self.peek_kind(), Some(TokenKind::RParen)) {
2341
+ self.advance(); // consume )
2342
+ if matches!(self.peek_kind(), Some(TokenKind::Arrow)) {
2343
+ self.advance(); // consume =>
2344
+ is_arrow = true;
2345
+ }
2346
+ }
2347
+ }
2348
+
2349
+ if !is_arrow {
2350
+ // Backtrack - it's not an arrow function
2351
+ self.pos = saved_pos;
2352
+ return Ok(None);
2353
+ }
2354
+
2355
+ let body = self.parse_arrow_body()?;
2356
+ let end = self.previous_span_end();
2357
+
2358
+ Ok(Some(Expr::ArrowFunction {
2359
+ params,
2360
+ body,
2361
+ span: Span {
2362
+ start: start_span.start,
2363
+ end,
2364
+ },
2365
+ }))
2366
+ }
2367
+
2368
+ /// Parse the body of an arrow function (either expression or block)
2369
+ fn parse_arrow_body(&mut self) -> Result<ArrowBody, String> {
2370
+ if matches!(self.peek_kind(), Some(TokenKind::LBrace)) {
2371
+ // Block body
2372
+ let block = self.parse_block()?;
2373
+ Ok(ArrowBody::Block(Box::new(block)))
2374
+ } else {
2375
+ // Expression body
2376
+ let expr = self.parse_expr()?;
2377
+ Ok(ArrowBody::Expr(Box::new(expr)))
2378
+ }
2379
+ }
2380
+
2381
+ fn previous_span_end(&self) -> (usize, usize) {
2382
+ if self.pos > 0 && self.pos <= self.tokens.len() {
2383
+ self.tokens[self.pos - 1].span.end
2384
+ } else {
2385
+ (1, 1)
2386
+ }
2387
+ }
2388
+
2389
+ /// Parse JSX element: <Tag props>children</Tag> or <Tag props />
2390
+ /// Caller has already consumed <.
2391
+ fn parse_jsx_element(&mut self, start: (usize, usize)) -> Result<Expr, String> {
2392
+ let tag_tok = self.expect(TokenKind::Ident)?;
2393
+ let tag = tag_tok.literal.clone().ok_or("Expected tag name")?;
2394
+
2395
+ let mut props = Vec::new();
2396
+ loop {
2397
+ match self.peek_kind() {
2398
+ Some(TokenKind::Slash) => {
2399
+ // Self-closing: />
2400
+ self.advance();
2401
+ self.expect(TokenKind::Gt)?;
2402
+ let end = self.previous_span_end();
2403
+ return Ok(Expr::JsxElement {
2404
+ tag,
2405
+ props,
2406
+ children: vec![],
2407
+ span: Span { start, end },
2408
+ });
2409
+ }
2410
+ Some(TokenKind::Gt) => break,
2411
+ Some(TokenKind::Spread) => {
2412
+ self.advance(); // ...
2413
+ let expr = self.parse_expr()?;
2414
+ self.expect(TokenKind::RBrace)?; // }
2415
+ props.push(JsxProp::Spread(expr));
2416
+ }
2417
+ // `type` is `TokenKind::Type` but valid as a JSX attr name; see docs/js-emit-philosophy.md.
2418
+ Some(TokenKind::Ident) | Some(TokenKind::Type) => {
2419
+ let name_tok = self.advance().unwrap();
2420
+ let name = name_tok.literal.clone().ok_or("Expected attr name")?;
2421
+ if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
2422
+ self.advance(); // =
2423
+ let value = if matches!(self.peek_kind(), Some(TokenKind::LBrace)) {
2424
+ self.advance(); // {
2425
+ let expr = self.parse_expr()?;
2426
+ self.expect(TokenKind::RBrace)?; // }
2427
+ JsxAttrValue::Expr(expr)
2428
+ } else {
2429
+ let s = self
2430
+ .expect(TokenKind::String)?
2431
+ .literal
2432
+ .clone()
2433
+ .ok_or("Expected string")?;
2434
+ JsxAttrValue::String(s)
2435
+ };
2436
+ props.push(JsxProp::Attr { name, value });
2437
+ } else {
2438
+ props.push(JsxProp::Attr {
2439
+ name,
2440
+ value: JsxAttrValue::ImplicitTrue,
2441
+ });
2442
+ }
2443
+ }
2444
+ _ => {
2445
+ return Err(format!(
2446
+ "Unexpected token in JSX props: {:?}",
2447
+ self.peek_kind()
2448
+ ))
2449
+ }
2450
+ }
2451
+ }
2452
+ self.advance(); // consume >
2453
+
2454
+ let children = self.parse_jsx_children(&tag)?;
2455
+ let end = self.previous_span_end();
2456
+ Ok(Expr::JsxElement {
2457
+ tag,
2458
+ props,
2459
+ children,
2460
+ span: Span { start, end },
2461
+ })
2462
+ }
2463
+
2464
+ fn token_as_jsx_text(kind: TokenKind) -> Option<&'static str> {
2465
+ use TokenKind::*;
2466
+ match kind {
2467
+ Not => Some("!"),
2468
+ Question => Some("?"),
2469
+ Dot => Some("."),
2470
+ Comma => Some(","),
2471
+ Colon => Some(":"),
2472
+ Semicolon => Some(";"),
2473
+ Plus => Some("+"),
2474
+ Minus => Some("-"),
2475
+ Star => Some("*"),
2476
+ Slash => Some("/"),
2477
+ Percent => Some("%"),
2478
+ Eq | Assign => Some("="),
2479
+ Gt => Some(">"),
2480
+ Le => Some("<="),
2481
+ Ge => Some(">="),
2482
+ Ne => Some("!="),
2483
+ StrictEq => Some("==="),
2484
+ StrictNe => Some("!=="),
2485
+ BitAnd => Some("&"),
2486
+ BitOr => Some("|"),
2487
+ BitXor => Some("^"),
2488
+ BitNot => Some("~"),
2489
+ And => Some("&&"),
2490
+ Or => Some("||"),
2491
+ LParen => Some("("),
2492
+ RParen => Some(")"),
2493
+ LBracket => Some("["),
2494
+ RBracket => Some("]"),
2495
+ PlusPlus => Some("++"),
2496
+ MinusMinus => Some("--"),
2497
+ StarStar => Some("**"),
2498
+ Arrow => Some("=>"),
2499
+ OptionalChain => Some("?."),
2500
+ NullishCoalesce => Some("??"),
2501
+ Shl => Some("<<"),
2502
+ Shr => Some(">>"),
2503
+ _ => None,
2504
+ }
2505
+ }
2506
+
2507
+ /// Merge text. Add space between words (Ident/Number/String); no space before/after punctuation.
2508
+ fn push_or_merge_text(&self, children: &mut Vec<JsxChild>, s: Arc<str>, is_punctuation: bool) {
2509
+ if let Some(JsxChild::Text(prev)) = children.last() {
2510
+ let sep = if is_punctuation { "" } else { " " };
2511
+ let merged = format!("{}{}{}", prev.as_ref(), sep, s.as_ref());
2512
+ let last = children.len() - 1;
2513
+ children[last] = JsxChild::Text(Arc::from(merged.as_str()));
2514
+ } else {
2515
+ children.push(JsxChild::Text(s));
2516
+ }
2517
+ }
2518
+
2519
+ /// Parse JSX children until </Tag> or </>
2520
+ fn parse_jsx_children(&mut self, close_tag: &str) -> Result<Vec<JsxChild>, String> {
2521
+ let mut children = Vec::new();
2522
+ loop {
2523
+ match self.peek_kind() {
2524
+ None => return Err("Unexpected EOF in JSX".to_string()),
2525
+ Some(TokenKind::Lt) => {
2526
+ let next = self.tokens.get(self.pos + 1);
2527
+ if let Some(t) = next {
2528
+ if t.kind == TokenKind::Slash {
2529
+ // </ closing tag
2530
+ self.advance(); // <
2531
+ self.advance(); // /
2532
+ let name = self
2533
+ .expect(TokenKind::Ident)?
2534
+ .literal
2535
+ .clone()
2536
+ .ok_or("Expected tag name")?;
2537
+ if name.as_ref() != close_tag {
2538
+ return Err(format!(
2539
+ "Mismatched JSX tag: expected </{}> got </{}>",
2540
+ close_tag, name
2541
+ ));
2542
+ }
2543
+ self.expect(TokenKind::Gt)?; // >
2544
+ return Ok(children);
2545
+ }
2546
+ if t.kind == TokenKind::Gt {
2547
+ return Err("Unexpected <> in JSX children".to_string());
2548
+ }
2549
+ }
2550
+ // <Tag - nested element
2551
+ let nested_start = self.peek().unwrap().span.start;
2552
+ self.advance(); // <
2553
+ let elem = self.parse_jsx_element(nested_start)?;
2554
+ children.push(JsxChild::Expr(elem));
2555
+ }
2556
+ Some(TokenKind::LBrace) => {
2557
+ self.advance(); // {
2558
+ let expr = self.parse_expr()?;
2559
+ self.expect(TokenKind::RBrace)?; // }
2560
+ children.push(JsxChild::Expr(expr));
2561
+ }
2562
+ Some(TokenKind::JsxText) => {
2563
+ let t = self.advance().unwrap();
2564
+ let s = t.literal.clone().unwrap_or_default();
2565
+ if !s.is_empty() {
2566
+ self.push_or_merge_text(&mut children, s, false);
2567
+ }
2568
+ }
2569
+ Some(TokenKind::String) => {
2570
+ let t = self.advance().unwrap();
2571
+ let s = t.literal.clone().unwrap_or_default();
2572
+ if !s.is_empty() {
2573
+ self.push_or_merge_text(&mut children, s, false);
2574
+ }
2575
+ }
2576
+ Some(TokenKind::Number) => {
2577
+ let t = self.advance().unwrap();
2578
+ let s = t.literal.clone().unwrap_or_default();
2579
+ if !s.is_empty() {
2580
+ self.push_or_merge_text(&mut children, s, false);
2581
+ }
2582
+ }
2583
+ Some(TokenKind::Ident) => {
2584
+ let t = self.advance().unwrap();
2585
+ let s = t.literal.clone().unwrap_or_default();
2586
+ if !s.is_empty() {
2587
+ self.push_or_merge_text(&mut children, s, false);
2588
+ }
2589
+ }
2590
+ Some(k) => {
2591
+ if let Some(s) = Self::token_as_jsx_text(k) {
2592
+ self.advance();
2593
+ self.push_or_merge_text(&mut children, Arc::from(s), true);
2594
+ } else {
2595
+ return Err(format!("Unexpected token in JSX children: {:?}", k));
2596
+ }
2597
+ }
2598
+ }
2599
+ }
2600
+ }
2601
+
2602
+ fn parse_jsx_fragment(&mut self, start: (usize, usize)) -> Result<Expr, String> {
2603
+ self.advance(); // consume >
2604
+ let mut children = Vec::new();
2605
+ loop {
2606
+ match self.peek_kind() {
2607
+ None => return Err("Unexpected EOF in JSX fragment".to_string()),
2608
+ Some(TokenKind::Lt) => {
2609
+ let next = self.tokens.get(self.pos + 1);
2610
+ if let Some(t) = next {
2611
+ if t.kind == TokenKind::Slash {
2612
+ // </
2613
+ let next2 = self.tokens.get(self.pos + 2);
2614
+ if let Some(t2) = next2 {
2615
+ if t2.kind == TokenKind::Gt {
2616
+ // </>
2617
+ self.advance();
2618
+ self.advance();
2619
+ self.advance();
2620
+ let end = self.previous_span_end();
2621
+ return Ok(Expr::JsxFragment {
2622
+ children,
2623
+ span: Span { start, end },
2624
+ });
2625
+ }
2626
+ }
2627
+ return Err("Expected </> to close fragment".to_string());
2628
+ }
2629
+ }
2630
+ let nested_start = self.peek().unwrap().span.start;
2631
+ self.advance(); // <
2632
+ let elem = self.parse_jsx_element(nested_start)?;
2633
+ children.push(JsxChild::Expr(elem));
2634
+ }
2635
+ Some(TokenKind::LBrace) => {
2636
+ self.advance();
2637
+ let expr = self.parse_expr()?;
2638
+ self.expect(TokenKind::RBrace)?;
2639
+ children.push(JsxChild::Expr(expr));
2640
+ }
2641
+ Some(TokenKind::JsxText) => {
2642
+ let t = self.advance().unwrap();
2643
+ let s = t.literal.clone().unwrap_or_default();
2644
+ if !s.is_empty() {
2645
+ self.push_or_merge_text(&mut children, s, false);
2646
+ }
2647
+ }
2648
+ Some(TokenKind::String) => {
2649
+ let t = self.advance().unwrap();
2650
+ let s = t.literal.clone().unwrap_or_default();
2651
+ if !s.is_empty() {
2652
+ self.push_or_merge_text(&mut children, s, false);
2653
+ }
2654
+ }
2655
+ Some(TokenKind::Number) => {
2656
+ let t = self.advance().unwrap();
2657
+ let s = t.literal.clone().unwrap_or_default();
2658
+ if !s.is_empty() {
2659
+ self.push_or_merge_text(&mut children, s, false);
2660
+ }
2661
+ }
2662
+ Some(TokenKind::Ident) => {
2663
+ let t = self.advance().unwrap();
2664
+ let s = t.literal.clone().unwrap_or_default();
2665
+ if !s.is_empty() {
2666
+ self.push_or_merge_text(&mut children, s, false);
2667
+ }
2668
+ }
2669
+ Some(k) => {
2670
+ if let Some(s) = Self::token_as_jsx_text(k) {
2671
+ self.advance();
2672
+ self.push_or_merge_text(&mut children, Arc::from(s), true);
2673
+ } else {
2674
+ return Err(format!("Unexpected token in JSX fragment: {:?}", k));
2675
+ }
2676
+ }
2677
+ }
2678
+ }
2679
+ }
2680
+ }
2681
+
2682
+ // Helper to get span from Expr. Uses trait so ExprSpan is referenced.
2683
+ trait ExprSpan {
2684
+ fn span(&self) -> Span;
2685
+ }
2686
+
2687
+ #[inline(always)]
2688
+ fn expr_span(e: &impl ExprSpan) -> Span {
2689
+ e.span()
2690
+ }
2691
+
2692
+ impl ExprSpan for Expr {
2693
+ fn span(&self) -> Span {
2694
+ match self {
2695
+ Expr::Literal { span, .. } => *span,
2696
+ Expr::Ident { span, .. } => *span,
2697
+ Expr::Binary { span, .. } => *span,
2698
+ Expr::Unary { span, .. } => *span,
2699
+ Expr::Call { span, .. } => *span,
2700
+ Expr::New { span, .. } => *span,
2701
+ Expr::Member { span, .. } => *span,
2702
+ Expr::Index { span, .. } => *span,
2703
+ Expr::Conditional { span, .. } => *span,
2704
+ Expr::NullishCoalesce { span, .. } => *span,
2705
+ Expr::Array { span, .. } => *span,
2706
+ Expr::Object { span, .. } => *span,
2707
+ Expr::Assign { span, .. } => *span,
2708
+ Expr::TypeOf { span, .. } => *span,
2709
+ Expr::Delete { span, .. } => *span,
2710
+ Expr::PostfixInc { span, .. } => *span,
2711
+ Expr::PostfixDec { span, .. } => *span,
2712
+ Expr::PrefixInc { span, .. } => *span,
2713
+ Expr::PrefixDec { span, .. } => *span,
2714
+ Expr::CompoundAssign { span, .. } => *span,
2715
+ Expr::LogicalAssign { span, .. } => *span,
2716
+ Expr::MemberAssign { span, .. } => *span,
2717
+ Expr::IndexAssign { span, .. } => *span,
2718
+ Expr::ArrowFunction { span, .. } => *span,
2719
+ Expr::TemplateLiteral { span, .. } => *span,
2720
+ Expr::Await { span, .. } => *span,
2721
+ Expr::JsxElement { span, .. } => *span,
2722
+ Expr::JsxFragment { span, .. } => *span,
2723
+ Expr::NativeModuleLoad { span, .. } => *span,
2724
+ }
2725
+ }
2726
+ }