Skip to main content

graphix_package_core/
lib.rs

1#![doc(
2    html_logo_url = "https://graphix-lang.github.io/graphix/graphix-icon.svg",
3    html_favicon_url = "https://graphix-lang.github.io/graphix/graphix-icon.svg"
4)]
5use anyhow::{bail, Result};
6use arcstr::{literal, ArcStr};
7use compact_str::format_compact;
8use graphix_compiler::{
9    err, errf,
10    expr::{Expr, ExprId},
11    node::genn,
12    typ::{FnType, TVal, Type, TypeRef},
13    Apply, BindId, BuiltIn, Event, ExecCtx, LambdaId, Node, Refs, Rt, Scope,
14    TypecheckPhase, UserEvent,
15};
16use graphix_rt::GXRt;
17use immutable_chunkmap::map::Map as CMap;
18use netidx::path::Path;
19use netidx::subscriber::Value;
20use netidx_core::utils::Either;
21use netidx_value::{FromValue, ValArray};
22use poolshark::local::LPooled;
23use std::{
24    any::Any,
25    collections::{hash_map::Entry, VecDeque},
26    fmt::Debug,
27    iter,
28    marker::PhantomData,
29    time::Duration,
30};
31use tokio::time::Instant;
32use triomphe::Arc as TArc;
33
34pub(crate) mod buffer;
35pub(crate) mod math;
36pub(crate) mod opt;
37pub(crate) mod queuefn;
38
39// ── Cast context for typed deserialization ────────────────────────
40
41/// Extract the success type from a resolved `Result<T, E>` return type.
42/// Returns `None` if `resolved_typ` is absent or `T` contains free tvars.
43pub fn extract_cast_type(resolved_typ: Option<&FnType>) -> Option<Type> {
44    let ft = resolved_typ?;
45    let typ = match &ft.rtype {
46        Type::Ref (TypeRef { name, params, .. })
47            if Path::basename(&**name) == Some("Result") && params.len() == 2 =>
48        {
49            params[0].clone()
50        }
51        // Handle the expanded form [T, Error<E>] — this occurs when the
52        // Result type alias was expanded during TVar binding in contains().
53        Type::Set(elements) if elements.len() == 2 => {
54            let mut success = None;
55            for elem in elements.iter() {
56                if !matches!(elem, Type::Error(_)) {
57                    success = Some(elem.clone());
58                }
59            }
60            success?
61        }
62        _ => return None,
63    };
64    if typ.has_unbound() {
65        return None;
66    }
67    Some(typ)
68}
69
70// ── Program arguments ─────────────────────────────────────────────
71
72/// Program arguments stored in LibState. Index 0 is the script filename.
73#[derive(Default)]
74pub struct ProgramArgs(pub Vec<ArcStr>);
75
76// ── Shared macros ──────────────────────────────────────────────────
77
78/// Implement `netidx_core::pack::Pack` as a non-serializable stub.
79/// Use this for abstract wrapper types that should never be encoded/decoded.
80#[macro_export]
81macro_rules! impl_no_pack {
82    ($t:ty) => {
83        impl ::netidx_core::pack::Pack for $t {
84            fn encoded_len(&self) -> usize {
85                0
86            }
87
88            fn encode(
89                &self,
90                _buf: &mut impl ::bytes::BufMut,
91            ) -> Result<(), ::netidx_core::pack::PackError> {
92                Err(::netidx_core::pack::PackError::Application(0))
93            }
94
95            fn decode(
96                _buf: &mut impl ::bytes::Buf,
97            ) -> Result<Self, ::netidx_core::pack::PackError> {
98                Err(::netidx_core::pack::PackError::Application(0))
99            }
100        }
101    };
102}
103
104/// Generates `PartialEq`, `Eq`, `PartialOrd`, `Ord`, `Hash`, `impl_no_pack!`,
105/// and the `LazyLock<AbstractWrapper<T>>` static for an abstract value type
106/// whose identity is determined by `Arc::as_ptr(&self.inner)`.
107#[macro_export]
108macro_rules! impl_abstract_arc {
109    ($name:ident, $wrapper_vis:vis static $wrapper:ident = [$($uuid:expr),* $(,)?]) => {
110        impl PartialEq for $name {
111            fn eq(&self, other: &Self) -> bool {
112                std::sync::Arc::ptr_eq(&self.inner, &other.inner)
113            }
114        }
115        impl Eq for $name {}
116        impl PartialOrd for $name {
117            fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
118                Some(self.cmp(other))
119            }
120        }
121        impl Ord for $name {
122            fn cmp(&self, other: &Self) -> std::cmp::Ordering {
123                std::sync::Arc::as_ptr(&self.inner).addr().cmp(&std::sync::Arc::as_ptr(&other.inner).addr())
124            }
125        }
126        impl std::hash::Hash for $name {
127            fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
128                std::sync::Arc::as_ptr(&self.inner).hash(state)
129            }
130        }
131        $crate::impl_no_pack!($name);
132        $wrapper_vis static $wrapper: std::sync::LazyLock<
133            netidx_value::abstract_type::AbstractWrapper<$name>,
134        > = std::sync::LazyLock::new(|| {
135            let id = uuid::Uuid::from_bytes([$($uuid),*]);
136            netidx_value::Abstract::register::<$name>(id)
137                .expect(concat!("failed to register ", stringify!($name)))
138        });
139    };
140}
141
142#[macro_export]
143macro_rules! arity1 {
144    ($from:expr, $updates:expr) => {
145        match (&*$from, &*$updates) {
146            ([arg], [arg_up]) => (arg, arg_up),
147            (_, _) => unreachable!(),
148        }
149    };
150}
151
152#[macro_export]
153macro_rules! arity2 {
154    ($from:expr, $updates:expr) => {
155        match (&*$from, &*$updates) {
156            ([arg0, arg1], [arg0_up, arg1_up]) => ((arg0, arg1), (arg0_up, arg1_up)),
157            (_, _) => unreachable!(),
158        }
159    };
160}
161
162// ── Testing infrastructure ─────────────────────────────────────────
163
164pub mod testing;
165
166// ── Shared helpers ────────────────────────────────────────────────
167
168/// Check if a Value is a struct-shaped array: non-empty, every element is
169/// a 2-element array with a string first element, keys sorted ascending.
170pub fn is_struct(arr: &ValArray) -> bool {
171    if arr.is_empty() {
172        return false;
173    }
174    let mut prev: Option<&ArcStr> = None;
175    for v in arr.iter() {
176        match v {
177            Value::Array(pair) if pair.len() == 2 => match &pair[0] {
178                Value::String(k) => {
179                    if let Some(p) = prev {
180                        if k <= p {
181                            return false;
182                        }
183                    }
184                    prev = Some(k);
185                }
186                _ => return false,
187            },
188            _ => return false,
189        }
190    }
191    true
192}
193
194// ── Shared traits and structs ──────────────────────────────────────
195
196#[derive(Debug)]
197pub struct CachedVals(pub Box<[Option<Value>]>);
198
199impl CachedVals {
200    pub fn new<R: Rt, E: UserEvent>(from: &[Node<R, E>]) -> CachedVals {
201        CachedVals(from.into_iter().map(|_| None).collect())
202    }
203
204    pub fn clear(&mut self) {
205        for v in &mut self.0 {
206            *v = None
207        }
208    }
209
210    pub fn update<R: Rt, E: UserEvent>(
211        &mut self,
212        ctx: &mut ExecCtx<R, E>,
213        from: &mut [Node<R, E>],
214        event: &mut Event<E>,
215    ) -> bool {
216        from.into_iter().enumerate().fold(false, |res, (i, src)| {
217            match src.update(ctx, event) {
218                None => res,
219                v @ Some(_) => {
220                    self.0[i] = v;
221                    true
222                }
223            }
224        })
225    }
226
227    /// Like update, but return the indexes of the nodes that updated
228    /// instead of a consolidated bool
229    pub fn update_diff<R: Rt, E: UserEvent>(
230        &mut self,
231        up: &mut [bool],
232        ctx: &mut ExecCtx<R, E>,
233        from: &mut [Node<R, E>],
234        event: &mut Event<E>,
235    ) {
236        for (i, n) in from.iter_mut().enumerate() {
237            match n.update(ctx, event) {
238                None => (),
239                v => {
240                    self.0[i] = v;
241                    up[i] = true
242                }
243            }
244        }
245    }
246
247    pub fn flat_iter<'a>(&'a self) -> impl Iterator<Item = Option<Value>> + 'a {
248        self.0.iter().flat_map(|v| match v {
249            None => Either::Left(iter::once(None)),
250            Some(v) => Either::Right(v.clone().flatten().map(Some)),
251        })
252    }
253
254    pub fn get<T: FromValue>(&self, i: usize) -> Option<T> {
255        self.0.get(i).and_then(|v| v.as_ref()).and_then(|v| v.clone().cast_to::<T>().ok())
256    }
257}
258
259pub type ByRefChain = immutable_chunkmap::map::MapS<BindId, BindId>;
260
261pub trait EvalCached<R: Rt, E: UserEvent>:
262    Debug + Default + Send + Sync + 'static
263{
264    const NAME: &str;
265    const NEEDS_CALLSITE: bool;
266
267    fn init(
268        _ctx: &mut ExecCtx<R, E>,
269        _typ: &FnType,
270        _resolved: Option<&FnType>,
271        _scope: &Scope,
272        _from: &[Node<R, E>],
273        _top_id: ExprId,
274    ) -> Self {
275        Self::default()
276    }
277
278    fn eval(&mut self, ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value>;
279
280    fn typecheck(
281        &mut self,
282        _ctx: &mut ExecCtx<R, E>,
283        _from: &mut [Node<R, E>],
284        _phase: TypecheckPhase<'_>,
285    ) -> Result<()> {
286        Ok(())
287    }
288}
289
290#[derive(Debug)]
291pub struct CachedArgs<T> {
292    cached: CachedVals,
293    t: T,
294}
295
296impl<R: Rt, E: UserEvent, T: EvalCached<R, E>> BuiltIn<R, E> for CachedArgs<T> {
297    const NAME: &str = T::NAME;
298    const NEEDS_CALLSITE: bool = T::NEEDS_CALLSITE;
299
300    fn init<'a, 'b, 'c, 'd>(
301        ctx: &'a mut ExecCtx<R, E>,
302        typ: &'a graphix_compiler::typ::FnType,
303        resolved: Option<&'d FnType>,
304        scope: &'b Scope,
305        from: &'c [Node<R, E>],
306        top_id: ExprId,
307    ) -> Result<Box<dyn Apply<R, E>>> {
308        let t = CachedArgs::<T> {
309            cached: CachedVals::new(from),
310            t: T::init(ctx, typ, resolved, scope, from, top_id),
311        };
312        Ok(Box::new(t))
313    }
314}
315
316impl<R: Rt, E: UserEvent, T: EvalCached<R, E>> Apply<R, E> for CachedArgs<T> {
317    fn update(
318        &mut self,
319        ctx: &mut ExecCtx<R, E>,
320        from: &mut [Node<R, E>],
321        event: &mut Event<E>,
322    ) -> Option<Value> {
323        if self.cached.update(ctx, from, event) {
324            self.t.eval(ctx, &self.cached)
325        } else {
326            None
327        }
328    }
329
330    fn typecheck(
331        &mut self,
332        ctx: &mut ExecCtx<R, E>,
333        from: &mut [Node<R, E>],
334        phase: TypecheckPhase<'_>,
335    ) -> Result<()> {
336        self.t.typecheck(ctx, from, phase)
337    }
338
339    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
340        self.cached.clear()
341    }
342}
343
344pub trait EvalCachedAsync: Debug + Default + Send + Sync + 'static {
345    const NAME: &str;
346    const NEEDS_CALLSITE: bool;
347
348    type Args: Debug + Any + Send + Sync;
349
350    fn init<R: Rt, E: UserEvent>(
351        _ctx: &mut ExecCtx<R, E>,
352        _typ: &FnType,
353        _resolved: Option<&FnType>,
354        _scope: &Scope,
355        _from: &[Node<R, E>],
356        _top_id: ExprId,
357    ) -> Self {
358        Self::default()
359    }
360
361    /// map the final value with access to self and ctx
362    fn map_value<R: Rt, E: UserEvent>(
363        &mut self,
364        _ctx: &mut ExecCtx<R, E>,
365        v: Value,
366    ) -> Option<Value> {
367        Some(v)
368    }
369
370    fn typecheck<R: Rt, E: UserEvent>(
371        &mut self,
372        _ctx: &mut ExecCtx<R, E>,
373        _from: &mut [Node<R, E>],
374        _phase: TypecheckPhase<'_>,
375    ) -> Result<()> {
376        Ok(())
377    }
378
379    fn prepare_args(&mut self, cached: &CachedVals) -> Option<Self::Args>;
380    fn eval(args: Self::Args) -> impl Future<Output = Value> + Send;
381}
382
383#[derive(Debug)]
384pub struct CachedArgsAsync<T: EvalCachedAsync> {
385    cached: CachedVals,
386    id: BindId,
387    top_id: ExprId,
388    queued: VecDeque<T::Args>,
389    running: bool,
390    t: T,
391}
392
393impl<R: Rt, E: UserEvent, T: EvalCachedAsync> BuiltIn<R, E> for CachedArgsAsync<T> {
394    const NAME: &str = T::NAME;
395    const NEEDS_CALLSITE: bool = T::NEEDS_CALLSITE;
396
397    fn init<'a, 'b, 'c, 'd>(
398        ctx: &'a mut ExecCtx<R, E>,
399        typ: &'a FnType,
400        resolved: Option<&'d FnType>,
401        scope: &'b Scope,
402        from: &'c [Node<R, E>],
403        top_id: ExprId,
404    ) -> Result<Box<dyn Apply<R, E>>> {
405        let id = BindId::new();
406        ctx.rt.ref_var(id, top_id);
407        let t = CachedArgsAsync::<T> {
408            id,
409            top_id,
410            cached: CachedVals::new(from),
411            queued: VecDeque::new(),
412            running: false,
413            t: T::init(ctx, typ, resolved, scope, from, top_id),
414        };
415        Ok(Box::new(t))
416    }
417}
418
419impl<R: Rt, E: UserEvent, T: EvalCachedAsync> Apply<R, E> for CachedArgsAsync<T> {
420    fn update(
421        &mut self,
422        ctx: &mut ExecCtx<R, E>,
423        from: &mut [Node<R, E>],
424        event: &mut Event<E>,
425    ) -> Option<Value> {
426        if self.cached.update(ctx, from, event)
427            && let Some(args) = self.t.prepare_args(&self.cached)
428        {
429            self.queued.push_back(args);
430        }
431        let res = event.variables.remove(&self.id).and_then(|v| {
432            self.running = false;
433            self.t.map_value(ctx, v)
434        });
435        if !self.running
436            && let Some(args) = self.queued.pop_front()
437        {
438            self.running = true;
439            let id = self.id;
440            ctx.rt.spawn_var(async move { (id, T::eval(args).await) });
441        }
442        res
443    }
444
445    fn typecheck(
446        &mut self,
447        ctx: &mut ExecCtx<R, E>,
448        from: &mut [Node<R, E>],
449        phase: TypecheckPhase<'_>,
450    ) -> Result<()> {
451        self.t.typecheck(ctx, from, phase)
452    }
453
454    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
455        ctx.rt.unref_var(self.id, self.top_id);
456        self.queued.clear();
457        self.cached.clear();
458    }
459
460    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
461        self.delete(ctx);
462        self.running = false;
463        let id = BindId::new();
464        ctx.rt.ref_var(id, self.top_id);
465        self.id = id;
466    }
467}
468
469pub trait MapCollection: Debug + Clone + Default + Send + Sync + 'static {
470    /// return the length of the collection
471    fn len(&self) -> usize;
472
473    /// iterate the collection elements as values
474    fn iter_values(&self) -> impl Iterator<Item = Value>;
475
476    /// given a value, return Some if the value is the collection type
477    /// we are mapping.
478    fn select(v: Value) -> Option<Self>;
479
480    /// given a collection wrap it in a value
481    fn project(self) -> Value;
482
483    /// return the element type given the function type
484    fn etyp(ft: &FnType) -> Result<Type>;
485}
486
487impl MapCollection for ValArray {
488    fn iter_values(&self) -> impl Iterator<Item = Value> {
489        (**self).iter().cloned()
490    }
491
492    fn len(&self) -> usize {
493        (**self).len()
494    }
495
496    fn select(v: Value) -> Option<Self> {
497        match v {
498            Value::Array(a) => Some(a.clone()),
499            _ => None,
500        }
501    }
502
503    fn project(self) -> Value {
504        Value::Array(self)
505    }
506
507    fn etyp(ft: &FnType) -> Result<Type> {
508        match &ft.args[0].typ {
509            Type::Array(et) => Ok((**et).clone()),
510            _ => bail!("expected array"),
511        }
512    }
513}
514
515impl MapCollection for CMap<Value, Value, 32> {
516    fn iter_values(&self) -> impl Iterator<Item = Value> {
517        self.into_iter().map(|(k, v)| {
518            Value::Array(ValArray::from_iter_exact([k.clone(), v.clone()].into_iter()))
519        })
520    }
521
522    fn len(&self) -> usize {
523        CMap::len(self)
524    }
525
526    fn select(v: Value) -> Option<Self> {
527        match v {
528            Value::Map(m) => Some(m.clone()),
529            _ => None,
530        }
531    }
532
533    fn project(self) -> Value {
534        Value::Map(self)
535    }
536
537    fn etyp(ft: &FnType) -> Result<Type> {
538        match &ft.args[0].typ {
539            Type::Map { key, value } => {
540                Ok(Type::Tuple(TArc::from_iter([(**key).clone(), (**value).clone()])))
541            }
542            _ => bail!("expected Map, got {:?}", ft.args[0].typ),
543        }
544    }
545}
546
547pub trait MapFn<R: Rt, E: UserEvent>: Debug + Default + Send + Sync + 'static {
548    type Collection: MapCollection;
549
550    const NAME: &str;
551
552    /// finish will be called when every lambda instance has produced
553    /// a value for the updated array. Out contains the output of the
554    /// predicate lambda for each index i, and a is the array. out and
555    /// a are guaranteed to have the same length. out\[i\].cur is
556    /// guaranteed to be Some.
557    fn finish(&mut self, slots: &[Slot<R, E>], a: &Self::Collection) -> Option<Value>;
558}
559
560#[derive(Debug)]
561pub struct Slot<R: Rt, E: UserEvent> {
562    pub id: BindId,
563    pub pred: Node<R, E>,
564    pub cur: Option<Value>,
565}
566
567impl<R: Rt, E: UserEvent> Slot<R, E> {
568    pub fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
569        self.pred.delete(ctx);
570        ctx.cached.remove(&self.id);
571        ctx.env.unbind_variable(self.id);
572    }
573}
574
575#[derive(Debug)]
576pub struct MapQ<R: Rt, E: UserEvent, T: MapFn<R, E>> {
577    scope: Scope,
578    predid: BindId,
579    top_id: ExprId,
580    mftyp: TArc<FnType>,
581    etyp: Type,
582    slots: Vec<Slot<R, E>>,
583    cur: T::Collection,
584    t: T,
585}
586
587impl<R: Rt, E: UserEvent, T: MapFn<R, E>> BuiltIn<R, E> for MapQ<R, E, T> {
588    const NAME: &str = T::NAME;
589    const NEEDS_CALLSITE: bool = false;
590
591    fn init<'a, 'b, 'c, 'd>(
592        _ctx: &'a mut ExecCtx<R, E>,
593        typ: &'a graphix_compiler::typ::FnType,
594        resolved: Option<&'d FnType>,
595        scope: &'b Scope,
596        from: &'c [Node<R, E>],
597        top_id: ExprId,
598    ) -> Result<Box<dyn Apply<R, E>>> {
599        match from {
600            [_, _] => {
601                let typ = resolved.unwrap_or(typ);
602                Ok(Box::new(Self {
603                    scope: scope
604                        .append(&format_compact!("fn{}", LambdaId::new().inner())),
605                    predid: BindId::new(),
606                    top_id,
607                    etyp: T::Collection::etyp(typ)?,
608                    mftyp: match &typ.args[1].typ {
609                        Type::Fn(ft) => ft.clone(),
610                        t => bail!("expected a function not {t}"),
611                    },
612                    slots: vec![],
613                    cur: Default::default(),
614                    t: T::default(),
615                }))
616            }
617            _ => bail!("expected two arguments"),
618        }
619    }
620}
621
622impl<R: Rt, E: UserEvent, T: MapFn<R, E>> Apply<R, E> for MapQ<R, E, T> {
623    fn update(
624        &mut self,
625        ctx: &mut ExecCtx<R, E>,
626        from: &mut [Node<R, E>],
627        event: &mut Event<E>,
628    ) -> Option<Value> {
629        let slen = self.slots.len();
630        if let Some(v) = from[1].update(ctx, event) {
631            ctx.cached.insert(self.predid, v.clone());
632            event.variables.insert(self.predid, v);
633        }
634        let (up, resized) =
635            match from[0].update(ctx, event).and_then(|v| T::Collection::select(v)) {
636                Some(a) if a.len() == slen => (Some(a), false),
637                Some(a) if a.len() < slen => {
638                    while self.slots.len() > a.len() {
639                        if let Some(mut s) = self.slots.pop() {
640                            s.delete(ctx)
641                        }
642                    }
643                    (Some(a), true)
644                }
645                Some(a) => {
646                    while self.slots.len() < a.len() {
647                        let (id, node) = genn::bind(
648                            ctx,
649                            &self.scope.lexical,
650                            "x",
651                            self.etyp.clone(),
652                            self.top_id,
653                        );
654                        let fargs = vec![node];
655                        let fnode = genn::reference(
656                            ctx,
657                            self.predid,
658                            Type::Fn(self.mftyp.clone()),
659                            self.top_id,
660                        );
661                        let pred = genn::apply(
662                            fnode,
663                            self.scope.clone(),
664                            fargs,
665                            &self.mftyp,
666                            self.top_id,
667                        );
668                        self.slots.push(Slot { id, pred, cur: None });
669                    }
670                    (Some(a), true)
671                }
672                None => (None, false),
673            };
674        if let Some(a) = up {
675            for (s, v) in self.slots.iter().zip(a.iter_values()) {
676                ctx.cached.insert(s.id, v.clone());
677                event.variables.insert(s.id, v);
678            }
679            self.cur = a.clone();
680            if a.len() == 0 {
681                return Some(T::Collection::project(a));
682            }
683        }
684        let init = event.init;
685        let mut up = resized;
686        for (i, s) in self.slots.iter_mut().enumerate() {
687            if i == slen {
688                // new nodes were added starting here
689                event.init = true;
690                if let Entry::Vacant(e) = event.variables.entry(self.predid)
691                    && let Some(v) = ctx.cached.get(&self.predid)
692                {
693                    e.insert(v.clone());
694                }
695            }
696            if let Some(v) = s.pred.update(ctx, event) {
697                s.cur = Some(v);
698                up = true;
699            }
700        }
701        event.init = init;
702        if up && self.slots.iter().all(|s| s.cur.is_some()) {
703            self.t.finish(&mut &self.slots, &self.cur)
704        } else {
705            None
706        }
707    }
708
709    fn typecheck(
710        &mut self,
711        ctx: &mut ExecCtx<R, E>,
712        from: &mut [Node<R, E>],
713        _phase: TypecheckPhase<'_>,
714    ) -> anyhow::Result<()> {
715        let mftyp = match &from[1].typ() {
716            Type::Fn(ft) => ft.clone(),
717            t => bail!("expected a function not {t}"),
718        };
719        let (_, node) =
720            genn::bind(ctx, &self.scope.lexical, "x", self.etyp.clone(), self.top_id);
721        let fargs = vec![node];
722        let ft = mftyp.clone();
723        let fnode = genn::reference(ctx, self.predid, Type::Fn(ft.clone()), self.top_id);
724        let mut node = genn::apply(fnode, self.scope.clone(), fargs, &ft, self.top_id);
725        node.typecheck(ctx)?;
726        node.delete(ctx);
727        Ok(())
728    }
729
730    fn refs(&self, refs: &mut Refs) {
731        for s in &self.slots {
732            s.pred.refs(refs)
733        }
734    }
735
736    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
737        ctx.cached.remove(&self.predid);
738        for sl in &mut self.slots {
739            sl.delete(ctx)
740        }
741    }
742
743    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
744        self.cur = Default::default();
745        for sl in &mut self.slots {
746            sl.cur = None;
747            sl.pred.sleep(ctx);
748        }
749    }
750}
751
752pub trait FoldFn<R: Rt, E: UserEvent>: Debug + Send + Sync + 'static {
753    type Collection: MapCollection;
754
755    const NAME: &str;
756}
757
758#[derive(Debug)]
759pub struct FoldQ<R: Rt, E: UserEvent, T: FoldFn<R, E>> {
760    top_id: ExprId,
761    fid: BindId,
762    scope: Scope,
763    binds: Vec<BindId>,
764    nodes: Vec<Node<R, E>>,
765    inits: Vec<Option<Value>>,
766    initids: Vec<BindId>,
767    initid: BindId,
768    mftype: TArc<FnType>,
769    etyp: Type,
770    ityp: Type,
771    init: Option<Value>,
772    t: PhantomData<T>,
773}
774
775impl<R: Rt, E: UserEvent, T: FoldFn<R, E>> BuiltIn<R, E> for FoldQ<R, E, T> {
776    const NAME: &str = T::NAME;
777    const NEEDS_CALLSITE: bool = false;
778
779    fn init<'a, 'b, 'c, 'd>(
780        _ctx: &'a mut ExecCtx<R, E>,
781        typ: &'a FnType,
782        resolved: Option<&'d FnType>,
783        scope: &'b Scope,
784        from: &'c [Node<R, E>],
785        top_id: ExprId,
786    ) -> Result<Box<dyn Apply<R, E>>> {
787        match from {
788            [_, _, _] => {
789                let typ = resolved.unwrap_or(typ);
790                Ok(Box::new(Self {
791                    top_id,
792                    scope: scope.clone(),
793                    binds: vec![],
794                    nodes: vec![],
795                    inits: vec![],
796                    initids: vec![],
797                    initid: BindId::new(),
798                    fid: BindId::new(),
799                    etyp: T::Collection::etyp(typ)?,
800                    ityp: typ.args[1].typ.clone(),
801                    mftype: match &typ.args[2].typ {
802                        Type::Fn(ft) => ft.clone(),
803                        t => bail!("expected a function not {t}"),
804                    },
805                    init: None,
806                    t: PhantomData,
807                }))
808            }
809            _ => bail!("expected three arguments"),
810        }
811    }
812}
813
814impl<R: Rt, E: UserEvent, T: FoldFn<R, E>> Apply<R, E> for FoldQ<R, E, T> {
815    fn update(
816        &mut self,
817        ctx: &mut ExecCtx<R, E>,
818        from: &mut [Node<R, E>],
819        event: &mut Event<E>,
820    ) -> Option<Value> {
821        let init = match from[0].update(ctx, event).and_then(|v| T::Collection::select(v))
822        {
823            None => self.nodes.len(),
824            Some(a) if a.len() == self.binds.len() => {
825                for (id, v) in self.binds.iter().zip(a.iter_values()) {
826                    ctx.cached.insert(*id, v.clone());
827                    event.variables.insert(*id, v.clone());
828                }
829                self.nodes.len()
830            }
831            Some(a) => {
832                let vals = a.iter_values().collect::<LPooled<Vec<Value>>>();
833                while self.binds.len() < a.len() {
834                    self.binds.push(BindId::new());
835                    self.inits.push(None);
836                    self.initids.push(BindId::new());
837                }
838                while a.len() < self.binds.len() {
839                    if let Some(id) = self.binds.pop() {
840                        ctx.cached.remove(&id);
841                    }
842                    if let Some(id) = self.initids.pop() {
843                        ctx.cached.remove(&id);
844                    }
845                    self.inits.pop();
846                    if let Some(mut n) = self.nodes.pop() {
847                        n.delete(ctx);
848                    }
849                }
850                let init = self.nodes.len();
851                for i in 0..self.binds.len() {
852                    ctx.cached.insert(self.binds[i], vals[i].clone());
853                    event.variables.insert(self.binds[i], vals[i].clone());
854                    if i >= self.nodes.len() {
855                        let n = genn::reference(
856                            ctx,
857                            if i == 0 { self.initid } else { self.initids[i - 1] },
858                            self.ityp.clone(),
859                            self.top_id,
860                        );
861                        let x = genn::reference(
862                            ctx,
863                            self.binds[i],
864                            self.etyp.clone(),
865                            self.top_id,
866                        );
867                        let fnode = genn::reference(
868                            ctx,
869                            self.fid,
870                            Type::Fn(self.mftype.clone()),
871                            self.top_id,
872                        );
873                        let node = genn::apply(
874                            fnode,
875                            self.scope.clone(),
876                            vec![n, x],
877                            &self.mftype,
878                            self.top_id,
879                        );
880                        self.nodes.push(node);
881                    }
882                }
883                init
884            }
885        };
886        if let Some(v) = from[1].update(ctx, event) {
887            ctx.cached.insert(self.initid, v.clone());
888            event.variables.insert(self.initid, v.clone());
889            self.init = Some(v);
890        }
891        if let Some(v) = from[2].update(ctx, event) {
892            ctx.cached.insert(self.fid, v.clone());
893            event.variables.insert(self.fid, v);
894        }
895        let old_init = event.init;
896        for i in 0..self.nodes.len() {
897            if i == init {
898                event.init = true;
899                if let Some(v) = ctx.cached.get(&self.fid)
900                    && let Entry::Vacant(e) = event.variables.entry(self.fid)
901                {
902                    e.insert(v.clone());
903                }
904                if i == 0 {
905                    if let Some(v) = self.init.as_ref()
906                        && let Entry::Vacant(e) = event.variables.entry(self.initid)
907                    {
908                        e.insert(v.clone());
909                    }
910                } else {
911                    if let Some(v) = self.inits[i - 1].clone() {
912                        event.variables.insert(self.initids[i - 1], v);
913                    }
914                }
915            }
916            match self.nodes[i].update(ctx, event) {
917                Some(v) => {
918                    ctx.cached.insert(self.initids[i], v.clone());
919                    event.variables.insert(self.initids[i], v.clone());
920                    self.inits[i] = Some(v);
921                }
922                None => {
923                    ctx.cached.remove(&self.initids[i]);
924                    event.variables.remove(&self.initids[i]);
925                    self.inits[i] = None;
926                }
927            }
928        }
929        event.init = old_init;
930        self.inits.last().and_then(|v| v.clone())
931    }
932
933    fn typecheck(
934        &mut self,
935        ctx: &mut ExecCtx<R, E>,
936        _from: &mut [Node<R, E>],
937        _phase: TypecheckPhase<'_>,
938    ) -> anyhow::Result<()> {
939        let mut n = genn::reference(ctx, self.initid, self.ityp.clone(), self.top_id);
940        let x = genn::reference(ctx, BindId::new(), self.etyp.clone(), self.top_id);
941        let fnode =
942            genn::reference(ctx, self.fid, Type::Fn(self.mftype.clone()), self.top_id);
943        n = genn::apply(fnode, self.scope.clone(), vec![n, x], &self.mftype, self.top_id);
944        n.typecheck(ctx)?;
945        n.delete(ctx);
946        Ok(())
947    }
948
949    fn refs(&self, refs: &mut Refs) {
950        for n in &self.nodes {
951            n.refs(refs)
952        }
953    }
954
955    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
956        let i =
957            iter::once(&self.initid).chain(self.binds.iter()).chain(self.initids.iter());
958        for id in i {
959            ctx.cached.remove(id);
960        }
961        for n in &mut self.nodes {
962            n.delete(ctx);
963        }
964    }
965
966    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
967        self.init = None;
968        for v in &mut self.inits {
969            *v = None
970        }
971        for n in &mut self.nodes {
972            n.sleep(ctx)
973        }
974    }
975}
976
977// ── Core builtins ──────────────────────────────────────────────────
978
979#[derive(Debug)]
980struct IsErr;
981
982impl<R: Rt, E: UserEvent> BuiltIn<R, E> for IsErr {
983    const NAME: &str = "core_is_err";
984    const NEEDS_CALLSITE: bool = false;
985
986    fn init<'a, 'b, 'c, 'd>(
987        _ctx: &'a mut ExecCtx<R, E>,
988        _typ: &'a FnType,
989        _resolved: Option<&'d FnType>,
990        _scope: &'b Scope,
991        _from: &'c [Node<R, E>],
992        _top_id: ExprId,
993    ) -> Result<Box<dyn Apply<R, E>>> {
994        Ok(Box::new(IsErr))
995    }
996}
997
998impl<R: Rt, E: UserEvent> Apply<R, E> for IsErr {
999    fn update(
1000        &mut self,
1001        ctx: &mut ExecCtx<R, E>,
1002        from: &mut [Node<R, E>],
1003        event: &mut Event<E>,
1004    ) -> Option<Value> {
1005        from[0].update(ctx, event).map(|v| match v {
1006            Value::Error(_) => Value::Bool(true),
1007            _ => Value::Bool(false),
1008        })
1009    }
1010
1011    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
1012}
1013
1014#[derive(Debug)]
1015struct FilterErr;
1016
1017impl<R: Rt, E: UserEvent> BuiltIn<R, E> for FilterErr {
1018    const NAME: &str = "core_filter_err";
1019    const NEEDS_CALLSITE: bool = false;
1020
1021    fn init<'a, 'b, 'c, 'd>(
1022        _ctx: &'a mut ExecCtx<R, E>,
1023        _typ: &'a FnType,
1024        _resolved: Option<&'d FnType>,
1025        _scope: &'b Scope,
1026        _from: &'c [Node<R, E>],
1027        _top_id: ExprId,
1028    ) -> Result<Box<dyn Apply<R, E>>> {
1029        Ok(Box::new(FilterErr))
1030    }
1031}
1032
1033impl<R: Rt, E: UserEvent> Apply<R, E> for FilterErr {
1034    fn update(
1035        &mut self,
1036        ctx: &mut ExecCtx<R, E>,
1037        from: &mut [Node<R, E>],
1038        event: &mut Event<E>,
1039    ) -> Option<Value> {
1040        from[0].update(ctx, event).and_then(|v| match v {
1041            v @ Value::Error(_) => Some(v),
1042            _ => None,
1043        })
1044    }
1045
1046    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
1047}
1048
1049#[derive(Debug)]
1050struct ToError;
1051
1052impl<R: Rt, E: UserEvent> BuiltIn<R, E> for ToError {
1053    const NAME: &str = "core_error";
1054    const NEEDS_CALLSITE: bool = false;
1055
1056    fn init<'a, 'b, 'c, 'd>(
1057        _ctx: &'a mut ExecCtx<R, E>,
1058        _typ: &'a FnType,
1059        _resolved: Option<&'d FnType>,
1060        _scope: &'b Scope,
1061        _from: &'c [Node<R, E>],
1062        _top_id: ExprId,
1063    ) -> Result<Box<dyn Apply<R, E>>> {
1064        Ok(Box::new(ToError))
1065    }
1066}
1067
1068impl<R: Rt, E: UserEvent> Apply<R, E> for ToError {
1069    fn update(
1070        &mut self,
1071        ctx: &mut ExecCtx<R, E>,
1072        from: &mut [Node<R, E>],
1073        event: &mut Event<E>,
1074    ) -> Option<Value> {
1075        from[0].update(ctx, event).map(|e| Value::Error(triomphe::Arc::new(e)))
1076    }
1077
1078    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
1079}
1080
1081#[derive(Debug)]
1082struct Once {
1083    val: bool,
1084}
1085
1086impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Once {
1087    const NAME: &str = "core_once";
1088    const NEEDS_CALLSITE: bool = false;
1089
1090    fn init<'a, 'b, 'c, 'd>(
1091        _ctx: &'a mut ExecCtx<R, E>,
1092        _typ: &'a FnType,
1093        _resolved: Option<&'d FnType>,
1094        _scope: &'b Scope,
1095        _from: &'c [Node<R, E>],
1096        _top_id: ExprId,
1097    ) -> Result<Box<dyn Apply<R, E>>> {
1098        Ok(Box::new(Once { val: false }))
1099    }
1100}
1101
1102impl<R: Rt, E: UserEvent> Apply<R, E> for Once {
1103    fn update(
1104        &mut self,
1105        ctx: &mut ExecCtx<R, E>,
1106        from: &mut [Node<R, E>],
1107        event: &mut Event<E>,
1108    ) -> Option<Value> {
1109        match from {
1110            [s] => s.update(ctx, event).and_then(|v| {
1111                if self.val {
1112                    None
1113                } else {
1114                    self.val = true;
1115                    Some(v)
1116                }
1117            }),
1118            _ => None,
1119        }
1120    }
1121
1122    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
1123        self.val = false
1124    }
1125}
1126
1127#[derive(Debug)]
1128struct Take {
1129    n: Option<usize>,
1130}
1131
1132impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Take {
1133    const NAME: &str = "core_take";
1134    const NEEDS_CALLSITE: bool = false;
1135
1136    fn init<'a, 'b, 'c, 'd>(
1137        _ctx: &'a mut ExecCtx<R, E>,
1138        _typ: &'a FnType,
1139        _resolved: Option<&'d FnType>,
1140        _scope: &'b Scope,
1141        _from: &'c [Node<R, E>],
1142        _top_id: ExprId,
1143    ) -> Result<Box<dyn Apply<R, E>>> {
1144        Ok(Box::new(Take { n: None }))
1145    }
1146}
1147
1148impl<R: Rt, E: UserEvent> Apply<R, E> for Take {
1149    fn update(
1150        &mut self,
1151        ctx: &mut ExecCtx<R, E>,
1152        from: &mut [Node<R, E>],
1153        event: &mut Event<E>,
1154    ) -> Option<Value> {
1155        if let Some(n) =
1156            from[0].update(ctx, event).and_then(|v| v.cast_to::<usize>().ok())
1157        {
1158            self.n = Some(n)
1159        }
1160        match from[1].update(ctx, event) {
1161            None => None,
1162            Some(v) => match &mut self.n {
1163                None => None,
1164                Some(n) if *n > 0 => {
1165                    *n -= 1;
1166                    return Some(v);
1167                }
1168                Some(_) => None,
1169            },
1170        }
1171    }
1172
1173    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
1174        self.n = None
1175    }
1176}
1177
1178#[derive(Debug)]
1179struct Skip {
1180    n: Option<usize>,
1181}
1182
1183impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Skip {
1184    const NAME: &str = "core_skip";
1185    const NEEDS_CALLSITE: bool = false;
1186
1187    fn init<'a, 'b, 'c, 'd>(
1188        _ctx: &'a mut ExecCtx<R, E>,
1189        _typ: &'a FnType,
1190        _resolved: Option<&'d FnType>,
1191        _scope: &'b Scope,
1192        _from: &'c [Node<R, E>],
1193        _top_id: ExprId,
1194    ) -> Result<Box<dyn Apply<R, E>>> {
1195        Ok(Box::new(Skip { n: None }))
1196    }
1197}
1198
1199impl<R: Rt, E: UserEvent> Apply<R, E> for Skip {
1200    fn update(
1201        &mut self,
1202        ctx: &mut ExecCtx<R, E>,
1203        from: &mut [Node<R, E>],
1204        event: &mut Event<E>,
1205    ) -> Option<Value> {
1206        if let Some(n) =
1207            from[0].update(ctx, event).and_then(|v| v.cast_to::<usize>().ok())
1208        {
1209            self.n = Some(n)
1210        }
1211        match from[1].update(ctx, event) {
1212            None => None,
1213            Some(v) => match &mut self.n {
1214                None => Some(v),
1215                Some(n) if *n > 0 => {
1216                    *n -= 1;
1217                    None
1218                }
1219                Some(_) => Some(v),
1220            },
1221        }
1222    }
1223
1224    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
1225        self.n = None
1226    }
1227}
1228
1229#[derive(Debug, Default)]
1230struct AllEv;
1231
1232impl<R: Rt, E: UserEvent> EvalCached<R, E> for AllEv {
1233    const NAME: &str = "core_all";
1234    const NEEDS_CALLSITE: bool = false;
1235
1236    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
1237        match &*from.0 {
1238            [] => None,
1239            [hd, tl @ ..] => match hd {
1240                None => None,
1241                v @ Some(_) => {
1242                    if tl.into_iter().all(|v1| v1 == v) {
1243                        v.clone()
1244                    } else {
1245                        None
1246                    }
1247                }
1248            },
1249        }
1250    }
1251}
1252
1253type All = CachedArgs<AllEv>;
1254
1255fn add_vals(lhs: Option<Value>, rhs: Option<Value>) -> Option<Value> {
1256    match (lhs, rhs) {
1257        (None, None) | (Some(_), None) => None,
1258        (None, r @ Some(_)) => r,
1259        (Some(l), Some(r)) => Some(l + r),
1260    }
1261}
1262
1263#[derive(Debug, Default)]
1264struct SumEv;
1265
1266impl<R: Rt, E: UserEvent> EvalCached<R, E> for SumEv {
1267    const NAME: &str = "core_sum";
1268    const NEEDS_CALLSITE: bool = false;
1269
1270    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
1271        from.flat_iter().fold(None, |res, v| match res {
1272            res @ Some(Value::Error(_)) => res,
1273            res => add_vals(res, v.clone()),
1274        })
1275    }
1276}
1277
1278type Sum = CachedArgs<SumEv>;
1279
1280#[derive(Debug, Default)]
1281struct ProductEv;
1282
1283fn prod_vals(lhs: Option<Value>, rhs: Option<Value>) -> Option<Value> {
1284    match (lhs, rhs) {
1285        (None, None) | (Some(_), None) => None,
1286        (None, r @ Some(_)) => r,
1287        (Some(l), Some(r)) => Some(l * r),
1288    }
1289}
1290
1291impl<R: Rt, E: UserEvent> EvalCached<R, E> for ProductEv {
1292    const NAME: &str = "core_product";
1293    const NEEDS_CALLSITE: bool = false;
1294
1295    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
1296        from.flat_iter().fold(None, |res, v| match res {
1297            res @ Some(Value::Error(_)) => res,
1298            res => prod_vals(res, v.clone()),
1299        })
1300    }
1301}
1302
1303type Product = CachedArgs<ProductEv>;
1304
1305#[derive(Debug, Default)]
1306struct DivideEv;
1307
1308fn div_vals(lhs: Option<Value>, rhs: Option<Value>) -> Option<Value> {
1309    match (lhs, rhs) {
1310        (None, None) | (Some(_), None) => None,
1311        (None, r @ Some(_)) => r,
1312        (Some(l), Some(r)) => Some(l / r),
1313    }
1314}
1315
1316impl<R: Rt, E: UserEvent> EvalCached<R, E> for DivideEv {
1317    const NAME: &str = "core_divide";
1318    const NEEDS_CALLSITE: bool = false;
1319
1320    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
1321        from.flat_iter().fold(None, |res, v| match res {
1322            res @ Some(Value::Error(_)) => res,
1323            res => div_vals(res, v.clone()),
1324        })
1325    }
1326}
1327
1328type Divide = CachedArgs<DivideEv>;
1329
1330#[derive(Debug, Default)]
1331struct MinEv;
1332
1333impl<R: Rt, E: UserEvent> EvalCached<R, E> for MinEv {
1334    const NAME: &str = "core_min";
1335    const NEEDS_CALLSITE: bool = false;
1336
1337    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
1338        let mut res = None;
1339        for v in from.flat_iter() {
1340            match (res, v) {
1341                (None, None) | (Some(_), None) => return None,
1342                (None, Some(v)) => {
1343                    res = Some(v);
1344                }
1345                (Some(v0), Some(v)) => {
1346                    res = if v < v0 { Some(v) } else { Some(v0) };
1347                }
1348            }
1349        }
1350        res
1351    }
1352}
1353
1354type Min = CachedArgs<MinEv>;
1355
1356#[derive(Debug, Default)]
1357struct MaxEv;
1358
1359impl<R: Rt, E: UserEvent> EvalCached<R, E> for MaxEv {
1360    const NAME: &str = "core_max";
1361    const NEEDS_CALLSITE: bool = false;
1362
1363    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
1364        let mut res = None;
1365        for v in from.flat_iter() {
1366            match (res, v) {
1367                (None, None) | (Some(_), None) => return None,
1368                (None, Some(v)) => {
1369                    res = Some(v);
1370                }
1371                (Some(v0), Some(v)) => {
1372                    res = if v > v0 { Some(v) } else { Some(v0) };
1373                }
1374            }
1375        }
1376        res
1377    }
1378}
1379
1380type Max = CachedArgs<MaxEv>;
1381
1382#[derive(Debug, Default)]
1383struct AndEv;
1384
1385impl<R: Rt, E: UserEvent> EvalCached<R, E> for AndEv {
1386    const NAME: &str = "core_and";
1387    const NEEDS_CALLSITE: bool = false;
1388
1389    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
1390        let mut res = Some(Value::Bool(true));
1391        for v in from.flat_iter() {
1392            match v {
1393                None => return None,
1394                Some(Value::Bool(true)) => (),
1395                Some(_) => {
1396                    res = Some(Value::Bool(false));
1397                }
1398            }
1399        }
1400        res
1401    }
1402}
1403
1404type And = CachedArgs<AndEv>;
1405
1406#[derive(Debug, Default)]
1407struct OrEv;
1408
1409impl<R: Rt, E: UserEvent> EvalCached<R, E> for OrEv {
1410    const NAME: &str = "core_or";
1411    const NEEDS_CALLSITE: bool = false;
1412
1413    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
1414        let mut res = Some(Value::Bool(false));
1415        for v in from.flat_iter() {
1416            match v {
1417                None => return None,
1418                Some(Value::Bool(true)) => {
1419                    res = Some(Value::Bool(true));
1420                }
1421                Some(_) => (),
1422            }
1423        }
1424        res
1425    }
1426}
1427
1428type Or = CachedArgs<OrEv>;
1429
1430// ── Bitwise operations ──────────────────────────────────────────
1431
1432macro_rules! int_binop {
1433    ($from:expr, $op:tt) => {
1434        match (&$from.0[0], &$from.0[1]) {
1435            (Some(Value::U8(l)), Some(Value::U8(r))) => Some(Value::U8(l $op r)),
1436            (Some(Value::I8(l)), Some(Value::I8(r))) => Some(Value::I8(l $op r)),
1437            (Some(Value::U16(l)), Some(Value::U16(r))) => Some(Value::U16(l $op r)),
1438            (Some(Value::I16(l)), Some(Value::I16(r))) => Some(Value::I16(l $op r)),
1439            (Some(Value::U32(l)), Some(Value::U32(r))) => Some(Value::U32(l $op r)),
1440            (Some(Value::V32(l)), Some(Value::V32(r))) => Some(Value::V32(l $op r)),
1441            (Some(Value::I32(l)), Some(Value::I32(r))) => Some(Value::I32(l $op r)),
1442            (Some(Value::Z32(l)), Some(Value::Z32(r))) => Some(Value::Z32(l $op r)),
1443            (Some(Value::U64(l)), Some(Value::U64(r))) => Some(Value::U64(l $op r)),
1444            (Some(Value::V64(l)), Some(Value::V64(r))) => Some(Value::V64(l $op r)),
1445            (Some(Value::I64(l)), Some(Value::I64(r))) => Some(Value::I64(l $op r)),
1446            (Some(Value::Z64(l)), Some(Value::Z64(r))) => Some(Value::Z64(l $op r)),
1447            _ => None,
1448        }
1449    };
1450}
1451
1452macro_rules! int_shift {
1453    ($from:expr, $method:ident) => {
1454        match (&$from.0[0], &$from.0[1]) {
1455            (Some(Value::U8(l)), Some(Value::U8(r))) => {
1456                Some(Value::U8(l.$method(*r as u32)))
1457            }
1458            (Some(Value::I8(l)), Some(Value::I8(r))) => {
1459                Some(Value::I8(l.$method(*r as u32)))
1460            }
1461            (Some(Value::U16(l)), Some(Value::U16(r))) => {
1462                Some(Value::U16(l.$method(*r as u32)))
1463            }
1464            (Some(Value::I16(l)), Some(Value::I16(r))) => {
1465                Some(Value::I16(l.$method(*r as u32)))
1466            }
1467            (Some(Value::U32(l)), Some(Value::U32(r))) => {
1468                Some(Value::U32(l.$method(*r as u32)))
1469            }
1470            (Some(Value::V32(l)), Some(Value::V32(r))) => {
1471                Some(Value::V32(l.$method(*r as u32)))
1472            }
1473            (Some(Value::I32(l)), Some(Value::I32(r))) => {
1474                Some(Value::I32(l.$method(*r as u32)))
1475            }
1476            (Some(Value::Z32(l)), Some(Value::Z32(r))) => {
1477                Some(Value::Z32(l.$method(*r as u32)))
1478            }
1479            (Some(Value::U64(l)), Some(Value::U64(r))) => {
1480                Some(Value::U64(l.$method(*r as u32)))
1481            }
1482            (Some(Value::V64(l)), Some(Value::V64(r))) => {
1483                Some(Value::V64(l.$method(*r as u32)))
1484            }
1485            (Some(Value::I64(l)), Some(Value::I64(r))) => {
1486                Some(Value::I64(l.$method(*r as u32)))
1487            }
1488            (Some(Value::Z64(l)), Some(Value::Z64(r))) => {
1489                Some(Value::Z64(l.$method(*r as u32)))
1490            }
1491            _ => None,
1492        }
1493    };
1494}
1495
1496#[derive(Debug, Default)]
1497struct BitAndEv;
1498
1499impl<R: Rt, E: UserEvent> EvalCached<R, E> for BitAndEv {
1500    const NAME: &str = "core_bit_and";
1501    const NEEDS_CALLSITE: bool = false;
1502
1503    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
1504        int_binop!(from, &)
1505    }
1506}
1507
1508type BitAnd = CachedArgs<BitAndEv>;
1509
1510#[derive(Debug, Default)]
1511struct BitOrEv;
1512
1513impl<R: Rt, E: UserEvent> EvalCached<R, E> for BitOrEv {
1514    const NAME: &str = "core_bit_or";
1515    const NEEDS_CALLSITE: bool = false;
1516
1517    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
1518        int_binop!(from, |)
1519    }
1520}
1521
1522type BitOr = CachedArgs<BitOrEv>;
1523
1524#[derive(Debug, Default)]
1525struct BitXorEv;
1526
1527impl<R: Rt, E: UserEvent> EvalCached<R, E> for BitXorEv {
1528    const NAME: &str = "core_bit_xor";
1529    const NEEDS_CALLSITE: bool = false;
1530
1531    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
1532        int_binop!(from, ^)
1533    }
1534}
1535
1536type BitXor = CachedArgs<BitXorEv>;
1537
1538#[derive(Debug, Default)]
1539struct BitNotEv;
1540
1541impl<R: Rt, E: UserEvent> EvalCached<R, E> for BitNotEv {
1542    const NAME: &str = "core_bit_not";
1543    const NEEDS_CALLSITE: bool = false;
1544
1545    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
1546        match &from.0[0] {
1547            Some(Value::U8(v)) => Some(Value::U8(!v)),
1548            Some(Value::I8(v)) => Some(Value::I8(!v)),
1549            Some(Value::U16(v)) => Some(Value::U16(!v)),
1550            Some(Value::I16(v)) => Some(Value::I16(!v)),
1551            Some(Value::U32(v)) => Some(Value::U32(!v)),
1552            Some(Value::V32(v)) => Some(Value::V32(!v)),
1553            Some(Value::I32(v)) => Some(Value::I32(!v)),
1554            Some(Value::Z32(v)) => Some(Value::Z32(!v)),
1555            Some(Value::U64(v)) => Some(Value::U64(!v)),
1556            Some(Value::V64(v)) => Some(Value::V64(!v)),
1557            Some(Value::I64(v)) => Some(Value::I64(!v)),
1558            Some(Value::Z64(v)) => Some(Value::Z64(!v)),
1559            _ => None,
1560        }
1561    }
1562}
1563
1564type BitNot = CachedArgs<BitNotEv>;
1565
1566#[derive(Debug, Default)]
1567struct ShlEv;
1568
1569impl<R: Rt, E: UserEvent> EvalCached<R, E> for ShlEv {
1570    const NAME: &str = "core_shl";
1571    const NEEDS_CALLSITE: bool = false;
1572
1573    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
1574        int_shift!(from, wrapping_shl)
1575    }
1576}
1577
1578type Shl = CachedArgs<ShlEv>;
1579
1580#[derive(Debug, Default)]
1581struct ShrEv;
1582
1583impl<R: Rt, E: UserEvent> EvalCached<R, E> for ShrEv {
1584    const NAME: &str = "core_shr";
1585    const NEEDS_CALLSITE: bool = false;
1586
1587    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
1588        int_shift!(from, wrapping_shr)
1589    }
1590}
1591
1592type Shr = CachedArgs<ShrEv>;
1593
1594/// Fire-and-forget filter: when the input produces a value we feed it
1595/// into `pred`, and emit the value whenever `pred` returns `true`. If a
1596/// new input arrives while `pred` is still working on the last one, the
1597/// new input replaces the pending value — the caller should wrap this
1598/// with `queue` if they need strict pairing between inputs and verdicts.
1599#[derive(Debug)]
1600struct Filter<R: Rt, E: UserEvent> {
1601    pred: Node<R, E>,
1602    pending: Option<Value>,
1603    fid: BindId,
1604    x: BindId,
1605}
1606
1607impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Filter<R, E> {
1608    const NAME: &str = "core_filter";
1609    const NEEDS_CALLSITE: bool = false;
1610
1611    fn init<'a, 'b, 'c, 'd>(
1612        ctx: &'a mut ExecCtx<R, E>,
1613        typ: &'a graphix_compiler::typ::FnType,
1614        resolved: Option<&'d FnType>,
1615        scope: &'b Scope,
1616        from: &'c [Node<R, E>],
1617        top_id: ExprId,
1618    ) -> Result<Box<dyn Apply<R, E>>> {
1619        match from {
1620            [_, _] => {
1621                let typ = resolved.unwrap_or(typ);
1622                let (x, xn) =
1623                    genn::bind(ctx, &scope.lexical, "x", typ.args[0].typ.clone(), top_id);
1624                let fid = BindId::new();
1625                let ptyp = match &typ.args[1].typ {
1626                    Type::Fn(ft) => ft.clone(),
1627                    t => bail!("expected a function not {t}"),
1628                };
1629                let fnode = genn::reference(ctx, fid, Type::Fn(ptyp.clone()), top_id);
1630                let pred = genn::apply(fnode, scope.clone(), vec![xn], &ptyp, top_id);
1631                Ok(Box::new(Self { pred, pending: None, fid, x }))
1632            }
1633            _ => bail!("expected two arguments"),
1634        }
1635    }
1636}
1637
1638impl<R: Rt, E: UserEvent> Apply<R, E> for Filter<R, E> {
1639    fn update(
1640        &mut self,
1641        ctx: &mut ExecCtx<R, E>,
1642        from: &mut [Node<R, E>],
1643        event: &mut Event<E>,
1644    ) -> Option<Value> {
1645        if let Some(v) = from[1].update(ctx, event) {
1646            ctx.cached.insert(self.fid, v.clone());
1647            event.variables.insert(self.fid, v);
1648        }
1649        if let Some(v) = from[0].update(ctx, event) {
1650            self.pending = Some(v.clone());
1651            ctx.cached.insert(self.x, v.clone());
1652            event.variables.insert(self.x, v);
1653        }
1654        self.pred.update(ctx, event).and_then(|b| match b {
1655            Value::Bool(true) => self.pending.clone(),
1656            _ => None,
1657        })
1658    }
1659
1660    fn typecheck(
1661        &mut self,
1662        ctx: &mut ExecCtx<R, E>,
1663        _from: &mut [Node<R, E>],
1664        _phase: TypecheckPhase<'_>,
1665    ) -> anyhow::Result<()> {
1666        self.pred.typecheck(ctx)?;
1667        Ok(())
1668    }
1669
1670    fn refs(&self, refs: &mut Refs) {
1671        self.pred.refs(refs)
1672    }
1673
1674    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
1675        ctx.cached.remove(&self.fid);
1676        ctx.cached.remove(&self.x);
1677        ctx.env.unbind_variable(self.x);
1678        self.pred.delete(ctx);
1679    }
1680
1681    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
1682        self.pending = None;
1683        self.pred.sleep(ctx);
1684    }
1685}
1686
1687#[derive(Debug)]
1688struct Queue {
1689    triggered: usize,
1690    queue: VecDeque<Value>,
1691    id: BindId,
1692    top_id: ExprId,
1693}
1694
1695impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Queue {
1696    const NAME: &str = "core_queue";
1697    const NEEDS_CALLSITE: bool = false;
1698
1699    fn init<'a, 'b, 'c, 'd>(
1700        ctx: &'a mut ExecCtx<R, E>,
1701        _typ: &'a FnType,
1702        _resolved: Option<&'d FnType>,
1703        _scope: &'b Scope,
1704        from: &'c [Node<R, E>],
1705        top_id: ExprId,
1706    ) -> Result<Box<dyn Apply<R, E>>> {
1707        match from {
1708            [_, _] => {
1709                let id = BindId::new();
1710                ctx.rt.ref_var(id, top_id);
1711                Ok(Box::new(Self { triggered: 0, queue: VecDeque::new(), id, top_id }))
1712            }
1713            _ => bail!("expected two arguments"),
1714        }
1715    }
1716}
1717
1718impl<R: Rt, E: UserEvent> Apply<R, E> for Queue {
1719    fn update(
1720        &mut self,
1721        ctx: &mut ExecCtx<R, E>,
1722        from: &mut [Node<R, E>],
1723        event: &mut Event<E>,
1724    ) -> Option<Value> {
1725        if from[0].update(ctx, event).is_some() {
1726            self.triggered += 1;
1727        }
1728        if let Some(v) = from[1].update(ctx, event) {
1729            self.queue.push_back(v);
1730        }
1731        while self.triggered > 0 && self.queue.len() > 0 {
1732            self.triggered -= 1;
1733            ctx.rt.set_var(self.id, self.queue.pop_front().unwrap());
1734        }
1735        event.variables.get(&self.id).cloned()
1736    }
1737
1738    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
1739        ctx.rt.unref_var(self.id, self.top_id);
1740    }
1741
1742    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
1743        ctx.rt.unref_var(self.id, self.top_id);
1744        self.id = BindId::new();
1745        ctx.rt.ref_var(self.id, self.top_id);
1746        self.triggered = 0;
1747        self.queue.clear();
1748    }
1749}
1750
1751#[derive(Debug)]
1752struct Hold {
1753    triggered: usize,
1754    current: Option<Value>,
1755}
1756
1757impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Hold {
1758    const NAME: &str = "core_hold";
1759    const NEEDS_CALLSITE: bool = false;
1760
1761    fn init<'a, 'b, 'c, 'd>(
1762        _ctx: &'a mut ExecCtx<R, E>,
1763        _typ: &'a FnType,
1764        _resolved: Option<&'d FnType>,
1765        _scope: &'b Scope,
1766        from: &'c [Node<R, E>],
1767        _top_id: ExprId,
1768    ) -> Result<Box<dyn Apply<R, E>>> {
1769        match from {
1770            [_, _] => Ok(Box::new(Self { triggered: 0, current: None })),
1771            _ => bail!("expected two arguments"),
1772        }
1773    }
1774}
1775
1776impl<R: Rt, E: UserEvent> Apply<R, E> for Hold {
1777    fn update(
1778        &mut self,
1779        ctx: &mut ExecCtx<R, E>,
1780        from: &mut [Node<R, E>],
1781        event: &mut Event<E>,
1782    ) -> Option<Value> {
1783        if from[0].update(ctx, event).is_some() {
1784            self.triggered += 1;
1785        }
1786        if let Some(v) = from[1].update(ctx, event) {
1787            self.current = Some(v);
1788        }
1789        if self.triggered > 0
1790            && let Some(v) = self.current.take()
1791        {
1792            self.triggered -= 1;
1793            Some(v)
1794        } else {
1795            None
1796        }
1797    }
1798
1799    fn delete(&mut self, _: &mut ExecCtx<R, E>) {}
1800
1801    fn sleep(&mut self, _: &mut ExecCtx<R, E>) {
1802        self.triggered = 0;
1803        self.current = None;
1804    }
1805}
1806
1807#[derive(Debug)]
1808struct Seq {
1809    id: BindId,
1810    top_id: ExprId,
1811    args: CachedVals,
1812}
1813
1814impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Seq {
1815    const NAME: &str = "core_seq";
1816    const NEEDS_CALLSITE: bool = false;
1817
1818    fn init<'a, 'b, 'c, 'd>(
1819        ctx: &'a mut ExecCtx<R, E>,
1820        _typ: &'a FnType,
1821        _resolved: Option<&'d FnType>,
1822        _scope: &'b Scope,
1823        from: &'c [Node<R, E>],
1824        top_id: ExprId,
1825    ) -> Result<Box<dyn Apply<R, E>>> {
1826        let id = BindId::new();
1827        ctx.rt.ref_var(id, top_id);
1828        let args = CachedVals::new(from);
1829        Ok(Box::new(Self { id, top_id, args }))
1830    }
1831}
1832
1833impl<R: Rt, E: UserEvent> Apply<R, E> for Seq {
1834    fn update(
1835        &mut self,
1836        ctx: &mut ExecCtx<R, E>,
1837        from: &mut [Node<R, E>],
1838        event: &mut Event<E>,
1839    ) -> Option<Value> {
1840        if self.args.update(ctx, from, event) {
1841            match &self.args.0[..] {
1842                [Some(Value::I64(i)), Some(Value::I64(j))] if i <= j => {
1843                    for v in *i..*j {
1844                        ctx.rt.set_var(self.id, Value::I64(v));
1845                    }
1846                }
1847                _ => {
1848                    let e = literal!("SeqError");
1849                    return Some(err!(e, "invalid args i must be <= j"));
1850                }
1851            }
1852        }
1853        event.variables.get(&self.id).cloned()
1854    }
1855
1856    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
1857        ctx.rt.unref_var(self.id, self.top_id);
1858    }
1859
1860    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
1861        ctx.rt.unref_var(self.id, self.top_id);
1862        self.id = BindId::new();
1863        ctx.rt.ref_var(self.id, self.top_id);
1864    }
1865}
1866
1867#[derive(Debug)]
1868struct Throttle {
1869    wait: Duration,
1870    last: Option<Instant>,
1871    tid: Option<BindId>,
1872    top_id: ExprId,
1873    args: CachedVals,
1874}
1875
1876impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Throttle {
1877    const NAME: &str = "core_throttle";
1878    const NEEDS_CALLSITE: bool = false;
1879
1880    fn init<'a, 'b, 'c, 'd>(
1881        _ctx: &'a mut ExecCtx<R, E>,
1882        _typ: &'a FnType,
1883        _resolved: Option<&'d FnType>,
1884        _scope: &'b Scope,
1885        from: &'c [Node<R, E>],
1886        top_id: ExprId,
1887    ) -> Result<Box<dyn Apply<R, E>>> {
1888        let args = CachedVals::new(from);
1889        Ok(Box::new(Self { wait: Duration::ZERO, last: None, tid: None, top_id, args }))
1890    }
1891}
1892
1893impl<R: Rt, E: UserEvent> Apply<R, E> for Throttle {
1894    fn update(
1895        &mut self,
1896        ctx: &mut ExecCtx<R, E>,
1897        from: &mut [Node<R, E>],
1898        event: &mut Event<E>,
1899    ) -> Option<Value> {
1900        macro_rules! maybe_schedule {
1901            ($last:expr) => {{
1902                let now = Instant::now();
1903                if now - *$last >= self.wait {
1904                    *$last = now;
1905                    return self.args.0[1].clone();
1906                } else {
1907                    let id = BindId::new();
1908                    ctx.rt.ref_var(id, self.top_id);
1909                    ctx.rt.set_timer(id, self.wait - (now - *$last));
1910                    self.tid = Some(id);
1911                    return None;
1912                }
1913            }};
1914        }
1915        let mut up = [false; 2];
1916        self.args.update_diff(&mut up, ctx, from, event);
1917        if up[0]
1918            && let Some(Value::Duration(d)) = &self.args.0[0]
1919        {
1920            self.wait = **d;
1921            if let Some(id) = self.tid.take()
1922                && let Some(last) = &mut self.last
1923            {
1924                ctx.rt.unref_var(id, self.top_id);
1925                maybe_schedule!(last)
1926            }
1927        }
1928        if up[1] && self.tid.is_none() {
1929            match &mut self.last {
1930                Some(last) => maybe_schedule!(last),
1931                None => {
1932                    self.last = Some(Instant::now());
1933                    return self.args.0[1].clone();
1934                }
1935            }
1936        }
1937        if let Some(id) = self.tid
1938            && let Some(_) = event.variables.get(&id)
1939        {
1940            ctx.rt.unref_var(id, self.top_id);
1941            self.tid = None;
1942            self.last = Some(Instant::now());
1943            return self.args.0[1].clone();
1944        }
1945        None
1946    }
1947
1948    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
1949        if let Some(id) = self.tid.take() {
1950            ctx.rt.unref_var(id, self.top_id);
1951        }
1952    }
1953
1954    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
1955        self.delete(ctx);
1956        self.last = None;
1957        self.wait = Duration::ZERO;
1958        self.args.clear();
1959    }
1960}
1961
1962#[derive(Debug)]
1963struct Count {
1964    count: i64,
1965}
1966
1967impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Count {
1968    const NAME: &str = "core_count";
1969    const NEEDS_CALLSITE: bool = false;
1970
1971    fn init<'a, 'b, 'c, 'd>(
1972        _ctx: &'a mut ExecCtx<R, E>,
1973        _typ: &'a FnType,
1974        _resolved: Option<&'d FnType>,
1975        _scope: &'b Scope,
1976        _from: &'c [Node<R, E>],
1977        _top_id: ExprId,
1978    ) -> Result<Box<dyn Apply<R, E>>> {
1979        Ok(Box::new(Count { count: 0 }))
1980    }
1981}
1982
1983impl<R: Rt, E: UserEvent> Apply<R, E> for Count {
1984    fn update(
1985        &mut self,
1986        ctx: &mut ExecCtx<R, E>,
1987        from: &mut [Node<R, E>],
1988        event: &mut Event<E>,
1989    ) -> Option<Value> {
1990        if from.into_iter().fold(false, |u, n| u || n.update(ctx, event).is_some()) {
1991            self.count += 1;
1992            Some(Value::I64(self.count))
1993        } else {
1994            None
1995        }
1996    }
1997
1998    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
1999        self.count = 0
2000    }
2001}
2002
2003#[derive(Debug, Default)]
2004struct MeanEv;
2005
2006impl<R: Rt, E: UserEvent> EvalCached<R, E> for MeanEv {
2007    const NAME: &str = "core_mean";
2008    const NEEDS_CALLSITE: bool = false;
2009
2010    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
2011        static TAG: ArcStr = literal!("MeanError");
2012        let mut total = 0.;
2013        let mut samples = 0;
2014        let mut error = None;
2015        for v in from.flat_iter() {
2016            if let Some(v) = v {
2017                match v.cast_to::<f64>() {
2018                    Err(e) => error = Some(errf!(TAG, "{e:?}")),
2019                    Ok(v) => {
2020                        total += v;
2021                        samples += 1;
2022                    }
2023                }
2024            }
2025        }
2026        if let Some(e) = error {
2027            Some(e)
2028        } else if samples == 0 {
2029            Some(err!(TAG, "mean requires at least one argument"))
2030        } else {
2031            Some(Value::F64(total / samples as f64))
2032        }
2033    }
2034}
2035
2036type Mean = CachedArgs<MeanEv>;
2037
2038#[derive(Debug)]
2039struct Uniq(Option<Value>);
2040
2041impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Uniq {
2042    const NAME: &str = "core_uniq";
2043    const NEEDS_CALLSITE: bool = false;
2044
2045    fn init<'a, 'b, 'c, 'd>(
2046        _ctx: &'a mut ExecCtx<R, E>,
2047        _typ: &'a FnType,
2048        _resolved: Option<&'d FnType>,
2049        _scope: &'b Scope,
2050        _from: &'c [Node<R, E>],
2051        _top_id: ExprId,
2052    ) -> Result<Box<dyn Apply<R, E>>> {
2053        Ok(Box::new(Uniq(None)))
2054    }
2055}
2056
2057impl<R: Rt, E: UserEvent> Apply<R, E> for Uniq {
2058    fn update(
2059        &mut self,
2060        ctx: &mut ExecCtx<R, E>,
2061        from: &mut [Node<R, E>],
2062        event: &mut Event<E>,
2063    ) -> Option<Value> {
2064        from[0].update(ctx, event).and_then(|v| {
2065            if Some(&v) != self.0.as_ref() {
2066                self.0 = Some(v.clone());
2067                Some(v)
2068            } else {
2069                None
2070            }
2071        })
2072    }
2073
2074    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
2075        self.0 = None
2076    }
2077}
2078
2079#[derive(Debug)]
2080struct Never;
2081
2082impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Never {
2083    const NAME: &str = "core_never";
2084    const NEEDS_CALLSITE: bool = false;
2085
2086    fn init<'a, 'b, 'c, 'd>(
2087        _ctx: &'a mut ExecCtx<R, E>,
2088        _typ: &'a FnType,
2089        _resolved: Option<&'d FnType>,
2090        _scope: &'b Scope,
2091        _from: &'c [Node<R, E>],
2092        _top_id: ExprId,
2093    ) -> Result<Box<dyn Apply<R, E>>> {
2094        Ok(Box::new(Never))
2095    }
2096}
2097
2098impl<R: Rt, E: UserEvent> Apply<R, E> for Never {
2099    fn update(
2100        &mut self,
2101        ctx: &mut ExecCtx<R, E>,
2102        from: &mut [Node<R, E>],
2103        event: &mut Event<E>,
2104    ) -> Option<Value> {
2105        for n in from {
2106            n.update(ctx, event);
2107        }
2108        None
2109    }
2110
2111    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
2112}
2113
2114#[derive(Debug, Clone, Copy)]
2115enum Level {
2116    Trace,
2117    Debug,
2118    Info,
2119    Warn,
2120    Error,
2121}
2122
2123impl FromValue for Level {
2124    fn from_value(v: Value) -> Result<Self> {
2125        match &*v.cast_to::<ArcStr>()? {
2126            "Trace" => Ok(Self::Trace),
2127            "Debug" => Ok(Self::Debug),
2128            "Info" => Ok(Self::Info),
2129            "Warn" => Ok(Self::Warn),
2130            "Error" => Ok(Self::Error),
2131            v => bail!("invalid log level {v}"),
2132        }
2133    }
2134}
2135
2136#[derive(Debug, Clone, Copy)]
2137enum LogDest {
2138    Stdout,
2139    Stderr,
2140    Log(Level),
2141}
2142
2143impl FromValue for LogDest {
2144    fn from_value(v: Value) -> Result<Self> {
2145        match &*v.clone().cast_to::<ArcStr>()? {
2146            "Stdout" => Ok(Self::Stdout),
2147            "Stderr" => Ok(Self::Stderr),
2148            _ => Ok(Self::Log(v.cast_to()?)),
2149        }
2150    }
2151}
2152
2153#[derive(Debug)]
2154struct Dbg {
2155    spec: Expr,
2156    dest: LogDest,
2157    typ: Type,
2158}
2159
2160impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Dbg {
2161    const NAME: &str = "core_dbg";
2162    const NEEDS_CALLSITE: bool = false;
2163
2164    fn init<'a, 'b, 'c, 'd>(
2165        _ctx: &'a mut ExecCtx<R, E>,
2166        _typ: &'a graphix_compiler::typ::FnType,
2167        _resolved: Option<&'d FnType>,
2168        _scope: &'b Scope,
2169        from: &'c [Node<R, E>],
2170        _top_id: ExprId,
2171    ) -> Result<Box<dyn Apply<R, E>>> {
2172        Ok(Box::new(Dbg {
2173            spec: from[1].spec().clone(),
2174            dest: LogDest::Stderr,
2175            typ: Type::Bottom,
2176        }))
2177    }
2178}
2179
2180impl<R: Rt, E: UserEvent> Apply<R, E> for Dbg {
2181    fn update(
2182        &mut self,
2183        ctx: &mut ExecCtx<R, E>,
2184        from: &mut [Node<R, E>],
2185        event: &mut Event<E>,
2186    ) -> Option<Value> {
2187        if let Some(v) = from[0].update(ctx, event)
2188            && let Ok(d) = v.cast_to::<LogDest>()
2189        {
2190            self.dest = d;
2191        }
2192        from[1].update(ctx, event).map(|v| {
2193            let tv = TVal { env: &ctx.env, typ: &self.typ, v: &v };
2194            match self.dest {
2195                LogDest::Stderr => {
2196                    eprintln!("{} dbg({}): {}", self.spec.pos, self.spec, tv)
2197                }
2198                LogDest::Stdout => {
2199                    println!("{} dbg({}): {}", self.spec.pos, self.spec, tv)
2200                }
2201                LogDest::Log(level) => match level {
2202                    Level::Trace => {
2203                        log::trace!("{} dbg({}): {}", self.spec.pos, self.spec, tv)
2204                    }
2205                    Level::Debug => {
2206                        log::debug!("{} dbg({}): {}", self.spec.pos, self.spec, tv)
2207                    }
2208                    Level::Info => {
2209                        log::info!("{} dbg({}): {}", self.spec.pos, self.spec, tv)
2210                    }
2211                    Level::Warn => {
2212                        log::warn!("{} dbg({}): {}", self.spec.pos, self.spec, tv)
2213                    }
2214                    Level::Error => {
2215                        log::error!("{} dbg({}): {}", self.spec.pos, self.spec, tv)
2216                    }
2217                },
2218            };
2219            v
2220        })
2221    }
2222
2223    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
2224
2225    fn typecheck(
2226        &mut self,
2227        _ctx: &mut ExecCtx<R, E>,
2228        from: &mut [Node<R, E>],
2229        _phase: TypecheckPhase<'_>,
2230    ) -> Result<()> {
2231        self.typ = from[1].typ().clone();
2232        Ok(())
2233    }
2234}
2235
2236#[derive(Debug)]
2237struct Log {
2238    scope: Scope,
2239    dest: LogDest,
2240}
2241
2242impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Log {
2243    const NAME: &str = "core_log";
2244    const NEEDS_CALLSITE: bool = false;
2245
2246    fn init<'a, 'b, 'c, 'd>(
2247        _ctx: &'a mut ExecCtx<R, E>,
2248        _typ: &'a graphix_compiler::typ::FnType,
2249        _resolved: Option<&'d FnType>,
2250        scope: &'b Scope,
2251        _from: &'c [Node<R, E>],
2252        _top_id: ExprId,
2253    ) -> Result<Box<dyn Apply<R, E>>> {
2254        Ok(Box::new(Self { scope: scope.clone(), dest: LogDest::Stdout }))
2255    }
2256}
2257
2258impl<R: Rt, E: UserEvent> Apply<R, E> for Log {
2259    fn update(
2260        &mut self,
2261        ctx: &mut ExecCtx<R, E>,
2262        from: &mut [Node<R, E>],
2263        event: &mut Event<E>,
2264    ) -> Option<Value> {
2265        if let Some(v) = from[0].update(ctx, event)
2266            && let Ok(d) = v.cast_to::<LogDest>()
2267        {
2268            self.dest = d;
2269        }
2270        if let Some(v) = from[1].update(ctx, event) {
2271            let tv = TVal { env: &ctx.env, typ: from[1].typ(), v: &v };
2272            match self.dest {
2273                LogDest::Stdout => println!("{}: {}", self.scope.lexical, tv),
2274                LogDest::Stderr => eprintln!("{}: {}", self.scope.lexical, tv),
2275                LogDest::Log(lvl) => match lvl {
2276                    Level::Trace => log::trace!("{}: {}", self.scope.lexical, tv),
2277                    Level::Debug => log::debug!("{}: {}", self.scope.lexical, tv),
2278                    Level::Info => log::info!("{}: {}", self.scope.lexical, tv),
2279                    Level::Warn => log::warn!("{}: {}", self.scope.lexical, tv),
2280                    Level::Error => log::error!("{}: {}", self.scope.lexical, tv),
2281                },
2282            }
2283        }
2284        None
2285    }
2286
2287    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
2288}
2289
2290macro_rules! printfn {
2291    ($type:ident, $name:literal, $print:ident, $eprint:ident) => {
2292        #[derive(Debug)]
2293        struct $type {
2294            dest: LogDest,
2295            buf: String,
2296        }
2297
2298        impl<R: Rt, E: UserEvent> BuiltIn<R, E> for $type {
2299            const NAME: &str = $name;
2300            const NEEDS_CALLSITE: bool = false;
2301
2302            fn init<'a, 'b, 'c, 'd>(
2303                _ctx: &'a mut ExecCtx<R, E>,
2304                _typ: &'a graphix_compiler::typ::FnType,
2305                _resolved: Option<&'d FnType>,
2306                _scope: &'b Scope,
2307                _from: &'c [Node<R, E>],
2308                _top_id: ExprId,
2309            ) -> Result<Box<dyn Apply<R, E>>> {
2310                Ok(Box::new(Self { dest: LogDest::Stdout, buf: String::new() }))
2311            }
2312        }
2313
2314        impl<R: Rt, E: UserEvent> Apply<R, E> for $type {
2315            fn update(
2316                &mut self,
2317                ctx: &mut ExecCtx<R, E>,
2318                from: &mut [Node<R, E>],
2319                event: &mut Event<E>,
2320            ) -> Option<Value> {
2321                use std::fmt::Write;
2322                if let Some(v) = from[0].update(ctx, event)
2323                    && let Ok(d) = v.cast_to::<LogDest>()
2324                {
2325                    self.dest = d;
2326                }
2327                if let Some(v) = from[1].update(ctx, event) {
2328                    self.buf.clear();
2329                    match v {
2330                        Value::String(s) => write!(self.buf, "{s}"),
2331                        v => write!(
2332                            self.buf,
2333                            "{}",
2334                            TVal { env: &ctx.env, typ: &from[1].typ(), v: &v }
2335                        ),
2336                    }
2337                    .unwrap();
2338                    match self.dest {
2339                        LogDest::Stdout => $print!("{}", self.buf),
2340                        LogDest::Stderr => $eprint!("{}", self.buf),
2341                        LogDest::Log(lvl) => match lvl {
2342                            Level::Trace => log::trace!("{}", self.buf),
2343                            Level::Debug => log::debug!("{}", self.buf),
2344                            Level::Info => log::info!("{}", self.buf),
2345                            Level::Warn => log::warn!("{}", self.buf),
2346                            Level::Error => log::error!("{}", self.buf),
2347                        },
2348                    }
2349                }
2350                None
2351            }
2352
2353            fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
2354        }
2355    };
2356}
2357
2358printfn!(Print, "core_print", print, eprint);
2359printfn!(Println, "core_println", println, eprintln);
2360
2361// ── Package registration ───────────────────────────────────────────
2362
2363graphix_derive::defpackage! {
2364    builtins => [
2365        IsErr,
2366        FilterErr,
2367        ToError,
2368        Once,
2369        Take,
2370        Skip,
2371        All,
2372        Sum,
2373        Product,
2374        Divide,
2375        Min,
2376        Max,
2377        And,
2378        Or,
2379        BitAnd,
2380        BitOr,
2381        BitXor,
2382        BitNot,
2383        Shl,
2384        Shr,
2385        Filter as Filter<GXRt<X>, X::UserEvent>,
2386        Queue,
2387        queuefn::QueueFn as queuefn::QueueFn<GXRt<X>, X::UserEvent>,
2388        Hold,
2389        Seq,
2390        Throttle,
2391        Count,
2392        Mean,
2393        Uniq,
2394        Never,
2395        Dbg,
2396        Log,
2397        Print,
2398        Println,
2399        buffer::BytesToString,
2400        buffer::BytesToStringLossy,
2401        buffer::BytesFromString,
2402        buffer::BytesConcat,
2403        buffer::BytesToArray,
2404        buffer::BytesFromArray,
2405        buffer::BytesLen,
2406        buffer::BufferEncode,
2407        buffer::BufferDecode,
2408        math::MathSin,
2409        math::MathCos,
2410        math::MathTan,
2411        math::MathAsin,
2412        math::MathAcos,
2413        math::MathAtan,
2414        math::MathAtan2,
2415        math::MathSinh,
2416        math::MathCosh,
2417        math::MathTanh,
2418        math::MathAsinh,
2419        math::MathAcosh,
2420        math::MathAtanh,
2421        math::MathExp,
2422        math::MathExp2,
2423        math::MathExpM1,
2424        math::MathLn,
2425        math::MathLn1p,
2426        math::MathLog2,
2427        math::MathLog10,
2428        math::MathLog,
2429        math::MathPow,
2430        math::MathSqrt,
2431        math::MathCbrt,
2432        math::MathHypot,
2433        math::MathFloor,
2434        math::MathCeil,
2435        math::MathRound,
2436        math::MathTrunc,
2437        math::MathFract,
2438        math::MathAbs,
2439        math::MathSignum,
2440        math::MathCopysign,
2441        math::MathMin,
2442        math::MathMax,
2443        math::MathClamp,
2444        math::MathIsNan,
2445        math::MathIsFinite,
2446        math::MathIsInfinite,
2447        math::MathToDegrees,
2448        math::MathToRadians,
2449        opt::IsSome,
2450        opt::IsNone,
2451        opt::Contains,
2452        opt::OrNever,
2453        opt::OrDefault,
2454        opt::Or,
2455        opt::And,
2456        opt::Xor,
2457        opt::OkOr,
2458        opt::Zip,
2459        opt::Unzip,
2460        opt::OptMap as opt::OptMap<GXRt<X>, X::UserEvent>,
2461        opt::OptFlatMap as opt::OptFlatMap<GXRt<X>, X::UserEvent>,
2462        opt::OptFilter as opt::OptFilter<GXRt<X>, X::UserEvent>,
2463        opt::OptOrElse as opt::OptOrElse<GXRt<X>, X::UserEvent>,
2464        opt::OptOkOrElse as opt::OptOkOrElse<GXRt<X>, X::UserEvent>,
2465        opt::OptIsSomeAnd as opt::OptIsSomeAnd<GXRt<X>, X::UserEvent>,
2466        opt::OptIsNoneOr as opt::OptIsNoneOr<GXRt<X>, X::UserEvent>,
2467    ],
2468}