@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.
- package/bin/tish +0 -0
- package/crates/js_to_tish/src/transform/expr.rs +5 -1
- package/crates/tish/Cargo.toml +1 -1
- package/crates/tish/src/cli_help.rs +12 -0
- package/crates/tish/src/main.rs +69 -11
- package/crates/tish_ast/src/ast.rs +12 -5
- package/crates/tish_build_utils/src/lib.rs +37 -0
- package/crates/tish_builtins/src/array.rs +82 -1
- package/crates/tish_builtins/src/globals.rs +50 -16
- package/crates/tish_builtins/src/math.rs +63 -9
- package/crates/tish_builtins/src/number.rs +20 -1
- package/crates/tish_builtins/src/string.rs +68 -4
- package/crates/tish_builtins/src/typedarrays.rs +23 -16
- package/crates/tish_bytecode/src/compiler.rs +94 -28
- package/crates/tish_bytecode/tests/break_continue_bytecode.rs +19 -0
- package/crates/tish_compile/src/check.rs +11 -10
- package/crates/tish_compile/src/codegen.rs +1386 -42
- package/crates/tish_compile/src/infer.rs +16 -16
- package/crates/tish_compile/src/lib.rs +38 -0
- package/crates/tish_compile/src/resolve.rs +3 -2
- package/crates/tish_compile/src/types.rs +26 -9
- package/crates/tish_compile_js/src/codegen.rs +55 -4
- package/crates/tish_compile_js/src/tests_jsx.rs +44 -0
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +1 -0
- package/crates/tish_core/Cargo.toml +2 -0
- package/crates/tish_core/src/json.rs +135 -34
- package/crates/tish_core/src/lib.rs +23 -1
- package/crates/tish_core/src/value.rs +64 -11
- package/crates/tish_eval/src/eval.rs +144 -197
- package/crates/tish_eval/src/natives.rs +45 -45
- package/crates/tish_eval/src/regex.rs +35 -27
- package/crates/tish_eval/src/value.rs +8 -0
- package/crates/tish_fmt/src/lib.rs +46 -2
- package/crates/tish_lint/src/bin/tish-lint.rs +17 -8
- package/crates/tish_lint/src/lib.rs +197 -21
- package/crates/tish_lsp/Cargo.toml +7 -1
- package/crates/tish_lsp/src/import_goto.rs +52 -7
- package/crates/tish_lsp/src/main.rs +913 -145
- package/crates/tish_opt/src/lib.rs +5 -3
- package/crates/tish_parser/src/lib.rs +23 -5
- package/crates/tish_parser/src/parser.rs +35 -35
- package/crates/tish_resolve/src/lib.rs +567 -17
- package/crates/tish_runtime/src/lib.rs +58 -18
- package/crates/tish_ui/src/jsx.rs +2 -2
- package/crates/tish_vm/src/jit.rs +212 -10
- package/crates/tish_vm/src/vm.rs +39 -18
- package/crates/tish_wasm/src/lib.rs +116 -22
- 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
|
@@ -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
|
-
|
|
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 =
|
|
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![
|
|
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
|
-
|
|
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
|
-
//
|
|
978
|
-
// the
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
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)?;
|