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}