@tishlang/tish 1.6.0 → 1.8.0
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.
- package/Cargo.toml +2 -0
- package/README.md +2 -0
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/error.rs +2 -8
- package/crates/js_to_tish/src/transform/expr.rs +128 -137
- package/crates/js_to_tish/src/transform/stmt.rs +62 -32
- package/crates/tish/Cargo.toml +15 -5
- package/crates/tish/src/cargo_native_registry.rs +29 -0
- package/crates/tish/src/cli_help.rs +92 -39
- package/crates/tish/src/main.rs +172 -86
- package/crates/tish/src/repl_completion.rs +3 -3
- package/crates/tish/tests/cargo_example_compile.rs +4 -2
- package/crates/tish/tests/integration_test.rs +216 -54
- package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
- package/crates/tish/tests/shortcircuit.rs +20 -5
- package/crates/tish_ast/src/ast.rs +92 -23
- package/crates/tish_build_utils/Cargo.toml +4 -0
- package/crates/tish_build_utils/src/lib.rs +136 -8
- package/crates/tish_builtins/Cargo.toml +5 -1
- package/crates/tish_builtins/src/array.rs +65 -33
- package/crates/tish_builtins/src/construct.rs +34 -39
- package/crates/tish_builtins/src/globals.rs +42 -26
- package/crates/tish_builtins/src/helpers.rs +2 -1
- package/crates/tish_builtins/src/lib.rs +5 -5
- package/crates/tish_builtins/src/math.rs +5 -3
- package/crates/tish_builtins/src/object.rs +3 -2
- package/crates/tish_builtins/src/string.rs +144 -22
- package/crates/tish_bytecode/src/chunk.rs +0 -1
- package/crates/tish_bytecode/src/compiler.rs +173 -71
- package/crates/tish_bytecode/src/opcode.rs +24 -6
- package/crates/tish_bytecode/src/peephole.rs +2 -2
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/codegen.rs +1621 -453
- package/crates/tish_compile/src/infer.rs +75 -19
- package/crates/tish_compile/src/lib.rs +19 -8
- package/crates/tish_compile/src/resolve.rs +278 -137
- package/crates/tish_compile/src/types.rs +184 -24
- package/crates/tish_compile_js/Cargo.toml +1 -0
- package/crates/tish_compile_js/src/codegen.rs +181 -37
- package/crates/tish_compile_js/src/lib.rs +3 -1
- package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
- package/crates/tish_compiler_wasm/src/lib.rs +16 -13
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +69 -59
- package/crates/tish_core/Cargo.toml +8 -0
- package/crates/tish_core/src/json.rs +107 -56
- package/crates/tish_core/src/lib.rs +4 -2
- package/crates/tish_core/src/macros.rs +5 -5
- package/crates/tish_core/src/uri.rs +9 -6
- package/crates/tish_core/src/value.rs +145 -43
- package/crates/tish_core/src/vmref.rs +178 -0
- package/crates/tish_cranelift/src/link.rs +6 -9
- package/crates/tish_cranelift/src/lower.rs +14 -8
- package/crates/tish_eval/Cargo.toml +17 -2
- package/crates/tish_eval/src/eval.rs +474 -165
- package/crates/tish_eval/src/http.rs +61 -0
- package/crates/tish_eval/src/lib.rs +12 -8
- package/crates/tish_eval/src/natives.rs +136 -38
- package/crates/tish_eval/src/promise.rs +14 -8
- package/crates/tish_eval/src/timers.rs +28 -19
- package/crates/tish_eval/src/value.rs +17 -6
- package/crates/tish_eval/src/value_convert.rs +13 -5
- package/crates/tish_fmt/src/lib.rs +149 -43
- package/crates/tish_lexer/src/lib.rs +232 -63
- package/crates/tish_lexer/src/token.rs +10 -6
- package/crates/tish_llvm/src/lib.rs +17 -8
- package/crates/tish_lsp/Cargo.toml +4 -1
- package/crates/tish_lsp/README.md +1 -1
- package/crates/tish_lsp/src/builtin_goto.rs +261 -0
- package/crates/tish_lsp/src/import_goto.rs +549 -0
- package/crates/tish_lsp/src/main.rs +504 -106
- package/crates/tish_native/src/build.rs +4 -8
- package/crates/tish_native/src/lib.rs +54 -21
- package/crates/tish_opt/src/lib.rs +84 -52
- package/crates/tish_parser/src/lib.rs +45 -13
- package/crates/tish_parser/src/parser.rs +505 -130
- package/crates/tish_resolve/Cargo.toml +13 -0
- package/crates/tish_resolve/src/lib.rs +3436 -0
- package/crates/tish_resolve/src/pos.rs +133 -0
- package/crates/tish_runtime/Cargo.toml +68 -3
- package/crates/tish_runtime/src/http.rs +1136 -145
- package/crates/tish_runtime/src/http_fetch.rs +38 -27
- package/crates/tish_runtime/src/http_hyper.rs +418 -0
- package/crates/tish_runtime/src/http_prefork.rs +189 -0
- package/crates/tish_runtime/src/lib.rs +375 -189
- package/crates/tish_runtime/src/promise.rs +199 -40
- package/crates/tish_runtime/src/promise_io.rs +2 -1
- package/crates/tish_runtime/src/timers.rs +37 -1
- package/crates/tish_runtime/src/ws.rs +65 -42
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
- package/crates/tish_ui/src/jsx.rs +317 -27
- package/crates/tish_ui/src/lib.rs +5 -2
- package/crates/tish_ui/src/runtime/hooks.rs +406 -45
- package/crates/tish_ui/src/runtime/mod.rs +36 -9
- package/crates/tish_vm/Cargo.toml +15 -5
- package/crates/tish_vm/src/vm.rs +725 -281
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +11 -4
- package/crates/tish_wasm/src/lib.rs +55 -42
- package/crates/tish_wasm_runtime/Cargo.toml +2 -1
- package/crates/tish_wasm_runtime/src/lib.rs +1 -1
- package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +120 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +350 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
- package/justfile +8 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish +0 -0
- package/platform/darwin-x64/tish +0 -0
- package/platform/linux-arm64/tish +0 -0
- package/platform/linux-x64/tish +0 -0
- package/platform/win32-x64/tish.exe +0 -0
|
@@ -52,7 +52,7 @@ macro_rules! binary_multi_op {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
use tishlang_ast::{
|
|
55
|
-
|
|
55
|
+
ArrayElement, ArrowBody, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern,
|
|
56
56
|
DestructProp, ExportDeclaration, Expr, FunParam, ImportSpecifier, JsxAttrValue, JsxChild,
|
|
57
57
|
JsxProp, Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Span, Statement,
|
|
58
58
|
TypeAnnotation, TypedParam, UnaryOp,
|
|
@@ -86,19 +86,21 @@ impl<'a> Parser<'a> {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
fn expect(&mut self, kind: TokenKind) -> Result<&Token, String> {
|
|
89
|
-
let t = self
|
|
89
|
+
let t = self
|
|
90
|
+
.advance()
|
|
91
|
+
.ok_or_else(|| format!("Expected {:?}, got EOF", kind))?;
|
|
90
92
|
if t.kind == kind {
|
|
91
93
|
Ok(t)
|
|
92
94
|
} else {
|
|
93
|
-
Err(format!(
|
|
95
|
+
Err(format!(
|
|
96
|
+
"Expected {:?}, got {:?} at {:?}",
|
|
97
|
+
kind, t.kind, t.span
|
|
98
|
+
))
|
|
94
99
|
}
|
|
95
100
|
}
|
|
96
101
|
|
|
97
102
|
fn span_end(&self, start: (usize, usize)) -> Span {
|
|
98
|
-
let end = self
|
|
99
|
-
.peek()
|
|
100
|
-
.map(|t| t.span.start)
|
|
101
|
-
.unwrap_or(start);
|
|
103
|
+
let end = self.peek().map(|t| t.span.start).unwrap_or(start);
|
|
102
104
|
Span { start, end }
|
|
103
105
|
}
|
|
104
106
|
|
|
@@ -157,6 +159,8 @@ impl<'a> Parser<'a> {
|
|
|
157
159
|
}
|
|
158
160
|
TokenKind::Import => self.parse_import()?,
|
|
159
161
|
TokenKind::Export => self.parse_export()?,
|
|
162
|
+
TokenKind::Type => self.parse_type_alias()?,
|
|
163
|
+
TokenKind::Declare => self.parse_declare()?,
|
|
160
164
|
_ => {
|
|
161
165
|
let expr = self.parse_expr()?;
|
|
162
166
|
let span_end = expr.span().end;
|
|
@@ -207,18 +211,33 @@ impl<'a> Parser<'a> {
|
|
|
207
211
|
statements.push(self.parse_statement()?);
|
|
208
212
|
}
|
|
209
213
|
|
|
210
|
-
if matches!(
|
|
214
|
+
if matches!(
|
|
215
|
+
self.peek_kind(),
|
|
216
|
+
Some(TokenKind::RBrace | TokenKind::Dedent)
|
|
217
|
+
) {
|
|
211
218
|
self.advance();
|
|
212
219
|
}
|
|
213
220
|
|
|
221
|
+
let peek_end = self.peek().map(|x| x.span.end);
|
|
222
|
+
let last_end = statements.last().map(|s| s.span().end);
|
|
223
|
+
let end = match (peek_end, last_end) {
|
|
224
|
+
(Some(p), Some(l)) => {
|
|
225
|
+
if p.0 > l.0 || (p.0 == l.0 && p.1 > l.1) {
|
|
226
|
+
p
|
|
227
|
+
} else {
|
|
228
|
+
l
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
(Some(p), None) => p,
|
|
232
|
+
(None, Some(l)) => l,
|
|
233
|
+
(None, None) => span_start,
|
|
234
|
+
};
|
|
235
|
+
|
|
214
236
|
Ok(Statement::Block {
|
|
215
237
|
statements,
|
|
216
238
|
span: Span {
|
|
217
239
|
start: span_start,
|
|
218
|
-
end
|
|
219
|
-
.peek()
|
|
220
|
-
.map(|x| x.span.end)
|
|
221
|
-
.unwrap_or(span_start),
|
|
240
|
+
end,
|
|
222
241
|
},
|
|
223
242
|
})
|
|
224
243
|
}
|
|
@@ -229,9 +248,12 @@ impl<'a> Parser<'a> {
|
|
|
229
248
|
} else {
|
|
230
249
|
self.expect(TokenKind::Const)?.span.start
|
|
231
250
|
};
|
|
232
|
-
|
|
251
|
+
|
|
233
252
|
// Check for destructuring pattern
|
|
234
|
-
if matches!(
|
|
253
|
+
if matches!(
|
|
254
|
+
self.peek_kind(),
|
|
255
|
+
Some(TokenKind::LBracket) | Some(TokenKind::LBrace)
|
|
256
|
+
) {
|
|
235
257
|
let pattern = self.parse_destruct_pattern()?;
|
|
236
258
|
self.expect(TokenKind::Assign)?;
|
|
237
259
|
let init = self.parse_expr()?;
|
|
@@ -242,9 +264,13 @@ impl<'a> Parser<'a> {
|
|
|
242
264
|
span: self.span_end(span_start),
|
|
243
265
|
});
|
|
244
266
|
}
|
|
245
|
-
|
|
246
|
-
let
|
|
247
|
-
|
|
267
|
+
|
|
268
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
269
|
+
let name_span = Span {
|
|
270
|
+
start: name_tok.span.start,
|
|
271
|
+
end: name_tok.span.end,
|
|
272
|
+
};
|
|
273
|
+
let name = name_tok
|
|
248
274
|
.literal
|
|
249
275
|
.clone()
|
|
250
276
|
.ok_or("Expected identifier")?;
|
|
@@ -265,13 +291,14 @@ impl<'a> Parser<'a> {
|
|
|
265
291
|
};
|
|
266
292
|
Ok(Statement::VarDecl {
|
|
267
293
|
name,
|
|
294
|
+
name_span,
|
|
268
295
|
mutable,
|
|
269
296
|
type_ann,
|
|
270
297
|
init,
|
|
271
298
|
span: self.span_end(span_start),
|
|
272
299
|
})
|
|
273
300
|
}
|
|
274
|
-
|
|
301
|
+
|
|
275
302
|
fn parse_destruct_pattern(&mut self) -> Result<DestructPattern, String> {
|
|
276
303
|
match self.peek_kind() {
|
|
277
304
|
Some(TokenKind::LBracket) => self.parse_array_destruct_pattern(),
|
|
@@ -279,11 +306,11 @@ impl<'a> Parser<'a> {
|
|
|
279
306
|
_ => Err("Expected destructuring pattern".to_string()),
|
|
280
307
|
}
|
|
281
308
|
}
|
|
282
|
-
|
|
309
|
+
|
|
283
310
|
fn parse_array_destruct_pattern(&mut self) -> Result<DestructPattern, String> {
|
|
284
311
|
self.expect(TokenKind::LBracket)?;
|
|
285
312
|
let mut elements = Vec::new();
|
|
286
|
-
|
|
313
|
+
|
|
287
314
|
while !matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
|
|
288
315
|
// Handle holes (elision): [a, , b]
|
|
289
316
|
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
@@ -291,15 +318,23 @@ impl<'a> Parser<'a> {
|
|
|
291
318
|
self.advance();
|
|
292
319
|
continue;
|
|
293
320
|
}
|
|
294
|
-
|
|
321
|
+
|
|
295
322
|
// Rest element: ...rest
|
|
296
323
|
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
297
324
|
self.advance();
|
|
298
|
-
let
|
|
299
|
-
|
|
325
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
326
|
+
let name_span = Span {
|
|
327
|
+
start: name_tok.span.start,
|
|
328
|
+
end: name_tok.span.end,
|
|
329
|
+
};
|
|
330
|
+
let name = name_tok
|
|
331
|
+
.literal
|
|
332
|
+
.clone()
|
|
333
|
+
.ok_or("Expected identifier")?;
|
|
334
|
+
elements.push(Some(DestructElement::Rest(name, name_span)));
|
|
300
335
|
break;
|
|
301
336
|
}
|
|
302
|
-
|
|
337
|
+
|
|
303
338
|
// Nested pattern or identifier
|
|
304
339
|
let elem = match self.peek_kind() {
|
|
305
340
|
Some(TokenKind::LBracket) | Some(TokenKind::LBrace) => {
|
|
@@ -307,31 +342,47 @@ impl<'a> Parser<'a> {
|
|
|
307
342
|
DestructElement::Pattern(Box::new(nested))
|
|
308
343
|
}
|
|
309
344
|
Some(TokenKind::Ident) => {
|
|
310
|
-
let
|
|
311
|
-
|
|
345
|
+
let name_tok = self.advance().ok_or("Unexpected EOF")?;
|
|
346
|
+
let name_span = Span {
|
|
347
|
+
start: name_tok.span.start,
|
|
348
|
+
end: name_tok.span.end,
|
|
349
|
+
};
|
|
350
|
+
let name = name_tok
|
|
351
|
+
.literal
|
|
352
|
+
.clone()
|
|
353
|
+
.ok_or("Expected identifier")?;
|
|
354
|
+
DestructElement::Ident(name, name_span)
|
|
312
355
|
}
|
|
313
356
|
_ => return Err("Expected identifier or pattern in destructuring".to_string()),
|
|
314
357
|
};
|
|
315
358
|
elements.push(Some(elem));
|
|
316
|
-
|
|
359
|
+
|
|
317
360
|
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
318
361
|
self.advance();
|
|
319
362
|
} else {
|
|
320
363
|
break;
|
|
321
364
|
}
|
|
322
365
|
}
|
|
323
|
-
|
|
366
|
+
|
|
324
367
|
self.expect(TokenKind::RBracket)?;
|
|
325
368
|
Ok(DestructPattern::Array(elements))
|
|
326
369
|
}
|
|
327
|
-
|
|
370
|
+
|
|
328
371
|
fn parse_object_destruct_pattern(&mut self) -> Result<DestructPattern, String> {
|
|
329
372
|
self.expect(TokenKind::LBrace)?;
|
|
330
373
|
let mut props = Vec::new();
|
|
331
|
-
|
|
374
|
+
|
|
332
375
|
while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
333
|
-
let
|
|
334
|
-
|
|
376
|
+
let key_tok = self.expect(TokenKind::Ident)?;
|
|
377
|
+
let key_span = Span {
|
|
378
|
+
start: key_tok.span.start,
|
|
379
|
+
end: key_tok.span.end,
|
|
380
|
+
};
|
|
381
|
+
let key = key_tok
|
|
382
|
+
.literal
|
|
383
|
+
.clone()
|
|
384
|
+
.ok_or("Expected identifier")?;
|
|
385
|
+
|
|
335
386
|
let value = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
336
387
|
self.advance();
|
|
337
388
|
// Could be renamed binding or nested pattern
|
|
@@ -341,25 +392,33 @@ impl<'a> Parser<'a> {
|
|
|
341
392
|
DestructElement::Pattern(Box::new(nested))
|
|
342
393
|
}
|
|
343
394
|
Some(TokenKind::Ident) => {
|
|
344
|
-
let
|
|
345
|
-
|
|
395
|
+
let name_tok = self.advance().ok_or("Unexpected EOF")?;
|
|
396
|
+
let name_span = Span {
|
|
397
|
+
start: name_tok.span.start,
|
|
398
|
+
end: name_tok.span.end,
|
|
399
|
+
};
|
|
400
|
+
let name = name_tok
|
|
401
|
+
.literal
|
|
402
|
+
.clone()
|
|
403
|
+
.ok_or("Expected identifier")?;
|
|
404
|
+
DestructElement::Ident(name, name_span)
|
|
346
405
|
}
|
|
347
406
|
_ => return Err("Expected identifier or pattern after ':'".to_string()),
|
|
348
407
|
}
|
|
349
408
|
} else {
|
|
350
409
|
// Shorthand: { key } is equivalent to { key: key }
|
|
351
|
-
DestructElement::Ident(key.clone())
|
|
410
|
+
DestructElement::Ident(key.clone(), key_span)
|
|
352
411
|
};
|
|
353
|
-
|
|
412
|
+
|
|
354
413
|
props.push(DestructProp { key, value });
|
|
355
|
-
|
|
414
|
+
|
|
356
415
|
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
357
416
|
self.advance();
|
|
358
417
|
} else {
|
|
359
418
|
break;
|
|
360
419
|
}
|
|
361
420
|
}
|
|
362
|
-
|
|
421
|
+
|
|
363
422
|
self.expect(TokenKind::RBrace)?;
|
|
364
423
|
Ok(DestructPattern::Object(props))
|
|
365
424
|
}
|
|
@@ -389,8 +448,12 @@ impl<'a> Parser<'a> {
|
|
|
389
448
|
default,
|
|
390
449
|
});
|
|
391
450
|
}
|
|
392
|
-
let
|
|
393
|
-
|
|
451
|
+
let param_tok = self.expect(TokenKind::Ident)?;
|
|
452
|
+
let name_span = Span {
|
|
453
|
+
start: param_tok.span.start,
|
|
454
|
+
end: param_tok.span.end,
|
|
455
|
+
};
|
|
456
|
+
let param_name = param_tok
|
|
394
457
|
.literal
|
|
395
458
|
.clone()
|
|
396
459
|
.ok_or("Expected param name")?;
|
|
@@ -408,22 +471,23 @@ impl<'a> Parser<'a> {
|
|
|
408
471
|
};
|
|
409
472
|
Ok(FunParam::Simple(TypedParam {
|
|
410
473
|
name: param_name,
|
|
474
|
+
name_span,
|
|
411
475
|
type_ann,
|
|
412
476
|
default,
|
|
413
477
|
}))
|
|
414
478
|
}
|
|
415
|
-
|
|
479
|
+
|
|
416
480
|
/// Parse a type annotation (number, string, T[], {a: T}, etc.)
|
|
417
481
|
fn parse_type_annotation(&mut self) -> Result<TypeAnnotation, String> {
|
|
418
482
|
let base = self.parse_type_primary()?;
|
|
419
|
-
|
|
483
|
+
|
|
420
484
|
// Check for array suffix: T[]
|
|
421
485
|
if matches!(self.peek_kind(), Some(TokenKind::LBracket)) {
|
|
422
486
|
self.advance(); // [
|
|
423
487
|
self.expect(TokenKind::RBracket)?; // ]
|
|
424
488
|
return Ok(TypeAnnotation::Array(Box::new(base)));
|
|
425
489
|
}
|
|
426
|
-
|
|
490
|
+
|
|
427
491
|
// Check for union: T | U
|
|
428
492
|
if matches!(self.peek_kind(), Some(TokenKind::BitOr)) {
|
|
429
493
|
let mut types = vec![base];
|
|
@@ -433,10 +497,10 @@ impl<'a> Parser<'a> {
|
|
|
433
497
|
}
|
|
434
498
|
return Ok(TypeAnnotation::Union(types));
|
|
435
499
|
}
|
|
436
|
-
|
|
500
|
+
|
|
437
501
|
Ok(base)
|
|
438
502
|
}
|
|
439
|
-
|
|
503
|
+
|
|
440
504
|
/// Parse a primary type (identifier, object, or function type)
|
|
441
505
|
fn parse_type_primary(&mut self) -> Result<TypeAnnotation, String> {
|
|
442
506
|
match self.peek_kind() {
|
|
@@ -445,6 +509,11 @@ impl<'a> Parser<'a> {
|
|
|
445
509
|
let name = tok.literal.clone().ok_or("Expected type name")?;
|
|
446
510
|
Ok(TypeAnnotation::Simple(name))
|
|
447
511
|
}
|
|
512
|
+
Some(TokenKind::Type | TokenKind::Declare) => {
|
|
513
|
+
let tok = self.advance().ok_or("Expected type name")?;
|
|
514
|
+
let name = tok.literal.clone().ok_or("Expected type name")?;
|
|
515
|
+
Ok(TypeAnnotation::Simple(name))
|
|
516
|
+
}
|
|
448
517
|
// Handle keywords that can be type names
|
|
449
518
|
Some(TokenKind::Null) => {
|
|
450
519
|
self.advance();
|
|
@@ -459,14 +528,22 @@ impl<'a> Parser<'a> {
|
|
|
459
528
|
self.advance(); // {
|
|
460
529
|
let mut props = Vec::new();
|
|
461
530
|
while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
462
|
-
let key = self
|
|
531
|
+
let key = self
|
|
532
|
+
.expect(TokenKind::Ident)?
|
|
533
|
+
.literal
|
|
534
|
+
.clone()
|
|
463
535
|
.ok_or("Expected property name")?;
|
|
464
536
|
self.expect(TokenKind::Colon)?;
|
|
465
537
|
let typ = self.parse_type_annotation()?;
|
|
466
538
|
props.push((key, typ));
|
|
467
539
|
if !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
468
|
-
//
|
|
469
|
-
|
|
540
|
+
// Accept `,` or `;` between items (TypeScript-style
|
|
541
|
+
// semicolons are common in interface/object type
|
|
542
|
+
// declarations); also tolerate a trailing separator.
|
|
543
|
+
if matches!(
|
|
544
|
+
self.peek_kind(),
|
|
545
|
+
Some(TokenKind::Comma) | Some(TokenKind::Semicolon)
|
|
546
|
+
) {
|
|
470
547
|
self.advance();
|
|
471
548
|
}
|
|
472
549
|
}
|
|
@@ -485,9 +562,7 @@ impl<'a> Parser<'a> {
|
|
|
485
562
|
}
|
|
486
563
|
}
|
|
487
564
|
self.expect(TokenKind::RParen)?;
|
|
488
|
-
|
|
489
|
-
self.expect(TokenKind::Assign)?; // =
|
|
490
|
-
self.expect(TokenKind::Gt)?; // > (forms =>)
|
|
565
|
+
self.expect(TokenKind::Arrow)?;
|
|
491
566
|
let returns = self.parse_type_annotation()?;
|
|
492
567
|
Ok(TypeAnnotation::Function {
|
|
493
568
|
params,
|
|
@@ -500,8 +575,12 @@ impl<'a> Parser<'a> {
|
|
|
500
575
|
|
|
501
576
|
fn parse_fun_decl(&mut self, async_: bool) -> Result<Statement, String> {
|
|
502
577
|
let span_start = self.expect(TokenKind::Fn)?.span.start;
|
|
503
|
-
let
|
|
504
|
-
|
|
578
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
579
|
+
let name_span = Span {
|
|
580
|
+
start: name_tok.span.start,
|
|
581
|
+
end: name_tok.span.end,
|
|
582
|
+
};
|
|
583
|
+
let name = name_tok
|
|
505
584
|
.literal
|
|
506
585
|
.clone()
|
|
507
586
|
.ok_or("Expected function name")?;
|
|
@@ -511,8 +590,12 @@ impl<'a> Parser<'a> {
|
|
|
511
590
|
while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
512
591
|
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
513
592
|
self.advance();
|
|
514
|
-
let
|
|
515
|
-
|
|
593
|
+
let rest_tok = self.expect(TokenKind::Ident)?;
|
|
594
|
+
let rest_name_span = Span {
|
|
595
|
+
start: rest_tok.span.start,
|
|
596
|
+
end: rest_tok.span.end,
|
|
597
|
+
};
|
|
598
|
+
let param_name = rest_tok
|
|
516
599
|
.literal
|
|
517
600
|
.clone()
|
|
518
601
|
.ok_or("Expected rest param name")?;
|
|
@@ -523,7 +606,12 @@ impl<'a> Parser<'a> {
|
|
|
523
606
|
} else {
|
|
524
607
|
None
|
|
525
608
|
};
|
|
526
|
-
rest_param = Some(TypedParam {
|
|
609
|
+
rest_param = Some(TypedParam {
|
|
610
|
+
name: param_name,
|
|
611
|
+
name_span: rest_name_span,
|
|
612
|
+
type_ann,
|
|
613
|
+
default: None,
|
|
614
|
+
});
|
|
527
615
|
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
528
616
|
return Err("Rest parameter must be last".to_string());
|
|
529
617
|
}
|
|
@@ -535,7 +623,7 @@ impl<'a> Parser<'a> {
|
|
|
535
623
|
}
|
|
536
624
|
}
|
|
537
625
|
self.expect(TokenKind::RParen)?;
|
|
538
|
-
|
|
626
|
+
|
|
539
627
|
// Optional return type: `: Type`
|
|
540
628
|
let return_type = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
541
629
|
self.advance();
|
|
@@ -559,13 +647,182 @@ impl<'a> Parser<'a> {
|
|
|
559
647
|
Box::new(self.parse_block()?)
|
|
560
648
|
};
|
|
561
649
|
|
|
650
|
+
// Span must cover the whole declaration through the body. `peek().start` alone can sit on
|
|
651
|
+
// the opening `{` (same as `span_start` at EOF) or otherwise truncate before inner spans.
|
|
652
|
+
let peek_start = self.peek().map(|t| t.span.start).unwrap_or(span_start);
|
|
653
|
+
let body_end = body.as_ref().span().end;
|
|
654
|
+
let end = if peek_start.0 > body_end.0
|
|
655
|
+
|| (peek_start.0 == body_end.0 && peek_start.1 > body_end.1)
|
|
656
|
+
{
|
|
657
|
+
peek_start
|
|
658
|
+
} else {
|
|
659
|
+
body_end
|
|
660
|
+
};
|
|
661
|
+
|
|
562
662
|
Ok(Statement::FunDecl {
|
|
563
663
|
async_,
|
|
564
664
|
name,
|
|
665
|
+
name_span,
|
|
565
666
|
params,
|
|
566
667
|
rest_param,
|
|
567
668
|
return_type,
|
|
568
669
|
body,
|
|
670
|
+
span: Span {
|
|
671
|
+
start: span_start,
|
|
672
|
+
end,
|
|
673
|
+
},
|
|
674
|
+
})
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
fn parse_type_alias(&mut self) -> Result<Statement, String> {
|
|
678
|
+
let span_start = self.expect(TokenKind::Type)?.span.start;
|
|
679
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
680
|
+
let name_span = Span {
|
|
681
|
+
start: name_tok.span.start,
|
|
682
|
+
end: name_tok.span.end,
|
|
683
|
+
};
|
|
684
|
+
let name = name_tok
|
|
685
|
+
.literal
|
|
686
|
+
.clone()
|
|
687
|
+
.ok_or("Expected type alias name")?;
|
|
688
|
+
self.expect(TokenKind::Assign)?;
|
|
689
|
+
let ty = self.parse_type_annotation()?;
|
|
690
|
+
Ok(Statement::TypeAlias {
|
|
691
|
+
name,
|
|
692
|
+
name_span,
|
|
693
|
+
ty,
|
|
694
|
+
span: self.span_end(span_start),
|
|
695
|
+
})
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
fn parse_declare(&mut self) -> Result<Statement, String> {
|
|
699
|
+
let span_start = self.expect(TokenKind::Declare)?.span.start;
|
|
700
|
+
let async_ = if matches!(self.peek_kind(), Some(TokenKind::Async)) {
|
|
701
|
+
self.advance();
|
|
702
|
+
true
|
|
703
|
+
} else {
|
|
704
|
+
false
|
|
705
|
+
};
|
|
706
|
+
if matches!(self.peek_kind(), Some(TokenKind::Fn)) {
|
|
707
|
+
return self.parse_declare_fun(span_start, async_);
|
|
708
|
+
}
|
|
709
|
+
let const_ = match self.peek_kind() {
|
|
710
|
+
Some(TokenKind::Let) => {
|
|
711
|
+
self.advance();
|
|
712
|
+
false
|
|
713
|
+
}
|
|
714
|
+
Some(TokenKind::Const) => {
|
|
715
|
+
self.advance();
|
|
716
|
+
true
|
|
717
|
+
}
|
|
718
|
+
_ => {
|
|
719
|
+
return Err(
|
|
720
|
+
"Expected `let`, `const`, `async fn`, or `fn` after `declare`".to_string(),
|
|
721
|
+
);
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
725
|
+
let name_span = Span {
|
|
726
|
+
start: name_tok.span.start,
|
|
727
|
+
end: name_tok.span.end,
|
|
728
|
+
};
|
|
729
|
+
let name = name_tok
|
|
730
|
+
.literal
|
|
731
|
+
.clone()
|
|
732
|
+
.ok_or("Expected identifier")?;
|
|
733
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
734
|
+
self.advance();
|
|
735
|
+
Some(self.parse_type_annotation()?)
|
|
736
|
+
} else {
|
|
737
|
+
None
|
|
738
|
+
};
|
|
739
|
+
if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
740
|
+
return Err("`declare` cannot have an initializer".to_string());
|
|
741
|
+
}
|
|
742
|
+
Ok(Statement::DeclareVar {
|
|
743
|
+
name,
|
|
744
|
+
name_span,
|
|
745
|
+
type_ann,
|
|
746
|
+
const_,
|
|
747
|
+
span: self.span_end(span_start),
|
|
748
|
+
})
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
fn parse_declare_fun(
|
|
752
|
+
&mut self,
|
|
753
|
+
span_start: (usize, usize),
|
|
754
|
+
async_: bool,
|
|
755
|
+
) -> Result<Statement, String> {
|
|
756
|
+
self.expect(TokenKind::Fn)?;
|
|
757
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
758
|
+
let name_span = Span {
|
|
759
|
+
start: name_tok.span.start,
|
|
760
|
+
end: name_tok.span.end,
|
|
761
|
+
};
|
|
762
|
+
let name = name_tok
|
|
763
|
+
.literal
|
|
764
|
+
.clone()
|
|
765
|
+
.ok_or("Expected function name")?;
|
|
766
|
+
self.expect(TokenKind::LParen)?;
|
|
767
|
+
let mut params = Vec::with_capacity(4);
|
|
768
|
+
let mut rest_param = None;
|
|
769
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
770
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
771
|
+
self.advance();
|
|
772
|
+
let rest_tok = self.expect(TokenKind::Ident)?;
|
|
773
|
+
let rest_name_span = Span {
|
|
774
|
+
start: rest_tok.span.start,
|
|
775
|
+
end: rest_tok.span.end,
|
|
776
|
+
};
|
|
777
|
+
let param_name = rest_tok
|
|
778
|
+
.literal
|
|
779
|
+
.clone()
|
|
780
|
+
.ok_or("Expected rest param name")?;
|
|
781
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
782
|
+
self.advance();
|
|
783
|
+
Some(self.parse_type_annotation()?)
|
|
784
|
+
} else {
|
|
785
|
+
None
|
|
786
|
+
};
|
|
787
|
+
rest_param = Some(TypedParam {
|
|
788
|
+
name: param_name,
|
|
789
|
+
name_span: rest_name_span,
|
|
790
|
+
type_ann,
|
|
791
|
+
default: None,
|
|
792
|
+
});
|
|
793
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
794
|
+
return Err("Rest parameter must be last".to_string());
|
|
795
|
+
}
|
|
796
|
+
break;
|
|
797
|
+
}
|
|
798
|
+
params.push(self.parse_fun_param()?);
|
|
799
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
800
|
+
self.expect(TokenKind::Comma)?;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
self.expect(TokenKind::RParen)?;
|
|
804
|
+
let return_type = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
805
|
+
self.advance();
|
|
806
|
+
Some(self.parse_type_annotation()?)
|
|
807
|
+
} else {
|
|
808
|
+
None
|
|
809
|
+
};
|
|
810
|
+
if matches!(
|
|
811
|
+
self.peek_kind(),
|
|
812
|
+
Some(TokenKind::Assign | TokenKind::LBrace | TokenKind::Indent)
|
|
813
|
+
) {
|
|
814
|
+
return Err("`declare function` must not have a body".to_string());
|
|
815
|
+
}
|
|
816
|
+
if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
817
|
+
self.advance();
|
|
818
|
+
}
|
|
819
|
+
Ok(Statement::DeclareFun {
|
|
820
|
+
async_,
|
|
821
|
+
name,
|
|
822
|
+
name_span,
|
|
823
|
+
params,
|
|
824
|
+
rest_param,
|
|
825
|
+
return_type,
|
|
569
826
|
span: self.span_end(span_start),
|
|
570
827
|
})
|
|
571
828
|
}
|
|
@@ -610,8 +867,12 @@ impl<'a> Parser<'a> {
|
|
|
610
867
|
let mutable = matches!(self.peek_kind(), Some(TokenKind::Let));
|
|
611
868
|
let var_span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
612
869
|
self.advance();
|
|
613
|
-
let
|
|
614
|
-
|
|
870
|
+
let for_name_tok = self.expect(TokenKind::Ident)?;
|
|
871
|
+
let name_span = Span {
|
|
872
|
+
start: for_name_tok.span.start,
|
|
873
|
+
end: for_name_tok.span.end,
|
|
874
|
+
};
|
|
875
|
+
let name = for_name_tok
|
|
615
876
|
.literal
|
|
616
877
|
.clone()
|
|
617
878
|
.ok_or("Expected identifier")?;
|
|
@@ -622,6 +883,7 @@ impl<'a> Parser<'a> {
|
|
|
622
883
|
let body = Box::new(self.parse_block_or_statement()?);
|
|
623
884
|
return Ok(Statement::ForOf {
|
|
624
885
|
name,
|
|
886
|
+
name_span,
|
|
625
887
|
iterable,
|
|
626
888
|
body,
|
|
627
889
|
span: self.span_end(span_start),
|
|
@@ -644,6 +906,7 @@ impl<'a> Parser<'a> {
|
|
|
644
906
|
}
|
|
645
907
|
Some(Box::new(Statement::VarDecl {
|
|
646
908
|
name,
|
|
909
|
+
name_span,
|
|
647
910
|
mutable,
|
|
648
911
|
type_ann,
|
|
649
912
|
init: init_expr,
|
|
@@ -665,7 +928,10 @@ impl<'a> Parser<'a> {
|
|
|
665
928
|
if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
666
929
|
self.advance();
|
|
667
930
|
}
|
|
668
|
-
let cond = if matches!(
|
|
931
|
+
let cond = if matches!(
|
|
932
|
+
self.peek_kind(),
|
|
933
|
+
Some(TokenKind::Semicolon | TokenKind::RParen)
|
|
934
|
+
) {
|
|
669
935
|
None
|
|
670
936
|
} else {
|
|
671
937
|
let c = self.parse_expr()?;
|
|
@@ -780,31 +1046,38 @@ impl<'a> Parser<'a> {
|
|
|
780
1046
|
fn parse_try(&mut self) -> Result<Statement, String> {
|
|
781
1047
|
let span_start = self.expect(TokenKind::Try)?.span.start;
|
|
782
1048
|
let body = Box::new(self.parse_block_or_statement()?);
|
|
783
|
-
|
|
1049
|
+
|
|
784
1050
|
let mut catch_param = None;
|
|
1051
|
+
let mut catch_param_span = None;
|
|
785
1052
|
let mut catch_body = None;
|
|
786
1053
|
let mut finally_body = None;
|
|
787
|
-
|
|
1054
|
+
|
|
788
1055
|
if matches!(self.peek_kind(), Some(TokenKind::Catch)) {
|
|
789
1056
|
self.advance();
|
|
790
1057
|
self.expect(TokenKind::LParen)?;
|
|
791
|
-
|
|
1058
|
+
let catch_tok = self.expect(TokenKind::Ident)?;
|
|
1059
|
+
catch_param_span = Some(Span {
|
|
1060
|
+
start: catch_tok.span.start,
|
|
1061
|
+
end: catch_tok.span.end,
|
|
1062
|
+
});
|
|
1063
|
+
catch_param = catch_tok.literal.clone();
|
|
792
1064
|
self.expect(TokenKind::RParen)?;
|
|
793
1065
|
catch_body = Some(Box::new(self.parse_block_or_statement()?));
|
|
794
1066
|
}
|
|
795
|
-
|
|
1067
|
+
|
|
796
1068
|
if matches!(self.peek_kind(), Some(TokenKind::Finally)) {
|
|
797
1069
|
self.advance();
|
|
798
1070
|
finally_body = Some(Box::new(self.parse_block_or_statement()?));
|
|
799
1071
|
}
|
|
800
|
-
|
|
1072
|
+
|
|
801
1073
|
if catch_body.is_none() && finally_body.is_none() {
|
|
802
1074
|
return Err("try statement requires catch or finally".to_string());
|
|
803
1075
|
}
|
|
804
|
-
|
|
1076
|
+
|
|
805
1077
|
Ok(Statement::Try {
|
|
806
1078
|
body,
|
|
807
1079
|
catch_param,
|
|
1080
|
+
catch_param_span,
|
|
808
1081
|
catch_body,
|
|
809
1082
|
finally_body,
|
|
810
1083
|
span: self.span_end(span_start),
|
|
@@ -818,25 +1091,42 @@ impl<'a> Parser<'a> {
|
|
|
818
1091
|
self.advance();
|
|
819
1092
|
let mut specs = Vec::new();
|
|
820
1093
|
while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
821
|
-
let
|
|
822
|
-
|
|
1094
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
1095
|
+
let name_span = Span {
|
|
1096
|
+
start: name_tok.span.start,
|
|
1097
|
+
end: name_tok.span.end,
|
|
1098
|
+
};
|
|
1099
|
+
let name = name_tok
|
|
823
1100
|
.literal
|
|
824
1101
|
.clone()
|
|
825
1102
|
.ok_or("Expected identifier in import")?;
|
|
826
|
-
let alias = if matches!(self.peek_kind(), Some(TokenKind::Ident))
|
|
1103
|
+
let (alias, alias_span) = if matches!(self.peek_kind(), Some(TokenKind::Ident))
|
|
827
1104
|
&& self.peek().and_then(|t| t.literal.as_deref()) == Some("as")
|
|
828
1105
|
{
|
|
829
1106
|
self.advance(); // consume 'as'
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
1107
|
+
let alias_tok = self.expect(TokenKind::Ident)?;
|
|
1108
|
+
let asp = Span {
|
|
1109
|
+
start: alias_tok.span.start,
|
|
1110
|
+
end: alias_tok.span.end,
|
|
1111
|
+
};
|
|
1112
|
+
(
|
|
1113
|
+
Some(
|
|
1114
|
+
alias_tok
|
|
1115
|
+
.literal
|
|
1116
|
+
.clone()
|
|
1117
|
+
.ok_or("Expected alias after 'as'")?,
|
|
1118
|
+
),
|
|
1119
|
+
Some(asp),
|
|
835
1120
|
)
|
|
836
1121
|
} else {
|
|
837
|
-
None
|
|
1122
|
+
(None, None)
|
|
838
1123
|
};
|
|
839
|
-
specs.push(ImportSpecifier::Named {
|
|
1124
|
+
specs.push(ImportSpecifier::Named {
|
|
1125
|
+
name,
|
|
1126
|
+
name_span,
|
|
1127
|
+
alias,
|
|
1128
|
+
alias_span,
|
|
1129
|
+
});
|
|
840
1130
|
if !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
841
1131
|
self.expect(TokenKind::Comma)?;
|
|
842
1132
|
}
|
|
@@ -850,20 +1140,31 @@ impl<'a> Parser<'a> {
|
|
|
850
1140
|
if as_tok.literal.as_deref() != Some("as") {
|
|
851
1141
|
return Err("Expected 'as' after '*' in namespace import".to_string());
|
|
852
1142
|
}
|
|
853
|
-
let
|
|
854
|
-
|
|
1143
|
+
let alias_tok = self.expect(TokenKind::Ident)?;
|
|
1144
|
+
let name_span = Span {
|
|
1145
|
+
start: alias_tok.span.start,
|
|
1146
|
+
end: alias_tok.span.end,
|
|
1147
|
+
};
|
|
1148
|
+
let alias = alias_tok
|
|
855
1149
|
.literal
|
|
856
1150
|
.clone()
|
|
857
1151
|
.ok_or("Expected identifier after 'as'")?;
|
|
858
|
-
vec![ImportSpecifier::Namespace
|
|
1152
|
+
vec![ImportSpecifier::Namespace {
|
|
1153
|
+
name: alias,
|
|
1154
|
+
name_span,
|
|
1155
|
+
}]
|
|
859
1156
|
} else if matches!(self.peek_kind(), Some(TokenKind::Ident)) {
|
|
860
1157
|
// Default: import X from "..."
|
|
861
|
-
let
|
|
862
|
-
|
|
1158
|
+
let def_tok = self.expect(TokenKind::Ident)?;
|
|
1159
|
+
let name_span = Span {
|
|
1160
|
+
start: def_tok.span.start,
|
|
1161
|
+
end: def_tok.span.end,
|
|
1162
|
+
};
|
|
1163
|
+
let name = def_tok
|
|
863
1164
|
.literal
|
|
864
1165
|
.clone()
|
|
865
1166
|
.ok_or("Expected identifier")?;
|
|
866
|
-
vec![ImportSpecifier::Default
|
|
1167
|
+
vec![ImportSpecifier::Default { name, name_span }]
|
|
867
1168
|
} else {
|
|
868
1169
|
return Err("Expected { }, * as name, or default import".to_string());
|
|
869
1170
|
};
|
|
@@ -889,6 +1190,10 @@ impl<'a> Parser<'a> {
|
|
|
889
1190
|
self.advance();
|
|
890
1191
|
let expr = self.parse_expr()?;
|
|
891
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()?))
|
|
892
1197
|
} else if matches!(self.peek_kind(), Some(TokenKind::Const)) {
|
|
893
1198
|
ExportDeclaration::Named(Box::new(self.parse_var_decl(false)?))
|
|
894
1199
|
} else if matches!(self.peek_kind(), Some(TokenKind::Let)) {
|
|
@@ -902,7 +1207,10 @@ impl<'a> Parser<'a> {
|
|
|
902
1207
|
}
|
|
903
1208
|
ExportDeclaration::Named(Box::new(self.parse_fun_decl(async_)?))
|
|
904
1209
|
} else {
|
|
905
|
-
return Err(
|
|
1210
|
+
return Err(
|
|
1211
|
+
"Expected 'default', 'type', 'declare', 'const', 'let', or 'fn' after export"
|
|
1212
|
+
.to_string(),
|
|
1213
|
+
);
|
|
906
1214
|
};
|
|
907
1215
|
Ok(Statement::Export {
|
|
908
1216
|
declaration: Box::new(declaration),
|
|
@@ -920,7 +1228,7 @@ impl<'a> Parser<'a> {
|
|
|
920
1228
|
// Check for simple assignment
|
|
921
1229
|
if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
922
1230
|
let start = left.span().start;
|
|
923
|
-
|
|
1231
|
+
|
|
924
1232
|
// Variable assignment: x = val
|
|
925
1233
|
if let Expr::Ident { name, .. } = &left {
|
|
926
1234
|
let name = Arc::clone(name);
|
|
@@ -933,9 +1241,14 @@ impl<'a> Parser<'a> {
|
|
|
933
1241
|
span: Span { start, end },
|
|
934
1242
|
});
|
|
935
1243
|
}
|
|
936
|
-
|
|
1244
|
+
|
|
937
1245
|
// Member assignment: obj.prop = val
|
|
938
|
-
if let Expr::Member {
|
|
1246
|
+
if let Expr::Member {
|
|
1247
|
+
object,
|
|
1248
|
+
prop: MemberProp::Name { name: prop_name, .. },
|
|
1249
|
+
..
|
|
1250
|
+
} = &left
|
|
1251
|
+
{
|
|
939
1252
|
let object = Box::clone(object);
|
|
940
1253
|
let prop = Arc::clone(prop_name);
|
|
941
1254
|
self.advance(); // =
|
|
@@ -948,7 +1261,7 @@ impl<'a> Parser<'a> {
|
|
|
948
1261
|
span: Span { start, end },
|
|
949
1262
|
});
|
|
950
1263
|
}
|
|
951
|
-
|
|
1264
|
+
|
|
952
1265
|
// Index assignment: arr[idx] = val or obj[key] = val
|
|
953
1266
|
if let Expr::Index { object, index, .. } = &left {
|
|
954
1267
|
let object = Box::clone(object);
|
|
@@ -1060,7 +1373,7 @@ impl<'a> Parser<'a> {
|
|
|
1060
1373
|
binary_single_op!(parse_bit_or, parse_bit_xor, BitOr, BinOp::BitOr);
|
|
1061
1374
|
binary_single_op!(parse_bit_xor, parse_bit_and, BitXor, BinOp::BitXor);
|
|
1062
1375
|
binary_single_op!(parse_bit_and, parse_shift, BitAnd, BinOp::BitAnd);
|
|
1063
|
-
|
|
1376
|
+
|
|
1064
1377
|
binary_multi_op!(parse_shift, parse_equality, Shl => BinOp::Shl, Shr => BinOp::Shr);
|
|
1065
1378
|
binary_multi_op!(parse_equality, parse_comparison,
|
|
1066
1379
|
StrictEq => BinOp::StrictEq, StrictNe => BinOp::StrictNe,
|
|
@@ -1099,14 +1412,20 @@ impl<'a> Parser<'a> {
|
|
|
1099
1412
|
let operand = self.parse_unary()?;
|
|
1100
1413
|
if let Expr::Ident { name, span } = &operand {
|
|
1101
1414
|
let name = Arc::clone(name);
|
|
1102
|
-
let span = Span {
|
|
1415
|
+
let span = Span {
|
|
1416
|
+
start: span_start,
|
|
1417
|
+
end: span.end,
|
|
1418
|
+
};
|
|
1103
1419
|
return Ok(if is_inc {
|
|
1104
1420
|
Expr::PrefixInc { name, span }
|
|
1105
1421
|
} else {
|
|
1106
1422
|
Expr::PrefixDec { name, span }
|
|
1107
1423
|
});
|
|
1108
1424
|
}
|
|
1109
|
-
return Err(format!(
|
|
1425
|
+
return Err(format!(
|
|
1426
|
+
"Prefix {} requires an identifier",
|
|
1427
|
+
if is_inc { "++" } else { "--" }
|
|
1428
|
+
));
|
|
1110
1429
|
}
|
|
1111
1430
|
let op = match self.peek_kind() {
|
|
1112
1431
|
Some(TokenKind::Not) => UnaryOp::Not,
|
|
@@ -1134,7 +1453,10 @@ impl<'a> Parser<'a> {
|
|
|
1134
1453
|
let end = operand.span().end;
|
|
1135
1454
|
return Ok(Expr::Await {
|
|
1136
1455
|
operand: Box::new(operand),
|
|
1137
|
-
span: Span {
|
|
1456
|
+
span: Span {
|
|
1457
|
+
start: span_start,
|
|
1458
|
+
end,
|
|
1459
|
+
},
|
|
1138
1460
|
});
|
|
1139
1461
|
}
|
|
1140
1462
|
_ => return self.parse_postfix(),
|
|
@@ -1161,16 +1483,23 @@ impl<'a> Parser<'a> {
|
|
|
1161
1483
|
TokenKind::Dot | TokenKind::OptionalChain => {
|
|
1162
1484
|
let optional = kind == TokenKind::OptionalChain;
|
|
1163
1485
|
self.advance();
|
|
1164
|
-
let
|
|
1165
|
-
|
|
1486
|
+
let prop_tok = self.expect(TokenKind::Ident)?;
|
|
1487
|
+
let prop = prop_tok
|
|
1166
1488
|
.literal
|
|
1167
1489
|
.clone()
|
|
1168
1490
|
.ok_or("Expected property name")?;
|
|
1491
|
+
let prop_span = Span {
|
|
1492
|
+
start: prop_tok.span.start,
|
|
1493
|
+
end: prop_tok.span.end,
|
|
1494
|
+
};
|
|
1169
1495
|
let start = expr.span().start;
|
|
1170
1496
|
let end = self.peek().map(|x| x.span.start).unwrap_or(start);
|
|
1171
1497
|
expr = Expr::Member {
|
|
1172
1498
|
object: Box::new(expr),
|
|
1173
|
-
prop: MemberProp::Name
|
|
1499
|
+
prop: MemberProp::Name {
|
|
1500
|
+
name: prop,
|
|
1501
|
+
span: prop_span,
|
|
1502
|
+
},
|
|
1174
1503
|
optional,
|
|
1175
1504
|
span: Span { start, end },
|
|
1176
1505
|
};
|
|
@@ -1274,16 +1603,23 @@ impl<'a> Parser<'a> {
|
|
|
1274
1603
|
TokenKind::Dot | TokenKind::OptionalChain => {
|
|
1275
1604
|
let optional = kind == TokenKind::OptionalChain;
|
|
1276
1605
|
self.advance();
|
|
1277
|
-
let
|
|
1278
|
-
|
|
1606
|
+
let prop_tok = self.expect(TokenKind::Ident)?;
|
|
1607
|
+
let prop = prop_tok
|
|
1279
1608
|
.literal
|
|
1280
1609
|
.clone()
|
|
1281
1610
|
.ok_or("Expected property name")?;
|
|
1611
|
+
let prop_span = Span {
|
|
1612
|
+
start: prop_tok.span.start,
|
|
1613
|
+
end: prop_tok.span.end,
|
|
1614
|
+
};
|
|
1282
1615
|
let start = expr.span().start;
|
|
1283
1616
|
let end = self.peek().map(|x| x.span.start).unwrap_or(start);
|
|
1284
1617
|
expr = Expr::Member {
|
|
1285
1618
|
object: Box::new(expr),
|
|
1286
|
-
prop: MemberProp::Name
|
|
1619
|
+
prop: MemberProp::Name {
|
|
1620
|
+
name: prop,
|
|
1621
|
+
span: prop_span,
|
|
1622
|
+
},
|
|
1287
1623
|
optional,
|
|
1288
1624
|
span: Span { start, end },
|
|
1289
1625
|
};
|
|
@@ -1302,11 +1638,18 @@ impl<'a> Parser<'a> {
|
|
|
1302
1638
|
};
|
|
1303
1639
|
}
|
|
1304
1640
|
TokenKind::PlusPlus | TokenKind::MinusMinus => {
|
|
1305
|
-
if let Expr::Ident {
|
|
1641
|
+
if let Expr::Ident {
|
|
1642
|
+
name,
|
|
1643
|
+
span: ident_span,
|
|
1644
|
+
} = &expr
|
|
1645
|
+
{
|
|
1306
1646
|
let name = Arc::clone(name);
|
|
1307
1647
|
let is_inc = kind == TokenKind::PlusPlus;
|
|
1308
1648
|
let tok = self.advance().ok_or("Unexpected EOF")?;
|
|
1309
|
-
let span = Span {
|
|
1649
|
+
let span = Span {
|
|
1650
|
+
start: ident_span.start,
|
|
1651
|
+
end: tok.span.end,
|
|
1652
|
+
};
|
|
1310
1653
|
expr = if is_inc {
|
|
1311
1654
|
Expr::PostfixInc { name, span }
|
|
1312
1655
|
} else {
|
|
@@ -1370,11 +1713,15 @@ impl<'a> Parser<'a> {
|
|
|
1370
1713
|
return Ok(Expr::ArrowFunction {
|
|
1371
1714
|
params: vec![FunParam::Simple(TypedParam {
|
|
1372
1715
|
name: name.clone(),
|
|
1716
|
+
name_span: span,
|
|
1373
1717
|
type_ann: None,
|
|
1374
1718
|
default: None,
|
|
1375
1719
|
})],
|
|
1376
1720
|
body,
|
|
1377
|
-
span: Span {
|
|
1721
|
+
span: Span {
|
|
1722
|
+
start: span.start,
|
|
1723
|
+
end,
|
|
1724
|
+
},
|
|
1378
1725
|
});
|
|
1379
1726
|
}
|
|
1380
1727
|
Ok(Expr::Ident { name, span })
|
|
@@ -1418,7 +1765,10 @@ impl<'a> Parser<'a> {
|
|
|
1418
1765
|
match self.peek_kind() {
|
|
1419
1766
|
Some(TokenKind::Ident) => self.parse_jsx_element(span.start),
|
|
1420
1767
|
Some(TokenKind::Gt) => self.parse_jsx_fragment(span.start),
|
|
1421
|
-
_ => Err(format!(
|
|
1768
|
+
_ => Err(format!(
|
|
1769
|
+
"Invalid JSX: expected tag name or <> after <, got {:?}",
|
|
1770
|
+
self.peek_kind()
|
|
1771
|
+
)),
|
|
1422
1772
|
}
|
|
1423
1773
|
}
|
|
1424
1774
|
TokenKind::LBrace => {
|
|
@@ -1431,11 +1781,8 @@ impl<'a> Parser<'a> {
|
|
|
1431
1781
|
} else {
|
|
1432
1782
|
let key_tok = self.advance().ok_or("Expected object key")?;
|
|
1433
1783
|
let (key, key_span, is_ident_key) = match key_tok.kind {
|
|
1434
|
-
TokenKind::Ident => {
|
|
1435
|
-
let k = key_tok
|
|
1436
|
-
.literal
|
|
1437
|
-
.clone()
|
|
1438
|
-
.ok_or("Expected key")?;
|
|
1784
|
+
TokenKind::Ident | TokenKind::Type | TokenKind::Declare => {
|
|
1785
|
+
let k = key_tok.literal.clone().ok_or("Expected key")?;
|
|
1439
1786
|
let sp = Span {
|
|
1440
1787
|
start: key_tok.span.start,
|
|
1441
1788
|
end: key_tok.span.end,
|
|
@@ -1443,20 +1790,19 @@ impl<'a> Parser<'a> {
|
|
|
1443
1790
|
(k, sp, true)
|
|
1444
1791
|
}
|
|
1445
1792
|
TokenKind::String => {
|
|
1446
|
-
let k = key_tok
|
|
1447
|
-
.literal
|
|
1448
|
-
.clone()
|
|
1449
|
-
.ok_or("Expected string key")?;
|
|
1793
|
+
let k = key_tok.literal.clone().ok_or("Expected string key")?;
|
|
1450
1794
|
let sp = Span {
|
|
1451
1795
|
start: key_tok.span.start,
|
|
1452
1796
|
end: key_tok.span.end,
|
|
1453
1797
|
};
|
|
1454
1798
|
(k, sp, false)
|
|
1455
1799
|
}
|
|
1456
|
-
_ =>
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1800
|
+
_ => {
|
|
1801
|
+
return Err(format!(
|
|
1802
|
+
"Expected object key (ident or string), got {:?}",
|
|
1803
|
+
key_tok.kind
|
|
1804
|
+
))
|
|
1805
|
+
}
|
|
1460
1806
|
};
|
|
1461
1807
|
let value = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
1462
1808
|
self.expect(TokenKind::Colon)?;
|
|
@@ -1499,12 +1845,12 @@ impl<'a> Parser<'a> {
|
|
|
1499
1845
|
// Template literal with interpolation: `text${
|
|
1500
1846
|
let mut quasis = vec![t.literal.clone().unwrap_or_default()];
|
|
1501
1847
|
let mut exprs = Vec::new();
|
|
1502
|
-
|
|
1848
|
+
|
|
1503
1849
|
loop {
|
|
1504
1850
|
// Parse the expression inside ${}
|
|
1505
1851
|
let expr = self.parse_expr()?;
|
|
1506
1852
|
exprs.push(expr);
|
|
1507
|
-
|
|
1853
|
+
|
|
1508
1854
|
// Next token should be TemplateMiddle or TemplateTail
|
|
1509
1855
|
let next = self.advance().ok_or("Unexpected EOF in template literal")?;
|
|
1510
1856
|
match next.kind {
|
|
@@ -1514,14 +1860,22 @@ impl<'a> Parser<'a> {
|
|
|
1514
1860
|
return Ok(Expr::TemplateLiteral {
|
|
1515
1861
|
quasis,
|
|
1516
1862
|
exprs,
|
|
1517
|
-
span: Span {
|
|
1863
|
+
span: Span {
|
|
1864
|
+
start: span.start,
|
|
1865
|
+
end,
|
|
1866
|
+
},
|
|
1518
1867
|
});
|
|
1519
1868
|
}
|
|
1520
1869
|
TokenKind::TemplateMiddle => {
|
|
1521
1870
|
quasis.push(next.literal.clone().unwrap_or_default());
|
|
1522
1871
|
// Continue parsing more expressions
|
|
1523
1872
|
}
|
|
1524
|
-
_ =>
|
|
1873
|
+
_ => {
|
|
1874
|
+
return Err(format!(
|
|
1875
|
+
"Expected template continuation, got {:?}",
|
|
1876
|
+
next.kind
|
|
1877
|
+
))
|
|
1878
|
+
}
|
|
1525
1879
|
}
|
|
1526
1880
|
}
|
|
1527
1881
|
}
|
|
@@ -1534,11 +1888,11 @@ impl<'a> Parser<'a> {
|
|
|
1534
1888
|
fn try_parse_arrow_function(&mut self, start_span: &Span) -> Result<Option<Expr>, String> {
|
|
1535
1889
|
// Save position for backtracking
|
|
1536
1890
|
let saved_pos = self.pos;
|
|
1537
|
-
|
|
1891
|
+
|
|
1538
1892
|
// Try to parse as arrow function params
|
|
1539
1893
|
let mut params = Vec::new();
|
|
1540
1894
|
let mut is_arrow = false;
|
|
1541
|
-
|
|
1895
|
+
|
|
1542
1896
|
// Check for empty params: () => ...
|
|
1543
1897
|
if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1544
1898
|
self.advance(); // consume )
|
|
@@ -1574,20 +1928,23 @@ impl<'a> Parser<'a> {
|
|
|
1574
1928
|
}
|
|
1575
1929
|
}
|
|
1576
1930
|
}
|
|
1577
|
-
|
|
1931
|
+
|
|
1578
1932
|
if !is_arrow {
|
|
1579
1933
|
// Backtrack - it's not an arrow function
|
|
1580
1934
|
self.pos = saved_pos;
|
|
1581
1935
|
return Ok(None);
|
|
1582
1936
|
}
|
|
1583
|
-
|
|
1937
|
+
|
|
1584
1938
|
let body = self.parse_arrow_body()?;
|
|
1585
1939
|
let end = self.previous_span_end();
|
|
1586
|
-
|
|
1940
|
+
|
|
1587
1941
|
Ok(Some(Expr::ArrowFunction {
|
|
1588
1942
|
params,
|
|
1589
1943
|
body,
|
|
1590
|
-
span: Span {
|
|
1944
|
+
span: Span {
|
|
1945
|
+
start: start_span.start,
|
|
1946
|
+
end,
|
|
1947
|
+
},
|
|
1591
1948
|
}))
|
|
1592
1949
|
}
|
|
1593
1950
|
|
|
@@ -1651,7 +2008,11 @@ impl<'a> Parser<'a> {
|
|
|
1651
2008
|
self.expect(TokenKind::RBrace)?; // }
|
|
1652
2009
|
JsxAttrValue::Expr(expr)
|
|
1653
2010
|
} else {
|
|
1654
|
-
let s = self
|
|
2011
|
+
let s = self
|
|
2012
|
+
.expect(TokenKind::String)?
|
|
2013
|
+
.literal
|
|
2014
|
+
.clone()
|
|
2015
|
+
.ok_or("Expected string")?;
|
|
1655
2016
|
JsxAttrValue::String(s)
|
|
1656
2017
|
};
|
|
1657
2018
|
props.push(JsxProp::Attr { name, value });
|
|
@@ -1662,7 +2023,12 @@ impl<'a> Parser<'a> {
|
|
|
1662
2023
|
});
|
|
1663
2024
|
}
|
|
1664
2025
|
}
|
|
1665
|
-
_ =>
|
|
2026
|
+
_ => {
|
|
2027
|
+
return Err(format!(
|
|
2028
|
+
"Unexpected token in JSX props: {:?}",
|
|
2029
|
+
self.peek_kind()
|
|
2030
|
+
))
|
|
2031
|
+
}
|
|
1666
2032
|
}
|
|
1667
2033
|
}
|
|
1668
2034
|
self.advance(); // consume >
|
|
@@ -1745,9 +2111,16 @@ impl<'a> Parser<'a> {
|
|
|
1745
2111
|
// </ closing tag
|
|
1746
2112
|
self.advance(); // <
|
|
1747
2113
|
self.advance(); // /
|
|
1748
|
-
let name = self
|
|
2114
|
+
let name = self
|
|
2115
|
+
.expect(TokenKind::Ident)?
|
|
2116
|
+
.literal
|
|
2117
|
+
.clone()
|
|
2118
|
+
.ok_or("Expected tag name")?;
|
|
1749
2119
|
if name.as_ref() != close_tag {
|
|
1750
|
-
return Err(format!(
|
|
2120
|
+
return Err(format!(
|
|
2121
|
+
"Mismatched JSX tag: expected </{}> got </{}>",
|
|
2122
|
+
close_tag, name
|
|
2123
|
+
));
|
|
1751
2124
|
}
|
|
1752
2125
|
self.expect(TokenKind::Gt)?; // >
|
|
1753
2126
|
return Ok(children);
|
|
@@ -1827,7 +2200,10 @@ impl<'a> Parser<'a> {
|
|
|
1827
2200
|
self.advance();
|
|
1828
2201
|
self.advance();
|
|
1829
2202
|
let end = self.previous_span_end();
|
|
1830
|
-
return Ok(Expr::JsxFragment {
|
|
2203
|
+
return Ok(Expr::JsxFragment {
|
|
2204
|
+
children,
|
|
2205
|
+
span: Span { start, end },
|
|
2206
|
+
});
|
|
1831
2207
|
}
|
|
1832
2208
|
}
|
|
1833
2209
|
return Err("Expected </> to close fragment".to_string());
|
|
@@ -1929,4 +2305,3 @@ impl ExprSpan for Expr {
|
|
|
1929
2305
|
}
|
|
1930
2306
|
}
|
|
1931
2307
|
}
|
|
1932
|
-
|