Skip to main content

nova_ast/
expr.rs

1//! Expression AST nodes.
2
3use nova_diagnostics::Spanned;
4
5use crate::{item::Param, pattern::Pattern, ty::Type, Block, Path};
6
7/// An expression.
8#[derive(Debug, Clone)]
9pub enum Expr {
10    Lit(Literal),
11    Path(Path),
12    Tuple(Vec<Spanned<Expr>>),
13    Array(Vec<Spanned<Expr>>),
14    /// `[init; n]` — an `n`-slot array whose every slot is `init`, with both
15    /// `init` and `n` evaluated once at runtime.
16    ///
17    /// `init` is **not** copied per slot: it is evaluated a single time and that
18    /// one value goes into every slot, so for a heap element type all `n` slots
19    /// refer to the same object (`[Cell { n: 0 }; 3]` is one `Cell` seen three
20    /// times, and `a[0].n = 42` shows through `a[1]` and `a[2]`). Nova has
21    /// reference semantics throughout and no `Copy`, so there is no per-slot
22    /// clone to perform.
23    ArrayRepeat {
24        init: Box<Spanned<Expr>>,
25        len: Box<Spanned<Expr>>,
26    },
27    Block(Block),
28
29    If {
30        cond: Box<Spanned<Expr>>,
31        then: Box<Spanned<Block>>,
32        else_: Option<Box<Spanned<Expr>>>,
33    },
34
35    Match {
36        scrutinee: Box<Spanned<Expr>>,
37        arms: Vec<MatchArm>,
38    },
39
40    While {
41        cond: Box<Spanned<Expr>>,
42        body: Box<Spanned<Block>>,
43    },
44
45    For {
46        pattern: Spanned<Pattern>,
47        iter: Box<Spanned<Expr>>,
48        body: Box<Spanned<Block>>,
49    },
50
51    /// A range expression `lo..hi` (exclusive) or `lo..=hi` (inclusive).
52    /// Phase 1 supports these only as the iterable of a `for` loop.
53    Range {
54        lo: Box<Spanned<Expr>>,
55        hi: Box<Spanned<Expr>>,
56        inclusive: bool,
57    },
58
59    Return(Option<Box<Spanned<Expr>>>),
60    Break(Option<Box<Spanned<Expr>>>),
61    Continue,
62
63    Closure {
64        params: Vec<Param>,
65        ret: Option<Spanned<Type>>,
66        body: Box<Spanned<Expr>>,
67    },
68
69    Record {
70        path: Path,
71        fields: Vec<FieldInit>,
72        /// `..base` spread
73        base: Option<Box<Spanned<Expr>>>,
74    },
75
76    Binary {
77        op: BinOp,
78        lhs: Box<Spanned<Expr>>,
79        rhs: Box<Spanned<Expr>>,
80    },
81
82    Unary {
83        op: UnOp,
84        expr: Box<Spanned<Expr>>,
85    },
86
87    Call {
88        callee: Box<Spanned<Expr>>,
89        args: Vec<Spanned<Expr>>,
90    },
91
92    Index {
93        target: Box<Spanned<Expr>>,
94        index: Box<Spanned<Expr>>,
95    },
96
97    Field {
98        target: Box<Spanned<Expr>>,
99        field: Spanned<String>,
100    },
101
102    /// `expr?` — the try / error-propagation operator.
103    Try(Box<Spanned<Expr>>),
104
105    /// `expr.await`.
106    Await(Box<Spanned<Expr>>),
107
108    /// `expr as Type`.
109    Cast {
110        expr: Box<Spanned<Expr>>,
111        ty: Spanned<Type>,
112    },
113
114    /// `lhs = rhs`, `lhs += rhs`, etc.
115    Assign {
116        op: AssignOp,
117        lhs: Box<Spanned<Expr>>,
118        rhs: Box<Spanned<Expr>>,
119    },
120
121    /// A string with interpolated expressions: `"Hello, ${name}!"`.
122    StringInterp(Vec<StringPart>),
123}
124
125/// A segment of a string-interpolation expression.
126#[derive(Debug, Clone)]
127pub enum StringPart {
128    Lit(String),
129    Expr(Spanned<Expr>),
130}
131
132/// A scalar literal value.
133#[derive(Debug, Clone)]
134pub enum Literal {
135    Int(i64),
136    Float(f64),
137    Str(String),
138    Char(char),
139    Bool(bool),
140}
141
142/// A single arm in a `match` expression.
143#[derive(Debug, Clone)]
144pub struct MatchArm {
145    pub pattern: Spanned<Pattern>,
146    pub guard: Option<Spanned<Expr>>,
147    pub body: Spanned<Expr>,
148}
149
150/// Field initialiser in a record literal: `point { x: 1.0, y: 2.0 }`.
151#[derive(Debug, Clone)]
152pub struct FieldInit {
153    pub name: Spanned<String>,
154    /// `None` for shorthand `{ x }` (equivalent to `{ x: x }`).
155    pub value: Option<Spanned<Expr>>,
156}
157
158/// Binary operators in order of precedence (low → high, mirroring the spec table).
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160pub enum BinOp {
161    Or,     // ||
162    And,    // &&
163    Eq,     // ==
164    Ne,     // !=
165    Lt,     // <
166    Le,     // <=
167    Gt,     // >
168    Ge,     // >=
169    BitOr,  // |
170    BitXor, // ^
171    BitAnd, // &
172    Shl,    // <<
173    Shr,    // >>
174    Add,    // +
175    Sub,    // -
176    Mul,    // *
177    Div,    // /
178    Rem,    // %
179}
180
181/// Unary (prefix) operators.
182#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183pub enum UnOp {
184    Neg,    // -
185    Not,    // !
186    BitNot, // ~
187    Ref,    // &
188    RefMut, // &mut
189    Deref,  // *
190}
191
192/// Assignment operators.
193#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194pub enum AssignOp {
195    Assign,
196    AddAssign,
197    SubAssign,
198    MulAssign,
199    DivAssign,
200    RemAssign,
201    BitOrAssign,
202    BitAndAssign,
203    BitXorAssign,
204    ShlAssign,
205    ShrAssign,
206}