@tishlang/tish-format 1.0.12 → 1.0.13

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