@tishlang/tish 1.6.0 → 1.7.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 +1 -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 +101 -130
- package/crates/js_to_tish/src/transform/stmt.rs +25 -22
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/cli_help.rs +76 -29
- package/crates/tish/src/main.rs +85 -54
- package/crates/tish/tests/cargo_example_compile.rs +3 -1
- package/crates/tish/tests/integration_test.rs +197 -47
- package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
- package/crates/tish/tests/shortcircuit.rs +19 -4
- package/crates/tish_ast/src/ast.rs +12 -14
- package/crates/tish_build_utils/src/lib.rs +31 -6
- package/crates/tish_builtins/src/array.rs +52 -21
- package/crates/tish_builtins/src/construct.rs +2 -8
- package/crates/tish_builtins/src/globals.rs +30 -15
- package/crates/tish_builtins/src/lib.rs +5 -5
- package/crates/tish_builtins/src/math.rs +5 -3
- package/crates/tish_builtins/src/string.rs +71 -19
- package/crates/tish_bytecode/src/chunk.rs +0 -1
- package/crates/tish_bytecode/src/compiler.rs +164 -60
- package/crates/tish_bytecode/src/opcode.rs +13 -4
- package/crates/tish_bytecode/src/peephole.rs +2 -2
- package/crates/tish_compile/src/codegen.rs +921 -299
- package/crates/tish_compile/src/infer.rs +69 -19
- package/crates/tish_compile/src/lib.rs +15 -5
- package/crates/tish_compile/src/resolve.rs +112 -69
- package/crates/tish_compile/src/types.rs +10 -14
- package/crates/tish_compile_js/src/codegen.rs +34 -13
- 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 +39 -48
- package/crates/tish_core/src/json.rs +5 -3
- package/crates/tish_core/src/lib.rs +1 -1
- package/crates/tish_core/src/uri.rs +9 -6
- package/crates/tish_core/src/value.rs +92 -28
- package/crates/tish_cranelift/src/link.rs +6 -9
- package/crates/tish_cranelift/src/lower.rs +14 -8
- package/crates/tish_eval/src/eval.rs +389 -142
- package/crates/tish_eval/src/lib.rs +10 -6
- package/crates/tish_eval/src/natives.rs +95 -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 +10 -3
- package/crates/tish_fmt/src/lib.rs +29 -13
- package/crates/tish_lexer/src/lib.rs +217 -63
- package/crates/tish_lexer/src/token.rs +6 -6
- package/crates/tish_llvm/src/lib.rs +15 -8
- package/crates/tish_lsp/src/main.rs +41 -43
- package/crates/tish_native/src/build.rs +1 -6
- package/crates/tish_native/src/lib.rs +48 -19
- package/crates/tish_opt/src/lib.rs +67 -50
- package/crates/tish_parser/src/lib.rs +36 -11
- package/crates/tish_parser/src/parser.rs +172 -87
- package/crates/tish_runtime/src/http.rs +15 -6
- package/crates/tish_runtime/src/http_fetch.rs +24 -14
- package/crates/tish_runtime/src/lib.rs +224 -168
- package/crates/tish_runtime/src/promise.rs +1 -5
- package/crates/tish_runtime/src/ws.rs +45 -20
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
- package/crates/tish_ui/src/jsx.rs +41 -22
- package/crates/tish_ui/src/lib.rs +2 -2
- package/crates/tish_vm/src/vm.rs +309 -112
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +8 -3
- package/crates/tish_wasm/src/lib.rs +38 -28
- package/crates/tishlang_cargo_bindgen/Cargo.toml +25 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +52 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -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
|
|
|
@@ -207,7 +209,10 @@ impl<'a> Parser<'a> {
|
|
|
207
209
|
statements.push(self.parse_statement()?);
|
|
208
210
|
}
|
|
209
211
|
|
|
210
|
-
if matches!(
|
|
212
|
+
if matches!(
|
|
213
|
+
self.peek_kind(),
|
|
214
|
+
Some(TokenKind::RBrace | TokenKind::Dedent)
|
|
215
|
+
) {
|
|
211
216
|
self.advance();
|
|
212
217
|
}
|
|
213
218
|
|
|
@@ -215,10 +220,7 @@ impl<'a> Parser<'a> {
|
|
|
215
220
|
statements,
|
|
216
221
|
span: Span {
|
|
217
222
|
start: span_start,
|
|
218
|
-
end: self
|
|
219
|
-
.peek()
|
|
220
|
-
.map(|x| x.span.end)
|
|
221
|
-
.unwrap_or(span_start),
|
|
223
|
+
end: self.peek().map(|x| x.span.end).unwrap_or(span_start),
|
|
222
224
|
},
|
|
223
225
|
})
|
|
224
226
|
}
|
|
@@ -229,9 +231,12 @@ impl<'a> Parser<'a> {
|
|
|
229
231
|
} else {
|
|
230
232
|
self.expect(TokenKind::Const)?.span.start
|
|
231
233
|
};
|
|
232
|
-
|
|
234
|
+
|
|
233
235
|
// Check for destructuring pattern
|
|
234
|
-
if matches!(
|
|
236
|
+
if matches!(
|
|
237
|
+
self.peek_kind(),
|
|
238
|
+
Some(TokenKind::LBracket) | Some(TokenKind::LBrace)
|
|
239
|
+
) {
|
|
235
240
|
let pattern = self.parse_destruct_pattern()?;
|
|
236
241
|
self.expect(TokenKind::Assign)?;
|
|
237
242
|
let init = self.parse_expr()?;
|
|
@@ -242,7 +247,7 @@ impl<'a> Parser<'a> {
|
|
|
242
247
|
span: self.span_end(span_start),
|
|
243
248
|
});
|
|
244
249
|
}
|
|
245
|
-
|
|
250
|
+
|
|
246
251
|
let name = self
|
|
247
252
|
.expect(TokenKind::Ident)?
|
|
248
253
|
.literal
|
|
@@ -271,7 +276,7 @@ impl<'a> Parser<'a> {
|
|
|
271
276
|
span: self.span_end(span_start),
|
|
272
277
|
})
|
|
273
278
|
}
|
|
274
|
-
|
|
279
|
+
|
|
275
280
|
fn parse_destruct_pattern(&mut self) -> Result<DestructPattern, String> {
|
|
276
281
|
match self.peek_kind() {
|
|
277
282
|
Some(TokenKind::LBracket) => self.parse_array_destruct_pattern(),
|
|
@@ -279,11 +284,11 @@ impl<'a> Parser<'a> {
|
|
|
279
284
|
_ => Err("Expected destructuring pattern".to_string()),
|
|
280
285
|
}
|
|
281
286
|
}
|
|
282
|
-
|
|
287
|
+
|
|
283
288
|
fn parse_array_destruct_pattern(&mut self) -> Result<DestructPattern, String> {
|
|
284
289
|
self.expect(TokenKind::LBracket)?;
|
|
285
290
|
let mut elements = Vec::new();
|
|
286
|
-
|
|
291
|
+
|
|
287
292
|
while !matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
|
|
288
293
|
// Handle holes (elision): [a, , b]
|
|
289
294
|
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
@@ -291,15 +296,19 @@ impl<'a> Parser<'a> {
|
|
|
291
296
|
self.advance();
|
|
292
297
|
continue;
|
|
293
298
|
}
|
|
294
|
-
|
|
299
|
+
|
|
295
300
|
// Rest element: ...rest
|
|
296
301
|
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
297
302
|
self.advance();
|
|
298
|
-
let name = self
|
|
303
|
+
let name = self
|
|
304
|
+
.expect(TokenKind::Ident)?
|
|
305
|
+
.literal
|
|
306
|
+
.clone()
|
|
307
|
+
.ok_or("Expected identifier")?;
|
|
299
308
|
elements.push(Some(DestructElement::Rest(name)));
|
|
300
309
|
break;
|
|
301
310
|
}
|
|
302
|
-
|
|
311
|
+
|
|
303
312
|
// Nested pattern or identifier
|
|
304
313
|
let elem = match self.peek_kind() {
|
|
305
314
|
Some(TokenKind::LBracket) | Some(TokenKind::LBrace) => {
|
|
@@ -307,31 +316,40 @@ impl<'a> Parser<'a> {
|
|
|
307
316
|
DestructElement::Pattern(Box::new(nested))
|
|
308
317
|
}
|
|
309
318
|
Some(TokenKind::Ident) => {
|
|
310
|
-
let name = self
|
|
319
|
+
let name = self
|
|
320
|
+
.advance()
|
|
321
|
+
.ok_or("Unexpected EOF")?
|
|
322
|
+
.literal
|
|
323
|
+
.clone()
|
|
324
|
+
.ok_or("Expected identifier")?;
|
|
311
325
|
DestructElement::Ident(name)
|
|
312
326
|
}
|
|
313
327
|
_ => return Err("Expected identifier or pattern in destructuring".to_string()),
|
|
314
328
|
};
|
|
315
329
|
elements.push(Some(elem));
|
|
316
|
-
|
|
330
|
+
|
|
317
331
|
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
318
332
|
self.advance();
|
|
319
333
|
} else {
|
|
320
334
|
break;
|
|
321
335
|
}
|
|
322
336
|
}
|
|
323
|
-
|
|
337
|
+
|
|
324
338
|
self.expect(TokenKind::RBracket)?;
|
|
325
339
|
Ok(DestructPattern::Array(elements))
|
|
326
340
|
}
|
|
327
|
-
|
|
341
|
+
|
|
328
342
|
fn parse_object_destruct_pattern(&mut self) -> Result<DestructPattern, String> {
|
|
329
343
|
self.expect(TokenKind::LBrace)?;
|
|
330
344
|
let mut props = Vec::new();
|
|
331
|
-
|
|
345
|
+
|
|
332
346
|
while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
333
|
-
let key = self
|
|
334
|
-
|
|
347
|
+
let key = self
|
|
348
|
+
.expect(TokenKind::Ident)?
|
|
349
|
+
.literal
|
|
350
|
+
.clone()
|
|
351
|
+
.ok_or("Expected identifier")?;
|
|
352
|
+
|
|
335
353
|
let value = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
336
354
|
self.advance();
|
|
337
355
|
// Could be renamed binding or nested pattern
|
|
@@ -341,7 +359,12 @@ impl<'a> Parser<'a> {
|
|
|
341
359
|
DestructElement::Pattern(Box::new(nested))
|
|
342
360
|
}
|
|
343
361
|
Some(TokenKind::Ident) => {
|
|
344
|
-
let name = self
|
|
362
|
+
let name = self
|
|
363
|
+
.advance()
|
|
364
|
+
.ok_or("Unexpected EOF")?
|
|
365
|
+
.literal
|
|
366
|
+
.clone()
|
|
367
|
+
.ok_or("Expected identifier")?;
|
|
345
368
|
DestructElement::Ident(name)
|
|
346
369
|
}
|
|
347
370
|
_ => return Err("Expected identifier or pattern after ':'".to_string()),
|
|
@@ -350,16 +373,16 @@ impl<'a> Parser<'a> {
|
|
|
350
373
|
// Shorthand: { key } is equivalent to { key: key }
|
|
351
374
|
DestructElement::Ident(key.clone())
|
|
352
375
|
};
|
|
353
|
-
|
|
376
|
+
|
|
354
377
|
props.push(DestructProp { key, value });
|
|
355
|
-
|
|
378
|
+
|
|
356
379
|
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
357
380
|
self.advance();
|
|
358
381
|
} else {
|
|
359
382
|
break;
|
|
360
383
|
}
|
|
361
384
|
}
|
|
362
|
-
|
|
385
|
+
|
|
363
386
|
self.expect(TokenKind::RBrace)?;
|
|
364
387
|
Ok(DestructPattern::Object(props))
|
|
365
388
|
}
|
|
@@ -412,18 +435,18 @@ impl<'a> Parser<'a> {
|
|
|
412
435
|
default,
|
|
413
436
|
}))
|
|
414
437
|
}
|
|
415
|
-
|
|
438
|
+
|
|
416
439
|
/// Parse a type annotation (number, string, T[], {a: T}, etc.)
|
|
417
440
|
fn parse_type_annotation(&mut self) -> Result<TypeAnnotation, String> {
|
|
418
441
|
let base = self.parse_type_primary()?;
|
|
419
|
-
|
|
442
|
+
|
|
420
443
|
// Check for array suffix: T[]
|
|
421
444
|
if matches!(self.peek_kind(), Some(TokenKind::LBracket)) {
|
|
422
445
|
self.advance(); // [
|
|
423
446
|
self.expect(TokenKind::RBracket)?; // ]
|
|
424
447
|
return Ok(TypeAnnotation::Array(Box::new(base)));
|
|
425
448
|
}
|
|
426
|
-
|
|
449
|
+
|
|
427
450
|
// Check for union: T | U
|
|
428
451
|
if matches!(self.peek_kind(), Some(TokenKind::BitOr)) {
|
|
429
452
|
let mut types = vec![base];
|
|
@@ -433,10 +456,10 @@ impl<'a> Parser<'a> {
|
|
|
433
456
|
}
|
|
434
457
|
return Ok(TypeAnnotation::Union(types));
|
|
435
458
|
}
|
|
436
|
-
|
|
459
|
+
|
|
437
460
|
Ok(base)
|
|
438
461
|
}
|
|
439
|
-
|
|
462
|
+
|
|
440
463
|
/// Parse a primary type (identifier, object, or function type)
|
|
441
464
|
fn parse_type_primary(&mut self) -> Result<TypeAnnotation, String> {
|
|
442
465
|
match self.peek_kind() {
|
|
@@ -459,7 +482,10 @@ impl<'a> Parser<'a> {
|
|
|
459
482
|
self.advance(); // {
|
|
460
483
|
let mut props = Vec::new();
|
|
461
484
|
while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
462
|
-
let key = self
|
|
485
|
+
let key = self
|
|
486
|
+
.expect(TokenKind::Ident)?
|
|
487
|
+
.literal
|
|
488
|
+
.clone()
|
|
463
489
|
.ok_or("Expected property name")?;
|
|
464
490
|
self.expect(TokenKind::Colon)?;
|
|
465
491
|
let typ = self.parse_type_annotation()?;
|
|
@@ -486,7 +512,7 @@ impl<'a> Parser<'a> {
|
|
|
486
512
|
}
|
|
487
513
|
self.expect(TokenKind::RParen)?;
|
|
488
514
|
// Expect => for return type
|
|
489
|
-
self.expect(TokenKind::Assign)?; // =
|
|
515
|
+
self.expect(TokenKind::Assign)?; // =
|
|
490
516
|
self.expect(TokenKind::Gt)?; // > (forms =>)
|
|
491
517
|
let returns = self.parse_type_annotation()?;
|
|
492
518
|
Ok(TypeAnnotation::Function {
|
|
@@ -523,7 +549,11 @@ impl<'a> Parser<'a> {
|
|
|
523
549
|
} else {
|
|
524
550
|
None
|
|
525
551
|
};
|
|
526
|
-
rest_param = Some(TypedParam {
|
|
552
|
+
rest_param = Some(TypedParam {
|
|
553
|
+
name: param_name,
|
|
554
|
+
type_ann,
|
|
555
|
+
default: None,
|
|
556
|
+
});
|
|
527
557
|
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
528
558
|
return Err("Rest parameter must be last".to_string());
|
|
529
559
|
}
|
|
@@ -535,7 +565,7 @@ impl<'a> Parser<'a> {
|
|
|
535
565
|
}
|
|
536
566
|
}
|
|
537
567
|
self.expect(TokenKind::RParen)?;
|
|
538
|
-
|
|
568
|
+
|
|
539
569
|
// Optional return type: `: Type`
|
|
540
570
|
let return_type = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
541
571
|
self.advance();
|
|
@@ -665,7 +695,10 @@ impl<'a> Parser<'a> {
|
|
|
665
695
|
if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
666
696
|
self.advance();
|
|
667
697
|
}
|
|
668
|
-
let cond = if matches!(
|
|
698
|
+
let cond = if matches!(
|
|
699
|
+
self.peek_kind(),
|
|
700
|
+
Some(TokenKind::Semicolon | TokenKind::RParen)
|
|
701
|
+
) {
|
|
669
702
|
None
|
|
670
703
|
} else {
|
|
671
704
|
let c = self.parse_expr()?;
|
|
@@ -780,11 +813,11 @@ impl<'a> Parser<'a> {
|
|
|
780
813
|
fn parse_try(&mut self) -> Result<Statement, String> {
|
|
781
814
|
let span_start = self.expect(TokenKind::Try)?.span.start;
|
|
782
815
|
let body = Box::new(self.parse_block_or_statement()?);
|
|
783
|
-
|
|
816
|
+
|
|
784
817
|
let mut catch_param = None;
|
|
785
818
|
let mut catch_body = None;
|
|
786
819
|
let mut finally_body = None;
|
|
787
|
-
|
|
820
|
+
|
|
788
821
|
if matches!(self.peek_kind(), Some(TokenKind::Catch)) {
|
|
789
822
|
self.advance();
|
|
790
823
|
self.expect(TokenKind::LParen)?;
|
|
@@ -792,16 +825,16 @@ impl<'a> Parser<'a> {
|
|
|
792
825
|
self.expect(TokenKind::RParen)?;
|
|
793
826
|
catch_body = Some(Box::new(self.parse_block_or_statement()?));
|
|
794
827
|
}
|
|
795
|
-
|
|
828
|
+
|
|
796
829
|
if matches!(self.peek_kind(), Some(TokenKind::Finally)) {
|
|
797
830
|
self.advance();
|
|
798
831
|
finally_body = Some(Box::new(self.parse_block_or_statement()?));
|
|
799
832
|
}
|
|
800
|
-
|
|
833
|
+
|
|
801
834
|
if catch_body.is_none() && finally_body.is_none() {
|
|
802
835
|
return Err("try statement requires catch or finally".to_string());
|
|
803
836
|
}
|
|
804
|
-
|
|
837
|
+
|
|
805
838
|
Ok(Statement::Try {
|
|
806
839
|
body,
|
|
807
840
|
catch_param,
|
|
@@ -920,7 +953,7 @@ impl<'a> Parser<'a> {
|
|
|
920
953
|
// Check for simple assignment
|
|
921
954
|
if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
922
955
|
let start = left.span().start;
|
|
923
|
-
|
|
956
|
+
|
|
924
957
|
// Variable assignment: x = val
|
|
925
958
|
if let Expr::Ident { name, .. } = &left {
|
|
926
959
|
let name = Arc::clone(name);
|
|
@@ -933,9 +966,14 @@ impl<'a> Parser<'a> {
|
|
|
933
966
|
span: Span { start, end },
|
|
934
967
|
});
|
|
935
968
|
}
|
|
936
|
-
|
|
969
|
+
|
|
937
970
|
// Member assignment: obj.prop = val
|
|
938
|
-
if let Expr::Member {
|
|
971
|
+
if let Expr::Member {
|
|
972
|
+
object,
|
|
973
|
+
prop: MemberProp::Name(prop_name),
|
|
974
|
+
..
|
|
975
|
+
} = &left
|
|
976
|
+
{
|
|
939
977
|
let object = Box::clone(object);
|
|
940
978
|
let prop = Arc::clone(prop_name);
|
|
941
979
|
self.advance(); // =
|
|
@@ -948,7 +986,7 @@ impl<'a> Parser<'a> {
|
|
|
948
986
|
span: Span { start, end },
|
|
949
987
|
});
|
|
950
988
|
}
|
|
951
|
-
|
|
989
|
+
|
|
952
990
|
// Index assignment: arr[idx] = val or obj[key] = val
|
|
953
991
|
if let Expr::Index { object, index, .. } = &left {
|
|
954
992
|
let object = Box::clone(object);
|
|
@@ -1060,7 +1098,7 @@ impl<'a> Parser<'a> {
|
|
|
1060
1098
|
binary_single_op!(parse_bit_or, parse_bit_xor, BitOr, BinOp::BitOr);
|
|
1061
1099
|
binary_single_op!(parse_bit_xor, parse_bit_and, BitXor, BinOp::BitXor);
|
|
1062
1100
|
binary_single_op!(parse_bit_and, parse_shift, BitAnd, BinOp::BitAnd);
|
|
1063
|
-
|
|
1101
|
+
|
|
1064
1102
|
binary_multi_op!(parse_shift, parse_equality, Shl => BinOp::Shl, Shr => BinOp::Shr);
|
|
1065
1103
|
binary_multi_op!(parse_equality, parse_comparison,
|
|
1066
1104
|
StrictEq => BinOp::StrictEq, StrictNe => BinOp::StrictNe,
|
|
@@ -1099,14 +1137,20 @@ impl<'a> Parser<'a> {
|
|
|
1099
1137
|
let operand = self.parse_unary()?;
|
|
1100
1138
|
if let Expr::Ident { name, span } = &operand {
|
|
1101
1139
|
let name = Arc::clone(name);
|
|
1102
|
-
let span = Span {
|
|
1140
|
+
let span = Span {
|
|
1141
|
+
start: span_start,
|
|
1142
|
+
end: span.end,
|
|
1143
|
+
};
|
|
1103
1144
|
return Ok(if is_inc {
|
|
1104
1145
|
Expr::PrefixInc { name, span }
|
|
1105
1146
|
} else {
|
|
1106
1147
|
Expr::PrefixDec { name, span }
|
|
1107
1148
|
});
|
|
1108
1149
|
}
|
|
1109
|
-
return Err(format!(
|
|
1150
|
+
return Err(format!(
|
|
1151
|
+
"Prefix {} requires an identifier",
|
|
1152
|
+
if is_inc { "++" } else { "--" }
|
|
1153
|
+
));
|
|
1110
1154
|
}
|
|
1111
1155
|
let op = match self.peek_kind() {
|
|
1112
1156
|
Some(TokenKind::Not) => UnaryOp::Not,
|
|
@@ -1134,7 +1178,10 @@ impl<'a> Parser<'a> {
|
|
|
1134
1178
|
let end = operand.span().end;
|
|
1135
1179
|
return Ok(Expr::Await {
|
|
1136
1180
|
operand: Box::new(operand),
|
|
1137
|
-
span: Span {
|
|
1181
|
+
span: Span {
|
|
1182
|
+
start: span_start,
|
|
1183
|
+
end,
|
|
1184
|
+
},
|
|
1138
1185
|
});
|
|
1139
1186
|
}
|
|
1140
1187
|
_ => return self.parse_postfix(),
|
|
@@ -1302,11 +1349,18 @@ impl<'a> Parser<'a> {
|
|
|
1302
1349
|
};
|
|
1303
1350
|
}
|
|
1304
1351
|
TokenKind::PlusPlus | TokenKind::MinusMinus => {
|
|
1305
|
-
if let Expr::Ident {
|
|
1352
|
+
if let Expr::Ident {
|
|
1353
|
+
name,
|
|
1354
|
+
span: ident_span,
|
|
1355
|
+
} = &expr
|
|
1356
|
+
{
|
|
1306
1357
|
let name = Arc::clone(name);
|
|
1307
1358
|
let is_inc = kind == TokenKind::PlusPlus;
|
|
1308
1359
|
let tok = self.advance().ok_or("Unexpected EOF")?;
|
|
1309
|
-
let span = Span {
|
|
1360
|
+
let span = Span {
|
|
1361
|
+
start: ident_span.start,
|
|
1362
|
+
end: tok.span.end,
|
|
1363
|
+
};
|
|
1310
1364
|
expr = if is_inc {
|
|
1311
1365
|
Expr::PostfixInc { name, span }
|
|
1312
1366
|
} else {
|
|
@@ -1374,7 +1428,10 @@ impl<'a> Parser<'a> {
|
|
|
1374
1428
|
default: None,
|
|
1375
1429
|
})],
|
|
1376
1430
|
body,
|
|
1377
|
-
span: Span {
|
|
1431
|
+
span: Span {
|
|
1432
|
+
start: span.start,
|
|
1433
|
+
end,
|
|
1434
|
+
},
|
|
1378
1435
|
});
|
|
1379
1436
|
}
|
|
1380
1437
|
Ok(Expr::Ident { name, span })
|
|
@@ -1418,7 +1475,10 @@ impl<'a> Parser<'a> {
|
|
|
1418
1475
|
match self.peek_kind() {
|
|
1419
1476
|
Some(TokenKind::Ident) => self.parse_jsx_element(span.start),
|
|
1420
1477
|
Some(TokenKind::Gt) => self.parse_jsx_fragment(span.start),
|
|
1421
|
-
_ => Err(format!(
|
|
1478
|
+
_ => Err(format!(
|
|
1479
|
+
"Invalid JSX: expected tag name or <> after <, got {:?}",
|
|
1480
|
+
self.peek_kind()
|
|
1481
|
+
)),
|
|
1422
1482
|
}
|
|
1423
1483
|
}
|
|
1424
1484
|
TokenKind::LBrace => {
|
|
@@ -1432,10 +1492,7 @@ impl<'a> Parser<'a> {
|
|
|
1432
1492
|
let key_tok = self.advance().ok_or("Expected object key")?;
|
|
1433
1493
|
let (key, key_span, is_ident_key) = match key_tok.kind {
|
|
1434
1494
|
TokenKind::Ident => {
|
|
1435
|
-
let k = key_tok
|
|
1436
|
-
.literal
|
|
1437
|
-
.clone()
|
|
1438
|
-
.ok_or("Expected key")?;
|
|
1495
|
+
let k = key_tok.literal.clone().ok_or("Expected key")?;
|
|
1439
1496
|
let sp = Span {
|
|
1440
1497
|
start: key_tok.span.start,
|
|
1441
1498
|
end: key_tok.span.end,
|
|
@@ -1443,20 +1500,19 @@ impl<'a> Parser<'a> {
|
|
|
1443
1500
|
(k, sp, true)
|
|
1444
1501
|
}
|
|
1445
1502
|
TokenKind::String => {
|
|
1446
|
-
let k = key_tok
|
|
1447
|
-
.literal
|
|
1448
|
-
.clone()
|
|
1449
|
-
.ok_or("Expected string key")?;
|
|
1503
|
+
let k = key_tok.literal.clone().ok_or("Expected string key")?;
|
|
1450
1504
|
let sp = Span {
|
|
1451
1505
|
start: key_tok.span.start,
|
|
1452
1506
|
end: key_tok.span.end,
|
|
1453
1507
|
};
|
|
1454
1508
|
(k, sp, false)
|
|
1455
1509
|
}
|
|
1456
|
-
_ =>
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1510
|
+
_ => {
|
|
1511
|
+
return Err(format!(
|
|
1512
|
+
"Expected object key (ident or string), got {:?}",
|
|
1513
|
+
key_tok.kind
|
|
1514
|
+
))
|
|
1515
|
+
}
|
|
1460
1516
|
};
|
|
1461
1517
|
let value = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
1462
1518
|
self.expect(TokenKind::Colon)?;
|
|
@@ -1499,12 +1555,12 @@ impl<'a> Parser<'a> {
|
|
|
1499
1555
|
// Template literal with interpolation: `text${
|
|
1500
1556
|
let mut quasis = vec![t.literal.clone().unwrap_or_default()];
|
|
1501
1557
|
let mut exprs = Vec::new();
|
|
1502
|
-
|
|
1558
|
+
|
|
1503
1559
|
loop {
|
|
1504
1560
|
// Parse the expression inside ${}
|
|
1505
1561
|
let expr = self.parse_expr()?;
|
|
1506
1562
|
exprs.push(expr);
|
|
1507
|
-
|
|
1563
|
+
|
|
1508
1564
|
// Next token should be TemplateMiddle or TemplateTail
|
|
1509
1565
|
let next = self.advance().ok_or("Unexpected EOF in template literal")?;
|
|
1510
1566
|
match next.kind {
|
|
@@ -1514,14 +1570,22 @@ impl<'a> Parser<'a> {
|
|
|
1514
1570
|
return Ok(Expr::TemplateLiteral {
|
|
1515
1571
|
quasis,
|
|
1516
1572
|
exprs,
|
|
1517
|
-
span: Span {
|
|
1573
|
+
span: Span {
|
|
1574
|
+
start: span.start,
|
|
1575
|
+
end,
|
|
1576
|
+
},
|
|
1518
1577
|
});
|
|
1519
1578
|
}
|
|
1520
1579
|
TokenKind::TemplateMiddle => {
|
|
1521
1580
|
quasis.push(next.literal.clone().unwrap_or_default());
|
|
1522
1581
|
// Continue parsing more expressions
|
|
1523
1582
|
}
|
|
1524
|
-
_ =>
|
|
1583
|
+
_ => {
|
|
1584
|
+
return Err(format!(
|
|
1585
|
+
"Expected template continuation, got {:?}",
|
|
1586
|
+
next.kind
|
|
1587
|
+
))
|
|
1588
|
+
}
|
|
1525
1589
|
}
|
|
1526
1590
|
}
|
|
1527
1591
|
}
|
|
@@ -1534,11 +1598,11 @@ impl<'a> Parser<'a> {
|
|
|
1534
1598
|
fn try_parse_arrow_function(&mut self, start_span: &Span) -> Result<Option<Expr>, String> {
|
|
1535
1599
|
// Save position for backtracking
|
|
1536
1600
|
let saved_pos = self.pos;
|
|
1537
|
-
|
|
1601
|
+
|
|
1538
1602
|
// Try to parse as arrow function params
|
|
1539
1603
|
let mut params = Vec::new();
|
|
1540
1604
|
let mut is_arrow = false;
|
|
1541
|
-
|
|
1605
|
+
|
|
1542
1606
|
// Check for empty params: () => ...
|
|
1543
1607
|
if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1544
1608
|
self.advance(); // consume )
|
|
@@ -1574,20 +1638,23 @@ impl<'a> Parser<'a> {
|
|
|
1574
1638
|
}
|
|
1575
1639
|
}
|
|
1576
1640
|
}
|
|
1577
|
-
|
|
1641
|
+
|
|
1578
1642
|
if !is_arrow {
|
|
1579
1643
|
// Backtrack - it's not an arrow function
|
|
1580
1644
|
self.pos = saved_pos;
|
|
1581
1645
|
return Ok(None);
|
|
1582
1646
|
}
|
|
1583
|
-
|
|
1647
|
+
|
|
1584
1648
|
let body = self.parse_arrow_body()?;
|
|
1585
1649
|
let end = self.previous_span_end();
|
|
1586
|
-
|
|
1650
|
+
|
|
1587
1651
|
Ok(Some(Expr::ArrowFunction {
|
|
1588
1652
|
params,
|
|
1589
1653
|
body,
|
|
1590
|
-
span: Span {
|
|
1654
|
+
span: Span {
|
|
1655
|
+
start: start_span.start,
|
|
1656
|
+
end,
|
|
1657
|
+
},
|
|
1591
1658
|
}))
|
|
1592
1659
|
}
|
|
1593
1660
|
|
|
@@ -1651,7 +1718,11 @@ impl<'a> Parser<'a> {
|
|
|
1651
1718
|
self.expect(TokenKind::RBrace)?; // }
|
|
1652
1719
|
JsxAttrValue::Expr(expr)
|
|
1653
1720
|
} else {
|
|
1654
|
-
let s = self
|
|
1721
|
+
let s = self
|
|
1722
|
+
.expect(TokenKind::String)?
|
|
1723
|
+
.literal
|
|
1724
|
+
.clone()
|
|
1725
|
+
.ok_or("Expected string")?;
|
|
1655
1726
|
JsxAttrValue::String(s)
|
|
1656
1727
|
};
|
|
1657
1728
|
props.push(JsxProp::Attr { name, value });
|
|
@@ -1662,7 +1733,12 @@ impl<'a> Parser<'a> {
|
|
|
1662
1733
|
});
|
|
1663
1734
|
}
|
|
1664
1735
|
}
|
|
1665
|
-
_ =>
|
|
1736
|
+
_ => {
|
|
1737
|
+
return Err(format!(
|
|
1738
|
+
"Unexpected token in JSX props: {:?}",
|
|
1739
|
+
self.peek_kind()
|
|
1740
|
+
))
|
|
1741
|
+
}
|
|
1666
1742
|
}
|
|
1667
1743
|
}
|
|
1668
1744
|
self.advance(); // consume >
|
|
@@ -1745,9 +1821,16 @@ impl<'a> Parser<'a> {
|
|
|
1745
1821
|
// </ closing tag
|
|
1746
1822
|
self.advance(); // <
|
|
1747
1823
|
self.advance(); // /
|
|
1748
|
-
let name = self
|
|
1824
|
+
let name = self
|
|
1825
|
+
.expect(TokenKind::Ident)?
|
|
1826
|
+
.literal
|
|
1827
|
+
.clone()
|
|
1828
|
+
.ok_or("Expected tag name")?;
|
|
1749
1829
|
if name.as_ref() != close_tag {
|
|
1750
|
-
return Err(format!(
|
|
1830
|
+
return Err(format!(
|
|
1831
|
+
"Mismatched JSX tag: expected </{}> got </{}>",
|
|
1832
|
+
close_tag, name
|
|
1833
|
+
));
|
|
1751
1834
|
}
|
|
1752
1835
|
self.expect(TokenKind::Gt)?; // >
|
|
1753
1836
|
return Ok(children);
|
|
@@ -1827,7 +1910,10 @@ impl<'a> Parser<'a> {
|
|
|
1827
1910
|
self.advance();
|
|
1828
1911
|
self.advance();
|
|
1829
1912
|
let end = self.previous_span_end();
|
|
1830
|
-
return Ok(Expr::JsxFragment {
|
|
1913
|
+
return Ok(Expr::JsxFragment {
|
|
1914
|
+
children,
|
|
1915
|
+
span: Span { start, end },
|
|
1916
|
+
});
|
|
1831
1917
|
}
|
|
1832
1918
|
}
|
|
1833
1919
|
return Err("Expected </> to close fragment".to_string());
|
|
@@ -1929,4 +2015,3 @@ impl ExprSpan for Expr {
|
|
|
1929
2015
|
}
|
|
1930
2016
|
}
|
|
1931
2017
|
}
|
|
1932
|
-
|
|
@@ -115,7 +115,8 @@ where
|
|
|
115
115
|
std::thread::spawn(move || {
|
|
116
116
|
std::thread::sleep(std::time::Duration::from_millis(50));
|
|
117
117
|
if let Ok(mut stream) = std::net::TcpStream::connect(format!("127.0.0.1:{}", port)) {
|
|
118
|
-
let _ = stream
|
|
118
|
+
let _ = stream
|
|
119
|
+
.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n");
|
|
119
120
|
let _ = stream.shutdown(std::net::Shutdown::Write);
|
|
120
121
|
}
|
|
121
122
|
});
|
|
@@ -239,13 +240,20 @@ pub fn value_to_response(value: &Value) -> (u16, Vec<(String, String)>, String)
|
|
|
239
240
|
/// If the response value has a "file" key, extract (status, headers, file_path) for streaming.
|
|
240
241
|
/// Used for binary files (e.g. .wasm) where readFile/body would fail.
|
|
241
242
|
fn extract_file_from_response(value: &Value) -> Option<(u16, Vec<(String, String)>, String)> {
|
|
242
|
-
let Value::Object(obj) = value else {
|
|
243
|
+
let Value::Object(obj) = value else {
|
|
244
|
+
return None;
|
|
245
|
+
};
|
|
243
246
|
let obj_ref = obj.borrow();
|
|
244
|
-
let Value::String(file_path) = obj_ref.get(&Arc::from("file"))? else {
|
|
247
|
+
let Value::String(file_path) = obj_ref.get(&Arc::from("file"))? else {
|
|
248
|
+
return None;
|
|
249
|
+
};
|
|
245
250
|
let file_path = file_path.to_string();
|
|
246
251
|
let status = obj_ref
|
|
247
252
|
.get(&Arc::from("status"))
|
|
248
|
-
.and_then(|v| match v {
|
|
253
|
+
.and_then(|v| match v {
|
|
254
|
+
Value::Number(n) => Some(*n as u16),
|
|
255
|
+
_ => None,
|
|
256
|
+
})
|
|
249
257
|
.unwrap_or(200);
|
|
250
258
|
let headers = obj_ref
|
|
251
259
|
.get(&Arc::from("headers"))
|
|
@@ -272,8 +280,9 @@ fn send_file_response(
|
|
|
272
280
|
Ok(f) => f,
|
|
273
281
|
Err(e) => {
|
|
274
282
|
eprintln!("Failed to open file {}: {}", file_path, e);
|
|
275
|
-
let fallback =
|
|
276
|
-
|
|
283
|
+
let fallback =
|
|
284
|
+
tiny_http::Response::from_string(format!("File not found: {}", file_path))
|
|
285
|
+
.with_status_code(tiny_http::StatusCode(500));
|
|
277
286
|
let _ = request.respond(fallback);
|
|
278
287
|
return;
|
|
279
288
|
}
|