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 Object(Vec<Prop>),
145 /// `...expr` — a spread element (array/call).
146 Spread(Box<Expr>),
147
148 Logical(LogicalOp, Box<Expr>, Box<Expr>),
149 Unary(UnOp, Box<Expr>),
150 Binary(BinOp, Box<Expr>, Box<Expr>),
151
152 /// `test ? cons : alt`.
153 Conditional {
154 test: Box<Expr>,
155 cons: Box<Expr>,
156 alt: Box<Expr>,
157 },
158
159 /// `target = value` (or a compound `target op= value` desugared by the parser).
160 Assign {
161 target: Box<Expr>,
162 value: Box<Expr>,
163 },
164 /// `++x` / `x++` / `--x` / `x--`.
165 Update {
166 op: UpdateOp,
167 prefix: bool,
168 target: Box<Expr>,
169 },
170
171 /// A call `func(args)`. `optional` marks `?.(`.
172 Call {
173 func: Box<Expr>,
174 args: Vec<Expr>,
175 optional: bool,
176 },
177 /// `new Ctor(args)`.
178 New {
179 callee: Box<Expr>,
180 args: Vec<Expr>,
181 },
182 /// `value.name` — `optional` marks `?.name`.
183 Member {
184 object: Box<Expr>,
185 property: String,
186 optional: bool,
187 },
188 /// `value[expr]` — `optional` marks `?.[expr]`.
189 Index {
190 object: Box<Expr>,
191 index: Box<Expr>,
192 optional: bool,
193 },
194
195 /// A function expression / arrow function.
196 Function {
197 params: Vec<Param>,
198 body: FnBody,
199 is_arrow: bool,
200 name: Option<String>,
201 is_generator: bool,
202 is_async: bool,
203 },
204
205 /// `,`-sequence expression: evaluate all, yield the last.
206 Sequence(Vec<Expr>),
207}
208
209/// A `class` declaration/expression body.
210#[derive(Debug, Clone, PartialEq)]
211pub struct ClassNode {
212 pub name: Option<String>,
213 /// The `extends` expression, if any.
214 pub parent: Option<Box<Expr>>,
215 pub members: Vec<ClassMember>,
216}
217
218/// One member of a class body: a method, accessor, or field, on the instance or
219/// static side.
220#[derive(Debug, Clone, PartialEq)]
221pub struct ClassMember {
222 /// The property key (an `Expr::Str` for a plain name, or any expr when
223 /// `computed`).
224 pub key: Expr,
225 pub computed: bool,
226 pub kind: MemberKind,
227 pub is_static: bool,
228 pub is_generator: bool,
229 pub is_async: bool,
230 /// Params + body for a method/accessor/constructor.
231 pub params: Vec<Param>,
232 pub body: Vec<Stmt>,
233 /// Initializer expression for a field (`x = expr;`).
234 pub field_init: Option<Expr>,
235}
236
237/// The kind of a class member.
238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
239pub enum MemberKind {
240 Constructor,
241 Method,
242 Get,
243 Set,
244 Field,
245}
246
247/// A function/arrow body: either a brace-delimited statement list or (arrow) a
248/// single expression whose value is returned.
249#[derive(Debug, Clone, PartialEq)]
250pub enum FnBody {
251 Block(Vec<Stmt>),
252 Expr(Box<Expr>),
253}
254
255/// A formal parameter.
256#[derive(Debug, Clone, PartialEq)]
257pub struct Param {
258 /// The binding target — an `Ident`, or an array/object pattern (also an
259 /// `Expr::Array`/`Expr::Object` used as a destructuring target).
260 pub pattern: Expr,
261 /// `= default`.
262 pub default: Option<Expr>,
263 /// `...rest`.
264 pub rest: bool,
265}
266
267/// One `case`/`default` clause of a `switch`.
268#[derive(Debug, Clone, PartialEq)]
269pub struct SwitchCase {
270 /// `None` for the `default:` clause.
271 pub test: Option<Expr>,
272 pub body: Vec<Stmt>,
273}
274
275/// A single declarator inside a `var`/`let`/`const`.
276#[derive(Debug, Clone, PartialEq)]
277pub struct Declarator {
278 pub target: Expr,
279 pub init: Option<Expr>,
280}
281
282/// A JavaScript statement.
283#[derive(Debug, Clone, PartialEq)]
284pub enum StmtKind {
285 /// An expression evaluated for effect (value discarded).
286 Expr(Expr),
287 /// `var`/`let`/`const` declaration list.
288 Decl {
289 kind: DeclKind,
290 decls: Vec<Declarator>,
291 },
292 /// `{ ... }` block.
293 Block(Vec<Stmt>),
294 /// `function name(params) { body }`.
295 FuncDecl {
296 name: String,
297 params: Vec<Param>,
298 body: Vec<Stmt>,
299 is_generator: bool,
300 is_async: bool,
301 },
302 /// `class Name … { … }`.
303 ClassDecl(ClassNode),
304
305 If {
306 test: Expr,
307 cons: Box<Stmt>,
308 alt: Option<Box<Stmt>>,
309 },
310 While {
311 test: Expr,
312 body: Box<Stmt>,
313 },
314 DoWhile {
315 body: Box<Stmt>,
316 test: Expr,
317 },
318 /// C-style `for (init; test; update) body`.
319 For {
320 init: Option<Box<Stmt>>,
321 test: Option<Expr>,
322 update: Option<Expr>,
323 body: Box<Stmt>,
324 },
325 /// `for (decl of iterable) body`. `is_await` marks `for await (…)`.
326 ForOf {
327 decl_kind: Option<DeclKind>,
328 target: Expr,
329 iter: Expr,
330 body: Box<Stmt>,
331 is_await: bool,
332 },
333 /// `for (decl in object) body`.
334 ForIn {
335 decl_kind: Option<DeclKind>,
336 target: Expr,
337 object: Expr,
338 body: Box<Stmt>,
339 },
340 Switch {
341 disc: Expr,
342 cases: Vec<SwitchCase>,
343 },
344
345 /// `label: stmt` — a labeled statement (typically a loop), targetable by
346 /// `break label` / `continue label`.
347 Labeled {
348 label: String,
349 body: Box<Stmt>,
350 },
351
352 Return(Option<Expr>),
353 Break(Option<String>),
354 Continue(Option<String>),
355 Throw(Expr),
356 Try {
357 block: Vec<Stmt>,
358 handler: Option<(Option<Expr>, Vec<Stmt>)>, // (param pattern, body)
359 finalizer: Option<Vec<Stmt>>,
360 },
361
362 Empty,
363}
364
365/// A statement plus its 1-based source line.
366#[derive(Debug, Clone, PartialEq)]
367pub struct Stmt {
368 pub kind: StmtKind,
369 pub line: u32,
370}
371
372impl Stmt {
373 pub fn new(kind: StmtKind, line: u32) -> Stmt {
374 Stmt { kind, line }
375 }
376}
377
378impl From<StmtKind> for Stmt {
379 /// Wrap a `StmtKind` as a synthetic statement (line 0).
380 fn from(kind: StmtKind) -> Stmt {
381 Stmt { kind, line: 0 }
382 }
383}