Skip to main content

decl_lang/
ast.rs

1//! AST of the runtime — the shape the tree-sitter CST lowers into
2//! (parse.rs), mirroring the reference implementation's ast.ts.
3use crate::semantics::Value;
4use std::cell::RefCell;
5use std::collections::HashMap;
6use std::rc::Rc;
7
8/// a source range: zero-based rows and columns (UTF-16 units, as the
9/// reference reads them from web-tree-sitter), end exclusive
10#[derive(Debug, Clone, Copy, PartialEq)]
11pub struct Loc {
12    /// the start line
13    pub sl: usize,
14    /// the start column
15    pub sc: usize,
16    /// the end line
17    pub el: usize,
18    /// the end column, exclusive
19    pub ec: usize,
20}
21
22// Every expression node carries its source range in a side table keyed
23// by the node's address (expressions are shared through `Rc`, so the
24// address is the node's identity, as object identity is in the
25// reference); types, members, and declarations carry `loc` inline.
26thread_local! {
27    static EXPR_LOCS: RefCell<HashMap<usize, Loc>> = RefCell::new(HashMap::new());
28}
29fn expr_key(e: &Rc<Expr>) -> usize {
30    Rc::as_ptr(e) as *const u8 as usize
31}
32/// Record an expression's source range.
33pub fn set_expr_loc(e: &Rc<Expr>, loc: Loc) {
34    EXPR_LOCS.with(|t| t.borrow_mut().insert(expr_key(e), loc));
35}
36/// An expression's source range, when recorded.
37pub fn expr_loc(e: &Rc<Expr>) -> Option<Loc> {
38    EXPR_LOCS.with(|t| t.borrow().get(&expr_key(e)).copied())
39}
40
41#[derive(Debug, Clone)]
42/// a type expression (§3; chapter 11)
43pub enum TypeAst {
44    /// a primitive type by name
45    Prim {
46        /// the name
47        name: String,
48        /// the source range
49        loc: Option<Loc>,
50    },
51    /// a literal type
52    Lit {
53        /// the literal
54        v: Value,
55        /// the source range
56        loc: Option<Loc>,
57    },
58    /// a numeric range
59    Range {
60        /// the lower bound
61        lo: Value,
62        /// the upper bound
63        hi: Value,
64        /// whether the upper bound is excluded
65        excl: bool,
66        /// the source range
67        loc: Option<Loc>,
68    },
69    /// a string pattern
70    Pattern {
71        /// the pattern's text
72        re: String,
73        /// the source range
74        loc: Option<Loc>,
75    },
76    /// a record type
77    Record {
78        /// its members
79        members: Vec<MemberAst>,
80        /// whether it is open (`...`)
81        open: bool,
82        /// the source range
83        loc: Option<Loc>,
84    },
85    /// a map type
86    Map {
87        /// the key type
88        key: Box<TypeAst>,
89        /// the value type
90        val: Box<TypeAst>,
91        /// the source range
92        loc: Option<Loc>,
93    },
94    /// an array type
95    Array {
96        /// the element type
97        elem: Box<TypeAst>,
98        /// the lower size bound
99        lo: Option<Value>,
100        /// the upper size bound
101        hi: Option<Value>,
102        /// whether the upper bound is excluded
103        excl: bool,
104        /// the source range
105        loc: Option<Loc>,
106    },
107    /// a union
108    Union {
109        /// its arms
110        arms: Vec<TypeAst>,
111        /// the source range
112        loc: Option<Loc>,
113    },
114    /// an intersection
115    Isect {
116        /// its arms
117        arms: Vec<TypeAst>,
118        /// the source range
119        loc: Option<Loc>,
120    },
121    /// a function type
122    Func {
123        /// the parameter types
124        params: Vec<TypeAst>,
125        /// the return type
126        ret: Box<TypeAst>,
127        /// the source range
128        loc: Option<Loc>,
129    },
130    /// a named type, with its arguments, predicates, and extension
131    Named {
132        /// the name
133        name: String,
134        /// its type arguments
135        args: Vec<TypeAst>,
136        /// its predicates, when refined
137        preds: Option<Vec<Rc<Expr>>>,
138        /// its extension (`{ … }`)
139        ext: Option<Box<TypeAst>>,
140        /// the source range
141        loc: Option<Loc>,
142    },
143}
144impl TypeAst {
145    /// The source range.
146    pub fn loc(&self) -> Option<Loc> {
147        match self {
148            TypeAst::Prim { loc, .. }
149            | TypeAst::Lit { loc, .. }
150            | TypeAst::Range { loc, .. }
151            | TypeAst::Pattern { loc, .. }
152            | TypeAst::Record { loc, .. }
153            | TypeAst::Map { loc, .. }
154            | TypeAst::Array { loc, .. }
155            | TypeAst::Union { loc, .. }
156            | TypeAst::Isect { loc, .. }
157            | TypeAst::Func { loc, .. }
158            | TypeAst::Named { loc, .. } => *loc,
159        }
160    }
161    /// Record the source range.
162    pub fn set_loc(&mut self, l: Loc) {
163        match self {
164            TypeAst::Prim { loc, .. }
165            | TypeAst::Lit { loc, .. }
166            | TypeAst::Range { loc, .. }
167            | TypeAst::Pattern { loc, .. }
168            | TypeAst::Record { loc, .. }
169            | TypeAst::Map { loc, .. }
170            | TypeAst::Array { loc, .. }
171            | TypeAst::Union { loc, .. }
172            | TypeAst::Isect { loc, .. }
173            | TypeAst::Func { loc, .. }
174            | TypeAst::Named { loc, .. } => *loc = Some(l),
175        }
176    }
177}
178
179#[derive(Debug, Clone)]
180/// a member of a record type (§5)
181pub enum MemberAst {
182    /// a value member: required, optional, or defaulted
183    Value {
184        /// the name
185        name: String,
186        /// optional (`?`)
187        opt: bool,
188        /// the type
189        ty: TypeAst,
190        /// the default, for a defaulted member
191        dflt: Option<Rc<Expr>>,
192        /// the annotations (§5.10)
193        annotations: Vec<Annotation>,
194        /// the source range
195        loc: Option<Loc>,
196    },
197    /// `hidden`: `x$ = e` — computed for the schema's own use, never part of the value (D34)
198    Derived {
199        /// the name
200        name: String,
201        /// the annotation, when given
202        ty: Option<TypeAst>,
203        /// the expression
204        expr: Rc<Expr>,
205        /// hidden (`x$ = e`)
206        hidden: bool,
207        /// the annotations (§5.10)
208        annotations: Vec<Annotation>,
209        /// the source range
210        loc: Option<Loc>,
211    },
212    /// a context declaration (`$parent: ref<P>`, §7.3)
213    Context {
214        /// the variable
215        variable: String,
216        /// the declared type
217        ty: TypeAst,
218        /// the annotations (§5.10)
219        annotations: Vec<Annotation>,
220        /// the source range
221        loc: Option<Loc>,
222    },
223    /// an assertion
224    Assert {
225        /// the name
226        name: String,
227        /// the condition
228        cond: Rc<Expr>,
229        /// the `else` tail
230        tail: Option<Tail>,
231        /// the annotations (§5.10)
232        annotations: Vec<Annotation>,
233        /// the source range
234        loc: Option<Loc>,
235    },
236    /// a guarded group of members (`when`)
237    When {
238        /// the condition
239        cond: Rc<Expr>,
240        /// the members
241        body: Vec<MemberAst>,
242        /// the annotations (§5.10)
243        annotations: Vec<Annotation>,
244        /// the source range
245        loc: Option<Loc>,
246    },
247}
248impl MemberAst {
249    /// The source range.
250    pub fn loc(&self) -> Option<Loc> {
251        match self {
252            MemberAst::Value { loc, .. }
253            | MemberAst::Derived { loc, .. }
254            | MemberAst::Context { loc, .. }
255            | MemberAst::Assert { loc, .. }
256            | MemberAst::When { loc, .. } => *loc,
257        }
258    }
259    /// Record the source range.
260    pub fn set_loc(&mut self, l: Loc) {
261        match self {
262            MemberAst::Value { loc, .. }
263            | MemberAst::Derived { loc, .. }
264            | MemberAst::Context { loc, .. }
265            | MemberAst::Assert { loc, .. }
266            | MemberAst::When { loc, .. } => *loc = Some(l),
267        }
268    }
269    /// Attach the annotations (§5.10).
270    pub fn set_annotations(&mut self, a: Vec<Annotation>) {
271        match self {
272            MemberAst::Value { annotations, .. }
273            | MemberAst::Derived { annotations, .. }
274            | MemberAst::Context { annotations, .. }
275            | MemberAst::Assert { annotations, .. }
276            | MemberAst::When { annotations, .. } => *annotations = a,
277        }
278    }
279    /// the annotations (§5.10)
280    pub fn annotations(&self) -> &[Annotation] {
281        match self {
282            MemberAst::Value { annotations, .. }
283            | MemberAst::Derived { annotations, .. }
284            | MemberAst::Context { annotations, .. }
285            | MemberAst::Assert { annotations, .. }
286            | MemberAst::When { annotations, .. } => annotations,
287        }
288    }
289    /// the member's name (`name` in the reference: value, derived, and assert members)
290    pub fn name(&self) -> Option<&str> {
291        match self {
292            MemberAst::Value { name, .. }
293            | MemberAst::Derived { name, .. }
294            | MemberAst::Assert { name, .. } => Some(name),
295            _ => None,
296        }
297    }
298}
299
300#[derive(Debug, Clone)]
301/// a part of a template: text, or an interpolated expression
302pub enum TPart {
303    /// text
304    Text(String),
305    /// an interpolated expression
306    Expr(Rc<Expr>),
307}
308
309#[derive(Debug, Clone)]
310/// an `else` tail: an inline message with a severity, or a diagnostic reference
311pub enum Tail {
312    /// an inline message
313    Inline {
314        /// the severity
315        severity: String,
316        /// the message template
317        template: Vec<TPart>,
318    },
319    /// a diagnostic reference
320    Ref {
321        /// the diagnostic's name
322        name: String,
323        /// its arguments
324        args: Vec<Rc<Expr>>,
325    },
326}
327
328#[derive(Debug, Clone)]
329/// a comprehension clause
330pub struct ForClause {
331    /// the variable
332    pub v: String,
333    /// what it ranges over
334    pub iter: Rc<Expr>,
335    /// the `if` filters
336    pub filters: Vec<Rc<Expr>>,
337}
338
339#[derive(Debug, Clone)]
340/// an arm of a `match`
341pub struct MatchArm {
342    /// the variable
343    pub v: String,
344    /// the type it matches; none for the catch-all
345    pub ty: Option<TypeAst>,
346    /// the body
347    pub body: Rc<Expr>,
348}
349
350#[derive(Debug, Clone)]
351/// an expression (§4)
352pub enum Expr {
353    /// a literal
354    Lit(Value),
355    /// a unit literal
356    UnitLit {
357        /// the number
358        num: f64,
359        /// the unit
360        unit: String,
361    },
362    /// a template string
363    Template(Vec<TPart>),
364    /// a name
365    Name(String),
366    /// a context variable (`$this`, `$parent`, `$root`, `$key`)
367    Ctx(String),
368    /// `$referrers(T, "m")`
369    Referrers {
370        /// the referring type
371        ty: String,
372        /// its member
373        member: String,
374    },
375    /// a record literal
376    Obj(Vec<(String, Rc<Expr>)>),
377    /// an array literal: (spread, item)
378    Arr(Vec<(bool, Rc<Expr>)>),
379    /// an array comprehension
380    Comp {
381        /// the head
382        head: Rc<Expr>,
383        /// the clauses
384        clauses: Vec<ForClause>,
385    },
386    /// a map comprehension
387    MapComp {
388        /// the key
389        key: Rc<Expr>,
390        /// the value
391        val: Rc<Expr>,
392        /// the clauses
393        clauses: Vec<ForClause>,
394    },
395    /// a binary operation
396    Bin {
397        /// the operator
398        op: String,
399        /// the left operand
400        l: Rc<Expr>,
401        /// the right operand
402        r: Rc<Expr>,
403    },
404    /// a unary operation
405    Un {
406        /// the operator
407        op: String,
408        /// the operand
409        x: Rc<Expr>,
410    },
411    /// a parenthesized expression
412    Paren(Rc<Expr>),
413    /// `if … then … else`
414    If {
415        /// the condition
416        c: Rc<Expr>,
417        /// the `then` branch
418        t: Rc<Expr>,
419        /// the `else` branch
420        f: Rc<Expr>,
421    },
422    /// a function literal
423    Lambda {
424        /// the parameters
425        params: Vec<String>,
426        /// the body
427        body: Rc<Expr>,
428    },
429    /// a call
430    Call {
431        /// the function
432        fun: Rc<Expr>,
433        /// the arguments
434        args: Vec<Rc<Expr>>,
435    },
436    /// a member access
437    Member {
438        /// the record
439        x: Rc<Expr>,
440        /// the member
441        name: String,
442        /// `?.`
443        safe: bool,
444    },
445    /// an index or key access
446    Index {
447        /// the array or map
448        x: Rc<Expr>,
449        /// the index or key
450        i: Rc<Expr>,
451    },
452    /// a record update (`with`)
453    With {
454        /// the base
455        base: Rc<Expr>,
456        /// the patch
457        patch: Rc<Expr>,
458    },
459    /// a pattern literal
460    Pattern(String),
461    /// a `match`
462    Match {
463        /// the subject
464        subject: Rc<Expr>,
465        /// the arms
466        arms: Vec<MatchArm>,
467    },
468}
469
470#[derive(Debug, Clone)]
471/// an annotation (§5.10): `@name` or `@name(args)` — metadata only (D4)
472pub struct Annotation {
473    /// the name
474    pub name: String,
475    /// the arguments
476    pub args: Vec<Rc<Expr>>,
477    /// the source range
478    pub loc: Option<Loc>,
479}
480
481#[derive(Debug, Clone)]
482/// a parameter of a function, a type, or a diagnostic
483pub struct Param {
484    /// the name
485    pub name: String,
486    /// the type, when annotated
487    pub ty: Option<TypeAst>,
488}
489
490#[derive(Debug, Clone)]
491/// an imported name, possibly renamed
492pub struct ImportItem {
493    /// the name
494    pub name: String,
495    /// the alias
496    pub alias: Option<String>,
497}
498
499// `ret` is lowered for fidelity with the reference AST; the runtime does
500// not check return annotations (that is the static checker's job)
501#[allow(dead_code)]
502#[derive(Debug, Clone)]
503/// a declaration (§5, §8)
504pub enum DeclBody {
505    /// a type
506    Type {
507        /// the name
508        name: String,
509        /// the type parameters
510        params: Vec<Param>,
511        /// the type
512        ty: TypeAst,
513        /// its `else` tail
514        tail: Option<Tail>,
515    },
516    /// a constant
517    Const {
518        /// the name
519        name: String,
520        /// the annotation, when given
521        ty: Option<TypeAst>,
522        /// the expression
523        expr: Rc<Expr>,
524    },
525    /// a function
526    Func {
527        /// the name
528        name: String,
529        /// the parameters
530        params: Vec<Param>,
531        /// the return type, when annotated
532        ret: Option<TypeAst>,
533        /// the body
534        body: Rc<Expr>,
535    },
536    /// an output root
537    Output {
538        /// the name
539        name: String,
540        /// the type
541        ty: TypeAst,
542        /// the expression
543        expr: Rc<Expr>,
544    },
545    /// an input root
546    Input {
547        /// the name
548        name: String,
549        /// the type
550        ty: TypeAst,
551        /// the fallback, when given
552        fallback: Option<Rc<Expr>>,
553    },
554    /// a diagnostic template
555    Diagnostic {
556        /// the name
557        name: String,
558        /// the parameters
559        params: Vec<Param>,
560        /// the severity
561        severity: String,
562        /// the message template
563        template: Vec<TPart>,
564    },
565    /// a dimension
566    Dimension {
567        /// the name
568        name: String,
569        /// its definition as (dimension, exponent) terms; none for a base dimension
570        terms: Option<Vec<(String, i32)>>,
571    },
572    /// a unit
573    Unit {
574        /// the name
575        name: String,
576        /// its dimension
577        dim: Option<String>,
578        /// its factor against the base unit
579        factor: Option<Rc<Expr>>,
580        /// the base unit it is defined against
581        base: Option<String>,
582    },
583    /// an import
584    Import {
585        /// the module
586        from: String,
587        /// the names imported; none for a namespace import
588        names: Option<Vec<ImportItem>>,
589        /// the namespace, for `import * as ns`
590        ns: Option<String>,
591    },
592    /// a re-export
593    ReExport {
594        /// the module
595        from: String,
596        /// the names
597        names: Vec<ImportItem>,
598    },
599}
600
601#[derive(Debug, Clone)]
602/// a declaration, with whether it is exported and its source range
603pub struct Decl {
604    /// the declaration
605    pub body: DeclBody,
606    /// `export`
607    pub exported: bool,
608    /// the annotations (§5.10)
609    pub annotations: Vec<Annotation>,
610    /// the declaration's source range (Phase 6 foundations); `export` included
611    pub loc: Option<Loc>,
612}
613
614impl Decl {
615    /// The declared name, when the declaration has one.
616    pub fn name(&self) -> Option<&str> {
617        match &self.body {
618            DeclBody::Type { name, .. }
619            | DeclBody::Const { name, .. }
620            | DeclBody::Func { name, .. }
621            | DeclBody::Output { name, .. }
622            | DeclBody::Input { name, .. }
623            | DeclBody::Diagnostic { name, .. }
624            | DeclBody::Dimension { name, .. }
625            | DeclBody::Unit { name, .. } => Some(name),
626            _ => None,
627        }
628    }
629}
630
631/// does an expression mention `$referrers` anywhere? (deferred slots)
632pub fn mentions_referrers(e: &Expr) -> bool {
633    match e {
634        Expr::Referrers { .. } => true,
635        Expr::Lit(_) | Expr::UnitLit { .. } | Expr::Name(_) | Expr::Ctx(_) | Expr::Pattern(_) => {
636            false
637        }
638        Expr::Template(parts) => parts
639            .iter()
640            .any(|p| matches!(p, TPart::Expr(x) if mentions_referrers(x))),
641        Expr::Obj(es) => es.iter().any(|(_, v)| mentions_referrers(v)),
642        Expr::Arr(items) => items.iter().any(|(_, v)| mentions_referrers(v)),
643        Expr::Comp { head, clauses } => {
644            mentions_referrers(head) || clauses.iter().any(clause_mentions)
645        }
646        Expr::MapComp { key, val, clauses } => {
647            mentions_referrers(key)
648                || mentions_referrers(val)
649                || clauses.iter().any(clause_mentions)
650        }
651        Expr::Bin { l, r, .. } => mentions_referrers(l) || mentions_referrers(r),
652        Expr::Un { x, .. } | Expr::Paren(x) => mentions_referrers(x),
653        Expr::If { c, t, f } => {
654            mentions_referrers(c) || mentions_referrers(t) || mentions_referrers(f)
655        }
656        Expr::Lambda { body, .. } => mentions_referrers(body),
657        Expr::Call { fun, args } => {
658            mentions_referrers(fun) || args.iter().any(|a| mentions_referrers(a))
659        }
660        Expr::Member { x, .. } => mentions_referrers(x),
661        Expr::Index { x, i } => mentions_referrers(x) || mentions_referrers(i),
662        Expr::With { base, patch } => mentions_referrers(base) || mentions_referrers(patch),
663        Expr::Match { subject, arms } => {
664            mentions_referrers(subject) || arms.iter().any(|a| mentions_referrers(&a.body))
665        }
666    }
667}
668
669fn clause_mentions(c: &ForClause) -> bool {
670    mentions_referrers(&c.iter) || c.filters.iter().any(|f| mentions_referrers(f))
671}
672
673/// A short name for an expression, for messages.
674pub fn expr_name(e: &Expr) -> String {
675    match e {
676        Expr::Name(n) => n.clone(),
677        Expr::Call { fun, .. } => expr_name(fun),
678        _ => "<predicate>".into(),
679    }
680}