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}