nodejs/ast.rs
1//! JavaScript abstract syntax tree.
2//!
3//! Every node here has a direct lowering in `compiler.rs`. JS is
4//! statement-oriented with brace-delimited blocks, so the tree separates `Stmt`
5//! (blocks of these form a program/function body) from `Expr`. Numbers are all
6//! IEEE-754 `f64`, matching JavaScript's single number type.
7
8/// A source range as UTF-8 byte offsets `(start, end)` into the text the
9/// parser read. `(0, 0)` means none was recorded (a `${…}` template field is
10/// re-parsed from a copy, so its offsets would not point into the script).
11/// `Function.prototype.toString` returns this slice (20.2.3.5).
12pub type Span = (u32, u32);
13
14/// A binary operator (`a <op> b`). `&&`/`||`/`??` are `LogicalOp` because they
15/// short-circuit and yield an operand value, not a coerced boolean.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum BinOp {
18 Add,
19 Sub,
20 Mul,
21 Div,
22 Mod,
23 Pow, // **
24 // Comparison
25 Lt,
26 Le,
27 Gt,
28 Ge,
29 EqEqEq, // ===
30 NeEqEq, // !==
31 EqEq, // == (loose, coercing)
32 NeEq, // !=
33 // Bitwise / shift
34 BitAnd,
35 BitOr,
36 BitXor,
37 Shl, // <<
38 Shr, // >>
39 UShr, // >>>
40 // `in` / `instanceof`
41 In,
42 InstanceOf,
43}
44
45/// A short-circuiting logical operator.
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum LogicalOp {
48 And, // &&
49 Or, // ||
50 Nullish, // ??
51}
52
53/// The operator carried by a COMPOUND assignment (`a += b`, `a ??= b`).
54///
55/// It rides on the [`Expr::Assign`] node rather than being desugared away at
56/// parse time. The parser used to rewrite `a op= b` into `a = a op b`, which
57/// duplicates the target expression — so every side effect in the target ran
58/// TWICE. `o[k()] += 1` called `k` once in node and twice here, and the same
59/// held for the logical forms even when they short-circuited and never wrote.
60/// Duplication cannot be undone later by inspecting the tree, because
61/// `o[k()] = o[k()] + 1` is a DIFFERENT program that legitimately calls `k`
62/// twice and is structurally identical after the rewrite.
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum AssignOp {
65 /// `+= -= *= /= %= **= &= |= ^= <<= >>= >>>=` — always reads, computes, writes.
66 Binary(BinOp),
67 /// `&&= ||= ??=` — reads, and writes only if the read did not short-circuit.
68 Logical(LogicalOp),
69}
70
71/// A unary prefix operator.
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum UnOp {
74 Neg, // -x
75 Pos, // +x
76 Not, // !x
77 BitNot, // ~x
78 TypeOf, // typeof x
79 Void, // void x
80 Delete, // delete x
81}
82
83/// The kind of a variable declaration.
84#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub enum DeclKind {
86 Var,
87 Let,
88 Const,
89}
90
91/// The update (increment/decrement) operator, prefix or postfix.
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93pub enum UpdateOp {
94 Inc, // ++
95 Dec, // --
96}
97
98/// A property of an object literal.
99#[derive(Debug, Clone, PartialEq)]
100pub enum Prop {
101 /// `key: value` — `computed` marks `[expr]: value`.
102 KeyValue {
103 key: Expr,
104 value: Expr,
105 computed: bool,
106 },
107 /// `...spread`.
108 Spread(Expr),
109 /// `get key() {}` / `set key(v) {}` — an accessor property.
110 Accessor {
111 key: Expr,
112 computed: bool,
113 /// `true` for a getter, `false` for a setter.
114 is_getter: bool,
115 /// The accessor function (an `Expr::Function`).
116 func: Expr,
117 },
118}
119
120/// A JavaScript expression.
121#[derive(Debug, Clone, PartialEq)]
122pub enum Expr {
123 Null,
124 Undefined,
125 True,
126 False,
127 Number(f64),
128 /// A `BigInt` literal as its canonical decimal digit string (`"123"` for
129 /// `123n`). Lowered to a heap `JsObj::BigInt`.
130 BigInt(String),
131 /// A regex literal: `(pattern, flags)`. Lowered to a `JsObj::RegExp`.
132 Regex(String, String),
133 Str(String),
134 /// A template literal: alternating literal quasis and interpolated exprs.
135 /// `quasis.len() == exprs.len() + 1`.
136 Template {
137 quasis: Vec<String>,
138 exprs: Vec<Expr>,
139 },
140 /// A tagged template `` tag`a${x}b` ``: calls `tag(strings, ...values)` where
141 /// `strings` is the cooked-quasi array carrying a `.raw` array of `raws`.
142 TaggedTemplate {
143 tag: Box<Expr>,
144 quasis: Vec<String>,
145 raws: Vec<String>,
146 exprs: Vec<Expr>,
147 },
148
149 /// A bare identifier (`x`); the compiler resolves scope at runtime.
150 Ident(String),
151 /// `this`.
152 This,
153 /// `super` (only valid as `super(...)` call callee or `super.x` object).
154 Super,
155 /// `new.target`.
156 NewTarget,
157 /// `yield expr` / `yield* expr` / `yield` (generator).
158 Yield {
159 arg: Option<Box<Expr>>,
160 delegate: bool,
161 },
162 /// `await expr` (async function).
163 Await(Box<Expr>),
164 /// A `class` expression.
165 Class(Box<ClassNode>),
166
167 Array(Vec<Expr>),
168 /// An elided array-literal element (`[1,,3]`) — a HOLE, which reads back as
169 /// `undefined` but is not an own property. Distinct from `Expr::Undefined`
170 /// so `compile_array` can record it and `destructure_array` can skip it.
171 Hole,
172 Object(Vec<Prop>),
173 /// `...expr` — a spread element (array/call).
174 Spread(Box<Expr>),
175
176 Logical(LogicalOp, Box<Expr>, Box<Expr>),
177 Unary(UnOp, Box<Expr>),
178 Binary(BinOp, Box<Expr>, Box<Expr>),
179
180 /// `test ? cons : alt`.
181 Conditional {
182 test: Box<Expr>,
183 cons: Box<Expr>,
184 alt: Box<Expr>,
185 },
186
187 /// `target = value`, or a compound `target op= value` when `op` is `Some`.
188 ///
189 /// A compound assignment evaluates the target reference ONCE: for
190 /// `o[k()] += 1` the object and the key are computed a single time, the
191 /// old value is read through them, and the result is written back through
192 /// the same reference (ECMA-262 13.15.2).
193 Assign {
194 target: Box<Expr>,
195 op: Option<AssignOp>,
196 value: Box<Expr>,
197 },
198 /// `++x` / `x++` / `--x` / `x--`.
199 Update {
200 op: UpdateOp,
201 prefix: bool,
202 target: Box<Expr>,
203 },
204
205 /// A call `func(args)`. `optional` marks `?.(`.
206 Call {
207 func: Box<Expr>,
208 args: Vec<Expr>,
209 optional: bool,
210 },
211 /// `new Ctor(args)`.
212 New {
213 callee: Box<Expr>,
214 args: Vec<Expr>,
215 },
216 /// `value.name` — `optional` marks `?.name`.
217 Member {
218 object: Box<Expr>,
219 property: String,
220 optional: bool,
221 },
222 /// `value[expr]` — `optional` marks `?.[expr]`.
223 Index {
224 object: Box<Expr>,
225 index: Box<Expr>,
226 optional: bool,
227 },
228
229 /// A function expression / arrow function.
230 Function {
231 params: Vec<Param>,
232 body: FnBody,
233 is_arrow: bool,
234 name: Option<String>,
235 is_generator: bool,
236 is_async: bool,
237 /// True for a MethodDefinition (`{ m(){} }`, `{ get x(){} }`) rather
238 /// than an ordinary function expression. A non-generator method owns no
239 /// `prototype` property (10.2.5 runs only for ordinary functions).
240 is_method: bool,
241 /// The definition's source text: from `async`/`function`/`get`/`*`/the
242 /// key or the arrow's parameters, to its last token.
243 span: Span,
244 },
245
246 /// `,`-sequence expression: evaluate all, yield the last.
247 Sequence(Vec<Expr>),
248}
249
250/// A `class` declaration/expression body.
251#[derive(Debug, Clone, PartialEq)]
252pub struct ClassNode {
253 pub name: Option<String>,
254 /// The `extends` expression, if any.
255 pub parent: Option<Box<Expr>>,
256 pub members: Vec<ClassMember>,
257 /// `class` through the closing brace.
258 pub span: Span,
259}
260
261/// One member of a class body: a method, accessor, or field, on the instance or
262/// static side.
263#[derive(Debug, Clone, PartialEq)]
264pub struct ClassMember {
265 /// The property key (an `Expr::Str` for a plain name, or any expr when
266 /// `computed`).
267 pub key: Expr,
268 pub computed: bool,
269 pub kind: MemberKind,
270 pub is_static: bool,
271 pub is_generator: bool,
272 pub is_async: bool,
273 /// Params + body for a method/accessor/constructor.
274 pub params: Vec<Param>,
275 pub body: Vec<Stmt>,
276 /// Initializer expression for a field (`x = expr;`).
277 pub field_init: Option<Expr>,
278 /// A method's source, from its first modifier after `static` (or its
279 /// key) to the closing brace.
280 pub span: Span,
281}
282
283/// The kind of a class member.
284#[derive(Debug, Clone, Copy, PartialEq, Eq)]
285pub enum MemberKind {
286 Constructor,
287 Method,
288 Get,
289 Set,
290 Field,
291 /// A `static { … }` initialization block (ES2022). It has no key and no
292 /// parameters: only `body` is meaningful, and it runs once at class-definition
293 /// time with `this` bound to the constructor.
294 StaticBlock,
295}
296
297/// A function/arrow body: either a brace-delimited statement list or (arrow) a
298/// single expression whose value is returned.
299#[derive(Debug, Clone, PartialEq)]
300pub enum FnBody {
301 Block(Vec<Stmt>),
302 Expr(Box<Expr>),
303}
304
305/// A formal parameter.
306#[derive(Debug, Clone, PartialEq)]
307pub struct Param {
308 /// The binding target — an `Ident`, or an array/object pattern (also an
309 /// `Expr::Array`/`Expr::Object` used as a destructuring target).
310 pub pattern: Expr,
311 /// `= default`.
312 pub default: Option<Expr>,
313 /// `...rest`.
314 pub rest: bool,
315}
316
317/// One `case`/`default` clause of a `switch`.
318#[derive(Debug, Clone, PartialEq)]
319pub struct SwitchCase {
320 /// `None` for the `default:` clause.
321 pub test: Option<Expr>,
322 pub body: Vec<Stmt>,
323}
324
325/// A single declarator inside a `var`/`let`/`const`.
326#[derive(Debug, Clone, PartialEq)]
327pub struct Declarator {
328 pub target: Expr,
329 pub init: Option<Expr>,
330}
331
332/// A JavaScript statement.
333#[derive(Debug, Clone, PartialEq)]
334pub enum StmtKind {
335 /// An expression evaluated for effect (value discarded).
336 Expr(Expr),
337 /// `var`/`let`/`const` declaration list.
338 Decl {
339 kind: DeclKind,
340 decls: Vec<Declarator>,
341 },
342 /// `{ ... }` block.
343 Block(Vec<Stmt>),
344 /// `function name(params) { body }`.
345 FuncDecl {
346 name: String,
347 params: Vec<Param>,
348 body: Vec<Stmt>,
349 is_generator: bool,
350 is_async: bool,
351 span: Span,
352 },
353 /// `class Name … { … }`.
354 ClassDecl(ClassNode),
355
356 If {
357 test: Expr,
358 cons: Box<Stmt>,
359 alt: Option<Box<Stmt>>,
360 },
361 While {
362 test: Expr,
363 body: Box<Stmt>,
364 },
365 DoWhile {
366 body: Box<Stmt>,
367 test: Expr,
368 },
369 /// C-style `for (init; test; update) body`.
370 For {
371 init: Option<Box<Stmt>>,
372 test: Option<Expr>,
373 update: Option<Expr>,
374 body: Box<Stmt>,
375 },
376 /// `for (decl of iterable) body`. `is_await` marks `for await (…)`.
377 ForOf {
378 decl_kind: Option<DeclKind>,
379 target: Expr,
380 iter: Expr,
381 body: Box<Stmt>,
382 is_await: bool,
383 },
384 /// `for (decl in object) body`.
385 ForIn {
386 decl_kind: Option<DeclKind>,
387 target: Expr,
388 object: Expr,
389 body: Box<Stmt>,
390 },
391 Switch {
392 disc: Expr,
393 cases: Vec<SwitchCase>,
394 },
395
396 /// `label: stmt` — a labeled statement (typically a loop), targetable by
397 /// `break label` / `continue label`.
398 Labeled {
399 label: String,
400 body: Box<Stmt>,
401 },
402
403 Return(Option<Expr>),
404 Break(Option<String>),
405 Continue(Option<String>),
406 Throw(Expr),
407 Try {
408 block: Vec<Stmt>,
409 handler: Option<(Option<Expr>, Vec<Stmt>)>, // (param pattern, body)
410 finalizer: Option<Vec<Stmt>>,
411 },
412
413 Empty,
414}
415
416/// A statement plus its 1-based source line.
417#[derive(Debug, Clone, PartialEq)]
418pub struct Stmt {
419 pub kind: StmtKind,
420 pub line: u32,
421}
422
423impl Stmt {
424 pub fn new(kind: StmtKind, line: u32) -> Stmt {
425 Stmt { kind, line }
426 }
427}
428
429impl From<StmtKind> for Stmt {
430 /// Wrap a `StmtKind` as a synthetic statement (line 0).
431 fn from(kind: StmtKind) -> Stmt {
432 Stmt { kind, line: 0 }
433 }
434}