Skip to main content

decl_lang/
semantics.rs

1//! Value model, environment, and type resolution — a port of the
2//! reference implementation's semantics.ts.
3use crate::ast::*;
4use num_bigint::BigInt;
5use num_traits::{ToPrimitive, Zero};
6use regex::Regex;
7use std::cell::{Cell, RefCell};
8use std::collections::{BTreeMap, HashMap, HashSet};
9use std::fmt;
10use std::rc::Rc;
11use std::sync::LazyLock;
12
13// the pattern-interpolation grammar (§3.6): compiled once
14static PATTERN_HOLE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"\$\{([^}]*)\}").unwrap());
15static PATTERN_STR_LIT: LazyLock<Regex> =
16    LazyLock::new(|| Regex::new(r#"^"((?:[^"\\]|\\.)*)"$"#).unwrap());
17static PATTERN_INT_RANGE: LazyLock<Regex> =
18    LazyLock::new(|| Regex::new(r"^(-?[0-9]+)\.\.(<?)(-?[0-9]+)$").unwrap());
19static PATTERN_INT_LIT: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^-?[0-9]+$").unwrap());
20static PATTERN_IDENT: LazyLock<Regex> =
21    LazyLock::new(|| Regex::new(r"^[A-Za-z_][A-Za-z0-9_.]*$").unwrap());
22
23// ---------------- paths ----------------
24#[derive(Clone, Debug, PartialEq)]
25/// a segment of a canonical path (§7.2)
26pub enum Seg {
27    /// a record member by name: dotted when the dot can spell it (§7.2)
28    Name(String),
29    /// an array index
30    Idx(usize),
31    /// a map key: always bracketed (§7.2)
32    Key(String),
33}
34/// a canonical path
35pub type SegPath = Vec<Seg>;
36/// A segment's text as the path spells it.
37pub fn seg_text(s: &Seg) -> String {
38    match s {
39        Seg::Name(n) | Seg::Key(n) => n.clone(),
40        Seg::Idx(i) => i.to_string(),
41    }
42}
43/// dot-spellable (§3.11, §4.3): identifier-shaped and not a literal keyword
44pub fn dot_spellable(name: &str) -> bool {
45    let mut cs = name.chars();
46    let head = matches!(cs.next(), Some(c) if c == '_' || c.is_ascii_alphabetic());
47    head && cs.all(|c| c == '_' || c.is_ascii_alphanumeric())
48        && !matches!(name, "true" | "false" | "null")
49}
50
51// ---------------- values ----------------
52#[derive(Clone)]
53/// a runtime value (§9): the scalars, quantities, references, and instances,
54/// and the engine's intermediate forms
55pub enum Value {
56    /// an integer
57    Int(BigInt),
58    /// a float
59    Float(f64),
60    /// a string
61    Str(String),
62    /// a boolean
63    Bool(bool),
64    /// null
65    Null,
66    /// an absent optional member (§4.6)
67    Absent,
68    /// not yet computed
69    Undef,
70    /// a quantity
71    Q {
72        /// its dimension key
73        dim: String,
74        /// its value in the base unit
75        value: f64,
76    },
77    /// a reference, by canonical path
78    Ref(Rc<SegPath>),
79    /// a record instance
80    Rec(Rc<RefCell<RecInst>>),
81    /// an array
82    Arr(Rc<RefCell<ArrV>>),
83    /// a map
84    Map(Rc<RefCell<MapV>>),
85    /// a range value
86    Range {
87        /// the lower bound
88        lo: Box<Value>,
89        /// the upper bound
90        hi: Box<Value>,
91        /// whether the upper bound is excluded
92        excl: bool,
93    },
94    /// a closure
95    Clo(Rc<Closure>),
96    /// a native function
97    Nat(NatFn),
98    /// a standard-library path, partially spelled
99    Std(Rc<Vec<String>>),
100    /// a namespace
101    NsRef(Rc<NsRefV>),
102    /// a pattern
103    Pat(String),
104    /// a record literal not yet bound
105    PreObj(Rc<Vec<(String, Value)>>),
106    /// an array literal not yet bound: (spread, item)
107    PreArr(Rc<Vec<(bool, Value)>>),
108    /// an expression not yet evaluated, with its scope
109    PreVal(Rc<PreValV>),
110    /// a JSON object, as read
111    JObj(Rc<Vec<(String, Value)>>),
112    /// a JSON array, as read
113    JArr(Rc<Vec<Value>>),
114    /// a path value
115    Segs(Rc<SegPath>),
116}
117
118impl fmt::Debug for Value {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        match self {
121            Value::Int(i) => write!(f, "{i}"),
122            Value::Float(x) => write!(f, "{x}"),
123            Value::Str(s) => write!(f, "{s:?}"),
124            Value::Bool(b) => write!(f, "{b}"),
125            Value::Null => write!(f, "null"),
126            Value::Absent => write!(f, "ABSENT"),
127            Value::Undef => write!(f, "UNDEF"),
128            Value::Q { dim, value } => write!(f, "{value}<{dim}>"),
129            other => write!(f, "<{}>", other.tag()),
130        }
131    }
132}
133
134impl Value {
135    /// The value's kind, as a word.
136    pub fn tag(&self) -> &'static str {
137        match self {
138            Value::Int(_) => "int",
139            Value::Float(_) => "float",
140            Value::Str(_) => "string",
141            Value::Bool(_) => "bool",
142            Value::Null => "null",
143            Value::Absent => "absent",
144            Value::Undef => "undef",
145            Value::Q { .. } => "quantity",
146            Value::Ref(_) => "ref",
147            Value::Rec(_) => "record",
148            Value::Arr(_) => "array",
149            Value::Map(_) => "map",
150            Value::Range { .. } => "range",
151            Value::Clo(_) => "closure",
152            Value::Nat(_) => "native",
153            Value::Std(_) => "std",
154            Value::NsRef(_) => "namespace",
155            Value::Pat(_) => "pattern",
156            Value::PreObj(_) => "pre-obj",
157            Value::PreArr(_) => "pre-arr",
158            Value::PreVal(_) => "pre-val",
159            Value::JObj(_) => "json-obj",
160            Value::JArr(_) => "json-arr",
161            Value::Segs(_) => "segs",
162        }
163    }
164    /// Whether the value is not yet computed.
165    pub fn is_undef(&self) -> bool {
166        matches!(self, Value::Undef)
167    }
168    /// Whether the value is absent.
169    pub fn is_absent(&self) -> bool {
170        matches!(self, Value::Absent)
171    }
172    /// The canonical path the value sits at or refers to, when it has one.
173    pub fn place(&self) -> Option<SegPath> {
174        match self {
175            Value::Ref(p) => Some((**p).clone()),
176            Value::Rec(r) => Some(r.borrow().path.clone()),
177            Value::Arr(a) => Some(a.borrow().path.clone()),
178            Value::Map(m) => Some(m.borrow().path.clone()),
179            _ => None,
180        }
181    }
182}
183
184/// a native function
185pub type NatFn = Rc<dyn Fn(&[Value]) -> R<Value>>;
186
187/// a function value: its parameters, its body, the scope it closed over
188pub struct Closure {
189    /// the parameters
190    pub params: Vec<String>,
191    /// the body
192    pub body: Rc<Expr>,
193    /// the scope
194    pub scope: Scope,
195}
196/// a namespace's exports
197pub struct NsRefV {
198    /// the exports, by name
199    pub exports: Rc<RefCell<HashMap<String, Export>>>,
200}
201/// an expression waiting to be evaluated in its scope
202pub struct PreValV {
203    /// the expression
204    pub expr: Rc<Expr>,
205    /// the scope
206    pub scope: Scope,
207}
208/// an array value
209pub struct ArrV {
210    /// the items
211    pub items: Vec<Value>,
212    /// its canonical path
213    pub path: SegPath,
214}
215/// a map value
216pub struct MapV {
217    /// the entries, in order
218    pub entries: Vec<(String, Value)>,
219    /// its canonical path
220    pub path: SegPath,
221}
222impl MapV {
223    /// The value at a key.
224    pub fn get(&self, k: &str) -> Option<&Value> {
225        self.entries.iter().find(|(n, _)| n == k).map(|(_, v)| v)
226    }
227    /// Whether the key is present.
228    pub fn has(&self, k: &str) -> bool {
229        self.entries.iter().any(|(n, _)| n == k)
230    }
231    /// Set a key's value.
232    pub fn set(&mut self, k: String, v: Value) {
233        if let Some(e) = self.entries.iter_mut().find(|(n, _)| *n == k) {
234            e.1 = v;
235        } else {
236            self.entries.push((k, v));
237        }
238    }
239}
240
241#[derive(Clone, Copy, PartialEq, Debug)]
242/// a member's kind (§5)
243pub enum MKind {
244    /// required
245    Req,
246    /// optional
247    Opt,
248    /// defaulted
249    Dflt,
250    /// derived
251    Der,
252}
253#[derive(Clone, Copy, PartialEq, Debug)]
254/// the state of a slot
255pub enum SlotState {
256    /// not forced yet
257    Unforced,
258    /// being forced
259    Forcing,
260    /// forced, with a value
261    Ok,
262    /// an error invalidated it
263    Invalid,
264    /// absent
265    Absent,
266}
267
268#[derive(Clone)]
269/// how a slot computes its value
270pub enum Compute {
271    /// a supplied value checked against the types
272    Check {
273        /// the value supplied
274        raw: Value,
275        /// the types to check against
276        types: Vec<RT>,
277        /// the member
278        name: String,
279        /// the root
280        root_name: String,
281        /// the module environment
282        menv: Option<Rc<Env>>,
283    },
284    /// a default expression
285    Default {
286        /// the expression
287        expr: Rc<Expr>,
288        /// the types to check against
289        types: Vec<RT>,
290        /// the member
291        name: String,
292        /// the root
293        root_name: String,
294        /// the module environment
295        menv: Option<Rc<Env>>,
296    },
297    /// a derived expression
298    Derived {
299        /// the expression
300        expr: Rc<Expr>,
301        /// the declared type
302        ty: Option<RT>,
303        /// the value the document supplied, to compare (§10.5)
304        supplied: Option<Value>,
305        /// the member
306        name: String,
307        /// the root
308        root_name: String,
309        /// the module environment
310        menv: Option<Rc<Env>>,
311    },
312}
313
314/// a member's slot in an instance
315pub struct Slot {
316    /// the member's kind
317    pub kind: MKind,
318    /// `x$ = e`: computed, never part of the value (D34)
319    pub hidden: bool,
320    /// its state
321    pub state: SlotState,
322    /// its value, once forced
323    pub value: Value,
324    /// how it computes
325    pub compute: Option<Compute>,
326}
327
328/// a record instance: its type, its path, its slots
329pub struct RecInst {
330    /// the type's name, when named
331    pub type_name: Option<String>,
332    /// the type
333    pub rt: RT,
334    /// its canonical path
335    pub path: SegPath,
336    /// the enclosing instance
337    pub parent: Option<Rc<RefCell<RecInst>>>,
338    // declaration order matters (forcing order drives diagnostic order)
339    /// the slots, in declaration order
340    pub slots: Vec<(String, Slot)>,
341    /// the order the document supplied the members
342    pub entry_order: Vec<String>,
343    /// members of an open record beyond its type
344    pub extras: Vec<(String, Value)>,
345    /// the module environment
346    pub menv: Option<Rc<Env>>,
347}
348impl RecInst {
349    /// An extra member's value.
350    pub fn extra(&self, n: &str) -> Option<&Value> {
351        self.extras.iter().find(|(k, _)| k == n).map(|(_, v)| v)
352    }
353    /// Set an extra member.
354    pub fn set_extra(&mut self, n: &str, v: Value) {
355        if let Some(e) = self.extras.iter_mut().find(|(k, _)| k == n) {
356            e.1 = v;
357        } else {
358            self.extras.push((n.to_string(), v));
359        }
360    }
361    /// A slot by name.
362    pub fn slot(&self, n: &str) -> Option<&Slot> {
363        self.slots.iter().find(|(k, _)| k == n).map(|(_, s)| s)
364    }
365    /// A slot by name, mutably.
366    pub fn slot_mut(&mut self, n: &str) -> Option<&mut Slot> {
367        self.slots.iter_mut().find(|(k, _)| k == n).map(|(_, s)| s)
368    }
369    /// Whether the slot exists.
370    pub fn has_slot(&self, n: &str) -> bool {
371        self.slots.iter().any(|(k, _)| k == n)
372    }
373}
374
375#[derive(Clone)]
376/// an evaluation scope: the enclosing instance, the local variables, the root, the module environment
377pub struct Scope {
378    /// the enclosing instance
379    pub inst: Option<Rc<RefCell<RecInst>>>,
380    /// the local variables
381    pub locals: Rc<HashMap<String, Value>>,
382    /// the root
383    pub root_name: String,
384    /// the module environment
385    pub menv: Option<Rc<Env>>,
386}
387impl Scope {
388    /// A scope at a root.
389    pub fn new(root_name: &str, menv: Option<Rc<Env>>) -> Scope {
390        Scope {
391            inst: None,
392            locals: Rc::new(HashMap::new()),
393            root_name: root_name.to_string(),
394            menv,
395        }
396    }
397    /// The scope with local variables.
398    pub fn with_locals(&self, locals: HashMap<String, Value>) -> Scope {
399        Scope {
400            inst: self.inst.clone(),
401            locals: Rc::new(locals),
402            root_name: self.root_name.clone(),
403            menv: self.menv.clone(),
404        }
405    }
406    /// The scope inside an instance.
407    pub fn with_inst(&self, inst: Option<Rc<RefCell<RecInst>>>) -> Scope {
408        Scope {
409            inst,
410            locals: self.locals.clone(),
411            root_name: self.root_name.clone(),
412            menv: self.menv.clone(),
413        }
414    }
415    /// The scope in a module environment.
416    pub fn with_menv(&self, menv: Option<Rc<Env>>) -> Scope {
417        Scope {
418            inst: self.inst.clone(),
419            locals: self.locals.clone(),
420            root_name: self.root_name.clone(),
421            menv,
422        }
423    }
424}
425
426// ---------------- failures & diagnostics ----------------
427/// an evaluation error, with its code when it has one
428pub struct EvalErr {
429    /// the message
430    pub msg: String,
431    /// the code
432    pub code: Option<String>,
433}
434/// why an evaluation stopped
435pub enum Fail {
436    /// it read an invalid value
437    Taint,
438    /// it must wait for phase 2
439    Defer,
440    /// an error
441    Eval(EvalErr),
442}
443/// an evaluation's outcome
444pub type R<T> = Result<T, Fail>;
445/// An evaluation error without a code.
446pub fn err<T>(msg: impl Into<String>) -> R<T> {
447    Err(Fail::Eval(EvalErr {
448        msg: msg.into(),
449        code: None,
450    }))
451}
452/// An evaluation error with a code.
453pub fn err_code<T>(msg: impl Into<String>, code: &str) -> R<T> {
454    Err(Fail::Eval(EvalErr {
455        msg: msg.into(),
456        code: Some(code.to_string()),
457    }))
458}
459
460#[derive(Clone, Debug)]
461/// a diagnostic (§6, §12)
462pub struct Diag {
463    /// `error`, `warn`, or `info`
464    pub severity: String,
465    /// the constraint's stable id, for an assertion
466    pub id: Option<String>,
467    /// the message, rendered
468    pub message: String,
469    /// the canonical path
470    pub path: String,
471    /// the code (§12)
472    pub code: Option<String>,
473    /// the source range the checker reported at (the declaration, or the expression under inference)
474    pub loc: Option<Loc>,
475    /// the evaluation step that produced it (a slot, a root, an assert): dependency tracking's tag
476    pub by: Option<String>,
477}
478impl Diag {
479    /// An error diagnostic at a path.
480    pub fn error(message: impl Into<String>, path: String, code: Option<&str>) -> Diag {
481        Diag {
482            severity: "error".into(),
483            id: None,
484            message: message.into(),
485            path,
486            code: code.map(|c| c.to_string()),
487            loc: None,
488            by: None,
489        }
490    }
491    /// The diagnostic as a JSON object in the report's field order (§12.2), with the file when given.
492    pub fn to_json(&self, file: Option<&str>) -> String {
493        let mut parts = Vec::new();
494        // the report's field order (§12.2): file, code, id, severity,
495        // message, path — absent fields omitted (byte-identical across
496        // implementations)
497        if let Some(f) = file {
498            parts.push(format!("\"file\":{}", json_str(f)));
499        }
500        if let Some(c) = &self.code {
501            parts.push(format!("\"code\":{}", json_str(c)));
502        }
503        if let Some(id) = &self.id {
504            parts.push(format!("\"id\":{}", json_str(id)));
505        }
506        parts.push(format!("\"severity\":{}", json_str(&self.severity)));
507        parts.push(format!("\"message\":{}", json_str(&self.message)));
508        parts.push(format!("\"path\":{}", json_str(&self.path)));
509        format!("{{{}}}", parts.join(","))
510    }
511}
512
513// ---------------- resolved types ----------------
514/// a resolved type, shared
515pub type RT = Rc<Ty>;
516
517/// a resolved type: its kind, its name when named, its `else` tail
518pub struct Ty {
519    /// the kind
520    pub k: RTk,
521    /// the name, for a named type
522    pub name: RefCell<Option<String>>,
523    /// the `else` tail
524    pub tail: RefCell<Option<Tail>>,
525}
526/// A resolved type of a kind.
527pub fn ty(k: RTk) -> RT {
528    Rc::new(Ty {
529        k,
530        name: RefCell::new(None),
531        tail: RefCell::new(None),
532    })
533}
534
535/// the kinds of resolved types (§3)
536pub enum RTk {
537    /// a primitive
538    Prim(String),
539    /// a literal
540    Lit(Value),
541    /// a numeric range
542    Range {
543        /// the lower bound
544        lo: Value,
545        /// the upper bound
546        hi: Value,
547        /// whether the upper bound is excluded
548        excl: bool,
549        /// int or float
550        base: String,
551    },
552    /// a string pattern
553    Pattern {
554        /// its text
555        src: String,
556        /// compiled
557        re: Regex,
558    },
559    /// an array
560    Arr {
561        /// the element type
562        elem: RT,
563        /// the lower size bound
564        lo: Option<i64>,
565        /// the upper size bound
566        hi: Option<i64>,
567    },
568    /// a map
569    Map {
570        /// the key type
571        key: RT,
572        /// the value type
573        val: RT,
574    },
575    /// a union
576    Union(Vec<RT>),
577    /// an intersection not yet merged
578    IsectN(Vec<RT>),
579    /// a record
580    Rec(RecType),
581    /// a predicate refinement
582    Pred {
583        /// the base type
584        base: RT,
585        /// the predicates
586        preds: Vec<Rc<Expr>>,
587    },
588    /// a reference type
589    Ref(RT),
590    /// a quantity of a dimension
591    Quantity(String),
592    /// a function type
593    Func {
594        /// the parameter types
595        params: Vec<RT>,
596        /// the return type
597        ret: RT,
598    },
599    /// any value
600    Any,
601}
602
603/// a record type's shape
604pub struct RecType {
605    /// whether it is open
606    pub open: Cell<bool>,
607    /// its members
608    pub members: RefCell<Vec<Member>>,
609    /// its assertions and guarded groups
610    pub asserts: RefCell<Vec<AssertItem>>,
611    /// `context $parent: ref<T>` declarations (D30), checked at embedding sites
612    pub ctx_decls: RefCell<Vec<(String, RT)>>,
613    /// still being filled; extensions of it wait in `pending` (§3.14)
614    pub filling: Cell<bool>,
615    /// extensions not yet merged (recursive types)
616    pub pending: RefCell<Vec<(RT, RT)>>,
617}
618/// An empty record type.
619pub fn rec_type(open: bool) -> RecType {
620    RecType {
621        open: Cell::new(open),
622        members: RefCell::new(vec![]),
623        asserts: RefCell::new(vec![]),
624        ctx_decls: RefCell::new(vec![]),
625        filling: Cell::new(false),
626        pending: RefCell::new(vec![]),
627    }
628}
629
630#[derive(Clone)]
631/// a member of a resolved record type
632pub struct Member {
633    /// the kind
634    pub kind: MKind,
635    /// the name
636    pub name: String,
637    /// a hidden member (D34): computed, never part of the value
638    pub hidden: bool,
639    /// the type
640    pub ty: Option<RT>,
641    /// the types a conjunction contributed
642    pub conj: Option<Vec<RT>>,
643    /// the default
644    pub dflt: Option<Rc<Expr>>,
645    /// the derived expression
646    pub expr: Option<Rc<Expr>>,
647    /// the module environment
648    pub menv: Option<Rc<Env>>,
649}
650
651#[derive(Clone)]
652/// an assertion or a guarded group of a record type
653pub struct AssertItem {
654    /// whether it is a `when` group
655    pub when: bool,
656    /// the name
657    pub name: String,
658    /// the condition
659    pub cond: Rc<Expr>,
660    /// the `else` tail
661    pub tail: Option<Tail>,
662    /// the members, for a group
663    pub body: Vec<MemberAst>,
664    /// the type declaring it, for the id
665    pub origin: Option<String>,
666    /// the module environment
667    pub menv: Option<Rc<Env>>,
668}
669
670/// The members of a record type; empty for any other type.
671pub fn rec_members(t: &RT) -> Vec<Member> {
672    match &t.k {
673        RTk::Rec(r) => r.members.borrow().clone(),
674        _ => vec![],
675    }
676}
677/// Whether the type is a record.
678pub fn is_rec(t: &RT) -> bool {
679    matches!(t.k, RTk::Rec(_))
680}
681
682// ---------------- dimension vectors ----------------
683/// a dimension as base dimensions with exponents
684pub type DimVec = BTreeMap<String, i32>;
685/// The dimension's key (`Length*Time^-1`).
686pub fn key_of_vec(v: &DimVec) -> String {
687    v.iter()
688        .filter(|(_, e)| **e != 0)
689        .map(|(n, e)| {
690            if *e == 1 {
691                n.clone()
692            } else {
693                format!("{n}^{e}")
694            }
695        })
696        .collect::<Vec<_>>()
697        .join("*")
698}
699/// A dimension from its key.
700pub fn vec_of_key(key: &str) -> DimVec {
701    let mut v = DimVec::new();
702    if key.is_empty() {
703        return v;
704    }
705    for p in key.split('*') {
706        let (n, e) = match p.split_once('^') {
707            Some((n, e)) => (n.to_string(), e.parse::<i32>().unwrap_or(1)),
708            None => (p.to_string(), 1),
709        };
710        *v.entry(n).or_insert(0) += e;
711    }
712    v
713}
714/// Two dimensions multiplied (`sign` 1) or divided (`sign` -1).
715pub fn vec_combine(a: &DimVec, b: &DimVec, sign: i32) -> DimVec {
716    let mut out = a.clone();
717    for (n, e) in b {
718        *out.entry(n.clone()).or_insert(0) += sign * e;
719    }
720    out
721}
722
723// ---------------- environment ----------------
724/// an exported name: the environment declaring it
725pub struct Export {
726    /// the environment
727    pub env: Rc<Env>,
728    /// the name there
729    pub name: String,
730}
731impl Clone for Export {
732    fn clone(&self) -> Self {
733        Export {
734            env: self.env.clone(),
735            name: self.name.clone(),
736        }
737    }
738}
739/// a constant's declaration and memoized value
740pub struct ConstEntry {
741    /// the expression
742    pub expr: Rc<Expr>,
743    /// the annotation
744    pub ty: Option<TypeAst>,
745    /// whether the value is computed
746    pub state: Cell<bool>,
747    /// the value
748    pub value: RefCell<Value>,
749}
750/// a function's declaration
751pub struct FuncEntry {
752    /// the parameters
753    pub params: Vec<Param>,
754    /// the return type
755    pub ret: Option<TypeAst>,
756    /// the body
757    pub body: Rc<Expr>,
758}
759/// a type's declaration
760pub struct TypeEntry {
761    /// the type
762    pub ast: TypeAst,
763    /// its `else` tail
764    pub tail: Option<Tail>,
765    /// the type parameters
766    pub params: Vec<Param>,
767}
768/// a diagnostic template's declaration
769pub struct DiagDecl {
770    /// the parameters
771    pub params: Vec<Param>,
772    /// the severity
773    pub severity: String,
774    /// the template
775    pub template: Vec<TPart>,
776}
777/// a unit's declaration
778pub struct UnitDecl {
779    /// its dimension
780    pub dim: Option<String>,
781    /// its factor
782    pub factor: Option<Rc<Expr>>,
783    /// its base unit
784    pub base: Option<String>,
785}
786/// evaluates a constant by name (the engine's)
787pub type ConstEval = Rc<dyn Fn(&str) -> R<Value>>;
788/// evaluates an expression (the engine's)
789pub type ExprEval = Rc<dyn Fn(&Rc<Expr>) -> R<Value>>;
790
791/// the environment of a module: its declarations, its imports, its roots and
792/// diagnostics, the unit space
793pub struct Env {
794    /// the type declarations
795    pub type_asts: RefCell<HashMap<String, Rc<TypeEntry>>>,
796    /// resolved types, memoized
797    pub type_memo: RefCell<HashMap<String, RT>>,
798    // names being spliced into a pattern right now, across nested
799    // resolutions — a mutually recursive pair is a cycle, not a stack overflow
800    /// the named types being resolved (recursion)
801    pub pattern_visiting: RefCell<Vec<String>>,
802    /// the constants
803    pub consts: RefCell<HashMap<String, Rc<ConstEntry>>>,
804    /// the functions
805    pub funcs: RefCell<HashMap<String, Rc<FuncEntry>>>,
806    /// names declared twice
807    pub duplicates: RefCell<Vec<String>>,
808    /// the outputs: name, type, expression
809    pub outputs: RefCell<Vec<(String, TypeAst, Rc<Expr>)>>,
810    /// the inputs: type, fallback
811    pub inputs: RefCell<HashMap<String, (TypeAst, Option<Rc<Expr>>)>>,
812    /// the diagnostic templates
813    pub diags: RefCell<HashMap<String, Rc<DiagDecl>>>,
814    /// every instance bound
815    pub registry: RefCell<Rc<RefCell<Vec<Rc<RefCell<RecInst>>>>>>,
816    /// the roots' values, by name
817    pub roots: RefCell<Rc<RefCell<Vec<(String, Value)>>>>,
818    /// the diagnostics raised
819    pub diagnostics: RefCell<Rc<RefCell<Vec<Diag>>>>,
820    /// the constant evaluator, once an engine is wired in
821    pub const_eval: RefCell<Option<ConstEval>>,
822    /// the expression evaluator, once an engine is wired in
823    pub expr_eval: RefCell<Option<ExprEval>>,
824    /// the imported names
825    pub imports: RefCell<HashMap<String, Export>>,
826    /// the namespace imports
827    pub namespaces: RefCell<HashMap<String, (Rc<Env>, Rc<RefCell<HashMap<String, Export>>>)>>,
828    const_diag_seen: RefCell<HashSet<String>>,
829    /// the dimension declarations
830    pub dim_decls: RefCell<HashMap<String, Option<Vec<(String, i32)>>>>,
831    /// dimensions resolved, memoized
832    pub dim_memo: RefCell<HashMap<String, DimVec>>,
833    /// the unit declarations
834    pub unit_decls: RefCell<HashMap<String, UnitDecl>>,
835    /// units resolved to (dimension, factor), memoized
836    pub unit_memo: RefCell<HashMap<String, (String, f64)>>,
837    /// the base unit of each dimension
838    pub base_unit_of: RefCell<HashMap<String, String>>,
839    /// the unit space's diagnostics (E4073 and friends)
840    pub space_diags: RefCell<Vec<Diag>>,
841    /// declaration order (HashMaps do not keep it; diagnostics follow it)
842    pub type_order: RefCell<Vec<String>>,
843    /// the units in declaration order
844    pub unit_order: RefCell<Vec<String>>,
845    /// installed by the checker: constant-evaluation errors go here instead of the report
846    pub const_diag_sink: RefCell<Option<Rc<RefCell<Vec<Diag>>>>>,
847    /// installed by the engine: the evaluation step a report is attributed to
848    pub tagger: RefCell<Option<Rc<dyn Fn() -> Option<String>>>>,
849}
850
851/// §6.7: evaluation- and validation-time diagnostics sort by (path, id), path in canonical order; stable
852pub fn sort_diags(diags: Vec<Diag>) -> Vec<Diag> {
853    let segs_of = |p: &str| -> SegPath {
854        if p.is_empty() {
855            return vec![];
856        }
857        parse_path(p, "").unwrap_or_else(|_| vec![Seg::Name(p.to_string())])
858    };
859    let mut keyed: Vec<(usize, SegPath, Diag)> = diags
860        .into_iter()
861        .enumerate()
862        .map(|(i, d)| (i, segs_of(&d.path), d))
863        .collect();
864    keyed.sort_by(|a, b| {
865        cmp_path(&a.1, &b.1)
866            .then_with(|| {
867                a.2.id
868                    .as_deref()
869                    .unwrap_or("")
870                    .cmp(b.2.id.as_deref().unwrap_or(""))
871            })
872            .then_with(|| a.0.cmp(&b.0))
873    });
874    keyed.into_iter().map(|(_, _, d)| d).collect()
875}
876
877const SI_PREFIXES: [(&str, f64); 20] = [
878    ("y", 1e-24),
879    ("z", 1e-21),
880    ("a", 1e-18),
881    ("f", 1e-15),
882    ("p", 1e-12),
883    ("n", 1e-9),
884    ("u", 1e-6),
885    ("m", 1e-3),
886    ("c", 1e-2),
887    ("d", 1e-1),
888    ("da", 1e1),
889    ("h", 1e2),
890    ("k", 1e3),
891    ("M", 1e6),
892    ("G", 1e9),
893    ("T", 1e12),
894    ("P", 1e15),
895    ("E", 1e18),
896    ("Z", 1e21),
897    ("Y", 1e24),
898];
899
900impl Env {
901    /// An empty environment.
902    pub fn new() -> Rc<Env> {
903        let env = Env {
904            type_asts: RefCell::new(HashMap::new()),
905            type_memo: RefCell::new(HashMap::new()),
906            pattern_visiting: RefCell::new(vec![]),
907            consts: RefCell::new(HashMap::new()),
908            funcs: RefCell::new(HashMap::new()),
909            duplicates: RefCell::new(vec![]),
910            outputs: RefCell::new(vec![]),
911            inputs: RefCell::new(HashMap::new()),
912            diags: RefCell::new(HashMap::new()),
913            registry: RefCell::new(Rc::new(RefCell::new(vec![]))),
914            roots: RefCell::new(Rc::new(RefCell::new(vec![]))),
915            diagnostics: RefCell::new(Rc::new(RefCell::new(vec![]))),
916            const_eval: RefCell::new(None),
917            expr_eval: RefCell::new(None),
918            imports: RefCell::new(HashMap::new()),
919            namespaces: RefCell::new(HashMap::new()),
920            const_diag_seen: RefCell::new(HashSet::new()),
921            dim_decls: RefCell::new(HashMap::new()),
922            dim_memo: RefCell::new(HashMap::new()),
923            unit_decls: RefCell::new(HashMap::new()),
924            unit_memo: RefCell::new(HashMap::new()),
925            base_unit_of: RefCell::new(HashMap::new()),
926            space_diags: RefCell::new(vec![]),
927            type_order: RefCell::new(vec![]),
928            unit_order: RefCell::new(vec![]),
929            const_diag_sink: RefCell::new(None),
930            tagger: RefCell::new(None),
931        };
932        env.seed_units();
933        Rc::new(env)
934    }
935
936    // std.units — the SI catalog generated from the §13.10 prefix rule (D15)
937    fn seed_units(&self) {
938        let unit = |sym: &str, dim: Option<&str>, factor: f64, base: &str| {
939            let mut m = self.unit_decls.borrow_mut();
940            if m.contains_key(sym) {
941                return;
942            }
943            self.unit_order.borrow_mut().push(sym.to_string());
944            m.insert(
945                sym.to_string(),
946                match dim {
947                    Some(d) => UnitDecl {
948                        dim: Some(d.to_string()),
949                        factor: None,
950                        base: None,
951                    },
952                    None => UnitDecl {
953                        dim: None,
954                        factor: Some(Rc::new(Expr::Lit(Value::Float(factor)))),
955                        base: Some(base.to_string()),
956                    },
957                },
958            );
959        };
960        let bases = [
961            ("Time", "s"),
962            ("Length", "m"),
963            ("Mass", "kg"),
964            ("Current", "A"),
965            ("Temperature", "K"),
966            ("Amount", "mol"),
967            ("LuminousIntensity", "cd"),
968        ];
969        for (d, _) in bases {
970            self.dim_decls.borrow_mut().insert(d.to_string(), None);
971        }
972        let t = |n: &str, e: i32| (n.to_string(), e);
973        let derived: Vec<(&str, Option<Vec<(String, i32)>>, &str)> = vec![
974            ("Frequency", Some(vec![t("Time", -1)]), "Hz"),
975            (
976                "Force",
977                Some(vec![t("Mass", 1), t("Length", 1), t("Time", -2)]),
978                "N",
979            ),
980            (
981                "Pressure",
982                Some(vec![t("Mass", 1), t("Length", -1), t("Time", -2)]),
983                "Pa",
984            ),
985            (
986                "Energy",
987                Some(vec![t("Mass", 1), t("Length", 2), t("Time", -2)]),
988                "J",
989            ),
990            (
991                "Power",
992                Some(vec![t("Mass", 1), t("Length", 2), t("Time", -3)]),
993                "W",
994            ),
995            ("Charge", Some(vec![t("Current", 1), t("Time", 1)]), "C"),
996            (
997                "Voltage",
998                Some(vec![
999                    t("Mass", 1),
1000                    t("Length", 2),
1001                    t("Time", -3),
1002                    t("Current", -1),
1003                ]),
1004                "V",
1005            ),
1006            (
1007                "Resistance",
1008                Some(vec![
1009                    t("Mass", 1),
1010                    t("Length", 2),
1011                    t("Time", -3),
1012                    t("Current", -2),
1013                ]),
1014                "Ohm",
1015            ),
1016            (
1017                "Capacitance",
1018                Some(vec![
1019                    t("Mass", -1),
1020                    t("Length", -2),
1021                    t("Time", 4),
1022                    t("Current", 2),
1023                ]),
1024                "F",
1025            ),
1026            ("DataSize", None, "bit"),
1027        ];
1028        for (d, terms, _) in &derived {
1029            self.dim_decls
1030                .borrow_mut()
1031                .insert(d.to_string(), terms.clone());
1032        }
1033        for (d, s) in bases {
1034            unit(s, Some(d), 1.0, "");
1035        }
1036        for (d, _, s) in &derived {
1037            unit(s, Some(d), 1.0, "");
1038        }
1039        unit("B", None, 8.0, "bit");
1040        unit("g", None, 1e-3, "kg");
1041        let mut prefixable: Vec<&str> = bases
1042            .iter()
1043            .map(|(_, s)| *s)
1044            .filter(|s| *s != "kg")
1045            .collect();
1046        prefixable.extend(derived.iter().map(|(_, _, s)| *s).filter(|s| *s != "bit"));
1047        prefixable.push("g");
1048        for u0 in prefixable {
1049            for (p, f) in SI_PREFIXES {
1050                unit(&format!("{p}{u0}"), None, f, u0);
1051            }
1052        }
1053        for u0 in ["bit", "B"] {
1054            for (p, f) in [
1055                ("Ki", 1024f64),
1056                ("Mi", 1024f64.powi(2)),
1057                ("Gi", 1024f64.powi(3)),
1058                ("Ti", 1024f64.powi(4)),
1059                ("Pi", 1024f64.powi(5)),
1060                ("Ei", 1024f64.powi(6)),
1061            ] {
1062                unit(&format!("{p}{u0}"), None, f, u0);
1063            }
1064            for (p, f) in SI_PREFIXES {
1065                if ["k", "M", "G", "T", "P", "E"].contains(&p) {
1066                    unit(&format!("{p}{u0}"), None, f, u0);
1067                }
1068            }
1069        }
1070    }
1071
1072    /// Load declarations into the environment (§5, §8).
1073    pub fn load(&self, decls: &[Decl]) {
1074        let mut seen: HashSet<String> = HashSet::new();
1075        for d in decls {
1076            if let Some(n) = d.name() {
1077                if !matches!(d.body, DeclBody::Unit { .. } | DeclBody::Dimension { .. })
1078                    && !seen.insert(n.to_string())
1079                {
1080                    self.duplicates.borrow_mut().push(n.to_string());
1081                }
1082            }
1083            match &d.body {
1084                DeclBody::Dimension { name, terms } => {
1085                    if self.dim_decls.borrow().contains_key(name) {
1086                        self.space_diags.borrow_mut().push(Diag::error(
1087                            format!("dimension {name} redeclared"),
1088                            String::new(),
1089                            Some("E3001"),
1090                        ));
1091                    } else {
1092                        self.dim_decls
1093                            .borrow_mut()
1094                            .insert(name.clone(), terms.clone());
1095                    }
1096                }
1097                DeclBody::Unit {
1098                    name,
1099                    dim,
1100                    factor,
1101                    base,
1102                } => {
1103                    if self.unit_decls.borrow().contains_key(name) {
1104                        self.space_diags.borrow_mut().push(Diag::error(
1105                            format!("unit {name} redeclared"),
1106                            String::new(),
1107                            Some("E4073"),
1108                        ));
1109                    } else {
1110                        self.unit_order.borrow_mut().push(name.clone());
1111                        self.unit_decls.borrow_mut().insert(
1112                            name.clone(),
1113                            UnitDecl {
1114                                dim: dim.clone(),
1115                                factor: factor.clone(),
1116                                base: base.clone(),
1117                            },
1118                        );
1119                    }
1120                }
1121                DeclBody::Type {
1122                    name,
1123                    params,
1124                    ty,
1125                    tail,
1126                } => {
1127                    if !self.type_asts.borrow().contains_key(name) {
1128                        self.type_order.borrow_mut().push(name.clone());
1129                    }
1130                    self.type_asts.borrow_mut().insert(
1131                        name.clone(),
1132                        Rc::new(TypeEntry {
1133                            ast: ty.clone(),
1134                            tail: tail.clone(),
1135                            params: params.clone(),
1136                        }),
1137                    );
1138                }
1139                DeclBody::Const { name, ty, expr } => {
1140                    self.consts.borrow_mut().insert(
1141                        name.clone(),
1142                        Rc::new(ConstEntry {
1143                            expr: expr.clone(),
1144                            ty: ty.clone(),
1145                            state: Cell::new(false),
1146                            value: RefCell::new(Value::Null),
1147                        }),
1148                    );
1149                }
1150                DeclBody::Func {
1151                    name,
1152                    params,
1153                    ret,
1154                    body,
1155                } => {
1156                    self.funcs.borrow_mut().insert(
1157                        name.clone(),
1158                        Rc::new(FuncEntry {
1159                            params: params.clone(),
1160                            ret: ret.clone(),
1161                            body: body.clone(),
1162                        }),
1163                    );
1164                }
1165                DeclBody::Output { name, ty, expr } => {
1166                    self.outputs
1167                        .borrow_mut()
1168                        .push((name.clone(), ty.clone(), expr.clone()))
1169                }
1170                DeclBody::Input { name, ty, fallback } => {
1171                    self.inputs
1172                        .borrow_mut()
1173                        .insert(name.clone(), (ty.clone(), fallback.clone()));
1174                }
1175                DeclBody::Diagnostic {
1176                    name,
1177                    params,
1178                    severity,
1179                    template,
1180                } => {
1181                    self.diags.borrow_mut().insert(
1182                        name.clone(),
1183                        Rc::new(DiagDecl {
1184                            params: params.clone(),
1185                            severity: severity.clone(),
1186                            template: template.clone(),
1187                        }),
1188                    );
1189                }
1190                _ => {}
1191            }
1192        }
1193    }
1194
1195    /// Raise a diagnostic.
1196    pub fn report(&self, d: Diag) {
1197        let by = self.tagger.borrow().as_ref().and_then(|t| t());
1198        let mut d = d;
1199        if by.is_some() {
1200            d.by = by;
1201        }
1202        self.diagnostics.borrow().borrow_mut().push(d);
1203    }
1204    /// replace every diagnostic (the run entry points sort them, §6.7)
1205    pub fn diag_set(&self, diags: Vec<Diag>) {
1206        let rc = self.diagnostics.borrow().clone();
1207        *rc.borrow_mut() = diags;
1208    }
1209    /// Forget a root.
1210    pub fn remove_root(&self, name: &str) {
1211        let rc = self.roots.borrow().clone();
1212        rc.borrow_mut().retain(|(n, _)| n != name);
1213    }
1214    /// The roots' names, in order.
1215    pub fn root_names(&self) -> Vec<String> {
1216        self.roots
1217            .borrow()
1218            .borrow()
1219            .iter()
1220            .map(|(n, _)| n.clone())
1221            .collect()
1222    }
1223    /// Keep the instances a predicate accepts.
1224    pub fn registry_retain(&self, mut pred: impl FnMut(&Rc<RefCell<RecInst>>) -> bool) {
1225        let rc = self.registry.borrow().clone();
1226        rc.borrow_mut().retain(|i| pred(i));
1227    }
1228    /// The diagnostics raised so far.
1229    pub fn diagnostics_vec(&self) -> Vec<Diag> {
1230        self.diagnostics.borrow().borrow().clone()
1231    }
1232    /// How many diagnostics were raised.
1233    pub fn diag_len(&self) -> usize {
1234        self.diagnostics.borrow().borrow().len()
1235    }
1236    /// Forget the diagnostics after the first `n`.
1237    pub fn diag_truncate(&self, n: usize) {
1238        self.diagnostics.borrow().borrow_mut().truncate(n);
1239    }
1240    /// A root's value.
1241    pub fn root(&self, name: &str) -> Option<Value> {
1242        self.roots
1243            .borrow()
1244            .borrow()
1245            .iter()
1246            .find(|(n, _)| n == name)
1247            .map(|(_, v)| v.clone())
1248    }
1249    /// Set a root's value.
1250    pub fn set_root(&self, name: &str, v: Value) {
1251        let rc = self.roots.borrow().clone();
1252        let mut roots = rc.borrow_mut();
1253        if let Some(e) = roots.iter_mut().find(|(n, _)| n == name) {
1254            e.1 = v;
1255        } else {
1256            roots.push((name.to_string(), v));
1257        }
1258    }
1259    /// The roots' values, in order.
1260    pub fn root_values(&self) -> Vec<Value> {
1261        self.roots
1262            .borrow()
1263            .borrow()
1264            .iter()
1265            .map(|(_, v)| v.clone())
1266            .collect()
1267    }
1268    /// Register an instance.
1269    pub fn registry_push(&self, inst: Rc<RefCell<RecInst>>) {
1270        self.registry.borrow().borrow_mut().push(inst);
1271    }
1272    /// The instances registered so far.
1273    pub fn registry_snapshot(&self) -> Vec<Rc<RefCell<RecInst>>> {
1274        self.registry.borrow().borrow().clone()
1275    }
1276
1277    // §4.13: a named endpoint in a constant position evaluates at elaboration time
1278    /// A numeric constant's value where a type expects a number (a size, a bound).
1279    pub fn const_num(&self, v: &Value) -> Value {
1280        let name = match v {
1281            Value::Str(s) => s.clone(),
1282            other => return other.clone(),
1283        };
1284        let ce = self.const_eval.borrow().clone();
1285        let Some(ce) = ce else { return v.clone() };
1286        if !self.consts.borrow().contains_key(&name) {
1287            return v.clone();
1288        }
1289        let diag = |code: &str, message: String| {
1290            let key = format!("{name}{code}");
1291            if self.const_diag_seen.borrow().contains(&key) {
1292                return;
1293            }
1294            self.const_diag_seen.borrow_mut().insert(key);
1295            let d = Diag::error(message, String::new(), Some(code));
1296            match &*self.const_diag_sink.borrow() {
1297                Some(sink) => sink.borrow_mut().push(d),
1298                None => self.report(d),
1299            }
1300        };
1301        match ce(&name) {
1302            Ok(Value::Int(i)) => Value::Int(i),
1303            Ok(Value::Float(f)) => Value::Float(f),
1304            Ok(Value::Undef) | Ok(Value::Null) => v.clone(),
1305            Ok(_) => {
1306                diag(
1307                    "E4021",
1308                    format!("constant {name} is not numeric in a constant position"),
1309                );
1310                v.clone()
1311            }
1312            Err(Fail::Eval(e)) => {
1313                let code = if e.msg.contains("zero") {
1314                    "E5001"
1315                } else if e.msg.contains("NaN") || e.msg.contains("Infinity") {
1316                    "E5002"
1317                } else {
1318                    "E5001"
1319                };
1320                diag(code, format!("evaluating constant {name}: {}", e.msg));
1321                v.clone()
1322            }
1323            Err(_) => v.clone(),
1324        }
1325    }
1326
1327    // ---- unit / dimension name spaces ----
1328    /// Resolve a dimension by name to its base dimensions.
1329    pub fn resolve_dim(&self, name: &str, visiting: &mut Vec<String>) -> Result<DimVec, String> {
1330        if let Some(v) = self.dim_memo.borrow().get(name) {
1331            return Ok(v.clone());
1332        }
1333        if visiting.iter().any(|v| v == name) {
1334            return Err(format!("circular dimension {name}"));
1335        }
1336        let decl = self
1337            .dim_decls
1338            .borrow()
1339            .get(name)
1340            .cloned()
1341            .ok_or_else(|| format!("unknown dimension {name}"))?;
1342        let mut vec = DimVec::new();
1343        match decl {
1344            None => {
1345                vec.insert(name.to_string(), 1);
1346            }
1347            Some(terms) => {
1348                visiting.push(name.to_string());
1349                for (tn, te) in terms {
1350                    let sub = self.resolve_dim(&tn, visiting)?;
1351                    for (n, e) in sub {
1352                        *vec.entry(n).or_insert(0) += e * te;
1353                    }
1354                }
1355                visiting.pop();
1356            }
1357        }
1358        self.dim_memo
1359            .borrow_mut()
1360            .insert(name.to_string(), vec.clone());
1361        Ok(vec)
1362    }
1363    /// A unit's dimension key and factor against the base unit.
1364    pub fn unit_info(&self, sym: &str) -> Result<(String, f64), String> {
1365        self.unit_info_v(sym, &mut vec![])
1366    }
1367    /// §3.16 unit/dimension-space findings for the checker: the load-time
1368    /// redeclarations plus unresolvable units and duplicate base units
1369    pub fn finalize_unit_space(&self) -> Vec<Diag> {
1370        let mut out = self.space_diags.borrow().clone();
1371        let mut base_seen: HashMap<String, String> = HashMap::new();
1372        let syms = self.unit_order.borrow().clone();
1373        for sym in syms {
1374            let has_dim = self
1375                .unit_decls
1376                .borrow()
1377                .get(&sym)
1378                .map(|u| u.dim.is_some())
1379                .unwrap_or(false);
1380            match self.unit_info(&sym) {
1381                Ok((key, _)) => {
1382                    if has_dim {
1383                        if let Some(prev) = base_seen.get(&key) {
1384                            out.push(Diag::error(
1385                                format!(
1386                                    "second base unit {sym} for dimension {key} (base is {prev})"
1387                                ),
1388                                String::new(),
1389                                Some("E4073"),
1390                            ));
1391                        } else {
1392                            base_seen.insert(key, sym.clone());
1393                        }
1394                    }
1395                }
1396                Err(msg) => {
1397                    let code = if msg.contains("unknown dimension")
1398                        || msg.contains("circular dimension")
1399                    {
1400                        "E3003"
1401                    } else {
1402                        "E4073"
1403                    };
1404                    out.push(Diag::error(msg, String::new(), Some(code)));
1405                }
1406            }
1407        }
1408        out
1409    }
1410    fn unit_info_v(&self, sym: &str, visiting: &mut Vec<String>) -> Result<(String, f64), String> {
1411        if let Some(v) = self.unit_memo.borrow().get(sym) {
1412            return Ok(v.clone());
1413        }
1414        if visiting.iter().any(|v| v == sym) {
1415            return Err(format!("circular unit {sym}"));
1416        }
1417        let (dim, factor, base) = {
1418            let m = self.unit_decls.borrow();
1419            let u = m.get(sym).ok_or_else(|| format!("unknown unit {sym}"))?;
1420            (u.dim.clone(), u.factor.clone(), u.base.clone())
1421        };
1422        let info = if let Some(d) = dim {
1423            let key = key_of_vec(&self.resolve_dim(&d, &mut vec![])?);
1424            self.base_unit_of
1425                .borrow_mut()
1426                .entry(key.clone())
1427                .or_insert_with(|| sym.to_string());
1428            (key, 1.0)
1429        } else {
1430            visiting.push(sym.to_string());
1431            let b = self.unit_info_v(base.as_deref().unwrap_or(""), visiting)?;
1432            visiting.pop();
1433            let mut f: Option<f64> = match factor.as_deref() {
1434                Some(Expr::Lit(Value::Float(x))) => Some(*x),
1435                Some(Expr::Lit(Value::Int(i))) => i.to_f64(),
1436                _ => None,
1437            };
1438            if f.is_none() {
1439                if let (Some(fx), Some(ee)) = (factor.clone(), self.expr_eval.borrow().clone()) {
1440                    f = match ee(&fx) {
1441                        Ok(Value::Float(x)) => Some(x),
1442                        Ok(Value::Int(i)) => i.to_f64(),
1443                        _ => None,
1444                    };
1445                }
1446            }
1447            let f = f.ok_or_else(|| format!("unit {sym}: factor is not a numeric constant"))?;
1448            (b.0, f * b.1)
1449        };
1450        self.unit_memo
1451            .borrow_mut()
1452            .insert(sym.to_string(), info.clone());
1453        Ok(info)
1454    }
1455
1456    // ---- type resolution ----
1457    /// Resolve a type annotation to a resolved type (§3), memoized under `name` for a named type.
1458    pub fn resolve(self: &Rc<Env>, ast: &TypeAst, name: Option<&str>) -> Result<RT, String> {
1459        Ok(match ast {
1460            TypeAst::Prim { name: n, .. } => ty(RTk::Prim(n.clone())),
1461            TypeAst::Lit { v, .. } => ty(RTk::Lit(v.clone())),
1462            TypeAst::Range { lo, hi, excl, .. } => {
1463                let lo = self.const_num(lo);
1464                let hi = self.const_num(hi);
1465                let is_f = matches!(lo, Value::Float(_)) || matches!(hi, Value::Float(_));
1466                ty(RTk::Range {
1467                    lo,
1468                    hi,
1469                    excl: *excl,
1470                    base: if is_f { "float".into() } else { "int".into() },
1471                })
1472            }
1473            TypeAst::Pattern { re: src, .. } => {
1474                let expanded = self.expand_pattern(src)?;
1475                if let Some(bad) = pattern_error(&expanded) {
1476                    return Err(format!("malformed pattern /{src}/: {bad}"));
1477                }
1478                let re = compile_pattern(&expanded)
1479                    .map_err(|e| format!("malformed pattern /{src}/: {e}"))?;
1480                ty(RTk::Pattern { src: expanded, re })
1481            }
1482            TypeAst::Map { key, val, .. } => ty(RTk::Map {
1483                key: self.resolve(key, None)?,
1484                val: self.resolve(val, None)?,
1485            }),
1486            TypeAst::Array {
1487                elem, lo, hi, excl, ..
1488            } => {
1489                let lo = lo.as_ref().map(|v| self.const_num(v));
1490                let hi0 = hi.as_ref().map(|v| self.const_num(v));
1491                let to_i = |v: &Value| match v {
1492                    Value::Int(i) => i.to_i64(),
1493                    Value::Float(f) => Some(*f as i64),
1494                    _ => None,
1495                };
1496                let lo_i = lo.as_ref().and_then(&to_i);
1497                let hi_i = hi0
1498                    .as_ref()
1499                    .and_then(to_i)
1500                    .map(|h| if *excl { h - 1 } else { h });
1501                ty(RTk::Arr {
1502                    elem: self.resolve(elem, None)?,
1503                    lo: lo_i,
1504                    hi: hi_i,
1505                })
1506            }
1507            TypeAst::Union { arms, .. } => ty(RTk::Union(
1508                arms.iter()
1509                    .map(|a| self.resolve(a, None))
1510                    .collect::<Result<_, _>>()?,
1511            )),
1512            TypeAst::Isect { arms, .. } => {
1513                let arms: Vec<RT> = arms
1514                    .iter()
1515                    .map(|a| self.resolve(a, None))
1516                    .collect::<Result<_, _>>()?;
1517                if arms.iter().all(is_rec) {
1518                    self.merge_isect(&arms, name)
1519                } else {
1520                    ty(RTk::IsectN(arms))
1521                }
1522            }
1523            TypeAst::Record { members, open, .. } => {
1524                let rt = ty(RTk::Rec(rec_type(*open)));
1525                *rt.name.borrow_mut() = name.map(|s| s.to_string());
1526                self.fill_record(&rt, members)?;
1527                rt
1528            }
1529            TypeAst::Func { params, ret, .. } => ty(RTk::Func {
1530                params: params
1531                    .iter()
1532                    .map(|p| self.resolve(p, None))
1533                    .collect::<Result<_, _>>()?,
1534                ret: self.resolve(ret, None)?,
1535            }),
1536            TypeAst::Named {
1537                name: n,
1538                args,
1539                preds,
1540                ext,
1541                ..
1542            } => {
1543                if let Some(preds) = preds {
1544                    if !preds.is_empty() {
1545                        let base = self.resolve(
1546                            &TypeAst::Named {
1547                                name: n.clone(),
1548                                args: args.clone(),
1549                                preds: None,
1550                                ext: ext.clone(),
1551                                loc: None,
1552                            },
1553                            name,
1554                        )?;
1555                        return Ok(ty(RTk::Pred {
1556                            base,
1557                            preds: preds.clone(),
1558                        }));
1559                    }
1560                }
1561                if n == "quantity" {
1562                    let dn = match args.first() {
1563                        Some(TypeAst::Named { name, .. }) | Some(TypeAst::Prim { name, .. }) => {
1564                            name.clone()
1565                        }
1566                        _ => return Err("quantity needs a dimension".into()),
1567                    };
1568                    return Ok(ty(RTk::Quantity(key_of_vec(
1569                        &self.resolve_dim(&dn, &mut vec![])?,
1570                    ))));
1571                }
1572                if n == "map" && args.len() == 2 {
1573                    return Ok(ty(RTk::Map {
1574                        key: self.resolve(&args[0], None)?,
1575                        val: self.resolve(&args[1], None)?,
1576                    }));
1577                }
1578                if n == "ref" {
1579                    return Ok(ty(RTk::Ref(self.resolve(&args[0], None)?)));
1580                }
1581                if ["int", "float", "bool", "string"].contains(&n.as_str())
1582                    && args.is_empty()
1583                    && ext.is_none()
1584                {
1585                    return Ok(ty(RTk::Prim(n.clone())));
1586                }
1587                let decl = self.type_asts.borrow().get(n).cloned();
1588                let Some(decl) = decl else {
1589                    let im = self.imports.borrow().get(n).cloned();
1590                    if let Some(im) = im {
1591                        return im.env.resolve(
1592                            &TypeAst::Named {
1593                                name: im.name.clone(),
1594                                args: args.clone(),
1595                                preds: None,
1596                                ext: ext.clone(),
1597                                loc: None,
1598                            },
1599                            name,
1600                        );
1601                    }
1602                    if let Some((ns, rest)) = n.split_once('.') {
1603                        let ex = self
1604                            .namespaces
1605                            .borrow()
1606                            .get(ns)
1607                            .and_then(|(_, exports)| exports.borrow().get(rest).cloned());
1608                        if let Some(ex) = ex {
1609                            return ex.env.resolve(
1610                                &TypeAst::Named {
1611                                    name: ex.name.clone(),
1612                                    args: args.clone(),
1613                                    preds: None,
1614                                    ext: ext.clone(),
1615                                    loc: None,
1616                                },
1617                                name,
1618                            );
1619                        }
1620                    }
1621                    return Err(format!("unknown type {n}"));
1622                };
1623                let memo = self.type_memo.borrow().get(n).cloned();
1624                let base = if !decl.params.is_empty() {
1625                    self.instantiate(n, args, &decl)?
1626                } else if let Some(b) = memo {
1627                    b
1628                } else {
1629                    match &decl.ast {
1630                        TypeAst::Record { members, open, .. } => {
1631                            let rt = ty(RTk::Rec(rec_type(*open)));
1632                            *rt.name.borrow_mut() = Some(n.clone());
1633                            *rt.tail.borrow_mut() = decl.tail.clone();
1634                            self.type_memo.borrow_mut().insert(n.clone(), rt.clone());
1635                            // a member that fails to resolve must not leave a half-filled
1636                            // record memoized (later lookups would miss its later members)
1637                            if let Err(e) = self.fill_record(&rt, members) {
1638                                self.type_memo.borrow_mut().remove(n);
1639                                return Err(e);
1640                            }
1641                            rt
1642                        }
1643                        TypeAst::Named {
1644                            name: pn,
1645                            args: pa,
1646                            preds: pp,
1647                            ext: Some(body),
1648                            ..
1649                        } => {
1650                            // an extension declaration (§3.14) is memoized before its parent
1651                            // resolves: in a recursive family — `type Base = { kids: { [string]:
1652                            // Kid } }`, `type Kid = Base { … }` — the parent's body names this
1653                            // type, and every reference must share the one final record rather
1654                            // than a snapshot of the parent's members taken mid-fill
1655                            let rt = ty(RTk::Rec(rec_type(false)));
1656                            if let RTk::Rec(r) = &rt.k {
1657                                r.filling.set(true);
1658                            }
1659                            *rt.name.borrow_mut() = Some(n.clone());
1660                            *rt.tail.borrow_mut() = decl.tail.clone();
1661                            self.type_memo.borrow_mut().insert(n.clone(), rt.clone());
1662                            let parent_ast = TypeAst::Named {
1663                                name: pn.clone(),
1664                                args: pa.clone(),
1665                                preds: pp.clone(),
1666                                ext: None,
1667                                loc: None,
1668                            };
1669                            let filled = self.resolve(&parent_ast, None).and_then(|parent| {
1670                                let extr = self.resolve(body, None)?;
1671                                self.extend_into(&rt, &parent, &extr);
1672                                Ok(())
1673                            });
1674                            if let Err(e) = filled {
1675                                self.type_memo.borrow_mut().remove(n);
1676                                return Err(e);
1677                            }
1678                            rt
1679                        }
1680                        other => {
1681                            let rt = self.resolve(other, Some(n))?;
1682                            if matches!(rt.k, RTk::Rec(_) | RTk::Union(_)) {
1683                                *rt.name.borrow_mut() = Some(n.clone());
1684                            }
1685                            if rt.tail.borrow().is_none() {
1686                                *rt.tail.borrow_mut() = decl.tail.clone();
1687                            }
1688                            self.type_memo.borrow_mut().insert(n.clone(), rt.clone());
1689                            rt
1690                        }
1691                    }
1692                };
1693                if let Some(ext) = ext {
1694                    // an inline extension in a type position: anonymous, never memoized
1695                    let extr = self.resolve(ext, None)?;
1696                    if !is_rec(&base) {
1697                        return Ok(base);
1698                    }
1699                    let merged = ty(RTk::Rec(rec_type(false)));
1700                    if let RTk::Rec(r) = &merged.k {
1701                        r.filling.set(true);
1702                    }
1703                    *merged.name.borrow_mut() = base.name.borrow().clone();
1704                    self.extend_into(&merged, &base, &extr);
1705                    return Ok(merged);
1706                }
1707                base
1708            }
1709        })
1710    }
1711
1712    // §3.14: fill `target` as `base` extended by the override body `ext` —
1713    // base members copied, overrides replacing or adding, asserts appended,
1714    // and a context declaration narrowed by the extension replacing the
1715    // inherited one (§7.3). A base still being filled (the recursive-family
1716    // case above) defers the merge until it completes; `target` stays marked
1717    // filling meanwhile, so an extension of an extension waits in turn.
1718    fn extend_into(&self, target: &RT, base: &RT, extr: &RT) {
1719        let (RTk::Rec(tr), RTk::Rec(br), RTk::Rec(er)) = (&target.k, &base.k, &extr.k) else {
1720            if let RTk::Rec(tr) = &target.k {
1721                tr.filling.set(false);
1722            }
1723            return;
1724        };
1725        if br.filling.get() {
1726            br.pending.borrow_mut().push((target.clone(), extr.clone()));
1727            return;
1728        }
1729        tr.open.set(br.open.get());
1730        if let Some(t) = base.tail.borrow().clone() {
1731            *target.tail.borrow_mut() = Some(t);
1732        }
1733        let mut members: Vec<Member> = br.members.borrow().clone();
1734        for om in er.members.borrow().iter() {
1735            if let Some(i) = members.iter().position(|m| m.name == om.name) {
1736                members[i] = om.clone();
1737            } else {
1738                members.push(om.clone());
1739            }
1740        }
1741        let mut asserts = br.asserts.borrow().clone();
1742        asserts.extend(er.asserts.borrow().iter().cloned());
1743        let mut ctx_decls: Vec<(String, RT)> = br.ctx_decls.borrow().clone();
1744        for cd in er.ctx_decls.borrow().iter() {
1745            if let Some(i) = ctx_decls.iter().position(|(v, _)| *v == cd.0) {
1746                ctx_decls[i] = cd.clone();
1747            } else {
1748                ctx_decls.push(cd.clone());
1749            }
1750        }
1751        *tr.members.borrow_mut() = members;
1752        *tr.asserts.borrow_mut() = asserts;
1753        *tr.ctx_decls.borrow_mut() = ctx_decls;
1754        self.complete_record(target);
1755    }
1756
1757    // a record's members are final: extensions that waited on it merge now
1758    fn complete_record(&self, rt: &RT) {
1759        let RTk::Rec(r) = &rt.k else { return };
1760        r.filling.set(false);
1761        let pending: Vec<(RT, RT)> = std::mem::take(&mut *r.pending.borrow_mut());
1762        for (target, extr) in pending {
1763            self.extend_into(&target, rt, &extr);
1764        }
1765    }
1766
1767    // §3.6: `${T}` inside a pattern splices another type — a string-shaped
1768    // T (pattern, string literal, union of those) as its regular language,
1769    // an integer-shaped T (int literal, int range, union) as the decimal
1770    // representations of its members
1771    fn expand_pattern(self: &Rc<Env>, re: &str) -> Result<String, String> {
1772        let hole: &Regex = &PATTERN_HOLE;
1773        let mut out = String::new();
1774        let mut last = 0;
1775        for m in hole.captures_iter(re) {
1776            let whole = m.get(0).unwrap();
1777            out.push_str(&re[last..whole.start()]);
1778            last = whole.end();
1779            let text = m.get(1).unwrap().as_str().trim().to_string();
1780            // the spliced type: a union of string literals, int literals, int
1781            // ranges, and named types — the type-expression subset that fits
1782            // inside a pattern token
1783            let arms: Vec<String> = text.split('|').map(|a| a.trim().to_string()).collect();
1784            let mut frags: Vec<String> = vec![];
1785            let str_lit: &Regex = &PATTERN_STR_LIT;
1786            let int_range: &Regex = &PATTERN_INT_RANGE;
1787            let int_lit: &Regex = &PATTERN_INT_LIT;
1788            let ident: &Regex = &PATTERN_IDENT;
1789            for arm in &arms {
1790                if str_lit.is_match(arm) {
1791                    let v = crate::parse::json_unquote(arm)?;
1792                    frags.push(self.pattern_fragment(&ty(RTk::Lit(Value::Str(v))), &text)?);
1793                    continue;
1794                }
1795                if let Some(c) = int_range.captures(arm) {
1796                    let lo = c[1].parse::<BigInt>().map_err(|e| e.to_string())?;
1797                    let hi = c[3].parse::<BigInt>().map_err(|e| e.to_string())?;
1798                    let rt = ty(RTk::Range {
1799                        lo: Value::Int(lo),
1800                        hi: Value::Int(hi),
1801                        excl: &c[2] == "<",
1802                        base: "int".into(),
1803                    });
1804                    frags.push(self.pattern_fragment(&rt, &text)?);
1805                    continue;
1806                }
1807                if int_lit.is_match(arm) {
1808                    let v = arm.parse::<BigInt>().map_err(|e| e.to_string())?;
1809                    frags.push(self.pattern_fragment(&ty(RTk::Lit(Value::Int(v))), &text)?);
1810                    continue;
1811                }
1812                if !ident.is_match(arm) {
1813                    return Err(format!(
1814                        "pattern interpolation of {text}: not a type (§3.6)"
1815                    ));
1816                }
1817                if self.pattern_visiting.borrow().iter().any(|v| v == arm) {
1818                    return Err(format!("pattern interpolation of {arm} is circular"));
1819                }
1820                self.pattern_visiting.borrow_mut().push(arm.clone());
1821                let resolved = self.resolve(
1822                    &TypeAst::Named {
1823                        name: arm.clone(),
1824                        args: vec![],
1825                        preds: None,
1826                        ext: None,
1827                        loc: None,
1828                    },
1829                    None,
1830                );
1831                self.pattern_visiting.borrow_mut().retain(|v| v != arm);
1832                let rt = match resolved {
1833                    Ok(rt) => rt,
1834                    Err(e) => {
1835                        if e.starts_with("unknown type") {
1836                            return Err(format!("pattern interpolation of {arm}: unknown type"));
1837                        }
1838                        return Err(e);
1839                    }
1840                };
1841                frags.push(self.pattern_fragment(&rt, arm)?);
1842            }
1843            if frags.len() == 1 {
1844                out.push_str(&frags[0]);
1845            } else {
1846                out.push_str(&format!("(?:{})", frags.join("|")));
1847            }
1848        }
1849        out.push_str(&re[last..]);
1850        Ok(out)
1851    }
1852    fn pattern_fragment(self: &Rc<Env>, rt: &RT, name: &str) -> Result<String, String> {
1853        let esc = |s: &str| -> String {
1854            let mut o = String::with_capacity(s.len());
1855            for c in s.chars() {
1856                if ".*+?^${}()|[]\\/".contains(c) {
1857                    o.push('\\');
1858                }
1859                o.push(c);
1860            }
1861            o
1862        };
1863        let bad = || {
1864            Err(format!("pattern interpolation of {name}: type is neither string- nor integer-shaped (§3.6)"))
1865        };
1866        match &rt.k {
1867            RTk::Pattern { src, .. } => Ok(format!("(?:{src})")),
1868            RTk::Lit(Value::Str(s)) => Ok(esc(s)),
1869            RTk::Lit(Value::Int(i)) => Ok(i.to_string()),
1870            RTk::Lit(_) => bad(),
1871            RTk::Range { lo, hi, excl, base } => {
1872                let (Value::Int(lo), Value::Int(hi)) = (lo, hi) else {
1873                    return bad();
1874                };
1875                if base != "int" {
1876                    return bad();
1877                }
1878                let hi = if *excl { hi - 1 } else { hi.clone() };
1879                if &hi - lo >= BigInt::from(65536) {
1880                    return Err(format!(
1881                        "pattern interpolation of {name}: range too large (limit 65536 values)"
1882                    ));
1883                }
1884                let mut alts: Vec<String> = vec![];
1885                let mut v = lo.clone();
1886                while v <= hi {
1887                    alts.push(v.to_string());
1888                    v += 1;
1889                }
1890                Ok(format!("(?:{})", alts.join("|")))
1891            }
1892            RTk::Union(arms) => {
1893                let parts = arms
1894                    .iter()
1895                    .map(|a| self.pattern_fragment(a, name))
1896                    .collect::<Result<Vec<_>, _>>()?;
1897                Ok(format!("(?:{})", parts.join("|")))
1898            }
1899            RTk::Pred { base, .. } => self.pattern_fragment(base, name),
1900            RTk::Prim(n) if n == "string" => Ok(".*".into()),
1901            RTk::Prim(n) if n == "int" => Ok("-?[0-9]+".into()),
1902            _ => bad(),
1903        }
1904    }
1905
1906    // §3.15 generics
1907    fn instantiate(
1908        self: &Rc<Env>,
1909        name: &str,
1910        args: &[TypeAst],
1911        decl: &Rc<TypeEntry>,
1912    ) -> Result<RT, String> {
1913        let ps = &decl.params;
1914        if args.len() != ps.len() {
1915            return Err(format!(
1916                "generic arity: {name} expects {} argument(s), got {}",
1917                ps.len(),
1918                args.len()
1919            ));
1920        }
1921        let mut types: HashMap<String, TypeAst> = HashMap::new();
1922        let mut values: HashMap<String, Value> = HashMap::new();
1923        let mut label = Vec::new();
1924        for (p, a) in ps.iter().zip(args) {
1925            if let Some(pty) = &p.ty {
1926                let v = match a {
1927                    TypeAst::Lit { v, .. } => v.clone(),
1928                    TypeAst::Named {
1929                        name: an,
1930                        args: aa,
1931                        ext: None,
1932                        preds: None,
1933                        ..
1934                    } if aa.is_empty() => {
1935                        let v = self.const_num(&Value::Str(an.clone()));
1936                        if matches!(v, Value::Str(_)) {
1937                            return Err(format!(
1938                                "non-constant value argument {an} for {} of {name}",
1939                                p.name
1940                            ));
1941                        }
1942                        v
1943                    }
1944                    _ => {
1945                        return Err(format!(
1946                            "generic arity: parameter {} of {name} takes a constant value",
1947                            p.name
1948                        ))
1949                    }
1950                };
1951                let bound = self.resolve(&subst_type(pty, &types, &values), None)?;
1952                if !crate::subsume::subsumes(self, &ty(RTk::Lit(v.clone())), &bound) {
1953                    return Err(format!(
1954                        "value argument {v:?} outside parameter {}'s type in {name}",
1955                        p.name
1956                    ));
1957                }
1958                label.push(format!("{v:?}"));
1959                values.insert(p.name.clone(), v);
1960            } else {
1961                label.push(match a {
1962                    TypeAst::Named { name, .. } | TypeAst::Prim { name, .. } => name.clone(),
1963                    _ => "type".into(),
1964                });
1965                types.insert(p.name.clone(), a.clone());
1966            }
1967        }
1968        let key = format!(
1969            "{name}<{}>",
1970            args.iter().map(type_key).collect::<Vec<_>>().join(",")
1971        );
1972        if let Some(rt) = self.type_memo.borrow().get(&key).cloned() {
1973            return Ok(rt);
1974        }
1975        let shown = format!("{name}<{}>", label.join(", "));
1976        let body = subst_type(&decl.ast, &types, &values);
1977        let rt = match &body {
1978            TypeAst::Record { members, open, .. } => {
1979                let rt = ty(RTk::Rec(rec_type(*open)));
1980                *rt.name.borrow_mut() = Some(shown);
1981                *rt.tail.borrow_mut() = decl.tail.clone();
1982                self.type_memo.borrow_mut().insert(key.clone(), rt.clone());
1983                if let Err(e) = self.fill_record(&rt, members) {
1984                    self.type_memo.borrow_mut().remove(&key);
1985                    return Err(e);
1986                }
1987                rt
1988            }
1989            other => {
1990                let rt = self.resolve(other, Some(&shown))?;
1991                if matches!(rt.k, RTk::Rec(_) | RTk::Union(_)) {
1992                    *rt.name.borrow_mut() = Some(shown);
1993                }
1994                if rt.tail.borrow().is_none() {
1995                    *rt.tail.borrow_mut() = decl.tail.clone();
1996                }
1997                self.type_memo.borrow_mut().insert(key, rt.clone());
1998                rt
1999            }
2000        };
2001        Ok(rt)
2002    }
2003
2004    fn fill_record(self: &Rc<Env>, rt: &RT, members: &[MemberAst]) -> Result<(), String> {
2005        let RTk::Rec(r) = &rt.k else { return Ok(()) };
2006        let origin = rt.name.borrow().clone();
2007        r.filling.set(true);
2008        for m in members {
2009            match m {
2010                MemberAst::Value {
2011                    name,
2012                    opt,
2013                    ty: t,
2014                    dflt,
2015                    ..
2016                } => r.members.borrow_mut().push(Member {
2017                    kind: if dflt.is_some() {
2018                        MKind::Dflt
2019                    } else if *opt {
2020                        MKind::Opt
2021                    } else {
2022                        MKind::Req
2023                    },
2024                    name: name.clone(),
2025                    hidden: false,
2026                    ty: Some(self.resolve(t, None)?),
2027                    conj: None,
2028                    dflt: dflt.clone(),
2029                    expr: None,
2030                    menv: Some(self.clone()),
2031                }),
2032                MemberAst::Derived {
2033                    name,
2034                    ty: t,
2035                    expr,
2036                    hidden,
2037                    ..
2038                } => r.members.borrow_mut().push(Member {
2039                    kind: MKind::Der,
2040                    name: name.clone(),
2041                    hidden: *hidden,
2042                    ty: match t {
2043                        Some(t) => Some(self.resolve(t, None)?),
2044                        None => None,
2045                    },
2046                    conj: None,
2047                    dflt: None,
2048                    expr: Some(expr.clone()),
2049                    menv: Some(self.clone()),
2050                }),
2051                MemberAst::Assert {
2052                    name, cond, tail, ..
2053                } => r.asserts.borrow_mut().push(AssertItem {
2054                    when: false,
2055                    name: name.clone(),
2056                    cond: cond.clone(),
2057                    tail: tail.clone(),
2058                    body: vec![],
2059                    origin: origin.clone(),
2060                    menv: Some(self.clone()),
2061                }),
2062                MemberAst::When { cond, body, .. } => r.asserts.borrow_mut().push(AssertItem {
2063                    when: true,
2064                    name: String::new(),
2065                    cond: cond.clone(),
2066                    tail: None,
2067                    body: body.clone(),
2068                    origin: origin.clone(),
2069                    menv: Some(self.clone()),
2070                }),
2071                MemberAst::Context {
2072                    variable, ty: t, ..
2073                } => r
2074                    .ctx_decls
2075                    .borrow_mut()
2076                    .push((variable.clone(), self.resolve(t, None)?)),
2077            }
2078        }
2079        self.complete_record(rt);
2080        Ok(())
2081    }
2082
2083    fn merge_isect(&self, arms: &[RT], name: Option<&str>) -> RT {
2084        let mut members: Vec<Member> = vec![];
2085        let mut asserts: Vec<AssertItem> = vec![];
2086        let mut open = true;
2087        for a in arms {
2088            let RTk::Rec(r) = &a.k else { continue };
2089            open = open && r.open.get();
2090            for m in r.members.borrow().iter() {
2091                if let Some(i) = members.iter().position(|x| x.name == m.name) {
2092                    let prev = members[i].clone();
2093                    let mut conj = prev
2094                        .conj
2095                        .clone()
2096                        .unwrap_or_else(|| prev.ty.iter().cloned().collect());
2097                    if let Some(t) = &m.ty {
2098                        conj.push(t.clone());
2099                    }
2100                    members[i] = Member {
2101                        conj: Some(conj),
2102                        kind: if m.kind == MKind::Req {
2103                            MKind::Req
2104                        } else {
2105                            prev.kind
2106                        },
2107                        ..prev
2108                    };
2109                } else {
2110                    members.push(m.clone());
2111                }
2112            }
2113            asserts.extend(r.asserts.borrow().iter().map(|x| AssertItem {
2114                origin: x.origin.clone().or_else(|| a.name.borrow().clone()),
2115                ..x.clone()
2116            }));
2117        }
2118        let rec = rec_type(open);
2119        *rec.members.borrow_mut() = members;
2120        *rec.asserts.borrow_mut() = asserts;
2121        let rt = ty(RTk::Rec(rec));
2122        *rt.name.borrow_mut() = name.map(|s| s.to_string());
2123        rt
2124    }
2125}
2126
2127fn type_key(t: &TypeAst) -> String {
2128    match t {
2129        TypeAst::Prim { name: n, .. } => format!("p:{n}"),
2130        TypeAst::Lit { v, .. } => format!("l:{v:?}"),
2131        TypeAst::Named { name, args, .. } => format!(
2132            "n:{name}<{}>",
2133            args.iter().map(type_key).collect::<Vec<_>>().join(",")
2134        ),
2135        TypeAst::Range { lo, hi, excl, .. } => format!("r:{lo:?}..{excl}{hi:?}"),
2136        TypeAst::Array { elem, lo, hi, .. } => format!("a:{}[{lo:?},{hi:?}]", type_key(elem)),
2137        TypeAst::Union { arms: a, .. } => {
2138            format!("u:{}", a.iter().map(type_key).collect::<Vec<_>>().join("|"))
2139        }
2140        TypeAst::Isect { arms: a, .. } => {
2141            format!("i:{}", a.iter().map(type_key).collect::<Vec<_>>().join("&"))
2142        }
2143        TypeAst::Map { key, val, .. } => format!("m:{}:{}", type_key(key), type_key(val)),
2144        TypeAst::Pattern { re: p, .. } => format!("pat:{p}"),
2145        TypeAst::Record { members, .. } => format!("rec:{}", members.len()),
2146        TypeAst::Func { params, ret, .. } => format!("f:{}->{}", params.len(), type_key(ret)),
2147    }
2148}
2149
2150// ---------------- generic substitution ----------------
2151/// Substitute a generic type's parameters (§3.15).
2152pub fn subst_type(
2153    ast: &TypeAst,
2154    types: &HashMap<String, TypeAst>,
2155    values: &HashMap<String, Value>,
2156) -> TypeAst {
2157    let t = |a: &TypeAst| subst_type(a, types, values);
2158    match ast {
2159        TypeAst::Named {
2160            name,
2161            args,
2162            preds,
2163            ext,
2164            loc,
2165        } => {
2166            let plain = args.is_empty() && ext.is_none() && preds.is_none();
2167            if plain {
2168                if let Some(x) = types.get(name) {
2169                    return x.clone();
2170                }
2171                if let Some(v) = values.get(name) {
2172                    return TypeAst::Lit {
2173                        v: v.clone(),
2174                        loc: *loc,
2175                    };
2176                }
2177            }
2178            TypeAst::Named {
2179                name: name.clone(),
2180                args: args.iter().map(t).collect(),
2181                preds: preds
2182                    .as_ref()
2183                    .map(|ps| ps.iter().map(|p| subst_expr(p, values)).collect()),
2184                ext: ext.as_ref().map(|e| Box::new(t(e))),
2185                loc: *loc,
2186            }
2187        }
2188        TypeAst::Range { lo, hi, excl, loc } => {
2189            let sub = |v: &Value| match v {
2190                Value::Str(s) if values.contains_key(s) => values[s].clone(),
2191                other => other.clone(),
2192            };
2193            TypeAst::Range {
2194                lo: sub(lo),
2195                hi: sub(hi),
2196                excl: *excl,
2197                loc: *loc,
2198            }
2199        }
2200        TypeAst::Array {
2201            elem,
2202            lo,
2203            hi,
2204            excl,
2205            loc,
2206        } => {
2207            let sub = |v: &Value| match v {
2208                Value::Str(s) if values.contains_key(s) => values[s].clone(),
2209                other => other.clone(),
2210            };
2211            TypeAst::Array {
2212                elem: Box::new(t(elem)),
2213                lo: lo.as_ref().map(sub),
2214                hi: hi.as_ref().map(sub),
2215                excl: *excl,
2216                loc: *loc,
2217            }
2218        }
2219        TypeAst::Record { members, open, loc } => TypeAst::Record {
2220            members: members
2221                .iter()
2222                .map(|m| subst_member(m, types, values))
2223                .collect(),
2224            open: *open,
2225            loc: *loc,
2226        },
2227        TypeAst::Map { key, val, loc } => TypeAst::Map {
2228            key: Box::new(t(key)),
2229            val: Box::new(t(val)),
2230            loc: *loc,
2231        },
2232        TypeAst::Union { arms: a, loc } => TypeAst::Union {
2233            arms: a.iter().map(t).collect(),
2234            loc: *loc,
2235        },
2236        TypeAst::Isect { arms: a, loc } => TypeAst::Isect {
2237            arms: a.iter().map(t).collect(),
2238            loc: *loc,
2239        },
2240        TypeAst::Func { params, ret, loc } => TypeAst::Func {
2241            params: params.iter().map(t).collect(),
2242            ret: Box::new(t(ret)),
2243            loc: *loc,
2244        },
2245        other => other.clone(),
2246    }
2247}
2248fn subst_member(
2249    m: &MemberAst,
2250    types: &HashMap<String, TypeAst>,
2251    values: &HashMap<String, Value>,
2252) -> MemberAst {
2253    let t = |a: &TypeAst| subst_type(a, types, values);
2254    match m {
2255        MemberAst::Value {
2256            name,
2257            opt,
2258            ty,
2259            dflt,
2260            annotations,
2261            loc,
2262        } => MemberAst::Value {
2263            name: name.clone(),
2264            opt: *opt,
2265            ty: t(ty),
2266            dflt: dflt.as_ref().map(|d| subst_expr(d, values)),
2267            annotations: annotations.clone(),
2268            loc: *loc,
2269        },
2270        MemberAst::Derived {
2271            name,
2272            ty,
2273            expr,
2274            hidden,
2275            annotations,
2276            loc,
2277        } => MemberAst::Derived {
2278            name: name.clone(),
2279            ty: ty.as_ref().map(t),
2280            expr: subst_expr(expr, values),
2281            hidden: *hidden,
2282            annotations: annotations.clone(),
2283            loc: *loc,
2284        },
2285        MemberAst::Context {
2286            variable,
2287            ty,
2288            annotations,
2289            loc,
2290        } => MemberAst::Context {
2291            variable: variable.clone(),
2292            ty: t(ty),
2293            annotations: annotations.clone(),
2294            loc: *loc,
2295        },
2296        MemberAst::Assert {
2297            name,
2298            cond,
2299            tail,
2300            annotations,
2301            loc,
2302        } => MemberAst::Assert {
2303            name: name.clone(),
2304            cond: subst_expr(cond, values),
2305            tail: tail.clone(),
2306            annotations: annotations.clone(),
2307            loc: *loc,
2308        },
2309        MemberAst::When {
2310            cond,
2311            body,
2312            annotations,
2313            loc,
2314        } => MemberAst::When {
2315            cond: subst_expr(cond, values),
2316            body: body
2317                .iter()
2318                .map(|b| subst_member(b, types, values))
2319                .collect(),
2320            annotations: annotations.clone(),
2321            loc: *loc,
2322        },
2323    }
2324}
2325/// Substitute values for names in an expression.
2326pub fn subst_expr(e: &Rc<Expr>, values: &HashMap<String, Value>) -> Rc<Expr> {
2327    if values.is_empty() {
2328        return e.clone();
2329    }
2330    let s = |x: &Rc<Expr>| subst_expr(x, values);
2331    let cls = |c: &ForClause| ForClause {
2332        v: c.v.clone(),
2333        iter: s(&c.iter),
2334        filters: c.filters.iter().map(s).collect(),
2335    };
2336    let out = Rc::new(match &**e {
2337        Expr::Name(n) if values.contains_key(n) => Expr::Lit(values[n].clone()),
2338        Expr::Template(parts) => Expr::Template(
2339            parts
2340                .iter()
2341                .map(|p| match p {
2342                    TPart::Expr(x) => TPart::Expr(s(x)),
2343                    other => other.clone(),
2344                })
2345                .collect(),
2346        ),
2347        Expr::Obj(es) => Expr::Obj(es.iter().map(|(k, v)| (k.clone(), s(v))).collect()),
2348        Expr::Arr(items) => Expr::Arr(items.iter().map(|(sp, v)| (*sp, s(v))).collect()),
2349        Expr::Comp { head, clauses } => Expr::Comp {
2350            head: s(head),
2351            clauses: clauses.iter().map(cls).collect(),
2352        },
2353        Expr::MapComp { key, val, clauses } => Expr::MapComp {
2354            key: s(key),
2355            val: s(val),
2356            clauses: clauses.iter().map(cls).collect(),
2357        },
2358        Expr::Bin { op, l, r } => Expr::Bin {
2359            op: op.clone(),
2360            l: s(l),
2361            r: s(r),
2362        },
2363        Expr::Un { op, x } => Expr::Un {
2364            op: op.clone(),
2365            x: s(x),
2366        },
2367        Expr::Paren(x) => Expr::Paren(s(x)),
2368        Expr::If { c, t, f } => Expr::If {
2369            c: s(c),
2370            t: s(t),
2371            f: s(f),
2372        },
2373        Expr::Lambda { params, body } => Expr::Lambda {
2374            params: params.clone(),
2375            body: s(body),
2376        },
2377        Expr::Call { fun, args } => Expr::Call {
2378            fun: s(fun),
2379            args: args.iter().map(s).collect(),
2380        },
2381        Expr::Member { x, name, safe } => Expr::Member {
2382            x: s(x),
2383            name: name.clone(),
2384            safe: *safe,
2385        },
2386        Expr::Index { x, i } => Expr::Index { x: s(x), i: s(i) },
2387        Expr::With { base, patch } => Expr::With {
2388            base: s(base),
2389            patch: s(patch),
2390        },
2391        Expr::Match { subject, arms } => Expr::Match {
2392            subject: s(subject),
2393            arms: arms
2394                .iter()
2395                .map(|a| MatchArm {
2396                    v: a.v.clone(),
2397                    ty: a.ty.clone(),
2398                    body: s(&a.body),
2399                })
2400                .collect(),
2401        },
2402        other => other.clone(),
2403    });
2404    // a substituted node keeps the source range of the node it replaces (the reference copies `loc`)
2405    if let Some(l) = expr_loc(e) {
2406        set_expr_loc(&out, l);
2407    }
2408    out
2409}
2410
2411// ---------------- helpers ----------------
2412// ---------------- patterns: the portable core (§3.6) ----------------
2413// A pattern body is validated against the specification's regular-
2414// expression core with one fixed set of messages, so every implementation
2415// reports the same text whatever engine runs the accepted patterns.
2416// Returns the reason a body is outside the core, or None when it is inside.
2417const PATTERN_PUNCT: &str = "\\/.*+?()[]{}|^$-";
2418fn pattern_escape(cs: &[char], i: &mut usize) -> Result<i64, String> {
2419    if *i + 1 >= cs.len() {
2420        return Err("trailing backslash".into());
2421    }
2422    let e = cs[*i + 1];
2423    *i += 2;
2424    if "dwsDWS".contains(e) {
2425        return Ok(-1);
2426    }
2427    match e {
2428        'n' => return Ok(10),
2429        't' => return Ok(9),
2430        'r' => return Ok(13),
2431        _ => {}
2432    }
2433    if PATTERN_PUNCT.contains(e) {
2434        return Ok(e as i64);
2435    }
2436    if e.is_ascii_digit() {
2437        return Err(format!("backreference \\{e} is not supported"));
2438    }
2439    Err(format!("unsupported escape \\{e}"))
2440}
2441/// Why a pattern body is outside the §3.6 core (E4119), when it is.
2442pub fn pattern_error(src: &str) -> Option<String> {
2443    let cs: Vec<char> = src.chars().collect();
2444    let n = cs.len();
2445    let (mut i, mut depth, mut can_repeat) = (0usize, 0i32, false);
2446    while i < n {
2447        match cs[i] {
2448            '\\' => {
2449                if let Err(r) = pattern_escape(&cs, &mut i) {
2450                    return Some(r);
2451                }
2452                can_repeat = true;
2453            }
2454            '[' => {
2455                i += 1;
2456                if i < n && cs[i] == '^' {
2457                    i += 1;
2458                }
2459                let mut items = 0;
2460                loop {
2461                    if i >= n {
2462                        return Some("unterminated character class".into());
2463                    }
2464                    if cs[i] == ']' {
2465                        i += 1;
2466                        break;
2467                    }
2468                    let lo = if cs[i] == '\\' {
2469                        match pattern_escape(&cs, &mut i) {
2470                            Ok(v) => v,
2471                            Err(r) => return Some(r),
2472                        }
2473                    } else {
2474                        let v = cs[i] as i64;
2475                        i += 1;
2476                        v
2477                    };
2478                    if i < n && cs[i] == '-' && i + 1 < n && cs[i + 1] != ']' {
2479                        i += 1;
2480                        let hi = if cs[i] == '\\' {
2481                            match pattern_escape(&cs, &mut i) {
2482                                Ok(v) => v,
2483                                Err(r) => return Some(r),
2484                            }
2485                        } else {
2486                            let v = cs[i] as i64;
2487                            i += 1;
2488                            v
2489                        };
2490                        if lo < 0 || hi < 0 || lo > hi {
2491                            return Some("invalid range in character class".into());
2492                        }
2493                    }
2494                    items += 1;
2495                }
2496                if items == 0 {
2497                    return Some("empty character class".into());
2498                }
2499                can_repeat = true;
2500            }
2501            ']' => return Some("unbalanced bracket".into()),
2502            '(' => {
2503                i += 1;
2504                if i < n && cs[i] == '?' {
2505                    if i + 1 < n && cs[i + 1] == ':' {
2506                        i += 2;
2507                    } else {
2508                        return Some("unsupported construct (?".into());
2509                    }
2510                }
2511                depth += 1;
2512                can_repeat = false;
2513            }
2514            ')' => {
2515                if depth == 0 {
2516                    return Some("unbalanced parenthesis".into());
2517                }
2518                depth -= 1;
2519                i += 1;
2520                can_repeat = true;
2521            }
2522            '|' => {
2523                i += 1;
2524                can_repeat = false;
2525            }
2526            '*' | '+' | '?' => {
2527                if !can_repeat {
2528                    return Some("nothing to repeat".into());
2529                }
2530                i += 1;
2531                can_repeat = false;
2532            }
2533            '{' => {
2534                if !can_repeat {
2535                    return Some("nothing to repeat".into());
2536                }
2537                let mut j = i + 1;
2538                let start = j;
2539                while j < n && cs[j].is_ascii_digit() {
2540                    j += 1;
2541                }
2542                if j == start {
2543                    return Some("malformed repetition".into());
2544                }
2545                let m: String = cs[start..j].iter().collect();
2546                let mut hi: Option<String> = None;
2547                if j < n && cs[j] == ',' {
2548                    j += 1;
2549                    let s2 = j;
2550                    while j < n && cs[j].is_ascii_digit() {
2551                        j += 1;
2552                    }
2553                    if j > s2 {
2554                        hi = Some(cs[s2..j].iter().collect());
2555                    }
2556                }
2557                if j >= n || cs[j] != '}' {
2558                    return Some("malformed repetition".into());
2559                }
2560                if let Some(h) = hi {
2561                    if h.parse::<BigInt>().unwrap_or_default()
2562                        < m.parse::<BigInt>().unwrap_or_default()
2563                    {
2564                        return Some("malformed repetition".into());
2565                    }
2566                }
2567                i = j + 1;
2568                can_repeat = false;
2569            }
2570            '}' => return Some("malformed repetition".into()),
2571            '^' | '$' => {
2572                i += 1;
2573                can_repeat = false;
2574            }
2575            _ => {
2576                i += 1;
2577                can_repeat = true;
2578            }
2579        }
2580    }
2581    if depth > 0 {
2582        Some("unbalanced parenthesis".into())
2583    } else {
2584        None
2585    }
2586}
2587/// Compile a pattern body to a regular expression.
2588pub fn compile_pattern(src: &str) -> Result<Regex, String> {
2589    Regex::new(&format!("^(?:{src})$")).map_err(|e| e.to_string())
2590}
2591
2592/// A canonical path's text, relative to `rel_root` (`$.…`) when given.
2593pub fn path_str(segs: &[Seg], rel_root: Option<&str>) -> String {
2594    let mut out = String::new();
2595    for (i, s) in segs.iter().enumerate() {
2596        match s {
2597            _ if i == 0 => {
2598                let n = seg_text(s);
2599                if rel_root == Some(n.as_str()) {
2600                    out.push('$');
2601                } else {
2602                    out.push_str(&n);
2603                }
2604            }
2605            Seg::Idx(k) => out.push_str(&format!("[{k}]")),
2606            Seg::Key(n) => out.push_str(&format!("[{}]", json_str(n))),
2607            Seg::Name(n) if dot_spellable(n) => {
2608                out.push('.');
2609                out.push_str(n);
2610            }
2611            Seg::Name(n) => out.push_str(&format!("[{}]", json_str(n))),
2612        }
2613    }
2614    out
2615}
2616
2617/// A path string from a document: `.name` is a member, `["…"]` a bracketed
2618/// segment (a map key, or a member the dot cannot spell — the canonical walk,
2619/// §7.5, decides which is legal where), `[n]` an index.
2620pub fn parse_path(s: &str, root_name: &str) -> R<SegPath> {
2621    let id_re = Regex::new(r"^[_A-Za-z][_A-Za-z0-9]*").unwrap();
2622    let mut segs = vec![];
2623    let mut i = if s.starts_with('$') {
2624        segs.push(Seg::Name(root_name.to_string()));
2625        1
2626    } else {
2627        let m = id_re
2628            .find(s)
2629            .ok_or(())
2630            .or_else(|_| err(format!("bad path {s}")))?;
2631        segs.push(Seg::Name(m.as_str().to_string()));
2632        m.end()
2633    };
2634    while i < s.len() {
2635        let rest = &s[i..];
2636        if let Some(r) = rest.strip_prefix('.') {
2637            let m = id_re
2638                .find(r)
2639                .ok_or(())
2640                .or_else(|_| err(format!("bad path {s}")))?;
2641            segs.push(Seg::Name(m.as_str().to_string()));
2642            i += 1 + m.end();
2643        } else if rest.starts_with('[') {
2644            let j = rest
2645                .find(']')
2646                .ok_or(())
2647                .or_else(|_| err(format!("bad path {s}")))?;
2648            let inner = &rest[1..j];
2649            if inner.starts_with('"') {
2650                segs.push(Seg::Key(
2651                    crate::parse::json_unquote(inner).unwrap_or_default(),
2652                ));
2653            } else {
2654                segs.push(Seg::Idx(inner.parse().unwrap_or(0)));
2655            }
2656            i += j + 1;
2657        } else {
2658            return err(format!("bad path {s}"));
2659        }
2660    }
2661    Ok(segs)
2662}
2663
2664/// Canonical path order (§7.2): segment-wise, indices numerically, names and
2665/// keys lexicographically, a prefix first.
2666pub fn cmp_path(a: &[Seg], b: &[Seg]) -> std::cmp::Ordering {
2667    for (x, y) in a.iter().zip(b) {
2668        match (x, y) {
2669            (Seg::Idx(i), Seg::Idx(j)) => {
2670                if i != j {
2671                    return i.cmp(j);
2672                }
2673            }
2674            _ => {
2675                let xs = seg_text(x);
2676                let ys = seg_text(y);
2677                if xs != ys {
2678                    return xs.cmp(&ys);
2679                }
2680            }
2681        }
2682    }
2683    a.len().cmp(&b.len())
2684}
2685
2686/// Structural equality of two values (§4.5).
2687pub fn value_eq(a: &Value, b: &Value) -> bool {
2688    let (pa, pb) = (a.place(), b.place());
2689    if let (Some(pa), Some(pb)) = (&pa, &pb) {
2690        if matches!(a, Value::Ref(_)) || matches!(b, Value::Ref(_)) {
2691            return cmp_path(pa, pb) == std::cmp::Ordering::Equal;
2692        }
2693    }
2694    match (a, b) {
2695        (Value::Int(x), Value::Int(y)) => x == y,
2696        (Value::Float(x), Value::Float(y)) => x == y,
2697        (Value::Str(x), Value::Str(y)) => x == y,
2698        (Value::Bool(x), Value::Bool(y)) => x == y,
2699        (Value::Null, Value::Null) => true,
2700        // two unforced slots compare equal, as in the reference (undefined === undefined)
2701        (Value::Undef, Value::Undef) => true,
2702        (Value::Q { dim: d1, value: v1 }, Value::Q { dim: d2, value: v2 }) => d1 == d2 && v1 == v2,
2703        (Value::Arr(x), Value::Arr(y)) => {
2704            let (x, y) = (x.borrow(), y.borrow());
2705            x.items.len() == y.items.len()
2706                && x.items.iter().zip(&y.items).all(|(p, q)| value_eq(p, q))
2707        }
2708        (Value::Map(x), Value::Map(y)) => {
2709            let (x, y) = (x.borrow(), y.borrow());
2710            x.entries.len() == y.entries.len()
2711                && x.entries
2712                    .iter()
2713                    .all(|(k, v)| y.get(k).map(|w| value_eq(v, w)).unwrap_or(false))
2714        }
2715        (Value::Rec(x), Value::Rec(y)) => {
2716            if Rc::ptr_eq(x, y) {
2717                return true;
2718            }
2719            let (x, y) = (x.borrow(), y.borrow());
2720            for (n, s) in &x.slots {
2721                if s.hidden {
2722                    continue; // a hidden member is not part of the value (D34)
2723                }
2724                let v1 = if s.state == SlotState::Absent {
2725                    Value::Absent
2726                } else {
2727                    s.value.clone()
2728                };
2729                let v2 = match y.slot(n) {
2730                    Some(s2) if s2.state != SlotState::Absent => s2.value.clone(),
2731                    _ => Value::Absent,
2732                };
2733                match (&v1, &v2) {
2734                    (Value::Absent, Value::Absent) => continue,
2735                    (Value::Absent, _) | (_, Value::Absent) => return false,
2736                    _ => {
2737                        if !value_eq(&v1, &v2) {
2738                            return false;
2739                        }
2740                    }
2741                }
2742            }
2743            true
2744        }
2745        _ => false,
2746    }
2747}
2748
2749// ---------------- lexical JSON (int/float by lexeme) ----------------
2750/// Read a JSON document (§10.2): objects keep their key order, integers stay
2751/// exact; trailing characters are an error.
2752pub fn read_json(src: &str) -> R<Value> {
2753    let b = src.as_bytes();
2754    let mut i = 0usize;
2755    fn ws(b: &[u8], i: &mut usize) {
2756        while *i < b.len() && matches!(b[*i], b' ' | b'\t' | b'\r' | b'\n') {
2757            *i += 1;
2758        }
2759    }
2760    fn string(src: &str, b: &[u8], i: &mut usize) -> R<String> {
2761        let mut j = *i + 1;
2762        let mut out = String::new();
2763        while j < b.len() && b[j] != b'"' {
2764            if b[j] == b'\\' {
2765                let e = b[j + 1] as char;
2766                match e {
2767                    'n' => out.push('\n'),
2768                    't' => out.push('\t'),
2769                    'r' => out.push('\r'),
2770                    'b' => out.push('\u{8}'),
2771                    'f' => out.push('\u{c}'),
2772                    'u' => {
2773                        let cp = u32::from_str_radix(&src[j + 2..j + 6], 16).unwrap_or(0xfffd);
2774                        out.push(char::from_u32(cp).unwrap_or('\u{fffd}'));
2775                        j += 4;
2776                    }
2777                    other => out.push(other),
2778                }
2779                j += 2;
2780            } else {
2781                let ch = src[j..].chars().next().unwrap();
2782                out.push(ch);
2783                j += ch.len_utf8();
2784            }
2785        }
2786        *i = j + 1;
2787        Ok(out)
2788    }
2789    fn val(src: &str, b: &[u8], i: &mut usize) -> R<Value> {
2790        ws(b, i);
2791        if *i >= b.len() {
2792            return err("bad JSON: unexpected end");
2793        }
2794        match b[*i] {
2795            b'{' => {
2796                *i += 1;
2797                let mut entries = vec![];
2798                ws(b, i);
2799                if b[*i] == b'}' {
2800                    *i += 1;
2801                    return Ok(Value::JObj(Rc::new(entries)));
2802                }
2803                loop {
2804                    ws(b, i);
2805                    let k = string(src, b, i)?;
2806                    ws(b, i);
2807                    *i += 1;
2808                    let v = val(src, b, i)?;
2809                    entries.push((k, v));
2810                    ws(b, i);
2811                    if b[*i] == b',' {
2812                        *i += 1;
2813                        continue;
2814                    }
2815                    *i += 1;
2816                    return Ok(Value::JObj(Rc::new(entries)));
2817                }
2818            }
2819            b'[' => {
2820                *i += 1;
2821                let mut items = vec![];
2822                ws(b, i);
2823                if b[*i] == b']' {
2824                    *i += 1;
2825                    return Ok(Value::JArr(Rc::new(items)));
2826                }
2827                loop {
2828                    items.push(val(src, b, i)?);
2829                    ws(b, i);
2830                    if b[*i] == b',' {
2831                        *i += 1;
2832                        continue;
2833                    }
2834                    *i += 1;
2835                    return Ok(Value::JArr(Rc::new(items)));
2836                }
2837            }
2838            b'"' => Ok(Value::Str(string(src, b, i)?)),
2839            _ => {
2840                let rest = &src[*i..];
2841                if rest.starts_with("true") {
2842                    *i += 4;
2843                    return Ok(Value::Bool(true));
2844                }
2845                if rest.starts_with("false") {
2846                    *i += 5;
2847                    return Ok(Value::Bool(false));
2848                }
2849                if rest.starts_with("null") {
2850                    *i += 4;
2851                    return Ok(Value::Null);
2852                }
2853                let re = Regex::new(r"^-?(?:0|[1-9][0-9]*)(\.[0-9]+)?([eE][-+]?[0-9]+)?").unwrap();
2854                let m = re
2855                    .captures(rest)
2856                    .ok_or(())
2857                    .or_else(|_| err(format!("bad JSON at {i}")))?;
2858                let whole = m.get(0).unwrap().as_str();
2859                *i += whole.len();
2860                if m.get(1).is_some() || m.get(2).is_some() {
2861                    Ok(Value::Float(whole.parse::<f64>().unwrap_or(0.0)))
2862                } else {
2863                    Ok(Value::Int(
2864                        whole.parse::<BigInt>().unwrap_or_else(|_| BigInt::zero()),
2865                    ))
2866                }
2867            }
2868        }
2869    }
2870    let v = val(src, b, &mut i)?;
2871    ws(b, &mut i);
2872    if i < b.len() {
2873        return err("bad JSON: trailing characters");
2874    }
2875    Ok(v)
2876}
2877
2878// ---------------- JS-compatible number printing ----------------
2879/// ECMAScript Number::toString for finite doubles (shortest round trip)
2880pub fn js_num_str(x: f64) -> String {
2881    if x == 0.0 {
2882        return "0".into();
2883    }
2884    let sci = format!("{:e}", x.abs());
2885    let (mant, exp) = sci.split_once('e').unwrap();
2886    let exp: i32 = exp.parse().unwrap();
2887    let digits: String = mant.chars().filter(|c| *c != '.').collect();
2888    let digits = digits.trim_end_matches('0');
2889    let digits = if digits.is_empty() { "0" } else { digits };
2890    let k = digits.len() as i32;
2891    let n = exp + 1;
2892    let body = if k <= n && n <= 21 {
2893        format!("{digits}{}", "0".repeat((n - k) as usize))
2894    } else if 0 < n && n <= 21 {
2895        format!("{}.{}", &digits[..n as usize], &digits[n as usize..])
2896    } else if -6 < n && n <= 0 {
2897        format!("0.{}{digits}", "0".repeat((-n) as usize))
2898    } else {
2899        let e = n - 1;
2900        let mant = if k > 1 {
2901            format!("{}.{}", &digits[..1], &digits[1..])
2902        } else {
2903            digits.to_string()
2904        };
2905        format!("{mant}e{}{}", if e > 0 { "+" } else { "-" }, e.abs())
2906    };
2907    if x < 0.0 {
2908        format!("-{body}")
2909    } else {
2910        body
2911    }
2912}
2913
2914/// A string as JSON text, escaped.
2915pub fn json_str(s: &str) -> String {
2916    let mut out = String::with_capacity(s.len() + 2);
2917    out.push('"');
2918    for c in s.chars() {
2919        match c {
2920            '"' => out.push_str("\\\""),
2921            '\\' => out.push_str("\\\\"),
2922            '\n' => out.push_str("\\n"),
2923            '\r' => out.push_str("\\r"),
2924            '\t' => out.push_str("\\t"),
2925            '\u{8}' => out.push_str("\\b"),
2926            '\u{c}' => out.push_str("\\f"),
2927            c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
2928            c => out.push(c),
2929        }
2930    }
2931    out.push('"');
2932    out
2933}