Skip to main content

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}