@tishlang/tish 1.13.2 → 2.0.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/bin/tish +0 -0
- package/crates/js_to_tish/src/transform/expr.rs +1 -0
- package/crates/tish/Cargo.toml +11 -3
- package/crates/tish/build.rs +21 -0
- package/crates/tish/src/cli_help.rs +15 -4
- package/crates/tish/src/main.rs +93 -21
- package/crates/tish/src/repl_completion.rs +0 -1
- package/crates/tish/tests/error_source_location.rs +36 -0
- package/crates/tish/tests/fixtures/runtime_error_location.tish +5 -0
- package/crates/tish/tests/fixtures/trycatch_runtime_errors.tish +15 -0
- package/crates/tish/tests/fixtures/tty_capability.tish +9 -0
- package/crates/tish/tests/integration_test.rs +402 -91
- package/crates/tish/tests/trycatch_runtime_errors.rs +45 -0
- package/crates/tish/tests/tty_capability.rs +43 -0
- package/crates/tish_ast/src/ast.rs +37 -8
- package/crates/tish_builtins/Cargo.toml +2 -0
- package/crates/tish_builtins/src/array.rs +375 -13
- package/crates/tish_builtins/src/collections.rs +481 -0
- package/crates/tish_builtins/src/construct.rs +59 -19
- package/crates/tish_builtins/src/date.rs +538 -0
- package/crates/tish_builtins/src/globals.rs +86 -6
- package/crates/tish_builtins/src/iterator.rs +129 -0
- package/crates/tish_builtins/src/lib.rs +5 -0
- package/crates/tish_builtins/src/number.rs +96 -0
- package/crates/tish_builtins/src/object.rs +2 -2
- package/crates/tish_builtins/src/string.rs +19 -20
- package/crates/tish_builtins/src/symbol.rs +1 -1
- package/crates/tish_builtins/src/typedarrays.rs +298 -0
- package/crates/tish_bytecode/src/chunk.rs +69 -1
- package/crates/tish_bytecode/src/compiler.rs +933 -89
- package/crates/tish_bytecode/src/encoding.rs +2 -0
- package/crates/tish_bytecode/src/lib.rs +2 -1
- package/crates/tish_bytecode/src/opcode.rs +47 -4
- package/crates/tish_bytecode/src/serialize.rs +31 -1
- package/crates/tish_compile/Cargo.toml +1 -0
- package/crates/tish_compile/src/check.rs +774 -0
- package/crates/tish_compile/src/codegen.rs +2334 -349
- package/crates/tish_compile/src/infer.rs +1395 -6
- package/crates/tish_compile/src/lib.rs +50 -8
- package/crates/tish_compile/src/resolve.rs +584 -21
- package/crates/tish_compile/src/types.rs +106 -2
- package/crates/tish_compile_js/src/codegen.rs +67 -0
- package/crates/tish_compile_js/src/tests_jsx.rs +64 -0
- package/crates/tish_core/Cargo.toml +7 -1
- package/crates/tish_core/src/console_style.rs +11 -1
- package/crates/tish_core/src/json.rs +81 -38
- package/crates/tish_core/src/lib.rs +3 -0
- package/crates/tish_core/src/shape.rs +85 -0
- package/crates/tish_core/src/value.rs +679 -25
- package/crates/tish_core/src/vmref.rs +13 -8
- package/crates/tish_cranelift/src/link.rs +17 -4
- package/crates/tish_cranelift_runtime/Cargo.toml +1 -0
- package/crates/tish_eval/Cargo.toml +6 -0
- package/crates/tish_eval/src/eval.rs +665 -117
- package/crates/tish_eval/src/http.rs +4 -1
- package/crates/tish_eval/src/natives.rs +165 -13
- package/crates/tish_eval/src/value.rs +31 -13
- package/crates/tish_eval/src/value_convert.rs +10 -4
- package/crates/tish_ffi/Cargo.toml +26 -0
- package/crates/tish_ffi/src/lib.rs +518 -0
- package/crates/tish_ffi/tests/fixtures/testmod/Cargo.toml +18 -0
- package/crates/tish_ffi/tests/fixtures/testmod/src/lib.rs +46 -0
- package/crates/tish_ffi/tests/loader.rs +65 -0
- package/crates/tish_fmt/src/lib.rs +43 -5
- package/crates/tish_lexer/src/lib.rs +397 -9
- package/crates/tish_lexer/src/token.rs +7 -0
- package/crates/tish_lint/src/lib.rs +2 -10
- package/crates/tish_lsp/src/import_goto.rs +2 -0
- package/crates/tish_lsp/src/main.rs +439 -26
- package/crates/tish_native/src/build.rs +55 -1
- package/crates/tish_opt/src/lib.rs +126 -23
- package/crates/tish_parser/src/lib.rs +55 -1
- package/crates/tish_parser/src/parser.rs +456 -34
- package/crates/tish_pg/src/lib.rs +3 -3
- package/crates/tish_resolve/src/lib.rs +99 -59
- package/crates/tish_runtime/Cargo.toml +4 -0
- package/crates/tish_runtime/src/http.rs +66 -17
- package/crates/tish_runtime/src/http_fetch.rs +29 -8
- package/crates/tish_runtime/src/http_hyper.rs +25 -2
- package/crates/tish_runtime/src/lib.rs +299 -44
- package/crates/tish_runtime/src/promise.rs +328 -18
- package/crates/tish_runtime/src/timers.rs +13 -7
- package/crates/tish_runtime/src/tty.rs +226 -0
- package/crates/tish_runtime/src/ws.rs +35 -18
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +2 -2
- package/crates/tish_ui/src/jsx.rs +10 -0
- package/crates/tish_ui/src/runtime/hooks.rs +19 -15
- package/crates/tish_ui/src/runtime/mod.rs +15 -12
- package/crates/tish_vm/Cargo.toml +14 -1
- package/crates/tish_vm/src/jit.rs +1050 -0
- package/crates/tish_vm/src/lib.rs +2 -0
- package/crates/tish_vm/src/vm.rs +1546 -202
- package/crates/tish_vm/tests/concurrent_shared_state.rs +140 -0
- package/crates/tish_wasm/src/lib.rs +6 -2
- package/crates/tish_wasm_runtime/src/gpu.rs +17 -1
- package/crates/tishlang_cargo_bindgen/src/classify.rs +1 -3
- package/crates/tishlang_cargo_bindgen/src/lib.rs +2 -2
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +1 -1
- 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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
//! Recursive descent parser for Tish.
|
|
2
2
|
|
|
3
|
+
use std::collections::{HashMap, HashSet};
|
|
3
4
|
use std::sync::Arc;
|
|
4
5
|
|
|
5
6
|
/// Macro to generate single-operator binary parsing functions.
|
|
@@ -55,18 +56,112 @@ use tishlang_ast::{
|
|
|
55
56
|
ArrayElement, ArrowBody, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern,
|
|
56
57
|
DestructProp, ExportDeclaration, Expr, FunParam, ImportSpecifier, JsxAttrValue, JsxChild,
|
|
57
58
|
JsxProp, Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Span, Statement,
|
|
58
|
-
TypeAnnotation, TypedParam, UnaryOp,
|
|
59
|
+
TypeAnnotation, TypeLiteral, TypedParam, UnaryOp,
|
|
59
60
|
};
|
|
60
61
|
use tishlang_lexer::{Token, TokenKind};
|
|
61
62
|
|
|
63
|
+
/// Mangle a generic instantiation `base<args>` into a Rust-ident-safe alias name (`Box__number`).
|
|
64
|
+
fn mangle_generic(base: &str, args: &[TypeAnnotation]) -> String {
|
|
65
|
+
let parts: Vec<String> = args.iter().map(mangle_type).collect();
|
|
66
|
+
format!("{}__{}", base, parts.join("_"))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
fn mangle_type(t: &TypeAnnotation) -> String {
|
|
70
|
+
match t {
|
|
71
|
+
TypeAnnotation::Simple(s) => s.chars().map(|c| if c.is_alphanumeric() { c } else { '_' }).collect(),
|
|
72
|
+
TypeAnnotation::Array(inner) => format!("{}Arr", mangle_type(inner)),
|
|
73
|
+
TypeAnnotation::Tuple(es) => {
|
|
74
|
+
format!("Tup{}", es.iter().map(mangle_type).collect::<Vec<_>>().join(""))
|
|
75
|
+
}
|
|
76
|
+
TypeAnnotation::Object(_) => "Obj".to_string(),
|
|
77
|
+
TypeAnnotation::Union(_) => "Un".to_string(),
|
|
78
|
+
TypeAnnotation::Intersection(_) => "Is".to_string(),
|
|
79
|
+
TypeAnnotation::Function { .. } => "Fn".to_string(),
|
|
80
|
+
TypeAnnotation::Literal(_) => "Lit".to_string(),
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// Substitute generic type parameters with their concrete arguments throughout a type body.
|
|
85
|
+
fn subst_type(t: &TypeAnnotation, map: &HashMap<&str, &TypeAnnotation>) -> TypeAnnotation {
|
|
86
|
+
match t {
|
|
87
|
+
TypeAnnotation::Simple(s) => map
|
|
88
|
+
.get(s.as_ref())
|
|
89
|
+
.map(|rep| (*rep).clone())
|
|
90
|
+
.unwrap_or_else(|| t.clone()),
|
|
91
|
+
TypeAnnotation::Array(inner) => TypeAnnotation::Array(Box::new(subst_type(inner, map))),
|
|
92
|
+
TypeAnnotation::Object(fields) => TypeAnnotation::Object(
|
|
93
|
+
fields.iter().map(|(k, v)| (k.clone(), subst_type(v, map))).collect(),
|
|
94
|
+
),
|
|
95
|
+
TypeAnnotation::Tuple(es) => {
|
|
96
|
+
TypeAnnotation::Tuple(es.iter().map(|e| subst_type(e, map)).collect())
|
|
97
|
+
}
|
|
98
|
+
TypeAnnotation::Union(es) => {
|
|
99
|
+
TypeAnnotation::Union(es.iter().map(|e| subst_type(e, map)).collect())
|
|
100
|
+
}
|
|
101
|
+
TypeAnnotation::Intersection(es) => {
|
|
102
|
+
TypeAnnotation::Intersection(es.iter().map(|e| subst_type(e, map)).collect())
|
|
103
|
+
}
|
|
104
|
+
TypeAnnotation::Function { params, returns } => TypeAnnotation::Function {
|
|
105
|
+
params: params.iter().map(|p| subst_type(p, map)).collect(),
|
|
106
|
+
returns: Box::new(subst_type(returns, map)),
|
|
107
|
+
},
|
|
108
|
+
TypeAnnotation::Literal(_) => t.clone(),
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
62
112
|
pub struct Parser<'a> {
|
|
63
113
|
tokens: &'a [Token],
|
|
64
114
|
pos: usize,
|
|
115
|
+
/// Outstanding `>` owed to enclosing generic-arg lists after a `>>` (`Shr`) token was split,
|
|
116
|
+
/// so nested `Array<Array<T>>` closes correctly (tish lexes `>>` as one token).
|
|
117
|
+
gt_debt: u32,
|
|
118
|
+
/// Generic `type`/`interface` decls (`type Box<T> = …`) → (type-param names, body), for
|
|
119
|
+
/// monomorphizing a reference `Box<number>` into a concrete native struct.
|
|
120
|
+
generic_aliases: HashMap<String, (Vec<Arc<str>>, TypeAnnotation)>,
|
|
121
|
+
/// Synthetic specialized aliases (e.g. `type Box__number = { value: number }`) generated for
|
|
122
|
+
/// each distinct `Generic<Args>` reference; appended to the program at the end.
|
|
123
|
+
generic_specializations: Vec<Statement>,
|
|
124
|
+
/// Names of specializations already generated (dedup).
|
|
125
|
+
generic_done: HashSet<String>,
|
|
65
126
|
}
|
|
66
127
|
|
|
67
128
|
impl<'a> Parser<'a> {
|
|
68
129
|
pub fn new(tokens: &'a [Token]) -> Self {
|
|
69
|
-
Self {
|
|
130
|
+
Self {
|
|
131
|
+
tokens,
|
|
132
|
+
pos: 0,
|
|
133
|
+
gt_debt: 0,
|
|
134
|
+
generic_aliases: HashMap::new(),
|
|
135
|
+
generic_specializations: Vec::new(),
|
|
136
|
+
generic_done: HashSet::new(),
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/// Close one generic-arg `>`: use a previously-split `>>` debt, consume a `>`, or split a
|
|
141
|
+
/// `>>` (consuming it and owing one `>` to the parent). Returns false if there's no closer.
|
|
142
|
+
fn try_close_angle(&mut self) -> bool {
|
|
143
|
+
if self.gt_debt > 0 {
|
|
144
|
+
self.gt_debt -= 1;
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
match self.peek_kind() {
|
|
148
|
+
Some(TokenKind::Gt) => {
|
|
149
|
+
self.advance();
|
|
150
|
+
true
|
|
151
|
+
}
|
|
152
|
+
Some(TokenKind::Shr) => {
|
|
153
|
+
self.advance();
|
|
154
|
+
self.gt_debt += 1;
|
|
155
|
+
true
|
|
156
|
+
}
|
|
157
|
+
Some(TokenKind::UShr) => {
|
|
158
|
+
// `>>>` closing three generic args, e.g. `Foo<Bar<Baz<T>>>`.
|
|
159
|
+
self.advance();
|
|
160
|
+
self.gt_debt += 2;
|
|
161
|
+
true
|
|
162
|
+
}
|
|
163
|
+
_ => false,
|
|
164
|
+
}
|
|
70
165
|
}
|
|
71
166
|
|
|
72
167
|
fn peek(&self) -> Option<&Token> {
|
|
@@ -99,11 +194,20 @@ impl<'a> Parser<'a> {
|
|
|
99
194
|
}
|
|
100
195
|
}
|
|
101
196
|
|
|
102
|
-
/// After `.` / `?.`, allow
|
|
197
|
+
/// After `.` / `?.`, allow contextual keywords as member names. In JS any `IdentifierName`
|
|
198
|
+
/// (including reserved words) is a valid property name — `arr.of`, `x.as`, `o.in`, `o.type` —
|
|
199
|
+
/// but the lexer emits dedicated keyword tokens for these, so they must be accepted explicitly
|
|
200
|
+
/// here. (`TypedArray.of` is the motivating case.) See `docs/js-emit-philosophy.md`.
|
|
103
201
|
fn expect_ident_or_type_member_name(&mut self) -> Result<&Token, String> {
|
|
104
202
|
match self.peek_kind() {
|
|
105
203
|
Some(TokenKind::Ident) => self.expect(TokenKind::Ident),
|
|
106
204
|
Some(TokenKind::Type) => self.expect(TokenKind::Type),
|
|
205
|
+
Some(TokenKind::Of) => self.expect(TokenKind::Of),
|
|
206
|
+
Some(TokenKind::As) => self.expect(TokenKind::As),
|
|
207
|
+
Some(TokenKind::In) => self.expect(TokenKind::In),
|
|
208
|
+
// `delete` is a keyword (the operator) but also a valid property name —
|
|
209
|
+
// `Set`/`Map` expose `.delete(...)`. Issue #40.
|
|
210
|
+
Some(TokenKind::Delete) => self.expect(TokenKind::Delete),
|
|
107
211
|
other => Err(format!(
|
|
108
212
|
"Expected property name after `.` or `?.`, got {:?}",
|
|
109
213
|
other
|
|
@@ -111,6 +215,26 @@ impl<'a> Parser<'a> {
|
|
|
111
215
|
}
|
|
112
216
|
}
|
|
113
217
|
|
|
218
|
+
/// Accept a plain identifier, or the contextual keywords `fn` / `type` used as an
|
|
219
|
+
/// identifier. JS treats `fn` as an ordinary identifier and `type` as only contextually
|
|
220
|
+
/// reserved, so both are valid binding / parameter names and value references (issue #55).
|
|
221
|
+
/// All three carry their spelling in `.literal`. Statement-leading `fn` / `type`
|
|
222
|
+
/// (function declarations / type aliases) are matched earlier in `parse_statement`, so this
|
|
223
|
+
/// only fires in identifier positions.
|
|
224
|
+
fn expect_identifier_name(&mut self) -> Result<&Token, String> {
|
|
225
|
+
let t = self
|
|
226
|
+
.advance()
|
|
227
|
+
.ok_or_else(|| "Expected identifier, got EOF".to_string())?;
|
|
228
|
+
if matches!(
|
|
229
|
+
t.kind,
|
|
230
|
+
TokenKind::Ident | TokenKind::Fn | TokenKind::Type
|
|
231
|
+
) {
|
|
232
|
+
Ok(t)
|
|
233
|
+
} else {
|
|
234
|
+
Err(format!("Expected Ident, got {:?} at {:?}", t.kind, t.span))
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
114
238
|
fn span_end(&self, start: (usize, usize)) -> Span {
|
|
115
239
|
let end = self.peek().map(|t| t.span.start).unwrap_or(start);
|
|
116
240
|
Span { start, end }
|
|
@@ -125,6 +249,13 @@ impl<'a> Parser<'a> {
|
|
|
125
249
|
}
|
|
126
250
|
statements.push(self.parse_statement()?);
|
|
127
251
|
}
|
|
252
|
+
// Prepend the synthetic monomorphized aliases (`type Box__number = …`) so they're declared
|
|
253
|
+
// before any use, for alias resolution + native struct emission.
|
|
254
|
+
if !self.generic_specializations.is_empty() {
|
|
255
|
+
let mut out = std::mem::take(&mut self.generic_specializations);
|
|
256
|
+
out.append(&mut statements);
|
|
257
|
+
statements = out;
|
|
258
|
+
}
|
|
128
259
|
Ok(Program { statements })
|
|
129
260
|
}
|
|
130
261
|
|
|
@@ -173,6 +304,7 @@ impl<'a> Parser<'a> {
|
|
|
173
304
|
TokenKind::Export => self.parse_export()?,
|
|
174
305
|
TokenKind::Type => self.parse_type_alias()?,
|
|
175
306
|
TokenKind::Declare => self.parse_declare()?,
|
|
307
|
+
TokenKind::Interface => self.parse_interface()?,
|
|
176
308
|
_ => {
|
|
177
309
|
let expr = self.parse_expr()?;
|
|
178
310
|
let span_end = expr.span().end;
|
|
@@ -270,7 +402,37 @@ impl<'a> Parser<'a> {
|
|
|
270
402
|
self.expect(TokenKind::Const)?.span.start
|
|
271
403
|
};
|
|
272
404
|
|
|
273
|
-
//
|
|
405
|
+
// First declarator keeps the `let`/`const`-anchored span (single-decl is
|
|
406
|
+
// the overwhelmingly common case and stays byte-identical to before).
|
|
407
|
+
let first = self.parse_one_declarator(mutable, span_start)?;
|
|
408
|
+
if !matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
409
|
+
return Ok(first);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Comma-separated declarators: `let a = 1, b = 2, c`. Each lowers to its
|
|
413
|
+
// own VarDecl inside a transparent `Multi` group (no new scope), so it
|
|
414
|
+
// composes anywhere a single statement is expected (incl. `for` init).
|
|
415
|
+
let mut statements = vec![first];
|
|
416
|
+
while matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
417
|
+
self.advance(); // consume ','
|
|
418
|
+
let decl_start = self.peek().map(|t| t.span.start).unwrap_or(span_start);
|
|
419
|
+
statements.push(self.parse_one_declarator(mutable, decl_start)?);
|
|
420
|
+
}
|
|
421
|
+
Ok(Statement::Multi {
|
|
422
|
+
statements,
|
|
423
|
+
span: self.span_end(span_start),
|
|
424
|
+
})
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/// Parse one declarator — `ident[: Type] [= init]` or `pattern = init` — into
|
|
428
|
+
/// its own statement. The leading `let`/`const` keyword is consumed by the
|
|
429
|
+
/// caller; `span_start` anchors this declarator's span.
|
|
430
|
+
fn parse_one_declarator(
|
|
431
|
+
&mut self,
|
|
432
|
+
mutable: bool,
|
|
433
|
+
span_start: (usize, usize),
|
|
434
|
+
) -> Result<Statement, String> {
|
|
435
|
+
// Destructuring pattern declarator.
|
|
274
436
|
if matches!(
|
|
275
437
|
self.peek_kind(),
|
|
276
438
|
Some(TokenKind::LBracket) | Some(TokenKind::LBrace)
|
|
@@ -286,7 +448,7 @@ impl<'a> Parser<'a> {
|
|
|
286
448
|
});
|
|
287
449
|
}
|
|
288
450
|
|
|
289
|
-
let name_tok = self.
|
|
451
|
+
let name_tok = self.expect_identifier_name()?;
|
|
290
452
|
let name_span = Span {
|
|
291
453
|
start: name_tok.span.start,
|
|
292
454
|
end: name_tok.span.end,
|
|
@@ -454,7 +616,7 @@ impl<'a> Parser<'a> {
|
|
|
454
616
|
default,
|
|
455
617
|
});
|
|
456
618
|
}
|
|
457
|
-
let param_tok = self.
|
|
619
|
+
let param_tok = self.expect_identifier_name()?;
|
|
458
620
|
let name_span = Span {
|
|
459
621
|
start: param_tok.span.start,
|
|
460
622
|
end: param_tok.span.end,
|
|
@@ -480,23 +642,88 @@ impl<'a> Parser<'a> {
|
|
|
480
642
|
}))
|
|
481
643
|
}
|
|
482
644
|
|
|
483
|
-
/// Parse a type
|
|
484
|
-
|
|
485
|
-
|
|
645
|
+
/// Parse a generic type-parameter list `<T, U, …>` on a `fn` / `type` declaration, returning the
|
|
646
|
+
/// parameter names. On functions the names are ignored (generic fns run gradually/boxed); on
|
|
647
|
+
/// `type`/`interface` they drive struct monomorphization (`Box<number>` → a native struct).
|
|
648
|
+
fn parse_type_params(&mut self) -> Result<Vec<Arc<str>>, String> {
|
|
649
|
+
let mut params = Vec::new();
|
|
650
|
+
if matches!(self.peek_kind(), Some(TokenKind::Lt)) {
|
|
651
|
+
self.advance(); // <
|
|
652
|
+
while !matches!(self.peek_kind(), Some(TokenKind::Gt)) {
|
|
653
|
+
let tok = self.expect(TokenKind::Ident)?;
|
|
654
|
+
if let Some(n) = &tok.literal {
|
|
655
|
+
params.push(Arc::from(n.as_ref()));
|
|
656
|
+
}
|
|
657
|
+
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
658
|
+
self.advance();
|
|
659
|
+
} else {
|
|
660
|
+
break;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
self.expect(TokenKind::Gt)?; // >
|
|
664
|
+
}
|
|
665
|
+
Ok(params)
|
|
666
|
+
}
|
|
486
667
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
668
|
+
/// Specialize a generic struct reference `base<args>` into a concrete synthetic alias name
|
|
669
|
+
/// (e.g. `Box__number`), emitting `type Box__number = { value: number }` once per instantiation
|
|
670
|
+
/// so it lowers to a native struct. Returns `None` if `base` isn't a known generic alias or the
|
|
671
|
+
/// arity mismatches (caller then falls back to erasing the args).
|
|
672
|
+
fn monomorphize_generic(&mut self, base: &str, args: &[TypeAnnotation]) -> Option<String> {
|
|
673
|
+
let (params, body) = self.generic_aliases.get(base)?.clone();
|
|
674
|
+
if params.len() != args.len() {
|
|
675
|
+
return None;
|
|
492
676
|
}
|
|
677
|
+
let spec_name = mangle_generic(base, args);
|
|
678
|
+
if self.generic_done.insert(spec_name.clone()) {
|
|
679
|
+
let subst: HashMap<&str, &TypeAnnotation> =
|
|
680
|
+
params.iter().map(|p| p.as_ref()).zip(args.iter()).collect();
|
|
681
|
+
let concrete = subst_type(&body, &subst);
|
|
682
|
+
let z = Span {
|
|
683
|
+
start: (0, 0),
|
|
684
|
+
end: (0, 0),
|
|
685
|
+
};
|
|
686
|
+
self.generic_specializations.push(Statement::TypeAlias {
|
|
687
|
+
name: Arc::from(spec_name.as_str()),
|
|
688
|
+
name_span: z,
|
|
689
|
+
ty: concrete,
|
|
690
|
+
span: z,
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
Some(spec_name)
|
|
694
|
+
}
|
|
493
695
|
|
|
494
|
-
|
|
696
|
+
/// Parse a generic type-argument list `<T, U, …>` on a type reference (`Array<number>`,
|
|
697
|
+
/// `Map<string, number>`). Nested `Array<Array<T>>` works because tish has no `>>` token.
|
|
698
|
+
fn parse_type_args(&mut self) -> Result<Vec<TypeAnnotation>, String> {
|
|
699
|
+
self.expect(TokenKind::Lt)?; // <
|
|
700
|
+
let mut args = Vec::new();
|
|
701
|
+
if !self.try_close_angle() {
|
|
702
|
+
loop {
|
|
703
|
+
args.push(self.parse_type_annotation()?);
|
|
704
|
+
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
705
|
+
self.advance();
|
|
706
|
+
continue;
|
|
707
|
+
}
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
710
|
+
if !self.try_close_angle() {
|
|
711
|
+
return Err("expected `>` to close type arguments".to_string());
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
Ok(args)
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/// Parse a type annotation (number, string, T[], T?, {a: T}, A | B, A & B, Array<T>, etc.)
|
|
718
|
+
fn parse_type_annotation(&mut self) -> Result<TypeAnnotation, String> {
|
|
719
|
+
let base = self.parse_type_intersection()?;
|
|
720
|
+
|
|
721
|
+
// Union: T | U | ... (binds looser than `&`)
|
|
495
722
|
if matches!(self.peek_kind(), Some(TokenKind::BitOr)) {
|
|
496
723
|
let mut types = vec![base];
|
|
497
724
|
while matches!(self.peek_kind(), Some(TokenKind::BitOr)) {
|
|
498
725
|
self.advance(); // |
|
|
499
|
-
types.push(self.
|
|
726
|
+
types.push(self.parse_type_intersection()?);
|
|
500
727
|
}
|
|
501
728
|
return Ok(TypeAnnotation::Union(types));
|
|
502
729
|
}
|
|
@@ -504,12 +731,65 @@ impl<'a> Parser<'a> {
|
|
|
504
731
|
Ok(base)
|
|
505
732
|
}
|
|
506
733
|
|
|
734
|
+
/// `A & B & …` intersection (binds tighter than `|`).
|
|
735
|
+
fn parse_type_intersection(&mut self) -> Result<TypeAnnotation, String> {
|
|
736
|
+
let base = self.parse_type_postfix()?;
|
|
737
|
+
if matches!(self.peek_kind(), Some(TokenKind::BitAnd)) {
|
|
738
|
+
let mut types = vec![base];
|
|
739
|
+
while matches!(self.peek_kind(), Some(TokenKind::BitAnd)) {
|
|
740
|
+
self.advance(); // &
|
|
741
|
+
types.push(self.parse_type_postfix()?);
|
|
742
|
+
}
|
|
743
|
+
return Ok(TypeAnnotation::Intersection(types));
|
|
744
|
+
}
|
|
745
|
+
Ok(base)
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/// A primary type plus any postfix `[]` (array) and `?` (optional, `T? === T | null`),
|
|
749
|
+
/// chained: `T[]`, `T[][]`, `T?`, `T?[]`, …
|
|
750
|
+
fn parse_type_postfix(&mut self) -> Result<TypeAnnotation, String> {
|
|
751
|
+
let mut t = self.parse_type_primary()?;
|
|
752
|
+
loop {
|
|
753
|
+
match self.peek_kind() {
|
|
754
|
+
Some(TokenKind::LBracket) => {
|
|
755
|
+
self.advance(); // [
|
|
756
|
+
self.expect(TokenKind::RBracket)?; // ]
|
|
757
|
+
t = TypeAnnotation::Array(Box::new(t));
|
|
758
|
+
}
|
|
759
|
+
Some(TokenKind::Question) => {
|
|
760
|
+
self.advance(); // ?
|
|
761
|
+
t = TypeAnnotation::Union(vec![t, TypeAnnotation::Simple("null".into())]);
|
|
762
|
+
}
|
|
763
|
+
_ => break,
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
Ok(t)
|
|
767
|
+
}
|
|
768
|
+
|
|
507
769
|
/// Parse a primary type (identifier, object, or function type)
|
|
508
770
|
fn parse_type_primary(&mut self) -> Result<TypeAnnotation, String> {
|
|
509
771
|
match self.peek_kind() {
|
|
510
772
|
Some(TokenKind::Ident) => {
|
|
511
773
|
let tok = self.advance().ok_or("Expected type name")?;
|
|
512
774
|
let name = tok.literal.clone().ok_or("Expected type name")?;
|
|
775
|
+
// Generic reference `Name<Args>`: `Array<T>` desugars to the native `T[]`; other
|
|
776
|
+
// generic refs erase their args (the base name resolves to its alias, whose type
|
|
777
|
+
// params already act as unknown -> `Value`).
|
|
778
|
+
if matches!(self.peek_kind(), Some(TokenKind::Lt)) {
|
|
779
|
+
let args = self.parse_type_args()?;
|
|
780
|
+
if name.as_ref() == "Array" && args.len() == 1 {
|
|
781
|
+
return Ok(TypeAnnotation::Array(Box::new(
|
|
782
|
+
args.into_iter().next().unwrap(),
|
|
783
|
+
)));
|
|
784
|
+
}
|
|
785
|
+
// Monomorphize a generic struct ref `Box<number>` into a synthetic concrete
|
|
786
|
+
// alias `Box__number` (a native struct). Falls back to erasing the args when
|
|
787
|
+
// `name` isn't a known generic alias (e.g. forward reference) or arity mismatches.
|
|
788
|
+
if let Some(spec) = self.monomorphize_generic(name.as_ref(), &args) {
|
|
789
|
+
return Ok(TypeAnnotation::Simple(Arc::from(spec.as_str())));
|
|
790
|
+
}
|
|
791
|
+
return Ok(TypeAnnotation::Simple(name));
|
|
792
|
+
}
|
|
513
793
|
Ok(TypeAnnotation::Simple(name))
|
|
514
794
|
}
|
|
515
795
|
Some(TokenKind::Type | TokenKind::Declare) => {
|
|
@@ -588,6 +868,39 @@ impl<'a> Parser<'a> {
|
|
|
588
868
|
returns: Box::new(returns),
|
|
589
869
|
})
|
|
590
870
|
}
|
|
871
|
+
// Tuple type: [T1, T2, ...]
|
|
872
|
+
Some(TokenKind::LBracket) => {
|
|
873
|
+
self.advance(); // [
|
|
874
|
+
let mut elems = Vec::new();
|
|
875
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
|
|
876
|
+
elems.push(self.parse_type_annotation()?);
|
|
877
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
|
|
878
|
+
self.expect(TokenKind::Comma)?;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
self.expect(TokenKind::RBracket)?;
|
|
882
|
+
Ok(TypeAnnotation::Tuple(elems))
|
|
883
|
+
}
|
|
884
|
+
// Literal types: "foo", 42, true, false
|
|
885
|
+
Some(TokenKind::String) => {
|
|
886
|
+
let tok = self.advance().ok_or("Expected string literal type")?;
|
|
887
|
+
let s = tok.literal.clone().ok_or("Expected string literal")?;
|
|
888
|
+
Ok(TypeAnnotation::Literal(TypeLiteral::Str(s)))
|
|
889
|
+
}
|
|
890
|
+
Some(TokenKind::Number) => {
|
|
891
|
+
let tok = self.advance().ok_or("Expected number literal type")?;
|
|
892
|
+
let s = tok.literal.as_ref().ok_or("Expected number literal")?;
|
|
893
|
+
let n: f64 = s.parse().map_err(|_| format!("Invalid number: {}", s))?;
|
|
894
|
+
Ok(TypeAnnotation::Literal(TypeLiteral::Num(n)))
|
|
895
|
+
}
|
|
896
|
+
Some(TokenKind::True) => {
|
|
897
|
+
self.advance();
|
|
898
|
+
Ok(TypeAnnotation::Literal(TypeLiteral::Bool(true)))
|
|
899
|
+
}
|
|
900
|
+
Some(TokenKind::False) => {
|
|
901
|
+
self.advance();
|
|
902
|
+
Ok(TypeAnnotation::Literal(TypeLiteral::Bool(false)))
|
|
903
|
+
}
|
|
591
904
|
_ => Err("Expected type annotation".to_string()),
|
|
592
905
|
}
|
|
593
906
|
}
|
|
@@ -600,6 +913,7 @@ impl<'a> Parser<'a> {
|
|
|
600
913
|
end: name_tok.span.end,
|
|
601
914
|
};
|
|
602
915
|
let name = name_tok.literal.clone().ok_or("Expected function name")?;
|
|
916
|
+
self.parse_type_params()?; // generic `fn f<T, U>(…)` — fn type params run gradually (boxed)
|
|
603
917
|
self.expect(TokenKind::LParen)?;
|
|
604
918
|
let mut params = Vec::with_capacity(4);
|
|
605
919
|
let mut rest_param = None;
|
|
@@ -695,8 +1009,62 @@ impl<'a> Parser<'a> {
|
|
|
695
1009
|
end: name_tok.span.end,
|
|
696
1010
|
};
|
|
697
1011
|
let name = name_tok.literal.clone().ok_or("Expected type alias name")?;
|
|
1012
|
+
let type_params = self.parse_type_params()?;
|
|
698
1013
|
self.expect(TokenKind::Assign)?;
|
|
699
1014
|
let ty = self.parse_type_annotation()?;
|
|
1015
|
+
if !type_params.is_empty() {
|
|
1016
|
+
self.generic_aliases
|
|
1017
|
+
.insert(name.to_string(), (type_params, ty.clone()));
|
|
1018
|
+
}
|
|
1019
|
+
Ok(Statement::TypeAlias {
|
|
1020
|
+
name,
|
|
1021
|
+
name_span,
|
|
1022
|
+
ty,
|
|
1023
|
+
span: self.span_end(span_start),
|
|
1024
|
+
})
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
/// `interface Name { k: T, ... }` — desugared to `type Name = { ... }` so the checker
|
|
1028
|
+
/// (structural matching) and codegen (native `TishStruct_*`) treat it exactly like an
|
|
1029
|
+
/// object-type alias. (`extends` is not yet supported.)
|
|
1030
|
+
fn parse_interface(&mut self) -> Result<Statement, String> {
|
|
1031
|
+
let span_start = self.expect(TokenKind::Interface)?.span.start;
|
|
1032
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
1033
|
+
let name_span = Span {
|
|
1034
|
+
start: name_tok.span.start,
|
|
1035
|
+
end: name_tok.span.end,
|
|
1036
|
+
};
|
|
1037
|
+
let name = name_tok.literal.clone().ok_or("Expected interface name")?;
|
|
1038
|
+
let type_params = self.parse_type_params()?;
|
|
1039
|
+
|
|
1040
|
+
// `extends Parent1, Parent2` — desugar to an intersection of the parents with the body, so
|
|
1041
|
+
// the inherited fields participate in structural checking + native struct emission.
|
|
1042
|
+
let mut parents: Vec<TypeAnnotation> = Vec::new();
|
|
1043
|
+
if matches!(self.peek_kind(), Some(TokenKind::Ident))
|
|
1044
|
+
&& self.peek().and_then(|t| t.literal.as_deref()) == Some("extends")
|
|
1045
|
+
{
|
|
1046
|
+
self.advance(); // extends
|
|
1047
|
+
loop {
|
|
1048
|
+
parents.push(self.parse_type_postfix()?);
|
|
1049
|
+
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
1050
|
+
self.advance();
|
|
1051
|
+
continue;
|
|
1052
|
+
}
|
|
1053
|
+
break;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
let body = self.parse_type_annotation()?;
|
|
1058
|
+
let ty = if parents.is_empty() {
|
|
1059
|
+
body
|
|
1060
|
+
} else {
|
|
1061
|
+
parents.push(body);
|
|
1062
|
+
TypeAnnotation::Intersection(parents)
|
|
1063
|
+
};
|
|
1064
|
+
if !type_params.is_empty() {
|
|
1065
|
+
self.generic_aliases
|
|
1066
|
+
.insert(name.to_string(), (type_params, ty.clone()));
|
|
1067
|
+
}
|
|
700
1068
|
Ok(Statement::TypeAlias {
|
|
701
1069
|
name,
|
|
702
1070
|
name_span,
|
|
@@ -767,6 +1135,7 @@ impl<'a> Parser<'a> {
|
|
|
767
1135
|
end: name_tok.span.end,
|
|
768
1136
|
};
|
|
769
1137
|
let name = name_tok.literal.clone().ok_or("Expected function name")?;
|
|
1138
|
+
self.parse_type_params()?; // generic `fn f<T, U>(…)` — fn type params run gradually (boxed)
|
|
770
1139
|
self.expect(TokenKind::LParen)?;
|
|
771
1140
|
let mut params = Vec::with_capacity(4);
|
|
772
1141
|
let mut rest_param = None;
|
|
@@ -899,17 +1268,33 @@ impl<'a> Parser<'a> {
|
|
|
899
1268
|
} else {
|
|
900
1269
|
None
|
|
901
1270
|
};
|
|
902
|
-
|
|
903
|
-
self.advance();
|
|
904
|
-
}
|
|
905
|
-
Some(Box::new(Statement::VarDecl {
|
|
1271
|
+
let first = Statement::VarDecl {
|
|
906
1272
|
name,
|
|
907
1273
|
name_span,
|
|
908
1274
|
mutable,
|
|
909
1275
|
type_ann,
|
|
910
1276
|
init: init_expr,
|
|
911
1277
|
span: self.span_end(var_span_start),
|
|
912
|
-
}
|
|
1278
|
+
};
|
|
1279
|
+
// Comma-separated for-init declarators: `for (let i = 0, n = len; ...)`.
|
|
1280
|
+
let decl = if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
1281
|
+
let mut statements = vec![first];
|
|
1282
|
+
while matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
1283
|
+
self.advance();
|
|
1284
|
+
let decl_start = self.peek().map(|t| t.span.start).unwrap_or(var_span_start);
|
|
1285
|
+
statements.push(self.parse_one_declarator(mutable, decl_start)?);
|
|
1286
|
+
}
|
|
1287
|
+
Statement::Multi {
|
|
1288
|
+
statements,
|
|
1289
|
+
span: self.span_end(var_span_start),
|
|
1290
|
+
}
|
|
1291
|
+
} else {
|
|
1292
|
+
first
|
|
1293
|
+
};
|
|
1294
|
+
if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
1295
|
+
self.advance();
|
|
1296
|
+
}
|
|
1297
|
+
Some(Box::new(decl))
|
|
913
1298
|
} else if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
914
1299
|
None
|
|
915
1300
|
} else {
|
|
@@ -960,9 +1345,22 @@ impl<'a> Parser<'a> {
|
|
|
960
1345
|
|
|
961
1346
|
fn parse_return(&mut self) -> Result<Statement, String> {
|
|
962
1347
|
let span_start = self.expect(TokenKind::Return)?.span.start;
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
1348
|
+
// JS restricted production: `return [no LineTerminator here] Expression`.
|
|
1349
|
+
// A line break between `return` and its argument inserts a semicolon, so
|
|
1350
|
+
// `return` alone is `return null`. The lexer emits no token for a same-indent
|
|
1351
|
+
// line break (only Indent/Dedent on level *changes*), so a braceless
|
|
1352
|
+
// `if (c) return` followed by a statement on the next line would otherwise
|
|
1353
|
+
// swallow that statement as the return value (#96). Detect the line break by
|
|
1354
|
+
// comparing the next token's line to the `return` keyword's line.
|
|
1355
|
+
let next_on_new_line = self
|
|
1356
|
+
.peek()
|
|
1357
|
+
.map(|t| t.span.start.0 > span_start.0)
|
|
1358
|
+
.unwrap_or(true);
|
|
1359
|
+
let value = if next_on_new_line
|
|
1360
|
+
|| matches!(
|
|
1361
|
+
self.peek_kind(),
|
|
1362
|
+
Some(TokenKind::Semicolon | TokenKind::Dedent | TokenKind::RBrace)
|
|
1363
|
+
)
|
|
966
1364
|
|| self.peek_kind().is_none()
|
|
967
1365
|
{
|
|
968
1366
|
None
|
|
@@ -1103,9 +1501,9 @@ impl<'a> Parser<'a> {
|
|
|
1103
1501
|
.literal
|
|
1104
1502
|
.clone()
|
|
1105
1503
|
.ok_or("Expected identifier in import")?;
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
{
|
|
1504
|
+
// `as` is a dedicated keyword token (also used for casts); in import-specifier
|
|
1505
|
+
// position it introduces a rename: `{ foo as bar }`.
|
|
1506
|
+
let (alias, alias_span) = if matches!(self.peek_kind(), Some(TokenKind::As)) {
|
|
1109
1507
|
self.advance(); // consume 'as'
|
|
1110
1508
|
let alias_tok = self.expect(TokenKind::Ident)?;
|
|
1111
1509
|
let asp = Span {
|
|
@@ -1139,10 +1537,7 @@ impl<'a> Parser<'a> {
|
|
|
1139
1537
|
} else if matches!(self.peek_kind(), Some(TokenKind::Star)) {
|
|
1140
1538
|
// Namespace: import * as M from "..."
|
|
1141
1539
|
self.advance();
|
|
1142
|
-
|
|
1143
|
-
if as_tok.literal.as_deref() != Some("as") {
|
|
1144
|
-
return Err("Expected 'as' after '*' in namespace import".to_string());
|
|
1145
|
-
}
|
|
1540
|
+
self.expect(TokenKind::As)?;
|
|
1146
1541
|
let alias_tok = self.expect(TokenKind::Ident)?;
|
|
1147
1542
|
let name_span = Span {
|
|
1148
1543
|
start: alias_tok.span.start,
|
|
@@ -1376,7 +1771,7 @@ impl<'a> Parser<'a> {
|
|
|
1376
1771
|
binary_single_op!(parse_bit_xor, parse_bit_and, BitXor, BinOp::BitXor);
|
|
1377
1772
|
binary_single_op!(parse_bit_and, parse_shift, BitAnd, BinOp::BitAnd);
|
|
1378
1773
|
|
|
1379
|
-
binary_multi_op!(parse_shift, parse_equality, Shl => BinOp::Shl, Shr => BinOp::Shr);
|
|
1774
|
+
binary_multi_op!(parse_shift, parse_equality, Shl => BinOp::Shl, Shr => BinOp::Shr, UShr => BinOp::UShr);
|
|
1380
1775
|
binary_multi_op!(parse_equality, parse_comparison,
|
|
1381
1776
|
StrictEq => BinOp::StrictEq, StrictNe => BinOp::StrictNe,
|
|
1382
1777
|
Eq => BinOp::Eq, Ne => BinOp::Ne);
|
|
@@ -1448,6 +1843,19 @@ impl<'a> Parser<'a> {
|
|
|
1448
1843
|
},
|
|
1449
1844
|
});
|
|
1450
1845
|
}
|
|
1846
|
+
Some(TokenKind::Delete) => {
|
|
1847
|
+
let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
1848
|
+
self.advance();
|
|
1849
|
+
let target = self.parse_unary()?;
|
|
1850
|
+
let end = target.span().end;
|
|
1851
|
+
return Ok(Expr::Delete {
|
|
1852
|
+
target: Box::new(target),
|
|
1853
|
+
span: Span {
|
|
1854
|
+
start: span_start,
|
|
1855
|
+
end,
|
|
1856
|
+
},
|
|
1857
|
+
});
|
|
1858
|
+
}
|
|
1451
1859
|
Some(TokenKind::Await) => {
|
|
1452
1860
|
let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
1453
1861
|
self.advance();
|
|
@@ -1574,6 +1982,13 @@ impl<'a> Parser<'a> {
|
|
|
1574
1982
|
};
|
|
1575
1983
|
while let Some(kind) = self.peek_kind() {
|
|
1576
1984
|
match kind {
|
|
1985
|
+
// `expr as Type` — a type assertion. Gradual + erased: consume the type and keep the
|
|
1986
|
+
// expression unchanged (no runtime effect; the checker is already lenient on what it
|
|
1987
|
+
// can't prove, so the assertion's only job — silencing a strict error — is moot).
|
|
1988
|
+
TokenKind::As => {
|
|
1989
|
+
self.advance(); // as
|
|
1990
|
+
self.parse_type_annotation()?;
|
|
1991
|
+
}
|
|
1577
1992
|
TokenKind::LParen => {
|
|
1578
1993
|
self.advance();
|
|
1579
1994
|
let mut args = Vec::new();
|
|
@@ -1699,7 +2114,10 @@ impl<'a> Parser<'a> {
|
|
|
1699
2114
|
value: Literal::Null,
|
|
1700
2115
|
span,
|
|
1701
2116
|
}),
|
|
1702
|
-
|
|
2117
|
+
// `fn` / `type` as a value reference or single-param arrow head (issue #55).
|
|
2118
|
+
// `fn`/`type` function-expressions don't exist and statement-leading decls are
|
|
2119
|
+
// handled in parse_statement, so treating them as identifiers here is additive.
|
|
2120
|
+
TokenKind::Ident | TokenKind::Fn | TokenKind::Type => {
|
|
1703
2121
|
let name = t.literal.clone().ok_or("Expected ident")?;
|
|
1704
2122
|
// Check if this is a single-param arrow function: x => ...
|
|
1705
2123
|
if matches!(self.peek_kind(), Some(TokenKind::Arrow)) {
|
|
@@ -1875,7 +2293,10 @@ impl<'a> Parser<'a> {
|
|
|
1875
2293
|
}
|
|
1876
2294
|
}
|
|
1877
2295
|
}
|
|
1878
|
-
|
|
2296
|
+
// Include the token span (matching `expect`'s `at {:?}` convention) so error consumers
|
|
2297
|
+
// — notably the LSP's `parse_error_pos` — can place the diagnostic at the real location
|
|
2298
|
+
// instead of falling back to (0, 0) / top-of-file.
|
|
2299
|
+
_ => Err(format!("Unexpected token: {:?} at {:?}", t.kind, t.span)),
|
|
1879
2300
|
}
|
|
1880
2301
|
}
|
|
1881
2302
|
|
|
@@ -2285,6 +2706,7 @@ impl ExprSpan for Expr {
|
|
|
2285
2706
|
Expr::Object { span, .. } => *span,
|
|
2286
2707
|
Expr::Assign { span, .. } => *span,
|
|
2287
2708
|
Expr::TypeOf { span, .. } => *span,
|
|
2709
|
+
Expr::Delete { span, .. } => *span,
|
|
2288
2710
|
Expr::PostfixInc { span, .. } => *span,
|
|
2289
2711
|
Expr::PostfixDec { span, .. } => *span,
|
|
2290
2712
|
Expr::PrefixInc { span, .. } => *span,
|