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}
86
87/// A JavaScript expression.
88#[derive(Debug, Clone, PartialEq)]
89pub enum Expr {
90    Null,
91    Undefined,
92    True,
93    False,
94    Number(f64),
95    Str(String),
96    /// A template literal: alternating literal quasis and interpolated exprs.
97    /// `quasis.len() == exprs.len() + 1`.
98    Template {
99        quasis: Vec<String>,
100        exprs: Vec<Expr>,
101    },
102
103    /// A bare identifier (`x`); the compiler resolves scope at runtime.
104    Ident(String),
105    /// `this`.
106    This,
107
108    Array(Vec<Expr>),
109    Object(Vec<Prop>),
110    /// `...expr` — a spread element (array/call).
111    Spread(Box<Expr>),
112
113    Logical(LogicalOp, Box<Expr>, Box<Expr>),
114    Unary(UnOp, Box<Expr>),
115    Binary(BinOp, Box<Expr>, Box<Expr>),
116
117    /// `test ? cons : alt`.
118    Conditional {
119        test: Box<Expr>,
120        cons: Box<Expr>,
121        alt: Box<Expr>,
122    },
123
124    /// `target = value` (or a compound `target op= value` desugared by the parser).
125    Assign {
126        target: Box<Expr>,
127        value: Box<Expr>,
128    },
129    /// `++x` / `x++` / `--x` / `x--`.
130    Update {
131        op: UpdateOp,
132        prefix: bool,
133        target: Box<Expr>,
134    },
135
136    /// A call `func(args)`. `optional` marks `?.(`.
137    Call {
138        func: Box<Expr>,
139        args: Vec<Expr>,
140        optional: bool,
141    },
142    /// `new Ctor(args)`.
143    New {
144        callee: Box<Expr>,
145        args: Vec<Expr>,
146    },
147    /// `value.name` — `optional` marks `?.name`.
148    Member {
149        object: Box<Expr>,
150        property: String,
151        optional: bool,
152    },
153    /// `value[expr]` — `optional` marks `?.[expr]`.
154    Index {
155        object: Box<Expr>,
156        index: Box<Expr>,
157        optional: bool,
158    },
159
160    /// A function expression / arrow function.
161    Function {
162        params: Vec<Param>,
163        body: FnBody,
164        is_arrow: bool,
165        name: Option<String>,
166    },
167
168    /// `,`-sequence expression: evaluate all, yield the last.
169    Sequence(Vec<Expr>),
170}
171
172/// A function/arrow body: either a brace-delimited statement list or (arrow) a
173/// single expression whose value is returned.
174#[derive(Debug, Clone, PartialEq)]
175pub enum FnBody {
176    Block(Vec<Stmt>),
177    Expr(Box<Expr>),
178}
179
180/// A formal parameter.
181#[derive(Debug, Clone, PartialEq)]
182pub struct Param {
183    /// The binding target — an `Ident`, or an array/object pattern (also an
184    /// `Expr::Array`/`Expr::Object` used as a destructuring target).
185    pub pattern: Expr,
186    /// `= default`.
187    pub default: Option<Expr>,
188    /// `...rest`.
189    pub rest: bool,
190}
191
192/// One `case`/`default` clause of a `switch`.
193#[derive(Debug, Clone, PartialEq)]
194pub struct SwitchCase {
195    /// `None` for the `default:` clause.
196    pub test: Option<Expr>,
197    pub body: Vec<Stmt>,
198}
199
200/// A single declarator inside a `var`/`let`/`const`.
201#[derive(Debug, Clone, PartialEq)]
202pub struct Declarator {
203    pub target: Expr,
204    pub init: Option<Expr>,
205}
206
207/// A JavaScript statement.
208#[derive(Debug, Clone, PartialEq)]
209pub enum StmtKind {
210    /// An expression evaluated for effect (value discarded).
211    Expr(Expr),
212    /// `var`/`let`/`const` declaration list.
213    Decl {
214        kind: DeclKind,
215        decls: Vec<Declarator>,
216    },
217    /// `{ ... }` block.
218    Block(Vec<Stmt>),
219    /// `function name(params) { body }`.
220    FuncDecl {
221        name: String,
222        params: Vec<Param>,
223        body: Vec<Stmt>,
224    },
225
226    If {
227        test: Expr,
228        cons: Box<Stmt>,
229        alt: Option<Box<Stmt>>,
230    },
231    While {
232        test: Expr,
233        body: Box<Stmt>,
234    },
235    DoWhile {
236        body: Box<Stmt>,
237        test: Expr,
238    },
239    /// C-style `for (init; test; update) body`.
240    For {
241        init: Option<Box<Stmt>>,
242        test: Option<Expr>,
243        update: Option<Expr>,
244        body: Box<Stmt>,
245    },
246    /// `for (decl of iterable) body`.
247    ForOf {
248        decl_kind: Option<DeclKind>,
249        target: Expr,
250        iter: Expr,
251        body: Box<Stmt>,
252    },
253    /// `for (decl in object) body`.
254    ForIn {
255        decl_kind: Option<DeclKind>,
256        target: Expr,
257        object: Expr,
258        body: Box<Stmt>,
259    },
260    Switch {
261        disc: Expr,
262        cases: Vec<SwitchCase>,
263    },
264
265    Return(Option<Expr>),
266    Break(Option<String>),
267    Continue(Option<String>),
268    Throw(Expr),
269    Try {
270        block: Vec<Stmt>,
271        handler: Option<(Option<Expr>, Vec<Stmt>)>, // (param pattern, body)
272        finalizer: Option<Vec<Stmt>>,
273    },
274
275    Empty,
276}
277
278/// A statement plus its 1-based source line.
279#[derive(Debug, Clone, PartialEq)]
280pub struct Stmt {
281    pub kind: StmtKind,
282    pub line: u32,
283}
284
285impl Stmt {
286    pub fn new(kind: StmtKind, line: u32) -> Stmt {
287        Stmt { kind, line }
288    }
289}
290
291impl From<StmtKind> for Stmt {
292    /// Wrap a `StmtKind` as a synthetic statement (line 0).
293    fn from(kind: StmtKind) -> Stmt {
294        Stmt { kind, line: 0 }
295    }
296}