@tishlang/tish 2.2.5 → 2.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.
Files changed (53) hide show
  1. package/bin/tish +0 -0
  2. package/crates/js_to_tish/src/transform/expr.rs +5 -1
  3. package/crates/tish/Cargo.toml +1 -1
  4. package/crates/tish/src/cli_help.rs +12 -0
  5. package/crates/tish/src/main.rs +69 -11
  6. package/crates/tish_ast/src/ast.rs +12 -5
  7. package/crates/tish_build_utils/src/lib.rs +37 -0
  8. package/crates/tish_builtins/src/array.rs +82 -1
  9. package/crates/tish_builtins/src/globals.rs +50 -16
  10. package/crates/tish_builtins/src/math.rs +63 -9
  11. package/crates/tish_builtins/src/number.rs +20 -1
  12. package/crates/tish_builtins/src/string.rs +68 -4
  13. package/crates/tish_builtins/src/typedarrays.rs +23 -16
  14. package/crates/tish_bytecode/src/compiler.rs +94 -28
  15. package/crates/tish_bytecode/tests/break_continue_bytecode.rs +19 -0
  16. package/crates/tish_compile/src/check.rs +11 -10
  17. package/crates/tish_compile/src/codegen.rs +1386 -42
  18. package/crates/tish_compile/src/infer.rs +16 -16
  19. package/crates/tish_compile/src/lib.rs +38 -0
  20. package/crates/tish_compile/src/resolve.rs +3 -2
  21. package/crates/tish_compile/src/types.rs +26 -9
  22. package/crates/tish_compile_js/src/codegen.rs +55 -4
  23. package/crates/tish_compile_js/src/tests_jsx.rs +44 -0
  24. package/crates/tish_compiler_wasm/src/resolve_virtual.rs +1 -0
  25. package/crates/tish_core/Cargo.toml +2 -0
  26. package/crates/tish_core/src/json.rs +135 -34
  27. package/crates/tish_core/src/lib.rs +23 -1
  28. package/crates/tish_core/src/value.rs +64 -11
  29. package/crates/tish_eval/src/eval.rs +144 -197
  30. package/crates/tish_eval/src/natives.rs +45 -45
  31. package/crates/tish_eval/src/regex.rs +35 -27
  32. package/crates/tish_eval/src/value.rs +8 -0
  33. package/crates/tish_fmt/src/lib.rs +46 -2
  34. package/crates/tish_lint/src/bin/tish-lint.rs +17 -8
  35. package/crates/tish_lint/src/lib.rs +197 -21
  36. package/crates/tish_lsp/Cargo.toml +7 -1
  37. package/crates/tish_lsp/src/import_goto.rs +52 -7
  38. package/crates/tish_lsp/src/main.rs +913 -145
  39. package/crates/tish_opt/src/lib.rs +5 -3
  40. package/crates/tish_parser/src/lib.rs +23 -5
  41. package/crates/tish_parser/src/parser.rs +35 -35
  42. package/crates/tish_resolve/src/lib.rs +567 -17
  43. package/crates/tish_runtime/src/lib.rs +58 -18
  44. package/crates/tish_ui/src/jsx.rs +2 -2
  45. package/crates/tish_vm/src/jit.rs +212 -10
  46. package/crates/tish_vm/src/vm.rs +39 -18
  47. package/crates/tish_wasm/src/lib.rs +116 -22
  48. package/package.json +1 -1
  49. package/platform/darwin-arm64/tish +0 -0
  50. package/platform/darwin-x64/tish +0 -0
  51. package/platform/linux-arm64/tish +0 -0
  52. package/platform/linux-x64/tish +0 -0
  53. package/platform/win32-x64/tish.exe +0 -0
@@ -429,8 +429,8 @@ fn optimize_expr(expr: &Expr) -> Expr {
429
429
  props: props
430
430
  .iter()
431
431
  .map(|p| match p {
432
- tishlang_ast::ObjectProp::KeyValue(k, v) => {
433
- tishlang_ast::ObjectProp::KeyValue(Arc::clone(k), optimize_expr(v))
432
+ tishlang_ast::ObjectProp::KeyValue(k, v, s) => {
433
+ tishlang_ast::ObjectProp::KeyValue(Arc::clone(k), optimize_expr(v), *s)
434
434
  }
435
435
  tishlang_ast::ObjectProp::Spread(e) => {
436
436
  tishlang_ast::ObjectProp::Spread(optimize_expr(e))
@@ -578,7 +578,9 @@ fn js_number_to_string(value: f64) -> String {
578
578
  return "-Infinity".to_string();
579
579
  }
580
580
  if value == 0.0 {
581
- return if value.is_sign_negative() { "-0" } else { "0" }.to_string();
581
+ // ECMAScript `Number::toString`: both `+0` and `-0` `"0"` (a constant-folded
582
+ // `"" + (-0)` must match the runtime ToString, not the inspect form). (#247)
583
+ return "0".to_string();
582
584
  }
583
585
  let negative = value < 0.0;
584
586
  let sci = format!("{:e}", value.abs());
@@ -45,6 +45,24 @@ mod tests {
45
45
  }
46
46
  }
47
47
 
48
+ // #158: a block's span — and the enclosing function's span — must end at the closing `}`, not
49
+ // overrun onto the statement on the following line.
50
+ #[test]
51
+ fn block_and_fn_spans_stop_at_closing_brace() {
52
+ // `}` is on line 3 (1-based); `let after` begins on line 4.
53
+ let program = parse("fn f(a) {\n let x = a\n}\nlet after = 1\n").expect("parse");
54
+ let Statement::FunDecl { span, body, .. } = &program.statements[0] else {
55
+ panic!("expected FunDecl");
56
+ };
57
+ assert_eq!(span.end.0, 3, "fn span must end on the `}}` line, not the next statement: {span:?}");
58
+ assert_eq!(
59
+ body.span().end.0,
60
+ 3,
61
+ "body block span must end on the `}}` line: {:?}",
62
+ body.span()
63
+ );
64
+ }
65
+
48
66
  #[test]
49
67
  fn test_object_literal_shorthand_single() {
50
68
  let program = parse("const o = { port }").expect("parse object shorthand");
@@ -62,7 +80,7 @@ mod tests {
62
80
  };
63
81
  assert_eq!(props.len(), 1);
64
82
  match &props[0] {
65
- ObjectProp::KeyValue(k, v) => {
83
+ ObjectProp::KeyValue(k, v, _) => {
66
84
  assert_eq!(k.as_ref(), "port");
67
85
  if let Expr::Ident { ref name, .. } = v {
68
86
  assert_eq!(name.as_ref(), "port");
@@ -92,11 +110,11 @@ mod tests {
92
110
  };
93
111
  assert_eq!(props.len(), 2);
94
112
  match &props[0] {
95
- ObjectProp::KeyValue(k, _) => assert_eq!(k.as_ref(), "ai-a"),
113
+ ObjectProp::KeyValue(k, _, _) => assert_eq!(k.as_ref(), "ai-a"),
96
114
  _ => panic!("expected KeyValue prop"),
97
115
  }
98
116
  match &props[1] {
99
- ObjectProp::KeyValue(k, _) => assert_eq!(k.as_ref(), "human"),
117
+ ObjectProp::KeyValue(k, _, _) => assert_eq!(k.as_ref(), "human"),
100
118
  _ => panic!("expected KeyValue prop"),
101
119
  }
102
120
  }
@@ -118,7 +136,7 @@ mod tests {
118
136
  };
119
137
  assert_eq!(props.len(), 2);
120
138
  match &props[0] {
121
- ObjectProp::KeyValue(k, v) => {
139
+ ObjectProp::KeyValue(k, v, _) => {
122
140
  assert_eq!(k.as_ref(), "port");
123
141
  if let Expr::Ident { ref name, .. } = v {
124
142
  assert_eq!(name.as_ref(), "port");
@@ -129,7 +147,7 @@ mod tests {
129
147
  _ => panic!("expected KeyValue prop"),
130
148
  }
131
149
  match &props[1] {
132
- ObjectProp::KeyValue(k, v) => {
150
+ ObjectProp::KeyValue(k, v, _) => {
133
151
  assert_eq!(k.as_ref(), "x");
134
152
  if let Expr::Literal { .. } = v {
135
153
  // x: 1
@@ -68,7 +68,7 @@ fn mangle_generic(base: &str, args: &[TypeAnnotation]) -> String {
68
68
 
69
69
  fn mangle_type(t: &TypeAnnotation) -> String {
70
70
  match t {
71
- TypeAnnotation::Simple(s) => s.chars().map(|c| if c.is_alphanumeric() { c } else { '_' }).collect(),
71
+ TypeAnnotation::Simple(s, _) => s.chars().map(|c| if c.is_alphanumeric() { c } else { '_' }).collect(),
72
72
  TypeAnnotation::Array(inner) => format!("{}Arr", mangle_type(inner)),
73
73
  TypeAnnotation::Tuple(es) => {
74
74
  format!("Tup{}", es.iter().map(mangle_type).collect::<Vec<_>>().join(""))
@@ -84,7 +84,7 @@ fn mangle_type(t: &TypeAnnotation) -> String {
84
84
  /// Substitute generic type parameters with their concrete arguments throughout a type body.
85
85
  fn subst_type(t: &TypeAnnotation, map: &HashMap<&str, &TypeAnnotation>) -> TypeAnnotation {
86
86
  match t {
87
- TypeAnnotation::Simple(s) => map
87
+ TypeAnnotation::Simple(s, _) => map
88
88
  .get(s.as_ref())
89
89
  .map(|rep| (*rep).clone())
90
90
  .unwrap_or_else(|| t.clone()),
@@ -364,27 +364,21 @@ impl<'a> Parser<'a> {
364
364
  statements.push(self.parse_statement()?);
365
365
  }
366
366
 
367
+ // Block ends at its CLOSING `}`/`Dedent`. Capture that token's end BEFORE advancing past it:
368
+ // reading `self.peek()` *after* the advance pointed at the NEXT token, overrunning the block
369
+ // span onto the following statement — so a cursor on the line after `}` was still "inside"
370
+ // the block and completion offered the body's params/locals there (#158). Fall back to the
371
+ // last inner statement's end for an unterminated block at EOF, else the block start.
372
+ let mut closer_end: Option<(usize, usize)> = None;
367
373
  if matches!(
368
374
  self.peek_kind(),
369
375
  Some(TokenKind::RBrace | TokenKind::Dedent)
370
376
  ) {
377
+ closer_end = self.peek().map(|t| t.span.end);
371
378
  self.advance();
372
379
  }
373
-
374
- let peek_end = self.peek().map(|x| x.span.end);
375
380
  let last_end = statements.last().map(|s| s.span().end);
376
- let end = match (peek_end, last_end) {
377
- (Some(p), Some(l)) => {
378
- if p.0 > l.0 || (p.0 == l.0 && p.1 > l.1) {
379
- p
380
- } else {
381
- l
382
- }
383
- }
384
- (Some(p), None) => p,
385
- (None, Some(l)) => l,
386
- (None, None) => span_start,
387
- };
381
+ let end = closer_end.or(last_end).unwrap_or(span_start);
388
382
 
389
383
  Ok(Statement::Block {
390
384
  statements,
@@ -758,7 +752,10 @@ impl<'a> Parser<'a> {
758
752
  }
759
753
  Some(TokenKind::Question) => {
760
754
  self.advance(); // ?
761
- t = TypeAnnotation::Union(vec![t, TypeAnnotation::Simple("null".into())]);
755
+ t = TypeAnnotation::Union(vec![
756
+ t,
757
+ TypeAnnotation::Simple("null".into(), Span::default()),
758
+ ]);
762
759
  }
763
760
  _ => break,
764
761
  }
@@ -772,6 +769,10 @@ impl<'a> Parser<'a> {
772
769
  Some(TokenKind::Ident) => {
773
770
  let tok = self.advance().ok_or("Expected type name")?;
774
771
  let name = tok.literal.clone().ok_or("Expected type name")?;
772
+ let span = Span {
773
+ start: tok.span.start,
774
+ end: tok.span.end,
775
+ };
775
776
  // Generic reference `Name<Args>`: `Array<T>` desugars to the native `T[]`; other
776
777
  // generic refs erase their args (the base name resolves to its alias, whose type
777
778
  // params already act as unknown -> `Value`).
@@ -786,25 +787,29 @@ impl<'a> Parser<'a> {
786
787
  // alias `Box__number` (a native struct). Falls back to erasing the args when
787
788
  // `name` isn't a known generic alias (e.g. forward reference) or arity mismatches.
788
789
  if let Some(spec) = self.monomorphize_generic(name.as_ref(), &args) {
789
- return Ok(TypeAnnotation::Simple(Arc::from(spec.as_str())));
790
+ return Ok(TypeAnnotation::Simple(Arc::from(spec.as_str()), span));
790
791
  }
791
- return Ok(TypeAnnotation::Simple(name));
792
+ return Ok(TypeAnnotation::Simple(name, span));
792
793
  }
793
- Ok(TypeAnnotation::Simple(name))
794
+ Ok(TypeAnnotation::Simple(name, span))
794
795
  }
795
796
  Some(TokenKind::Type | TokenKind::Declare) => {
796
797
  let tok = self.advance().ok_or("Expected type name")?;
797
798
  let name = tok.literal.clone().ok_or("Expected type name")?;
798
- Ok(TypeAnnotation::Simple(name))
799
+ let span = Span {
800
+ start: tok.span.start,
801
+ end: tok.span.end,
802
+ };
803
+ Ok(TypeAnnotation::Simple(name, span))
799
804
  }
800
805
  // Handle keywords that can be type names
801
806
  Some(TokenKind::Null) => {
802
807
  self.advance();
803
- Ok(TypeAnnotation::Simple("null".into()))
808
+ Ok(TypeAnnotation::Simple("null".into(), Span::default()))
804
809
  }
805
810
  Some(TokenKind::Void) => {
806
811
  self.advance();
807
- Ok(TypeAnnotation::Simple("void".into()))
812
+ Ok(TypeAnnotation::Simple("void".into(), Span::default()))
808
813
  }
809
814
  Some(TokenKind::LBrace) => {
810
815
  // Object type: { key: Type, ... }
@@ -974,17 +979,12 @@ impl<'a> Parser<'a> {
974
979
  Box::new(self.parse_block()?)
975
980
  };
976
981
 
977
- // Span must cover the whole declaration through the body. `peek().start` alone can sit on
978
- // the opening `{` (same as `span_start` at EOF) or otherwise truncate before inner spans.
979
- let peek_start = self.peek().map(|t| t.span.start).unwrap_or(span_start);
980
- let body_end = body.as_ref().span().end;
981
- let end = if peek_start.0 > body_end.0
982
- || (peek_start.0 == body_end.0 && peek_start.1 > body_end.1)
983
- {
984
- peek_start
985
- } else {
986
- body_end
987
- };
982
+ // The declaration spans from `fn`/`async` through the END of its body. `parse_block` now
983
+ // ends the body block exactly at its closing `}` (#158), so the body's end IS the function's
984
+ // end. (This previously maxed with `peek().start` — the NEXT token after the function — to
985
+ // compensate for the old block-span truncation, which instead overran the function span past
986
+ // `}` and inflated its folding / document-symbol range onto the following line. #158)
987
+ let end = body.as_ref().span().end;
988
988
 
989
989
  Ok(Statement::FunDecl {
990
990
  async_,
@@ -2232,7 +2232,7 @@ impl<'a> Parser<'a> {
2232
2232
  return Err("String key in object literal requires explicit value (key: value)".to_string());
2233
2233
  }
2234
2234
  };
2235
- props.push(ObjectProp::KeyValue(key, value));
2235
+ props.push(ObjectProp::KeyValue(key, value, key_span));
2236
2236
  }
2237
2237
  if !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
2238
2238
  self.expect(TokenKind::Comma)?;