Skip to main content

decl_lang/
engine.rs

1//! Binding, evaluation, validation, and serialization — a port of the
2//! reference implementation's engine.ts. Semantics are the spec's: lazy
3//! slots with cycle detection, taint / root-cause diagnostics,
4//! $referrers universe ordering, canonical JSON output.
5use crate::ast::*;
6use crate::semantics::*;
7use crate::subsume::subsumes;
8use num_bigint::BigInt;
9use num_traits::{FromPrimitive, Signed, ToPrimitive, Zero};
10use std::cell::{Cell, RefCell};
11use std::cmp::Ordering::{self, Equal, Greater, Less};
12use std::collections::{HashMap, HashSet};
13use std::rc::{Rc, Weak};
14
15pub type Inst = Rc<RefCell<RecInst>>;
16
17pub struct Engine {
18    pub env: Rc<Env>,
19    pub deferred_slots: RefCell<Vec<(Inst, String)>>,
20    no_reg: Cell<u32>,
21    phase: Cell<u8>,
22    /// inputs whose fallback failed to bind: later demands are tainted, not re-reported
23    pub failed_inputs: RefCell<HashSet<String>>,
24    // ---- dependency tracking (Phase 6 foundations; docs/tooling/02_repl.md §6) ----
25    // every evaluation step — a slot being computed (`path.member`), a root
26    // being bound (`root:name`), an instance's asserts (`assert:path`) —
27    // records what it read: slots, roots, and `$referrers` queries by type
28    // (`referrers:Type`); diagnostics carry the step that produced them
29    pub reads: RefCell<HashMap<String, HashSet<String>>>,
30    pub computing: RefCell<Vec<String>>,
31    pub slots_by_key: RefCell<HashMap<String, (Inst, String)>>,
32    /// roots whose binding deferred through `$referrers` in phase 1: bound again after the deferred slots
33    pub deferred_roots: RefCell<Vec<DeferredRoot>>,
34}
35
36/// a root binding kept for phase 2 (its source owned)
37pub struct DeferredRoot {
38    pub name: String,
39    pub src: OwnedRootSrc,
40    pub rt: RT,
41    pub sc: Scope,
42}
43pub enum OwnedRootSrc {
44    Expr(Rc<Expr>),
45    Doc(Value),
46}
47
48/// what an evaluation root is bound from: an output's expression, or an
49/// input's document (bound as-is)
50pub enum RootSrc<'a> {
51    Expr(&'a Rc<Expr>),
52    Doc(Value),
53}
54
55fn num_cmp(a: &Value, b: &Value) -> Option<Ordering> {
56    match (a, b) {
57        (Value::Int(x), Value::Int(y)) => Some(x.cmp(y)),
58        (Value::Float(x), Value::Float(y)) => x.partial_cmp(y),
59        (Value::Int(x), Value::Float(y)) => x.to_f64()?.partial_cmp(y),
60        (Value::Float(x), Value::Int(y)) => x.partial_cmp(&y.to_f64()?),
61        (Value::Str(x), Value::Str(y)) => Some(x.cmp(y)),
62        _ => None,
63    }
64}
65
66fn exact_float(i: &BigInt) -> Option<f64> {
67    let f = i.to_f64()?;
68    if !f.is_finite() {
69        return None;
70    }
71    if BigInt::from_f64(f).as_ref() == Some(i) {
72        Some(f)
73    } else {
74        None
75    }
76}
77
78fn num_s(v: &Value) -> String {
79    match v {
80        Value::Float(f) => js_num_str(*f),
81        Value::Int(i) => i.to_string(),
82        Value::Str(s) => s.clone(),
83        other => format!("{other:?}"),
84    }
85}
86
87fn lit_display(v: &Value) -> String {
88    match v {
89        Value::Bool(b) => {
90            if *b {
91                "true".into()
92            } else {
93                "false".into()
94            }
95        }
96        Value::Null => "null".into(),
97        other => num_s(other),
98    }
99}
100
101fn to_index(v: &Value) -> R<i64> {
102    match v {
103        Value::Int(i) => i.to_i64().ok_or(()).or_else(|_| err("index out of range")),
104        Value::Float(f) => Ok(*f as i64),
105        _ => err("index must be an integer"),
106    }
107}
108
109fn path_key_cmp(a: &[Seg], b: &[Seg]) -> Ordering {
110    for (x, y) in a.iter().zip(b) {
111        let o = match (x, y) {
112            (Seg::Idx(i), Seg::Idx(j)) => i.cmp(j),
113            (Seg::Idx(_), _) => Less,
114            (_, Seg::Idx(_)) => Greater,
115            _ => seg_text(x).cmp(&seg_text(y)),
116        };
117        if o != Equal {
118            return o;
119        }
120    }
121    a.len().cmp(&b.len())
122}
123
124fn arg<'a>(a: &'a [Value], i: usize, name: &str) -> R<&'a Value> {
125    a.get(i)
126        .ok_or(())
127        .or_else(|_| err(format!("std.{name}: missing argument {i}")))
128}
129
130fn add_num(a: &Value, b: &Value) -> R<Value> {
131    Ok(match (a, b) {
132        (Value::Int(x), Value::Int(y)) => Value::Int(x + y),
133        (Value::Float(x), Value::Float(y)) => Value::Float(x + y),
134        (Value::Int(x), Value::Float(y)) => Value::Float(x.to_f64().unwrap_or(f64::INFINITY) + y),
135        (Value::Float(x), Value::Int(y)) => Value::Float(x + y.to_f64().unwrap_or(f64::INFINITY)),
136        _ => return err("bad operands for +"),
137    })
138}
139
140fn structural_of(v: &Value) -> RT {
141    match v {
142        Value::Bool(_) => ty(RTk::Prim("bool".into())),
143        Value::Int(_) => ty(RTk::Prim("int".into())),
144        Value::Float(_) => ty(RTk::Prim("float".into())),
145        Value::Str(_) => ty(RTk::Prim("string".into())),
146        Value::Null => ty(RTk::Prim("null".into())),
147        Value::Ref(_) => ty(RTk::Ref(ty(RTk::Any))),
148        Value::Arr(a) => {
149            let elem = a
150                .borrow()
151                .items
152                .first()
153                .map(structural_of)
154                .unwrap_or_else(|| ty(RTk::Any));
155            ty(RTk::Arr {
156                elem,
157                lo: None,
158                hi: None,
159            })
160        }
161        Value::Q { dim, .. } => ty(RTk::Quantity(dim.clone())),
162        _ => ty(RTk::Any),
163    }
164}
165
166pub fn fmt_f(n: f64) -> String {
167    let s = js_num_str(n);
168    if s.contains('.') || s.contains('e') || s.contains('E') {
169        s
170    } else {
171        format!("{s}.0")
172    }
173}
174
175impl Engine {
176    /// an engine without registering itself as the environment's evaluator
177    pub fn bare(env: Rc<Env>) -> Rc<Engine> {
178        let eng = Rc::new(Engine {
179            env: env.clone(),
180            deferred_slots: RefCell::new(vec![]),
181            no_reg: Cell::new(0),
182            phase: Cell::new(1),
183            failed_inputs: RefCell::new(HashSet::new()),
184            reads: RefCell::new(HashMap::new()),
185            computing: RefCell::new(vec![]),
186            slots_by_key: RefCell::new(HashMap::new()),
187            deferred_roots: RefCell::new(vec![]),
188        });
189        let w = Rc::downgrade(&eng);
190        *env.tagger.borrow_mut() = Some(Rc::new(move || {
191            w.upgrade()
192                .and_then(|e| e.computing.borrow().last().cloned())
193        }));
194        eng
195    }
196    pub fn slot_key(inst: &Inst, name: &str) -> String {
197        format!("{}.{}", path_str(&inst.borrow().path, None), name)
198    }
199    pub fn record(&self, read: String) {
200        let top = self.computing.borrow().last().cloned();
201        if let Some(top) = top {
202            self.reads.borrow_mut().entry(top).or_default().insert(read);
203        }
204    }
205    pub fn step<T>(&self, key: &str, f: impl FnOnce() -> T) -> T {
206        self.computing.borrow_mut().push(key.to_string());
207        self.reads
208            .borrow_mut()
209            .insert(key.to_string(), HashSet::new());
210        let r = f();
211        self.computing.borrow_mut().pop();
212        r
213    }
214    pub fn phase(&self) -> u8 {
215        self.phase.get()
216    }
217    pub fn new(env: Rc<Env>) -> Rc<Engine> {
218        let eng = Self::bare(env.clone());
219        eng.install_hooks(&env, false);
220        eng
221    }
222    /// §4.13 / unit factors: let the environment evaluate constants through this engine
223    pub fn install_hooks(self: &Rc<Self>, env: &Rc<Env>, with_menv: bool) {
224        let w = Rc::downgrade(self);
225        let we: Weak<Env> = Rc::downgrade(env);
226        *env.const_eval.borrow_mut() =
227            Some(Rc::new(move |n: &str| match (w.upgrade(), we.upgrade()) {
228                (Some(eng), Some(e)) => eng.force_const_in(&e, n, ""),
229                _ => Ok(Value::Null),
230            }));
231        let w2 = Rc::downgrade(self);
232        let we2: Weak<Env> = Rc::downgrade(env);
233        *env.expr_eval.borrow_mut() = Some(Rc::new(move |x: &Rc<Expr>| match w2.upgrade() {
234            Some(eng) => eng.ev(
235                x,
236                &Scope::new("", if with_menv { we2.upgrade() } else { None }),
237            ),
238            None => Ok(Value::Null),
239        }));
240    }
241
242    // ---------- expression evaluation ----------
243    pub fn ev(&self, e: &Rc<Expr>, sc: &Scope) -> R<Value> {
244        match &**e {
245            Expr::Lit(v) => Ok(v.clone()),
246            Expr::Pattern(s) => Ok(Value::Pat(s.clone())),
247            Expr::UnitLit { num, unit } => {
248                let (key, to_base) = self.env.unit_info(unit).or_else(err)?;
249                Ok(Value::Q {
250                    dim: key,
251                    value: num * to_base,
252                })
253            }
254            Expr::Paren(x) => self.ev(x, sc),
255            Expr::MapComp { key, val, clauses } => {
256                let mut entries = vec![];
257                self.map_comp(key, val, clauses, 0, (*sc.locals).clone(), sc, &mut entries)?;
258                Ok(Value::PreObj(Rc::new(entries)))
259            }
260            Expr::Template(parts) => Ok(Value::Str(self.render(parts, sc)?)),
261            Expr::Name(name) => {
262                if let Some(v) = sc.locals.get(name) {
263                    return Ok(v.clone());
264                }
265                if let Some(inst) = &sc.inst {
266                    if let Some(v) = self.slot_lookup(inst, name)? {
267                        return Ok(v);
268                    }
269                }
270                let menv = sc.menv.clone().unwrap_or_else(|| self.env.clone());
271                if let Some(v) = self.module_value(&menv, name, &sc.root_name)? {
272                    return Ok(v);
273                }
274                if name == "std" {
275                    return Ok(Value::Std(Rc::new(vec![])));
276                }
277                if let Some(v) = self.env.root(name) {
278                    self.record(format!("root:{name}"));
279                    return Ok(v);
280                }
281                if let Some(v) = self.demand_input(&menv, name)? {
282                    return Ok(v);
283                }
284                err(format!("unknown name {name}"))
285            }
286            Expr::Ctx(n) => {
287                // $this / $parent / $root are references (§7.3): each denotes an
288                // instance that contains the current one, so a value reading would
289                // be a self-containing value; $key and $path are plain values
290                let inst = sc.inst.as_ref();
291                match n.as_str() {
292                    "$this" => match inst {
293                        Some(i) => Ok(Value::Ref(Rc::new(i.borrow().path.clone()))),
294                        None => err_code("$this outside a record instance", "E4090"),
295                    },
296                    "$parent" => match inst.and_then(|i| i.borrow().parent.clone()) {
297                        Some(p) => Ok(Value::Ref(Rc::new(p.borrow().path.clone()))),
298                        None => err_code("$parent: the evaluation root has no owner", "E4090"),
299                    },
300                    "$root" => {
301                        if sc.root_name.is_empty() || self.env.root(&sc.root_name).is_none() {
302                            return err_code("$root outside an evaluation root", "E4090");
303                        }
304                        Ok(Value::Ref(Rc::new(vec![Seg::Name(sc.root_name.clone())])))
305                    }
306                    "$key" => {
307                        // the key or index under which $this sits in its parent's
308                        // collection: the last path segment, present only when the
309                        // instance is a collection element (not a direct member)
310                        let Some(i) = inst else {
311                            return err_code(
312                                "$key: the instance is not a collection element",
313                                "E4090",
314                            );
315                        };
316                        let b = i.borrow();
317                        let Some(parent) = &b.parent else {
318                            return err_code(
319                                "$key: the instance is not a collection element",
320                                "E4090",
321                            );
322                        };
323                        if b.path.len() < parent.borrow().path.len() + 2 {
324                            return err_code(
325                                "$key: the instance is not a collection element",
326                                "E4090",
327                            );
328                        }
329                        Ok(match b.path.last() {
330                            Some(Seg::Idx(k)) => Value::Int(BigInt::from(*k)),
331                            Some(Seg::Name(k)) | Some(Seg::Key(k)) => Value::Str(k.clone()),
332                            None => Value::Absent,
333                        })
334                    }
335                    "$path" => match inst {
336                        Some(i) => Ok(Value::Str(path_str(&i.borrow().path, None))),
337                        None => err_code("$path outside a record instance", "E4090"),
338                    },
339                    _ => err(format!("unsupported context var {n}")),
340                }
341            }
342            Expr::Referrers { ty, member } => self.referrers(ty, member, sc),
343            Expr::Obj(entries) => Ok(Value::PreObj(Rc::new(
344                entries
345                    .iter()
346                    .map(|(k, v)| {
347                        (
348                            k.clone(),
349                            Value::PreVal(Rc::new(PreValV {
350                                expr: v.clone(),
351                                scope: sc.clone(),
352                            })),
353                        )
354                    })
355                    .collect(),
356            ))),
357            Expr::Arr(items) => Ok(Value::PreArr(Rc::new(
358                items
359                    .iter()
360                    .map(|(sp, v)| {
361                        (
362                            *sp,
363                            Value::PreVal(Rc::new(PreValV {
364                                expr: v.clone(),
365                                scope: sc.clone(),
366                            })),
367                        )
368                    })
369                    .collect(),
370            ))),
371            Expr::Comp { head, clauses } => {
372                let mut items = vec![];
373                self.comp(head, clauses, 0, (*sc.locals).clone(), sc, &mut items)?;
374                Ok(Value::PreArr(Rc::new(items)))
375            }
376            Expr::If { c, t, f } => {
377                if self.truthy(&self.ev(c, sc)?)? {
378                    self.ev(t, sc)
379                } else {
380                    self.ev(f, sc)
381                }
382            }
383            Expr::Match { subject, arms } => {
384                let subj = self.deref(self.ev(subject, sc)?)?;
385                let run = |arm: &MatchArm| {
386                    let mut l2 = (*sc.locals).clone();
387                    l2.insert(arm.v.clone(), subj.clone());
388                    self.ev(&arm.body, &sc.with_locals(l2))
389                };
390                let mut catch_all = None;
391                for arm in arms {
392                    let Some(t) = &arm.ty else {
393                        catch_all = Some(arm);
394                        continue;
395                    };
396                    let menv = sc.menv.clone().unwrap_or_else(|| self.env.clone());
397                    let rt = menv.resolve(t, None).or_else(err)?;
398                    if self.member_of(&subj, &rt, sc) {
399                        return run(arm);
400                    }
401                }
402                if let Some(arm) = catch_all {
403                    return run(arm);
404                }
405                err("match: no arm matched")
406            }
407            Expr::Lambda { params, body } => Ok(Value::Clo(Rc::new(Closure {
408                params: params.clone(),
409                body: body.clone(),
410                scope: sc.clone(),
411            }))),
412            Expr::Un { op, x } => {
413                let x = self.ev(x, sc)?;
414                match op.as_str() {
415                    "!" => Ok(Value::Bool(!self.truthy(&x)?)),
416                    "-" => match x {
417                        Value::Absent => err("absent consumed"),
418                        Value::Q { dim, value } => Ok(Value::Q { dim, value: -value }),
419                        Value::Int(i) => Ok(Value::Int(-i)),
420                        Value::Float(f) => Ok(Value::Float(-f)),
421                        _ => err("bad operand for unary -"),
422                    },
423                    "~" => match x {
424                        Value::Int(i) => Ok(Value::Int(-i - 1)),
425                        _ => err("bad operand for ~"),
426                    },
427                    _ => err("un"),
428                }
429            }
430            Expr::Bin { op, l, r } => {
431                if op == "|>" {
432                    let call = match &**r {
433                        Expr::Call { fun, args } => {
434                            let mut a = vec![l.clone()];
435                            a.extend(args.iter().cloned());
436                            Expr::Call {
437                                fun: fun.clone(),
438                                args: a,
439                            }
440                        }
441                        _ => Expr::Call {
442                            fun: r.clone(),
443                            args: vec![l.clone()],
444                        },
445                    };
446                    return self.ev(&Rc::new(call), sc);
447                }
448                self.binop(op, l, r, sc)
449            }
450            Expr::Member { x, name, safe } => {
451                let x0 = self.ev(x, sc)?;
452                if let Value::NsRef(ns) = &x0 {
453                    return self.ns_value(ns, name, sc);
454                }
455                if *safe && matches!(x0, Value::Null | Value::Absent) {
456                    return Ok(Value::Absent);
457                }
458                let d = self.deref(x0)?;
459                self.access(&d, name)
460            }
461            Expr::Index { x, i } => {
462                let x = self.deref(self.ev(x, sc)?)?;
463                let i = self.ev(i, sc)?;
464                match &x {
465                    Value::Arr(a) => {
466                        let n = to_index(&i)?;
467                        let a = a.borrow();
468                        if n < 0 || n as usize >= a.items.len() {
469                            return err_code(format!("index {n} out of bounds"), "E5005");
470                        }
471                        Ok(a.items[n as usize].clone())
472                    }
473                    Value::Map(m) => match &i {
474                        Value::Str(k) => Ok(m.borrow().get(k).cloned().unwrap_or(Value::Absent)),
475                        _ => Ok(Value::Absent),
476                    },
477                    Value::Rec(_) => match &i {
478                        Value::Str(k) => self.access(&x, k),
479                        _ => err("index on record needs a string"),
480                    },
481                    _ => err("index on non-collection"),
482                }
483            }
484            Expr::Call { fun, args } => {
485                let mut a = Vec::with_capacity(args.len());
486                for x in args {
487                    a.push(self.ev(x, sc)?);
488                }
489                let f = self.ev_callee(fun, sc)?;
490                self.call(&f, a, sc)
491            }
492            Expr::With { base, patch } => self.with_expr(base, patch, sc),
493        }
494    }
495
496    fn render(&self, parts: &[TPart], sc: &Scope) -> R<String> {
497        let mut s = String::new();
498        for p in parts {
499            match p {
500                TPart::Text(t) => s.push_str(t),
501                TPart::Expr(x) => s.push_str(&self.to_str(&self.ev(x, sc)?)?),
502            }
503        }
504        Ok(s)
505    }
506
507    fn comp(
508        &self,
509        head: &Rc<Expr>,
510        clauses: &[ForClause],
511        ci: usize,
512        locals: HashMap<String, Value>,
513        sc: &Scope,
514        out: &mut Vec<(bool, Value)>,
515    ) -> R<()> {
516        if ci == clauses.len() {
517            out.push((
518                false,
519                Value::PreVal(Rc::new(PreValV {
520                    expr: head.clone(),
521                    scope: sc.with_locals(locals),
522                })),
523            ));
524            return Ok(());
525        }
526        let cl = &clauses[ci];
527        let it = self.ev(&cl.iter, &sc.with_locals(locals.clone()))?;
528        for el in self.iterate(&it)? {
529            let mut l2 = locals.clone();
530            l2.insert(cl.v.clone(), el);
531            let sc2 = sc.with_locals(l2.clone());
532            let mut ok = true;
533            for f in &cl.filters {
534                if !self.truthy(&self.ev(f, &sc2)?)? {
535                    ok = false;
536                    break;
537                }
538            }
539            if ok {
540                self.comp(head, clauses, ci + 1, l2, sc, out)?;
541            }
542        }
543        Ok(())
544    }
545
546    fn map_comp(
547        &self,
548        key: &Rc<Expr>,
549        val: &Rc<Expr>,
550        clauses: &[ForClause],
551        ci: usize,
552        locals: HashMap<String, Value>,
553        sc: &Scope,
554        out: &mut Vec<(String, Value)>,
555    ) -> R<()> {
556        if ci == clauses.len() {
557            let sc2 = sc.with_locals(locals);
558            let Value::Str(k) = self.ev(key, &sc2)? else {
559                return err("map key must be string");
560            };
561            if out.iter().any(|(kk, _)| *kk == k) {
562                return err_code(format!("duplicate key {k}"), "E5004");
563            }
564            let v = self.ev(val, &sc2)?;
565            out.push((k, v));
566            return Ok(());
567        }
568        let cl = &clauses[ci];
569        let it = self.ev(&cl.iter, &sc.with_locals(locals.clone()))?;
570        for el in self.iterate(&it)? {
571            let mut l2 = locals.clone();
572            l2.insert(cl.v.clone(), el);
573            let sc2 = sc.with_locals(l2.clone());
574            let mut ok = true;
575            for f in &cl.filters {
576                if !self.truthy(&self.ev(f, &sc2)?)? {
577                    ok = false;
578                    break;
579                }
580            }
581            if ok {
582                self.map_comp(key, val, clauses, ci + 1, l2, sc, out)?;
583            }
584        }
585        Ok(())
586    }
587
588    pub fn member_of(&self, v: &Value, rt: &RT, sc: &Scope) -> bool {
589        if let Value::Rec(r) = v {
590            let vrt = r.borrow().rt.clone();
591            return subsumes(&self.env, &vrt, rt);
592        }
593        let mark = self.env.diag_len();
594        self.no_reg.set(self.no_reg.get() + 1);
595        let ok = self
596            .bind(v.clone(), rt, &[Seg::Name("<match>".into())], None, sc)
597            .is_ok();
598        self.no_reg.set(self.no_reg.get() - 1);
599        self.env.diag_truncate(mark);
600        ok
601    }
602
603    fn ev_callee(&self, e: &Rc<Expr>, sc: &Scope) -> R<Value> {
604        if let Expr::Member { x, name, .. } = &**e {
605            let x = self.ev_callee(x, sc)?;
606            return match &x {
607                Value::Std(p) => {
608                    let mut p2 = (**p).clone();
609                    p2.push(name.clone());
610                    Ok(Value::Std(Rc::new(p2)))
611                }
612                Value::NsRef(ns) => self.ns_value(ns, name, sc),
613                _ => {
614                    let d = self.deref(x)?;
615                    self.access(&d, name)
616                }
617            };
618        }
619        self.ev(e, sc)
620    }
621
622    fn module_value(&self, menv: &Rc<Env>, name: &str, root_name: &str) -> R<Option<Value>> {
623        if menv.consts.borrow().contains_key(name) {
624            return Ok(Some(self.force_const_in(menv, name, root_name)?));
625        }
626        let f = menv.funcs.borrow().get(name).cloned();
627        if let Some(f) = f {
628            return Ok(Some(Value::Clo(Rc::new(Closure {
629                params: f.params.iter().map(|p| p.name.clone()).collect(),
630                body: f.body.clone(),
631                scope: Scope::new(root_name, Some(menv.clone())),
632            }))));
633        }
634        let im = menv.imports.borrow().get(name).cloned();
635        if let Some(im) = im {
636            if let Some(v) = self.module_value(&im.env, &im.name, root_name)? {
637                return Ok(Some(v));
638            }
639            if let Some(v) = self.env.root(&im.name) {
640                self.record(format!("root:{}", im.name));
641                return Ok(Some(v)); // imported output/input root
642            }
643            return self.demand_input(&im.env, &im.name);
644        }
645        let ns = menv.namespaces.borrow().get(name).cloned();
646        if let Some((_, exports)) = ns {
647            return Ok(Some(Value::NsRef(Rc::new(NsRefV { exports }))));
648        }
649        Ok(None)
650    }
651
652    fn ns_value(&self, ns: &NsRefV, name: &str, sc: &Scope) -> R<Value> {
653        let ex = ns.exports.borrow().get(name).cloned();
654        let Some(ex) = ex else {
655            return err(format!("namespace has no export {name}"));
656        };
657        if let Some(v) = self.module_value(&ex.env, &ex.name, &sc.root_name)? {
658            return Ok(v);
659        }
660        if let Some(v) = self.env.root(&ex.name) {
661            self.record(format!("root:{}", ex.name));
662            return Ok(v);
663        }
664        if let Some(v) = self.demand_input(&ex.env, &ex.name)? {
665            return Ok(v);
666        }
667        err(format!("{name} is not a value"))
668    }
669    // an input demanded by evaluation (§5.6, §9.4): the bound document if
670    // the tool bound one, else its fallback — bound on first demand and
671    // memoized as a root; a fallback-less unbound input is E5006 at the
672    // demanding path. Returns None when `name` is not an input.
673    pub fn demand_input(&self, menv: &Rc<Env>, name: &str) -> R<Option<Value>> {
674        let decl = menv.inputs.borrow().get(name).cloned();
675        let Some((ty_ast, fallback)) = decl else {
676            return Ok(None);
677        };
678        self.record(format!("root:{name}"));
679        if let Some(v) = self.env.root(name) {
680            return Ok(Some(v));
681        }
682        if self.failed_inputs.borrow().contains(name) {
683            return Err(Fail::Taint);
684        }
685        let Some(fallback) = fallback else {
686            return err_code(format!("input {name} is not bound"), "E5006");
687        };
688        let sc = Scope::new(name, Some(menv.clone()));
689        let bound = self.step(&format!("root:{name}"), || -> R<Value> {
690            let v = self.ev(&fallback, &sc)?;
691            let rt = menv.resolve(&ty_ast, None).or_else(err)?;
692            self.bind(v, &rt, &[Seg::Name(name.to_string())], None, &sc)
693        });
694        match bound {
695            Ok(v) => {
696                self.env.set_root(name, v.clone());
697                Ok(Some(v))
698            }
699            Err(e) => {
700                if matches!(e, Fail::Taint) {
701                    self.failed_inputs.borrow_mut().insert(name.to_string());
702                }
703                Err(e)
704            }
705        }
706    }
707    // bind an evaluation root (an output's expression, or an input's
708    // document / fallback): a failing root is reported at its own path and
709    // left unset — its demanders are tainted, nothing else is
710    pub fn bind_root(&self, name: &str, src: RootSrc, rt: &RT, sc: &Scope) {
711        let keep = match &src {
712            RootSrc::Expr(e) => OwnedRootSrc::Expr((*e).clone()),
713            RootSrc::Doc(v) => OwnedRootSrc::Doc(v.clone()),
714        };
715        let bound = self.step(&format!("root:{name}"), || -> R<Value> {
716            let raw = match src {
717                RootSrc::Expr(e) => self.ev(e, sc)?,
718                RootSrc::Doc(v) => v,
719            };
720            self.bind(raw, rt, &[Seg::Name(name.to_string())], None, sc)
721        });
722        match bound {
723            Ok(v) => self.env.set_root(name, v),
724            Err(Fail::Eval(e)) => self.env.report(Diag {
725                severity: "error".into(),
726                id: None,
727                message: e.msg,
728                path: name.to_string(),
729                code: e.code,
730                loc: None,
731                by: None,
732            }),
733            Err(Fail::Defer) => {
734                if self.phase.get() < 2 {
735                    self.deferred_roots.borrow_mut().push(DeferredRoot {
736                        name: name.to_string(),
737                        src: keep,
738                        rt: rt.clone(),
739                        sc: sc.clone(),
740                    });
741                }
742            }
743            Err(Fail::Taint) => {}
744        }
745    }
746    /// phase 2: the roots that deferred are bound again (after the deferred slots)
747    pub fn bind_deferred_roots(&self) {
748        let pending: Vec<DeferredRoot> = self.deferred_roots.borrow_mut().drain(..).collect();
749        for d in pending {
750            match &d.src {
751                OwnedRootSrc::Expr(e) => self.bind_root(&d.name, RootSrc::Expr(e), &d.rt, &d.sc),
752                OwnedRootSrc::Doc(v) => {
753                    self.bind_root(&d.name, RootSrc::Doc(v.clone()), &d.rt, &d.sc)
754                }
755            }
756        }
757    }
758
759    fn q_arith(&self, op: &str, l: &Value, r: &Value) -> R<Value> {
760        let dim_or_1 = |d: &str| {
761            if d.is_empty() {
762                "1".to_string()
763            } else {
764                d.to_string()
765            }
766        };
767        match op {
768            "+" | "-" => {
769                let (Value::Q { dim: ld, value: lv }, Value::Q { dim: rd, value: rv }) = (l, r)
770                else {
771                    return err(format!("`{op}` mixes quantity and plain number"));
772                };
773                if ld != rd {
774                    return err(format!(
775                        "quantity dimension mismatch: {} vs {}",
776                        dim_or_1(ld),
777                        dim_or_1(rd)
778                    ));
779                }
780                Ok(Value::Q {
781                    dim: ld.clone(),
782                    value: if op == "+" { lv + rv } else { lv - rv },
783                })
784            }
785            "<" | "<=" | ">" | ">=" => {
786                let (Value::Q { dim: ld, value: lv }, Value::Q { dim: rd, value: rv }) = (l, r)
787                else {
788                    return err("quantity dimension mismatch in comparison");
789                };
790                if ld != rd {
791                    return err("quantity dimension mismatch in comparison");
792                }
793                Ok(Value::Bool(match op {
794                    "<" => lv < rv,
795                    "<=" => lv <= rv,
796                    ">" => lv > rv,
797                    _ => lv >= rv,
798                }))
799            }
800            _ => {
801                let mag = |v: &Value| -> Option<f64> {
802                    match v {
803                        Value::Q { value, .. } => Some(*value),
804                        Value::Int(i) => Some(i.to_f64().unwrap_or(f64::INFINITY)),
805                        Value::Float(f) => Some(*f),
806                        _ => None,
807                    }
808                };
809                let (Some(lm), Some(rm)) = (mag(l), mag(r)) else {
810                    return err(format!("bad operands for {op}"));
811                };
812                if op == "/" && rm == 0.0 {
813                    return err_code("division by zero", "E5001");
814                }
815                let dv = |v: &Value| match v {
816                    Value::Q { dim, .. } => vec_of_key(dim),
817                    _ => DimVec::new(),
818                };
819                let vec = vec_combine(&dv(l), &dv(r), if op == "*" { 1 } else { -1 });
820                let value = if op == "*" { lm * rm } else { lm / rm };
821                if !value.is_finite() {
822                    return err_code("non-finite", "E5002");
823                }
824                let key = key_of_vec(&vec);
825                Ok(if key.is_empty() {
826                    Value::Float(value)
827                } else {
828                    Value::Q { dim: key, value }
829                })
830            }
831        }
832    }
833
834    fn iterate(&self, v: &Value) -> R<Vec<Value>> {
835        match v {
836            Value::PreArr(_) | Value::PreObj(_) => self.mat_arr(v),
837            Value::Arr(a) => Ok(a.borrow().items.clone()),
838            Value::Range { lo, hi, excl } => {
839                let (Value::Int(lo), Value::Int(hi)) = (&**lo, &**hi) else {
840                    return err("range bounds must be integers");
841                };
842                let hi = if *excl { hi.clone() } else { hi + 1 };
843                let mut out = vec![];
844                let mut i = lo.clone();
845                while i < hi {
846                    out.push(Value::Int(i.clone()));
847                    i += 1;
848                }
849                Ok(out)
850            }
851            _ => err("not iterable"),
852        }
853    }
854
855    fn truthy(&self, v: &Value) -> R<bool> {
856        match v {
857            Value::Bool(b) => Ok(*b),
858            _ => err("non-bool condition"),
859        }
860    }
861
862    fn binop(&self, op: &str, le: &Rc<Expr>, re: &Rc<Expr>, sc: &Scope) -> R<Value> {
863        match op {
864            "&&" => {
865                return if self.truthy(&self.ev(le, sc)?)? {
866                    Ok(Value::Bool(self.truthy(&self.ev(re, sc)?)?))
867                } else {
868                    Ok(Value::Bool(false))
869                };
870            }
871            "||" => {
872                return if self.truthy(&self.ev(le, sc)?)? {
873                    Ok(Value::Bool(true))
874                } else {
875                    Ok(Value::Bool(self.truthy(&self.ev(re, sc)?)?))
876                };
877            }
878            "??" => {
879                let l = self.ev(le, sc)?;
880                return if matches!(l, Value::Absent | Value::Null) {
881                    self.ev(re, sc)
882                } else {
883                    Ok(l)
884                };
885            }
886            _ => {}
887        }
888        let l = self.ev(le, sc)?;
889        let mut r = self.ev(re, sc)?;
890        match op {
891            ".." | "..<" => {
892                return Ok(Value::Range {
893                    lo: Box::new(l),
894                    hi: Box::new(r),
895                    excl: op == "..<",
896                })
897            }
898            "matches" => {
899                let (Value::Str(s), Value::Pat(p)) = (&l, &r) else {
900                    return err("matches needs a string and a pattern");
901                };
902                if let Some(bad) = pattern_error(p) {
903                    return err_code(format!("malformed pattern /{p}/: {bad}"), "E4119");
904                }
905                let re = compile_pattern(p).or_else(err)?;
906                return Ok(Value::Bool(re.is_match(s)));
907            }
908            "==" => return Ok(Value::Bool(value_eq(&l, &r))),
909            "!=" => return Ok(Value::Bool(!value_eq(&l, &r))),
910            "in" => {
911                if matches!(r, Value::Ref(_)) {
912                    r = self.deref(r)?; // the container may be reached through a reference ($this, $parent)
913                }
914                return match &r {
915                    Value::Range { lo, hi, excl } => {
916                        let ge = matches!(num_cmp(&l, lo), Some(Greater | Equal));
917                        let hi_ok = if *excl {
918                            num_cmp(&l, hi) == Some(Less)
919                        } else {
920                            matches!(num_cmp(&l, hi), Some(Less | Equal))
921                        };
922                        Ok(Value::Bool(ge && hi_ok))
923                    }
924                    Value::PreArr(_) | Value::PreObj(_) => Ok(Value::Bool(
925                        self.mat_arr(&r)?.iter().any(|x| value_eq(&l, x)),
926                    )),
927                    Value::Arr(a) => Ok(Value::Bool(
928                        a.borrow().items.iter().any(|x| value_eq(&l, x)),
929                    )),
930                    Value::Map(m) => {
931                        let Value::Str(k) = &l else {
932                            return Ok(Value::Bool(false));
933                        };
934                        Ok(Value::Bool(m.borrow().has(k)))
935                    }
936                    Value::Rec(rec) => {
937                        let Value::Str(k) = &l else {
938                            return Ok(Value::Bool(false));
939                        };
940                        let has = rec.borrow().has_slot(k);
941                        Ok(Value::Bool(
942                            has && self.force_state(rec, k) != SlotState::Absent,
943                        ))
944                    }
945                    _ => err("in: bad container"),
946                };
947            }
948            _ => {}
949        }
950        if l.is_absent() || r.is_absent() {
951            return err("absent consumed");
952        }
953        if (matches!(l, Value::Q { .. }) || matches!(r, Value::Q { .. }))
954            && ["+", "-", "*", "/", "<", "<=", ">", ">="].contains(&op)
955        {
956            return self.q_arith(op, &l, &r);
957        }
958        let both_i = matches!((&l, &r), (Value::Int(_), Value::Int(_)));
959        let both_f = matches!((&l, &r), (Value::Float(_), Value::Float(_)));
960        let both_s = matches!((&l, &r), (Value::Str(_), Value::Str(_)));
961        match (op, &l, &r) {
962            ("+", Value::Str(a), Value::Str(b)) => Ok(Value::Str(format!("{a}{b}"))),
963            ("+", Value::Int(a), Value::Int(b)) => Ok(Value::Int(a + b)),
964            ("+", Value::Float(a), Value::Float(b)) => Ok(Value::Float(a + b)),
965            ("-", Value::Int(a), Value::Int(b)) => Ok(Value::Int(a - b)),
966            ("-", Value::Float(a), Value::Float(b)) => Ok(Value::Float(a - b)),
967            ("*", Value::Int(a), Value::Int(b)) => Ok(Value::Int(a * b)),
968            ("*", Value::Float(a), Value::Float(b)) => Ok(Value::Float(a * b)),
969            ("/", Value::Int(a), Value::Int(b)) => {
970                if b.is_zero() {
971                    return err_code("division by zero", "E5001");
972                }
973                Ok(Value::Int(a / b))
974            }
975            ("/", Value::Float(a), Value::Float(b)) => {
976                if *b == 0.0 {
977                    return err_code("division by zero", "E5001");
978                }
979                let q = a / b;
980                if !q.is_finite() {
981                    return err_code("non-finite", "E5002");
982                }
983                Ok(Value::Float(q))
984            }
985            ("%", Value::Int(a), Value::Int(b)) => {
986                if b.is_zero() {
987                    return err_code("mod zero", "E5001");
988                }
989                Ok(Value::Int(a % b))
990            }
991            ("<" | "<=" | ">" | ">=", _, _) if both_i || both_f || both_s => {
992                let o = num_cmp(&l, &r);
993                Ok(Value::Bool(matches!(
994                    (op, o),
995                    ("<", Some(Less))
996                        | ("<=", Some(Less | Equal))
997                        | (">", Some(Greater))
998                        | (">=", Some(Greater | Equal))
999                )))
1000            }
1001            ("&", Value::Int(a), Value::Int(b)) => Ok(Value::Int(a.clone() & b.clone())),
1002            ("|", Value::Int(a), Value::Int(b)) => Ok(Value::Int(a.clone() | b.clone())),
1003            ("^", Value::Int(a), Value::Int(b)) => Ok(Value::Int(a.clone() ^ b.clone())),
1004            ("<<" | ">>", Value::Int(a), Value::Int(b)) => {
1005                if b.is_negative() {
1006                    return err_code("negative shift count", "E5003");
1007                }
1008                let n = b
1009                    .to_usize()
1010                    .ok_or(())
1011                    .or_else(|_| err("shift count too large"))?;
1012                Ok(Value::Int(if op == "<<" {
1013                    a.clone() << n
1014                } else {
1015                    a.clone() >> n
1016                }))
1017            }
1018            _ => err(format!("bad operands for {op}")),
1019        }
1020    }
1021
1022    fn to_str(&self, v: &Value) -> R<String> {
1023        match v {
1024            Value::Str(s) => Ok(s.clone()),
1025            Value::Bool(b) => Ok(if *b { "true".into() } else { "false".into() }),
1026            Value::Int(i) => Ok(i.to_string()),
1027            Value::Float(f) => Ok(js_num_str(*f)),
1028            _ => err("template: non-convertible"),
1029        }
1030    }
1031
1032    pub fn deref(&self, v: Value) -> R<Value> {
1033        if let Value::Ref(p) = &v {
1034            let target = self.resolve_segs(p)?;
1035            if target.is_undef() {
1036                return err_code(format!("dangling reference {}", path_str(p, None)), "E6002");
1037            }
1038            return Ok(target);
1039        }
1040        Ok(v)
1041    }
1042
1043    // the value at a place; Undef when there is none (an absent optional
1044    // member counts as none — §7.5). Lenient about segment kinds: engine-built
1045    // paths are canonical by construction
1046    pub fn resolve_segs(&self, segs: &[Seg]) -> R<Value> {
1047        let mut cur = match segs.first() {
1048            Some(Seg::Name(n)) => self.env.root(n).unwrap_or(Value::Undef),
1049            _ => Value::Undef,
1050        };
1051        for s in segs.iter().skip(1) {
1052            if cur.is_undef() {
1053                break;
1054            }
1055            cur = match (&cur, s) {
1056                (Value::Rec(r), Seg::Name(n) | Seg::Key(n)) => {
1057                    if r.borrow().slot(n).is_some() {
1058                        self.force_slot(r, n)?
1059                    } else {
1060                        Value::Undef
1061                    }
1062                }
1063                (Value::Arr(a), Seg::Idx(i)) => {
1064                    a.borrow().items.get(*i).cloned().unwrap_or(Value::Undef)
1065                }
1066                (Value::Map(m), Seg::Name(n) | Seg::Key(n)) => {
1067                    m.borrow().get(n).cloned().unwrap_or(Value::Undef)
1068                }
1069                _ => Value::Undef,
1070            };
1071            if cur.is_absent() {
1072                cur = Value::Undef;
1073            }
1074            if let Value::Ref(_) = cur {
1075                cur = self.deref(cur)?;
1076            }
1077        }
1078        Ok(cur)
1079    }
1080    // a path from a document must be canonical (§7.2, §7.5): a map key
1081    // bracketed, a record member dotted when the dot can spell it and
1082    // bracketed otherwise, an array index numeric — any other spelling does
1083    // not resolve
1084    pub fn resolve_canonical(&self, segs: &[Seg]) -> R<Value> {
1085        let mut cur = match segs.first() {
1086            Some(Seg::Name(n)) => self.env.root(n).unwrap_or(Value::Undef),
1087            _ => Value::Undef,
1088        };
1089        for s in segs.iter().skip(1) {
1090            if cur.is_undef() {
1091                break;
1092            }
1093            cur = match (&cur, s) {
1094                (Value::Rec(r), Seg::Name(n)) if dot_spellable(n) => {
1095                    if r.borrow().slot(n).is_some() {
1096                        self.force_slot(r, n)?
1097                    } else {
1098                        Value::Undef
1099                    }
1100                }
1101                (Value::Rec(r), Seg::Key(n)) if !dot_spellable(n) => {
1102                    if r.borrow().slot(n).is_some() {
1103                        self.force_slot(r, n)?
1104                    } else {
1105                        Value::Undef
1106                    }
1107                }
1108                (Value::Arr(a), Seg::Idx(i)) => {
1109                    a.borrow().items.get(*i).cloned().unwrap_or(Value::Undef)
1110                }
1111                (Value::Map(m), Seg::Key(n)) => m.borrow().get(n).cloned().unwrap_or(Value::Undef),
1112                _ => Value::Undef,
1113            };
1114            if cur.is_absent() {
1115                cur = Value::Undef;
1116            }
1117            if let Value::Ref(_) = cur {
1118                cur = self.deref(cur)?;
1119            }
1120        }
1121        Ok(cur)
1122    }
1123
1124    fn access(&self, x: &Value, name: &str) -> R<Value> {
1125        match x {
1126            Value::Rec(r) => {
1127                let (has, is_extra) = {
1128                    let b = r.borrow();
1129                    (b.has_slot(name), b.extra(name).is_some())
1130                };
1131                if has {
1132                    if self.force_state(r, name) == SlotState::Absent {
1133                        return Ok(Value::Absent);
1134                    }
1135                    return self.force_slot(r, name);
1136                }
1137                if is_extra {
1138                    return err(format!("opaque field {name} accessed"));
1139                }
1140                err(format!("no member {name}"))
1141            }
1142            Value::PreObj(es) => {
1143                for (k, v) in es.iter() {
1144                    if k == name {
1145                        return match v {
1146                            Value::PreVal(pv) => self.ev(&pv.expr, &pv.scope),
1147                            other => Ok(other.clone()),
1148                        };
1149                    }
1150                }
1151                Ok(Value::Absent)
1152            }
1153            Value::Null => err("member access on null"),
1154            Value::Absent => Ok(Value::Absent),
1155            _ => err(format!("member access on non-record ({name})")),
1156        }
1157    }
1158
1159    fn slot_lookup(&self, inst: &Inst, name: &str) -> R<Option<Value>> {
1160        let mut cur = Some(inst.clone());
1161        while let Some(c) = cur {
1162            if c.borrow().has_slot(name) {
1163                if self.force_state(&c, name) == SlotState::Absent {
1164                    return Ok(Some(Value::Absent));
1165                }
1166                return Ok(Some(self.force_slot(&c, name)?));
1167            }
1168            cur = c.borrow().parent.clone();
1169        }
1170        Ok(None)
1171    }
1172
1173    pub fn call(&self, f: &Value, args: Vec<Value>, sc: &Scope) -> R<Value> {
1174        match f {
1175            Value::Clo(c) => {
1176                let mut locals = (*c.scope.locals).clone();
1177                for (p, a) in c.params.iter().zip(args) {
1178                    locals.insert(p.clone(), a);
1179                }
1180                self.ev(&c.body, &c.scope.with_locals(locals))
1181            }
1182            Value::Nat(f) => f(&args),
1183            Value::Std(p) => self.std(&p.join("."), args, sc),
1184            _ => err("call of non-function"),
1185        }
1186    }
1187
1188    fn std(&self, name: &str, a: Vec<Value>, sc: &Scope) -> R<Value> {
1189        let domain = |msg: String| -> Fail {
1190            Fail::Eval(EvalErr {
1191                msg: format!("std.{name}: {msg}"),
1192                code: Some("E5008".into()),
1193            })
1194        };
1195        let arr = |path: Vec<Value>| {
1196            Value::Arr(Rc::new(RefCell::new(ArrV {
1197                items: path,
1198                path: vec![],
1199            })))
1200        };
1201        let s = |v: &Value| -> R<String> {
1202            match v {
1203                Value::Str(s) => Ok(s.clone()),
1204                _ => err(format!("std.{name}: expected string")),
1205            }
1206        };
1207        match name {
1208            "array.count" => Ok(Value::Int(BigInt::from(
1209                self.mat_arr(arg(&a, 0, name)?)?.len(),
1210            ))),
1211            "array.all" => {
1212                for x in self.mat_arr(arg(&a, 0, name)?)? {
1213                    if !self.truthy(&self.call(arg(&a, 1, name)?, vec![x], sc)?)? {
1214                        return Ok(Value::Bool(false));
1215                    }
1216                }
1217                Ok(Value::Bool(true))
1218            }
1219            "array.any" => {
1220                for x in self.mat_arr(arg(&a, 0, name)?)? {
1221                    if self.truthy(&self.call(arg(&a, 1, name)?, vec![x], sc)?)? {
1222                        return Ok(Value::Bool(true));
1223                    }
1224                }
1225                Ok(Value::Bool(false))
1226            }
1227            "array.filter" => {
1228                let mut out = vec![];
1229                for x in self.mat_arr(arg(&a, 0, name)?)? {
1230                    if self.truthy(&self.call(arg(&a, 1, name)?, vec![x.clone()], sc)?)? {
1231                        out.push(x);
1232                    }
1233                }
1234                Ok(arr(out))
1235            }
1236            "array.all_distinct" => {
1237                let items = self.mat_arr(arg(&a, 0, name)?)?;
1238                for i in 0..items.len() {
1239                    for j in i + 1..items.len() {
1240                        if value_eq(&items[i], &items[j]) {
1241                            return Ok(Value::Bool(false));
1242                        }
1243                    }
1244                }
1245                Ok(Value::Bool(true))
1246            }
1247            "array.sum" => {
1248                let items = self.mat_arr(arg(&a, 0, name)?)?;
1249                let Some(first) = items.first() else {
1250                    return Ok(Value::Int(BigInt::zero()));
1251                };
1252                let mut acc = if matches!(first, Value::Float(_)) {
1253                    Value::Float(0.0)
1254                } else {
1255                    Value::Int(BigInt::zero())
1256                };
1257                for x in &items {
1258                    acc = add_num(&acc, x)?;
1259                }
1260                Ok(acc)
1261            }
1262            "array.fold" => {
1263                let mut acc = arg(&a, 1, name)?.clone();
1264                for x in self.mat_arr(arg(&a, 0, name)?)? {
1265                    acc = self.call(arg(&a, 2, name)?, vec![acc, x], sc)?;
1266                }
1267                Ok(acc)
1268            }
1269            "map.keys" => Ok(arr(self
1270                .mat_map(arg(&a, 0, name)?)?
1271                .borrow()
1272                .entries
1273                .iter()
1274                .map(|(k, _)| Value::Str(k.clone()))
1275                .collect())),
1276            "map.values" => Ok(arr(self
1277                .mat_map(arg(&a, 0, name)?)?
1278                .borrow()
1279                .entries
1280                .iter()
1281                .map(|(_, v)| v.clone())
1282                .collect())),
1283            "map.entries" => Ok(arr(self
1284                .mat_map(arg(&a, 0, name)?)?
1285                .borrow()
1286                .entries
1287                .iter()
1288                .map(|(k, v)| {
1289                    Value::PreObj(Rc::new(vec![
1290                        ("key".into(), Value::Str(k.clone())),
1291                        ("value".into(), v.clone()),
1292                    ]))
1293                })
1294                .collect())),
1295            "string.length" => Ok(Value::Int(BigInt::from(
1296                s(arg(&a, 0, name)?)?.chars().count(),
1297            ))),
1298            "string.of" => Ok(Value::Str(self.to_str(arg(&a, 0, name)?)?)),
1299            "string.join" => {
1300                let sep = s(arg(&a, 1, name)?)?;
1301                let parts: Vec<String> = self
1302                    .mat_arr(arg(&a, 0, name)?)?
1303                    .iter()
1304                    .map(s)
1305                    .collect::<R<_>>()?;
1306                Ok(Value::Str(parts.join(&sep)))
1307            }
1308            "string.starts_with" => Ok(Value::Bool(
1309                s(arg(&a, 0, name)?)?.starts_with(&s(arg(&a, 1, name)?)?),
1310            )),
1311            "string.ends_with" => Ok(Value::Bool(
1312                s(arg(&a, 0, name)?)?.ends_with(&s(arg(&a, 1, name)?)?),
1313            )),
1314            "string.contains" => Ok(Value::Bool(
1315                s(arg(&a, 0, name)?)?.contains(&s(arg(&a, 1, name)?)?),
1316            )),
1317            "string.split" => {
1318                let sep = s(arg(&a, 1, name)?)?;
1319                if sep.is_empty() {
1320                    return Err(domain("separator must be non-empty".into()));
1321                }
1322                Ok(arr(s(arg(&a, 0, name)?)?
1323                    .split(&sep)
1324                    .map(|p| Value::Str(p.to_string()))
1325                    .collect()))
1326            }
1327            "ref.path" => match arg(&a, 0, name)? {
1328                Value::Ref(p) => Ok(Value::Str(path_str(p, None))),
1329                _ => err("ref.path on non-reference"),
1330            },
1331            "math.abs" => match arg(&a, 0, name)? {
1332                Value::Int(i) => Ok(Value::Int(i.abs())),
1333                Value::Float(f) => Ok(Value::Float(f.abs())),
1334                _ => err("std.math.abs: bad operand"),
1335            },
1336            "math.min" | "math.max" => {
1337                let (x, y) = (arg(&a, 0, name)?, arg(&a, 1, name)?);
1338                let Some(o) = num_cmp(x, y) else {
1339                    return err(format!("std.{name}: bad operands"));
1340                };
1341                let pick_x = if name == "math.min" {
1342                    o == Less
1343                } else {
1344                    o == Greater
1345                };
1346                Ok(if pick_x { x.clone() } else { y.clone() })
1347            }
1348            "math.clog2" => {
1349                let n = arg(&a, 0, name)?;
1350                let Value::Int(i) = n else {
1351                    return Err(domain(format!("n >= 1 required, got {}", num_s(n))));
1352                };
1353                if i < &BigInt::from(1) {
1354                    return Err(domain(format!("n >= 1 required, got {i}")));
1355                }
1356                Ok(Value::Int(BigInt::from((i - BigInt::from(1)).bits())))
1357            }
1358            "math.floor" | "math.ceil" => match arg(&a, 0, name)? {
1359                Value::Float(f) => {
1360                    let r = if name == "math.floor" {
1361                        f.floor()
1362                    } else {
1363                        f.ceil()
1364                    };
1365                    BigInt::from_f64(r)
1366                        .map(Value::Int)
1367                        .ok_or(())
1368                        .or_else(|_| err(format!("std.{name}: non-finite")))
1369                }
1370                Value::Int(i) => Ok(Value::Int(i.clone())),
1371                _ => err(format!("std.{name}: bad operand")),
1372            },
1373            "math.round" => match arg(&a, 0, name)? {
1374                Value::Float(x) => {
1375                    let f = x.floor();
1376                    let frac = x - f;
1377                    let r = if frac > 0.5 {
1378                        f + 1.0
1379                    } else if frac < 0.5 || f % 2.0 == 0.0 {
1380                        f
1381                    } else {
1382                        f + 1.0
1383                    };
1384                    BigInt::from_f64(r)
1385                        .map(Value::Int)
1386                        .ok_or(())
1387                        .or_else(|_| err("std.math.round: non-finite"))
1388                }
1389                Value::Int(i) => Ok(Value::Int(i.clone())),
1390                _ => err("std.math.round: bad operand"),
1391            },
1392            "int.of" => match arg(&a, 0, name)? {
1393                Value::Float(x) if x.fract() == 0.0 && x.is_finite() => {
1394                    Ok(Value::Int(BigInt::from_f64(*x).unwrap()))
1395                }
1396                other => Err(domain(format!(
1397                    "no fractional part allowed, got {}",
1398                    num_s(other)
1399                ))),
1400            },
1401            "int.at_least" | "int.at_most" => {
1402                let n = arg(&a, 0, name)?.clone();
1403                let least = name == "int.at_least";
1404                let f: NatFn = Rc::new(move |args: &[Value]| {
1405                    let Some(x) = args.first() else {
1406                        return err("missing argument");
1407                    };
1408                    match num_cmp(x, &n) {
1409                        Some(o) => Ok(Value::Bool(if least { o != Less } else { o != Greater })),
1410                        None => err("bad operands"),
1411                    }
1412                });
1413                Ok(Value::Nat(f))
1414            }
1415            "float.of" => {
1416                let v = match arg(&a, 0, name)? {
1417                    Value::Int(i) => i.to_f64().unwrap_or(f64::INFINITY),
1418                    Value::Float(f) => *f,
1419                    Value::Str(s) => s
1420                        .parse::<f64>()
1421                        .or_else(|_| err("std.float.of: bad operand"))?,
1422                    _ => return err("std.float.of: bad operand"),
1423                };
1424                if !v.is_finite() {
1425                    return Err(domain("magnitude outside binary64 range".into()));
1426                }
1427                Ok(Value::Float(v))
1428            }
1429            "object.merge" => self.deep_merge(arg(&a, 0, name)?, arg(&a, 1, name)?),
1430            _ => err(format!("std.{name} does not exist")),
1431        }
1432    }
1433
1434    fn deep_merge(&self, base0: &Value, patch0: &Value) -> R<Value> {
1435        let base = self.mat_rec(base0)?;
1436        let patch = self.mat_rec(patch0)?;
1437        let val = |r: &Inst, n: &str| -> R<Option<Value>> {
1438            {
1439                let b = r.borrow();
1440                if let Some(v) = b.extra(n) {
1441                    return Ok(Some(v.clone()));
1442                }
1443                match b.slot(n) {
1444                    None => return Ok(None),
1445                    Some(s) if s.kind == MKind::Der => return Ok(None),
1446                    _ => {}
1447                }
1448            }
1449            if self.force_state(r, n) == SlotState::Absent {
1450                return Ok(None);
1451            }
1452            Ok(Some(self.force_slot(r, n)?))
1453        };
1454        let mut names: Vec<String> = vec![];
1455        let mut push = |n: &str| {
1456            if !names.iter().any(|x| x == n) {
1457                names.push(n.to_string());
1458            }
1459        };
1460        for r in [&base, &patch] {
1461            let b = r.borrow();
1462            for n in &b.entry_order {
1463                push(n);
1464            }
1465            for m in rec_members(&b.rt) {
1466                if m.kind == MKind::Dflt {
1467                    push(&m.name);
1468                }
1469            }
1470        }
1471        let mut entries = vec![];
1472        for n in &names {
1473            let is_der = |r: &Inst| {
1474                r.borrow()
1475                    .slot(n)
1476                    .map(|s| s.kind == MKind::Der)
1477                    .unwrap_or(false)
1478            };
1479            if is_der(&base) || is_der(&patch) {
1480                continue;
1481            }
1482            let bv = val(&base, n)?;
1483            let pv = val(&patch, n)?;
1484            match (bv, pv) {
1485                (Some(bv), Some(pv)) => {
1486                    let bd = self.deref(bv.clone())?;
1487                    let pd = self.deref(pv.clone())?;
1488                    if matches!(bd, Value::Rec(_)) && matches!(pd, Value::Rec(_)) {
1489                        entries.push((n.clone(), self.deep_merge(&bv, &pv)?));
1490                    } else {
1491                        entries.push((n.clone(), pv));
1492                    }
1493                }
1494                (_, Some(pv)) => entries.push((n.clone(), pv)),
1495                (Some(bv), None) => entries.push((n.clone(), bv)),
1496                (None, None) => {}
1497            }
1498        }
1499        Ok(Value::PreObj(Rc::new(entries)))
1500    }
1501
1502    fn mat_rec(&self, v: &Value) -> R<Inst> {
1503        let mut d = self.deref(v.clone())?;
1504        if matches!(d, Value::PreObj(_) | Value::PreArr(_) | Value::JObj(_)) {
1505            d = self.materialize(d, &[])?;
1506        }
1507        match d {
1508            Value::Rec(r) => Ok(r),
1509            _ => err_code("std.object.merge: expected records", "E5008"),
1510        }
1511    }
1512    fn mat_arr(&self, v: &Value) -> R<Vec<Value>> {
1513        let mut d = self.deref(v.clone())?;
1514        if matches!(d, Value::PreObj(_) | Value::PreArr(_)) {
1515            d = self.materialize(d, &[])?;
1516        }
1517        match d {
1518            Value::Arr(a) => Ok(a.borrow().items.clone()),
1519            _ => err("expected array"),
1520        }
1521    }
1522    fn mat_map(&self, v: &Value) -> R<Rc<RefCell<MapV>>> {
1523        let mut d = self.deref(v.clone())?;
1524        if matches!(d, Value::PreObj(_) | Value::PreArr(_)) {
1525            d = self.materialize(d, &[])?;
1526        }
1527        match d {
1528            Value::Map(m) => Ok(m),
1529            _ => err("expected map"),
1530        }
1531    }
1532
1533    // ---------- referrers ----------
1534    fn referrers(&self, type_name: &str, member: &str, sc: &Scope) -> R<Value> {
1535        if self.phase.get() < 2 {
1536            return Err(Fail::Defer);
1537        }
1538        self.record(format!("referrers:{type_name}"));
1539        let Some(self_inst) = &sc.inst else {
1540            return err("$referrers outside a record");
1541        };
1542        let target = self_inst.borrow().path.clone();
1543        let mut out: Vec<Inst> = vec![];
1544        for cand in self.env.registry_snapshot() {
1545            let (tn, has) = {
1546                let b = cand.borrow();
1547                (b.type_name.clone(), b.has_slot(member))
1548            };
1549            if tn.as_deref() != Some(type_name) || !has {
1550                continue;
1551            }
1552            let Ok(v) = self.force_slot(&cand, member) else {
1553                continue;
1554            };
1555            if self.contains_ref_to(&v, &target) {
1556                out.push(cand);
1557            }
1558        }
1559        out.sort_by(|a, b| path_key_cmp(&a.borrow().path, &b.borrow().path));
1560        let items = out
1561            .iter()
1562            .map(|c| Value::Ref(Rc::new(c.borrow().path.clone())))
1563            .collect();
1564        Ok(Value::Arr(Rc::new(RefCell::new(ArrV {
1565            items,
1566            path: vec![],
1567        }))))
1568    }
1569
1570    fn contains_ref_to(&self, v: &Value, target: &[Seg]) -> bool {
1571        match v {
1572            Value::Ref(p) => cmp_path(p, target) == Equal,
1573            Value::Arr(a) => a
1574                .borrow()
1575                .items
1576                .iter()
1577                .any(|x| self.contains_ref_to(x, target)),
1578            Value::Map(m) => m
1579                .borrow()
1580                .entries
1581                .iter()
1582                .any(|(_, x)| self.contains_ref_to(x, target)),
1583            _ => false,
1584        }
1585    }
1586
1587    // ---------- binding / checking ----------
1588    pub fn bind(
1589        &self,
1590        raw: Value,
1591        rt: &RT,
1592        path: &[Seg],
1593        parent: Option<&Inst>,
1594        sc: &Scope,
1595    ) -> R<Value> {
1596        if let Value::PreVal(pv) = &raw {
1597            let inst = parent.cloned().or_else(|| pv.scope.inst.clone());
1598            let sc2 = pv.scope.with_inst(inst);
1599            if let RTk::Ref(_) = rt.k {
1600                return match self.eval_place(&pv.expr, &sc2)? {
1601                    Some(p) => {
1602                        // reference integrity (§7.5): the place must hold a value
1603                        if self.resolve_segs(&p)?.is_undef() {
1604                            self.env.report(Diag::error(
1605                                format!("dangling reference {}", path_str(&p, None)),
1606                                path_str(path, None),
1607                                Some("E6002"),
1608                            ));
1609                            return Err(Fail::Taint);
1610                        }
1611                        Ok(Value::Ref(Rc::new(p)))
1612                    }
1613                    None => err("not a place in ref position"),
1614                };
1615            }
1616            let v = self.ev(&pv.expr, &sc2)?;
1617            return self.bind(v, rt, path, parent, sc);
1618        }
1619        let fail = |msg: String, code: Option<&str>| -> Fail {
1620            let tail = rt.tail.borrow();
1621            match &*tail {
1622                Some(Tail::Inline { template, .. }) => {
1623                    let text: String = template
1624                        .iter()
1625                        .filter_map(|p| {
1626                            if let TPart::Text(t) = p {
1627                                Some(t.as_str())
1628                            } else {
1629                                None
1630                            }
1631                        })
1632                        .collect();
1633                    self.env.report(Diag {
1634                        severity: "error".into(),
1635                        id: rt.name.borrow().clone(),
1636                        message: text,
1637                        path: path_str(path, None),
1638                        code: Some("E4001".into()),
1639                        loc: None,
1640                        by: None,
1641                    });
1642                }
1643                _ => self.env.report(Diag::error(
1644                    msg,
1645                    path_str(path, None),
1646                    Some(code.unwrap_or("E4001")),
1647                )),
1648            }
1649            Fail::Taint
1650        };
1651        match &rt.k {
1652            RTk::Prim(n) => match (n.as_str(), &raw) {
1653                ("int", Value::Int(_))
1654                | ("float", Value::Float(_))
1655                | ("bool", Value::Bool(_))
1656                | ("string", Value::Str(_))
1657                | ("null", Value::Null) => Ok(raw),
1658                ("float", Value::Int(i)) => match exact_float(i) {
1659                    Some(f) => Ok(Value::Float(f)),
1660                    None => Err(fail(format!("expected {n}"), None)),
1661                },
1662                _ => Err(fail(format!("expected {n}"), None)),
1663            },
1664            RTk::Lit(v) => {
1665                if value_eq(&raw, v) {
1666                    Ok(raw)
1667                } else {
1668                    Err(fail(
1669                        format!("expected {}", json_str(&lit_display(v))),
1670                        None,
1671                    ))
1672                }
1673            }
1674            RTk::Range { lo, hi, excl, base } => {
1675                let raw = match (&raw, base.as_str()) {
1676                    (Value::Int(i), "float") => match exact_float(i) {
1677                        Some(f) => Value::Float(f),
1678                        None => raw,
1679                    },
1680                    _ => raw,
1681                };
1682                let ok = if base == "int" {
1683                    matches!(raw, Value::Int(_))
1684                } else {
1685                    matches!(raw, Value::Float(_))
1686                };
1687                if !ok {
1688                    return Err(fail(format!("expected {base} in range"), None));
1689                }
1690                let hi_ok = if *excl {
1691                    num_cmp(&raw, hi) == Some(Less)
1692                } else {
1693                    matches!(num_cmp(&raw, hi), Some(Less | Equal))
1694                };
1695                if matches!(num_cmp(&raw, lo), Some(Greater | Equal)) && hi_ok {
1696                    return Ok(raw);
1697                }
1698                Err(fail(
1699                    format!(
1700                        "out of range {}..{}{}",
1701                        num_s(lo),
1702                        if *excl { "<" } else { "" },
1703                        num_s(hi)
1704                    ),
1705                    None,
1706                ))
1707            }
1708            RTk::Pattern { src, re } => match &raw {
1709                Value::Str(s) if re.is_match(s) => Ok(raw),
1710                _ => Err(fail(format!("does not match /{src}/"), None)),
1711            },
1712            RTk::Quantity(dim) => {
1713                if let Value::Q { dim: d, .. } = &raw {
1714                    if d == dim {
1715                        return Ok(raw);
1716                    }
1717                }
1718                if let Value::JObj(es) = &raw {
1719                    if es.len() == 2 {
1720                        let v = es.iter().find(|(k, _)| k == "value").map(|(_, v)| v);
1721                        let u = es.iter().find(|(k, _)| k == "unit").map(|(_, v)| v);
1722                        if let (Some(v), Some(Value::Str(u))) = (v, u) {
1723                            let Ok((key, to_base)) = self.env.unit_info(u) else {
1724                                return Err(fail(format!("unknown unit {u}"), Some("E4073")));
1725                            };
1726                            if key != *dim {
1727                                return Err(fail("unit of wrong dimension".into(), Some("E4073")));
1728                            }
1729                            let f = match v {
1730                                Value::Int(i) => i.to_f64().unwrap_or(f64::INFINITY),
1731                                Value::Float(f) => *f,
1732                                _ => return Err(fail("expected quantity".into(), None)),
1733                            };
1734                            return Ok(Value::Q {
1735                                dim: dim.clone(),
1736                                value: f * to_base,
1737                            });
1738                        }
1739                    }
1740                }
1741                Err(fail("expected quantity".into(), None))
1742            }
1743            RTk::Ref(_) => match &raw {
1744                Value::Ref(_) => Ok(raw),
1745                Value::Str(s) => {
1746                    let segs = parse_path(s, &sc.root_name)?;
1747                    if self.resolve_canonical(&segs)?.is_undef() {
1748                        return Err(fail(format!("dangling reference {s}"), Some("E6002")));
1749                    }
1750                    Ok(Value::Ref(Rc::new(segs)))
1751                }
1752                Value::Rec(_) | Value::Arr(_) | Value::Map(_) => {
1753                    Ok(Value::Ref(Rc::new(raw.place().unwrap())))
1754                }
1755                _ => Err(fail("expected reference path".into(), None)),
1756            },
1757            RTk::Arr { elem, lo, hi } => {
1758                let items: Vec<Value> = match &raw {
1759                    Value::PreArr(pa) => {
1760                        let mut items = vec![];
1761                        for (spread, v) in pa.iter() {
1762                            if *spread {
1763                                let s = match v {
1764                                    Value::PreVal(pv) => {
1765                                        self.deref(self.ev(&pv.expr, &pv.scope)?)?
1766                                    }
1767                                    other => self.deref(other.clone())?,
1768                                };
1769                                items.extend(self.mat_arr(&s)?);
1770                            } else {
1771                                items.push(v.clone());
1772                            }
1773                        }
1774                        items
1775                    }
1776                    Value::JArr(a) => (**a).clone(),
1777                    Value::Arr(a) => a.borrow().items.clone(),
1778                    _ => return Err(fail("expected array".into(), None)),
1779                };
1780                if let Some(lo) = lo {
1781                    let n = items.len() as i64;
1782                    let h = hi.unwrap_or(i64::MAX);
1783                    if n < *lo || n > h {
1784                        return Err(fail(format!("array size {n} outside {lo}..{h}"), None));
1785                    }
1786                }
1787                let arr = Rc::new(RefCell::new(ArrV {
1788                    items: vec![],
1789                    path: path.to_vec(),
1790                }));
1791                for (i, it) in items.into_iter().enumerate() {
1792                    let mut p = path.to_vec();
1793                    p.push(Seg::Idx(i));
1794                    match self.bind(it, elem, &p, parent, sc) {
1795                        Ok(v) => arr.borrow_mut().items.push(v),
1796                        Err(Fail::Taint) => arr.borrow_mut().items.push(Value::Absent),
1797                        Err(e) => return Err(e),
1798                    }
1799                }
1800                Ok(Value::Arr(arr))
1801            }
1802            RTk::Map { key, val } => {
1803                let es: Vec<(String, Value)> = match &raw {
1804                    Value::JObj(e) | Value::PreObj(e) => (**e).clone(),
1805                    Value::Map(m) => m.borrow().entries.clone(),
1806                    _ => return Err(fail("expected map".into(), None)),
1807                };
1808                let m = Rc::new(RefCell::new(MapV {
1809                    entries: vec![],
1810                    path: path.to_vec(),
1811                }));
1812                for (k, v) in es {
1813                    match self.bind(Value::Str(k.clone()), key, path, parent, sc) {
1814                        Ok(_) => {}
1815                        Err(Fail::Taint) => continue,
1816                        Err(e) => return Err(e),
1817                    }
1818                    let mut p = path.to_vec();
1819                    p.push(Seg::Key(k.clone()));
1820                    match self.bind(v, val, &p, parent, sc) {
1821                        Ok(bv) => m.borrow_mut().set(k, bv),
1822                        Err(Fail::Taint) => {}
1823                        Err(e) => return Err(e),
1824                    }
1825                }
1826                Ok(Value::Map(m))
1827            }
1828            RTk::Union(arms) => {
1829                let rec_arms: Vec<&RT> = arms.iter().filter(|a| is_rec(a)).collect();
1830                if matches!(raw, Value::JObj(_) | Value::PreObj(_) | Value::Rec(_))
1831                    && !rec_arms.is_empty()
1832                {
1833                    let is_lit =
1834                        |m: &Member| matches!(m.ty.as_ref().map(|t| &t.k), Some(RTk::Lit(_)));
1835                    let first = rec_members(rec_arms[0]);
1836                    let disc_names: Vec<String> = first
1837                        .iter()
1838                        .filter(|m| {
1839                            is_lit(m)
1840                                && rec_arms.iter().all(|a| {
1841                                    rec_members(a).iter().any(|x| x.name == m.name && is_lit(x))
1842                                })
1843                        })
1844                        .map(|m| m.name.clone())
1845                        .collect();
1846                    for arm in &rec_arms {
1847                        let members = rec_members(arm);
1848                        let mut ok = true;
1849                        for dn in &disc_names {
1850                            let lit = members
1851                                .iter()
1852                                .find(|x| &x.name == dn)
1853                                .and_then(|x| x.ty.clone())
1854                                .and_then(|t| {
1855                                    if let RTk::Lit(v) = &t.k {
1856                                        Some(v.clone())
1857                                    } else {
1858                                        None
1859                                    }
1860                                })
1861                                .unwrap_or(Value::Undef);
1862                            match self.raw_entry(&raw, dn)? {
1863                                Some(mv) => {
1864                                    if !value_eq(&self.raw_lit(&mv)?, &lit) {
1865                                        ok = false;
1866                                        break;
1867                                    }
1868                                }
1869                                None => {
1870                                    ok = false;
1871                                    break;
1872                                }
1873                            }
1874                        }
1875                        if ok {
1876                            return self.bind(raw, arm, path, parent, sc);
1877                        }
1878                    }
1879                    return Err(fail("no union arm matches discriminant".into(), None));
1880                }
1881                for arm in arms {
1882                    if self.kind_matches(&raw, arm)? {
1883                        return self.bind(raw, arm, path, parent, sc);
1884                    }
1885                }
1886                Err(fail("no union arm matches".into(), None))
1887            }
1888            RTk::Rec(_) => self.bind_record(raw, rt, path, parent, sc),
1889            RTk::Pred { base, preds } => {
1890                let v = self.bind(raw, base, path, parent, sc)?;
1891                for p in preds {
1892                    let f = self.ev(p, &Scope::new(&sc.root_name, sc.menv.clone()))?;
1893                    let ok = matches!(self.call(&f, vec![v.clone()], sc), Ok(Value::Bool(true)));
1894                    if !ok {
1895                        return Err(fail(
1896                            format!("predicate {} not satisfied", json_str(&expr_name(p))),
1897                            None,
1898                        ));
1899                    }
1900                }
1901                Ok(v)
1902            }
1903            RTk::IsectN(arms) => {
1904                let mut v = raw.clone();
1905                for arm in arms {
1906                    v = self.bind(raw.clone(), arm, path, parent, sc)?;
1907                }
1908                Ok(v)
1909            }
1910            RTk::Any => Ok(raw),
1911            RTk::Func { .. } => match &raw {
1912                Value::Clo(_) | Value::Nat(_) | Value::Std(_) => Ok(raw),
1913                _ => Err(fail("expected function".into(), None)),
1914            },
1915        }
1916    }
1917
1918    fn raw_entry(&self, raw: &Value, name: &str) -> R<Option<Value>> {
1919        match raw {
1920            Value::JObj(es) | Value::PreObj(es) => {
1921                Ok(es.iter().find(|(k, _)| k == name).map(|(_, v)| v.clone()))
1922            }
1923            Value::Rec(r) => {
1924                if r.borrow().has_slot(name) {
1925                    Ok(Some(self.force_slot(r, name)?))
1926                } else {
1927                    Ok(None)
1928                }
1929            }
1930            _ => Ok(None),
1931        }
1932    }
1933
1934    fn raw_lit(&self, v: &Value) -> R<Value> {
1935        match v {
1936            Value::PreVal(pv) => self.ev(&pv.expr, &pv.scope),
1937            other => Ok(other.clone()),
1938        }
1939    }
1940
1941    fn kind_matches(&self, raw: &Value, rt: &RT) -> R<bool> {
1942        Ok(match &rt.k {
1943            RTk::Prim(n) => matches!(
1944                (n.as_str(), raw),
1945                ("int", Value::Int(_))
1946                    | ("float", Value::Float(_))
1947                    | ("bool", Value::Bool(_))
1948                    | ("string", Value::Str(_))
1949                    | ("null", Value::Null)
1950            ),
1951            RTk::Lit(v) => value_eq(&self.raw_lit(raw)?, v),
1952            RTk::Range { base, .. } => {
1953                if base == "int" {
1954                    matches!(raw, Value::Int(_))
1955                } else {
1956                    matches!(raw, Value::Float(_))
1957                }
1958            }
1959            RTk::Pattern { .. } => matches!(raw, Value::Str(_)),
1960            RTk::Arr { .. } => matches!(raw, Value::JArr(_) | Value::PreArr(_) | Value::Arr(_)),
1961            _ => true,
1962        })
1963    }
1964
1965    pub fn eval_place(&self, e: &Rc<Expr>, sc: &Scope) -> R<Option<SegPath>> {
1966        let v = self.ev_nav(e, sc)?;
1967        Ok(match &v {
1968            Value::Segs(p) => Some((**p).clone()),
1969            Value::Ref(p) => Some((**p).clone()), // $this / $parent / $root, or a reference read through
1970            Value::Rec(_) | Value::Arr(_) | Value::Map(_) => v.place(),
1971            _ => None,
1972        })
1973    }
1974
1975    fn ev_nav(&self, e: &Rc<Expr>, sc: &Scope) -> R<Value> {
1976        match &**e {
1977            // a conditional in a ref position chooses between places (§7.4):
1978            // only the taken branch is navigated
1979            Expr::If { c, t, f } => {
1980                if self.truthy(&self.ev(c, sc)?)? {
1981                    self.ev_nav(t, sc)
1982                } else {
1983                    self.ev_nav(f, sc)
1984                }
1985            }
1986            Expr::Paren(x) => self.ev_nav(x, sc),
1987            // a step past a missing place (an absent optional member, a key or
1988            // index that is not there) is still a place: the chain keeps naming
1989            // the location, and reference integrity (§7.5) judges it when the
1990            // reference is bound. `?.` is an ordinary step here — a navigation in
1991            // a ref position denotes the place regardless of maybe-absent steps
1992            Expr::Member { x, name, .. } => {
1993                let x0 = self.ev_nav(x, sc)?;
1994                if let Value::Segs(p) = &x0 {
1995                    let mut p = (**p).clone();
1996                    p.push(Seg::Name(name.clone()));
1997                    return Ok(Value::Segs(Rc::new(p)));
1998                }
1999                let x = self.deref(x0)?;
2000                let v = self.access(&x, name)?;
2001                if v.is_absent() {
2002                    if let Value::Rec(r) = &x {
2003                        let mut p = r.borrow().path.clone();
2004                        p.push(Seg::Name(name.clone()));
2005                        return Ok(Value::Segs(Rc::new(p)));
2006                    }
2007                }
2008                Ok(v)
2009            }
2010            Expr::Index { x, i } => {
2011                let x0 = self.ev_nav(x, sc)?;
2012                let i = self.ev(i, sc)?;
2013                if let Value::Segs(p) = &x0 {
2014                    let mut p = (**p).clone();
2015                    // past a missing place a string index can only be a map key (bracket
2016                    // access to a dot-spellable member is a compile error, §4.3)
2017                    match &i {
2018                        Value::Str(k) => p.push(Seg::Key(k.clone())),
2019                        _ => p.push(Seg::Idx(to_index(&i)?.max(0) as usize)),
2020                    }
2021                    return Ok(Value::Segs(Rc::new(p)));
2022                }
2023                let x = self.deref(x0)?;
2024                match &x {
2025                    Value::Arr(a) => {
2026                        let n = to_index(&i)?;
2027                        let b = a.borrow();
2028                        if n >= 0 && (n as usize) < b.items.len() {
2029                            Ok(b.items[n as usize].clone())
2030                        } else {
2031                            let mut p = b.path.clone();
2032                            p.push(Seg::Idx(n.max(0) as usize));
2033                            Ok(Value::Segs(Rc::new(p)))
2034                        }
2035                    }
2036                    Value::Map(m) => {
2037                        let Value::Str(k) = &i else {
2038                            return err("map index needs a string");
2039                        };
2040                        let b = m.borrow();
2041                        match b.get(k) {
2042                            Some(v) => Ok(v.clone()),
2043                            None => {
2044                                let mut p = b.path.clone();
2045                                p.push(Seg::Key(k.clone()));
2046                                Ok(Value::Segs(Rc::new(p)))
2047                            }
2048                        }
2049                    }
2050                    Value::Rec(r) => {
2051                        let Value::Str(k) = &i else {
2052                            return err("index on record needs a string");
2053                        };
2054                        let v = self.access(&x, k)?;
2055                        if v.is_absent() {
2056                            let mut p = r.borrow().path.clone();
2057                            p.push(Seg::Name(k.clone()));
2058                            Ok(Value::Segs(Rc::new(p)))
2059                        } else {
2060                            Ok(v)
2061                        }
2062                    }
2063                    _ => self.ev(e, sc),
2064                }
2065            }
2066            _ => self.ev(e, sc),
2067        }
2068    }
2069
2070    fn bind_record(
2071        &self,
2072        raw: Value,
2073        rt: &RT,
2074        path: &[Seg],
2075        parent: Option<&Inst>,
2076        sc: &Scope,
2077    ) -> R<Value> {
2078        let RTk::Rec(rec) = &rt.k else {
2079            return err("bind_record on non-record type");
2080        };
2081        let entries: Vec<(String, Value)> = match &raw {
2082            Value::JObj(e) | Value::PreObj(e) => (**e).clone(),
2083            Value::Rec(r) => {
2084                let (order, extras, ders) = {
2085                    let b = r.borrow();
2086                    (
2087                        b.entry_order.clone(),
2088                        b.extras.clone(),
2089                        b.slots
2090                            .iter()
2091                            .filter(|(_, s)| s.kind == MKind::Der)
2092                            .map(|(n, _)| n.clone())
2093                            .collect::<Vec<_>>(),
2094                    )
2095                };
2096                let mut out = vec![];
2097                for n in order {
2098                    if ders.contains(&n) {
2099                        continue;
2100                    }
2101                    if let Some((_, v)) = extras.iter().find(|(k, _)| *k == n) {
2102                        out.push((n, v.clone()));
2103                    } else {
2104                        let v = self.force_slot(r, &n)?;
2105                        out.push((n, v));
2106                    }
2107                }
2108                out
2109            }
2110            _ => {
2111                self.env.report(Diag::error(
2112                    "expected record",
2113                    path_str(path, None),
2114                    Some("E4001"),
2115                ));
2116                return Err(Fail::Taint);
2117            }
2118        };
2119        let members = rec.members.borrow().clone();
2120        let inst: Inst = Rc::new(RefCell::new(RecInst {
2121            type_name: rt.name.borrow().clone(),
2122            rt: rt.clone(),
2123            path: path.to_vec(),
2124            parent: parent.cloned(),
2125            slots: vec![],
2126            entry_order: entries.iter().map(|(k, _)| k.clone()).collect(),
2127            extras: vec![],
2128            menv: sc.menv.clone(),
2129        }));
2130        if self.no_reg.get() == 0 {
2131            self.env.registry_push(inst.clone());
2132        }
2133        let mut supplied: HashMap<String, Value> = HashMap::new();
2134        for (k, v) in &entries {
2135            supplied.insert(k.clone(), v.clone());
2136        }
2137        for m in &members {
2138            let name = m.name.clone();
2139            let types: Vec<RT> = m
2140                .conj
2141                .clone()
2142                .unwrap_or_else(|| m.ty.iter().cloned().collect());
2143            let menv = m.menv.clone().or_else(|| sc.menv.clone());
2144            let root_name = sc.root_name.clone();
2145            let has = supplied.get(&name).cloned();
2146            let mut push_deferred = false;
2147            // a hidden member (D34) is never part of the value: a document or
2148            // literal that supplies it is in error — there is nothing to restate
2149            if m.kind == MKind::Der && m.hidden && has.is_some() {
2150                let mut p = path.to_vec();
2151                p.push(Seg::Name(name.clone()));
2152                self.env.report(Diag::error(
2153                    format!("hidden member {name} supplied"),
2154                    path_str(&p, None),
2155                    Some("E4006"),
2156                ));
2157                inst.borrow_mut().slots.push((
2158                    name.clone(),
2159                    Slot {
2160                        kind: MKind::Der,
2161                        hidden: true,
2162                        state: SlotState::Invalid,
2163                        value: Value::Undef,
2164                        compute: None,
2165                    },
2166                ));
2167                continue;
2168            }
2169            let slot = match (m.kind, has) {
2170                (MKind::Der, has) => {
2171                    let expr = m
2172                        .expr
2173                        .clone()
2174                        .unwrap_or_else(|| Rc::new(Expr::Lit(Value::Null)));
2175                    // $referrers needs the whole universe: schedule for phase 2 up front
2176                    push_deferred = mentions_referrers(&expr);
2177                    Slot {
2178                        kind: MKind::Der,
2179                        hidden: m.hidden,
2180                        state: SlotState::Unforced,
2181                        value: Value::Undef,
2182                        compute: Some(Compute::Derived {
2183                            expr,
2184                            ty: m.ty.clone(),
2185                            supplied: has,
2186                            name: name.clone(),
2187                            root_name,
2188                            menv,
2189                        }),
2190                    }
2191                }
2192                (kind, Some(raw_v)) => Slot {
2193                    kind,
2194                    hidden: false,
2195                    state: SlotState::Unforced,
2196                    value: Value::Undef,
2197                    compute: Some(Compute::Check {
2198                        raw: raw_v,
2199                        types,
2200                        name: name.clone(),
2201                        root_name,
2202                        menv,
2203                    }),
2204                },
2205                (MKind::Dflt, None) => {
2206                    let expr = m
2207                        .dflt
2208                        .clone()
2209                        .unwrap_or_else(|| Rc::new(Expr::Lit(Value::Null)));
2210                    Slot {
2211                        kind: MKind::Dflt,
2212                        hidden: false,
2213                        state: SlotState::Unforced,
2214                        value: Value::Undef,
2215                        compute: Some(Compute::Default {
2216                            expr,
2217                            types,
2218                            name: name.clone(),
2219                            root_name,
2220                            menv,
2221                        }),
2222                    }
2223                }
2224                (MKind::Opt, None) => Slot {
2225                    kind: MKind::Opt,
2226                    hidden: false,
2227                    state: SlotState::Absent,
2228                    value: Value::Undef,
2229                    compute: None,
2230                },
2231                (MKind::Req, None) => {
2232                    let mut p = path.to_vec();
2233                    p.push(Seg::Name(name.clone()));
2234                    self.env.report(Diag::error(
2235                        format!("required member {name} missing"),
2236                        path_str(&p, None),
2237                        Some("E4002"),
2238                    ));
2239                    Slot {
2240                        kind: MKind::Req,
2241                        hidden: false,
2242                        state: SlotState::Invalid,
2243                        value: Value::Undef,
2244                        compute: None,
2245                    }
2246                }
2247            };
2248            inst.borrow_mut().slots.push((name.clone(), slot));
2249            if push_deferred {
2250                self.deferred_slots.borrow_mut().push((inst.clone(), name));
2251            }
2252        }
2253        for (k, v) in &entries {
2254            if members.iter().any(|m| m.name == *k) {
2255                continue;
2256            }
2257            if rec.open.get() {
2258                inst.borrow_mut().set_extra(k, v.clone());
2259            } else {
2260                let nm = rt
2261                    .name
2262                    .borrow()
2263                    .as_ref()
2264                    .map(|n| format!(" {n}"))
2265                    .unwrap_or_default();
2266                let mut p = path.to_vec();
2267                p.push(Seg::Name(k.clone()));
2268                self.env.report(Diag::error(
2269                    format!("undeclared member {k} on closed record{nm}"),
2270                    path_str(&p, None),
2271                    Some("E4003"),
2272                ));
2273            }
2274        }
2275        Ok(Value::Rec(inst))
2276    }
2277
2278    fn run_compute(&self, inst: &Inst, c: &Compute) -> R<Value> {
2279        let path = inst.borrow().path.clone();
2280        let scope_for = |root_name: &str, menv: &Option<Rc<Env>>| Scope {
2281            inst: Some(inst.clone()),
2282            locals: Rc::new(HashMap::new()),
2283            root_name: root_name.to_string(),
2284            menv: menv.clone(),
2285        };
2286        let member_path = |name: &str| {
2287            let mut p = path.clone();
2288            p.push(Seg::Name(name.to_string()));
2289            p
2290        };
2291        match c {
2292            Compute::Check {
2293                raw,
2294                types,
2295                name,
2296                root_name,
2297                menv,
2298            } => {
2299                let isc = scope_for(root_name, menv);
2300                let mp = member_path(name);
2301                let mut v = Value::Null;
2302                for t in types {
2303                    v = self.bind(raw.clone(), t, &mp, Some(inst), &isc)?;
2304                }
2305                Ok(v)
2306            }
2307            Compute::Default {
2308                expr,
2309                types,
2310                name,
2311                root_name,
2312                menv,
2313            } => {
2314                let isc = scope_for(root_name, menv);
2315                let mp = member_path(name);
2316                if types.len() == 1 && matches!(types[0].k, RTk::Ref(_)) {
2317                    let pv = Value::PreVal(Rc::new(PreValV {
2318                        expr: expr.clone(),
2319                        scope: isc.clone(),
2320                    }));
2321                    return self.bind(pv, &types[0], &mp, Some(inst), &isc);
2322                }
2323                let v = self.ev(expr, &isc)?;
2324                let mut out = Value::Null;
2325                for t in types {
2326                    out = self.bind(v.clone(), t, &mp, Some(inst), &isc)?;
2327                }
2328                Ok(out)
2329            }
2330            Compute::Derived {
2331                expr,
2332                ty: t,
2333                supplied,
2334                name,
2335                root_name,
2336                menv,
2337            } => {
2338                let isc = scope_for(root_name, menv);
2339                let mp = member_path(name);
2340                // a member declared `ref<T>` holds a navigation (§7.4): the
2341                // expression names a place, and is bound as one
2342                let mut v = match t {
2343                    Some(t) if matches!(t.k, RTk::Ref(_)) => {
2344                        let pv = Value::PreVal(Rc::new(PreValV {
2345                            expr: expr.clone(),
2346                            scope: isc.clone(),
2347                        }));
2348                        self.bind(pv, t, &mp, Some(inst), &isc)?
2349                    }
2350                    _ => self.ev(expr, &isc)?,
2351                };
2352                if let Some(t) = t {
2353                    if !matches!(t.k, RTk::Ref(_)) {
2354                        v = self.bind(v, t, &mp, Some(inst), &isc)?;
2355                    }
2356                } else if matches!(v, Value::PreObj(_) | Value::PreArr(_) | Value::JObj(_)) {
2357                    v = self.materialize(v, &mp)?;
2358                }
2359                if let Some(sv) = supplied {
2360                    let against = t.clone().unwrap_or_else(|| structural_of(&v));
2361                    self.no_reg.set(self.no_reg.get() + 1);
2362                    let restated = self.bind(sv.clone(), &against, &mp, Some(inst), &isc);
2363                    self.no_reg.set(self.no_reg.get() - 1);
2364                    let restated = restated?;
2365                    if !value_eq(&v, &restated) {
2366                        self.env.report(Diag::error(
2367                            format!("derived member {name} restated with a differing value"),
2368                            path_str(&mp, None),
2369                            Some("E4005"),
2370                        ));
2371                        return Err(Fail::Taint);
2372                    }
2373                }
2374                Ok(v)
2375            }
2376        }
2377    }
2378
2379    pub fn materialize(&self, v: Value, path: &[Seg]) -> R<Value> {
2380        match v {
2381            Value::PreArr(items) => {
2382                let arr = Rc::new(RefCell::new(ArrV {
2383                    items: vec![],
2384                    path: path.to_vec(),
2385                }));
2386                for (i, (_, it)) in items.iter().enumerate() {
2387                    let x = match it {
2388                        Value::PreVal(pv) => self.ev(&pv.expr, &pv.scope)?,
2389                        other => other.clone(),
2390                    };
2391                    let mut p = path.to_vec();
2392                    p.push(Seg::Idx(i));
2393                    let m = self.materialize(x, &p)?;
2394                    arr.borrow_mut().items.push(m);
2395                }
2396                Ok(Value::Arr(arr))
2397            }
2398            Value::PreObj(entries) => {
2399                let m = Rc::new(RefCell::new(MapV {
2400                    entries: vec![],
2401                    path: path.to_vec(),
2402                }));
2403                for (k, pv) in entries.iter() {
2404                    let x = match pv {
2405                        Value::PreVal(pv) => self.ev(&pv.expr, &pv.scope)?,
2406                        other => other.clone(),
2407                    };
2408                    let mut p = path.to_vec();
2409                    p.push(Seg::Key(k.clone()));
2410                    let mv = self.materialize(x, &p)?;
2411                    m.borrow_mut().set(k.clone(), mv);
2412                }
2413                Ok(Value::Map(m))
2414            }
2415            other => Ok(other),
2416        }
2417    }
2418
2419    fn with_expr(&self, base: &Rc<Expr>, patch: &Rc<Expr>, sc: &Scope) -> R<Value> {
2420        let base = self.deref(self.ev(base, sc)?)?;
2421        let merge = |entries: &mut Vec<(String, Value)>, patch: Value| -> R<()> {
2422            let Value::PreObj(pes) = patch else {
2423                return err("with: patch must be an object");
2424            };
2425            for (pk, pv) in pes.iter() {
2426                if let Some(e) = entries.iter_mut().find(|(n, _)| n == pk) {
2427                    e.1 = pv.clone();
2428                } else {
2429                    entries.push((pk.clone(), pv.clone()));
2430                }
2431            }
2432            Ok(())
2433        };
2434        if let Value::PreObj(bes) = &base {
2435            let p = self.ev(patch, sc)?;
2436            let mut entries = (**bes).clone();
2437            merge(&mut entries, p)?;
2438            return Ok(Value::PreObj(Rc::new(entries)));
2439        }
2440        let Value::Rec(r) = &base else {
2441            return err("with on non-record");
2442        };
2443        let p = self.ev(patch, sc)?;
2444        let (order, members) = {
2445            let b = r.borrow();
2446            (b.entry_order.clone(), rec_members(&b.rt))
2447        };
2448        let mut entries: Vec<(String, Value)> = vec![];
2449        for n in order {
2450            let (extra, skip) = {
2451                let b = r.borrow();
2452                match b.extra(&n) {
2453                    Some(v) => (Some(v.clone()), false),
2454                    None => (
2455                        None,
2456                        b.slot(&n)
2457                            .map(|s| s.kind == MKind::Der || s.state == SlotState::Absent)
2458                            .unwrap_or(true),
2459                    ),
2460                }
2461            };
2462            if let Some(v) = extra {
2463                entries.push((n, v));
2464                continue;
2465            }
2466            if skip {
2467                continue;
2468            }
2469            let v = self.force_slot(r, &n)?;
2470            entries.push((n, v));
2471        }
2472        for m in &members {
2473            if m.kind != MKind::Dflt || entries.iter().any(|(k, _)| *k == m.name) {
2474                continue;
2475            }
2476            let present = r
2477                .borrow()
2478                .slot(&m.name)
2479                .map(|s| s.state != SlotState::Absent)
2480                .unwrap_or(false);
2481            if present {
2482                let v = self.force_slot(r, &m.name)?;
2483                entries.push((m.name.clone(), v));
2484            }
2485        }
2486        merge(&mut entries, p)?;
2487        Ok(Value::PreObj(Rc::new(entries)))
2488    }
2489
2490    // ---------- slots ----------
2491    pub fn force_state(&self, inst: &Inst, name: &str) -> SlotState {
2492        self.force_slot_safe(inst, name);
2493        inst.borrow()
2494            .slot(name)
2495            .map(|s| s.state)
2496            .unwrap_or(SlotState::Invalid)
2497    }
2498
2499    pub fn force_slot_safe(&self, inst: &Inst, name: &str) {
2500        let _ = self.force_slot(inst, name);
2501    }
2502
2503    pub fn force_slot(&self, inst: &Inst, name: &str) -> R<Value> {
2504        let (state, compute) = {
2505            let b = inst.borrow();
2506            let Some(s) = b.slot(name) else {
2507                return err(format!("no member {name}"));
2508            };
2509            (s.state, s.compute.clone())
2510        };
2511        let key = Engine::slot_key(inst, name);
2512        self.record(key.clone());
2513        match state {
2514            SlotState::Ok => return Ok(inst.borrow().slot(name).unwrap().value.clone()),
2515            SlotState::Absent => return Ok(Value::Absent),
2516            SlotState::Invalid => return Err(Fail::Taint),
2517            _ => {}
2518        }
2519        let mut mp = inst.borrow().path.clone();
2520        mp.push(Seg::Name(name.to_string()));
2521        if state == SlotState::Forcing {
2522            self.env.report(Diag::error(
2523                format!("dependency cycle at {name}"),
2524                path_str(&mp, None),
2525                Some("E5007"),
2526            ));
2527            inst.borrow_mut().slot_mut(name).unwrap().state = SlotState::Invalid;
2528            return Err(Fail::Taint);
2529        }
2530        inst.borrow_mut().slot_mut(name).unwrap().state = SlotState::Forcing;
2531        self.slots_by_key
2532            .borrow_mut()
2533            .insert(key.clone(), (inst.clone(), name.to_string()));
2534        let res = match &compute {
2535            Some(c) => self.step(&key, || self.run_compute(inst, c)),
2536            None => Ok(Value::Null),
2537        };
2538        match res {
2539            Ok(v) => {
2540                let mut b = inst.borrow_mut();
2541                let s = b.slot_mut(name).unwrap();
2542                s.state = SlotState::Ok;
2543                s.value = v.clone();
2544                Ok(v)
2545            }
2546            Err(Fail::Defer) => {
2547                inst.borrow_mut().slot_mut(name).unwrap().state = SlotState::Unforced;
2548                self.deferred_slots
2549                    .borrow_mut()
2550                    .push((inst.clone(), name.to_string()));
2551                Err(Fail::Defer)
2552            }
2553            Err(Fail::Eval(e)) => {
2554                {
2555                    let mut b = inst.borrow_mut();
2556                    let s = b.slot_mut(name).unwrap();
2557                    if s.state == SlotState::Forcing {
2558                        s.state = SlotState::Invalid;
2559                    }
2560                }
2561                self.env.report(Diag {
2562                    severity: "error".into(),
2563                    id: None,
2564                    message: e.msg,
2565                    path: path_str(&mp, None),
2566                    code: e.code,
2567                    loc: None,
2568                    by: None,
2569                });
2570                Err(Fail::Taint)
2571            }
2572            Err(Fail::Taint) => {
2573                let mut b = inst.borrow_mut();
2574                let s = b.slot_mut(name).unwrap();
2575                if s.state == SlotState::Forcing {
2576                    s.state = SlotState::Invalid;
2577                }
2578                Err(Fail::Taint)
2579            }
2580        }
2581    }
2582
2583    pub fn force_const_in(&self, env: &Rc<Env>, name: &str, root_name: &str) -> R<Value> {
2584        let c = env.consts.borrow().get(name).cloned();
2585        let Some(c) = c else {
2586            return err(format!("unknown constant {name}"));
2587        };
2588        if c.state.get() {
2589            return Ok(c.value.borrow().clone());
2590        }
2591        c.state.set(true);
2592        let sc = Scope::new(root_name, Some(env.clone()));
2593        let mut v = self.ev(&c.expr, &sc)?;
2594        if matches!(v, Value::PreObj(_) | Value::PreArr(_) | Value::JObj(_)) {
2595            v = match &c.ty {
2596                Some(t) => {
2597                    let rt = env.resolve(t, None).or_else(err)?;
2598                    self.bind(v, &rt, &[Seg::Name(name.to_string())], None, &sc)?
2599                }
2600                None => self.materialize(v, &[Seg::Name(name.to_string())])?,
2601            };
2602        }
2603        *c.value.borrow_mut() = v.clone();
2604        Ok(v)
2605    }
2606
2607    // ---------- driving ----------
2608    pub fn force_all(&self, v: &Value) {
2609        match v {
2610            Value::Rec(r) => {
2611                let names: Vec<String> = r.borrow().slots.iter().map(|(n, _)| n.clone()).collect();
2612                for n in names {
2613                    self.force_slot_safe(r, &n);
2614                    let child = {
2615                        let b = r.borrow();
2616                        b.slot(&n)
2617                            .filter(|s| s.state == SlotState::Ok)
2618                            .map(|s| s.value.clone())
2619                    };
2620                    if let Some(c) = child {
2621                        self.force_all(&c);
2622                    }
2623                }
2624            }
2625            Value::Arr(a) => {
2626                let items = a.borrow().items.clone();
2627                for x in &items {
2628                    self.force_all(x);
2629                }
2630            }
2631            Value::Map(m) => {
2632                let vals: Vec<Value> = m.borrow().entries.iter().map(|(_, v)| v.clone()).collect();
2633                for x in &vals {
2634                    self.force_all(x);
2635                }
2636            }
2637            _ => {}
2638        }
2639    }
2640
2641    /// the evaluation phase: 1 materialization, 2 universe complete ($referrers answers)
2642    pub fn set_phase(&self, p: u8) {
2643        self.phase.set(p);
2644    }
2645
2646    /// force every root, run the $referrers phase, then the assertion pass
2647    /// force every root, the roots demanded on the way included (an input
2648    /// bound through its fallback while another root is forced becomes a
2649    /// root too, and is forced in the same pass — the reference walks the
2650    /// live root map)
2651    pub fn force_roots(&self, env: &Env) {
2652        let mut i = 0;
2653        loop {
2654            let v = {
2655                let rc = env.roots.borrow().clone();
2656                let roots = rc.borrow();
2657                if i >= roots.len() {
2658                    break;
2659                }
2660                roots[i].1.clone()
2661            };
2662            self.force_all(&v);
2663            i += 1;
2664        }
2665    }
2666    pub fn drive(&self, env: &Env) {
2667        self.force_roots(env);
2668        self.phase.set(2);
2669        let mut i = 0;
2670        loop {
2671            let item = {
2672                let d = self.deferred_slots.borrow();
2673                if i >= d.len() {
2674                    break;
2675                }
2676                d[i].clone()
2677            };
2678            self.force_slot_safe(&item.0, &item.1);
2679            i += 1;
2680        }
2681        self.bind_deferred_roots();
2682        self.force_roots(env);
2683        self.validate_all("");
2684    }
2685
2686    pub fn validate_all(&self, root_name: &str) {
2687        for inst in self.env.registry_snapshot() {
2688            self.validate_inst(&inst, root_name);
2689        }
2690    }
2691    pub fn validate_inst(&self, inst: &Inst, root_name: &str) {
2692        let asserts = match &inst.borrow().rt.k {
2693            RTk::Rec(r) => r.asserts.borrow().clone(),
2694            _ => vec![],
2695        };
2696        let key = format!("assert:{}", path_str(&inst.borrow().path, None));
2697        self.step(&key, || self.run_asserts(inst, &asserts, root_name));
2698    }
2699    /// reset a computed slot so that it is computed again (dependency tracking)
2700    pub fn reset_slot(&self, key: &str) -> bool {
2701        let entry = self.slots_by_key.borrow().get(key).cloned();
2702        let Some((inst, name)) = entry else {
2703            return false;
2704        };
2705        let mut b = inst.borrow_mut();
2706        let Some(s) = b.slot_mut(&name) else {
2707            return false;
2708        };
2709        if s.compute.is_none() {
2710            return false;
2711        }
2712        if s.state == SlotState::Ok || s.state == SlotState::Invalid {
2713            s.state = SlotState::Unforced;
2714            s.value = Value::Null;
2715        }
2716        true
2717    }
2718
2719    fn run_asserts(&self, inst: &Inst, asserts: &[AssertItem], root_name: &str) {
2720        let (menv0, ipath, type_name) = {
2721            let b = inst.borrow();
2722            (b.menv.clone(), path_str(&b.path, None), b.type_name.clone())
2723        };
2724        let sc0 = Scope {
2725            inst: Some(inst.clone()),
2726            locals: Rc::new(HashMap::new()),
2727            root_name: root_name.to_string(),
2728            menv: menv0.clone(),
2729        };
2730        for a in asserts {
2731            let sc = if a.menv.is_some() {
2732                sc0.with_menv(a.menv.clone())
2733            } else {
2734                sc0.clone()
2735            };
2736            if a.when {
2737                let Ok(cond) = self.ev(&a.cond, &sc) else {
2738                    continue;
2739                };
2740                if matches!(cond, Value::Bool(true)) {
2741                    let inner: Vec<AssertItem> = a
2742                        .body
2743                        .iter()
2744                        .filter_map(|b| match b {
2745                            MemberAst::Assert {
2746                                name, cond, tail, ..
2747                            } => Some(AssertItem {
2748                                when: false,
2749                                name: name.clone(),
2750                                cond: cond.clone(),
2751                                tail: tail.clone(),
2752                                body: vec![],
2753                                origin: a.origin.clone(),
2754                                menv: a.menv.clone(),
2755                            }),
2756                            MemberAst::When { cond, body, .. } => Some(AssertItem {
2757                                when: true,
2758                                name: String::new(),
2759                                cond: cond.clone(),
2760                                tail: None,
2761                                body: body.clone(),
2762                                origin: a.origin.clone(),
2763                                menv: a.menv.clone(),
2764                            }),
2765                            _ => None,
2766                        })
2767                        .collect();
2768                    self.run_asserts(inst, &inner, root_name);
2769                }
2770                continue;
2771            }
2772            let ok = match self.ev(&a.cond, &sc) {
2773                Ok(v) => v,
2774                Err(Fail::Eval(e)) => {
2775                    self.env.report(Diag {
2776                        severity: "error".into(),
2777                        id: None,
2778                        message: format!("{}: {}", a.name, e.msg),
2779                        path: ipath.clone(),
2780                        code: e.code,
2781                        loc: None,
2782                        by: None,
2783                    });
2784                    continue;
2785                }
2786                Err(_) => continue,
2787            };
2788            if matches!(ok, Value::Bool(true)) {
2789                continue;
2790            }
2791            let id = format!(
2792                "{}.{}",
2793                a.origin
2794                    .clone()
2795                    .or_else(|| type_name.clone())
2796                    .unwrap_or_default(),
2797                a.name
2798            );
2799            match &a.tail {
2800                None => self.env.report(Diag {
2801                    severity: "error".into(),
2802                    id: Some(id),
2803                    message: format!("assert {} failed", a.name),
2804                    path: ipath.clone(),
2805                    code: Some("E6001".into()),
2806                    loc: None,
2807                    by: None,
2808                }),
2809                Some(Tail::Inline { severity, template }) => {
2810                    let msg = self.render_lenient(template, &sc);
2811                    let code = match severity.as_str() {
2812                        "error" => "E6001",
2813                        "warn" => "W6001",
2814                        _ => "I6001",
2815                    };
2816                    self.env.report(Diag {
2817                        severity: severity.clone(),
2818                        id: Some(id),
2819                        message: msg,
2820                        path: ipath.clone(),
2821                        code: Some(code.into()),
2822                        loc: None,
2823                        by: None,
2824                    });
2825                }
2826                Some(Tail::Ref { name, args }) => {
2827                    let d = menv0
2828                        .as_ref()
2829                        .and_then(|e| e.diags.borrow().get(name).cloned())
2830                        .or_else(|| self.env.diags.borrow().get(name).cloned());
2831                    let Some(d) = d else {
2832                        self.env.report(Diag::error(
2833                            format!("unknown diagnostic {name}"),
2834                            ipath.clone(),
2835                            None,
2836                        ));
2837                        continue;
2838                    };
2839                    let argv: Vec<Value> = args
2840                        .iter()
2841                        .map(|x| self.ev(x, &sc).unwrap_or(Value::Absent))
2842                        .collect();
2843                    let mut locals = HashMap::new();
2844                    for (i, p) in d.params.iter().enumerate() {
2845                        locals.insert(
2846                            p.name.clone(),
2847                            argv.get(i).cloned().unwrap_or(Value::Absent),
2848                        );
2849                    }
2850                    let psc = Scope {
2851                        inst: None,
2852                        locals: Rc::new(locals),
2853                        root_name: root_name.to_string(),
2854                        menv: menv0.clone(),
2855                    };
2856                    let msg = self.render_lenient(&d.template, &psc);
2857                    let code = if d.severity == "error" {
2858                        "E6001"
2859                    } else {
2860                        "W6001"
2861                    };
2862                    self.env.report(Diag {
2863                        severity: d.severity.clone(),
2864                        id: Some(id),
2865                        message: msg,
2866                        path: ipath.clone(),
2867                        code: Some(code.into()),
2868                        loc: None,
2869                        by: None,
2870                    });
2871                }
2872            }
2873        }
2874    }
2875
2876    fn render_lenient(&self, template: &[TPart], sc: &Scope) -> String {
2877        let mut s = String::new();
2878        for p in template {
2879            match p {
2880                TPart::Text(t) => s.push_str(t),
2881                TPart::Expr(x) => {
2882                    if let Ok(v) = self.ev(x, sc) {
2883                        if let Ok(t) = self.to_str(&v) {
2884                            s.push_str(&t);
2885                        }
2886                    }
2887                }
2888            }
2889        }
2890        s
2891    }
2892
2893    // ---------- serialization ----------
2894    /// canonical JSON of a value (§10.4, D29): derived members included,
2895    /// hidden never; `settable_only` is the tool option D29 provides — the
2896    /// settable projection (required, optional, defaulted members) that a
2897    /// document for the same type would carry
2898    pub fn serialize(&self, v: &Value, root_name: &str, settable_only: bool) -> String {
2899        self.go(v, root_name, settable_only).unwrap_or_default()
2900    }
2901
2902    fn go(&self, x: &Value, root: &str, settable_only: bool) -> Option<String> {
2903        Some(match x {
2904            Value::Absent | Value::Undef => return None,
2905            Value::Null => "null".into(),
2906            Value::Bool(b) => {
2907                if *b {
2908                    "true".into()
2909                } else {
2910                    "false".into()
2911                }
2912            }
2913            Value::Int(i) => i.to_string(),
2914            Value::Float(f) => fmt_f(*f),
2915            Value::Str(s) => json_str(s),
2916            Value::Q { dim, value } => {
2917                let unit = self
2918                    .env
2919                    .base_unit_of
2920                    .borrow()
2921                    .get(dim)
2922                    .cloned()
2923                    .unwrap_or_else(|| dim.clone());
2924                format!(
2925                    "{{\"value\":{},\"unit\":{}}}",
2926                    fmt_f(*value),
2927                    json_str(&unit)
2928                )
2929            }
2930            Value::Ref(p) => json_str(&path_str(p, Some(root))),
2931            Value::Arr(a) => format!(
2932                "[{}]",
2933                a.borrow()
2934                    .items
2935                    .iter()
2936                    .filter_map(|i| self.go(i, root, settable_only))
2937                    .collect::<Vec<_>>()
2938                    .join(",")
2939            ),
2940            Value::Map(m) => format!(
2941                "{{{}}}",
2942                m.borrow()
2943                    .entries
2944                    .iter()
2945                    .filter_map(|(k, v)| self
2946                        .go(v, root, settable_only)
2947                        .map(|g| format!("{}:{g}", json_str(k))))
2948                    .collect::<Vec<_>>()
2949                    .join(",")
2950            ),
2951            Value::Rec(r) => {
2952                let b = r.borrow();
2953                let mut parts = vec![];
2954                let mut done: HashSet<String> = HashSet::new();
2955                for n in &b.entry_order {
2956                    done.insert(n.clone());
2957                    if let Some(v) = b.extra(n) {
2958                        parts.push(format!("{}:{}", json_str(n), self.raw_json(v, root)));
2959                        continue;
2960                    }
2961                    let Some(s) = b.slot(n) else { continue };
2962                    if matches!(s.state, SlotState::Invalid | SlotState::Absent)
2963                        || s.kind == MKind::Der
2964                    {
2965                        continue;
2966                    }
2967                    if let Some(g) = self.go(&s.value, root, settable_only) {
2968                        parts.push(format!("{}:{g}", json_str(n)));
2969                    }
2970                }
2971                for m in rec_members(&b.rt) {
2972                    if done.contains(&m.name) && m.kind != MKind::Der {
2973                        continue;
2974                    }
2975                    if settable_only && m.kind == MKind::Der {
2976                        continue;
2977                    }
2978                    let Some(s) = b.slot(&m.name) else { continue };
2979                    if s.hidden
2980                        || matches!(
2981                            s.state,
2982                            SlotState::Invalid | SlotState::Absent | SlotState::Unforced
2983                        )
2984                    {
2985                        continue;
2986                    }
2987                    if let Some(g) = self.go(&s.value, root, settable_only) {
2988                        parts.push(format!("{}:{g}", json_str(&m.name)));
2989                    }
2990                }
2991                format!("{{{}}}", parts.join(","))
2992            }
2993            Value::JObj(_) | Value::JArr(_) => self.raw_json(x, root),
2994            _ => return None,
2995        })
2996    }
2997
2998    fn raw_json(&self, v: &Value, root: &str) -> String {
2999        match v {
3000            Value::Null => "null".into(),
3001            Value::Bool(b) => {
3002                if *b {
3003                    "true".into()
3004                } else {
3005                    "false".into()
3006                }
3007            }
3008            Value::Int(i) => i.to_string(),
3009            Value::Float(f) => fmt_f(*f),
3010            Value::Str(s) => json_str(s),
3011            Value::JArr(items) => format!(
3012                "[{}]",
3013                items
3014                    .iter()
3015                    .map(|x| self.raw_json(x, root))
3016                    .collect::<Vec<_>>()
3017                    .join(",")
3018            ),
3019            Value::JObj(es) => format!(
3020                "{{{}}}",
3021                es.iter()
3022                    .map(|(k, x)| format!("{}:{}", json_str(k), self.raw_json(x, root)))
3023                    .collect::<Vec<_>>()
3024                    .join(",")
3025            ),
3026            Value::PreVal(pv) => match self.ev(&pv.expr, &pv.scope) {
3027                Ok(v) => self.go(&v, root, false).unwrap_or_else(|| "null".into()),
3028                Err(_) => "null".into(),
3029            },
3030            other => self.go(other, root, false).unwrap_or_else(|| "null".into()),
3031        }
3032    }
3033}