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    pub sl: usize,
13    pub sc: usize,
14    pub el: usize,
15    pub ec: usize,
16}
17
18// Every expression node carries its source range in a side table keyed
19// by the node's address (expressions are shared through `Rc`, so the
20// address is the node's identity, as object identity is in the
21// reference); types, members, and declarations carry `loc` inline.
22thread_local! {
23    static EXPR_LOCS: RefCell<HashMap<usize, Loc>> = RefCell::new(HashMap::new());
24}
25fn expr_key(e: &Rc<Expr>) -> usize {
26    Rc::as_ptr(e) as *const u8 as usize
27}
28pub fn set_expr_loc(e: &Rc<Expr>, loc: Loc) {
29    EXPR_LOCS.with(|t| t.borrow_mut().insert(expr_key(e), loc));
30}
31pub fn expr_loc(e: &Rc<Expr>) -> Option<Loc> {
32    EXPR_LOCS.with(|t| t.borrow().get(&expr_key(e)).copied())
33}
34
35#[derive(Debug, Clone)]
36pub enum TypeAst {
37    Prim {
38        name: String,
39        loc: Option<Loc>,
40    },
41    Lit {
42        v: Value,
43        loc: Option<Loc>,
44    },
45    Range {
46        lo: Value,
47        hi: Value,
48        excl: bool,
49        loc: Option<Loc>,
50    },
51    Pattern {
52        re: String,
53        loc: Option<Loc>,
54    },
55    Record {
56        members: Vec<MemberAst>,
57        open: bool,
58        loc: Option<Loc>,
59    },
60    Map {
61        key: Box<TypeAst>,
62        val: Box<TypeAst>,
63        loc: Option<Loc>,
64    },
65    Array {
66        elem: Box<TypeAst>,
67        lo: Option<Value>,
68        hi: Option<Value>,
69        excl: bool,
70        loc: Option<Loc>,
71    },
72    Union {
73        arms: Vec<TypeAst>,
74        loc: Option<Loc>,
75    },
76    Isect {
77        arms: Vec<TypeAst>,
78        loc: Option<Loc>,
79    },
80    Func {
81        params: Vec<TypeAst>,
82        ret: Box<TypeAst>,
83        loc: Option<Loc>,
84    },
85    Named {
86        name: String,
87        args: Vec<TypeAst>,
88        preds: Option<Vec<Rc<Expr>>>,
89        ext: Option<Box<TypeAst>>,
90        loc: Option<Loc>,
91    },
92}
93impl TypeAst {
94    pub fn loc(&self) -> Option<Loc> {
95        match self {
96            TypeAst::Prim { loc, .. }
97            | TypeAst::Lit { loc, .. }
98            | TypeAst::Range { loc, .. }
99            | TypeAst::Pattern { loc, .. }
100            | TypeAst::Record { loc, .. }
101            | TypeAst::Map { loc, .. }
102            | TypeAst::Array { loc, .. }
103            | TypeAst::Union { loc, .. }
104            | TypeAst::Isect { loc, .. }
105            | TypeAst::Func { loc, .. }
106            | TypeAst::Named { loc, .. } => *loc,
107        }
108    }
109    pub fn set_loc(&mut self, l: Loc) {
110        match self {
111            TypeAst::Prim { loc, .. }
112            | TypeAst::Lit { loc, .. }
113            | TypeAst::Range { loc, .. }
114            | TypeAst::Pattern { loc, .. }
115            | TypeAst::Record { loc, .. }
116            | TypeAst::Map { loc, .. }
117            | TypeAst::Array { loc, .. }
118            | TypeAst::Union { loc, .. }
119            | TypeAst::Isect { loc, .. }
120            | TypeAst::Func { loc, .. }
121            | TypeAst::Named { loc, .. } => *loc = Some(l),
122        }
123    }
124}
125
126#[derive(Debug, Clone)]
127pub enum MemberAst {
128    Value {
129        name: String,
130        opt: bool,
131        ty: TypeAst,
132        dflt: Option<Rc<Expr>>,
133        loc: Option<Loc>,
134    },
135    /// `hidden`: `x$ = e` — computed for the schema's own use, never part of the value (D34)
136    Derived {
137        name: String,
138        ty: Option<TypeAst>,
139        expr: Rc<Expr>,
140        hidden: bool,
141        loc: Option<Loc>,
142    },
143    Context {
144        variable: String,
145        ty: TypeAst,
146        loc: Option<Loc>,
147    },
148    Assert {
149        name: String,
150        cond: Rc<Expr>,
151        tail: Option<Tail>,
152        loc: Option<Loc>,
153    },
154    When {
155        cond: Rc<Expr>,
156        body: Vec<MemberAst>,
157        loc: Option<Loc>,
158    },
159}
160impl MemberAst {
161    pub fn loc(&self) -> Option<Loc> {
162        match self {
163            MemberAst::Value { loc, .. }
164            | MemberAst::Derived { loc, .. }
165            | MemberAst::Context { loc, .. }
166            | MemberAst::Assert { loc, .. }
167            | MemberAst::When { loc, .. } => *loc,
168        }
169    }
170    pub fn set_loc(&mut self, l: Loc) {
171        match self {
172            MemberAst::Value { loc, .. }
173            | MemberAst::Derived { loc, .. }
174            | MemberAst::Context { loc, .. }
175            | MemberAst::Assert { loc, .. }
176            | MemberAst::When { loc, .. } => *loc = Some(l),
177        }
178    }
179    /// the member's name (`name` in the reference: value, derived, and assert members)
180    pub fn name(&self) -> Option<&str> {
181        match self {
182            MemberAst::Value { name, .. }
183            | MemberAst::Derived { name, .. }
184            | MemberAst::Assert { name, .. } => Some(name),
185            _ => None,
186        }
187    }
188}
189
190#[derive(Debug, Clone)]
191pub enum TPart {
192    Text(String),
193    Expr(Rc<Expr>),
194}
195
196#[derive(Debug, Clone)]
197pub enum Tail {
198    Inline {
199        severity: String,
200        template: Vec<TPart>,
201    },
202    Ref {
203        name: String,
204        args: Vec<Rc<Expr>>,
205    },
206}
207
208#[derive(Debug, Clone)]
209pub struct ForClause {
210    pub v: String,
211    pub iter: Rc<Expr>,
212    pub filters: Vec<Rc<Expr>>,
213}
214
215#[derive(Debug, Clone)]
216pub struct MatchArm {
217    pub v: String,
218    pub ty: Option<TypeAst>,
219    pub body: Rc<Expr>,
220}
221
222#[derive(Debug, Clone)]
223pub enum Expr {
224    Lit(Value),
225    UnitLit {
226        num: f64,
227        unit: String,
228    },
229    Template(Vec<TPart>),
230    Name(String),
231    Ctx(String),
232    Referrers {
233        ty: String,
234        member: String,
235    },
236    Obj(Vec<(String, Rc<Expr>)>),
237    Arr(Vec<(bool, Rc<Expr>)>),
238    Comp {
239        head: Rc<Expr>,
240        clauses: Vec<ForClause>,
241    },
242    MapComp {
243        key: Rc<Expr>,
244        val: Rc<Expr>,
245        clauses: Vec<ForClause>,
246    },
247    Bin {
248        op: String,
249        l: Rc<Expr>,
250        r: Rc<Expr>,
251    },
252    Un {
253        op: String,
254        x: Rc<Expr>,
255    },
256    Paren(Rc<Expr>),
257    If {
258        c: Rc<Expr>,
259        t: Rc<Expr>,
260        f: Rc<Expr>,
261    },
262    Lambda {
263        params: Vec<String>,
264        body: Rc<Expr>,
265    },
266    Call {
267        fun: Rc<Expr>,
268        args: Vec<Rc<Expr>>,
269    },
270    Member {
271        x: Rc<Expr>,
272        name: String,
273        safe: bool,
274    },
275    Index {
276        x: Rc<Expr>,
277        i: Rc<Expr>,
278    },
279    With {
280        base: Rc<Expr>,
281        patch: Rc<Expr>,
282    },
283    Pattern(String),
284    Match {
285        subject: Rc<Expr>,
286        arms: Vec<MatchArm>,
287    },
288}
289
290#[derive(Debug, Clone)]
291pub struct Param {
292    pub name: String,
293    pub ty: Option<TypeAst>,
294}
295
296#[derive(Debug, Clone)]
297pub struct ImportItem {
298    pub name: String,
299    pub alias: Option<String>,
300}
301
302// `ret` is lowered for fidelity with the reference AST; the runtime does
303// not check return annotations (that is the static checker's job)
304#[allow(dead_code)]
305#[derive(Debug, Clone)]
306pub enum DeclBody {
307    Type {
308        name: String,
309        params: Vec<Param>,
310        ty: TypeAst,
311        tail: Option<Tail>,
312    },
313    Const {
314        name: String,
315        ty: Option<TypeAst>,
316        expr: Rc<Expr>,
317    },
318    Func {
319        name: String,
320        params: Vec<Param>,
321        ret: Option<TypeAst>,
322        body: Rc<Expr>,
323    },
324    Output {
325        name: String,
326        ty: TypeAst,
327        expr: Rc<Expr>,
328    },
329    Input {
330        name: String,
331        ty: TypeAst,
332        fallback: Option<Rc<Expr>>,
333    },
334    Diagnostic {
335        name: String,
336        params: Vec<Param>,
337        severity: String,
338        template: Vec<TPart>,
339    },
340    Dimension {
341        name: String,
342        terms: Option<Vec<(String, i32)>>,
343    },
344    Unit {
345        name: String,
346        dim: Option<String>,
347        factor: Option<Rc<Expr>>,
348        base: Option<String>,
349    },
350    Import {
351        from: String,
352        names: Option<Vec<ImportItem>>,
353        ns: Option<String>,
354    },
355    ReExport {
356        from: String,
357        names: Vec<ImportItem>,
358    },
359}
360
361#[derive(Debug, Clone)]
362pub struct Decl {
363    pub body: DeclBody,
364    pub exported: bool,
365    /// the declaration's source range (Phase 6 foundations); `export` included
366    pub loc: Option<Loc>,
367}
368
369impl Decl {
370    pub fn name(&self) -> Option<&str> {
371        match &self.body {
372            DeclBody::Type { name, .. }
373            | DeclBody::Const { name, .. }
374            | DeclBody::Func { name, .. }
375            | DeclBody::Output { name, .. }
376            | DeclBody::Input { name, .. }
377            | DeclBody::Diagnostic { name, .. }
378            | DeclBody::Dimension { name, .. }
379            | DeclBody::Unit { name, .. } => Some(name),
380            _ => None,
381        }
382    }
383}
384
385/// does an expression mention `$referrers` anywhere? (deferred slots)
386pub fn mentions_referrers(e: &Expr) -> bool {
387    match e {
388        Expr::Referrers { .. } => true,
389        Expr::Lit(_) | Expr::UnitLit { .. } | Expr::Name(_) | Expr::Ctx(_) | Expr::Pattern(_) => {
390            false
391        }
392        Expr::Template(parts) => parts
393            .iter()
394            .any(|p| matches!(p, TPart::Expr(x) if mentions_referrers(x))),
395        Expr::Obj(es) => es.iter().any(|(_, v)| mentions_referrers(v)),
396        Expr::Arr(items) => items.iter().any(|(_, v)| mentions_referrers(v)),
397        Expr::Comp { head, clauses } => {
398            mentions_referrers(head) || clauses.iter().any(clause_mentions)
399        }
400        Expr::MapComp { key, val, clauses } => {
401            mentions_referrers(key)
402                || mentions_referrers(val)
403                || clauses.iter().any(clause_mentions)
404        }
405        Expr::Bin { l, r, .. } => mentions_referrers(l) || mentions_referrers(r),
406        Expr::Un { x, .. } | Expr::Paren(x) => mentions_referrers(x),
407        Expr::If { c, t, f } => {
408            mentions_referrers(c) || mentions_referrers(t) || mentions_referrers(f)
409        }
410        Expr::Lambda { body, .. } => mentions_referrers(body),
411        Expr::Call { fun, args } => {
412            mentions_referrers(fun) || args.iter().any(|a| mentions_referrers(a))
413        }
414        Expr::Member { x, .. } => mentions_referrers(x),
415        Expr::Index { x, i } => mentions_referrers(x) || mentions_referrers(i),
416        Expr::With { base, patch } => mentions_referrers(base) || mentions_referrers(patch),
417        Expr::Match { subject, arms } => {
418            mentions_referrers(subject) || arms.iter().any(|a| mentions_referrers(&a.body))
419        }
420    }
421}
422
423fn clause_mentions(c: &ForClause) -> bool {
424    mentions_referrers(&c.iter) || c.filters.iter().any(|f| mentions_referrers(f))
425}
426
427pub fn expr_name(e: &Expr) -> String {
428    match e {
429        Expr::Name(n) => n.clone(),
430        Expr::Call { fun, .. } => expr_name(fun),
431        _ => "<predicate>".into(),
432    }
433}