cl_ast/ast.rs
1//! The Abstract Syntax Tree defines an interface between the parser and type checker
2
3use std::hash::Hash;
4
5mod display;
6
7pub mod fold;
8pub mod macro_matcher;
9pub mod types;
10pub mod visit;
11
12pub use types::DefaultTypes;
13
14/// Common bounds on AST nodes
15pub trait AstNode: Clone + std::fmt::Display + std::fmt::Debug + PartialEq + Eq + Hash {}
16
17impl<T: Clone + std::fmt::Debug + std::fmt::Display + PartialEq + Eq + Hash> AstNode for T {}
18
19pub trait AstTypes: AstNode {
20 /// An annotation on an arbitrary [Expr] or [Pat]
21 type Annotation: AstNode + Copy;
22
23 /// A literal value
24 type Literal: AstNode;
25
26 /// A (possibly interned) symbol or index which implements [`AsRef<str>`]
27 type MacroId: AstNode + Hash + AsRef<str>;
28
29 /// A (possibly interned) symbol or index
30 type Symbol: AstNode + Copy + Hash;
31
32 /// A (possibly compound) symbol or index
33 type Path: AstNode;
34}
35
36/// A value with an annotation.
37#[derive(Clone, PartialEq, Eq, Hash)]
38pub struct At<T: AstNode, A: AstTypes = DefaultTypes>(pub T, pub A::Annotation);
39
40impl<T: AstNode, A: AstTypes> At<T, A> {
41 pub fn value(&self) -> &T {
42 &self.0
43 }
44 pub fn a(&self) -> &A::Annotation {
45 &self.1
46 }
47 pub fn map<U: AstNode>(self, f: impl FnOnce(T) -> U) -> At<U, A> {
48 At(f(self.0), self.1)
49 }
50 pub fn map_ref<U: AstNode>(&self, f: impl FnOnce(&T) -> U) -> At<U, A> {
51 At(f(&self.0), self.1)
52 }
53 pub fn map_a<B: AstTypes>(self, f: impl FnOnce(A::Annotation) -> B::Annotation) -> At<T, B> {
54 At(self.0, f(self.1))
55 }
56}
57
58/// Expressions: The beating heart of Conlang.
59///
60/// A program in Conlang is a single expression which, at compile time,
61/// sets up the state in which a program will run. This expression binds types,
62/// functions, and values to names which are exposed at runtime.
63///
64/// Whereas in the body of a function, `do` sequences are ordered, in the global
65/// scope (or subsequent module scopes, which are children of the global module,)
66/// `do` sequences are considered unordered, and subexpressions may be reordered
67/// in whichever way the compiler sees fit. This is especially important when
68/// performing import resolution, as imports typically depend on the order
69/// in which names are bound.
70#[derive(Clone, PartialEq, Eq, Hash)]
71pub enum Expr<A: AstTypes = DefaultTypes> {
72 /// Omitted by semicolon insertion-elision rules
73 Omitted,
74 /// An identifier
75 Id(A::Path),
76 /// An escaped token for macro binding
77 MetId(A::MacroId),
78 /// A literal bool, string, char, or int
79 Lit(A::Literal),
80 /// use Use
81 Use(Use<A>),
82 /// `let Pat::NoTopAlt (= expr (else expr)?)?` |
83 /// `(fn | mod | impl) Pat::Fn Expr`
84 Bind(Box<Bind<A>>),
85 /// Expr { (Ident (: Expr)?),* }
86 Make(Box<Make<A>>),
87 /// `match Expr { (Pat => Expr),* }`
88 Match(Box<Match<A>>),
89 /// Op Expr | Expr Op | Expr (Op Expr)+ | Op Expr Expr else Expr
90 Op(Op, Vec<At<Self, A>>),
91}
92
93/// Conlang's AST is partitioned by data representation, so it
94/// considers any expression which is composed solely of keywords,
95/// symbols, and other expressions as operator expressions.
96///
97/// This includes:
98/// - Do-sequence expressions: `Expr ; Expr `
99/// - Type-cast expressions `Expr as Expr`
100/// - Binding-modifier expressions: `pub Expr`, `#[Expr] Expr`
101/// - Block and Group expressions: `{Expr?}`, `(Expr?)`
102/// - Control flow: `if`, `while`, `loop`, `match`, `break`, `return`
103/// - Function calls `Expr (Expr,*)`
104/// - Traditional binary and unary operators (add, sub, neg, assign)
105#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
106pub enum Op {
107 /// `Expr (; Expr)*`
108 Do,
109 /// `Expr as Expr`
110 As,
111 /// `{ Expr }`
112 Block,
113 /// `[ Expr,* ]`
114 Array,
115 /// `[ Expr ; Expr ]`
116 ArRep,
117 /// `( Expr )`
118 Group,
119 /// `Expr (, Expr)*`
120 Tuple,
121 /// `#![ Expr ]`
122 MetaInner,
123 /// `#[ Expr ]`
124 MetaOuter,
125
126 /// `Expr '?'`
127 Try,
128 /// `Expr [ Expr ]`
129 Index,
130 /// `Expr ( Expr )`
131 Call,
132
133 /// `pub Expr`
134 Pub,
135 /// `const Expr`
136 Const,
137 /// `static Expr`
138 Static,
139 /// `macro Expr`
140 Macro,
141 /// <code>`Expr`</code>
142 Quote,
143 /// `loop Expr`
144 Loop,
145 /// `if Expr Expr (else Expr)?`
146 If,
147 /// `while Expr Expr (else Expr)?`
148 While,
149 /// `defer Expr`
150 Defer,
151 /// `break Expr`
152 Break,
153 /// `return Expr`
154 Return,
155 /// `continue`
156 Continue,
157
158 /// `Expr . Expr`
159 Dot,
160
161 /// `Expr? ..Expr`
162 RangeEx,
163 /// `Expr? ..=Expr`
164 RangeIn,
165 /// `-Expr`
166 Neg,
167 /// `!Expr`
168 Not,
169 /// `!!Expr`
170 Identity,
171 /// `&Expr`
172 Refer,
173 /// `*Expr`
174 Deref,
175
176 /// `Expr * Expr`
177 Mul,
178 /// `Expr / Expr`
179 Div,
180 /// `Expr % Expr`
181 Rem,
182
183 /// `Expr + Expr`
184 Add,
185 /// `Expr - Expr`
186 Sub,
187
188 /// `Expr << Expr`
189 Shl,
190 /// `Expr >> Expr`
191 Shr,
192
193 /// `Expr & Expr`
194 And,
195 /// `Expr ^ Expr`
196 Xor,
197 /// `Expr | Expr`
198 Or,
199
200 /// `Expr < Expr`
201 Lt,
202 /// `Expr <= Expr`
203 Leq,
204 /// `Expr == Expr`
205 Eq,
206 /// `Expr != Expr`
207 Neq,
208 /// `Expr >= Expr`
209 Geq,
210 /// `Expr > Expr`
211 Gt,
212
213 /// `Expr && Expr`
214 LogAnd,
215 /// `Expr ^^ Expr`
216 LogXor,
217 /// `Expr || Expr`
218 LogOr,
219
220 /// `Expr = Expr`
221 Set,
222 /// `Expr *= Expr`
223 MulSet,
224 /// `Expr /= Expr`
225 DivSet,
226 /// `Expr %= Expr`
227 RemSet,
228 /// `Expr += Expr`
229 AddSet,
230 /// `Expr -= Expr`
231 SubSet,
232 /// `Expr <<= Expr`
233 ShlSet,
234 /// `Expr >>= Expr`
235 ShrSet,
236 /// `Expr &= Expr`
237 AndSet,
238 /// `Expr ^= Expr`
239 XorSet,
240 /// `Expr |= Expr`
241 OrSet,
242}
243
244impl<A: AstTypes> Expr<A> {
245 /// Attaches this [Expr] to an [At] node with the provided [AstTypes::Annotation].
246 pub const fn at(self, annotation: A::Annotation) -> At<Expr<A>, A> {
247 At(self, annotation)
248 }
249
250 /// Attaches another expression to this one, continuing a [`do`](Op::Do) chain if possible.
251 pub fn and_do(self, annotation: A::Annotation, other: At<Expr<A>, A>) -> Self {
252 let Self::Op(Op::Do, mut exprs) = self else {
253 return Self::Op(Op::Do, vec![self.at(annotation), other]);
254 };
255 let At(Self::Op(Op::Do, mut other), _) = other else {
256 exprs.push(other);
257 return Self::Op(Op::Do, exprs);
258 };
259 exprs.append(&mut other);
260 Self::Op(Op::Do, exprs)
261 }
262
263 /// Removes omitted expressions from positions where expressions are allowed to be omitted.
264 pub fn deomit(self) -> Self {
265 let Self::Op(op @ (Op::Do | Op::Tuple | Op::Array), mut exprs) = self else {
266 return self;
267 };
268 exprs.retain(|expr| !matches!(expr.0, Self::Omitted));
269 Self::Op(op, exprs)
270 }
271
272 /// Turns this expression into a [`tuple`](Op::Tuple) if it isn't already one.
273 pub fn to_tuple(self, annotation: A::Annotation) -> Self {
274 match self {
275 Self::Op(Op::Tuple, _) => self,
276 _ => Self::Op(Op::Tuple, vec![self.at(annotation)]),
277 }
278 }
279
280 /// Returns whether `self` is a "place projection" expression (identifier, index, dot, or deref)
281 pub const fn is_place(&self) -> bool {
282 matches!(
283 self,
284 Self::Id(_) | Self::Op(Op::Index | Op::Dot | Op::Deref, _)
285 )
286 }
287
288 /// Returns whether `self` is NOT a "place projection" expression
289 pub const fn is_value(&self) -> bool {
290 !self.is_place()
291 }
292
293 /// If `self` is an [Expr::Op], returns the [Op] and a slice of its arguments.
294 #[allow(clippy::type_complexity)]
295 pub const fn as_slice(&self) -> Option<(Op, &[At<Expr<A>, A>])> {
296 match self {
297 Expr::Op(op, args) => Some((*op, args.as_slice())),
298 _ => None,
299 }
300 }
301}
302
303/// A pattern binding
304/// ```ignore
305/// let Pat (= Expr (else Expr)?)?
306/// type Pat (= Expr)?
307/// fn Pat =? Expr
308/// mod Pat =? Expr
309/// impl Pat =? Expr
310/// struct Pat
311/// enum Pat
312/// for Pat in Expr Expr (else Expr)?
313/// ```
314#[derive(Clone, PartialEq, Eq, Hash)]
315pub struct Bind<A: AstTypes = DefaultTypes>(
316 pub BindOp,
317 pub Vec<A::Path>,
318 pub At<Pat<A>, A>,
319 pub Vec<At<Expr<A>, A>>,
320);
321
322/// The binding operation used by a [Bind].
323///
324/// See [Bind] for their syntactic representations
325#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
326pub enum BindOp {
327 /// A `let Pat (= Expr (else Expr)?)?` binding
328 Let,
329 /// A type-alias binding
330 Type,
331 /// A `fn Pat Expr` binding
332 Fn,
333 /// A `mod Pat Expr` binding
334 Mod,
335 /// An `impl Pat Expr` binding
336 Impl,
337 /// A struct definition
338 Struct,
339 /// An enum definition
340 Enum,
341 /// A `for Pat in Expr Expr (else Expr)?` binding
342 For,
343}
344
345/// Binding patterns for each kind of matchable value.
346///
347/// This covers both bindings and type annotations in [Bind] expressions.
348#[derive(Clone, PartialEq, Eq, Hash)]
349pub enum Pat<A: AstTypes = DefaultTypes> {
350 /// `_`: Matches anything without binding
351 Ignore,
352 /// `!`: Matches nothing, ever
353 Never,
354 /// `$Token`: Matches nothing; used for macro substitution
355 MetId(A::MacroId),
356 /// `Identifier`: Matches anything, and binds it to a name
357 Name(A::Symbol),
358 /// `Expr`: Matches a value by equality comparison
359 Value(Box<At<Expr<A>, A>>),
360 /// Matches a compound pattern
361 Op(PatOp, Vec<At<Pat<A>, A>>),
362}
363
364/// Operators on lists of patterns
365#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
366pub enum PatOp {
367 /// `#![ .* ] Pat`
368 MetaInner,
369 /// `#[ .* ] Pat`
370 MetaOuter,
371 /// Changes the visibility mode to "public"
372 Pub,
373 /// Changes the binding mode to "mutable"
374 Mut,
375 /// Matches the dereference of a pointer (`&pat`)
376 Ref,
377 /// Matches the dereference of a raw pointer (`*pat`)
378 Ptr,
379 /// Matches a partial decomposition (`..rest`) or upper-bounded range (`..100`)
380 Rest,
381 /// Matches an exclusive bounded range (`0..100`)
382 RangeEx,
383 /// Matches an inclusive bounded range (`0..=100`)
384 RangeIn,
385 /// Matches the elements of a record or struct { a, b, c }
386 Record,
387 /// Matches the elements of a tuple ( a, b, c )
388 Tuple,
389 /// Matches the elements of a slice or array [ a, b, c ]
390 Slice,
391 /// Matches a constant-size slice with repeating elements
392 ArRep,
393 /// Matches a type annotation or struct member
394 Typed,
395 /// Matches a prefix-type-annotated structure
396 TypePrefixed,
397 /// Matches a generic specialization annotation
398 Generic,
399 /// Changes the binding mode to "function-body"
400 Fn,
401 /// Matches a guard pattern (`Pat if Expr`)
402 Guard,
403 /// Matches one of a list of alternatives
404 Alt,
405}
406
407impl<A: AstTypes> Pat<A> {
408 /// Attaches this [Pat] to an [At] node with the provided [AstTypes::Annotation].
409 pub const fn at(self, annotation: A::Annotation) -> At<Pat<A>, A> {
410 At(self, annotation)
411 }
412 /// Turns this pattern into a [`tuple`](PatOp::Tuple) if it isn't already one.
413 pub fn to_tuple(self, annotation: A::Annotation) -> Self {
414 match self {
415 Self::Op(PatOp::Tuple, _) => self,
416 _ => Self::Op(PatOp::Tuple, vec![self.at(annotation)]),
417 }
418 }
419 /// Returns the single, unambiguous name bound by this pattern, if there is one.
420 ///
421 /// Else, returns [None].
422 pub fn name(&self) -> Option<A::Symbol> {
423 match self {
424 Self::Name(name) => Some(*name),
425 Self::Op(
426 PatOp::TypePrefixed
427 | PatOp::Typed
428 | PatOp::Pub
429 | PatOp::Mut
430 | PatOp::Generic
431 | PatOp::Guard,
432 pats,
433 ) if let [At(pat, _), ..] = &pats[..] => pat.name(),
434 _ => None,
435 }
436 }
437}
438
439/// A compound import declaration
440#[derive(Clone, Debug, PartialEq, Eq, Hash)]
441pub enum Use<A: AstTypes = DefaultTypes> {
442 /// "*"
443 Glob,
444 /// Identifier
445 Name(A::Symbol),
446 /// Identifier as Identifier
447 Alias(A::Symbol, A::Symbol),
448 /// Identifier :: Use
449 Path(A::Symbol, Box<Use<A>>),
450 /// { Use, * }
451 Tree(Vec<Use<A>>),
452}
453
454/// A make (constructor) expression
455/// ```ignore
456/// Expr { (Ident (: Expr)?),* }
457/// ```
458#[derive(Clone, Debug, PartialEq, Eq, Hash)]
459pub struct Make<A: AstTypes = DefaultTypes>(pub At<Expr<A>, A>, pub Vec<MakeArm<A>>);
460
461/// A single "arm" of a make expression
462/// ```text
463/// Identifier (':' Expr)?
464/// ```
465#[derive(Clone, Debug, PartialEq, Eq, Hash)]
466pub struct MakeArm<A: AstTypes = DefaultTypes>(pub A::Symbol, pub Option<At<Expr<A>, A>>);
467
468/// A match expression has a scrutinee and zero or more arms
469#[derive(Clone, Debug, PartialEq, Eq, Hash)]
470pub struct Match<A: AstTypes = DefaultTypes>(pub At<Expr<A>, A>, pub Vec<MatchArm<A>>);
471
472/// A single arm of a `match` expression
473/// ```text
474/// Pat => Expr
475/// ```
476#[derive(Clone, Debug, PartialEq, Eq, Hash)]
477pub struct MatchArm<A: AstTypes = DefaultTypes>(pub At<Pat<A>, A>, pub At<Expr<A>, A>);