@tishlang/tish-format 1.0.12 → 1.0.13
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 +49 -0
- package/LICENSE +13 -0
- package/README.md +138 -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 +610 -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 +54 -0
- package/crates/tish/src/cargo_native_registry.rs +32 -0
- package/crates/tish/src/cli_help.rs +565 -0
- package/crates/tish/src/main.rs +781 -0
- package/crates/tish/src/repl_completion.rs +200 -0
- package/crates/tish/tests/cargo_example_compile.rs +67 -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/integration_test.rs +1095 -0
- package/crates/tish/tests/run_optimize_stdout_parity.rs +50 -0
- package/crates/tish/tests/shortcircuit.rs +65 -0
- package/crates/tish_ast/Cargo.toml +9 -0
- package/crates/tish_ast/src/ast.rs +620 -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 +20 -0
- package/crates/tish_builtins/src/array.rs +441 -0
- package/crates/tish_builtins/src/construct.rs +159 -0
- package/crates/tish_builtins/src/globals.rs +213 -0
- package/crates/tish_builtins/src/helpers.rs +35 -0
- package/crates/tish_builtins/src/lib.rs +16 -0
- package/crates/tish_builtins/src/math.rs +89 -0
- package/crates/tish_builtins/src/object.rs +36 -0
- package/crates/tish_builtins/src/string.rs +647 -0
- package/crates/tish_builtins/src/symbol.rs +83 -0
- package/crates/tish_bytecode/Cargo.toml +17 -0
- package/crates/tish_bytecode/src/chunk.rs +96 -0
- package/crates/tish_bytecode/src/compiler.rs +1760 -0
- package/crates/tish_bytecode/src/encoding.rs +100 -0
- package/crates/tish_bytecode/src/lib.rs +19 -0
- package/crates/tish_bytecode/src/opcode.rs +142 -0
- package/crates/tish_bytecode/src/peephole.rs +189 -0
- package/crates/tish_bytecode/src/serialize.rs +163 -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 +26 -0
- package/crates/tish_compile/src/codegen.rs +5332 -0
- package/crates/tish_compile/src/infer.rs +292 -0
- package/crates/tish_compile/src/lib.rs +164 -0
- package/crates/tish_compile/src/resolve.rs +1388 -0
- package/crates/tish_compile/src/types.rs +501 -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 +871 -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 +350 -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 +26 -0
- package/crates/tish_core/src/console_style.rs +160 -0
- package/crates/tish_core/src/json.rs +387 -0
- package/crates/tish_core/src/lib.rs +17 -0
- package/crates/tish_core/src/macros.rs +36 -0
- package/crates/tish_core/src/uri.rs +118 -0
- package/crates/tish_core/src/value.rs +696 -0
- package/crates/tish_core/src/vmref.rs +178 -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 +117 -0
- package/crates/tish_cranelift/src/lower.rs +85 -0
- package/crates/tish_cranelift_runtime/Cargo.toml +25 -0
- package/crates/tish_cranelift_runtime/src/lib.rs +45 -0
- package/crates/tish_eval/Cargo.toml +45 -0
- package/crates/tish_eval/src/eval.rs +3717 -0
- package/crates/tish_eval/src/http.rs +188 -0
- package/crates/tish_eval/src/lib.rs +99 -0
- package/crates/tish_eval/src/natives.rs +399 -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 +318 -0
- package/crates/tish_eval/src/value_convert.rs +111 -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 +2101 -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 +716 -0
- package/crates/tish_lexer/src/token.rs +163 -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 +289 -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 +562 -0
- package/crates/tish_lsp/src/main.rs +1046 -0
- package/crates/tish_native/Cargo.toml +16 -0
- package/crates/tish_native/src/build.rs +427 -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 +943 -0
- package/crates/tish_parser/Cargo.toml +11 -0
- package/crates/tish_parser/src/lib.rs +332 -0
- package/crates/tish_parser/src/parser.rs +2304 -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 +3561 -0
- package/crates/tish_resolve/src/pos.rs +141 -0
- package/crates/tish_runtime/Cargo.toml +96 -0
- package/crates/tish_runtime/src/http.rs +1298 -0
- package/crates/tish_runtime/src/http_fetch.rs +471 -0
- package/crates/tish_runtime/src/http_hyper.rs +418 -0
- package/crates/tish_runtime/src/http_prefork.rs +189 -0
- package/crates/tish_runtime/src/lib.rs +1192 -0
- package/crates/tish_runtime/src/native_promise.rs +15 -0
- package/crates/tish_runtime/src/promise.rs +248 -0
- package/crates/tish_runtime/src/promise_io.rs +38 -0
- package/crates/tish_runtime/src/timers.rs +166 -0
- package/crates/tish_runtime/src/ws.rs +761 -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 +682 -0
- package/crates/tish_ui/src/lib.rs +20 -0
- package/crates/tish_ui/src/runtime/hooks.rs +569 -0
- package/crates/tish_ui/src/runtime/mod.rs +180 -0
- package/crates/tish_vm/Cargo.toml +47 -0
- package/crates/tish_vm/src/lib.rs +39 -0
- package/crates/tish_vm/src/vm.rs +2192 -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 +424 -0
- package/crates/tish_wasm_runtime/Cargo.toml +37 -0
- package/crates/tish_wasm_runtime/src/gpu.rs +413 -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 +263 -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 +268 -0
- package/package.json +1 -1
- package/platform/darwin-arm64/tish-fmt +0 -0
|
@@ -0,0 +1,2304 @@
|
|
|
1
|
+
//! Recursive descent parser for Tish.
|
|
2
|
+
|
|
3
|
+
use std::sync::Arc;
|
|
4
|
+
|
|
5
|
+
/// Macro to generate single-operator binary parsing functions.
|
|
6
|
+
/// Reduces boilerplate for Or, And, BitOr, BitXor, BitAnd parsers.
|
|
7
|
+
macro_rules! binary_single_op {
|
|
8
|
+
($name:ident, $next:ident, $token:ident, $op:expr) => {
|
|
9
|
+
fn $name(&mut self) -> Result<Expr, String> {
|
|
10
|
+
let mut left = self.$next()?;
|
|
11
|
+
while matches!(self.peek_kind(), Some(TokenKind::$token)) {
|
|
12
|
+
self.advance();
|
|
13
|
+
let right = self.$next()?;
|
|
14
|
+
let start = expr_span(&left).start;
|
|
15
|
+
let end = expr_span(&right).end;
|
|
16
|
+
left = Expr::Binary {
|
|
17
|
+
left: Box::new(left),
|
|
18
|
+
op: $op,
|
|
19
|
+
right: Box::new(right),
|
|
20
|
+
span: Span { start, end },
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
Ok(left)
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// Macro for multi-operator binary parsing with a match block.
|
|
29
|
+
macro_rules! binary_multi_op {
|
|
30
|
+
($name:ident, $next:ident, $( $token:ident => $op:expr ),+ $(,)?) => {
|
|
31
|
+
fn $name(&mut self) -> Result<Expr, String> {
|
|
32
|
+
let mut left = self.$next()?;
|
|
33
|
+
loop {
|
|
34
|
+
let op = match self.peek_kind() {
|
|
35
|
+
$( Some(TokenKind::$token) => $op, )+
|
|
36
|
+
_ => break,
|
|
37
|
+
};
|
|
38
|
+
self.advance();
|
|
39
|
+
let right = self.$next()?;
|
|
40
|
+
let start = expr_span(&left).start;
|
|
41
|
+
let end = expr_span(&right).end;
|
|
42
|
+
left = Expr::Binary {
|
|
43
|
+
left: Box::new(left),
|
|
44
|
+
op,
|
|
45
|
+
right: Box::new(right),
|
|
46
|
+
span: Span { start, end },
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
Ok(left)
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
use tishlang_ast::{
|
|
55
|
+
ArrayElement, ArrowBody, BinOp, CallArg, CompoundOp, DestructElement, DestructPattern,
|
|
56
|
+
DestructProp, ExportDeclaration, Expr, FunParam, ImportSpecifier, JsxAttrValue, JsxChild,
|
|
57
|
+
JsxProp, Literal, LogicalAssignOp, MemberProp, ObjectProp, Program, Span, Statement,
|
|
58
|
+
TypeAnnotation, TypedParam, UnaryOp,
|
|
59
|
+
};
|
|
60
|
+
use tishlang_lexer::{Token, TokenKind};
|
|
61
|
+
|
|
62
|
+
pub struct Parser<'a> {
|
|
63
|
+
tokens: &'a [Token],
|
|
64
|
+
pos: usize,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
impl<'a> Parser<'a> {
|
|
68
|
+
pub fn new(tokens: &'a [Token]) -> Self {
|
|
69
|
+
Self { tokens, pos: 0 }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fn peek(&self) -> Option<&Token> {
|
|
73
|
+
self.tokens.get(self.pos)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
fn peek_kind(&self) -> Option<TokenKind> {
|
|
77
|
+
self.peek().map(|t| t.kind)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fn advance(&mut self) -> Option<&Token> {
|
|
81
|
+
let t = self.tokens.get(self.pos);
|
|
82
|
+
if t.is_some() {
|
|
83
|
+
self.pos += 1;
|
|
84
|
+
}
|
|
85
|
+
t
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
fn expect(&mut self, kind: TokenKind) -> Result<&Token, String> {
|
|
89
|
+
let t = self
|
|
90
|
+
.advance()
|
|
91
|
+
.ok_or_else(|| format!("Expected {:?}, got EOF", kind))?;
|
|
92
|
+
if t.kind == kind {
|
|
93
|
+
Ok(t)
|
|
94
|
+
} else {
|
|
95
|
+
Err(format!(
|
|
96
|
+
"Expected {:?}, got {:?} at {:?}",
|
|
97
|
+
kind, t.kind, t.span
|
|
98
|
+
))
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/// After `.` / `?.`, allow `type` as a member name (`TokenKind::Type`); see `docs/js-emit-philosophy.md`.
|
|
103
|
+
fn expect_ident_or_type_member_name(&mut self) -> Result<&Token, String> {
|
|
104
|
+
match self.peek_kind() {
|
|
105
|
+
Some(TokenKind::Ident) => self.expect(TokenKind::Ident),
|
|
106
|
+
Some(TokenKind::Type) => self.expect(TokenKind::Type),
|
|
107
|
+
other => Err(format!(
|
|
108
|
+
"Expected property name after `.` or `?.`, got {:?}",
|
|
109
|
+
other
|
|
110
|
+
)),
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
fn span_end(&self, start: (usize, usize)) -> Span {
|
|
115
|
+
let end = self.peek().map(|t| t.span.start).unwrap_or(start);
|
|
116
|
+
Span { start, end }
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
pub fn parse_program(&mut self) -> Result<Program, String> {
|
|
120
|
+
let mut statements = Vec::with_capacity(8);
|
|
121
|
+
while self.peek_kind().is_some() {
|
|
122
|
+
if matches!(self.peek_kind(), Some(TokenKind::Dedent)) {
|
|
123
|
+
self.advance();
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
statements.push(self.parse_statement()?);
|
|
127
|
+
}
|
|
128
|
+
Ok(Program { statements })
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
fn parse_statement(&mut self) -> Result<Statement, String> {
|
|
132
|
+
let kind = self.peek_kind().ok_or("Unexpected EOF")?;
|
|
133
|
+
let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
134
|
+
|
|
135
|
+
let stmt = match kind {
|
|
136
|
+
TokenKind::LBrace | TokenKind::Indent => self.parse_block()?,
|
|
137
|
+
TokenKind::Let => self.parse_var_decl(true)?,
|
|
138
|
+
TokenKind::Const => self.parse_var_decl(false)?,
|
|
139
|
+
TokenKind::Async => {
|
|
140
|
+
self.advance(); // consume 'async'
|
|
141
|
+
self.parse_fun_decl(true)? // parse_fun_decl expects 'fn' next
|
|
142
|
+
}
|
|
143
|
+
TokenKind::Fn => self.parse_fun_decl(false)?,
|
|
144
|
+
TokenKind::If => self.parse_if()?,
|
|
145
|
+
TokenKind::While => self.parse_while()?,
|
|
146
|
+
TokenKind::For => self.parse_for()?,
|
|
147
|
+
TokenKind::Return => self.parse_return()?,
|
|
148
|
+
TokenKind::Switch => self.parse_switch()?,
|
|
149
|
+
TokenKind::Do => self.parse_do_while()?,
|
|
150
|
+
TokenKind::Throw => self.parse_throw()?,
|
|
151
|
+
TokenKind::Try => self.parse_try()?,
|
|
152
|
+
TokenKind::Break => {
|
|
153
|
+
self.advance();
|
|
154
|
+
let span_end = self.peek().map(|t| t.span.end).unwrap_or(span_start);
|
|
155
|
+
Statement::Break {
|
|
156
|
+
span: Span {
|
|
157
|
+
start: span_start,
|
|
158
|
+
end: span_end,
|
|
159
|
+
},
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
TokenKind::Continue => {
|
|
163
|
+
self.advance();
|
|
164
|
+
let span_end = self.peek().map(|t| t.span.end).unwrap_or(span_start);
|
|
165
|
+
Statement::Continue {
|
|
166
|
+
span: Span {
|
|
167
|
+
start: span_start,
|
|
168
|
+
end: span_end,
|
|
169
|
+
},
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
TokenKind::Import => self.parse_import()?,
|
|
173
|
+
TokenKind::Export => self.parse_export()?,
|
|
174
|
+
TokenKind::Type => self.parse_type_alias()?,
|
|
175
|
+
TokenKind::Declare => self.parse_declare()?,
|
|
176
|
+
_ => {
|
|
177
|
+
let expr = self.parse_expr()?;
|
|
178
|
+
let span_end = expr.span().end;
|
|
179
|
+
Statement::ExprStmt {
|
|
180
|
+
expr,
|
|
181
|
+
span: Span {
|
|
182
|
+
start: span_start,
|
|
183
|
+
end: span_end,
|
|
184
|
+
},
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Optional semicolon
|
|
190
|
+
if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
191
|
+
self.advance();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
Ok(stmt)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
fn parse_block_or_statement(&mut self) -> Result<Statement, String> {
|
|
198
|
+
if matches!(self.peek_kind(), Some(TokenKind::LBrace))
|
|
199
|
+
|| matches!(self.peek_kind(), Some(TokenKind::Indent))
|
|
200
|
+
{
|
|
201
|
+
return self.parse_block();
|
|
202
|
+
}
|
|
203
|
+
self.parse_statement()
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
fn parse_block(&mut self) -> Result<Statement, String> {
|
|
207
|
+
let span_start = self.peek().ok_or("Unexpected EOF")?.span.start;
|
|
208
|
+
|
|
209
|
+
let opened_with_brace = matches!(self.peek_kind(), Some(TokenKind::LBrace));
|
|
210
|
+
if opened_with_brace {
|
|
211
|
+
self.advance(); // {
|
|
212
|
+
// After `{`, the lexer often emits `Indent` for the first indented line of the body.
|
|
213
|
+
// `parse_statement` treats a leading `Indent` as starting a *nested* indent-block, so
|
|
214
|
+
// without consuming this token we get `Block { Block { let ... } ; ... }` and the first
|
|
215
|
+
// `let`/`const` is scoped too narrowly (JS ReferenceError). This indent is layout for
|
|
216
|
+
// *this* brace block, not an inner block.
|
|
217
|
+
if matches!(self.peek_kind(), Some(TokenKind::Indent)) {
|
|
218
|
+
self.advance();
|
|
219
|
+
}
|
|
220
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Indent)) {
|
|
221
|
+
self.advance(); // Indent
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
let mut statements = Vec::with_capacity(4);
|
|
225
|
+
loop {
|
|
226
|
+
if matches!(self.peek_kind(), Some(TokenKind::RBrace))
|
|
227
|
+
|| matches!(self.peek_kind(), Some(TokenKind::Dedent))
|
|
228
|
+
|| self.peek_kind().is_none()
|
|
229
|
+
{
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
statements.push(self.parse_statement()?);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if matches!(
|
|
236
|
+
self.peek_kind(),
|
|
237
|
+
Some(TokenKind::RBrace | TokenKind::Dedent)
|
|
238
|
+
) {
|
|
239
|
+
self.advance();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
let peek_end = self.peek().map(|x| x.span.end);
|
|
243
|
+
let last_end = statements.last().map(|s| s.span().end);
|
|
244
|
+
let end = match (peek_end, last_end) {
|
|
245
|
+
(Some(p), Some(l)) => {
|
|
246
|
+
if p.0 > l.0 || (p.0 == l.0 && p.1 > l.1) {
|
|
247
|
+
p
|
|
248
|
+
} else {
|
|
249
|
+
l
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
(Some(p), None) => p,
|
|
253
|
+
(None, Some(l)) => l,
|
|
254
|
+
(None, None) => span_start,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
Ok(Statement::Block {
|
|
258
|
+
statements,
|
|
259
|
+
span: Span {
|
|
260
|
+
start: span_start,
|
|
261
|
+
end,
|
|
262
|
+
},
|
|
263
|
+
})
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
fn parse_var_decl(&mut self, mutable: bool) -> Result<Statement, String> {
|
|
267
|
+
let span_start = if mutable {
|
|
268
|
+
self.expect(TokenKind::Let)?.span.start
|
|
269
|
+
} else {
|
|
270
|
+
self.expect(TokenKind::Const)?.span.start
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// Check for destructuring pattern
|
|
274
|
+
if matches!(
|
|
275
|
+
self.peek_kind(),
|
|
276
|
+
Some(TokenKind::LBracket) | Some(TokenKind::LBrace)
|
|
277
|
+
) {
|
|
278
|
+
let pattern = self.parse_destruct_pattern()?;
|
|
279
|
+
self.expect(TokenKind::Assign)?;
|
|
280
|
+
let init = self.parse_expr()?;
|
|
281
|
+
return Ok(Statement::VarDeclDestructure {
|
|
282
|
+
pattern,
|
|
283
|
+
mutable,
|
|
284
|
+
init,
|
|
285
|
+
span: self.span_end(span_start),
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
290
|
+
let name_span = Span {
|
|
291
|
+
start: name_tok.span.start,
|
|
292
|
+
end: name_tok.span.end,
|
|
293
|
+
};
|
|
294
|
+
let name = name_tok.literal.clone().ok_or("Expected identifier")?;
|
|
295
|
+
|
|
296
|
+
// Optional type annotation: `: Type`
|
|
297
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
298
|
+
self.advance(); // consume :
|
|
299
|
+
Some(self.parse_type_annotation()?)
|
|
300
|
+
} else {
|
|
301
|
+
None
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
let init = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
305
|
+
self.advance();
|
|
306
|
+
Some(self.parse_expr()?)
|
|
307
|
+
} else {
|
|
308
|
+
None
|
|
309
|
+
};
|
|
310
|
+
Ok(Statement::VarDecl {
|
|
311
|
+
name,
|
|
312
|
+
name_span,
|
|
313
|
+
mutable,
|
|
314
|
+
type_ann,
|
|
315
|
+
init,
|
|
316
|
+
span: self.span_end(span_start),
|
|
317
|
+
})
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
fn parse_destruct_pattern(&mut self) -> Result<DestructPattern, String> {
|
|
321
|
+
match self.peek_kind() {
|
|
322
|
+
Some(TokenKind::LBracket) => self.parse_array_destruct_pattern(),
|
|
323
|
+
Some(TokenKind::LBrace) => self.parse_object_destruct_pattern(),
|
|
324
|
+
_ => Err("Expected destructuring pattern".to_string()),
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
fn parse_array_destruct_pattern(&mut self) -> Result<DestructPattern, String> {
|
|
329
|
+
self.expect(TokenKind::LBracket)?;
|
|
330
|
+
let mut elements = Vec::new();
|
|
331
|
+
|
|
332
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
|
|
333
|
+
// Handle holes (elision): [a, , b]
|
|
334
|
+
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
335
|
+
elements.push(None);
|
|
336
|
+
self.advance();
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Rest element: ...rest
|
|
341
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
342
|
+
self.advance();
|
|
343
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
344
|
+
let name_span = Span {
|
|
345
|
+
start: name_tok.span.start,
|
|
346
|
+
end: name_tok.span.end,
|
|
347
|
+
};
|
|
348
|
+
let name = name_tok.literal.clone().ok_or("Expected identifier")?;
|
|
349
|
+
elements.push(Some(DestructElement::Rest(name, name_span)));
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Nested pattern or identifier
|
|
354
|
+
let elem = match self.peek_kind() {
|
|
355
|
+
Some(TokenKind::LBracket) | Some(TokenKind::LBrace) => {
|
|
356
|
+
let nested = self.parse_destruct_pattern()?;
|
|
357
|
+
DestructElement::Pattern(Box::new(nested))
|
|
358
|
+
}
|
|
359
|
+
Some(TokenKind::Ident) => {
|
|
360
|
+
let name_tok = self.advance().ok_or("Unexpected EOF")?;
|
|
361
|
+
let name_span = Span {
|
|
362
|
+
start: name_tok.span.start,
|
|
363
|
+
end: name_tok.span.end,
|
|
364
|
+
};
|
|
365
|
+
let name = name_tok.literal.clone().ok_or("Expected identifier")?;
|
|
366
|
+
DestructElement::Ident(name, name_span)
|
|
367
|
+
}
|
|
368
|
+
_ => return Err("Expected identifier or pattern in destructuring".to_string()),
|
|
369
|
+
};
|
|
370
|
+
elements.push(Some(elem));
|
|
371
|
+
|
|
372
|
+
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
373
|
+
self.advance();
|
|
374
|
+
} else {
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
self.expect(TokenKind::RBracket)?;
|
|
380
|
+
Ok(DestructPattern::Array(elements))
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
fn parse_object_destruct_pattern(&mut self) -> Result<DestructPattern, String> {
|
|
384
|
+
self.expect(TokenKind::LBrace)?;
|
|
385
|
+
let mut props = Vec::new();
|
|
386
|
+
|
|
387
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
388
|
+
let key_tok = self.expect(TokenKind::Ident)?;
|
|
389
|
+
let key_span = Span {
|
|
390
|
+
start: key_tok.span.start,
|
|
391
|
+
end: key_tok.span.end,
|
|
392
|
+
};
|
|
393
|
+
let key = key_tok.literal.clone().ok_or("Expected identifier")?;
|
|
394
|
+
|
|
395
|
+
let value = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
396
|
+
self.advance();
|
|
397
|
+
// Could be renamed binding or nested pattern
|
|
398
|
+
match self.peek_kind() {
|
|
399
|
+
Some(TokenKind::LBracket) | Some(TokenKind::LBrace) => {
|
|
400
|
+
let nested = self.parse_destruct_pattern()?;
|
|
401
|
+
DestructElement::Pattern(Box::new(nested))
|
|
402
|
+
}
|
|
403
|
+
Some(TokenKind::Ident) => {
|
|
404
|
+
let name_tok = self.advance().ok_or("Unexpected EOF")?;
|
|
405
|
+
let name_span = Span {
|
|
406
|
+
start: name_tok.span.start,
|
|
407
|
+
end: name_tok.span.end,
|
|
408
|
+
};
|
|
409
|
+
let name = name_tok.literal.clone().ok_or("Expected identifier")?;
|
|
410
|
+
DestructElement::Ident(name, name_span)
|
|
411
|
+
}
|
|
412
|
+
_ => return Err("Expected identifier or pattern after ':'".to_string()),
|
|
413
|
+
}
|
|
414
|
+
} else {
|
|
415
|
+
// Shorthand: { key } is equivalent to { key: key }
|
|
416
|
+
DestructElement::Ident(key.clone(), key_span)
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
props.push(DestructProp { key, value });
|
|
420
|
+
|
|
421
|
+
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
422
|
+
self.advance();
|
|
423
|
+
} else {
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
self.expect(TokenKind::RBrace)?;
|
|
429
|
+
Ok(DestructPattern::Object(props))
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/// One formal parameter: `name`, `name: T`, `name = expr`, or a destructuring pattern.
|
|
433
|
+
fn parse_fun_param(&mut self) -> Result<FunParam, String> {
|
|
434
|
+
if matches!(
|
|
435
|
+
self.peek_kind(),
|
|
436
|
+
Some(TokenKind::LBracket | TokenKind::LBrace)
|
|
437
|
+
) {
|
|
438
|
+
let pattern = self.parse_destruct_pattern()?;
|
|
439
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
440
|
+
self.advance();
|
|
441
|
+
Some(self.parse_type_annotation()?)
|
|
442
|
+
} else {
|
|
443
|
+
None
|
|
444
|
+
};
|
|
445
|
+
let default = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
446
|
+
self.advance();
|
|
447
|
+
Some(self.parse_expr()?)
|
|
448
|
+
} else {
|
|
449
|
+
None
|
|
450
|
+
};
|
|
451
|
+
return Ok(FunParam::Destructure {
|
|
452
|
+
pattern,
|
|
453
|
+
type_ann,
|
|
454
|
+
default,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
let param_tok = self.expect(TokenKind::Ident)?;
|
|
458
|
+
let name_span = Span {
|
|
459
|
+
start: param_tok.span.start,
|
|
460
|
+
end: param_tok.span.end,
|
|
461
|
+
};
|
|
462
|
+
let param_name = param_tok.literal.clone().ok_or("Expected param name")?;
|
|
463
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
464
|
+
self.advance();
|
|
465
|
+
Some(self.parse_type_annotation()?)
|
|
466
|
+
} else {
|
|
467
|
+
None
|
|
468
|
+
};
|
|
469
|
+
let default = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
470
|
+
self.advance();
|
|
471
|
+
Some(self.parse_expr()?)
|
|
472
|
+
} else {
|
|
473
|
+
None
|
|
474
|
+
};
|
|
475
|
+
Ok(FunParam::Simple(TypedParam {
|
|
476
|
+
name: param_name,
|
|
477
|
+
name_span,
|
|
478
|
+
type_ann,
|
|
479
|
+
default,
|
|
480
|
+
}))
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/// Parse a type annotation (number, string, T[], {a: T}, etc.)
|
|
484
|
+
fn parse_type_annotation(&mut self) -> Result<TypeAnnotation, String> {
|
|
485
|
+
let base = self.parse_type_primary()?;
|
|
486
|
+
|
|
487
|
+
// Check for array suffix: T[]
|
|
488
|
+
if matches!(self.peek_kind(), Some(TokenKind::LBracket)) {
|
|
489
|
+
self.advance(); // [
|
|
490
|
+
self.expect(TokenKind::RBracket)?; // ]
|
|
491
|
+
return Ok(TypeAnnotation::Array(Box::new(base)));
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Check for union: T | U
|
|
495
|
+
if matches!(self.peek_kind(), Some(TokenKind::BitOr)) {
|
|
496
|
+
let mut types = vec![base];
|
|
497
|
+
while matches!(self.peek_kind(), Some(TokenKind::BitOr)) {
|
|
498
|
+
self.advance(); // |
|
|
499
|
+
types.push(self.parse_type_primary()?);
|
|
500
|
+
}
|
|
501
|
+
return Ok(TypeAnnotation::Union(types));
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
Ok(base)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/// Parse a primary type (identifier, object, or function type)
|
|
508
|
+
fn parse_type_primary(&mut self) -> Result<TypeAnnotation, String> {
|
|
509
|
+
match self.peek_kind() {
|
|
510
|
+
Some(TokenKind::Ident) => {
|
|
511
|
+
let tok = self.advance().ok_or("Expected type name")?;
|
|
512
|
+
let name = tok.literal.clone().ok_or("Expected type name")?;
|
|
513
|
+
Ok(TypeAnnotation::Simple(name))
|
|
514
|
+
}
|
|
515
|
+
Some(TokenKind::Type | TokenKind::Declare) => {
|
|
516
|
+
let tok = self.advance().ok_or("Expected type name")?;
|
|
517
|
+
let name = tok.literal.clone().ok_or("Expected type name")?;
|
|
518
|
+
Ok(TypeAnnotation::Simple(name))
|
|
519
|
+
}
|
|
520
|
+
// Handle keywords that can be type names
|
|
521
|
+
Some(TokenKind::Null) => {
|
|
522
|
+
self.advance();
|
|
523
|
+
Ok(TypeAnnotation::Simple("null".into()))
|
|
524
|
+
}
|
|
525
|
+
Some(TokenKind::Void) => {
|
|
526
|
+
self.advance();
|
|
527
|
+
Ok(TypeAnnotation::Simple("void".into()))
|
|
528
|
+
}
|
|
529
|
+
Some(TokenKind::LBrace) => {
|
|
530
|
+
// Object type: { key: Type, ... }
|
|
531
|
+
self.advance(); // {
|
|
532
|
+
let mut props = Vec::new();
|
|
533
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
534
|
+
// `for` is a keyword but a common method name (`Symbol.for`); allow it here.
|
|
535
|
+
let key: Arc<str> = match self.peek_kind() {
|
|
536
|
+
Some(TokenKind::Ident) => {
|
|
537
|
+
let tok = self.expect(TokenKind::Ident)?;
|
|
538
|
+
Arc::from(
|
|
539
|
+
tok.literal
|
|
540
|
+
.as_deref()
|
|
541
|
+
.ok_or("Expected property name")?,
|
|
542
|
+
)
|
|
543
|
+
}
|
|
544
|
+
Some(TokenKind::For) => {
|
|
545
|
+
self.advance();
|
|
546
|
+
Arc::from("for")
|
|
547
|
+
}
|
|
548
|
+
_ => {
|
|
549
|
+
return Err(format!(
|
|
550
|
+
"Expected Ident or `for` as object type property name, got {:?}",
|
|
551
|
+
self.peek_kind()
|
|
552
|
+
));
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
self.expect(TokenKind::Colon)?;
|
|
556
|
+
let typ = self.parse_type_annotation()?;
|
|
557
|
+
props.push((key, typ));
|
|
558
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
559
|
+
// Accept `,` or `;` between items (TypeScript-style
|
|
560
|
+
// semicolons are common in interface/object type
|
|
561
|
+
// declarations); also tolerate a trailing separator.
|
|
562
|
+
if matches!(
|
|
563
|
+
self.peek_kind(),
|
|
564
|
+
Some(TokenKind::Comma) | Some(TokenKind::Semicolon)
|
|
565
|
+
) {
|
|
566
|
+
self.advance();
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
self.expect(TokenKind::RBrace)?;
|
|
571
|
+
Ok(TypeAnnotation::Object(props))
|
|
572
|
+
}
|
|
573
|
+
Some(TokenKind::LParen) => {
|
|
574
|
+
// Function type: (T1, T2) => R
|
|
575
|
+
self.advance(); // (
|
|
576
|
+
let mut params = Vec::new();
|
|
577
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
578
|
+
params.push(self.parse_type_annotation()?);
|
|
579
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
580
|
+
self.expect(TokenKind::Comma)?;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
self.expect(TokenKind::RParen)?;
|
|
584
|
+
self.expect(TokenKind::Arrow)?;
|
|
585
|
+
let returns = self.parse_type_annotation()?;
|
|
586
|
+
Ok(TypeAnnotation::Function {
|
|
587
|
+
params,
|
|
588
|
+
returns: Box::new(returns),
|
|
589
|
+
})
|
|
590
|
+
}
|
|
591
|
+
_ => Err("Expected type annotation".to_string()),
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
fn parse_fun_decl(&mut self, async_: bool) -> Result<Statement, String> {
|
|
596
|
+
let span_start = self.expect(TokenKind::Fn)?.span.start;
|
|
597
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
598
|
+
let name_span = Span {
|
|
599
|
+
start: name_tok.span.start,
|
|
600
|
+
end: name_tok.span.end,
|
|
601
|
+
};
|
|
602
|
+
let name = name_tok.literal.clone().ok_or("Expected function name")?;
|
|
603
|
+
self.expect(TokenKind::LParen)?;
|
|
604
|
+
let mut params = Vec::with_capacity(4);
|
|
605
|
+
let mut rest_param = None;
|
|
606
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
607
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
608
|
+
self.advance();
|
|
609
|
+
let rest_tok = self.expect(TokenKind::Ident)?;
|
|
610
|
+
let rest_name_span = Span {
|
|
611
|
+
start: rest_tok.span.start,
|
|
612
|
+
end: rest_tok.span.end,
|
|
613
|
+
};
|
|
614
|
+
let param_name = rest_tok.literal.clone().ok_or("Expected rest param name")?;
|
|
615
|
+
// Optional type annotation for rest param
|
|
616
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
617
|
+
self.advance();
|
|
618
|
+
Some(self.parse_type_annotation()?)
|
|
619
|
+
} else {
|
|
620
|
+
None
|
|
621
|
+
};
|
|
622
|
+
rest_param = Some(TypedParam {
|
|
623
|
+
name: param_name,
|
|
624
|
+
name_span: rest_name_span,
|
|
625
|
+
type_ann,
|
|
626
|
+
default: None,
|
|
627
|
+
});
|
|
628
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
629
|
+
return Err("Rest parameter must be last".to_string());
|
|
630
|
+
}
|
|
631
|
+
break;
|
|
632
|
+
}
|
|
633
|
+
params.push(self.parse_fun_param()?);
|
|
634
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
635
|
+
self.expect(TokenKind::Comma)?;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
self.expect(TokenKind::RParen)?;
|
|
639
|
+
|
|
640
|
+
// Optional return type: `: Type`
|
|
641
|
+
let return_type = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
642
|
+
self.advance();
|
|
643
|
+
Some(self.parse_type_annotation()?)
|
|
644
|
+
} else {
|
|
645
|
+
None
|
|
646
|
+
};
|
|
647
|
+
|
|
648
|
+
let body = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
649
|
+
self.advance(); // =
|
|
650
|
+
let expr = self.parse_expr()?;
|
|
651
|
+
let span = expr.span();
|
|
652
|
+
Box::new(Statement::Block {
|
|
653
|
+
statements: vec![Statement::Return {
|
|
654
|
+
value: Some(expr),
|
|
655
|
+
span,
|
|
656
|
+
}],
|
|
657
|
+
span,
|
|
658
|
+
})
|
|
659
|
+
} else {
|
|
660
|
+
Box::new(self.parse_block()?)
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
// Span must cover the whole declaration through the body. `peek().start` alone can sit on
|
|
664
|
+
// the opening `{` (same as `span_start` at EOF) or otherwise truncate before inner spans.
|
|
665
|
+
let peek_start = self.peek().map(|t| t.span.start).unwrap_or(span_start);
|
|
666
|
+
let body_end = body.as_ref().span().end;
|
|
667
|
+
let end = if peek_start.0 > body_end.0
|
|
668
|
+
|| (peek_start.0 == body_end.0 && peek_start.1 > body_end.1)
|
|
669
|
+
{
|
|
670
|
+
peek_start
|
|
671
|
+
} else {
|
|
672
|
+
body_end
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
Ok(Statement::FunDecl {
|
|
676
|
+
async_,
|
|
677
|
+
name,
|
|
678
|
+
name_span,
|
|
679
|
+
params,
|
|
680
|
+
rest_param,
|
|
681
|
+
return_type,
|
|
682
|
+
body,
|
|
683
|
+
span: Span {
|
|
684
|
+
start: span_start,
|
|
685
|
+
end,
|
|
686
|
+
},
|
|
687
|
+
})
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
fn parse_type_alias(&mut self) -> Result<Statement, String> {
|
|
691
|
+
let span_start = self.expect(TokenKind::Type)?.span.start;
|
|
692
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
693
|
+
let name_span = Span {
|
|
694
|
+
start: name_tok.span.start,
|
|
695
|
+
end: name_tok.span.end,
|
|
696
|
+
};
|
|
697
|
+
let name = name_tok.literal.clone().ok_or("Expected type alias name")?;
|
|
698
|
+
self.expect(TokenKind::Assign)?;
|
|
699
|
+
let ty = self.parse_type_annotation()?;
|
|
700
|
+
Ok(Statement::TypeAlias {
|
|
701
|
+
name,
|
|
702
|
+
name_span,
|
|
703
|
+
ty,
|
|
704
|
+
span: self.span_end(span_start),
|
|
705
|
+
})
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
fn parse_declare(&mut self) -> Result<Statement, String> {
|
|
709
|
+
let span_start = self.expect(TokenKind::Declare)?.span.start;
|
|
710
|
+
let async_ = if matches!(self.peek_kind(), Some(TokenKind::Async)) {
|
|
711
|
+
self.advance();
|
|
712
|
+
true
|
|
713
|
+
} else {
|
|
714
|
+
false
|
|
715
|
+
};
|
|
716
|
+
if matches!(self.peek_kind(), Some(TokenKind::Fn)) {
|
|
717
|
+
return self.parse_declare_fun(span_start, async_);
|
|
718
|
+
}
|
|
719
|
+
let const_ = match self.peek_kind() {
|
|
720
|
+
Some(TokenKind::Let) => {
|
|
721
|
+
self.advance();
|
|
722
|
+
false
|
|
723
|
+
}
|
|
724
|
+
Some(TokenKind::Const) => {
|
|
725
|
+
self.advance();
|
|
726
|
+
true
|
|
727
|
+
}
|
|
728
|
+
_ => {
|
|
729
|
+
return Err(
|
|
730
|
+
"Expected `let`, `const`, `async fn`, or `fn` after `declare`".to_string(),
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
735
|
+
let name_span = Span {
|
|
736
|
+
start: name_tok.span.start,
|
|
737
|
+
end: name_tok.span.end,
|
|
738
|
+
};
|
|
739
|
+
let name = name_tok.literal.clone().ok_or("Expected identifier")?;
|
|
740
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
741
|
+
self.advance();
|
|
742
|
+
Some(self.parse_type_annotation()?)
|
|
743
|
+
} else {
|
|
744
|
+
None
|
|
745
|
+
};
|
|
746
|
+
if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
747
|
+
return Err("`declare` cannot have an initializer".to_string());
|
|
748
|
+
}
|
|
749
|
+
Ok(Statement::DeclareVar {
|
|
750
|
+
name,
|
|
751
|
+
name_span,
|
|
752
|
+
type_ann,
|
|
753
|
+
const_,
|
|
754
|
+
span: self.span_end(span_start),
|
|
755
|
+
})
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
fn parse_declare_fun(
|
|
759
|
+
&mut self,
|
|
760
|
+
span_start: (usize, usize),
|
|
761
|
+
async_: bool,
|
|
762
|
+
) -> Result<Statement, String> {
|
|
763
|
+
self.expect(TokenKind::Fn)?;
|
|
764
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
765
|
+
let name_span = Span {
|
|
766
|
+
start: name_tok.span.start,
|
|
767
|
+
end: name_tok.span.end,
|
|
768
|
+
};
|
|
769
|
+
let name = name_tok.literal.clone().ok_or("Expected function name")?;
|
|
770
|
+
self.expect(TokenKind::LParen)?;
|
|
771
|
+
let mut params = Vec::with_capacity(4);
|
|
772
|
+
let mut rest_param = None;
|
|
773
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
774
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
775
|
+
self.advance();
|
|
776
|
+
let rest_tok = self.expect(TokenKind::Ident)?;
|
|
777
|
+
let rest_name_span = Span {
|
|
778
|
+
start: rest_tok.span.start,
|
|
779
|
+
end: rest_tok.span.end,
|
|
780
|
+
};
|
|
781
|
+
let param_name = rest_tok.literal.clone().ok_or("Expected rest param name")?;
|
|
782
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
783
|
+
self.advance();
|
|
784
|
+
Some(self.parse_type_annotation()?)
|
|
785
|
+
} else {
|
|
786
|
+
None
|
|
787
|
+
};
|
|
788
|
+
rest_param = Some(TypedParam {
|
|
789
|
+
name: param_name,
|
|
790
|
+
name_span: rest_name_span,
|
|
791
|
+
type_ann,
|
|
792
|
+
default: None,
|
|
793
|
+
});
|
|
794
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
795
|
+
return Err("Rest parameter must be last".to_string());
|
|
796
|
+
}
|
|
797
|
+
break;
|
|
798
|
+
}
|
|
799
|
+
params.push(self.parse_fun_param()?);
|
|
800
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
801
|
+
self.expect(TokenKind::Comma)?;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
self.expect(TokenKind::RParen)?;
|
|
805
|
+
let return_type = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
806
|
+
self.advance();
|
|
807
|
+
Some(self.parse_type_annotation()?)
|
|
808
|
+
} else {
|
|
809
|
+
None
|
|
810
|
+
};
|
|
811
|
+
if matches!(
|
|
812
|
+
self.peek_kind(),
|
|
813
|
+
Some(TokenKind::Assign | TokenKind::LBrace | TokenKind::Indent)
|
|
814
|
+
) {
|
|
815
|
+
return Err("`declare function` must not have a body".to_string());
|
|
816
|
+
}
|
|
817
|
+
if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
818
|
+
self.advance();
|
|
819
|
+
}
|
|
820
|
+
Ok(Statement::DeclareFun {
|
|
821
|
+
async_,
|
|
822
|
+
name,
|
|
823
|
+
name_span,
|
|
824
|
+
params,
|
|
825
|
+
rest_param,
|
|
826
|
+
return_type,
|
|
827
|
+
span: self.span_end(span_start),
|
|
828
|
+
})
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
fn parse_if(&mut self) -> Result<Statement, String> {
|
|
832
|
+
let span_start = self.expect(TokenKind::If)?.span.start;
|
|
833
|
+
self.expect(TokenKind::LParen)?;
|
|
834
|
+
let cond = self.parse_expr()?;
|
|
835
|
+
self.expect(TokenKind::RParen)?;
|
|
836
|
+
let then_branch = Box::new(self.parse_block_or_statement()?);
|
|
837
|
+
let else_branch = if matches!(self.peek_kind(), Some(TokenKind::Else)) {
|
|
838
|
+
self.advance();
|
|
839
|
+
Some(Box::new(self.parse_block_or_statement()?))
|
|
840
|
+
} else {
|
|
841
|
+
None
|
|
842
|
+
};
|
|
843
|
+
Ok(Statement::If {
|
|
844
|
+
cond,
|
|
845
|
+
then_branch,
|
|
846
|
+
else_branch,
|
|
847
|
+
span: self.span_end(span_start),
|
|
848
|
+
})
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
fn parse_while(&mut self) -> Result<Statement, String> {
|
|
852
|
+
let span_start = self.expect(TokenKind::While)?.span.start;
|
|
853
|
+
self.expect(TokenKind::LParen)?;
|
|
854
|
+
let cond = self.parse_expr()?;
|
|
855
|
+
self.expect(TokenKind::RParen)?;
|
|
856
|
+
let body = Box::new(self.parse_block_or_statement()?);
|
|
857
|
+
Ok(Statement::While {
|
|
858
|
+
cond,
|
|
859
|
+
body,
|
|
860
|
+
span: self.span_end(span_start),
|
|
861
|
+
})
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
fn parse_for(&mut self) -> Result<Statement, String> {
|
|
865
|
+
let span_start = self.expect(TokenKind::For)?.span.start;
|
|
866
|
+
self.expect(TokenKind::LParen)?;
|
|
867
|
+
let init = if matches!(self.peek_kind(), Some(TokenKind::Let | TokenKind::Const)) {
|
|
868
|
+
let mutable = matches!(self.peek_kind(), Some(TokenKind::Let));
|
|
869
|
+
let var_span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
870
|
+
self.advance();
|
|
871
|
+
let for_name_tok = self.expect(TokenKind::Ident)?;
|
|
872
|
+
let name_span = Span {
|
|
873
|
+
start: for_name_tok.span.start,
|
|
874
|
+
end: for_name_tok.span.end,
|
|
875
|
+
};
|
|
876
|
+
let name = for_name_tok.literal.clone().ok_or("Expected identifier")?;
|
|
877
|
+
if matches!(self.peek_kind(), Some(TokenKind::Of)) {
|
|
878
|
+
self.advance();
|
|
879
|
+
let iterable = self.parse_expr()?;
|
|
880
|
+
self.expect(TokenKind::RParen)?;
|
|
881
|
+
let body = Box::new(self.parse_block_or_statement()?);
|
|
882
|
+
return Ok(Statement::ForOf {
|
|
883
|
+
name,
|
|
884
|
+
name_span,
|
|
885
|
+
iterable,
|
|
886
|
+
body,
|
|
887
|
+
span: self.span_end(span_start),
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
891
|
+
self.advance();
|
|
892
|
+
Some(self.parse_type_annotation()?)
|
|
893
|
+
} else {
|
|
894
|
+
None
|
|
895
|
+
};
|
|
896
|
+
let init_expr = if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
897
|
+
self.advance();
|
|
898
|
+
Some(self.parse_expr()?)
|
|
899
|
+
} else {
|
|
900
|
+
None
|
|
901
|
+
};
|
|
902
|
+
if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
903
|
+
self.advance();
|
|
904
|
+
}
|
|
905
|
+
Some(Box::new(Statement::VarDecl {
|
|
906
|
+
name,
|
|
907
|
+
name_span,
|
|
908
|
+
mutable,
|
|
909
|
+
type_ann,
|
|
910
|
+
init: init_expr,
|
|
911
|
+
span: self.span_end(var_span_start),
|
|
912
|
+
}))
|
|
913
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
914
|
+
None
|
|
915
|
+
} else {
|
|
916
|
+
let st = self.peek().ok_or("Unexpected EOF in for init")?;
|
|
917
|
+
let span_start = st.span.start;
|
|
918
|
+
Some(Box::new(Statement::ExprStmt {
|
|
919
|
+
expr: self.parse_expr()?,
|
|
920
|
+
span: Span {
|
|
921
|
+
start: span_start,
|
|
922
|
+
end: self.peek().map(|x| x.span.end).unwrap_or(span_start),
|
|
923
|
+
},
|
|
924
|
+
}))
|
|
925
|
+
};
|
|
926
|
+
if matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
927
|
+
self.advance();
|
|
928
|
+
}
|
|
929
|
+
let cond = if matches!(
|
|
930
|
+
self.peek_kind(),
|
|
931
|
+
Some(TokenKind::Semicolon | TokenKind::RParen)
|
|
932
|
+
) {
|
|
933
|
+
None
|
|
934
|
+
} else {
|
|
935
|
+
let c = self.parse_expr()?;
|
|
936
|
+
self.expect(TokenKind::Semicolon)?;
|
|
937
|
+
Some(c)
|
|
938
|
+
};
|
|
939
|
+
// `for (init; ; update)` — when the condition is empty we matched `;` above but did not
|
|
940
|
+
// consume it; skip it so `update` / `)` parse correctly (e.g. `for (;;)`).
|
|
941
|
+
if cond.is_none() && matches!(self.peek_kind(), Some(TokenKind::Semicolon)) {
|
|
942
|
+
self.advance();
|
|
943
|
+
}
|
|
944
|
+
let update = if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
945
|
+
None
|
|
946
|
+
} else {
|
|
947
|
+
let u = self.parse_expr()?;
|
|
948
|
+
Some(u)
|
|
949
|
+
};
|
|
950
|
+
self.expect(TokenKind::RParen)?;
|
|
951
|
+
let body = Box::new(self.parse_block_or_statement()?);
|
|
952
|
+
Ok(Statement::For {
|
|
953
|
+
init,
|
|
954
|
+
cond,
|
|
955
|
+
update,
|
|
956
|
+
body,
|
|
957
|
+
span: self.span_end(span_start),
|
|
958
|
+
})
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
fn parse_return(&mut self) -> Result<Statement, String> {
|
|
962
|
+
let span_start = self.expect(TokenKind::Return)?.span.start;
|
|
963
|
+
let value = if matches!(self.peek_kind(), Some(TokenKind::Semicolon))
|
|
964
|
+
|| matches!(self.peek_kind(), Some(TokenKind::Dedent))
|
|
965
|
+
|| matches!(self.peek_kind(), Some(TokenKind::RBrace))
|
|
966
|
+
|| self.peek_kind().is_none()
|
|
967
|
+
{
|
|
968
|
+
None
|
|
969
|
+
} else {
|
|
970
|
+
Some(self.parse_expr()?)
|
|
971
|
+
};
|
|
972
|
+
Ok(Statement::Return {
|
|
973
|
+
value,
|
|
974
|
+
span: self.span_end(span_start),
|
|
975
|
+
})
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
fn parse_switch(&mut self) -> Result<Statement, String> {
|
|
979
|
+
let span_start = self.expect(TokenKind::Switch)?.span.start;
|
|
980
|
+
self.expect(TokenKind::LParen)?;
|
|
981
|
+
let expr = self.parse_expr()?;
|
|
982
|
+
self.expect(TokenKind::RParen)?;
|
|
983
|
+
self.expect(TokenKind::LBrace)?;
|
|
984
|
+
let mut cases = Vec::new();
|
|
985
|
+
let mut default_body = None;
|
|
986
|
+
loop {
|
|
987
|
+
if matches!(self.peek_kind(), Some(TokenKind::Case)) {
|
|
988
|
+
self.advance();
|
|
989
|
+
let case_expr = self.parse_expr()?;
|
|
990
|
+
self.expect(TokenKind::Colon)?;
|
|
991
|
+
let mut body = Vec::new();
|
|
992
|
+
while !matches!(self.peek_kind(), Some(TokenKind::Case))
|
|
993
|
+
&& !matches!(self.peek_kind(), Some(TokenKind::Default))
|
|
994
|
+
&& !matches!(self.peek_kind(), Some(TokenKind::RBrace))
|
|
995
|
+
&& self.peek_kind().is_some()
|
|
996
|
+
{
|
|
997
|
+
body.push(self.parse_statement()?);
|
|
998
|
+
}
|
|
999
|
+
cases.push((Some(case_expr), body));
|
|
1000
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Default)) {
|
|
1001
|
+
self.advance();
|
|
1002
|
+
self.expect(TokenKind::Colon)?;
|
|
1003
|
+
let mut body = Vec::new();
|
|
1004
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RBrace))
|
|
1005
|
+
&& self.peek_kind().is_some()
|
|
1006
|
+
{
|
|
1007
|
+
body.push(self.parse_statement()?);
|
|
1008
|
+
}
|
|
1009
|
+
default_body = Some(body);
|
|
1010
|
+
break;
|
|
1011
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
1012
|
+
break;
|
|
1013
|
+
} else {
|
|
1014
|
+
return Err("Expected case or default in switch".to_string());
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
self.expect(TokenKind::RBrace)?;
|
|
1018
|
+
Ok(Statement::Switch {
|
|
1019
|
+
expr,
|
|
1020
|
+
cases,
|
|
1021
|
+
default_body,
|
|
1022
|
+
span: self.span_end(span_start),
|
|
1023
|
+
})
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
fn parse_do_while(&mut self) -> Result<Statement, String> {
|
|
1027
|
+
let span_start = self.expect(TokenKind::Do)?.span.start;
|
|
1028
|
+
let body = Box::new(self.parse_block_or_statement()?);
|
|
1029
|
+
self.expect(TokenKind::While)?;
|
|
1030
|
+
self.expect(TokenKind::LParen)?;
|
|
1031
|
+
let cond = self.parse_expr()?;
|
|
1032
|
+
self.expect(TokenKind::RParen)?;
|
|
1033
|
+
Ok(Statement::DoWhile {
|
|
1034
|
+
body,
|
|
1035
|
+
cond,
|
|
1036
|
+
span: self.span_end(span_start),
|
|
1037
|
+
})
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
fn parse_throw(&mut self) -> Result<Statement, String> {
|
|
1041
|
+
let span_start = self.expect(TokenKind::Throw)?.span.start;
|
|
1042
|
+
let value = self.parse_expr()?;
|
|
1043
|
+
Ok(Statement::Throw {
|
|
1044
|
+
value,
|
|
1045
|
+
span: self.span_end(span_start),
|
|
1046
|
+
})
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
fn parse_try(&mut self) -> Result<Statement, String> {
|
|
1050
|
+
let span_start = self.expect(TokenKind::Try)?.span.start;
|
|
1051
|
+
let body = Box::new(self.parse_block_or_statement()?);
|
|
1052
|
+
|
|
1053
|
+
let mut catch_param = None;
|
|
1054
|
+
let mut catch_param_span = None;
|
|
1055
|
+
let mut catch_body = None;
|
|
1056
|
+
let mut finally_body = None;
|
|
1057
|
+
|
|
1058
|
+
if matches!(self.peek_kind(), Some(TokenKind::Catch)) {
|
|
1059
|
+
self.advance();
|
|
1060
|
+
self.expect(TokenKind::LParen)?;
|
|
1061
|
+
let catch_tok = self.expect(TokenKind::Ident)?;
|
|
1062
|
+
catch_param_span = Some(Span {
|
|
1063
|
+
start: catch_tok.span.start,
|
|
1064
|
+
end: catch_tok.span.end,
|
|
1065
|
+
});
|
|
1066
|
+
catch_param = catch_tok.literal.clone();
|
|
1067
|
+
self.expect(TokenKind::RParen)?;
|
|
1068
|
+
catch_body = Some(Box::new(self.parse_block_or_statement()?));
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
if matches!(self.peek_kind(), Some(TokenKind::Finally)) {
|
|
1072
|
+
self.advance();
|
|
1073
|
+
finally_body = Some(Box::new(self.parse_block_or_statement()?));
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
if catch_body.is_none() && finally_body.is_none() {
|
|
1077
|
+
return Err("try statement requires catch or finally".to_string());
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
Ok(Statement::Try {
|
|
1081
|
+
body,
|
|
1082
|
+
catch_param,
|
|
1083
|
+
catch_param_span,
|
|
1084
|
+
catch_body,
|
|
1085
|
+
finally_body,
|
|
1086
|
+
span: self.span_end(span_start),
|
|
1087
|
+
})
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
fn parse_import(&mut self) -> Result<Statement, String> {
|
|
1091
|
+
let span_start = self.expect(TokenKind::Import)?.span.start;
|
|
1092
|
+
let specifiers = if matches!(self.peek_kind(), Some(TokenKind::LBrace)) {
|
|
1093
|
+
// Named: import { a, b as c } from "..."
|
|
1094
|
+
self.advance();
|
|
1095
|
+
let mut specs = Vec::new();
|
|
1096
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
1097
|
+
let name_tok = self.expect(TokenKind::Ident)?;
|
|
1098
|
+
let name_span = Span {
|
|
1099
|
+
start: name_tok.span.start,
|
|
1100
|
+
end: name_tok.span.end,
|
|
1101
|
+
};
|
|
1102
|
+
let name = name_tok
|
|
1103
|
+
.literal
|
|
1104
|
+
.clone()
|
|
1105
|
+
.ok_or("Expected identifier in import")?;
|
|
1106
|
+
let (alias, alias_span) = if matches!(self.peek_kind(), Some(TokenKind::Ident))
|
|
1107
|
+
&& self.peek().and_then(|t| t.literal.as_deref()) == Some("as")
|
|
1108
|
+
{
|
|
1109
|
+
self.advance(); // consume 'as'
|
|
1110
|
+
let alias_tok = self.expect(TokenKind::Ident)?;
|
|
1111
|
+
let asp = Span {
|
|
1112
|
+
start: alias_tok.span.start,
|
|
1113
|
+
end: alias_tok.span.end,
|
|
1114
|
+
};
|
|
1115
|
+
(
|
|
1116
|
+
Some(
|
|
1117
|
+
alias_tok
|
|
1118
|
+
.literal
|
|
1119
|
+
.clone()
|
|
1120
|
+
.ok_or("Expected alias after 'as'")?,
|
|
1121
|
+
),
|
|
1122
|
+
Some(asp),
|
|
1123
|
+
)
|
|
1124
|
+
} else {
|
|
1125
|
+
(None, None)
|
|
1126
|
+
};
|
|
1127
|
+
specs.push(ImportSpecifier::Named {
|
|
1128
|
+
name,
|
|
1129
|
+
name_span,
|
|
1130
|
+
alias,
|
|
1131
|
+
alias_span,
|
|
1132
|
+
});
|
|
1133
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
1134
|
+
self.expect(TokenKind::Comma)?;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
self.expect(TokenKind::RBrace)?;
|
|
1138
|
+
specs
|
|
1139
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Star)) {
|
|
1140
|
+
// Namespace: import * as M from "..."
|
|
1141
|
+
self.advance();
|
|
1142
|
+
let as_tok = self.expect(TokenKind::Ident)?;
|
|
1143
|
+
if as_tok.literal.as_deref() != Some("as") {
|
|
1144
|
+
return Err("Expected 'as' after '*' in namespace import".to_string());
|
|
1145
|
+
}
|
|
1146
|
+
let alias_tok = self.expect(TokenKind::Ident)?;
|
|
1147
|
+
let name_span = Span {
|
|
1148
|
+
start: alias_tok.span.start,
|
|
1149
|
+
end: alias_tok.span.end,
|
|
1150
|
+
};
|
|
1151
|
+
let alias = alias_tok
|
|
1152
|
+
.literal
|
|
1153
|
+
.clone()
|
|
1154
|
+
.ok_or("Expected identifier after 'as'")?;
|
|
1155
|
+
vec![ImportSpecifier::Namespace {
|
|
1156
|
+
name: alias,
|
|
1157
|
+
name_span,
|
|
1158
|
+
}]
|
|
1159
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Ident)) {
|
|
1160
|
+
// Default: import X from "..."
|
|
1161
|
+
let def_tok = self.expect(TokenKind::Ident)?;
|
|
1162
|
+
let name_span = Span {
|
|
1163
|
+
start: def_tok.span.start,
|
|
1164
|
+
end: def_tok.span.end,
|
|
1165
|
+
};
|
|
1166
|
+
let name = def_tok.literal.clone().ok_or("Expected identifier")?;
|
|
1167
|
+
vec![ImportSpecifier::Default { name, name_span }]
|
|
1168
|
+
} else {
|
|
1169
|
+
return Err("Expected { }, * as name, or default import".to_string());
|
|
1170
|
+
};
|
|
1171
|
+
let from_tok = self.expect(TokenKind::Ident)?;
|
|
1172
|
+
if from_tok.literal.as_deref() != Some("from") {
|
|
1173
|
+
return Err("Expected 'from' in import statement".to_string());
|
|
1174
|
+
}
|
|
1175
|
+
let from = self
|
|
1176
|
+
.expect(TokenKind::String)?
|
|
1177
|
+
.literal
|
|
1178
|
+
.clone()
|
|
1179
|
+
.ok_or("Expected string path in import")?;
|
|
1180
|
+
Ok(Statement::Import {
|
|
1181
|
+
specifiers,
|
|
1182
|
+
from,
|
|
1183
|
+
span: self.span_end(span_start),
|
|
1184
|
+
})
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
fn parse_export(&mut self) -> Result<Statement, String> {
|
|
1188
|
+
let span_start = self.expect(TokenKind::Export)?.span.start;
|
|
1189
|
+
let declaration = if matches!(self.peek_kind(), Some(TokenKind::Default)) {
|
|
1190
|
+
self.advance();
|
|
1191
|
+
let expr = self.parse_expr()?;
|
|
1192
|
+
ExportDeclaration::Default(expr)
|
|
1193
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Type)) {
|
|
1194
|
+
ExportDeclaration::Named(Box::new(self.parse_type_alias()?))
|
|
1195
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Declare)) {
|
|
1196
|
+
ExportDeclaration::Named(Box::new(self.parse_declare()?))
|
|
1197
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Const)) {
|
|
1198
|
+
ExportDeclaration::Named(Box::new(self.parse_var_decl(false)?))
|
|
1199
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Let)) {
|
|
1200
|
+
ExportDeclaration::Named(Box::new(self.parse_var_decl(true)?))
|
|
1201
|
+
} else if matches!(self.peek_kind(), Some(TokenKind::Async))
|
|
1202
|
+
|| matches!(self.peek_kind(), Some(TokenKind::Fn))
|
|
1203
|
+
{
|
|
1204
|
+
let async_ = matches!(self.peek_kind(), Some(TokenKind::Async));
|
|
1205
|
+
if async_ {
|
|
1206
|
+
self.advance();
|
|
1207
|
+
}
|
|
1208
|
+
ExportDeclaration::Named(Box::new(self.parse_fun_decl(async_)?))
|
|
1209
|
+
} else {
|
|
1210
|
+
return Err(
|
|
1211
|
+
"Expected 'default', 'type', 'declare', 'const', 'let', or 'fn' after export"
|
|
1212
|
+
.to_string(),
|
|
1213
|
+
);
|
|
1214
|
+
};
|
|
1215
|
+
Ok(Statement::Export {
|
|
1216
|
+
declaration: Box::new(declaration),
|
|
1217
|
+
span: self.span_end(span_start),
|
|
1218
|
+
})
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
fn parse_expr(&mut self) -> Result<Expr, String> {
|
|
1222
|
+
self.parse_assign()
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
fn parse_assign(&mut self) -> Result<Expr, String> {
|
|
1226
|
+
let left = self.parse_conditional()?;
|
|
1227
|
+
|
|
1228
|
+
// Check for simple assignment
|
|
1229
|
+
if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
1230
|
+
let start = left.span().start;
|
|
1231
|
+
|
|
1232
|
+
// Variable assignment: x = val
|
|
1233
|
+
if let Expr::Ident { name, .. } = &left {
|
|
1234
|
+
let name = Arc::clone(name);
|
|
1235
|
+
self.advance(); // =
|
|
1236
|
+
let value = self.parse_assign()?;
|
|
1237
|
+
let end = value.span().end;
|
|
1238
|
+
return Ok(Expr::Assign {
|
|
1239
|
+
name,
|
|
1240
|
+
value: Box::new(value),
|
|
1241
|
+
span: Span { start, end },
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
// Member assignment: obj.prop = val
|
|
1246
|
+
if let Expr::Member {
|
|
1247
|
+
object,
|
|
1248
|
+
prop: MemberProp::Name {
|
|
1249
|
+
name: prop_name, ..
|
|
1250
|
+
},
|
|
1251
|
+
..
|
|
1252
|
+
} = &left
|
|
1253
|
+
{
|
|
1254
|
+
let object = Box::clone(object);
|
|
1255
|
+
let prop = Arc::clone(prop_name);
|
|
1256
|
+
self.advance(); // =
|
|
1257
|
+
let value = self.parse_assign()?;
|
|
1258
|
+
let end = value.span().end;
|
|
1259
|
+
return Ok(Expr::MemberAssign {
|
|
1260
|
+
object,
|
|
1261
|
+
prop,
|
|
1262
|
+
value: Box::new(value),
|
|
1263
|
+
span: Span { start, end },
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
// Index assignment: arr[idx] = val or obj[key] = val
|
|
1268
|
+
if let Expr::Index { object, index, .. } = &left {
|
|
1269
|
+
let object = Box::clone(object);
|
|
1270
|
+
let index = Box::clone(index);
|
|
1271
|
+
self.advance(); // =
|
|
1272
|
+
let value = self.parse_assign()?;
|
|
1273
|
+
let end = value.span().end;
|
|
1274
|
+
return Ok(Expr::IndexAssign {
|
|
1275
|
+
object,
|
|
1276
|
+
index,
|
|
1277
|
+
value: Box::new(value),
|
|
1278
|
+
span: Span { start, end },
|
|
1279
|
+
});
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
// Check for compound assignment (+=, -=, *=, /=, %=)
|
|
1284
|
+
let compound_op = match self.peek_kind() {
|
|
1285
|
+
Some(TokenKind::PlusAssign) => Some(CompoundOp::Add),
|
|
1286
|
+
Some(TokenKind::MinusAssign) => Some(CompoundOp::Sub),
|
|
1287
|
+
Some(TokenKind::StarAssign) => Some(CompoundOp::Mul),
|
|
1288
|
+
Some(TokenKind::SlashAssign) => Some(CompoundOp::Div),
|
|
1289
|
+
Some(TokenKind::PercentAssign) => Some(CompoundOp::Mod),
|
|
1290
|
+
_ => None,
|
|
1291
|
+
};
|
|
1292
|
+
|
|
1293
|
+
if let Some(op) = compound_op {
|
|
1294
|
+
if let Expr::Ident { name, span } = &left {
|
|
1295
|
+
let name = Arc::clone(name);
|
|
1296
|
+
let start = span.start;
|
|
1297
|
+
self.advance(); // consume the compound operator
|
|
1298
|
+
let value = self.parse_assign()?;
|
|
1299
|
+
let end = value.span().end;
|
|
1300
|
+
return Ok(Expr::CompoundAssign {
|
|
1301
|
+
name,
|
|
1302
|
+
op,
|
|
1303
|
+
value: Box::new(value),
|
|
1304
|
+
span: Span { start, end },
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// Check for logical assignment (&&=, ||=, ??=)
|
|
1310
|
+
let logical_op = match self.peek_kind() {
|
|
1311
|
+
Some(TokenKind::AndAndAssign) => Some(LogicalAssignOp::AndAnd),
|
|
1312
|
+
Some(TokenKind::OrOrAssign) => Some(LogicalAssignOp::OrOr),
|
|
1313
|
+
Some(TokenKind::NullishAssign) => Some(LogicalAssignOp::Nullish),
|
|
1314
|
+
_ => None,
|
|
1315
|
+
};
|
|
1316
|
+
|
|
1317
|
+
if let Some(op) = logical_op {
|
|
1318
|
+
if let Expr::Ident { name, span } = &left {
|
|
1319
|
+
let name = Arc::clone(name);
|
|
1320
|
+
let start = span.start;
|
|
1321
|
+
self.advance(); // consume the logical assign operator
|
|
1322
|
+
let value = self.parse_assign()?;
|
|
1323
|
+
let end = value.span().end;
|
|
1324
|
+
return Ok(Expr::LogicalAssign {
|
|
1325
|
+
name,
|
|
1326
|
+
op,
|
|
1327
|
+
value: Box::new(value),
|
|
1328
|
+
span: Span { start, end },
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
Ok(left)
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
fn parse_conditional(&mut self) -> Result<Expr, String> {
|
|
1337
|
+
let cond = self.parse_nullish_coalesce()?;
|
|
1338
|
+
if matches!(self.peek_kind(), Some(TokenKind::Question)) {
|
|
1339
|
+
let start = cond.span().start;
|
|
1340
|
+
self.advance(); // ?
|
|
1341
|
+
let then_branch = self.parse_expr()?;
|
|
1342
|
+
self.expect(TokenKind::Colon)?;
|
|
1343
|
+
let else_branch = self.parse_conditional()?; // right-associative
|
|
1344
|
+
let end = else_branch.span().end;
|
|
1345
|
+
Ok(Expr::Conditional {
|
|
1346
|
+
cond: Box::new(cond),
|
|
1347
|
+
then_branch: Box::new(then_branch),
|
|
1348
|
+
else_branch: Box::new(else_branch),
|
|
1349
|
+
span: Span { start, end },
|
|
1350
|
+
})
|
|
1351
|
+
} else {
|
|
1352
|
+
Ok(cond)
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
fn parse_nullish_coalesce(&mut self) -> Result<Expr, String> {
|
|
1357
|
+
let mut left = self.parse_or()?;
|
|
1358
|
+
while matches!(self.peek_kind(), Some(TokenKind::NullishCoalesce)) {
|
|
1359
|
+
self.advance();
|
|
1360
|
+
let right = self.parse_or()?;
|
|
1361
|
+
let start = left.span().start;
|
|
1362
|
+
let end = right.span().end;
|
|
1363
|
+
left = Expr::NullishCoalesce {
|
|
1364
|
+
left: Box::new(left),
|
|
1365
|
+
right: Box::new(right),
|
|
1366
|
+
span: Span { start, end },
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
Ok(left)
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// Binary operators generated by macros to reduce duplication
|
|
1373
|
+
binary_single_op!(parse_or, parse_and, Or, BinOp::Or);
|
|
1374
|
+
binary_single_op!(parse_and, parse_bit_or, And, BinOp::And);
|
|
1375
|
+
binary_single_op!(parse_bit_or, parse_bit_xor, BitOr, BinOp::BitOr);
|
|
1376
|
+
binary_single_op!(parse_bit_xor, parse_bit_and, BitXor, BinOp::BitXor);
|
|
1377
|
+
binary_single_op!(parse_bit_and, parse_shift, BitAnd, BinOp::BitAnd);
|
|
1378
|
+
|
|
1379
|
+
binary_multi_op!(parse_shift, parse_equality, Shl => BinOp::Shl, Shr => BinOp::Shr);
|
|
1380
|
+
binary_multi_op!(parse_equality, parse_comparison,
|
|
1381
|
+
StrictEq => BinOp::StrictEq, StrictNe => BinOp::StrictNe,
|
|
1382
|
+
Eq => BinOp::Eq, Ne => BinOp::Ne);
|
|
1383
|
+
binary_multi_op!(parse_comparison, parse_term,
|
|
1384
|
+
Lt => BinOp::Lt, Le => BinOp::Le, Gt => BinOp::Gt, Ge => BinOp::Ge, In => BinOp::In);
|
|
1385
|
+
binary_multi_op!(parse_term, parse_factor, Plus => BinOp::Add, Minus => BinOp::Sub);
|
|
1386
|
+
binary_multi_op!(parse_factor, parse_pow, Star => BinOp::Mul, Slash => BinOp::Div, Percent => BinOp::Mod);
|
|
1387
|
+
|
|
1388
|
+
fn parse_pow(&mut self) -> Result<Expr, String> {
|
|
1389
|
+
let left = self.parse_unary()?;
|
|
1390
|
+
if matches!(self.peek_kind(), Some(TokenKind::StarStar)) {
|
|
1391
|
+
self.advance();
|
|
1392
|
+
let right = self.parse_pow()?; // right-associative
|
|
1393
|
+
let start = left.span().start;
|
|
1394
|
+
let end = right.span().end;
|
|
1395
|
+
return Ok(Expr::Binary {
|
|
1396
|
+
left: Box::new(left),
|
|
1397
|
+
op: BinOp::Pow,
|
|
1398
|
+
right: Box::new(right),
|
|
1399
|
+
span: Span { start, end },
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
Ok(left)
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
fn parse_unary(&mut self) -> Result<Expr, String> {
|
|
1406
|
+
// Handle prefix ++/-- (consolidated)
|
|
1407
|
+
if let Some(is_inc) = match self.peek_kind() {
|
|
1408
|
+
Some(TokenKind::PlusPlus) => Some(true),
|
|
1409
|
+
Some(TokenKind::MinusMinus) => Some(false),
|
|
1410
|
+
_ => None,
|
|
1411
|
+
} {
|
|
1412
|
+
let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
1413
|
+
self.advance();
|
|
1414
|
+
let operand = self.parse_unary()?;
|
|
1415
|
+
if let Expr::Ident { name, span } = &operand {
|
|
1416
|
+
let name = Arc::clone(name);
|
|
1417
|
+
let span = Span {
|
|
1418
|
+
start: span_start,
|
|
1419
|
+
end: span.end,
|
|
1420
|
+
};
|
|
1421
|
+
return Ok(if is_inc {
|
|
1422
|
+
Expr::PrefixInc { name, span }
|
|
1423
|
+
} else {
|
|
1424
|
+
Expr::PrefixDec { name, span }
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
return Err(format!(
|
|
1428
|
+
"Prefix {} requires an identifier",
|
|
1429
|
+
if is_inc { "++" } else { "--" }
|
|
1430
|
+
));
|
|
1431
|
+
}
|
|
1432
|
+
let op = match self.peek_kind() {
|
|
1433
|
+
Some(TokenKind::Not) => UnaryOp::Not,
|
|
1434
|
+
Some(TokenKind::Minus) => UnaryOp::Neg,
|
|
1435
|
+
Some(TokenKind::Plus) => UnaryOp::Pos,
|
|
1436
|
+
Some(TokenKind::BitNot) => UnaryOp::BitNot,
|
|
1437
|
+
Some(TokenKind::Void) => UnaryOp::Void,
|
|
1438
|
+
Some(TokenKind::TypeOf) => {
|
|
1439
|
+
let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
1440
|
+
self.advance();
|
|
1441
|
+
let operand = self.parse_unary()?;
|
|
1442
|
+
let end = operand.span().end;
|
|
1443
|
+
return Ok(Expr::TypeOf {
|
|
1444
|
+
operand: Box::new(operand),
|
|
1445
|
+
span: Span {
|
|
1446
|
+
start: span_start,
|
|
1447
|
+
end,
|
|
1448
|
+
},
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
Some(TokenKind::Await) => {
|
|
1452
|
+
let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
1453
|
+
self.advance();
|
|
1454
|
+
let operand = self.parse_unary()?;
|
|
1455
|
+
let end = operand.span().end;
|
|
1456
|
+
return Ok(Expr::Await {
|
|
1457
|
+
operand: Box::new(operand),
|
|
1458
|
+
span: Span {
|
|
1459
|
+
start: span_start,
|
|
1460
|
+
end,
|
|
1461
|
+
},
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
_ => return self.parse_postfix(),
|
|
1465
|
+
};
|
|
1466
|
+
let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
1467
|
+
self.advance();
|
|
1468
|
+
let operand = self.parse_unary()?;
|
|
1469
|
+
let end = operand.span().end;
|
|
1470
|
+
Ok(Expr::Unary {
|
|
1471
|
+
op,
|
|
1472
|
+
operand: Box::new(operand),
|
|
1473
|
+
span: Span {
|
|
1474
|
+
start: span_start,
|
|
1475
|
+
end,
|
|
1476
|
+
},
|
|
1477
|
+
})
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
/// Member chain (`.`, `?.`, `[]`) without consuming a call `(...)`.
|
|
1481
|
+
fn parse_member_expression_no_call(&mut self) -> Result<Expr, String> {
|
|
1482
|
+
let mut expr = self.parse_primary()?;
|
|
1483
|
+
while let Some(kind) = self.peek_kind() {
|
|
1484
|
+
match kind {
|
|
1485
|
+
TokenKind::Dot | TokenKind::OptionalChain => {
|
|
1486
|
+
let optional = kind == TokenKind::OptionalChain;
|
|
1487
|
+
self.advance();
|
|
1488
|
+
let prop_tok = self.expect_ident_or_type_member_name()?;
|
|
1489
|
+
let prop = prop_tok.literal.clone().ok_or("Expected property name")?;
|
|
1490
|
+
let prop_span = Span {
|
|
1491
|
+
start: prop_tok.span.start,
|
|
1492
|
+
end: prop_tok.span.end,
|
|
1493
|
+
};
|
|
1494
|
+
let start = expr.span().start;
|
|
1495
|
+
let end = self.peek().map(|x| x.span.start).unwrap_or(start);
|
|
1496
|
+
expr = Expr::Member {
|
|
1497
|
+
object: Box::new(expr),
|
|
1498
|
+
prop: MemberProp::Name {
|
|
1499
|
+
name: prop,
|
|
1500
|
+
span: prop_span,
|
|
1501
|
+
},
|
|
1502
|
+
optional,
|
|
1503
|
+
span: Span { start, end },
|
|
1504
|
+
};
|
|
1505
|
+
}
|
|
1506
|
+
TokenKind::LBracket => {
|
|
1507
|
+
self.advance();
|
|
1508
|
+
let index = self.parse_expr()?;
|
|
1509
|
+
self.expect(TokenKind::RBracket)?;
|
|
1510
|
+
let start = expr.span().start;
|
|
1511
|
+
let end = self.peek().map(|x| x.span.start).unwrap_or(start);
|
|
1512
|
+
expr = Expr::Index {
|
|
1513
|
+
object: Box::new(expr),
|
|
1514
|
+
index: Box::new(index),
|
|
1515
|
+
optional: false,
|
|
1516
|
+
span: Span { start, end },
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
_ => break,
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
Ok(expr)
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
/// ECMAScript `NewExpression`: `new` chains, then member expression without call, optional `(...)`.
|
|
1526
|
+
fn parse_new_expression(&mut self) -> Result<Expr, String> {
|
|
1527
|
+
if matches!(self.peek_kind(), Some(TokenKind::New)) {
|
|
1528
|
+
let span_start = self.peek().map(|t| t.span.start).unwrap_or((0, 0));
|
|
1529
|
+
self.advance();
|
|
1530
|
+
let callee = Box::new(self.parse_new_expression()?);
|
|
1531
|
+
let args = if matches!(self.peek_kind(), Some(TokenKind::LParen)) {
|
|
1532
|
+
self.advance();
|
|
1533
|
+
let mut args = Vec::new();
|
|
1534
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1535
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
1536
|
+
self.advance();
|
|
1537
|
+
let arg_expr = self.parse_expr()?;
|
|
1538
|
+
args.push(CallArg::Spread(arg_expr));
|
|
1539
|
+
} else {
|
|
1540
|
+
let arg_expr = self.parse_expr()?;
|
|
1541
|
+
args.push(CallArg::Expr(arg_expr));
|
|
1542
|
+
}
|
|
1543
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1544
|
+
self.expect(TokenKind::Comma)?;
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
self.expect(TokenKind::RParen)?;
|
|
1548
|
+
args
|
|
1549
|
+
} else {
|
|
1550
|
+
Vec::new()
|
|
1551
|
+
};
|
|
1552
|
+
let end = self
|
|
1553
|
+
.peek()
|
|
1554
|
+
.map(|x| x.span.start)
|
|
1555
|
+
.unwrap_or(callee.as_ref().span().end);
|
|
1556
|
+
Ok(Expr::New {
|
|
1557
|
+
callee,
|
|
1558
|
+
args,
|
|
1559
|
+
span: Span {
|
|
1560
|
+
start: span_start,
|
|
1561
|
+
end,
|
|
1562
|
+
},
|
|
1563
|
+
})
|
|
1564
|
+
} else {
|
|
1565
|
+
self.parse_member_expression_no_call()
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
fn parse_postfix(&mut self) -> Result<Expr, String> {
|
|
1570
|
+
let mut expr = if matches!(self.peek_kind(), Some(TokenKind::New)) {
|
|
1571
|
+
self.parse_new_expression()?
|
|
1572
|
+
} else {
|
|
1573
|
+
self.parse_primary()?
|
|
1574
|
+
};
|
|
1575
|
+
while let Some(kind) = self.peek_kind() {
|
|
1576
|
+
match kind {
|
|
1577
|
+
TokenKind::LParen => {
|
|
1578
|
+
self.advance();
|
|
1579
|
+
let mut args = Vec::new();
|
|
1580
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1581
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
1582
|
+
self.advance();
|
|
1583
|
+
let arg_expr = self.parse_expr()?;
|
|
1584
|
+
args.push(CallArg::Spread(arg_expr));
|
|
1585
|
+
} else {
|
|
1586
|
+
let arg_expr = self.parse_expr()?;
|
|
1587
|
+
args.push(CallArg::Expr(arg_expr));
|
|
1588
|
+
}
|
|
1589
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1590
|
+
self.expect(TokenKind::Comma)?;
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
self.expect(TokenKind::RParen)?;
|
|
1594
|
+
let start = expr.span().start;
|
|
1595
|
+
let end = self.peek().map(|x| x.span.start).unwrap_or(start);
|
|
1596
|
+
expr = Expr::Call {
|
|
1597
|
+
callee: Box::new(expr),
|
|
1598
|
+
args,
|
|
1599
|
+
span: Span { start, end },
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
TokenKind::Dot | TokenKind::OptionalChain => {
|
|
1603
|
+
let optional = kind == TokenKind::OptionalChain;
|
|
1604
|
+
self.advance();
|
|
1605
|
+
let prop_tok = self.expect_ident_or_type_member_name()?;
|
|
1606
|
+
let prop = prop_tok.literal.clone().ok_or("Expected property name")?;
|
|
1607
|
+
let prop_span = Span {
|
|
1608
|
+
start: prop_tok.span.start,
|
|
1609
|
+
end: prop_tok.span.end,
|
|
1610
|
+
};
|
|
1611
|
+
let start = expr.span().start;
|
|
1612
|
+
let end = self.peek().map(|x| x.span.start).unwrap_or(start);
|
|
1613
|
+
expr = Expr::Member {
|
|
1614
|
+
object: Box::new(expr),
|
|
1615
|
+
prop: MemberProp::Name {
|
|
1616
|
+
name: prop,
|
|
1617
|
+
span: prop_span,
|
|
1618
|
+
},
|
|
1619
|
+
optional,
|
|
1620
|
+
span: Span { start, end },
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1623
|
+
TokenKind::LBracket => {
|
|
1624
|
+
self.advance();
|
|
1625
|
+
let index = self.parse_expr()?;
|
|
1626
|
+
self.expect(TokenKind::RBracket)?;
|
|
1627
|
+
let start = expr.span().start;
|
|
1628
|
+
let end = self.peek().map(|x| x.span.start).unwrap_or(start);
|
|
1629
|
+
expr = Expr::Index {
|
|
1630
|
+
object: Box::new(expr),
|
|
1631
|
+
index: Box::new(index),
|
|
1632
|
+
optional: false,
|
|
1633
|
+
span: Span { start, end },
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
TokenKind::PlusPlus | TokenKind::MinusMinus => {
|
|
1637
|
+
if let Expr::Ident {
|
|
1638
|
+
name,
|
|
1639
|
+
span: ident_span,
|
|
1640
|
+
} = &expr
|
|
1641
|
+
{
|
|
1642
|
+
let name = Arc::clone(name);
|
|
1643
|
+
let is_inc = kind == TokenKind::PlusPlus;
|
|
1644
|
+
let tok = self.advance().ok_or("Unexpected EOF")?;
|
|
1645
|
+
let span = Span {
|
|
1646
|
+
start: ident_span.start,
|
|
1647
|
+
end: tok.span.end,
|
|
1648
|
+
};
|
|
1649
|
+
expr = if is_inc {
|
|
1650
|
+
Expr::PostfixInc { name, span }
|
|
1651
|
+
} else {
|
|
1652
|
+
Expr::PostfixDec { name, span }
|
|
1653
|
+
};
|
|
1654
|
+
} else {
|
|
1655
|
+
break;
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
TokenKind::Question => {
|
|
1659
|
+
// Ternary is parsed in parse_conditional for correct precedence
|
|
1660
|
+
break;
|
|
1661
|
+
}
|
|
1662
|
+
_ => break,
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
Ok(expr)
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
fn parse_primary(&mut self) -> Result<Expr, String> {
|
|
1669
|
+
let t = self.advance().ok_or("Unexpected EOF")?;
|
|
1670
|
+
let span = Span {
|
|
1671
|
+
start: t.span.start,
|
|
1672
|
+
end: t.span.end,
|
|
1673
|
+
};
|
|
1674
|
+
match t.kind {
|
|
1675
|
+
TokenKind::Number => {
|
|
1676
|
+
let s = t.literal.as_ref().ok_or("Expected number")?;
|
|
1677
|
+
let n: f64 = s.parse().map_err(|_| format!("Invalid number: {}", s))?;
|
|
1678
|
+
Ok(Expr::Literal {
|
|
1679
|
+
value: Literal::Number(n),
|
|
1680
|
+
span,
|
|
1681
|
+
})
|
|
1682
|
+
}
|
|
1683
|
+
TokenKind::String => {
|
|
1684
|
+
let s = t.literal.clone().ok_or("Expected string")?;
|
|
1685
|
+
Ok(Expr::Literal {
|
|
1686
|
+
value: Literal::String(s),
|
|
1687
|
+
span,
|
|
1688
|
+
})
|
|
1689
|
+
}
|
|
1690
|
+
TokenKind::True => Ok(Expr::Literal {
|
|
1691
|
+
value: Literal::Bool(true),
|
|
1692
|
+
span,
|
|
1693
|
+
}),
|
|
1694
|
+
TokenKind::False => Ok(Expr::Literal {
|
|
1695
|
+
value: Literal::Bool(false),
|
|
1696
|
+
span,
|
|
1697
|
+
}),
|
|
1698
|
+
TokenKind::Null => Ok(Expr::Literal {
|
|
1699
|
+
value: Literal::Null,
|
|
1700
|
+
span,
|
|
1701
|
+
}),
|
|
1702
|
+
TokenKind::Ident => {
|
|
1703
|
+
let name = t.literal.clone().ok_or("Expected ident")?;
|
|
1704
|
+
// Check if this is a single-param arrow function: x => ...
|
|
1705
|
+
if matches!(self.peek_kind(), Some(TokenKind::Arrow)) {
|
|
1706
|
+
self.advance(); // consume =>
|
|
1707
|
+
let body = self.parse_arrow_body()?;
|
|
1708
|
+
let end = self.previous_span_end();
|
|
1709
|
+
return Ok(Expr::ArrowFunction {
|
|
1710
|
+
params: vec![FunParam::Simple(TypedParam {
|
|
1711
|
+
name: name.clone(),
|
|
1712
|
+
name_span: span,
|
|
1713
|
+
type_ann: None,
|
|
1714
|
+
default: None,
|
|
1715
|
+
})],
|
|
1716
|
+
body,
|
|
1717
|
+
span: Span {
|
|
1718
|
+
start: span.start,
|
|
1719
|
+
end,
|
|
1720
|
+
},
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
Ok(Expr::Ident { name, span })
|
|
1724
|
+
}
|
|
1725
|
+
TokenKind::LParen => {
|
|
1726
|
+
// Check if this is an arrow function: (params) => ...
|
|
1727
|
+
if let Some(arrow_fn) = self.try_parse_arrow_function(&span)? {
|
|
1728
|
+
return Ok(arrow_fn);
|
|
1729
|
+
}
|
|
1730
|
+
// Otherwise it's a grouping expression
|
|
1731
|
+
let expr = self.parse_expr()?;
|
|
1732
|
+
self.expect(TokenKind::RParen)?;
|
|
1733
|
+
Ok(expr)
|
|
1734
|
+
}
|
|
1735
|
+
TokenKind::LBracket => {
|
|
1736
|
+
let mut elements = Vec::new();
|
|
1737
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
|
|
1738
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
1739
|
+
self.advance();
|
|
1740
|
+
let expr = self.parse_expr()?;
|
|
1741
|
+
elements.push(ArrayElement::Spread(expr));
|
|
1742
|
+
} else {
|
|
1743
|
+
let expr = self.parse_expr()?;
|
|
1744
|
+
elements.push(ArrayElement::Expr(expr));
|
|
1745
|
+
}
|
|
1746
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RBracket)) {
|
|
1747
|
+
self.expect(TokenKind::Comma)?;
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
self.expect(TokenKind::RBracket)?;
|
|
1751
|
+
Ok(Expr::Array {
|
|
1752
|
+
elements,
|
|
1753
|
+
span: Span {
|
|
1754
|
+
start: span.start,
|
|
1755
|
+
end: self.peek().map(|x| x.span.end).unwrap_or(span.end),
|
|
1756
|
+
},
|
|
1757
|
+
})
|
|
1758
|
+
}
|
|
1759
|
+
TokenKind::Lt => {
|
|
1760
|
+
// JSX: <Tag or <>
|
|
1761
|
+
match self.peek_kind() {
|
|
1762
|
+
Some(TokenKind::Ident) => self.parse_jsx_element(span.start),
|
|
1763
|
+
Some(TokenKind::Gt) => self.parse_jsx_fragment(span.start),
|
|
1764
|
+
_ => Err(format!(
|
|
1765
|
+
"Invalid JSX: expected tag name or <> after <, got {:?}",
|
|
1766
|
+
self.peek_kind()
|
|
1767
|
+
)),
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
TokenKind::LBrace => {
|
|
1771
|
+
let mut props = Vec::new();
|
|
1772
|
+
while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
1773
|
+
if matches!(self.peek_kind(), Some(TokenKind::Spread)) {
|
|
1774
|
+
self.advance();
|
|
1775
|
+
let expr = self.parse_expr()?;
|
|
1776
|
+
props.push(ObjectProp::Spread(expr));
|
|
1777
|
+
} else {
|
|
1778
|
+
let key_tok = self.advance().ok_or("Expected object key")?;
|
|
1779
|
+
let (key, key_span, is_ident_key) = match key_tok.kind {
|
|
1780
|
+
TokenKind::Ident | TokenKind::Type | TokenKind::Declare => {
|
|
1781
|
+
let k = key_tok.literal.clone().ok_or("Expected key")?;
|
|
1782
|
+
let sp = Span {
|
|
1783
|
+
start: key_tok.span.start,
|
|
1784
|
+
end: key_tok.span.end,
|
|
1785
|
+
};
|
|
1786
|
+
(k, sp, true)
|
|
1787
|
+
}
|
|
1788
|
+
TokenKind::String => {
|
|
1789
|
+
let k = key_tok.literal.clone().ok_or("Expected string key")?;
|
|
1790
|
+
let sp = Span {
|
|
1791
|
+
start: key_tok.span.start,
|
|
1792
|
+
end: key_tok.span.end,
|
|
1793
|
+
};
|
|
1794
|
+
(k, sp, false)
|
|
1795
|
+
}
|
|
1796
|
+
_ => {
|
|
1797
|
+
return Err(format!(
|
|
1798
|
+
"Expected object key (ident or string), got {:?}",
|
|
1799
|
+
key_tok.kind
|
|
1800
|
+
))
|
|
1801
|
+
}
|
|
1802
|
+
};
|
|
1803
|
+
let value = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
|
|
1804
|
+
self.expect(TokenKind::Colon)?;
|
|
1805
|
+
self.parse_expr()?
|
|
1806
|
+
} else {
|
|
1807
|
+
// ES6 shorthand: { key } => { key: key } (ident only, not string keys)
|
|
1808
|
+
if is_ident_key {
|
|
1809
|
+
Expr::Ident {
|
|
1810
|
+
name: key.clone(),
|
|
1811
|
+
span: key_span,
|
|
1812
|
+
}
|
|
1813
|
+
} else {
|
|
1814
|
+
return Err("String key in object literal requires explicit value (key: value)".to_string());
|
|
1815
|
+
}
|
|
1816
|
+
};
|
|
1817
|
+
props.push(ObjectProp::KeyValue(key, value));
|
|
1818
|
+
}
|
|
1819
|
+
if !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
|
|
1820
|
+
self.expect(TokenKind::Comma)?;
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
self.expect(TokenKind::RBrace)?;
|
|
1824
|
+
Ok(Expr::Object {
|
|
1825
|
+
props,
|
|
1826
|
+
span: Span {
|
|
1827
|
+
start: span.start,
|
|
1828
|
+
end: self.peek().map(|x| x.span.end).unwrap_or(span.end),
|
|
1829
|
+
},
|
|
1830
|
+
})
|
|
1831
|
+
}
|
|
1832
|
+
TokenKind::TemplateNoSub => {
|
|
1833
|
+
// Simple template literal without interpolation
|
|
1834
|
+
Ok(Expr::TemplateLiteral {
|
|
1835
|
+
quasis: vec![t.literal.clone().unwrap_or_default()],
|
|
1836
|
+
exprs: vec![],
|
|
1837
|
+
span,
|
|
1838
|
+
})
|
|
1839
|
+
}
|
|
1840
|
+
TokenKind::TemplateHead => {
|
|
1841
|
+
// Template literal with interpolation: `text${
|
|
1842
|
+
let mut quasis = vec![t.literal.clone().unwrap_or_default()];
|
|
1843
|
+
let mut exprs = Vec::new();
|
|
1844
|
+
|
|
1845
|
+
loop {
|
|
1846
|
+
// Parse the expression inside ${}
|
|
1847
|
+
let expr = self.parse_expr()?;
|
|
1848
|
+
exprs.push(expr);
|
|
1849
|
+
|
|
1850
|
+
// Next token should be TemplateMiddle or TemplateTail
|
|
1851
|
+
let next = self.advance().ok_or("Unexpected EOF in template literal")?;
|
|
1852
|
+
match next.kind {
|
|
1853
|
+
TokenKind::TemplateTail => {
|
|
1854
|
+
quasis.push(next.literal.clone().unwrap_or_default());
|
|
1855
|
+
let end = self.previous_span_end();
|
|
1856
|
+
return Ok(Expr::TemplateLiteral {
|
|
1857
|
+
quasis,
|
|
1858
|
+
exprs,
|
|
1859
|
+
span: Span {
|
|
1860
|
+
start: span.start,
|
|
1861
|
+
end,
|
|
1862
|
+
},
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
TokenKind::TemplateMiddle => {
|
|
1866
|
+
quasis.push(next.literal.clone().unwrap_or_default());
|
|
1867
|
+
// Continue parsing more expressions
|
|
1868
|
+
}
|
|
1869
|
+
_ => {
|
|
1870
|
+
return Err(format!(
|
|
1871
|
+
"Expected template continuation, got {:?}",
|
|
1872
|
+
next.kind
|
|
1873
|
+
))
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
_ => Err(format!("Unexpected token: {:?}", t.kind)),
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
/// Try to parse an arrow function starting with '(' already consumed.
|
|
1883
|
+
/// Returns Some(Expr) if successful, None if it's not an arrow function.
|
|
1884
|
+
fn try_parse_arrow_function(&mut self, start_span: &Span) -> Result<Option<Expr>, String> {
|
|
1885
|
+
// Save position for backtracking
|
|
1886
|
+
let saved_pos = self.pos;
|
|
1887
|
+
|
|
1888
|
+
// Try to parse as arrow function params
|
|
1889
|
+
let mut params = Vec::new();
|
|
1890
|
+
let mut is_arrow = false;
|
|
1891
|
+
|
|
1892
|
+
// Check for empty params: () => ...
|
|
1893
|
+
if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1894
|
+
self.advance(); // consume )
|
|
1895
|
+
if matches!(self.peek_kind(), Some(TokenKind::Arrow)) {
|
|
1896
|
+
self.advance(); // consume =>
|
|
1897
|
+
is_arrow = true;
|
|
1898
|
+
}
|
|
1899
|
+
} else {
|
|
1900
|
+
// Try to parse params: (x, y), ({ a }), ([a, b]), with optional types/defaults
|
|
1901
|
+
let mut params_ok = true;
|
|
1902
|
+
loop {
|
|
1903
|
+
if matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1904
|
+
break;
|
|
1905
|
+
}
|
|
1906
|
+
match self.parse_fun_param() {
|
|
1907
|
+
Ok(param) => params.push(param),
|
|
1908
|
+
Err(_) => {
|
|
1909
|
+
params_ok = false;
|
|
1910
|
+
break;
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
if matches!(self.peek_kind(), Some(TokenKind::Comma)) {
|
|
1914
|
+
self.advance();
|
|
1915
|
+
} else {
|
|
1916
|
+
break;
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
if params_ok && matches!(self.peek_kind(), Some(TokenKind::RParen)) {
|
|
1920
|
+
self.advance(); // consume )
|
|
1921
|
+
if matches!(self.peek_kind(), Some(TokenKind::Arrow)) {
|
|
1922
|
+
self.advance(); // consume =>
|
|
1923
|
+
is_arrow = true;
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
if !is_arrow {
|
|
1929
|
+
// Backtrack - it's not an arrow function
|
|
1930
|
+
self.pos = saved_pos;
|
|
1931
|
+
return Ok(None);
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
let body = self.parse_arrow_body()?;
|
|
1935
|
+
let end = self.previous_span_end();
|
|
1936
|
+
|
|
1937
|
+
Ok(Some(Expr::ArrowFunction {
|
|
1938
|
+
params,
|
|
1939
|
+
body,
|
|
1940
|
+
span: Span {
|
|
1941
|
+
start: start_span.start,
|
|
1942
|
+
end,
|
|
1943
|
+
},
|
|
1944
|
+
}))
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
/// Parse the body of an arrow function (either expression or block)
|
|
1948
|
+
fn parse_arrow_body(&mut self) -> Result<ArrowBody, String> {
|
|
1949
|
+
if matches!(self.peek_kind(), Some(TokenKind::LBrace)) {
|
|
1950
|
+
// Block body
|
|
1951
|
+
let block = self.parse_block()?;
|
|
1952
|
+
Ok(ArrowBody::Block(Box::new(block)))
|
|
1953
|
+
} else {
|
|
1954
|
+
// Expression body
|
|
1955
|
+
let expr = self.parse_expr()?;
|
|
1956
|
+
Ok(ArrowBody::Expr(Box::new(expr)))
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
fn previous_span_end(&self) -> (usize, usize) {
|
|
1961
|
+
if self.pos > 0 && self.pos <= self.tokens.len() {
|
|
1962
|
+
self.tokens[self.pos - 1].span.end
|
|
1963
|
+
} else {
|
|
1964
|
+
(1, 1)
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
/// Parse JSX element: <Tag props>children</Tag> or <Tag props />
|
|
1969
|
+
/// Caller has already consumed <.
|
|
1970
|
+
fn parse_jsx_element(&mut self, start: (usize, usize)) -> Result<Expr, String> {
|
|
1971
|
+
let tag_tok = self.expect(TokenKind::Ident)?;
|
|
1972
|
+
let tag = tag_tok.literal.clone().ok_or("Expected tag name")?;
|
|
1973
|
+
|
|
1974
|
+
let mut props = Vec::new();
|
|
1975
|
+
loop {
|
|
1976
|
+
match self.peek_kind() {
|
|
1977
|
+
Some(TokenKind::Slash) => {
|
|
1978
|
+
// Self-closing: />
|
|
1979
|
+
self.advance();
|
|
1980
|
+
self.expect(TokenKind::Gt)?;
|
|
1981
|
+
let end = self.previous_span_end();
|
|
1982
|
+
return Ok(Expr::JsxElement {
|
|
1983
|
+
tag,
|
|
1984
|
+
props,
|
|
1985
|
+
children: vec![],
|
|
1986
|
+
span: Span { start, end },
|
|
1987
|
+
});
|
|
1988
|
+
}
|
|
1989
|
+
Some(TokenKind::Gt) => break,
|
|
1990
|
+
Some(TokenKind::Spread) => {
|
|
1991
|
+
self.advance(); // ...
|
|
1992
|
+
let expr = self.parse_expr()?;
|
|
1993
|
+
self.expect(TokenKind::RBrace)?; // }
|
|
1994
|
+
props.push(JsxProp::Spread(expr));
|
|
1995
|
+
}
|
|
1996
|
+
// `type` is `TokenKind::Type` but valid as a JSX attr name; see docs/js-emit-philosophy.md.
|
|
1997
|
+
Some(TokenKind::Ident) | Some(TokenKind::Type) => {
|
|
1998
|
+
let name_tok = self.advance().unwrap();
|
|
1999
|
+
let name = name_tok.literal.clone().ok_or("Expected attr name")?;
|
|
2000
|
+
if matches!(self.peek_kind(), Some(TokenKind::Assign)) {
|
|
2001
|
+
self.advance(); // =
|
|
2002
|
+
let value = if matches!(self.peek_kind(), Some(TokenKind::LBrace)) {
|
|
2003
|
+
self.advance(); // {
|
|
2004
|
+
let expr = self.parse_expr()?;
|
|
2005
|
+
self.expect(TokenKind::RBrace)?; // }
|
|
2006
|
+
JsxAttrValue::Expr(expr)
|
|
2007
|
+
} else {
|
|
2008
|
+
let s = self
|
|
2009
|
+
.expect(TokenKind::String)?
|
|
2010
|
+
.literal
|
|
2011
|
+
.clone()
|
|
2012
|
+
.ok_or("Expected string")?;
|
|
2013
|
+
JsxAttrValue::String(s)
|
|
2014
|
+
};
|
|
2015
|
+
props.push(JsxProp::Attr { name, value });
|
|
2016
|
+
} else {
|
|
2017
|
+
props.push(JsxProp::Attr {
|
|
2018
|
+
name,
|
|
2019
|
+
value: JsxAttrValue::ImplicitTrue,
|
|
2020
|
+
});
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
_ => {
|
|
2024
|
+
return Err(format!(
|
|
2025
|
+
"Unexpected token in JSX props: {:?}",
|
|
2026
|
+
self.peek_kind()
|
|
2027
|
+
))
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
self.advance(); // consume >
|
|
2032
|
+
|
|
2033
|
+
let children = self.parse_jsx_children(&tag)?;
|
|
2034
|
+
let end = self.previous_span_end();
|
|
2035
|
+
Ok(Expr::JsxElement {
|
|
2036
|
+
tag,
|
|
2037
|
+
props,
|
|
2038
|
+
children,
|
|
2039
|
+
span: Span { start, end },
|
|
2040
|
+
})
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
fn token_as_jsx_text(kind: TokenKind) -> Option<&'static str> {
|
|
2044
|
+
use TokenKind::*;
|
|
2045
|
+
match kind {
|
|
2046
|
+
Not => Some("!"),
|
|
2047
|
+
Question => Some("?"),
|
|
2048
|
+
Dot => Some("."),
|
|
2049
|
+
Comma => Some(","),
|
|
2050
|
+
Colon => Some(":"),
|
|
2051
|
+
Semicolon => Some(";"),
|
|
2052
|
+
Plus => Some("+"),
|
|
2053
|
+
Minus => Some("-"),
|
|
2054
|
+
Star => Some("*"),
|
|
2055
|
+
Slash => Some("/"),
|
|
2056
|
+
Percent => Some("%"),
|
|
2057
|
+
Eq | Assign => Some("="),
|
|
2058
|
+
Gt => Some(">"),
|
|
2059
|
+
Le => Some("<="),
|
|
2060
|
+
Ge => Some(">="),
|
|
2061
|
+
Ne => Some("!="),
|
|
2062
|
+
StrictEq => Some("==="),
|
|
2063
|
+
StrictNe => Some("!=="),
|
|
2064
|
+
BitAnd => Some("&"),
|
|
2065
|
+
BitOr => Some("|"),
|
|
2066
|
+
BitXor => Some("^"),
|
|
2067
|
+
BitNot => Some("~"),
|
|
2068
|
+
And => Some("&&"),
|
|
2069
|
+
Or => Some("||"),
|
|
2070
|
+
LParen => Some("("),
|
|
2071
|
+
RParen => Some(")"),
|
|
2072
|
+
LBracket => Some("["),
|
|
2073
|
+
RBracket => Some("]"),
|
|
2074
|
+
PlusPlus => Some("++"),
|
|
2075
|
+
MinusMinus => Some("--"),
|
|
2076
|
+
StarStar => Some("**"),
|
|
2077
|
+
Arrow => Some("=>"),
|
|
2078
|
+
OptionalChain => Some("?."),
|
|
2079
|
+
NullishCoalesce => Some("??"),
|
|
2080
|
+
Shl => Some("<<"),
|
|
2081
|
+
Shr => Some(">>"),
|
|
2082
|
+
_ => None,
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
/// Merge text. Add space between words (Ident/Number/String); no space before/after punctuation.
|
|
2087
|
+
fn push_or_merge_text(&self, children: &mut Vec<JsxChild>, s: Arc<str>, is_punctuation: bool) {
|
|
2088
|
+
if let Some(JsxChild::Text(prev)) = children.last() {
|
|
2089
|
+
let sep = if is_punctuation { "" } else { " " };
|
|
2090
|
+
let merged = format!("{}{}{}", prev.as_ref(), sep, s.as_ref());
|
|
2091
|
+
let last = children.len() - 1;
|
|
2092
|
+
children[last] = JsxChild::Text(Arc::from(merged.as_str()));
|
|
2093
|
+
} else {
|
|
2094
|
+
children.push(JsxChild::Text(s));
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
/// Parse JSX children until </Tag> or </>
|
|
2099
|
+
fn parse_jsx_children(&mut self, close_tag: &str) -> Result<Vec<JsxChild>, String> {
|
|
2100
|
+
let mut children = Vec::new();
|
|
2101
|
+
loop {
|
|
2102
|
+
match self.peek_kind() {
|
|
2103
|
+
None => return Err("Unexpected EOF in JSX".to_string()),
|
|
2104
|
+
Some(TokenKind::Lt) => {
|
|
2105
|
+
let next = self.tokens.get(self.pos + 1);
|
|
2106
|
+
if let Some(t) = next {
|
|
2107
|
+
if t.kind == TokenKind::Slash {
|
|
2108
|
+
// </ closing tag
|
|
2109
|
+
self.advance(); // <
|
|
2110
|
+
self.advance(); // /
|
|
2111
|
+
let name = self
|
|
2112
|
+
.expect(TokenKind::Ident)?
|
|
2113
|
+
.literal
|
|
2114
|
+
.clone()
|
|
2115
|
+
.ok_or("Expected tag name")?;
|
|
2116
|
+
if name.as_ref() != close_tag {
|
|
2117
|
+
return Err(format!(
|
|
2118
|
+
"Mismatched JSX tag: expected </{}> got </{}>",
|
|
2119
|
+
close_tag, name
|
|
2120
|
+
));
|
|
2121
|
+
}
|
|
2122
|
+
self.expect(TokenKind::Gt)?; // >
|
|
2123
|
+
return Ok(children);
|
|
2124
|
+
}
|
|
2125
|
+
if t.kind == TokenKind::Gt {
|
|
2126
|
+
return Err("Unexpected <> in JSX children".to_string());
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
// <Tag - nested element
|
|
2130
|
+
let nested_start = self.peek().unwrap().span.start;
|
|
2131
|
+
self.advance(); // <
|
|
2132
|
+
let elem = self.parse_jsx_element(nested_start)?;
|
|
2133
|
+
children.push(JsxChild::Expr(elem));
|
|
2134
|
+
}
|
|
2135
|
+
Some(TokenKind::LBrace) => {
|
|
2136
|
+
self.advance(); // {
|
|
2137
|
+
let expr = self.parse_expr()?;
|
|
2138
|
+
self.expect(TokenKind::RBrace)?; // }
|
|
2139
|
+
children.push(JsxChild::Expr(expr));
|
|
2140
|
+
}
|
|
2141
|
+
Some(TokenKind::JsxText) => {
|
|
2142
|
+
let t = self.advance().unwrap();
|
|
2143
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2144
|
+
if !s.is_empty() {
|
|
2145
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
Some(TokenKind::String) => {
|
|
2149
|
+
let t = self.advance().unwrap();
|
|
2150
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2151
|
+
if !s.is_empty() {
|
|
2152
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
Some(TokenKind::Number) => {
|
|
2156
|
+
let t = self.advance().unwrap();
|
|
2157
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2158
|
+
if !s.is_empty() {
|
|
2159
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
Some(TokenKind::Ident) => {
|
|
2163
|
+
let t = self.advance().unwrap();
|
|
2164
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2165
|
+
if !s.is_empty() {
|
|
2166
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
Some(k) => {
|
|
2170
|
+
if let Some(s) = Self::token_as_jsx_text(k) {
|
|
2171
|
+
self.advance();
|
|
2172
|
+
self.push_or_merge_text(&mut children, Arc::from(s), true);
|
|
2173
|
+
} else {
|
|
2174
|
+
return Err(format!("Unexpected token in JSX children: {:?}", k));
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
fn parse_jsx_fragment(&mut self, start: (usize, usize)) -> Result<Expr, String> {
|
|
2182
|
+
self.advance(); // consume >
|
|
2183
|
+
let mut children = Vec::new();
|
|
2184
|
+
loop {
|
|
2185
|
+
match self.peek_kind() {
|
|
2186
|
+
None => return Err("Unexpected EOF in JSX fragment".to_string()),
|
|
2187
|
+
Some(TokenKind::Lt) => {
|
|
2188
|
+
let next = self.tokens.get(self.pos + 1);
|
|
2189
|
+
if let Some(t) = next {
|
|
2190
|
+
if t.kind == TokenKind::Slash {
|
|
2191
|
+
// </
|
|
2192
|
+
let next2 = self.tokens.get(self.pos + 2);
|
|
2193
|
+
if let Some(t2) = next2 {
|
|
2194
|
+
if t2.kind == TokenKind::Gt {
|
|
2195
|
+
// </>
|
|
2196
|
+
self.advance();
|
|
2197
|
+
self.advance();
|
|
2198
|
+
self.advance();
|
|
2199
|
+
let end = self.previous_span_end();
|
|
2200
|
+
return Ok(Expr::JsxFragment {
|
|
2201
|
+
children,
|
|
2202
|
+
span: Span { start, end },
|
|
2203
|
+
});
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
return Err("Expected </> to close fragment".to_string());
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
let nested_start = self.peek().unwrap().span.start;
|
|
2210
|
+
self.advance(); // <
|
|
2211
|
+
let elem = self.parse_jsx_element(nested_start)?;
|
|
2212
|
+
children.push(JsxChild::Expr(elem));
|
|
2213
|
+
}
|
|
2214
|
+
Some(TokenKind::LBrace) => {
|
|
2215
|
+
self.advance();
|
|
2216
|
+
let expr = self.parse_expr()?;
|
|
2217
|
+
self.expect(TokenKind::RBrace)?;
|
|
2218
|
+
children.push(JsxChild::Expr(expr));
|
|
2219
|
+
}
|
|
2220
|
+
Some(TokenKind::JsxText) => {
|
|
2221
|
+
let t = self.advance().unwrap();
|
|
2222
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2223
|
+
if !s.is_empty() {
|
|
2224
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
Some(TokenKind::String) => {
|
|
2228
|
+
let t = self.advance().unwrap();
|
|
2229
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2230
|
+
if !s.is_empty() {
|
|
2231
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
Some(TokenKind::Number) => {
|
|
2235
|
+
let t = self.advance().unwrap();
|
|
2236
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2237
|
+
if !s.is_empty() {
|
|
2238
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
Some(TokenKind::Ident) => {
|
|
2242
|
+
let t = self.advance().unwrap();
|
|
2243
|
+
let s = t.literal.clone().unwrap_or_default();
|
|
2244
|
+
if !s.is_empty() {
|
|
2245
|
+
self.push_or_merge_text(&mut children, s, false);
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
Some(k) => {
|
|
2249
|
+
if let Some(s) = Self::token_as_jsx_text(k) {
|
|
2250
|
+
self.advance();
|
|
2251
|
+
self.push_or_merge_text(&mut children, Arc::from(s), true);
|
|
2252
|
+
} else {
|
|
2253
|
+
return Err(format!("Unexpected token in JSX fragment: {:?}", k));
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
// Helper to get span from Expr. Uses trait so ExprSpan is referenced.
|
|
2262
|
+
trait ExprSpan {
|
|
2263
|
+
fn span(&self) -> Span;
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
#[inline(always)]
|
|
2267
|
+
fn expr_span(e: &impl ExprSpan) -> Span {
|
|
2268
|
+
e.span()
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
impl ExprSpan for Expr {
|
|
2272
|
+
fn span(&self) -> Span {
|
|
2273
|
+
match self {
|
|
2274
|
+
Expr::Literal { span, .. } => *span,
|
|
2275
|
+
Expr::Ident { span, .. } => *span,
|
|
2276
|
+
Expr::Binary { span, .. } => *span,
|
|
2277
|
+
Expr::Unary { span, .. } => *span,
|
|
2278
|
+
Expr::Call { span, .. } => *span,
|
|
2279
|
+
Expr::New { span, .. } => *span,
|
|
2280
|
+
Expr::Member { span, .. } => *span,
|
|
2281
|
+
Expr::Index { span, .. } => *span,
|
|
2282
|
+
Expr::Conditional { span, .. } => *span,
|
|
2283
|
+
Expr::NullishCoalesce { span, .. } => *span,
|
|
2284
|
+
Expr::Array { span, .. } => *span,
|
|
2285
|
+
Expr::Object { span, .. } => *span,
|
|
2286
|
+
Expr::Assign { span, .. } => *span,
|
|
2287
|
+
Expr::TypeOf { span, .. } => *span,
|
|
2288
|
+
Expr::PostfixInc { span, .. } => *span,
|
|
2289
|
+
Expr::PostfixDec { span, .. } => *span,
|
|
2290
|
+
Expr::PrefixInc { span, .. } => *span,
|
|
2291
|
+
Expr::PrefixDec { span, .. } => *span,
|
|
2292
|
+
Expr::CompoundAssign { span, .. } => *span,
|
|
2293
|
+
Expr::LogicalAssign { span, .. } => *span,
|
|
2294
|
+
Expr::MemberAssign { span, .. } => *span,
|
|
2295
|
+
Expr::IndexAssign { span, .. } => *span,
|
|
2296
|
+
Expr::ArrowFunction { span, .. } => *span,
|
|
2297
|
+
Expr::TemplateLiteral { span, .. } => *span,
|
|
2298
|
+
Expr::Await { span, .. } => *span,
|
|
2299
|
+
Expr::JsxElement { span, .. } => *span,
|
|
2300
|
+
Expr::JsxFragment { span, .. } => *span,
|
|
2301
|
+
Expr::NativeModuleLoad { span, .. } => *span,
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
}
|