Skip to main content

varyk_syntax/
ast.rs

1//! The AST produced by the parser (spec section 6.3). Every node carries a
2//! [`Span`], except [`Program`] and [`Item`], whose span is whichever
3//! variant/item they hold.
4
5use crate::span::Span;
6
7/// A name plus the span it was written at.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct Ident {
10    pub name: String,
11    pub span: Span,
12}
13
14/// A named type: `string`, `i32`, `User`, and so on are all just names,
15/// resolved later by the compiler's resolver. A type may carry an optional
16/// module segment (`m::User`, spec 2.10) and generic arguments (`Vec<i32>`,
17/// `Result<User, string>`, nested freely, spec 2.6); the parser only records
18/// them, and the resolver checks that the module, the name, and the argument
19/// count exist.
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct TypeExpr {
22    pub module: Option<Ident>,
23    pub name: Ident,
24    pub args: Vec<TypeExpr>,
25    pub span: Span,
26}
27
28impl TypeExpr {
29    /// Renders the whole type back to source text: the module segment (if
30    /// any), the name, and any generic arguments, recursively. Used
31    /// wherever a diagnostic or fix-it needs to show the type as written,
32    /// rather than just its base name.
33    pub fn display_name(&self) -> String {
34        let mut out = String::new();
35        if let Some(module) = &self.module {
36            out.push_str(&module.name);
37            out.push_str("::");
38        }
39        out.push_str(&self.name.name);
40        if !self.args.is_empty() {
41            out.push('<');
42            for (i, arg) in self.args.iter().enumerate() {
43                if i > 0 {
44                    out.push_str(", ");
45                }
46                out.push_str(&arg.display_name());
47            }
48            out.push('>');
49        }
50        out
51    }
52}
53
54/// A whole parsed source file: the top-level items it declares, in order.
55#[derive(Debug, Clone, PartialEq)]
56pub struct Program {
57    pub items: Vec<Item>,
58}
59
60/// A top-level declaration (spec 4.1, 2.2, 2.5): a function, a struct, an
61/// enum, an `impl` block, or a module declaration. `mod name;` only
62/// declares a submodule; resolving it to the submodule's own file and items
63/// is the compiler's `resolve` module's job.
64#[derive(Debug, Clone, PartialEq)]
65pub enum Item {
66    Function(Function),
67    Struct(StructDecl),
68    Enum(EnumDecl),
69    Impl(ImplBlock),
70    Mod(ModDecl),
71}
72
73/// Whether a function is a plain function, or a method with a `self`
74/// receiver (spec 2.5): `self` is a shared borrow, `mut self` a mutable
75/// borrow. There is no owned `self` in milestone 2.
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum SelfMode {
78    None,
79    Shared,
80    Mutable,
81}
82
83/// `fn name(params) -> ReturnType { body }`, optionally `pub`. Inside an
84/// `impl` block the parameter list may start with a `self` receiver
85/// (`self_mode`), which is never a [`Param`] (spec 2.5).
86#[derive(Debug, Clone, PartialEq)]
87pub struct Function {
88    pub name: Ident,
89    pub is_pub: bool,
90    pub self_mode: SelfMode,
91    /// The `self` keyword of the receiver, where a `mut ` fix-it inserts;
92    /// `None` exactly when `self_mode` is [`SelfMode::None`].
93    pub self_span: Option<Span>,
94    pub params: Vec<Param>,
95    pub return_type: Option<TypeExpr>,
96    pub body: Block,
97    pub span: Span,
98}
99
100/// One parameter: `name: T` is a shared borrow, `mut name: T` is a mutable
101/// borrow (spec 4.2).
102#[derive(Debug, Clone, PartialEq)]
103pub struct Param {
104    pub name: Ident,
105    pub mutable: bool,
106    pub ty: TypeExpr,
107    pub span: Span,
108}
109
110/// `struct Name { fields... }`, optionally `pub` (which makes every field
111/// public; field-level `pub` is milestone 2, spec 4.1).
112#[derive(Debug, Clone, PartialEq)]
113pub struct StructDecl {
114    pub name: Ident,
115    pub is_pub: bool,
116    pub fields: Vec<FieldDecl>,
117    pub span: Span,
118}
119
120#[derive(Debug, Clone, PartialEq)]
121pub struct FieldDecl {
122    pub name: Ident,
123    pub ty: TypeExpr,
124    pub span: Span,
125}
126
127/// `enum Name { variants... }` (spec 2.2), optionally `pub`. An enum has one
128/// or more variants; an empty `{ }` is `V0002`, since the parser found a
129/// complete-looking declaration missing the one thing it must have.
130#[derive(Debug, Clone, PartialEq)]
131pub struct EnumDecl {
132    pub name: Ident,
133    pub is_pub: bool,
134    pub variants: Vec<EnumVariant>,
135    pub span: Span,
136}
137
138/// A unit variant (`Point`) or a tuple variant (`Circle(f64)`); `fields` is
139/// empty for a unit variant. Named-field variants (`Circle { x: f64 }`) are
140/// milestone 4 and never produce one of these: they are `V0001`.
141#[derive(Debug, Clone, PartialEq)]
142pub struct EnumVariant {
143    pub name: Ident,
144    pub fields: Vec<TypeExpr>,
145    pub span: Span,
146}
147
148/// `impl Name { fns... }` (spec 2.5): a block of functions and methods for
149/// a struct or enum declared in the same file. Whether `name` actually
150/// names a struct or enum is the compiler's `resolve` module's job.
151#[derive(Debug, Clone, PartialEq)]
152pub struct ImplBlock {
153    pub type_name: Ident,
154    pub functions: Vec<Function>,
155    pub span: Span,
156}
157
158/// `mod name;` (spec 4.5): declares a submodule resolved to `name.vr` or
159/// `name.rs` in the same directory. Resolving and merging the submodule's
160/// items is the compiler's `resolve` module's job; this node only records
161/// the declaration.
162#[derive(Debug, Clone, PartialEq)]
163pub struct ModDecl {
164    pub name: Ident,
165    pub is_pub: bool,
166    pub span: Span,
167}
168
169/// A statement: `let`, assignment, `return`, `while`, `break`, `continue`,
170/// or an expression used for its side effects (or as a block's tail,
171/// distinguished by `has_semi` on [`Stmt::Expr`] so the type checker can tell a
172/// mid-block statement from a block's value).
173#[derive(Debug, Clone, PartialEq)]
174pub enum Stmt {
175    Let {
176        name: Ident,
177        mutable: bool,
178        ty: Option<TypeExpr>,
179        value: Expr,
180        span: Span,
181    },
182    /// Assignment to a place expression: a path (a variable) or a field
183    /// access. Anything else as the target is rejected (`V0002`) before
184    /// this node is ever built.
185    Assign {
186        target: Expr,
187        value: Expr,
188        span: Span,
189    },
190    /// An expression statement. `has_semi` is false only for a block-like
191    /// expression (`if`, a bare block) written without a trailing `;` in
192    /// the middle of a block, not at its end.
193    Expr {
194        expr: Expr,
195        span: Span,
196        has_semi: bool,
197    },
198    Return {
199        value: Option<Expr>,
200        span: Span,
201    },
202    While {
203        cond: Expr,
204        body: Block,
205        span: Span,
206    },
207    /// `for var in head { body }` (spec 2.4). `head` is either a half-open
208    /// range or a `Vec` iterated element by element; the loop variable's
209    /// type and whether the `Vec` case borrows or owns each element is the
210    /// type checker's job (task 8).
211    For {
212        var: Ident,
213        head: ForHead,
214        body: Block,
215        span: Span,
216    },
217    Break {
218        span: Span,
219    },
220    Continue {
221        span: Span,
222    },
223}
224
225/// The head of a `for` loop (spec 2.4): a half-open integer range
226/// (`a..b`), parsed only here since ranges exist nowhere else in Varyk, or
227/// an expression iterated element by element as a `Vec`. `..=` never
228/// produces a [`ForHead::Range`]: it is `V0001`, milestone 4.
229#[derive(Debug, Clone, PartialEq)]
230pub enum ForHead {
231    Range { start: Box<Expr>, end: Box<Expr> },
232    Expr(Box<Expr>),
233}
234
235/// `{ stmts...; tail }`. The tail expression, if present, is the block's
236/// value.
237#[derive(Debug, Clone, PartialEq)]
238pub struct Block {
239    pub stmts: Vec<Stmt>,
240    pub tail: Option<Box<Expr>>,
241    pub span: Span,
242}
243
244/// Unary operators (spec 4.1). Unary binds tighter than any binary
245/// operator.
246#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247pub enum UnaryOp {
248    Neg,
249    Not,
250}
251
252/// Binary operators (spec 4.1), in Rust's precedence order from loosest to
253/// tightest: `||`, `&&`, the comparisons (non-associative), `+ -`, then
254/// `* / %`.
255#[derive(Debug, Clone, Copy, PartialEq, Eq)]
256pub enum BinaryOp {
257    Or,
258    And,
259    Eq,
260    Ne,
261    Lt,
262    Le,
263    Gt,
264    Ge,
265    Add,
266    Sub,
267    Mul,
268    Div,
269    Rem,
270}
271
272impl BinaryOp {
273    /// The operator as written, the same in Varyk and Rust.
274    pub fn as_str(self) -> &'static str {
275        match self {
276            BinaryOp::Or => "||",
277            BinaryOp::And => "&&",
278            BinaryOp::Eq => "==",
279            BinaryOp::Ne => "!=",
280            BinaryOp::Lt => "<",
281            BinaryOp::Le => "<=",
282            BinaryOp::Gt => ">",
283            BinaryOp::Ge => ">=",
284            BinaryOp::Add => "+",
285            BinaryOp::Sub => "-",
286            BinaryOp::Mul => "*",
287            BinaryOp::Div => "/",
288            BinaryOp::Rem => "%",
289        }
290    }
291}
292
293/// An expression node: its kind plus the span it spans in the source.
294#[derive(Debug, Clone, PartialEq)]
295pub struct Expr {
296    pub kind: ExprKind,
297    pub span: Span,
298}
299
300#[derive(Debug, Clone, PartialEq)]
301pub enum ExprKind {
302    /// Raw digits, as lexed: the type checker decides the type from context.
303    Integer(String),
304    /// Raw `digits.digits`, as lexed.
305    Float(String),
306    Bool(bool),
307    /// Raw text between the quotes, as written: escapes are copied unchanged
308    /// into the generated Rust, which resolves them.
309    String(String),
310
311    /// `name`, `module::name`, or `module::Type::name` (spec 2.5, 2.10): a
312    /// plain name, a module-qualified one, or a module-and-type-qualified
313    /// one (an associated function or enum variant reached through a
314    /// module). A two-segment path fills `module` only; a three-segment
315    /// one fills both `module` and `type_`. A path longer than three
316    /// segments is rejected with `V0001` (still built from its first two
317    /// segments and its last, so later passes have something to work
318    /// with). Resolving `type_` is milestone 2 tasks 6 and 7's job; until
319    /// then the compiler rejects it with `V0001` rather than silently
320    /// mis-resolving it.
321    Path {
322        module: Option<Ident>,
323        type_: Option<Ident>,
324        name: Ident,
325    },
326
327    Unary {
328        op: UnaryOp,
329        operand: Box<Expr>,
330    },
331
332    Binary {
333        op: BinaryOp,
334        lhs: Box<Expr>,
335        rhs: Box<Expr>,
336    },
337
338    /// `callee(args...)`. `callee` must be a `Path`, naming a plain
339    /// function or an associated function (spec 2.5); `x.f(args)` is
340    /// method-call syntax and parses as [`ExprKind::MethodCall`] instead,
341    /// never as a `Call` with a `Field` callee. Any other callee shape
342    /// (calling the result of a grouped expression, an index, and so on)
343    /// is rejected: `V0002` from the parser, and defensively `V0001` from
344    /// the type checker if such a node ever reaches it.
345    Call {
346        callee: Box<Expr>,
347        args: Vec<Expr>,
348    },
349
350    Field {
351        base: Box<Expr>,
352        name: Ident,
353    },
354
355    /// `receiver.method(args...)` (spec 2.5): a method call, `value.name(args)`.
356    MethodCall {
357        receiver: Box<Expr>,
358        method: Ident,
359        args: Vec<Expr>,
360    },
361
362    /// `base[index]` (spec 2.6): indexing, `v[i]`. Whether `base` is
363    /// actually a `Vec`, and whether `index` is a `usize`, is the type
364    /// checker's job.
365    Index {
366        base: Box<Expr>,
367        index: Box<Expr>,
368    },
369
370    /// `operand?` (spec 2.8): propagates an `Err` out of the enclosing
371    /// function. Whether `operand` is actually a `Result` compatible with
372    /// the function's return type is the type checker's job.
373    Try {
374        operand: Box<Expr>,
375    },
376
377    /// `match scrutinee { arms... }` (spec 2.3), an expression like `if`.
378    /// It may also stand as a statement without a trailing `;`, the same
379    /// as `if` and a bare block, distinguished the same way by
380    /// `Stmt::Expr`'s `has_semi`.
381    Match {
382        scrutinee: Box<Expr>,
383        arms: Vec<MatchArm>,
384    },
385
386    /// `vec![a, b, c]` (spec 2.9), a compiler intrinsic like `println!`,
387    /// not a macro system: its elements, each an owned slot.
388    VecLit(Vec<Expr>),
389
390    /// `Name { field: expr, ... }`, or `module::Name { field: expr, ... }`
391    /// (spec 2.10). Never parsed in condition position.
392    StructLit {
393        module: Option<Ident>,
394        name: Ident,
395        fields: Vec<(Ident, Expr)>,
396    },
397
398    Block(Block),
399
400    /// `if cond { ... }` or `if cond { ... } else { ... }`. `cond` is
401    /// parsed in condition position, so no struct literal is parsed
402    /// directly in it. An `else if` is represented as `else_` holding a
403    /// synthetic block whose only content is the nested `If` as its tail
404    /// expression.
405    If {
406        cond: Box<Expr>,
407        then: Block,
408        else_: Option<Block>,
409    },
410
411    /// `println!(format, args...)` or `format!(format, args...)` (spec
412    /// 2.9): compiler intrinsics sharing one shape, told apart by `name`.
413    /// `format` keeps the format string's raw text and span; placeholder
414    /// checking is the type checker's job.
415    Intrinsic {
416        name: Ident,
417        format: (String, Span),
418        args: Vec<Expr>,
419    },
420}
421
422/// One arm of a `match` (spec 2.3): `pattern => body`, with an optional
423/// trailing comma the parser consumes but does not record.
424#[derive(Debug, Clone, PartialEq)]
425pub struct MatchArm {
426    pub pattern: Pattern,
427    pub body: Expr,
428    pub span: Span,
429}
430
431/// A `match` pattern, one level deep (spec 2.3). Not in milestone 2, each
432/// `V0001`: nested patterns, literal patterns, guards (`pattern if cond`),
433/// alternatives (`a | b`), rest patterns (`..`), range patterns, and `@`
434/// bindings.
435#[derive(Debug, Clone, PartialEq)]
436pub enum Pattern {
437    /// `_`: matches anything, binds nothing.
438    Wildcard(Span),
439    /// A bare name: matches anything and binds it. The parser cannot tell
440    /// this apart from a zero-argument variant written without its type
441    /// (`None`): the type checker (task 8) decides against the matched
442    /// value's type.
443    Name(Ident),
444    /// A variant, optionally reached through a type and a module:
445    /// `Point`, `Some(p)`, `Shape::Circle(p, q)`, `geo::Shape::Point`.
446    Variant(VariantPattern),
447}
448
449impl Pattern {
450    pub fn span(&self) -> Span {
451        match self {
452            Pattern::Wildcard(span) => *span,
453            Pattern::Name(ident) => ident.span,
454            Pattern::Variant(variant) => variant.span,
455        }
456    }
457}
458
459/// A variant pattern (spec 2.3): `name`, or `type_::name`, or
460/// `module::type_::name`, each with an optional parenthesized list of
461/// sub-patterns. A single remaining segment always fills `type_`, never
462/// `module`: unlike [`ExprKind::Path`], a pattern never calls anything, so
463/// there is no bare-module case to weigh it against. A pattern path longer
464/// than three segments is `V0001`.
465#[derive(Debug, Clone, PartialEq)]
466pub struct VariantPattern {
467    pub module: Option<Ident>,
468    pub type_: Option<Ident>,
469    pub name: Ident,
470    pub subpatterns: Vec<SubPattern>,
471    pub span: Span,
472}
473
474/// A sub-pattern inside a variant pattern's parentheses (spec 2.3): a name
475/// or `_`. Nothing else is one level deep.
476#[derive(Debug, Clone, PartialEq)]
477pub enum SubPattern {
478    Wildcard(Span),
479    Name(Ident),
480}