@tishlang/tish-format 1.0.12 → 2.0.1
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 +51 -0
- package/LICENSE +13 -0
- package/bin/tish-format +0 -0
- package/crates/js_to_tish/Cargo.toml +11 -0
- package/crates/js_to_tish/README.md +18 -0
- package/crates/js_to_tish/src/error.rs +55 -0
- package/crates/js_to_tish/src/lib.rs +11 -0
- package/crates/js_to_tish/src/span_util.rs +35 -0
- package/crates/js_to_tish/src/transform/expr.rs +611 -0
- package/crates/js_to_tish/src/transform/stmt.rs +503 -0
- package/crates/js_to_tish/src/transform.rs +60 -0
- package/crates/tish/Cargo.toml +62 -0
- package/crates/tish/build.rs +21 -0
- package/crates/tish/src/cargo_native_registry.rs +32 -0
- package/crates/tish/src/cli_help.rs +576 -0
- package/crates/tish/src/main.rs +853 -0
- package/crates/tish/src/repl_completion.rs +199 -0
- package/crates/tish/tests/cargo_example_compile.rs +67 -0
- package/crates/tish/tests/error_source_location.rs +36 -0
- package/crates/tish/tests/fixtures/cargo_example_project/Cargo.toml +3 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/Cargo.toml +11 -0
- package/crates/tish/tests/fixtures/cargo_example_project/crates/demo-shim/src/lib.rs +12 -0
- package/crates/tish/tests/fixtures/cargo_example_project/package.json +10 -0
- package/crates/tish/tests/fixtures/cargo_example_project/src/main.tish +3 -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 +1406 -0
- package/crates/tish/tests/run_optimize_stdout_parity.rs +50 -0
- package/crates/tish/tests/shortcircuit.rs +65 -0
- package/crates/tish/tests/trycatch_runtime_errors.rs +45 -0
- package/crates/tish/tests/tty_capability.rs +43 -0
- package/crates/tish_ast/Cargo.toml +9 -0
- package/crates/tish_ast/src/ast.rs +649 -0
- package/crates/tish_ast/src/lib.rs +5 -0
- package/crates/tish_build_utils/Cargo.toml +11 -0
- package/crates/tish_build_utils/src/lib.rs +577 -0
- package/crates/tish_builtins/Cargo.toml +22 -0
- package/crates/tish_builtins/src/array.rs +803 -0
- package/crates/tish_builtins/src/collections.rs +481 -0
- package/crates/tish_builtins/src/construct.rs +199 -0
- package/crates/tish_builtins/src/date.rs +538 -0
- package/crates/tish_builtins/src/globals.rs +293 -0
- package/crates/tish_builtins/src/helpers.rs +35 -0
- package/crates/tish_builtins/src/iterator.rs +129 -0
- package/crates/tish_builtins/src/lib.rs +21 -0
- package/crates/tish_builtins/src/math.rs +89 -0
- package/crates/tish_builtins/src/number.rs +96 -0
- package/crates/tish_builtins/src/object.rs +36 -0
- package/crates/tish_builtins/src/string.rs +646 -0
- package/crates/tish_builtins/src/symbol.rs +83 -0
- package/crates/tish_builtins/src/typedarrays.rs +298 -0
- package/crates/tish_bytecode/Cargo.toml +17 -0
- package/crates/tish_bytecode/src/chunk.rs +164 -0
- package/crates/tish_bytecode/src/compiler.rs +2604 -0
- package/crates/tish_bytecode/src/encoding.rs +102 -0
- package/crates/tish_bytecode/src/lib.rs +20 -0
- package/crates/tish_bytecode/src/opcode.rs +185 -0
- package/crates/tish_bytecode/src/peephole.rs +189 -0
- package/crates/tish_bytecode/src/serialize.rs +193 -0
- package/crates/tish_bytecode/tests/break_continue_bytecode.rs +44 -0
- package/crates/tish_bytecode/tests/constant_folding.rs +84 -0
- package/crates/tish_bytecode/tests/sort_optimization.rs +31 -0
- package/crates/tish_compile/Cargo.toml +27 -0
- package/crates/tish_compile/src/check.rs +774 -0
- package/crates/tish_compile/src/codegen.rs +7317 -0
- package/crates/tish_compile/src/infer.rs +1681 -0
- package/crates/tish_compile/src/lib.rs +206 -0
- package/crates/tish_compile/src/resolve.rs +1951 -0
- package/crates/tish_compile/src/types.rs +605 -0
- package/crates/tish_compile_js/Cargo.toml +18 -0
- package/crates/tish_compile_js/examples/jsx_vdom_smoke.tish +8 -0
- package/crates/tish_compile_js/src/codegen.rs +938 -0
- package/crates/tish_compile_js/src/error.rs +20 -0
- package/crates/tish_compile_js/src/lib.rs +26 -0
- package/crates/tish_compile_js/src/tests_jsx.rs +414 -0
- package/crates/tish_compiler_wasm/Cargo.toml +21 -0
- package/crates/tish_compiler_wasm/src/lib.rs +57 -0
- package/crates/tish_compiler_wasm/src/resolve_virtual.rs +473 -0
- package/crates/tish_core/Cargo.toml +32 -0
- package/crates/tish_core/src/console_style.rs +170 -0
- package/crates/tish_core/src/json.rs +430 -0
- package/crates/tish_core/src/lib.rs +20 -0
- package/crates/tish_core/src/macros.rs +36 -0
- package/crates/tish_core/src/shape.rs +85 -0
- package/crates/tish_core/src/uri.rs +118 -0
- package/crates/tish_core/src/value.rs +1350 -0
- package/crates/tish_core/src/vmref.rs +183 -0
- package/crates/tish_cranelift/Cargo.toml +19 -0
- package/crates/tish_cranelift/src/lib.rs +43 -0
- package/crates/tish_cranelift/src/link.rs +130 -0
- package/crates/tish_cranelift/src/lower.rs +85 -0
- package/crates/tish_cranelift_runtime/Cargo.toml +26 -0
- package/crates/tish_cranelift_runtime/src/lib.rs +45 -0
- package/crates/tish_eval/Cargo.toml +51 -0
- package/crates/tish_eval/src/eval.rs +4265 -0
- package/crates/tish_eval/src/http.rs +191 -0
- package/crates/tish_eval/src/lib.rs +99 -0
- package/crates/tish_eval/src/natives.rs +551 -0
- package/crates/tish_eval/src/promise.rs +179 -0
- package/crates/tish_eval/src/regex.rs +299 -0
- package/crates/tish_eval/src/timers.rs +120 -0
- package/crates/tish_eval/src/value.rs +336 -0
- package/crates/tish_eval/src/value_convert.rs +117 -0
- 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/Cargo.toml +16 -0
- package/crates/tish_fmt/src/bin/tish-fmt.rs +41 -0
- package/crates/tish_fmt/src/lib.rs +2157 -0
- package/crates/tish_jsx_web/Cargo.toml +9 -0
- package/crates/tish_jsx_web/README.md +5 -0
- package/crates/tish_jsx_web/src/lib.rs +2 -0
- package/crates/tish_lexer/Cargo.toml +9 -0
- package/crates/tish_lexer/src/lib.rs +1104 -0
- package/crates/tish_lexer/src/token.rs +170 -0
- package/crates/tish_lint/Cargo.toml +18 -0
- package/crates/tish_lint/src/bin/tish-lint.rs +195 -0
- package/crates/tish_lint/src/lib.rs +281 -0
- package/crates/tish_llvm/Cargo.toml +13 -0
- package/crates/tish_llvm/src/lib.rs +115 -0
- package/crates/tish_lsp/Cargo.toml +25 -0
- package/crates/tish_lsp/README.md +26 -0
- package/crates/tish_lsp/src/builtin_goto.rs +362 -0
- package/crates/tish_lsp/src/import_goto.rs +564 -0
- package/crates/tish_lsp/src/main.rs +1459 -0
- package/crates/tish_native/Cargo.toml +16 -0
- package/crates/tish_native/src/build.rs +481 -0
- package/crates/tish_native/src/config.rs +48 -0
- package/crates/tish_native/src/lib.rs +416 -0
- package/crates/tish_opt/Cargo.toml +13 -0
- package/crates/tish_opt/src/lib.rs +1046 -0
- package/crates/tish_parser/Cargo.toml +11 -0
- package/crates/tish_parser/src/lib.rs +386 -0
- package/crates/tish_parser/src/parser.rs +2726 -0
- package/crates/tish_pg/Cargo.toml +34 -0
- package/crates/tish_pg/README.md +38 -0
- package/crates/tish_pg/src/error.rs +52 -0
- package/crates/tish_pg/src/lib.rs +955 -0
- package/crates/tish_resolve/Cargo.toml +13 -0
- package/crates/tish_resolve/src/lib.rs +3601 -0
- package/crates/tish_resolve/src/pos.rs +141 -0
- package/crates/tish_runtime/Cargo.toml +100 -0
- package/crates/tish_runtime/src/http.rs +1347 -0
- package/crates/tish_runtime/src/http_fetch.rs +492 -0
- package/crates/tish_runtime/src/http_hyper.rs +441 -0
- package/crates/tish_runtime/src/http_prefork.rs +189 -0
- package/crates/tish_runtime/src/lib.rs +1447 -0
- package/crates/tish_runtime/src/native_promise.rs +15 -0
- package/crates/tish_runtime/src/promise.rs +558 -0
- package/crates/tish_runtime/src/promise_io.rs +38 -0
- package/crates/tish_runtime/src/timers.rs +172 -0
- package/crates/tish_runtime/src/tty.rs +226 -0
- package/crates/tish_runtime/src/ws.rs +778 -0
- package/crates/tish_runtime/tests/fetch_readable_stream.rs +102 -0
- package/crates/tish_ui/Cargo.toml +17 -0
- package/crates/tish_ui/src/jsx.rs +692 -0
- package/crates/tish_ui/src/lib.rs +20 -0
- package/crates/tish_ui/src/runtime/hooks.rs +573 -0
- package/crates/tish_ui/src/runtime/mod.rs +183 -0
- package/crates/tish_vm/Cargo.toml +60 -0
- package/crates/tish_vm/src/jit.rs +1050 -0
- package/crates/tish_vm/src/lib.rs +41 -0
- package/crates/tish_vm/src/vm.rs +3536 -0
- package/crates/tish_vm/tests/concurrent_shared_state.rs +140 -0
- package/crates/tish_vm/tests/fixtures/or_string_cmd.tish +2 -0
- package/crates/tish_vm/tests/lexical_scope_declare.rs +34 -0
- package/crates/tish_vm/tests/peephole_jump_chain_logical_or.rs +150 -0
- package/crates/tish_wasm/Cargo.toml +15 -0
- package/crates/tish_wasm/src/lib.rs +428 -0
- package/crates/tish_wasm_runtime/Cargo.toml +37 -0
- package/crates/tish_wasm_runtime/src/gpu.rs +429 -0
- package/crates/tish_wasm_runtime/src/lib.rs +42 -0
- package/crates/tishlang_cargo_bindgen/Cargo.toml +26 -0
- package/crates/tishlang_cargo_bindgen/src/classify.rs +261 -0
- package/crates/tishlang_cargo_bindgen/src/discover.rs +125 -0
- package/crates/tishlang_cargo_bindgen/src/infer.rs +382 -0
- package/crates/tishlang_cargo_bindgen/src/lib.rs +349 -0
- package/crates/tishlang_cargo_bindgen/src/main.rs +167 -0
- package/crates/tishlang_cargo_bindgen/src/metadata.rs +117 -0
- package/justfile +276 -0
- package/package.json +2 -2
- package/platform/darwin-arm64/tish-fmt +0 -0
- package/platform/darwin-x64/tish-fmt +0 -0
- package/platform/linux-arm64/tish-fmt +0 -0
- package/platform/linux-x64/tish-fmt +0 -0
- package/platform/win32-x64/tish-fmt.exe +0 -0
|
@@ -0,0 +1,2726 @@
|
|
|
1
|
+
//! Recursive descent parser for Tish.
|
|
2
|
+
|
|
3
|
+
use std::collections::{HashMap, HashSet};
|
|
4
|
+
use std::sync::Arc;
|
|
5
|
+
|
|
6
|
+
/// Macro to generate single-operator binary parsing functions.
|
|
7
|
+
/// Reduces boilerplate for Or, And, BitOr, BitXor, BitAnd parsers.
|
|
8
|
+
macro_rules! binary_single_op {
|
|
9
|
+
($name:ident, $next:ident, $token:ident, $op:expr) => {
|
|
10
|
+
fn $name(&mut self) -> Result<Expr, String> {
|
|
11
|
+
let mut left = self.$next()?;
|
|
12
|
+
while matches!(self.peek_kind(), Some(TokenKind::$token)) {
|
|
13
|
+
self.advance();
|
|
14
|
+
let right = self.$next()?;
|
|
15
|
+
let start = expr_span(&left).start;
|
|
16
|
+
let end = expr_span(&right).end;
|
|
17
|
+
left = Expr::Binary {
|
|
18
|
+
left: Box::new(left),
|
|
19
|
+
op: $op,
|
|
20
|
+
right: Box::new(right),
|
|
21
|
+
span: Span { start, end },
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
Ok(left)
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/// Macro for multi-operator binary parsing with a match block.
|
|
30
|
+
macro_rules! binary_multi_op {
|
|
31
|
+
($name:ident, $next:ident, $( $token:ident => $op:expr ),+ $(,)?) => {
|
|
32
|
+
fn $name(&mut self) -> Result<Expr, String> {
|
|
33
|
+
let mut left = self.$next()?;
|
|
34
|
+
loop {
|
|
35
|
+
let op = match self.peek_kind() {
|
|
36
|
+
$( Some(TokenKind::$token) => $op, )+
|
|
37
|
+
_ => break,
|
|
38
|
+
};
|
|
39
|
+
self.advance();
|
|
40
|
+
let right = self.$next()?;
|
|
41
|
+
let start = expr_span(&left).start;
|
|
42
|
+
let end = expr_span(&right).end;
|
|
43
|
+
left = Expr::Binary {
|
|
44
|
+
left: Box::new(left),
|
|
45
|
+
op,
|
|
46
|
+
right: Box::new(right),
|
|
47
|
+
span: Span { start, end },
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
Ok(left)
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
use tishlang_ast::{
|
|
56
|
+
ArrayElement, ArrowBody, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern,
|
|
57
|
+
DestructProp, ExportDeclaration, Expr, FunParam, ImportSpecifier, JsxAttrValue, JsxChild,
|
|
58
|
+
JsxProp, Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Span, Statement,
|
|
59
|
+
TypeAnnotation, TypeLiteral, TypedParam, UnaryOp,
|
|
60
|
+
};
|
|
61
|
+
use tishlang_lexer::{Token, TokenKind};
|
|
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
|
+
|
|
112
|
+
pub struct Parser<'a> {
|
|
113
|
+
tokens: &'a [Token],
|
|
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>,
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
impl<'a> Parser<'a> {
|
|
129
|
+
pub fn new(tokens: &'a [Token]) -> 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
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
fn peek(&self) -> Option<&Token> {
|
|
168
|
+
self.tokens.get(self.pos)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
fn peek_kind(&self) -> Option<TokenKind> {
|
|
172
|
+
self.peek().map(|t| t.kind)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
fn advance(&mut self) -> Option<&Token> {
|
|
176
|
+
let t = self.tokens.get(self.pos);
|
|
177
|
+
if t.is_some() {
|
|
178
|
+
self.pos += 1;
|
|
179
|
+
}
|
|
180
|
+
t
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
fn expect(&mut self, kind: TokenKind) -> Result<&Token, String> {
|
|
184
|
+
let t = self
|
|
185
|
+
.advance()
|
|
186
|
+
.ok_or_else(|| format!("Expected {:?}, got EOF", kind))?;
|
|
187
|
+
if t.kind == kind {
|
|
188
|
+
Ok(t)
|
|
189
|
+
} else {
|
|
190
|
+
Err(format!(
|
|
191
|
+
"Expected {:?}, got {:?} at {:?}",
|
|
192
|
+
kind, t.kind, t.span
|
|
193
|
+
))
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
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`.
|
|
201
|
+
fn expect_ident_or_type_member_name(&mut self) -> Result<&Token, String> {
|
|
202
|
+
match self.peek_kind() {
|
|
203
|
+
Some(TokenKind::Ident) => self.expect(TokenKind::Ident),
|
|
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),
|
|
211
|
+
other => Err(format!(
|
|
212
|
+
"Expected property name after `.` or `?.`, got {:?}",
|
|
213
|
+
other
|
|
214
|
+
)),
|
|
215
|
+
}
|
|
216
|
+
}
|
|
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
|
+
|
|
238
|
+
fn span_end(&self, start: (usize, usize)) -> Span {
|
|
239
|
+
let end = self.peek().map(|t| t.span.start).unwrap_or(start);
|
|
240
|
+
Span { start, end }
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
pub fn parse_program(&mut self) -> Result<Program, String> {
|
|
244
|
+
let mut statements = Vec::with_capacity(8);
|
|
245
|
+
while self.peek_kind().is_some() {
|
|
246
|
+
if matches!(self.peek_kind(), Some(TokenKind::Dedent)) {
|
|
247
|
+
self.advance();
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
statements.push(self.parse_statement()?);
|
|
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
|
+
}
|
|
259
|
+
Ok(Program { statements })
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
fn parse_statement(&mut self) -> Result<Statement, String> {
|
|
263
|
+
let kind = self.peek_kind().ok_or("Unexpected EOF")?;
|
|
264
|
+
let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
265
|
+
|
|
266
|
+
let stmt = match kind {
|
|
267
|
+
TokenKind::LBrace | TokenKind::Indent => self.parse_block()?,
|
|
268
|
+
TokenKind::Let => self.parse_var_decl(true)?,
|
|
269
|
+
TokenKind::Const => self.parse_var_decl(false)?,
|
|
270
|
+
TokenKind::Async => {
|
|
271
|
+
self.advance(); // consume 'async'
|
|
272
|
+
self.parse_fun_decl(true)? // parse_fun_decl expects 'fn' next
|
|
273
|
+
}
|
|
274
|
+
TokenKind::Fn => self.parse_fun_decl(false)?,
|
|
275
|
+
TokenKind::If => self.parse_if()?,
|
|
276
|
+
TokenKind::While => self.parse_while()?,
|
|
277
|
+
TokenKind::For => self.parse_for()?,
|
|
278
|
+
TokenKind::Return => self.parse_return()?,
|
|
279
|
+
TokenKind::Switch => self.parse_switch()?,
|
|
280
|
+
TokenKind::Do => self.parse_do_while()?,
|
|
281
|
+
TokenKind::Throw => self.parse_throw()?,
|
|
282
|
+
TokenKind::Try => self.parse_try()?,
|
|
283
|
+
TokenKind::Break => {
|
|
284
|
+
self.advance();
|
|
285
|
+
let span_end = self.peek().map(|t| t.span.end).unwrap_or(span_start);
|
|
286
|
+
Statement::Break {
|
|
287
|
+
span: Span {
|
|
288
|
+
start: span_start,
|
|
289
|
+
end: span_end,
|
|
290
|
+
},
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
TokenKind::Continue => {
|
|
294
|
+
self.advance();
|
|
295
|
+
let span_end = self.peek().map(|t| t.span.end).unwrap_or(span_start);
|
|
296
|
+
Statement::Continue {
|
|
297
|
+
span: Span {
|
|
298
|
+
start: span_start,
|
|
299
|
+
end: span_end,
|
|
300
|
+
},
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
TokenKind::Import => self.parse_import()?,
|
|
304
|
+
TokenKind::Export => self.parse_export()?,
|
|
305
|
+
TokenKind::Type => self.parse_type_alias()?,
|
|
306
|
+
TokenKind::Declare => self.parse_declare()?,
|
|
307
|
+
TokenKind::Interface => self.parse_interface()?,
|
|
308
|
+
_ => {
|
|
309
|
+
let expr = self.parse_expr()?;
|
|
310
|
+
let span_end = expr.span().end;
|
|
311
|
+
Statement::ExprStmt {
|
|
312
|
+
expr,
|
|
313
|
+
span: Span {
|
|
314
|
+
start: span_start,
|
|
315
|
+
end: span_end,
|
|
316
|
+
},
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// Optional semicolon
|
|
322
|
+
if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
323
|
+
self.advance();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
Ok(stmt)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
fn parse_block_or_statement(&mut self) -> Result<Statement, String> {
|
|
330
|
+
if matches!(self.peek_kind(), Some(TokenKind::LBrace))
|
|
331
|
+
|| matches!(self.peek_kind(), Some(TokenKind::Indent))
|
|
332
|
+
{
|
|
333
|
+
return self.parse_block();
|
|
334
|
+
}
|
|
335
|
+
self.parse_statement()
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
fn parse_block(&mut self) -> Result<Statement, String> {
|
|
339
|
+
let span_start = self.peek().ok_or("Unexpected EOF")?.span.start;
|
|
340
|
+
|
|
341
|
+
let opened_with_brace = matches!(self.peek_kind(), Some(TokenKind::LBrace));
|
|
342
|
+
if opened_with_brace {
|
|
343
|
+
self.advance(); // {
|
|
344
|
+
// After `{`, the lexer often emits `Indent` for the first indented line of the body.
|
|
345
|
+
// `parse_statement` treats a leading `Indent` as starting a *nested* indent-block, so
|
|
346
|
+
// without consuming this token we get `Block { Block { let ... } ; ... }` and the first
|
|
347
|
+
// `let`/`const` is scoped too narrowly (JS ReferenceError). This indent is layout for
|
|
348
|
+
// *this* brace block, not an inner block.
|
|
349
|
+
if matches!(self.peek_kind(), Some(TokenKind::Indent)) {
|
|
350
|
+
self.advance();
|
|
351
|
+
}
|
|
352
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Indent)) {
|
|
353
|
+
self.advance(); // Indent
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
let mut statements = Vec::with_capacity(4);
|
|
357
|
+
loop {
|
|
358
|
+
if matches!(self.peek_kind(), Some(TokenKind::RBrace))
|
|
359
|
+
|| matches!(self.peek_kind(), Some(TokenKind::Dedent))
|
|
360
|
+
|| self.peek_kind().is_none()
|
|
361
|
+
{
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
statements.push(self.parse_statement()?);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if matches!(
|
|
368
|
+
self.peek_kind(),
|
|
369
|
+
Some(TokenKind::RBrace | TokenKind::Dedent)
|
|
370
|
+
) {
|
|
371
|
+
self.advance();
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
let peek_end = self.peek().map(|x| x.span.end);
|
|
375
|
+
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
|
+
};
|
|
388
|
+
|
|
389
|
+
Ok(Statement::Block {
|
|
390
|
+
statements,
|
|
391
|
+
span: Span {
|
|
392
|
+
start: span_start,
|
|
393
|
+
end,
|
|
394
|
+
},
|
|
395
|
+
})
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
fn parse_var_decl(&mut self, mutable: bool) -> Result<Statement, String> {
|
|
399
|
+
let span_start = if mutable {
|
|
400
|
+
self.expect(TokenKind::Let)?.span.start
|
|
401
|
+
} else {
|
|
402
|
+
self.expect(TokenKind::Const)?.span.start
|
|
403
|
+
};
|
|
404
|
+
|
|
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.
|
|
436
|
+
if matches!(
|
|
437
|
+
self.peek_kind(),
|
|
438
|
+
Some(TokenKind::LBracket) | Some(TokenKind::LBrace)
|
|
439
|
+
) {
|
|
440
|
+
let pattern = self.parse_destruct_pattern()?;
|
|
441
|
+
self.expect(TokenKind::Assign)?;
|
|
442
|
+
let init = self.parse_expr()?;
|
|
443
|
+
return Ok(Statement::VarDeclDestructure {
|
|
444
|
+
pattern,
|
|
445
|
+
mutable,
|
|
446
|
+
init,
|
|
447
|
+
span: self.span_end(span_start),
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
let name_tok = self.expect_identifier_name()?;
|
|
452
|
+
let name_span = Span {
|
|
453
|
+
start: name_tok.span.start,
|
|
454
|
+
end: name_tok.span.end,
|
|
455
|
+
};
|
|
456
|
+
let name = name_tok.literal.clone().ok_or("Expected identifier")?;
|
|
457
|
+
|
|
458
|
+
// Optional type annotation: `: Type`
|
|
459
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
460
|
+
self.advance(); // consume :
|
|
461
|
+
Some(self.parse_type_annotation()?)
|
|
462
|
+
} else {
|
|
463
|
+
None
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
let init = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
467
|
+
self.advance();
|
|
468
|
+
Some(self.parse_expr()?)
|
|
469
|
+
} else {
|
|
470
|
+
None
|
|
471
|
+
};
|
|
472
|
+
Ok(Statement::VarDecl {
|
|
473
|
+
name,
|
|
474
|
+
name_span,
|
|
475
|
+
mutable,
|
|
476
|
+
type_ann,
|
|
477
|
+
init,
|
|
478
|
+
span: self.span_end(span_start),
|
|
479
|
+
})
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
fn parse_destruct_pattern(&mut self) -> Result<DestructPattern, String> {
|
|
483
|
+
match self.peek_kind() {
|
|
484
|
+
Some(TokenKind::LBracket) => self.parse_array_destruct_pattern(),
|
|
485
|
+
Some(TokenKind::LBrace) => self.parse_object_destruct_pattern(),
|
|
486
|
+
_ => Err("Expected destructuring pattern".to_string()),
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
fn parse_array_destruct_pattern(&mut self) -> Result<DestructPattern, String> {
|
|
491
|
+
self.expect(TokenKind::LBracket)?;
|
|
492
|
+
let mut elements = Vec::new();
|
|
493
|
+
|
|
494
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
|
|
495
|
+
// Handle holes (elision): [a, , b]
|
|
496
|
+
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
497
|
+
elements.push(None);
|
|
498
|
+
self.advance();
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Rest element: ...rest
|
|
503
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
504
|
+
self.advance();
|
|
505
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
506
|
+
let name_span = Span {
|
|
507
|
+
start: name_tok.span.start,
|
|
508
|
+
end: name_tok.span.end,
|
|
509
|
+
};
|
|
510
|
+
let name = name_tok.literal.clone().ok_or("Expected identifier")?;
|
|
511
|
+
elements.push(Some(DestructElement::Rest(name, name_span)));
|
|
512
|
+
break;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Nested pattern or identifier
|
|
516
|
+
let elem = match self.peek_kind() {
|
|
517
|
+
Some(TokenKind::LBracket) | Some(TokenKind::LBrace) => {
|
|
518
|
+
let nested = self.parse_destruct_pattern()?;
|
|
519
|
+
DestructElement::Pattern(Box::new(nested))
|
|
520
|
+
}
|
|
521
|
+
Some(TokenKind::Ident) => {
|
|
522
|
+
let name_tok = self.advance().ok_or("Unexpected EOF")?;
|
|
523
|
+
let name_span = Span {
|
|
524
|
+
start: name_tok.span.start,
|
|
525
|
+
end: name_tok.span.end,
|
|
526
|
+
};
|
|
527
|
+
let name = name_tok.literal.clone().ok_or("Expected identifier")?;
|
|
528
|
+
DestructElement::Ident(name, name_span)
|
|
529
|
+
}
|
|
530
|
+
_ => return Err("Expected identifier or pattern in destructuring".to_string()),
|
|
531
|
+
};
|
|
532
|
+
elements.push(Some(elem));
|
|
533
|
+
|
|
534
|
+
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
535
|
+
self.advance();
|
|
536
|
+
} else {
|
|
537
|
+
break;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
self.expect(TokenKind::RBracket)?;
|
|
542
|
+
Ok(DestructPattern::Array(elements))
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
fn parse_object_destruct_pattern(&mut self) -> Result<DestructPattern, String> {
|
|
546
|
+
self.expect(TokenKind::LBrace)?;
|
|
547
|
+
let mut props = Vec::new();
|
|
548
|
+
|
|
549
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
550
|
+
let key_tok = self.expect(TokenKind::Ident)?;
|
|
551
|
+
let key_span = Span {
|
|
552
|
+
start: key_tok.span.start,
|
|
553
|
+
end: key_tok.span.end,
|
|
554
|
+
};
|
|
555
|
+
let key = key_tok.literal.clone().ok_or("Expected identifier")?;
|
|
556
|
+
|
|
557
|
+
let value = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
558
|
+
self.advance();
|
|
559
|
+
// Could be renamed binding or nested pattern
|
|
560
|
+
match self.peek_kind() {
|
|
561
|
+
Some(TokenKind::LBracket) | Some(TokenKind::LBrace) => {
|
|
562
|
+
let nested = self.parse_destruct_pattern()?;
|
|
563
|
+
DestructElement::Pattern(Box::new(nested))
|
|
564
|
+
}
|
|
565
|
+
Some(TokenKind::Ident) => {
|
|
566
|
+
let name_tok = self.advance().ok_or("Unexpected EOF")?;
|
|
567
|
+
let name_span = Span {
|
|
568
|
+
start: name_tok.span.start,
|
|
569
|
+
end: name_tok.span.end,
|
|
570
|
+
};
|
|
571
|
+
let name = name_tok.literal.clone().ok_or("Expected identifier")?;
|
|
572
|
+
DestructElement::Ident(name, name_span)
|
|
573
|
+
}
|
|
574
|
+
_ => return Err("Expected identifier or pattern after ':'".to_string()),
|
|
575
|
+
}
|
|
576
|
+
} else {
|
|
577
|
+
// Shorthand: { key } is equivalent to { key: key }
|
|
578
|
+
DestructElement::Ident(key.clone(), key_span)
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
props.push(DestructProp { key, value });
|
|
582
|
+
|
|
583
|
+
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
584
|
+
self.advance();
|
|
585
|
+
} else {
|
|
586
|
+
break;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
self.expect(TokenKind::RBrace)?;
|
|
591
|
+
Ok(DestructPattern::Object(props))
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/// One formal parameter: `name`, `name: T`, `name = expr`, or a destructuring pattern.
|
|
595
|
+
fn parse_fun_param(&mut self) -> Result<FunParam, String> {
|
|
596
|
+
if matches!(
|
|
597
|
+
self.peek_kind(),
|
|
598
|
+
Some(TokenKind::LBracket | TokenKind::LBrace)
|
|
599
|
+
) {
|
|
600
|
+
let pattern = self.parse_destruct_pattern()?;
|
|
601
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
602
|
+
self.advance();
|
|
603
|
+
Some(self.parse_type_annotation()?)
|
|
604
|
+
} else {
|
|
605
|
+
None
|
|
606
|
+
};
|
|
607
|
+
let default = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
608
|
+
self.advance();
|
|
609
|
+
Some(self.parse_expr()?)
|
|
610
|
+
} else {
|
|
611
|
+
None
|
|
612
|
+
};
|
|
613
|
+
return Ok(FunParam::Destructure {
|
|
614
|
+
pattern,
|
|
615
|
+
type_ann,
|
|
616
|
+
default,
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
let param_tok = self.expect_identifier_name()?;
|
|
620
|
+
let name_span = Span {
|
|
621
|
+
start: param_tok.span.start,
|
|
622
|
+
end: param_tok.span.end,
|
|
623
|
+
};
|
|
624
|
+
let param_name = param_tok.literal.clone().ok_or("Expected param name")?;
|
|
625
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
626
|
+
self.advance();
|
|
627
|
+
Some(self.parse_type_annotation()?)
|
|
628
|
+
} else {
|
|
629
|
+
None
|
|
630
|
+
};
|
|
631
|
+
let default = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
632
|
+
self.advance();
|
|
633
|
+
Some(self.parse_expr()?)
|
|
634
|
+
} else {
|
|
635
|
+
None
|
|
636
|
+
};
|
|
637
|
+
Ok(FunParam::Simple(TypedParam {
|
|
638
|
+
name: param_name,
|
|
639
|
+
name_span,
|
|
640
|
+
type_ann,
|
|
641
|
+
default,
|
|
642
|
+
}))
|
|
643
|
+
}
|
|
644
|
+
|
|
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
|
+
}
|
|
667
|
+
|
|
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;
|
|
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
|
+
}
|
|
695
|
+
|
|
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 `&`)
|
|
722
|
+
if matches!(self.peek_kind(), Some(TokenKind::BitOr)) {
|
|
723
|
+
let mut types = vec![base];
|
|
724
|
+
while matches!(self.peek_kind(), Some(TokenKind::BitOr)) {
|
|
725
|
+
self.advance(); // |
|
|
726
|
+
types.push(self.parse_type_intersection()?);
|
|
727
|
+
}
|
|
728
|
+
return Ok(TypeAnnotation::Union(types));
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
Ok(base)
|
|
732
|
+
}
|
|
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
|
+
|
|
769
|
+
/// Parse a primary type (identifier, object, or function type)
|
|
770
|
+
fn parse_type_primary(&mut self) -> Result<TypeAnnotation, String> {
|
|
771
|
+
match self.peek_kind() {
|
|
772
|
+
Some(TokenKind::Ident) => {
|
|
773
|
+
let tok = self.advance().ok_or("Expected type name")?;
|
|
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
|
+
}
|
|
793
|
+
Ok(TypeAnnotation::Simple(name))
|
|
794
|
+
}
|
|
795
|
+
Some(TokenKind::Type | TokenKind::Declare) => {
|
|
796
|
+
let tok = self.advance().ok_or("Expected type name")?;
|
|
797
|
+
let name = tok.literal.clone().ok_or("Expected type name")?;
|
|
798
|
+
Ok(TypeAnnotation::Simple(name))
|
|
799
|
+
}
|
|
800
|
+
// Handle keywords that can be type names
|
|
801
|
+
Some(TokenKind::Null) => {
|
|
802
|
+
self.advance();
|
|
803
|
+
Ok(TypeAnnotation::Simple("null".into()))
|
|
804
|
+
}
|
|
805
|
+
Some(TokenKind::Void) => {
|
|
806
|
+
self.advance();
|
|
807
|
+
Ok(TypeAnnotation::Simple("void".into()))
|
|
808
|
+
}
|
|
809
|
+
Some(TokenKind::LBrace) => {
|
|
810
|
+
// Object type: { key: Type, ... }
|
|
811
|
+
self.advance(); // {
|
|
812
|
+
let mut props = Vec::new();
|
|
813
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
814
|
+
// `for` is a keyword but a common method name (`Symbol.for`); allow it here.
|
|
815
|
+
let key: Arc<str> = match self.peek_kind() {
|
|
816
|
+
Some(TokenKind::Ident) => {
|
|
817
|
+
let tok = self.expect(TokenKind::Ident)?;
|
|
818
|
+
Arc::from(
|
|
819
|
+
tok.literal
|
|
820
|
+
.as_deref()
|
|
821
|
+
.ok_or("Expected property name")?,
|
|
822
|
+
)
|
|
823
|
+
}
|
|
824
|
+
Some(TokenKind::For) => {
|
|
825
|
+
self.advance();
|
|
826
|
+
Arc::from("for")
|
|
827
|
+
}
|
|
828
|
+
_ => {
|
|
829
|
+
return Err(format!(
|
|
830
|
+
"Expected Ident or `for` as object type property name, got {:?}",
|
|
831
|
+
self.peek_kind()
|
|
832
|
+
));
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
self.expect(TokenKind::Colon)?;
|
|
836
|
+
let typ = self.parse_type_annotation()?;
|
|
837
|
+
props.push((key, typ));
|
|
838
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
839
|
+
// Accept `,` or `;` between items (TypeScript-style
|
|
840
|
+
// semicolons are common in interface/object type
|
|
841
|
+
// declarations); also tolerate a trailing separator.
|
|
842
|
+
if matches!(
|
|
843
|
+
self.peek_kind(),
|
|
844
|
+
Some(TokenKind::Comma) | Some(TokenKind::Semicolon)
|
|
845
|
+
) {
|
|
846
|
+
self.advance();
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
self.expect(TokenKind::RBrace)?;
|
|
851
|
+
Ok(TypeAnnotation::Object(props))
|
|
852
|
+
}
|
|
853
|
+
Some(TokenKind::LParen) => {
|
|
854
|
+
// Function type: (T1, T2) => R
|
|
855
|
+
self.advance(); // (
|
|
856
|
+
let mut params = Vec::new();
|
|
857
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
858
|
+
params.push(self.parse_type_annotation()?);
|
|
859
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
860
|
+
self.expect(TokenKind::Comma)?;
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
self.expect(TokenKind::RParen)?;
|
|
864
|
+
self.expect(TokenKind::Arrow)?;
|
|
865
|
+
let returns = self.parse_type_annotation()?;
|
|
866
|
+
Ok(TypeAnnotation::Function {
|
|
867
|
+
params,
|
|
868
|
+
returns: Box::new(returns),
|
|
869
|
+
})
|
|
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
|
+
}
|
|
904
|
+
_ => Err("Expected type annotation".to_string()),
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
fn parse_fun_decl(&mut self, async_: bool) -> Result<Statement, String> {
|
|
909
|
+
let span_start = self.expect(TokenKind::Fn)?.span.start;
|
|
910
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
911
|
+
let name_span = Span {
|
|
912
|
+
start: name_tok.span.start,
|
|
913
|
+
end: name_tok.span.end,
|
|
914
|
+
};
|
|
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)
|
|
917
|
+
self.expect(TokenKind::LParen)?;
|
|
918
|
+
let mut params = Vec::with_capacity(4);
|
|
919
|
+
let mut rest_param = None;
|
|
920
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
921
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
922
|
+
self.advance();
|
|
923
|
+
let rest_tok = self.expect(TokenKind::Ident)?;
|
|
924
|
+
let rest_name_span = Span {
|
|
925
|
+
start: rest_tok.span.start,
|
|
926
|
+
end: rest_tok.span.end,
|
|
927
|
+
};
|
|
928
|
+
let param_name = rest_tok.literal.clone().ok_or("Expected rest param name")?;
|
|
929
|
+
// Optional type annotation for rest param
|
|
930
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
931
|
+
self.advance();
|
|
932
|
+
Some(self.parse_type_annotation()?)
|
|
933
|
+
} else {
|
|
934
|
+
None
|
|
935
|
+
};
|
|
936
|
+
rest_param = Some(TypedParam {
|
|
937
|
+
name: param_name,
|
|
938
|
+
name_span: rest_name_span,
|
|
939
|
+
type_ann,
|
|
940
|
+
default: None,
|
|
941
|
+
});
|
|
942
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
943
|
+
return Err("Rest parameter must be last".to_string());
|
|
944
|
+
}
|
|
945
|
+
break;
|
|
946
|
+
}
|
|
947
|
+
params.push(self.parse_fun_param()?);
|
|
948
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
949
|
+
self.expect(TokenKind::Comma)?;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
self.expect(TokenKind::RParen)?;
|
|
953
|
+
|
|
954
|
+
// Optional return type: `: Type`
|
|
955
|
+
let return_type = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
956
|
+
self.advance();
|
|
957
|
+
Some(self.parse_type_annotation()?)
|
|
958
|
+
} else {
|
|
959
|
+
None
|
|
960
|
+
};
|
|
961
|
+
|
|
962
|
+
let body = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
963
|
+
self.advance(); // =
|
|
964
|
+
let expr = self.parse_expr()?;
|
|
965
|
+
let span = expr.span();
|
|
966
|
+
Box::new(Statement::Block {
|
|
967
|
+
statements: vec![Statement::Return {
|
|
968
|
+
value: Some(expr),
|
|
969
|
+
span,
|
|
970
|
+
}],
|
|
971
|
+
span,
|
|
972
|
+
})
|
|
973
|
+
} else {
|
|
974
|
+
Box::new(self.parse_block()?)
|
|
975
|
+
};
|
|
976
|
+
|
|
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
|
+
};
|
|
988
|
+
|
|
989
|
+
Ok(Statement::FunDecl {
|
|
990
|
+
async_,
|
|
991
|
+
name,
|
|
992
|
+
name_span,
|
|
993
|
+
params,
|
|
994
|
+
rest_param,
|
|
995
|
+
return_type,
|
|
996
|
+
body,
|
|
997
|
+
span: Span {
|
|
998
|
+
start: span_start,
|
|
999
|
+
end,
|
|
1000
|
+
},
|
|
1001
|
+
})
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
fn parse_type_alias(&mut self) -> Result<Statement, String> {
|
|
1005
|
+
let span_start = self.expect(TokenKind::Type)?.span.start;
|
|
1006
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
1007
|
+
let name_span = Span {
|
|
1008
|
+
start: name_tok.span.start,
|
|
1009
|
+
end: name_tok.span.end,
|
|
1010
|
+
};
|
|
1011
|
+
let name = name_tok.literal.clone().ok_or("Expected type alias name")?;
|
|
1012
|
+
let type_params = self.parse_type_params()?;
|
|
1013
|
+
self.expect(TokenKind::Assign)?;
|
|
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
|
+
}
|
|
1068
|
+
Ok(Statement::TypeAlias {
|
|
1069
|
+
name,
|
|
1070
|
+
name_span,
|
|
1071
|
+
ty,
|
|
1072
|
+
span: self.span_end(span_start),
|
|
1073
|
+
})
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
fn parse_declare(&mut self) -> Result<Statement, String> {
|
|
1077
|
+
let span_start = self.expect(TokenKind::Declare)?.span.start;
|
|
1078
|
+
let async_ = if matches!(self.peek_kind(), Some(TokenKind::Async)) {
|
|
1079
|
+
self.advance();
|
|
1080
|
+
true
|
|
1081
|
+
} else {
|
|
1082
|
+
false
|
|
1083
|
+
};
|
|
1084
|
+
if matches!(self.peek_kind(), Some(TokenKind::Fn)) {
|
|
1085
|
+
return self.parse_declare_fun(span_start, async_);
|
|
1086
|
+
}
|
|
1087
|
+
let const_ = match self.peek_kind() {
|
|
1088
|
+
Some(TokenKind::Let) => {
|
|
1089
|
+
self.advance();
|
|
1090
|
+
false
|
|
1091
|
+
}
|
|
1092
|
+
Some(TokenKind::Const) => {
|
|
1093
|
+
self.advance();
|
|
1094
|
+
true
|
|
1095
|
+
}
|
|
1096
|
+
_ => {
|
|
1097
|
+
return Err(
|
|
1098
|
+
"Expected `let`, `const`, `async fn`, or `fn` after `declare`".to_string(),
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
};
|
|
1102
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
1103
|
+
let name_span = Span {
|
|
1104
|
+
start: name_tok.span.start,
|
|
1105
|
+
end: name_tok.span.end,
|
|
1106
|
+
};
|
|
1107
|
+
let name = name_tok.literal.clone().ok_or("Expected identifier")?;
|
|
1108
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
1109
|
+
self.advance();
|
|
1110
|
+
Some(self.parse_type_annotation()?)
|
|
1111
|
+
} else {
|
|
1112
|
+
None
|
|
1113
|
+
};
|
|
1114
|
+
if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
1115
|
+
return Err("`declare` cannot have an initializer".to_string());
|
|
1116
|
+
}
|
|
1117
|
+
Ok(Statement::DeclareVar {
|
|
1118
|
+
name,
|
|
1119
|
+
name_span,
|
|
1120
|
+
type_ann,
|
|
1121
|
+
const_,
|
|
1122
|
+
span: self.span_end(span_start),
|
|
1123
|
+
})
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
fn parse_declare_fun(
|
|
1127
|
+
&mut self,
|
|
1128
|
+
span_start: (usize, usize),
|
|
1129
|
+
async_: bool,
|
|
1130
|
+
) -> Result<Statement, String> {
|
|
1131
|
+
self.expect(TokenKind::Fn)?;
|
|
1132
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
1133
|
+
let name_span = Span {
|
|
1134
|
+
start: name_tok.span.start,
|
|
1135
|
+
end: name_tok.span.end,
|
|
1136
|
+
};
|
|
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)
|
|
1139
|
+
self.expect(TokenKind::LParen)?;
|
|
1140
|
+
let mut params = Vec::with_capacity(4);
|
|
1141
|
+
let mut rest_param = None;
|
|
1142
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1143
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
1144
|
+
self.advance();
|
|
1145
|
+
let rest_tok = self.expect(TokenKind::Ident)?;
|
|
1146
|
+
let rest_name_span = Span {
|
|
1147
|
+
start: rest_tok.span.start,
|
|
1148
|
+
end: rest_tok.span.end,
|
|
1149
|
+
};
|
|
1150
|
+
let param_name = rest_tok.literal.clone().ok_or("Expected rest param name")?;
|
|
1151
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
1152
|
+
self.advance();
|
|
1153
|
+
Some(self.parse_type_annotation()?)
|
|
1154
|
+
} else {
|
|
1155
|
+
None
|
|
1156
|
+
};
|
|
1157
|
+
rest_param = Some(TypedParam {
|
|
1158
|
+
name: param_name,
|
|
1159
|
+
name_span: rest_name_span,
|
|
1160
|
+
type_ann,
|
|
1161
|
+
default: None,
|
|
1162
|
+
});
|
|
1163
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1164
|
+
return Err("Rest parameter must be last".to_string());
|
|
1165
|
+
}
|
|
1166
|
+
break;
|
|
1167
|
+
}
|
|
1168
|
+
params.push(self.parse_fun_param()?);
|
|
1169
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1170
|
+
self.expect(TokenKind::Comma)?;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
self.expect(TokenKind::RParen)?;
|
|
1174
|
+
let return_type = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
1175
|
+
self.advance();
|
|
1176
|
+
Some(self.parse_type_annotation()?)
|
|
1177
|
+
} else {
|
|
1178
|
+
None
|
|
1179
|
+
};
|
|
1180
|
+
if matches!(
|
|
1181
|
+
self.peek_kind(),
|
|
1182
|
+
Some(TokenKind::Assign | TokenKind::LBrace | TokenKind::Indent)
|
|
1183
|
+
) {
|
|
1184
|
+
return Err("`declare function` must not have a body".to_string());
|
|
1185
|
+
}
|
|
1186
|
+
if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
1187
|
+
self.advance();
|
|
1188
|
+
}
|
|
1189
|
+
Ok(Statement::DeclareFun {
|
|
1190
|
+
async_,
|
|
1191
|
+
name,
|
|
1192
|
+
name_span,
|
|
1193
|
+
params,
|
|
1194
|
+
rest_param,
|
|
1195
|
+
return_type,
|
|
1196
|
+
span: self.span_end(span_start),
|
|
1197
|
+
})
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
fn parse_if(&mut self) -> Result<Statement, String> {
|
|
1201
|
+
let span_start = self.expect(TokenKind::If)?.span.start;
|
|
1202
|
+
self.expect(TokenKind::LParen)?;
|
|
1203
|
+
let cond = self.parse_expr()?;
|
|
1204
|
+
self.expect(TokenKind::RParen)?;
|
|
1205
|
+
let then_branch = Box::new(self.parse_block_or_statement()?);
|
|
1206
|
+
let else_branch = if matches!(self.peek_kind(), Some(TokenKind::Else)) {
|
|
1207
|
+
self.advance();
|
|
1208
|
+
Some(Box::new(self.parse_block_or_statement()?))
|
|
1209
|
+
} else {
|
|
1210
|
+
None
|
|
1211
|
+
};
|
|
1212
|
+
Ok(Statement::If {
|
|
1213
|
+
cond,
|
|
1214
|
+
then_branch,
|
|
1215
|
+
else_branch,
|
|
1216
|
+
span: self.span_end(span_start),
|
|
1217
|
+
})
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
fn parse_while(&mut self) -> Result<Statement, String> {
|
|
1221
|
+
let span_start = self.expect(TokenKind::While)?.span.start;
|
|
1222
|
+
self.expect(TokenKind::LParen)?;
|
|
1223
|
+
let cond = self.parse_expr()?;
|
|
1224
|
+
self.expect(TokenKind::RParen)?;
|
|
1225
|
+
let body = Box::new(self.parse_block_or_statement()?);
|
|
1226
|
+
Ok(Statement::While {
|
|
1227
|
+
cond,
|
|
1228
|
+
body,
|
|
1229
|
+
span: self.span_end(span_start),
|
|
1230
|
+
})
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
fn parse_for(&mut self) -> Result<Statement, String> {
|
|
1234
|
+
let span_start = self.expect(TokenKind::For)?.span.start;
|
|
1235
|
+
self.expect(TokenKind::LParen)?;
|
|
1236
|
+
let init = if matches!(self.peek_kind(), Some(TokenKind::Let | TokenKind::Const)) {
|
|
1237
|
+
let mutable = matches!(self.peek_kind(), Some(TokenKind::Let));
|
|
1238
|
+
let var_span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
1239
|
+
self.advance();
|
|
1240
|
+
let for_name_tok = self.expect(TokenKind::Ident)?;
|
|
1241
|
+
let name_span = Span {
|
|
1242
|
+
start: for_name_tok.span.start,
|
|
1243
|
+
end: for_name_tok.span.end,
|
|
1244
|
+
};
|
|
1245
|
+
let name = for_name_tok.literal.clone().ok_or("Expected identifier")?;
|
|
1246
|
+
if matches!(self.peek_kind(), Some(TokenKind::Of)) {
|
|
1247
|
+
self.advance();
|
|
1248
|
+
let iterable = self.parse_expr()?;
|
|
1249
|
+
self.expect(TokenKind::RParen)?;
|
|
1250
|
+
let body = Box::new(self.parse_block_or_statement()?);
|
|
1251
|
+
return Ok(Statement::ForOf {
|
|
1252
|
+
name,
|
|
1253
|
+
name_span,
|
|
1254
|
+
iterable,
|
|
1255
|
+
body,
|
|
1256
|
+
span: self.span_end(span_start),
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
1260
|
+
self.advance();
|
|
1261
|
+
Some(self.parse_type_annotation()?)
|
|
1262
|
+
} else {
|
|
1263
|
+
None
|
|
1264
|
+
};
|
|
1265
|
+
let init_expr = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
1266
|
+
self.advance();
|
|
1267
|
+
Some(self.parse_expr()?)
|
|
1268
|
+
} else {
|
|
1269
|
+
None
|
|
1270
|
+
};
|
|
1271
|
+
let first = Statement::VarDecl {
|
|
1272
|
+
name,
|
|
1273
|
+
name_span,
|
|
1274
|
+
mutable,
|
|
1275
|
+
type_ann,
|
|
1276
|
+
init: init_expr,
|
|
1277
|
+
span: self.span_end(var_span_start),
|
|
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))
|
|
1298
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
1299
|
+
None
|
|
1300
|
+
} else {
|
|
1301
|
+
let st = self.peek().ok_or("Unexpected EOF in for init")?;
|
|
1302
|
+
let span_start = st.span.start;
|
|
1303
|
+
Some(Box::new(Statement::ExprStmt {
|
|
1304
|
+
expr: self.parse_expr()?,
|
|
1305
|
+
span: Span {
|
|
1306
|
+
start: span_start,
|
|
1307
|
+
end: self.peek().map(|x| x.span.end).unwrap_or(span_start),
|
|
1308
|
+
},
|
|
1309
|
+
}))
|
|
1310
|
+
};
|
|
1311
|
+
if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
1312
|
+
self.advance();
|
|
1313
|
+
}
|
|
1314
|
+
let cond = if matches!(
|
|
1315
|
+
self.peek_kind(),
|
|
1316
|
+
Some(TokenKind::Semicolon | TokenKind::RParen)
|
|
1317
|
+
) {
|
|
1318
|
+
None
|
|
1319
|
+
} else {
|
|
1320
|
+
let c = self.parse_expr()?;
|
|
1321
|
+
self.expect(TokenKind::Semicolon)?;
|
|
1322
|
+
Some(c)
|
|
1323
|
+
};
|
|
1324
|
+
// `for (init; ; update)` — when the condition is empty we matched `;` above but did not
|
|
1325
|
+
// consume it; skip it so `update` / `)` parse correctly (e.g. `for (;;)`).
|
|
1326
|
+
if cond.is_none() && matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
1327
|
+
self.advance();
|
|
1328
|
+
}
|
|
1329
|
+
let update = if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1330
|
+
None
|
|
1331
|
+
} else {
|
|
1332
|
+
let u = self.parse_expr()?;
|
|
1333
|
+
Some(u)
|
|
1334
|
+
};
|
|
1335
|
+
self.expect(TokenKind::RParen)?;
|
|
1336
|
+
let body = Box::new(self.parse_block_or_statement()?);
|
|
1337
|
+
Ok(Statement::For {
|
|
1338
|
+
init,
|
|
1339
|
+
cond,
|
|
1340
|
+
update,
|
|
1341
|
+
body,
|
|
1342
|
+
span: self.span_end(span_start),
|
|
1343
|
+
})
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
fn parse_return(&mut self) -> Result<Statement, String> {
|
|
1347
|
+
let span_start = self.expect(TokenKind::Return)?.span.start;
|
|
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
|
+
)
|
|
1364
|
+
|| self.peek_kind().is_none()
|
|
1365
|
+
{
|
|
1366
|
+
None
|
|
1367
|
+
} else {
|
|
1368
|
+
Some(self.parse_expr()?)
|
|
1369
|
+
};
|
|
1370
|
+
Ok(Statement::Return {
|
|
1371
|
+
value,
|
|
1372
|
+
span: self.span_end(span_start),
|
|
1373
|
+
})
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
fn parse_switch(&mut self) -> Result<Statement, String> {
|
|
1377
|
+
let span_start = self.expect(TokenKind::Switch)?.span.start;
|
|
1378
|
+
self.expect(TokenKind::LParen)?;
|
|
1379
|
+
let expr = self.parse_expr()?;
|
|
1380
|
+
self.expect(TokenKind::RParen)?;
|
|
1381
|
+
self.expect(TokenKind::LBrace)?;
|
|
1382
|
+
let mut cases = Vec::new();
|
|
1383
|
+
let mut default_body = None;
|
|
1384
|
+
loop {
|
|
1385
|
+
if matches!(self.peek_kind(), Some(TokenKind::Case)) {
|
|
1386
|
+
self.advance();
|
|
1387
|
+
let case_expr = self.parse_expr()?;
|
|
1388
|
+
self.expect(TokenKind::Colon)?;
|
|
1389
|
+
let mut body = Vec::new();
|
|
1390
|
+
while !matches!(self.peek_kind(), Some(TokenKind::Case))
|
|
1391
|
+
&& !matches!(self.peek_kind(), Some(TokenKind::Default))
|
|
1392
|
+
&& !matches!(self.peek_kind(), Some(TokenKind::RBrace))
|
|
1393
|
+
&& self.peek_kind().is_some()
|
|
1394
|
+
{
|
|
1395
|
+
body.push(self.parse_statement()?);
|
|
1396
|
+
}
|
|
1397
|
+
cases.push((Some(case_expr), body));
|
|
1398
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Default)) {
|
|
1399
|
+
self.advance();
|
|
1400
|
+
self.expect(TokenKind::Colon)?;
|
|
1401
|
+
let mut body = Vec::new();
|
|
1402
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RBrace))
|
|
1403
|
+
&& self.peek_kind().is_some()
|
|
1404
|
+
{
|
|
1405
|
+
body.push(self.parse_statement()?);
|
|
1406
|
+
}
|
|
1407
|
+
default_body = Some(body);
|
|
1408
|
+
break;
|
|
1409
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
1410
|
+
break;
|
|
1411
|
+
} else {
|
|
1412
|
+
return Err("Expected case or default in switch".to_string());
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
self.expect(TokenKind::RBrace)?;
|
|
1416
|
+
Ok(Statement::Switch {
|
|
1417
|
+
expr,
|
|
1418
|
+
cases,
|
|
1419
|
+
default_body,
|
|
1420
|
+
span: self.span_end(span_start),
|
|
1421
|
+
})
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
fn parse_do_while(&mut self) -> Result<Statement, String> {
|
|
1425
|
+
let span_start = self.expect(TokenKind::Do)?.span.start;
|
|
1426
|
+
let body = Box::new(self.parse_block_or_statement()?);
|
|
1427
|
+
self.expect(TokenKind::While)?;
|
|
1428
|
+
self.expect(TokenKind::LParen)?;
|
|
1429
|
+
let cond = self.parse_expr()?;
|
|
1430
|
+
self.expect(TokenKind::RParen)?;
|
|
1431
|
+
Ok(Statement::DoWhile {
|
|
1432
|
+
body,
|
|
1433
|
+
cond,
|
|
1434
|
+
span: self.span_end(span_start),
|
|
1435
|
+
})
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
fn parse_throw(&mut self) -> Result<Statement, String> {
|
|
1439
|
+
let span_start = self.expect(TokenKind::Throw)?.span.start;
|
|
1440
|
+
let value = self.parse_expr()?;
|
|
1441
|
+
Ok(Statement::Throw {
|
|
1442
|
+
value,
|
|
1443
|
+
span: self.span_end(span_start),
|
|
1444
|
+
})
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
fn parse_try(&mut self) -> Result<Statement, String> {
|
|
1448
|
+
let span_start = self.expect(TokenKind::Try)?.span.start;
|
|
1449
|
+
let body = Box::new(self.parse_block_or_statement()?);
|
|
1450
|
+
|
|
1451
|
+
let mut catch_param = None;
|
|
1452
|
+
let mut catch_param_span = None;
|
|
1453
|
+
let mut catch_body = None;
|
|
1454
|
+
let mut finally_body = None;
|
|
1455
|
+
|
|
1456
|
+
if matches!(self.peek_kind(), Some(TokenKind::Catch)) {
|
|
1457
|
+
self.advance();
|
|
1458
|
+
self.expect(TokenKind::LParen)?;
|
|
1459
|
+
let catch_tok = self.expect(TokenKind::Ident)?;
|
|
1460
|
+
catch_param_span = Some(Span {
|
|
1461
|
+
start: catch_tok.span.start,
|
|
1462
|
+
end: catch_tok.span.end,
|
|
1463
|
+
});
|
|
1464
|
+
catch_param = catch_tok.literal.clone();
|
|
1465
|
+
self.expect(TokenKind::RParen)?;
|
|
1466
|
+
catch_body = Some(Box::new(self.parse_block_or_statement()?));
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
if matches!(self.peek_kind(), Some(TokenKind::Finally)) {
|
|
1470
|
+
self.advance();
|
|
1471
|
+
finally_body = Some(Box::new(self.parse_block_or_statement()?));
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
if catch_body.is_none() && finally_body.is_none() {
|
|
1475
|
+
return Err("try statement requires catch or finally".to_string());
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
Ok(Statement::Try {
|
|
1479
|
+
body,
|
|
1480
|
+
catch_param,
|
|
1481
|
+
catch_param_span,
|
|
1482
|
+
catch_body,
|
|
1483
|
+
finally_body,
|
|
1484
|
+
span: self.span_end(span_start),
|
|
1485
|
+
})
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
fn parse_import(&mut self) -> Result<Statement, String> {
|
|
1489
|
+
let span_start = self.expect(TokenKind::Import)?.span.start;
|
|
1490
|
+
let specifiers = if matches!(self.peek_kind(), Some(TokenKind::LBrace)) {
|
|
1491
|
+
// Named: import { a, b as c } from "..."
|
|
1492
|
+
self.advance();
|
|
1493
|
+
let mut specs = Vec::new();
|
|
1494
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
1495
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
1496
|
+
let name_span = Span {
|
|
1497
|
+
start: name_tok.span.start,
|
|
1498
|
+
end: name_tok.span.end,
|
|
1499
|
+
};
|
|
1500
|
+
let name = name_tok
|
|
1501
|
+
.literal
|
|
1502
|
+
.clone()
|
|
1503
|
+
.ok_or("Expected identifier in import")?;
|
|
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)) {
|
|
1507
|
+
self.advance(); // consume 'as'
|
|
1508
|
+
let alias_tok = self.expect(TokenKind::Ident)?;
|
|
1509
|
+
let asp = Span {
|
|
1510
|
+
start: alias_tok.span.start,
|
|
1511
|
+
end: alias_tok.span.end,
|
|
1512
|
+
};
|
|
1513
|
+
(
|
|
1514
|
+
Some(
|
|
1515
|
+
alias_tok
|
|
1516
|
+
.literal
|
|
1517
|
+
.clone()
|
|
1518
|
+
.ok_or("Expected alias after 'as'")?,
|
|
1519
|
+
),
|
|
1520
|
+
Some(asp),
|
|
1521
|
+
)
|
|
1522
|
+
} else {
|
|
1523
|
+
(None, None)
|
|
1524
|
+
};
|
|
1525
|
+
specs.push(ImportSpecifier::Named {
|
|
1526
|
+
name,
|
|
1527
|
+
name_span,
|
|
1528
|
+
alias,
|
|
1529
|
+
alias_span,
|
|
1530
|
+
});
|
|
1531
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
1532
|
+
self.expect(TokenKind::Comma)?;
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
self.expect(TokenKind::RBrace)?;
|
|
1536
|
+
specs
|
|
1537
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Star)) {
|
|
1538
|
+
// Namespace: import * as M from "..."
|
|
1539
|
+
self.advance();
|
|
1540
|
+
self.expect(TokenKind::As)?;
|
|
1541
|
+
let alias_tok = self.expect(TokenKind::Ident)?;
|
|
1542
|
+
let name_span = Span {
|
|
1543
|
+
start: alias_tok.span.start,
|
|
1544
|
+
end: alias_tok.span.end,
|
|
1545
|
+
};
|
|
1546
|
+
let alias = alias_tok
|
|
1547
|
+
.literal
|
|
1548
|
+
.clone()
|
|
1549
|
+
.ok_or("Expected identifier after 'as'")?;
|
|
1550
|
+
vec![ImportSpecifier::Namespace {
|
|
1551
|
+
name: alias,
|
|
1552
|
+
name_span,
|
|
1553
|
+
}]
|
|
1554
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Ident)) {
|
|
1555
|
+
// Default: import X from "..."
|
|
1556
|
+
let def_tok = self.expect(TokenKind::Ident)?;
|
|
1557
|
+
let name_span = Span {
|
|
1558
|
+
start: def_tok.span.start,
|
|
1559
|
+
end: def_tok.span.end,
|
|
1560
|
+
};
|
|
1561
|
+
let name = def_tok.literal.clone().ok_or("Expected identifier")?;
|
|
1562
|
+
vec![ImportSpecifier::Default { name, name_span }]
|
|
1563
|
+
} else {
|
|
1564
|
+
return Err("Expected { }, * as name, or default import".to_string());
|
|
1565
|
+
};
|
|
1566
|
+
let from_tok = self.expect(TokenKind::Ident)?;
|
|
1567
|
+
if from_tok.literal.as_deref() != Some("from") {
|
|
1568
|
+
return Err("Expected 'from' in import statement".to_string());
|
|
1569
|
+
}
|
|
1570
|
+
let from = self
|
|
1571
|
+
.expect(TokenKind::String)?
|
|
1572
|
+
.literal
|
|
1573
|
+
.clone()
|
|
1574
|
+
.ok_or("Expected string path in import")?;
|
|
1575
|
+
Ok(Statement::Import {
|
|
1576
|
+
specifiers,
|
|
1577
|
+
from,
|
|
1578
|
+
span: self.span_end(span_start),
|
|
1579
|
+
})
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
fn parse_export(&mut self) -> Result<Statement, String> {
|
|
1583
|
+
let span_start = self.expect(TokenKind::Export)?.span.start;
|
|
1584
|
+
let declaration = if matches!(self.peek_kind(), Some(TokenKind::Default)) {
|
|
1585
|
+
self.advance();
|
|
1586
|
+
let expr = self.parse_expr()?;
|
|
1587
|
+
ExportDeclaration::Default(expr)
|
|
1588
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Type)) {
|
|
1589
|
+
ExportDeclaration::Named(Box::new(self.parse_type_alias()?))
|
|
1590
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Declare)) {
|
|
1591
|
+
ExportDeclaration::Named(Box::new(self.parse_declare()?))
|
|
1592
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Const)) {
|
|
1593
|
+
ExportDeclaration::Named(Box::new(self.parse_var_decl(false)?))
|
|
1594
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Let)) {
|
|
1595
|
+
ExportDeclaration::Named(Box::new(self.parse_var_decl(true)?))
|
|
1596
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Async))
|
|
1597
|
+
|| matches!(self.peek_kind(), Some(TokenKind::Fn))
|
|
1598
|
+
{
|
|
1599
|
+
let async_ = matches!(self.peek_kind(), Some(TokenKind::Async));
|
|
1600
|
+
if async_ {
|
|
1601
|
+
self.advance();
|
|
1602
|
+
}
|
|
1603
|
+
ExportDeclaration::Named(Box::new(self.parse_fun_decl(async_)?))
|
|
1604
|
+
} else {
|
|
1605
|
+
return Err(
|
|
1606
|
+
"Expected 'default', 'type', 'declare', 'const', 'let', or 'fn' after export"
|
|
1607
|
+
.to_string(),
|
|
1608
|
+
);
|
|
1609
|
+
};
|
|
1610
|
+
Ok(Statement::Export {
|
|
1611
|
+
declaration: Box::new(declaration),
|
|
1612
|
+
span: self.span_end(span_start),
|
|
1613
|
+
})
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
fn parse_expr(&mut self) -> Result<Expr, String> {
|
|
1617
|
+
self.parse_assign()
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
fn parse_assign(&mut self) -> Result<Expr, String> {
|
|
1621
|
+
let left = self.parse_conditional()?;
|
|
1622
|
+
|
|
1623
|
+
// Check for simple assignment
|
|
1624
|
+
if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
1625
|
+
let start = left.span().start;
|
|
1626
|
+
|
|
1627
|
+
// Variable assignment: x = val
|
|
1628
|
+
if let Expr::Ident { name, .. } = &left {
|
|
1629
|
+
let name = Arc::clone(name);
|
|
1630
|
+
self.advance(); // =
|
|
1631
|
+
let value = self.parse_assign()?;
|
|
1632
|
+
let end = value.span().end;
|
|
1633
|
+
return Ok(Expr::Assign {
|
|
1634
|
+
name,
|
|
1635
|
+
value: Box::new(value),
|
|
1636
|
+
span: Span { start, end },
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
// Member assignment: obj.prop = val
|
|
1641
|
+
if let Expr::Member {
|
|
1642
|
+
object,
|
|
1643
|
+
prop: MemberProp::Name {
|
|
1644
|
+
name: prop_name, ..
|
|
1645
|
+
},
|
|
1646
|
+
..
|
|
1647
|
+
} = &left
|
|
1648
|
+
{
|
|
1649
|
+
let object = Box::clone(object);
|
|
1650
|
+
let prop = Arc::clone(prop_name);
|
|
1651
|
+
self.advance(); // =
|
|
1652
|
+
let value = self.parse_assign()?;
|
|
1653
|
+
let end = value.span().end;
|
|
1654
|
+
return Ok(Expr::MemberAssign {
|
|
1655
|
+
object,
|
|
1656
|
+
prop,
|
|
1657
|
+
value: Box::new(value),
|
|
1658
|
+
span: Span { start, end },
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
// Index assignment: arr[idx] = val or obj[key] = val
|
|
1663
|
+
if let Expr::Index { object, index, .. } = &left {
|
|
1664
|
+
let object = Box::clone(object);
|
|
1665
|
+
let index = Box::clone(index);
|
|
1666
|
+
self.advance(); // =
|
|
1667
|
+
let value = self.parse_assign()?;
|
|
1668
|
+
let end = value.span().end;
|
|
1669
|
+
return Ok(Expr::IndexAssign {
|
|
1670
|
+
object,
|
|
1671
|
+
index,
|
|
1672
|
+
value: Box::new(value),
|
|
1673
|
+
span: Span { start, end },
|
|
1674
|
+
});
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
// Check for compound assignment (+=, -=, *=, /=, %=)
|
|
1679
|
+
let compound_op = match self.peek_kind() {
|
|
1680
|
+
Some(TokenKind::PlusAssign) => Some(CompoundOp::Add),
|
|
1681
|
+
Some(TokenKind::MinusAssign) => Some(CompoundOp::Sub),
|
|
1682
|
+
Some(TokenKind::StarAssign) => Some(CompoundOp::Mul),
|
|
1683
|
+
Some(TokenKind::SlashAssign) => Some(CompoundOp::Div),
|
|
1684
|
+
Some(TokenKind::PercentAssign) => Some(CompoundOp::Mod),
|
|
1685
|
+
_ => None,
|
|
1686
|
+
};
|
|
1687
|
+
|
|
1688
|
+
if let Some(op) = compound_op {
|
|
1689
|
+
if let Expr::Ident { name, span } = &left {
|
|
1690
|
+
let name = Arc::clone(name);
|
|
1691
|
+
let start = span.start;
|
|
1692
|
+
self.advance(); // consume the compound operator
|
|
1693
|
+
let value = self.parse_assign()?;
|
|
1694
|
+
let end = value.span().end;
|
|
1695
|
+
return Ok(Expr::CompoundAssign {
|
|
1696
|
+
name,
|
|
1697
|
+
op,
|
|
1698
|
+
value: Box::new(value),
|
|
1699
|
+
span: Span { start, end },
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
// Check for logical assignment (&&=, ||=, ??=)
|
|
1705
|
+
let logical_op = match self.peek_kind() {
|
|
1706
|
+
Some(TokenKind::AndAndAssign) => Some(LogicalAssignOp::AndAnd),
|
|
1707
|
+
Some(TokenKind::OrOrAssign) => Some(LogicalAssignOp::OrOr),
|
|
1708
|
+
Some(TokenKind::NullishAssign) => Some(LogicalAssignOp::Nullish),
|
|
1709
|
+
_ => None,
|
|
1710
|
+
};
|
|
1711
|
+
|
|
1712
|
+
if let Some(op) = logical_op {
|
|
1713
|
+
if let Expr::Ident { name, span } = &left {
|
|
1714
|
+
let name = Arc::clone(name);
|
|
1715
|
+
let start = span.start;
|
|
1716
|
+
self.advance(); // consume the logical assign operator
|
|
1717
|
+
let value = self.parse_assign()?;
|
|
1718
|
+
let end = value.span().end;
|
|
1719
|
+
return Ok(Expr::LogicalAssign {
|
|
1720
|
+
name,
|
|
1721
|
+
op,
|
|
1722
|
+
value: Box::new(value),
|
|
1723
|
+
span: Span { start, end },
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
Ok(left)
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
fn parse_conditional(&mut self) -> Result<Expr, String> {
|
|
1732
|
+
let cond = self.parse_nullish_coalesce()?;
|
|
1733
|
+
if matches!(self.peek_kind(), Some(TokenKind::Question)) {
|
|
1734
|
+
let start = cond.span().start;
|
|
1735
|
+
self.advance(); // ?
|
|
1736
|
+
let then_branch = self.parse_expr()?;
|
|
1737
|
+
self.expect(TokenKind::Colon)?;
|
|
1738
|
+
let else_branch = self.parse_conditional()?; // right-associative
|
|
1739
|
+
let end = else_branch.span().end;
|
|
1740
|
+
Ok(Expr::Conditional {
|
|
1741
|
+
cond: Box::new(cond),
|
|
1742
|
+
then_branch: Box::new(then_branch),
|
|
1743
|
+
else_branch: Box::new(else_branch),
|
|
1744
|
+
span: Span { start, end },
|
|
1745
|
+
})
|
|
1746
|
+
} else {
|
|
1747
|
+
Ok(cond)
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
fn parse_nullish_coalesce(&mut self) -> Result<Expr, String> {
|
|
1752
|
+
let mut left = self.parse_or()?;
|
|
1753
|
+
while matches!(self.peek_kind(), Some(TokenKind::NullishCoalesce)) {
|
|
1754
|
+
self.advance();
|
|
1755
|
+
let right = self.parse_or()?;
|
|
1756
|
+
let start = left.span().start;
|
|
1757
|
+
let end = right.span().end;
|
|
1758
|
+
left = Expr::NullishCoalesce {
|
|
1759
|
+
left: Box::new(left),
|
|
1760
|
+
right: Box::new(right),
|
|
1761
|
+
span: Span { start, end },
|
|
1762
|
+
};
|
|
1763
|
+
}
|
|
1764
|
+
Ok(left)
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
// Binary operators generated by macros to reduce duplication
|
|
1768
|
+
binary_single_op!(parse_or, parse_and, Or, BinOp::Or);
|
|
1769
|
+
binary_single_op!(parse_and, parse_bit_or, And, BinOp::And);
|
|
1770
|
+
binary_single_op!(parse_bit_or, parse_bit_xor, BitOr, BinOp::BitOr);
|
|
1771
|
+
binary_single_op!(parse_bit_xor, parse_bit_and, BitXor, BinOp::BitXor);
|
|
1772
|
+
binary_single_op!(parse_bit_and, parse_shift, BitAnd, BinOp::BitAnd);
|
|
1773
|
+
|
|
1774
|
+
binary_multi_op!(parse_shift, parse_equality, Shl => BinOp::Shl, Shr => BinOp::Shr, UShr => BinOp::UShr);
|
|
1775
|
+
binary_multi_op!(parse_equality, parse_comparison,
|
|
1776
|
+
StrictEq => BinOp::StrictEq, StrictNe => BinOp::StrictNe,
|
|
1777
|
+
Eq => BinOp::Eq, Ne => BinOp::Ne);
|
|
1778
|
+
binary_multi_op!(parse_comparison, parse_term,
|
|
1779
|
+
Lt => BinOp::Lt, Le => BinOp::Le, Gt => BinOp::Gt, Ge => BinOp::Ge, In => BinOp::In);
|
|
1780
|
+
binary_multi_op!(parse_term, parse_factor, Plus => BinOp::Add, Minus => BinOp::Sub);
|
|
1781
|
+
binary_multi_op!(parse_factor, parse_pow, Star => BinOp::Mul, Slash => BinOp::Div, Percent => BinOp::Mod);
|
|
1782
|
+
|
|
1783
|
+
fn parse_pow(&mut self) -> Result<Expr, String> {
|
|
1784
|
+
let left = self.parse_unary()?;
|
|
1785
|
+
if matches!(self.peek_kind(), Some(TokenKind::StarStar)) {
|
|
1786
|
+
self.advance();
|
|
1787
|
+
let right = self.parse_pow()?; // right-associative
|
|
1788
|
+
let start = left.span().start;
|
|
1789
|
+
let end = right.span().end;
|
|
1790
|
+
return Ok(Expr::Binary {
|
|
1791
|
+
left: Box::new(left),
|
|
1792
|
+
op: BinOp::Pow,
|
|
1793
|
+
right: Box::new(right),
|
|
1794
|
+
span: Span { start, end },
|
|
1795
|
+
});
|
|
1796
|
+
}
|
|
1797
|
+
Ok(left)
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
fn parse_unary(&mut self) -> Result<Expr, String> {
|
|
1801
|
+
// Handle prefix ++/-- (consolidated)
|
|
1802
|
+
if let Some(is_inc) = match self.peek_kind() {
|
|
1803
|
+
Some(TokenKind::PlusPlus) => Some(true),
|
|
1804
|
+
Some(TokenKind::MinusMinus) => Some(false),
|
|
1805
|
+
_ => None,
|
|
1806
|
+
} {
|
|
1807
|
+
let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
1808
|
+
self.advance();
|
|
1809
|
+
let operand = self.parse_unary()?;
|
|
1810
|
+
if let Expr::Ident { name, span } = &operand {
|
|
1811
|
+
let name = Arc::clone(name);
|
|
1812
|
+
let span = Span {
|
|
1813
|
+
start: span_start,
|
|
1814
|
+
end: span.end,
|
|
1815
|
+
};
|
|
1816
|
+
return Ok(if is_inc {
|
|
1817
|
+
Expr::PrefixInc { name, span }
|
|
1818
|
+
} else {
|
|
1819
|
+
Expr::PrefixDec { name, span }
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
return Err(format!(
|
|
1823
|
+
"Prefix {} requires an identifier",
|
|
1824
|
+
if is_inc { "++" } else { "--" }
|
|
1825
|
+
));
|
|
1826
|
+
}
|
|
1827
|
+
let op = match self.peek_kind() {
|
|
1828
|
+
Some(TokenKind::Not) => UnaryOp::Not,
|
|
1829
|
+
Some(TokenKind::Minus) => UnaryOp::Neg,
|
|
1830
|
+
Some(TokenKind::Plus) => UnaryOp::Pos,
|
|
1831
|
+
Some(TokenKind::BitNot) => UnaryOp::BitNot,
|
|
1832
|
+
Some(TokenKind::Void) => UnaryOp::Void,
|
|
1833
|
+
Some(TokenKind::TypeOf) => {
|
|
1834
|
+
let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
1835
|
+
self.advance();
|
|
1836
|
+
let operand = self.parse_unary()?;
|
|
1837
|
+
let end = operand.span().end;
|
|
1838
|
+
return Ok(Expr::TypeOf {
|
|
1839
|
+
operand: Box::new(operand),
|
|
1840
|
+
span: Span {
|
|
1841
|
+
start: span_start,
|
|
1842
|
+
end,
|
|
1843
|
+
},
|
|
1844
|
+
});
|
|
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
|
+
}
|
|
1859
|
+
Some(TokenKind::Await) => {
|
|
1860
|
+
let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
1861
|
+
self.advance();
|
|
1862
|
+
let operand = self.parse_unary()?;
|
|
1863
|
+
let end = operand.span().end;
|
|
1864
|
+
return Ok(Expr::Await {
|
|
1865
|
+
operand: Box::new(operand),
|
|
1866
|
+
span: Span {
|
|
1867
|
+
start: span_start,
|
|
1868
|
+
end,
|
|
1869
|
+
},
|
|
1870
|
+
});
|
|
1871
|
+
}
|
|
1872
|
+
_ => return self.parse_postfix(),
|
|
1873
|
+
};
|
|
1874
|
+
let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
1875
|
+
self.advance();
|
|
1876
|
+
let operand = self.parse_unary()?;
|
|
1877
|
+
let end = operand.span().end;
|
|
1878
|
+
Ok(Expr::Unary {
|
|
1879
|
+
op,
|
|
1880
|
+
operand: Box::new(operand),
|
|
1881
|
+
span: Span {
|
|
1882
|
+
start: span_start,
|
|
1883
|
+
end,
|
|
1884
|
+
},
|
|
1885
|
+
})
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
/// Member chain (`.`, `?.`, `[]`) without consuming a call `(...)`.
|
|
1889
|
+
fn parse_member_expression_no_call(&mut self) -> Result<Expr, String> {
|
|
1890
|
+
let mut expr = self.parse_primary()?;
|
|
1891
|
+
while let Some(kind) = self.peek_kind() {
|
|
1892
|
+
match kind {
|
|
1893
|
+
TokenKind::Dot | TokenKind::OptionalChain => {
|
|
1894
|
+
let optional = kind == TokenKind::OptionalChain;
|
|
1895
|
+
self.advance();
|
|
1896
|
+
let prop_tok = self.expect_ident_or_type_member_name()?;
|
|
1897
|
+
let prop = prop_tok.literal.clone().ok_or("Expected property name")?;
|
|
1898
|
+
let prop_span = Span {
|
|
1899
|
+
start: prop_tok.span.start,
|
|
1900
|
+
end: prop_tok.span.end,
|
|
1901
|
+
};
|
|
1902
|
+
let start = expr.span().start;
|
|
1903
|
+
let end = self.peek().map(|x| x.span.start).unwrap_or(start);
|
|
1904
|
+
expr = Expr::Member {
|
|
1905
|
+
object: Box::new(expr),
|
|
1906
|
+
prop: MemberProp::Name {
|
|
1907
|
+
name: prop,
|
|
1908
|
+
span: prop_span,
|
|
1909
|
+
},
|
|
1910
|
+
optional,
|
|
1911
|
+
span: Span { start, end },
|
|
1912
|
+
};
|
|
1913
|
+
}
|
|
1914
|
+
TokenKind::LBracket => {
|
|
1915
|
+
self.advance();
|
|
1916
|
+
let index = self.parse_expr()?;
|
|
1917
|
+
self.expect(TokenKind::RBracket)?;
|
|
1918
|
+
let start = expr.span().start;
|
|
1919
|
+
let end = self.peek().map(|x| x.span.start).unwrap_or(start);
|
|
1920
|
+
expr = Expr::Index {
|
|
1921
|
+
object: Box::new(expr),
|
|
1922
|
+
index: Box::new(index),
|
|
1923
|
+
optional: false,
|
|
1924
|
+
span: Span { start, end },
|
|
1925
|
+
};
|
|
1926
|
+
}
|
|
1927
|
+
_ => break,
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
Ok(expr)
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
/// ECMAScript `NewExpression`: `new` chains, then member expression without call, optional `(...)`.
|
|
1934
|
+
fn parse_new_expression(&mut self) -> Result<Expr, String> {
|
|
1935
|
+
if matches!(self.peek_kind(), Some(TokenKind::New)) {
|
|
1936
|
+
let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
1937
|
+
self.advance();
|
|
1938
|
+
let callee = Box::new(self.parse_new_expression()?);
|
|
1939
|
+
let args = if matches!(self.peek_kind(), Some(TokenKind::LParen)) {
|
|
1940
|
+
self.advance();
|
|
1941
|
+
let mut args = Vec::new();
|
|
1942
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1943
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
1944
|
+
self.advance();
|
|
1945
|
+
let arg_expr = self.parse_expr()?;
|
|
1946
|
+
args.push(CallArg::Spread(arg_expr));
|
|
1947
|
+
} else {
|
|
1948
|
+
let arg_expr = self.parse_expr()?;
|
|
1949
|
+
args.push(CallArg::Expr(arg_expr));
|
|
1950
|
+
}
|
|
1951
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1952
|
+
self.expect(TokenKind::Comma)?;
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
self.expect(TokenKind::RParen)?;
|
|
1956
|
+
args
|
|
1957
|
+
} else {
|
|
1958
|
+
Vec::new()
|
|
1959
|
+
};
|
|
1960
|
+
let end = self
|
|
1961
|
+
.peek()
|
|
1962
|
+
.map(|x| x.span.start)
|
|
1963
|
+
.unwrap_or(callee.as_ref().span().end);
|
|
1964
|
+
Ok(Expr::New {
|
|
1965
|
+
callee,
|
|
1966
|
+
args,
|
|
1967
|
+
span: Span {
|
|
1968
|
+
start: span_start,
|
|
1969
|
+
end,
|
|
1970
|
+
},
|
|
1971
|
+
})
|
|
1972
|
+
} else {
|
|
1973
|
+
self.parse_member_expression_no_call()
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
fn parse_postfix(&mut self) -> Result<Expr, String> {
|
|
1978
|
+
let mut expr = if matches!(self.peek_kind(), Some(TokenKind::New)) {
|
|
1979
|
+
self.parse_new_expression()?
|
|
1980
|
+
} else {
|
|
1981
|
+
self.parse_primary()?
|
|
1982
|
+
};
|
|
1983
|
+
while let Some(kind) = self.peek_kind() {
|
|
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
|
+
}
|
|
1992
|
+
TokenKind::LParen => {
|
|
1993
|
+
self.advance();
|
|
1994
|
+
let mut args = Vec::new();
|
|
1995
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1996
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
1997
|
+
self.advance();
|
|
1998
|
+
let arg_expr = self.parse_expr()?;
|
|
1999
|
+
args.push(CallArg::Spread(arg_expr));
|
|
2000
|
+
} else {
|
|
2001
|
+
let arg_expr = self.parse_expr()?;
|
|
2002
|
+
args.push(CallArg::Expr(arg_expr));
|
|
2003
|
+
}
|
|
2004
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
2005
|
+
self.expect(TokenKind::Comma)?;
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
self.expect(TokenKind::RParen)?;
|
|
2009
|
+
let start = expr.span().start;
|
|
2010
|
+
let end = self.peek().map(|x| x.span.start).unwrap_or(start);
|
|
2011
|
+
expr = Expr::Call {
|
|
2012
|
+
callee: Box::new(expr),
|
|
2013
|
+
args,
|
|
2014
|
+
span: Span { start, end },
|
|
2015
|
+
};
|
|
2016
|
+
}
|
|
2017
|
+
TokenKind::Dot | TokenKind::OptionalChain => {
|
|
2018
|
+
let optional = kind == TokenKind::OptionalChain;
|
|
2019
|
+
self.advance();
|
|
2020
|
+
let prop_tok = self.expect_ident_or_type_member_name()?;
|
|
2021
|
+
let prop = prop_tok.literal.clone().ok_or("Expected property name")?;
|
|
2022
|
+
let prop_span = Span {
|
|
2023
|
+
start: prop_tok.span.start,
|
|
2024
|
+
end: prop_tok.span.end,
|
|
2025
|
+
};
|
|
2026
|
+
let start = expr.span().start;
|
|
2027
|
+
let end = self.peek().map(|x| x.span.start).unwrap_or(start);
|
|
2028
|
+
expr = Expr::Member {
|
|
2029
|
+
object: Box::new(expr),
|
|
2030
|
+
prop: MemberProp::Name {
|
|
2031
|
+
name: prop,
|
|
2032
|
+
span: prop_span,
|
|
2033
|
+
},
|
|
2034
|
+
optional,
|
|
2035
|
+
span: Span { start, end },
|
|
2036
|
+
};
|
|
2037
|
+
}
|
|
2038
|
+
TokenKind::LBracket => {
|
|
2039
|
+
self.advance();
|
|
2040
|
+
let index = self.parse_expr()?;
|
|
2041
|
+
self.expect(TokenKind::RBracket)?;
|
|
2042
|
+
let start = expr.span().start;
|
|
2043
|
+
let end = self.peek().map(|x| x.span.start).unwrap_or(start);
|
|
2044
|
+
expr = Expr::Index {
|
|
2045
|
+
object: Box::new(expr),
|
|
2046
|
+
index: Box::new(index),
|
|
2047
|
+
optional: false,
|
|
2048
|
+
span: Span { start, end },
|
|
2049
|
+
};
|
|
2050
|
+
}
|
|
2051
|
+
TokenKind::PlusPlus | TokenKind::MinusMinus => {
|
|
2052
|
+
if let Expr::Ident {
|
|
2053
|
+
name,
|
|
2054
|
+
span: ident_span,
|
|
2055
|
+
} = &expr
|
|
2056
|
+
{
|
|
2057
|
+
let name = Arc::clone(name);
|
|
2058
|
+
let is_inc = kind == TokenKind::PlusPlus;
|
|
2059
|
+
let tok = self.advance().ok_or("Unexpected EOF")?;
|
|
2060
|
+
let span = Span {
|
|
2061
|
+
start: ident_span.start,
|
|
2062
|
+
end: tok.span.end,
|
|
2063
|
+
};
|
|
2064
|
+
expr = if is_inc {
|
|
2065
|
+
Expr::PostfixInc { name, span }
|
|
2066
|
+
} else {
|
|
2067
|
+
Expr::PostfixDec { name, span }
|
|
2068
|
+
};
|
|
2069
|
+
} else {
|
|
2070
|
+
break;
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
TokenKind::Question => {
|
|
2074
|
+
// Ternary is parsed in parse_conditional for correct precedence
|
|
2075
|
+
break;
|
|
2076
|
+
}
|
|
2077
|
+
_ => break,
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
Ok(expr)
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
fn parse_primary(&mut self) -> Result<Expr, String> {
|
|
2084
|
+
let t = self.advance().ok_or("Unexpected EOF")?;
|
|
2085
|
+
let span = Span {
|
|
2086
|
+
start: t.span.start,
|
|
2087
|
+
end: t.span.end,
|
|
2088
|
+
};
|
|
2089
|
+
match t.kind {
|
|
2090
|
+
TokenKind::Number => {
|
|
2091
|
+
let s = t.literal.as_ref().ok_or("Expected number")?;
|
|
2092
|
+
let n: f64 = s.parse().map_err(|_| format!("Invalid number: {}", s))?;
|
|
2093
|
+
Ok(Expr::Literal {
|
|
2094
|
+
value: Literal::Number(n),
|
|
2095
|
+
span,
|
|
2096
|
+
})
|
|
2097
|
+
}
|
|
2098
|
+
TokenKind::String => {
|
|
2099
|
+
let s = t.literal.clone().ok_or("Expected string")?;
|
|
2100
|
+
Ok(Expr::Literal {
|
|
2101
|
+
value: Literal::String(s),
|
|
2102
|
+
span,
|
|
2103
|
+
})
|
|
2104
|
+
}
|
|
2105
|
+
TokenKind::True => Ok(Expr::Literal {
|
|
2106
|
+
value: Literal::Bool(true),
|
|
2107
|
+
span,
|
|
2108
|
+
}),
|
|
2109
|
+
TokenKind::False => Ok(Expr::Literal {
|
|
2110
|
+
value: Literal::Bool(false),
|
|
2111
|
+
span,
|
|
2112
|
+
}),
|
|
2113
|
+
TokenKind::Null => Ok(Expr::Literal {
|
|
2114
|
+
value: Literal::Null,
|
|
2115
|
+
span,
|
|
2116
|
+
}),
|
|
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 => {
|
|
2121
|
+
let name = t.literal.clone().ok_or("Expected ident")?;
|
|
2122
|
+
// Check if this is a single-param arrow function: x => ...
|
|
2123
|
+
if matches!(self.peek_kind(), Some(TokenKind::Arrow)) {
|
|
2124
|
+
self.advance(); // consume =>
|
|
2125
|
+
let body = self.parse_arrow_body()?;
|
|
2126
|
+
let end = self.previous_span_end();
|
|
2127
|
+
return Ok(Expr::ArrowFunction {
|
|
2128
|
+
params: vec![FunParam::Simple(TypedParam {
|
|
2129
|
+
name: name.clone(),
|
|
2130
|
+
name_span: span,
|
|
2131
|
+
type_ann: None,
|
|
2132
|
+
default: None,
|
|
2133
|
+
})],
|
|
2134
|
+
body,
|
|
2135
|
+
span: Span {
|
|
2136
|
+
start: span.start,
|
|
2137
|
+
end,
|
|
2138
|
+
},
|
|
2139
|
+
});
|
|
2140
|
+
}
|
|
2141
|
+
Ok(Expr::Ident { name, span })
|
|
2142
|
+
}
|
|
2143
|
+
TokenKind::LParen => {
|
|
2144
|
+
// Check if this is an arrow function: (params) => ...
|
|
2145
|
+
if let Some(arrow_fn) = self.try_parse_arrow_function(&span)? {
|
|
2146
|
+
return Ok(arrow_fn);
|
|
2147
|
+
}
|
|
2148
|
+
// Otherwise it's a grouping expression
|
|
2149
|
+
let expr = self.parse_expr()?;
|
|
2150
|
+
self.expect(TokenKind::RParen)?;
|
|
2151
|
+
Ok(expr)
|
|
2152
|
+
}
|
|
2153
|
+
TokenKind::LBracket => {
|
|
2154
|
+
let mut elements = Vec::new();
|
|
2155
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
|
|
2156
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
2157
|
+
self.advance();
|
|
2158
|
+
let expr = self.parse_expr()?;
|
|
2159
|
+
elements.push(ArrayElement::Spread(expr));
|
|
2160
|
+
} else {
|
|
2161
|
+
let expr = self.parse_expr()?;
|
|
2162
|
+
elements.push(ArrayElement::Expr(expr));
|
|
2163
|
+
}
|
|
2164
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
|
|
2165
|
+
self.expect(TokenKind::Comma)?;
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
self.expect(TokenKind::RBracket)?;
|
|
2169
|
+
Ok(Expr::Array {
|
|
2170
|
+
elements,
|
|
2171
|
+
span: Span {
|
|
2172
|
+
start: span.start,
|
|
2173
|
+
end: self.peek().map(|x| x.span.end).unwrap_or(span.end),
|
|
2174
|
+
},
|
|
2175
|
+
})
|
|
2176
|
+
}
|
|
2177
|
+
TokenKind::Lt => {
|
|
2178
|
+
// JSX: <Tag or <>
|
|
2179
|
+
match self.peek_kind() {
|
|
2180
|
+
Some(TokenKind::Ident) => self.parse_jsx_element(span.start),
|
|
2181
|
+
Some(TokenKind::Gt) => self.parse_jsx_fragment(span.start),
|
|
2182
|
+
_ => Err(format!(
|
|
2183
|
+
"Invalid JSX: expected tag name or <> after <, got {:?}",
|
|
2184
|
+
self.peek_kind()
|
|
2185
|
+
)),
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
TokenKind::LBrace => {
|
|
2189
|
+
let mut props = Vec::new();
|
|
2190
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
2191
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
2192
|
+
self.advance();
|
|
2193
|
+
let expr = self.parse_expr()?;
|
|
2194
|
+
props.push(ObjectProp::Spread(expr));
|
|
2195
|
+
} else {
|
|
2196
|
+
let key_tok = self.advance().ok_or("Expected object key")?;
|
|
2197
|
+
let (key, key_span, is_ident_key) = match key_tok.kind {
|
|
2198
|
+
TokenKind::Ident | TokenKind::Type | TokenKind::Declare => {
|
|
2199
|
+
let k = key_tok.literal.clone().ok_or("Expected key")?;
|
|
2200
|
+
let sp = Span {
|
|
2201
|
+
start: key_tok.span.start,
|
|
2202
|
+
end: key_tok.span.end,
|
|
2203
|
+
};
|
|
2204
|
+
(k, sp, true)
|
|
2205
|
+
}
|
|
2206
|
+
TokenKind::String => {
|
|
2207
|
+
let k = key_tok.literal.clone().ok_or("Expected string key")?;
|
|
2208
|
+
let sp = Span {
|
|
2209
|
+
start: key_tok.span.start,
|
|
2210
|
+
end: key_tok.span.end,
|
|
2211
|
+
};
|
|
2212
|
+
(k, sp, false)
|
|
2213
|
+
}
|
|
2214
|
+
_ => {
|
|
2215
|
+
return Err(format!(
|
|
2216
|
+
"Expected object key (ident or string), got {:?}",
|
|
2217
|
+
key_tok.kind
|
|
2218
|
+
))
|
|
2219
|
+
}
|
|
2220
|
+
};
|
|
2221
|
+
let value = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
2222
|
+
self.expect(TokenKind::Colon)?;
|
|
2223
|
+
self.parse_expr()?
|
|
2224
|
+
} else {
|
|
2225
|
+
// ES6 shorthand: { key } => { key: key } (ident only, not string keys)
|
|
2226
|
+
if is_ident_key {
|
|
2227
|
+
Expr::Ident {
|
|
2228
|
+
name: key.clone(),
|
|
2229
|
+
span: key_span,
|
|
2230
|
+
}
|
|
2231
|
+
} else {
|
|
2232
|
+
return Err("String key in object literal requires explicit value (key: value)".to_string());
|
|
2233
|
+
}
|
|
2234
|
+
};
|
|
2235
|
+
props.push(ObjectProp::KeyValue(key, value));
|
|
2236
|
+
}
|
|
2237
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
2238
|
+
self.expect(TokenKind::Comma)?;
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
self.expect(TokenKind::RBrace)?;
|
|
2242
|
+
Ok(Expr::Object {
|
|
2243
|
+
props,
|
|
2244
|
+
span: Span {
|
|
2245
|
+
start: span.start,
|
|
2246
|
+
end: self.peek().map(|x| x.span.end).unwrap_or(span.end),
|
|
2247
|
+
},
|
|
2248
|
+
})
|
|
2249
|
+
}
|
|
2250
|
+
TokenKind::TemplateNoSub => {
|
|
2251
|
+
// Simple template literal without interpolation
|
|
2252
|
+
Ok(Expr::TemplateLiteral {
|
|
2253
|
+
quasis: vec![t.literal.clone().unwrap_or_default()],
|
|
2254
|
+
exprs: vec![],
|
|
2255
|
+
span,
|
|
2256
|
+
})
|
|
2257
|
+
}
|
|
2258
|
+
TokenKind::TemplateHead => {
|
|
2259
|
+
// Template literal with interpolation: `text${
|
|
2260
|
+
let mut quasis = vec![t.literal.clone().unwrap_or_default()];
|
|
2261
|
+
let mut exprs = Vec::new();
|
|
2262
|
+
|
|
2263
|
+
loop {
|
|
2264
|
+
// Parse the expression inside ${}
|
|
2265
|
+
let expr = self.parse_expr()?;
|
|
2266
|
+
exprs.push(expr);
|
|
2267
|
+
|
|
2268
|
+
// Next token should be TemplateMiddle or TemplateTail
|
|
2269
|
+
let next = self.advance().ok_or("Unexpected EOF in template literal")?;
|
|
2270
|
+
match next.kind {
|
|
2271
|
+
TokenKind::TemplateTail => {
|
|
2272
|
+
quasis.push(next.literal.clone().unwrap_or_default());
|
|
2273
|
+
let end = self.previous_span_end();
|
|
2274
|
+
return Ok(Expr::TemplateLiteral {
|
|
2275
|
+
quasis,
|
|
2276
|
+
exprs,
|
|
2277
|
+
span: Span {
|
|
2278
|
+
start: span.start,
|
|
2279
|
+
end,
|
|
2280
|
+
},
|
|
2281
|
+
});
|
|
2282
|
+
}
|
|
2283
|
+
TokenKind::TemplateMiddle => {
|
|
2284
|
+
quasis.push(next.literal.clone().unwrap_or_default());
|
|
2285
|
+
// Continue parsing more expressions
|
|
2286
|
+
}
|
|
2287
|
+
_ => {
|
|
2288
|
+
return Err(format!(
|
|
2289
|
+
"Expected template continuation, got {:?}",
|
|
2290
|
+
next.kind
|
|
2291
|
+
))
|
|
2292
|
+
}
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
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)),
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
/// Try to parse an arrow function starting with '(' already consumed.
|
|
2304
|
+
/// Returns Some(Expr) if successful, None if it's not an arrow function.
|
|
2305
|
+
fn try_parse_arrow_function(&mut self, start_span: &Span) -> Result<Option<Expr>, String> {
|
|
2306
|
+
// Save position for backtracking
|
|
2307
|
+
let saved_pos = self.pos;
|
|
2308
|
+
|
|
2309
|
+
// Try to parse as arrow function params
|
|
2310
|
+
let mut params = Vec::new();
|
|
2311
|
+
let mut is_arrow = false;
|
|
2312
|
+
|
|
2313
|
+
// Check for empty params: () => ...
|
|
2314
|
+
if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
2315
|
+
self.advance(); // consume )
|
|
2316
|
+
if matches!(self.peek_kind(), Some(TokenKind::Arrow)) {
|
|
2317
|
+
self.advance(); // consume =>
|
|
2318
|
+
is_arrow = true;
|
|
2319
|
+
}
|
|
2320
|
+
} else {
|
|
2321
|
+
// Try to parse params: (x, y), ({ a }), ([a, b]), with optional types/defaults
|
|
2322
|
+
let mut params_ok = true;
|
|
2323
|
+
loop {
|
|
2324
|
+
if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
2325
|
+
break;
|
|
2326
|
+
}
|
|
2327
|
+
match self.parse_fun_param() {
|
|
2328
|
+
Ok(param) => params.push(param),
|
|
2329
|
+
Err(_) => {
|
|
2330
|
+
params_ok = false;
|
|
2331
|
+
break;
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
2335
|
+
self.advance();
|
|
2336
|
+
} else {
|
|
2337
|
+
break;
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
if params_ok && matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
2341
|
+
self.advance(); // consume )
|
|
2342
|
+
if matches!(self.peek_kind(), Some(TokenKind::Arrow)) {
|
|
2343
|
+
self.advance(); // consume =>
|
|
2344
|
+
is_arrow = true;
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
|
|
2349
|
+
if !is_arrow {
|
|
2350
|
+
// Backtrack - it's not an arrow function
|
|
2351
|
+
self.pos = saved_pos;
|
|
2352
|
+
return Ok(None);
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
let body = self.parse_arrow_body()?;
|
|
2356
|
+
let end = self.previous_span_end();
|
|
2357
|
+
|
|
2358
|
+
Ok(Some(Expr::ArrowFunction {
|
|
2359
|
+
params,
|
|
2360
|
+
body,
|
|
2361
|
+
span: Span {
|
|
2362
|
+
start: start_span.start,
|
|
2363
|
+
end,
|
|
2364
|
+
},
|
|
2365
|
+
}))
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2368
|
+
/// Parse the body of an arrow function (either expression or block)
|
|
2369
|
+
fn parse_arrow_body(&mut self) -> Result<ArrowBody, String> {
|
|
2370
|
+
if matches!(self.peek_kind(), Some(TokenKind::LBrace)) {
|
|
2371
|
+
// Block body
|
|
2372
|
+
let block = self.parse_block()?;
|
|
2373
|
+
Ok(ArrowBody::Block(Box::new(block)))
|
|
2374
|
+
} else {
|
|
2375
|
+
// Expression body
|
|
2376
|
+
let expr = self.parse_expr()?;
|
|
2377
|
+
Ok(ArrowBody::Expr(Box::new(expr)))
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
fn previous_span_end(&self) -> (usize, usize) {
|
|
2382
|
+
if self.pos > 0 && self.pos <= self.tokens.len() {
|
|
2383
|
+
self.tokens[self.pos - 1].span.end
|
|
2384
|
+
} else {
|
|
2385
|
+
(1, 1)
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
/// Parse JSX element: <Tag props>children</Tag> or <Tag props />
|
|
2390
|
+
/// Caller has already consumed <.
|
|
2391
|
+
fn parse_jsx_element(&mut self, start: (usize, usize)) -> Result<Expr, String> {
|
|
2392
|
+
let tag_tok = self.expect(TokenKind::Ident)?;
|
|
2393
|
+
let tag = tag_tok.literal.clone().ok_or("Expected tag name")?;
|
|
2394
|
+
|
|
2395
|
+
let mut props = Vec::new();
|
|
2396
|
+
loop {
|
|
2397
|
+
match self.peek_kind() {
|
|
2398
|
+
Some(TokenKind::Slash) => {
|
|
2399
|
+
// Self-closing: />
|
|
2400
|
+
self.advance();
|
|
2401
|
+
self.expect(TokenKind::Gt)?;
|
|
2402
|
+
let end = self.previous_span_end();
|
|
2403
|
+
return Ok(Expr::JsxElement {
|
|
2404
|
+
tag,
|
|
2405
|
+
props,
|
|
2406
|
+
children: vec![],
|
|
2407
|
+
span: Span { start, end },
|
|
2408
|
+
});
|
|
2409
|
+
}
|
|
2410
|
+
Some(TokenKind::Gt) => break,
|
|
2411
|
+
Some(TokenKind::Spread) => {
|
|
2412
|
+
self.advance(); // ...
|
|
2413
|
+
let expr = self.parse_expr()?;
|
|
2414
|
+
self.expect(TokenKind::RBrace)?; // }
|
|
2415
|
+
props.push(JsxProp::Spread(expr));
|
|
2416
|
+
}
|
|
2417
|
+
// `type` is `TokenKind::Type` but valid as a JSX attr name; see docs/js-emit-philosophy.md.
|
|
2418
|
+
Some(TokenKind::Ident) | Some(TokenKind::Type) => {
|
|
2419
|
+
let name_tok = self.advance().unwrap();
|
|
2420
|
+
let name = name_tok.literal.clone().ok_or("Expected attr name")?;
|
|
2421
|
+
if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
2422
|
+
self.advance(); // =
|
|
2423
|
+
let value = if matches!(self.peek_kind(), Some(TokenKind::LBrace)) {
|
|
2424
|
+
self.advance(); // {
|
|
2425
|
+
let expr = self.parse_expr()?;
|
|
2426
|
+
self.expect(TokenKind::RBrace)?; // }
|
|
2427
|
+
JsxAttrValue::Expr(expr)
|
|
2428
|
+
} else {
|
|
2429
|
+
let s = self
|
|
2430
|
+
.expect(TokenKind::String)?
|
|
2431
|
+
.literal
|
|
2432
|
+
.clone()
|
|
2433
|
+
.ok_or("Expected string")?;
|
|
2434
|
+
JsxAttrValue::String(s)
|
|
2435
|
+
};
|
|
2436
|
+
props.push(JsxProp::Attr { name, value });
|
|
2437
|
+
} else {
|
|
2438
|
+
props.push(JsxProp::Attr {
|
|
2439
|
+
name,
|
|
2440
|
+
value: JsxAttrValue::ImplicitTrue,
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
_ => {
|
|
2445
|
+
return Err(format!(
|
|
2446
|
+
"Unexpected token in JSX props: {:?}",
|
|
2447
|
+
self.peek_kind()
|
|
2448
|
+
))
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
self.advance(); // consume >
|
|
2453
|
+
|
|
2454
|
+
let children = self.parse_jsx_children(&tag)?;
|
|
2455
|
+
let end = self.previous_span_end();
|
|
2456
|
+
Ok(Expr::JsxElement {
|
|
2457
|
+
tag,
|
|
2458
|
+
props,
|
|
2459
|
+
children,
|
|
2460
|
+
span: Span { start, end },
|
|
2461
|
+
})
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
fn token_as_jsx_text(kind: TokenKind) -> Option<&'static str> {
|
|
2465
|
+
use TokenKind::*;
|
|
2466
|
+
match kind {
|
|
2467
|
+
Not => Some("!"),
|
|
2468
|
+
Question => Some("?"),
|
|
2469
|
+
Dot => Some("."),
|
|
2470
|
+
Comma => Some(","),
|
|
2471
|
+
Colon => Some(":"),
|
|
2472
|
+
Semicolon => Some(";"),
|
|
2473
|
+
Plus => Some("+"),
|
|
2474
|
+
Minus => Some("-"),
|
|
2475
|
+
Star => Some("*"),
|
|
2476
|
+
Slash => Some("/"),
|
|
2477
|
+
Percent => Some("%"),
|
|
2478
|
+
Eq | Assign => Some("="),
|
|
2479
|
+
Gt => Some(">"),
|
|
2480
|
+
Le => Some("<="),
|
|
2481
|
+
Ge => Some(">="),
|
|
2482
|
+
Ne => Some("!="),
|
|
2483
|
+
StrictEq => Some("==="),
|
|
2484
|
+
StrictNe => Some("!=="),
|
|
2485
|
+
BitAnd => Some("&"),
|
|
2486
|
+
BitOr => Some("|"),
|
|
2487
|
+
BitXor => Some("^"),
|
|
2488
|
+
BitNot => Some("~"),
|
|
2489
|
+
And => Some("&&"),
|
|
2490
|
+
Or => Some("||"),
|
|
2491
|
+
LParen => Some("("),
|
|
2492
|
+
RParen => Some(")"),
|
|
2493
|
+
LBracket => Some("["),
|
|
2494
|
+
RBracket => Some("]"),
|
|
2495
|
+
PlusPlus => Some("++"),
|
|
2496
|
+
MinusMinus => Some("--"),
|
|
2497
|
+
StarStar => Some("**"),
|
|
2498
|
+
Arrow => Some("=>"),
|
|
2499
|
+
OptionalChain => Some("?."),
|
|
2500
|
+
NullishCoalesce => Some("??"),
|
|
2501
|
+
Shl => Some("<<"),
|
|
2502
|
+
Shr => Some(">>"),
|
|
2503
|
+
_ => None,
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
/// Merge text. Add space between words (Ident/Number/String); no space before/after punctuation.
|
|
2508
|
+
fn push_or_merge_text(&self, children: &mut Vec<JsxChild>, s: Arc<str>, is_punctuation: bool) {
|
|
2509
|
+
if let Some(JsxChild::Text(prev)) = children.last() {
|
|
2510
|
+
let sep = if is_punctuation { "" } else { " " };
|
|
2511
|
+
let merged = format!("{}{}{}", prev.as_ref(), sep, s.as_ref());
|
|
2512
|
+
let last = children.len() - 1;
|
|
2513
|
+
children[last] = JsxChild::Text(Arc::from(merged.as_str()));
|
|
2514
|
+
} else {
|
|
2515
|
+
children.push(JsxChild::Text(s));
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
/// Parse JSX children until </Tag> or </>
|
|
2520
|
+
fn parse_jsx_children(&mut self, close_tag: &str) -> Result<Vec<JsxChild>, String> {
|
|
2521
|
+
let mut children = Vec::new();
|
|
2522
|
+
loop {
|
|
2523
|
+
match self.peek_kind() {
|
|
2524
|
+
None => return Err("Unexpected EOF in JSX".to_string()),
|
|
2525
|
+
Some(TokenKind::Lt) => {
|
|
2526
|
+
let next = self.tokens.get(self.pos + 1);
|
|
2527
|
+
if let Some(t) = next {
|
|
2528
|
+
if t.kind == TokenKind::Slash {
|
|
2529
|
+
// </ closing tag
|
|
2530
|
+
self.advance(); // <
|
|
2531
|
+
self.advance(); // /
|
|
2532
|
+
let name = self
|
|
2533
|
+
.expect(TokenKind::Ident)?
|
|
2534
|
+
.literal
|
|
2535
|
+
.clone()
|
|
2536
|
+
.ok_or("Expected tag name")?;
|
|
2537
|
+
if name.as_ref() != close_tag {
|
|
2538
|
+
return Err(format!(
|
|
2539
|
+
"Mismatched JSX tag: expected </{}> got </{}>",
|
|
2540
|
+
close_tag, name
|
|
2541
|
+
));
|
|
2542
|
+
}
|
|
2543
|
+
self.expect(TokenKind::Gt)?; // >
|
|
2544
|
+
return Ok(children);
|
|
2545
|
+
}
|
|
2546
|
+
if t.kind == TokenKind::Gt {
|
|
2547
|
+
return Err("Unexpected <> in JSX children".to_string());
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
// <Tag - nested element
|
|
2551
|
+
let nested_start = self.peek().unwrap().span.start;
|
|
2552
|
+
self.advance(); // <
|
|
2553
|
+
let elem = self.parse_jsx_element(nested_start)?;
|
|
2554
|
+
children.push(JsxChild::Expr(elem));
|
|
2555
|
+
}
|
|
2556
|
+
Some(TokenKind::LBrace) => {
|
|
2557
|
+
self.advance(); // {
|
|
2558
|
+
let expr = self.parse_expr()?;
|
|
2559
|
+
self.expect(TokenKind::RBrace)?; // }
|
|
2560
|
+
children.push(JsxChild::Expr(expr));
|
|
2561
|
+
}
|
|
2562
|
+
Some(TokenKind::JsxText) => {
|
|
2563
|
+
let t = self.advance().unwrap();
|
|
2564
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2565
|
+
if !s.is_empty() {
|
|
2566
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
Some(TokenKind::String) => {
|
|
2570
|
+
let t = self.advance().unwrap();
|
|
2571
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2572
|
+
if !s.is_empty() {
|
|
2573
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
Some(TokenKind::Number) => {
|
|
2577
|
+
let t = self.advance().unwrap();
|
|
2578
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2579
|
+
if !s.is_empty() {
|
|
2580
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2583
|
+
Some(TokenKind::Ident) => {
|
|
2584
|
+
let t = self.advance().unwrap();
|
|
2585
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2586
|
+
if !s.is_empty() {
|
|
2587
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
Some(k) => {
|
|
2591
|
+
if let Some(s) = Self::token_as_jsx_text(k) {
|
|
2592
|
+
self.advance();
|
|
2593
|
+
self.push_or_merge_text(&mut children, Arc::from(s), true);
|
|
2594
|
+
} else {
|
|
2595
|
+
return Err(format!("Unexpected token in JSX children: {:?}", k));
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
}
|
|
2601
|
+
|
|
2602
|
+
fn parse_jsx_fragment(&mut self, start: (usize, usize)) -> Result<Expr, String> {
|
|
2603
|
+
self.advance(); // consume >
|
|
2604
|
+
let mut children = Vec::new();
|
|
2605
|
+
loop {
|
|
2606
|
+
match self.peek_kind() {
|
|
2607
|
+
None => return Err("Unexpected EOF in JSX fragment".to_string()),
|
|
2608
|
+
Some(TokenKind::Lt) => {
|
|
2609
|
+
let next = self.tokens.get(self.pos + 1);
|
|
2610
|
+
if let Some(t) = next {
|
|
2611
|
+
if t.kind == TokenKind::Slash {
|
|
2612
|
+
// </
|
|
2613
|
+
let next2 = self.tokens.get(self.pos + 2);
|
|
2614
|
+
if let Some(t2) = next2 {
|
|
2615
|
+
if t2.kind == TokenKind::Gt {
|
|
2616
|
+
// </>
|
|
2617
|
+
self.advance();
|
|
2618
|
+
self.advance();
|
|
2619
|
+
self.advance();
|
|
2620
|
+
let end = self.previous_span_end();
|
|
2621
|
+
return Ok(Expr::JsxFragment {
|
|
2622
|
+
children,
|
|
2623
|
+
span: Span { start, end },
|
|
2624
|
+
});
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
return Err("Expected </> to close fragment".to_string());
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
let nested_start = self.peek().unwrap().span.start;
|
|
2631
|
+
self.advance(); // <
|
|
2632
|
+
let elem = self.parse_jsx_element(nested_start)?;
|
|
2633
|
+
children.push(JsxChild::Expr(elem));
|
|
2634
|
+
}
|
|
2635
|
+
Some(TokenKind::LBrace) => {
|
|
2636
|
+
self.advance();
|
|
2637
|
+
let expr = self.parse_expr()?;
|
|
2638
|
+
self.expect(TokenKind::RBrace)?;
|
|
2639
|
+
children.push(JsxChild::Expr(expr));
|
|
2640
|
+
}
|
|
2641
|
+
Some(TokenKind::JsxText) => {
|
|
2642
|
+
let t = self.advance().unwrap();
|
|
2643
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2644
|
+
if !s.is_empty() {
|
|
2645
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
Some(TokenKind::String) => {
|
|
2649
|
+
let t = self.advance().unwrap();
|
|
2650
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2651
|
+
if !s.is_empty() {
|
|
2652
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
Some(TokenKind::Number) => {
|
|
2656
|
+
let t = self.advance().unwrap();
|
|
2657
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2658
|
+
if !s.is_empty() {
|
|
2659
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
Some(TokenKind::Ident) => {
|
|
2663
|
+
let t = self.advance().unwrap();
|
|
2664
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2665
|
+
if !s.is_empty() {
|
|
2666
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
Some(k) => {
|
|
2670
|
+
if let Some(s) = Self::token_as_jsx_text(k) {
|
|
2671
|
+
self.advance();
|
|
2672
|
+
self.push_or_merge_text(&mut children, Arc::from(s), true);
|
|
2673
|
+
} else {
|
|
2674
|
+
return Err(format!("Unexpected token in JSX fragment: {:?}", k));
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
// Helper to get span from Expr. Uses trait so ExprSpan is referenced.
|
|
2683
|
+
trait ExprSpan {
|
|
2684
|
+
fn span(&self) -> Span;
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
#[inline(always)]
|
|
2688
|
+
fn expr_span(e: &impl ExprSpan) -> Span {
|
|
2689
|
+
e.span()
|
|
2690
|
+
}
|
|
2691
|
+
|
|
2692
|
+
impl ExprSpan for Expr {
|
|
2693
|
+
fn span(&self) -> Span {
|
|
2694
|
+
match self {
|
|
2695
|
+
Expr::Literal { span, .. } => *span,
|
|
2696
|
+
Expr::Ident { span, .. } => *span,
|
|
2697
|
+
Expr::Binary { span, .. } => *span,
|
|
2698
|
+
Expr::Unary { span, .. } => *span,
|
|
2699
|
+
Expr::Call { span, .. } => *span,
|
|
2700
|
+
Expr::New { span, .. } => *span,
|
|
2701
|
+
Expr::Member { span, .. } => *span,
|
|
2702
|
+
Expr::Index { span, .. } => *span,
|
|
2703
|
+
Expr::Conditional { span, .. } => *span,
|
|
2704
|
+
Expr::NullishCoalesce { span, .. } => *span,
|
|
2705
|
+
Expr::Array { span, .. } => *span,
|
|
2706
|
+
Expr::Object { span, .. } => *span,
|
|
2707
|
+
Expr::Assign { span, .. } => *span,
|
|
2708
|
+
Expr::TypeOf { span, .. } => *span,
|
|
2709
|
+
Expr::Delete { span, .. } => *span,
|
|
2710
|
+
Expr::PostfixInc { span, .. } => *span,
|
|
2711
|
+
Expr::PostfixDec { span, .. } => *span,
|
|
2712
|
+
Expr::PrefixInc { span, .. } => *span,
|
|
2713
|
+
Expr::PrefixDec { span, .. } => *span,
|
|
2714
|
+
Expr::CompoundAssign { span, .. } => *span,
|
|
2715
|
+
Expr::LogicalAssign { span, .. } => *span,
|
|
2716
|
+
Expr::MemberAssign { span, .. } => *span,
|
|
2717
|
+
Expr::IndexAssign { span, .. } => *span,
|
|
2718
|
+
Expr::ArrowFunction { span, .. } => *span,
|
|
2719
|
+
Expr::TemplateLiteral { span, .. } => *span,
|
|
2720
|
+
Expr::Await { span, .. } => *span,
|
|
2721
|
+
Expr::JsxElement { span, .. } => *span,
|
|
2722
|
+
Expr::JsxFragment { span, .. } => *span,
|
|
2723
|
+
Expr::NativeModuleLoad { span, .. } => *span,
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
}
|