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