@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.
Files changed (79) hide show
  1. package/Cargo.toml +1 -0
  2. package/bin/tish +0 -0
  3. package/crates/js_to_tish/src/error.rs +2 -8
  4. package/crates/js_to_tish/src/transform/expr.rs +101 -130
  5. package/crates/js_to_tish/src/transform/stmt.rs +25 -22
  6. package/crates/tish/Cargo.toml +1 -1
  7. package/crates/tish/src/cli_help.rs +76 -29
  8. package/crates/tish/src/main.rs +85 -54
  9. package/crates/tish/tests/cargo_example_compile.rs +3 -1
  10. package/crates/tish/tests/integration_test.rs +197 -47
  11. package/crates/tish/tests/run_optimize_stdout_parity.rs +3 -7
  12. package/crates/tish/tests/shortcircuit.rs +19 -4
  13. package/crates/tish_ast/src/ast.rs +12 -14
  14. package/crates/tish_build_utils/src/lib.rs +31 -6
  15. package/crates/tish_builtins/src/array.rs +52 -21
  16. package/crates/tish_builtins/src/construct.rs +2 -8
  17. package/crates/tish_builtins/src/globals.rs +30 -15
  18. package/crates/tish_builtins/src/lib.rs +5 -5
  19. package/crates/tish_builtins/src/math.rs +5 -3
  20. package/crates/tish_builtins/src/string.rs +71 -19
  21. package/crates/tish_bytecode/src/chunk.rs +0 -1
  22. package/crates/tish_bytecode/src/compiler.rs +164 -60
  23. package/crates/tish_bytecode/src/opcode.rs +13 -4
  24. package/crates/tish_bytecode/src/peephole.rs +2 -2
  25. package/crates/tish_compile/src/codegen.rs +921 -299
  26. package/crates/tish_compile/src/infer.rs +69 -19
  27. package/crates/tish_compile/src/lib.rs +15 -5
  28. package/crates/tish_compile/src/resolve.rs +112 -69
  29. package/crates/tish_compile/src/types.rs +10 -14
  30. package/crates/tish_compile_js/src/codegen.rs +34 -13
  31. package/crates/tish_compile_js/src/tests_jsx.rs +30 -6
  32. package/crates/tish_compiler_wasm/src/lib.rs +16 -13
  33. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +39 -48
  34. package/crates/tish_core/src/json.rs +5 -3
  35. package/crates/tish_core/src/lib.rs +1 -1
  36. package/crates/tish_core/src/uri.rs +9 -6
  37. package/crates/tish_core/src/value.rs +92 -28
  38. package/crates/tish_cranelift/src/link.rs +6 -9
  39. package/crates/tish_cranelift/src/lower.rs +14 -8
  40. package/crates/tish_eval/src/eval.rs +389 -142
  41. package/crates/tish_eval/src/lib.rs +10 -6
  42. package/crates/tish_eval/src/natives.rs +95 -38
  43. package/crates/tish_eval/src/promise.rs +14 -8
  44. package/crates/tish_eval/src/timers.rs +28 -19
  45. package/crates/tish_eval/src/value.rs +10 -3
  46. package/crates/tish_fmt/src/lib.rs +29 -13
  47. package/crates/tish_lexer/src/lib.rs +217 -63
  48. package/crates/tish_lexer/src/token.rs +6 -6
  49. package/crates/tish_llvm/src/lib.rs +15 -8
  50. package/crates/tish_lsp/src/main.rs +41 -43
  51. package/crates/tish_native/src/build.rs +1 -6
  52. package/crates/tish_native/src/lib.rs +48 -19
  53. package/crates/tish_opt/src/lib.rs +67 -50
  54. package/crates/tish_parser/src/lib.rs +36 -11
  55. package/crates/tish_parser/src/parser.rs +172 -87
  56. package/crates/tish_runtime/src/http.rs +15 -6
  57. package/crates/tish_runtime/src/http_fetch.rs +24 -14
  58. package/crates/tish_runtime/src/lib.rs +224 -168
  59. package/crates/tish_runtime/src/promise.rs +1 -5
  60. package/crates/tish_runtime/src/ws.rs +45 -20
  61. package/crates/tish_runtime/tests/fetch_readable_stream.rs +5 -4
  62. package/crates/tish_ui/src/jsx.rs +41 -22
  63. package/crates/tish_ui/src/lib.rs +2 -2
  64. package/crates/tish_vm/src/vm.rs +309 -112
  65. package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +8 -3
  66. package/crates/tish_wasm/src/lib.rs +38 -28
  67. package/crates/tishlang_cargo_bindgen/Cargo.toml +25 -0
  68. package/crates/tishlang_cargo_bindgen/src/classify.rs +265 -0
  69. package/crates/tishlang_cargo_bindgen/src/discover.rs +52 -0
  70. package/crates/tishlang_cargo_bindgen/src/infer.rs +372 -0
  71. package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
  72. package/crates/tishlang_cargo_bindgen/src/main.rs +164 -0
  73. package/crates/tishlang_cargo_bindgen/src/metadata.rs +114 -0
  74. package/package.json +1 -1
  75. package/platform/darwin-arm64/tish +0 -0
  76. package/platform/darwin-x64/tish +0 -0
  77. package/platform/linux-arm64/tish +0 -0
  78. package/platform/linux-x64/tish +0 -0
  79. 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
- ArrowBody, ArrayElement, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern,
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.advance().ok_or_else(|| format!("Expected {:?}, got EOF", kind))?;
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!("Expected {:?}, got {:?} at {:?}", kind, t.kind, t.span))
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!(self.peek_kind(), Some(TokenKind::RBrace | TokenKind::Dedent)) {
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!(self.peek_kind(), Some(TokenKind::LBracket) | Some(TokenKind::LBrace)) {
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.expect(TokenKind::Ident)?.literal.clone().ok_or("Expected identifier")?;
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.advance().ok_or("Unexpected EOF")?.literal.clone().ok_or("Expected identifier")?;
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.expect(TokenKind::Ident)?.literal.clone().ok_or("Expected identifier")?;
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.advance().ok_or("Unexpected EOF")?.literal.clone().ok_or("Expected identifier")?;
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.expect(TokenKind::Ident)?.literal.clone()
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 { name: param_name, type_ann, default: None });
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!(self.peek_kind(), Some(TokenKind::Semicolon | TokenKind::RParen)) {
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 { object, prop: MemberProp::Name(prop_name), .. } = &left {
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 { start: span_start, end: span.end };
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!("Prefix {} requires an identifier", if is_inc { "++" } else { "--" }));
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 { start: span_start, end },
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 { name, span: ident_span } = &expr {
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 { start: ident_span.start, end: tok.span.end };
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 { start: span.start, end },
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!("Invalid JSX: expected tag name or <> after <, got {:?}", self.peek_kind())),
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
- _ => return Err(format!(
1457
- "Expected object key (ident or string), got {:?}",
1458
- key_tok.kind
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 { start: span.start, end },
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
- _ => return Err(format!("Expected template continuation, got {:?}", next.kind)),
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 { start: start_span.start, end },
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.expect(TokenKind::String)?.literal.clone().ok_or("Expected string")?;
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
- _ => return Err(format!("Unexpected token in JSX props: {:?}", self.peek_kind())),
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.expect(TokenKind::Ident)?.literal.clone().ok_or("Expected tag name")?;
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!("Mismatched JSX tag: expected </{}> got </{}>", close_tag, name));
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 { children, span: Span { start, end } });
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.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n");
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 { return None };
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 { return None };
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 { Value::Number(n) => Some(*n as u16), _ => None })
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 = tiny_http::Response::from_string(format!("File not found: {}", file_path))
276
- .with_status_code(tiny_http::StatusCode(500));
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
  }