lux/ast.rs
1//! The abstract syntax tree: the shape of a parsed lux program.
2//!
3//! A program is a list of statements. Statements declare names, assign to
4//! them, branch, loop, define functions, return, or evaluate an expression for
5//! its effect (like `print`). Expressions produce values. Every node carries a
6//! `Span` so the interpreter can blame the right place when something goes
7//! wrong.
8
9use crate::diagnostic::Span;
10
11/// A written type annotation, like the `int` in `var count: int` or the
12/// `[int]` in `let primes: [int]`. Types nest, so this is recursive.
13#[derive(Debug, Clone)]
14pub struct TypeAnn {
15 pub kind: TypeKind,
16 pub span: Span,
17}
18
19#[derive(Debug, Clone)]
20pub enum TypeKind {
21 /// A plain named type: `int`, `float`, `string`, `bool`.
22 Named(String),
23 /// An array type: `[int]`, `[[string]]`.
24 Array(Box<TypeAnn>),
25 /// A type with parameters: `Option<int>`, `Result<int, string>`. So far
26 /// only the built-in `Option` and `Result` take parameters — user-defined
27 /// generics are a later milestone.
28 Generic(String, Vec<TypeAnn>),
29}
30
31/// One parameter in a function signature, like the `x: int` in `func f(x: int)`.
32#[derive(Debug, Clone)]
33pub struct Param {
34 pub name: String,
35 pub ty: TypeAnn,
36 pub span: Span,
37}
38
39/// One field in a struct, or one labelled value carried by an enum case, like
40/// the `x: int` in `struct Point { x: int }` or the `radius: float` in
41/// `circle(radius: float)`. Same shape as a parameter, but it names data rather
42/// than an argument, so it gets its own type for clarity.
43#[derive(Debug, Clone)]
44pub struct FieldDef {
45 pub name: String,
46 pub ty: TypeAnn,
47 pub span: Span,
48}
49
50/// One case of an enum, like `circle(radius: float)` or the payload-less `dot`.
51#[derive(Debug, Clone)]
52pub struct VariantDef {
53 pub name: String,
54 pub fields: Vec<FieldDef>,
55 pub span: Span,
56}
57
58#[derive(Debug, Clone)]
59pub enum Stmt {
60 /// `let name = value` — an immutable binding.
61 Let {
62 name: String,
63 ty: Option<TypeAnn>,
64 value: Expr,
65 span: Span,
66 },
67 /// `var name = value` or `var name: type` — a mutable binding.
68 Var {
69 name: String,
70 ty: Option<TypeAnn>,
71 value: Option<Expr>,
72 span: Span,
73 },
74 /// `name = value`, `name += value`, `name -= value`.
75 Assign {
76 name: String,
77 name_span: Span,
78 op: AssignOp,
79 value: Expr,
80 span: Span,
81 },
82 /// `func name(params) -> ret { body }`. A missing `-> ret` means the
83 /// function returns nothing.
84 Func {
85 name: String,
86 params: Vec<Param>,
87 ret: Option<TypeAnn>,
88 body: Vec<Stmt>,
89 span: Span,
90 },
91 /// `return` or `return value`.
92 Return { value: Option<Expr>, span: Span },
93 /// `struct Name { field: type ... }` — declares a record type.
94 Struct {
95 name: String,
96 fields: Vec<FieldDef>,
97 span: Span,
98 },
99 /// `enum Name { case ... }` — declares a type that is exactly one of a
100 /// fixed set of cases, each of which may carry its own values.
101 Enum {
102 name: String,
103 variants: Vec<VariantDef>,
104 span: Span,
105 },
106 /// `if cond { ... } else { ... }`. An `else if` is represented as an
107 /// `else` body holding a single nested `If`.
108 If {
109 cond: Expr,
110 then_body: Vec<Stmt>,
111 else_body: Option<Vec<Stmt>>,
112 span: Span,
113 },
114 /// `while cond { ... }`.
115 While {
116 cond: Expr,
117 body: Vec<Stmt>,
118 span: Span,
119 },
120 /// `for var in iter { ... }`, where `iter` is an array or a range.
121 For {
122 var: String,
123 iter: Expr,
124 body: Vec<Stmt>,
125 span: Span,
126 },
127 /// A bare expression run for its effect, like `print("hi")`.
128 Expr(Expr),
129}
130
131#[derive(Debug, Clone, Copy, PartialEq)]
132pub enum AssignOp {
133 Set,
134 Add,
135 Sub,
136}
137
138#[derive(Debug, Clone)]
139pub enum Expr {
140 Int(i64, Span),
141 Float(f64, Span),
142 Str(String, Span),
143 Bool(bool, Span),
144 Ident(String, Span),
145 /// An array literal: `[2, 3, 5]`.
146 Array(Vec<Expr>, Span),
147 Unary {
148 op: UnOp,
149 rhs: Box<Expr>,
150 span: Span,
151 },
152 Binary {
153 op: BinOp,
154 lhs: Box<Expr>,
155 rhs: Box<Expr>,
156 span: Span,
157 },
158 /// Reading an element by position: `xs[0]`.
159 Index {
160 base: Box<Expr>,
161 index: Box<Expr>,
162 span: Span,
163 },
164 /// A half-open range: `0..5` means 0, 1, 2, 3, 4.
165 Range {
166 start: Box<Expr>,
167 end: Box<Expr>,
168 span: Span,
169 },
170 /// A function call. Built-ins (print, string, int, float, length) and
171 /// user-defined functions share this node.
172 Call {
173 name: String,
174 args: Vec<Expr>,
175 span: Span,
176 },
177 /// Building a struct by naming its fields: `Point(x: 0, y: 0)`.
178 StructLit {
179 name: String,
180 fields: Vec<(String, Expr)>,
181 span: Span,
182 },
183 /// Building an enum case: `Shape.circle(radius: 2.0)`. A payload-less case
184 /// like `Shape.dot` parses as a `Field` and is resolved at run time.
185 EnumLit {
186 enum_name: String,
187 variant: String,
188 fields: Vec<(String, Expr)>,
189 span: Span,
190 },
191 /// Reading a struct field with a dot: `origin.x`.
192 Field {
193 base: Box<Expr>,
194 field: String,
195 span: Span,
196 },
197 /// `match scrutinee { pattern => expr ... }`. An expression: it evaluates to
198 /// the body of the one arm that matches.
199 Match {
200 scrutinee: Box<Expr>,
201 arms: Vec<MatchArm>,
202 span: Span,
203 },
204}
205
206/// One arm of a `match`: a pattern and the expression to evaluate when it fits.
207#[derive(Debug, Clone)]
208pub struct MatchArm {
209 pub pattern: Pattern,
210 pub body: Expr,
211 pub span: Span,
212}
213
214/// What a `match` arm tests for.
215#[derive(Debug, Clone)]
216pub enum Pattern {
217 /// `_` — matches anything, binds nothing.
218 Wildcard(Span),
219 /// A literal value: `0`, `"hi"`, `true`.
220 Int(i64, Span),
221 Str(String, Span),
222 Bool(bool, Span),
223 /// An enum case, optionally capturing its values: `dot`, `circle(let r)`.
224 Variant {
225 name: String,
226 bindings: Vec<String>,
227 span: Span,
228 },
229}
230
231impl Pattern {
232 pub fn span(&self) -> Span {
233 match self {
234 Pattern::Wildcard(s)
235 | Pattern::Int(_, s)
236 | Pattern::Str(_, s)
237 | Pattern::Bool(_, s) => *s,
238 Pattern::Variant { span, .. } => *span,
239 }
240 }
241}
242
243impl Expr {
244 /// The source span covering this whole expression.
245 pub fn span(&self) -> Span {
246 match self {
247 Expr::Int(_, s)
248 | Expr::Float(_, s)
249 | Expr::Str(_, s)
250 | Expr::Bool(_, s)
251 | Expr::Ident(_, s)
252 | Expr::Array(_, s) => *s,
253 Expr::Unary { span, .. }
254 | Expr::Binary { span, .. }
255 | Expr::Index { span, .. }
256 | Expr::Range { span, .. }
257 | Expr::Call { span, .. }
258 | Expr::StructLit { span, .. }
259 | Expr::EnumLit { span, .. }
260 | Expr::Field { span, .. }
261 | Expr::Match { span, .. } => *span,
262 }
263 }
264}
265
266#[derive(Debug, Clone, Copy, PartialEq)]
267pub enum UnOp {
268 Neg,
269 Not,
270}
271
272#[derive(Debug, Clone, Copy, PartialEq)]
273pub enum BinOp {
274 Add,
275 Sub,
276 Mul,
277 Div,
278 Mod,
279 Eq,
280 Ne,
281 Lt,
282 Gt,
283 Le,
284 Ge,
285 And,
286 Or,
287}