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    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}