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