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},
13    Apply, BindId, BuiltIn, Event, ExecCtx, LambdaId, Node, Refs, Rt, Scope, UserEvent,
14};
15use graphix_rt::GXRt;
16use immutable_chunkmap::map::Map as CMap;
17use netidx::subscriber::Value;
18use netidx_core::utils::Either;
19use netidx_value::{FromValue, ValArray};
20use poolshark::local::LPooled;
21use std::{
22    any::Any,
23    collections::{hash_map::Entry, VecDeque},
24    fmt::Debug,
25    iter,
26    marker::PhantomData,
27    sync::LazyLock,
28    time::Duration,
29};
30use tokio::time::Instant;
31use triomphe::Arc as TArc;
32
33// ── Shared macros ──────────────────────────────────────────────────
34
35#[macro_export]
36macro_rules! deftype {
37    ($s:literal) => {
38        const TYP: ::std::sync::LazyLock<graphix_compiler::typ::FnType> =
39            ::std::sync::LazyLock::new(|| {
40                graphix_compiler::expr::parser::parse_fn_type($s)
41                    .expect("failed to parse fn type {s}")
42            });
43    };
44}
45
46#[macro_export]
47macro_rules! arity1 {
48    ($from:expr, $updates:expr) => {
49        match (&*$from, &*$updates) {
50            ([arg], [arg_up]) => (arg, arg_up),
51            (_, _) => unreachable!(),
52        }
53    };
54}
55
56#[macro_export]
57macro_rules! arity2 {
58    ($from:expr, $updates:expr) => {
59        match (&*$from, &*$updates) {
60            ([arg0, arg1], [arg0_up, arg1_up]) => ((arg0, arg1), (arg0_up, arg1_up)),
61            (_, _) => unreachable!(),
62        }
63    };
64}
65
66// ── Testing infrastructure ─────────────────────────────────────────
67
68pub mod testing;
69
70// ── Shared traits and structs ──────────────────────────────────────
71
72#[derive(Debug)]
73pub struct CachedVals(pub Box<[Option<Value>]>);
74
75impl CachedVals {
76    pub fn new<R: Rt, E: UserEvent>(from: &[Node<R, E>]) -> CachedVals {
77        CachedVals(from.into_iter().map(|_| None).collect())
78    }
79
80    pub fn clear(&mut self) {
81        for v in &mut self.0 {
82            *v = None
83        }
84    }
85
86    pub fn update<R: Rt, E: UserEvent>(
87        &mut self,
88        ctx: &mut ExecCtx<R, E>,
89        from: &mut [Node<R, E>],
90        event: &mut Event<E>,
91    ) -> bool {
92        from.into_iter().enumerate().fold(false, |res, (i, src)| {
93            match src.update(ctx, event) {
94                None => res,
95                v @ Some(_) => {
96                    self.0[i] = v;
97                    true
98                }
99            }
100        })
101    }
102
103    /// Like update, but return the indexes of the nodes that updated
104    /// instead of a consolidated bool
105    pub fn update_diff<R: Rt, E: UserEvent>(
106        &mut self,
107        up: &mut [bool],
108        ctx: &mut ExecCtx<R, E>,
109        from: &mut [Node<R, E>],
110        event: &mut Event<E>,
111    ) {
112        for (i, n) in from.iter_mut().enumerate() {
113            match n.update(ctx, event) {
114                None => (),
115                v => {
116                    self.0[i] = v;
117                    up[i] = true
118                }
119            }
120        }
121    }
122
123    pub fn flat_iter<'a>(&'a self) -> impl Iterator<Item = Option<Value>> + 'a {
124        self.0.iter().flat_map(|v| match v {
125            None => Either::Left(iter::once(None)),
126            Some(v) => Either::Right(v.clone().flatten().map(Some)),
127        })
128    }
129
130    pub fn get<T: FromValue>(&self, i: usize) -> Option<T> {
131        self.0.get(i).and_then(|v| v.as_ref()).and_then(|v| v.clone().cast_to::<T>().ok())
132    }
133}
134
135pub trait EvalCached: Debug + Default + Send + Sync + 'static {
136    const NAME: &str;
137    const TYP: LazyLock<FnType>;
138
139    fn eval(&mut self, from: &CachedVals) -> Option<Value>;
140}
141
142#[derive(Debug)]
143pub struct CachedArgs<T: EvalCached> {
144    cached: CachedVals,
145    t: T,
146}
147
148impl<R: Rt, E: UserEvent, T: EvalCached> BuiltIn<R, E> for CachedArgs<T> {
149    const NAME: &str = T::NAME;
150    const TYP: LazyLock<FnType> = T::TYP;
151
152    fn init<'a, 'b, 'c>(
153        _ctx: &'a mut ExecCtx<R, E>,
154        _typ: &'a graphix_compiler::typ::FnType,
155        _scope: &'b Scope,
156        from: &'c [Node<R, E>],
157        _top_id: ExprId,
158    ) -> Result<Box<dyn Apply<R, E>>> {
159        let t = CachedArgs::<T> { cached: CachedVals::new(from), t: T::default() };
160        Ok(Box::new(t))
161    }
162}
163
164impl<R: Rt, E: UserEvent, T: EvalCached> Apply<R, E> for CachedArgs<T> {
165    fn update(
166        &mut self,
167        ctx: &mut ExecCtx<R, E>,
168        from: &mut [Node<R, E>],
169        event: &mut Event<E>,
170    ) -> Option<Value> {
171        if self.cached.update(ctx, from, event) {
172            self.t.eval(&self.cached)
173        } else {
174            None
175        }
176    }
177
178    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
179        self.cached.clear()
180    }
181}
182
183pub trait EvalCachedAsync: Debug + Default + Send + Sync + 'static {
184    const NAME: &str;
185    const TYP: LazyLock<FnType>;
186    type Args: Debug + Any + Send + Sync;
187
188    fn prepare_args(&mut self, cached: &CachedVals) -> Option<Self::Args>;
189    fn eval(args: Self::Args) -> impl Future<Output = Value> + Send;
190}
191
192#[derive(Debug)]
193pub struct CachedArgsAsync<T: EvalCachedAsync> {
194    cached: CachedVals,
195    id: BindId,
196    top_id: ExprId,
197    queued: VecDeque<T::Args>,
198    running: bool,
199    t: T,
200}
201
202impl<R: Rt, E: UserEvent, T: EvalCachedAsync> BuiltIn<R, E> for CachedArgsAsync<T> {
203    const NAME: &str = T::NAME;
204    const TYP: LazyLock<FnType> = T::TYP;
205
206    fn init<'a, 'b, 'c>(
207        ctx: &'a mut ExecCtx<R, E>,
208        _typ: &'a graphix_compiler::typ::FnType,
209        _scope: &'b Scope,
210        from: &'c [Node<R, E>],
211        top_id: ExprId,
212    ) -> Result<Box<dyn Apply<R, E>>> {
213        let id = BindId::new();
214        ctx.rt.ref_var(id, top_id);
215        let t = CachedArgsAsync::<T> {
216            id,
217            top_id,
218            cached: CachedVals::new(from),
219            queued: VecDeque::new(),
220            running: false,
221            t: T::default(),
222        };
223        Ok(Box::new(t))
224    }
225}
226
227impl<R: Rt, E: UserEvent, T: EvalCachedAsync> Apply<R, E> for CachedArgsAsync<T> {
228    fn update(
229        &mut self,
230        ctx: &mut ExecCtx<R, E>,
231        from: &mut [Node<R, E>],
232        event: &mut Event<E>,
233    ) -> Option<Value> {
234        if self.cached.update(ctx, from, event)
235            && let Some(args) = self.t.prepare_args(&self.cached)
236        {
237            self.queued.push_back(args);
238        }
239        let res = event.variables.remove(&self.id).map(|v| {
240            self.running = false;
241            v
242        });
243        if !self.running
244            && let Some(args) = self.queued.pop_front()
245        {
246            self.running = true;
247            let id = self.id;
248            ctx.rt.spawn_var(async move { (id, T::eval(args).await) });
249        }
250        res
251    }
252
253    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
254        ctx.rt.unref_var(self.id, self.top_id);
255        self.queued.clear();
256        self.cached.clear();
257    }
258
259    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
260        self.delete(ctx);
261        let id = BindId::new();
262        ctx.rt.ref_var(id, self.top_id);
263    }
264}
265
266pub trait MapCollection: Debug + Clone + Default + Send + Sync + 'static {
267    /// return the length of the collection
268    fn len(&self) -> usize;
269
270    /// iterate the collection elements as values
271    fn iter_values(&self) -> impl Iterator<Item = Value>;
272
273    /// given a value, return Some if the value is the collection type
274    /// we are mapping.
275    fn select(v: Value) -> Option<Self>;
276
277    /// given a collection wrap it in a value
278    fn project(self) -> Value;
279
280    /// return the element type given the function type
281    fn etyp(ft: &FnType) -> Result<Type>;
282}
283
284impl MapCollection for ValArray {
285    fn iter_values(&self) -> impl Iterator<Item = Value> {
286        (**self).iter().cloned()
287    }
288
289    fn len(&self) -> usize {
290        (**self).len()
291    }
292
293    fn select(v: Value) -> Option<Self> {
294        match v {
295            Value::Array(a) => Some(a.clone()),
296            _ => None,
297        }
298    }
299
300    fn project(self) -> Value {
301        Value::Array(self)
302    }
303
304    fn etyp(ft: &FnType) -> Result<Type> {
305        match &ft.args[0].typ {
306            Type::Array(et) => Ok((**et).clone()),
307            _ => bail!("expected array"),
308        }
309    }
310}
311
312impl MapCollection for CMap<Value, Value, 32> {
313    fn iter_values(&self) -> impl Iterator<Item = Value> {
314        self.into_iter().map(|(k, v)| {
315            Value::Array(ValArray::from_iter_exact([k.clone(), v.clone()].into_iter()))
316        })
317    }
318
319    fn len(&self) -> usize {
320        CMap::len(self)
321    }
322
323    fn select(v: Value) -> Option<Self> {
324        match v {
325            Value::Map(m) => Some(m.clone()),
326            _ => None,
327        }
328    }
329
330    fn project(self) -> Value {
331        Value::Map(self)
332    }
333
334    fn etyp(ft: &FnType) -> Result<Type> {
335        match &ft.args[0].typ {
336            Type::Map { key, value } => {
337                Ok(Type::Tuple(TArc::from_iter([(**key).clone(), (**value).clone()])))
338            }
339            _ => bail!("expected Map, got {:?}", ft.args[0].typ),
340        }
341    }
342}
343
344pub trait MapFn<R: Rt, E: UserEvent>: Debug + Default + Send + Sync + 'static {
345    type Collection: MapCollection;
346
347    const NAME: &str;
348    const TYP: LazyLock<FnType>;
349
350    /// finish will be called when every lambda instance has produced
351    /// a value for the updated array. Out contains the output of the
352    /// predicate lambda for each index i, and a is the array. out and
353    /// a are guaranteed to have the same length. out\[i\].cur is
354    /// guaranteed to be Some.
355    fn finish(&mut self, slots: &[Slot<R, E>], a: &Self::Collection) -> Option<Value>;
356}
357
358#[derive(Debug)]
359pub struct Slot<R: Rt, E: UserEvent> {
360    pub id: BindId,
361    pub pred: Node<R, E>,
362    pub cur: Option<Value>,
363}
364
365impl<R: Rt, E: UserEvent> Slot<R, E> {
366    pub fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
367        self.pred.delete(ctx);
368        ctx.cached.remove(&self.id);
369        ctx.env.unbind_variable(self.id);
370    }
371}
372
373#[derive(Debug)]
374pub struct MapQ<R: Rt, E: UserEvent, T: MapFn<R, E>> {
375    scope: Scope,
376    predid: BindId,
377    top_id: ExprId,
378    mftyp: TArc<FnType>,
379    etyp: Type,
380    slots: Vec<Slot<R, E>>,
381    cur: T::Collection,
382    t: T,
383}
384
385impl<R: Rt, E: UserEvent, T: MapFn<R, E>> BuiltIn<R, E> for MapQ<R, E, T> {
386    const NAME: &str = T::NAME;
387    const TYP: LazyLock<FnType> = T::TYP;
388
389    fn init<'a, 'b, 'c>(
390        _ctx: &'a mut ExecCtx<R, E>,
391        typ: &'a graphix_compiler::typ::FnType,
392        scope: &'b Scope,
393        from: &'c [Node<R, E>],
394        top_id: ExprId,
395    ) -> Result<Box<dyn Apply<R, E>>> {
396        match from {
397            [_, _] => Ok(Box::new(Self {
398                scope: scope.append(&format_compact!("fn{}", LambdaId::new().inner())),
399                predid: BindId::new(),
400                top_id,
401                etyp: T::Collection::etyp(typ)?,
402                mftyp: match &typ.args[1].typ {
403                    Type::Fn(ft) => ft.clone(),
404                    t => bail!("expected a function not {t}"),
405                },
406                slots: vec![],
407                cur: Default::default(),
408                t: T::default(),
409            })),
410            _ => bail!("expected two arguments"),
411        }
412    }
413}
414
415impl<R: Rt, E: UserEvent, T: MapFn<R, E>> Apply<R, E> for MapQ<R, E, T> {
416    fn update(
417        &mut self,
418        ctx: &mut ExecCtx<R, E>,
419        from: &mut [Node<R, E>],
420        event: &mut Event<E>,
421    ) -> Option<Value> {
422        let slen = self.slots.len();
423        if let Some(v) = from[1].update(ctx, event) {
424            ctx.cached.insert(self.predid, v.clone());
425            event.variables.insert(self.predid, v);
426        }
427        let (up, resized) =
428            match from[0].update(ctx, event).and_then(|v| T::Collection::select(v)) {
429                Some(a) if a.len() == slen => (Some(a), false),
430                Some(a) if a.len() < slen => {
431                    while self.slots.len() > a.len() {
432                        if let Some(mut s) = self.slots.pop() {
433                            s.delete(ctx)
434                        }
435                    }
436                    (Some(a), true)
437                }
438                Some(a) => {
439                    while self.slots.len() < a.len() {
440                        let (id, node) = genn::bind(
441                            ctx,
442                            &self.scope.lexical,
443                            "x",
444                            self.etyp.clone(),
445                            self.top_id,
446                        );
447                        let fargs = vec![node];
448                        let fnode = genn::reference(
449                            ctx,
450                            self.predid,
451                            Type::Fn(self.mftyp.clone()),
452                            self.top_id,
453                        );
454                        let pred = genn::apply(
455                            fnode,
456                            self.scope.clone(),
457                            fargs,
458                            &self.mftyp,
459                            self.top_id,
460                        );
461                        self.slots.push(Slot { id, pred, cur: None });
462                    }
463                    (Some(a), true)
464                }
465                None => (None, false),
466            };
467        if let Some(a) = up {
468            for (s, v) in self.slots.iter().zip(a.iter_values()) {
469                ctx.cached.insert(s.id, v.clone());
470                event.variables.insert(s.id, v);
471            }
472            self.cur = a.clone();
473            if a.len() == 0 {
474                return Some(T::Collection::project(a));
475            }
476        }
477        let init = event.init;
478        let mut up = resized;
479        for (i, s) in self.slots.iter_mut().enumerate() {
480            if i == slen {
481                // new nodes were added starting here
482                event.init = true;
483                if let Entry::Vacant(e) = event.variables.entry(self.predid)
484                    && let Some(v) = ctx.cached.get(&self.predid)
485                {
486                    e.insert(v.clone());
487                }
488            }
489            if let Some(v) = s.pred.update(ctx, event) {
490                s.cur = Some(v);
491                up = true;
492            }
493        }
494        event.init = init;
495        if up && self.slots.iter().all(|s| s.cur.is_some()) {
496            self.t.finish(&mut &self.slots, &self.cur)
497        } else {
498            None
499        }
500    }
501
502    fn typecheck(
503        &mut self,
504        ctx: &mut ExecCtx<R, E>,
505        _from: &mut [Node<R, E>],
506    ) -> anyhow::Result<()> {
507        let (_, node) =
508            genn::bind(ctx, &self.scope.lexical, "x", self.etyp.clone(), self.top_id);
509        let fargs = vec![node];
510        let ft = self.mftyp.clone();
511        let fnode = genn::reference(ctx, self.predid, Type::Fn(ft.clone()), self.top_id);
512        let mut node = genn::apply(fnode, self.scope.clone(), fargs, &ft, self.top_id);
513        let r = node.typecheck(ctx);
514        node.delete(ctx);
515        r
516    }
517
518    fn refs(&self, refs: &mut Refs) {
519        for s in &self.slots {
520            s.pred.refs(refs)
521        }
522    }
523
524    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
525        ctx.cached.remove(&self.predid);
526        for sl in &mut self.slots {
527            sl.delete(ctx)
528        }
529    }
530
531    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
532        self.cur = Default::default();
533        for sl in &mut self.slots {
534            sl.cur = None;
535            sl.pred.sleep(ctx);
536        }
537    }
538}
539
540pub trait FoldFn<R: Rt, E: UserEvent>: Debug + Send + Sync + 'static {
541    type Collection: MapCollection;
542
543    const NAME: &str;
544    const TYP: LazyLock<FnType>;
545}
546
547#[derive(Debug)]
548pub struct FoldQ<R: Rt, E: UserEvent, T: FoldFn<R, E>> {
549    top_id: ExprId,
550    fid: BindId,
551    scope: Scope,
552    binds: Vec<BindId>,
553    nodes: Vec<Node<R, E>>,
554    inits: Vec<Option<Value>>,
555    initids: Vec<BindId>,
556    initid: BindId,
557    mftype: TArc<FnType>,
558    etyp: Type,
559    ityp: Type,
560    init: Option<Value>,
561    t: PhantomData<T>,
562}
563
564impl<R: Rt, E: UserEvent, T: FoldFn<R, E>> BuiltIn<R, E> for FoldQ<R, E, T> {
565    const NAME: &str = T::NAME;
566    const TYP: LazyLock<FnType> = T::TYP;
567
568    fn init<'a, 'b, 'c>(
569        _ctx: &'a mut ExecCtx<R, E>,
570        typ: &'a graphix_compiler::typ::FnType,
571        scope: &'b Scope,
572        from: &'c [Node<R, E>],
573        top_id: ExprId,
574    ) -> Result<Box<dyn Apply<R, E>>> {
575        match from {
576            [_, _, _] => Ok(Box::new(Self {
577                top_id,
578                scope: scope.clone(),
579                binds: vec![],
580                nodes: vec![],
581                inits: vec![],
582                initids: vec![],
583                initid: BindId::new(),
584                fid: BindId::new(),
585                etyp: T::Collection::etyp(typ)?,
586                ityp: typ.args[1].typ.clone(),
587                mftype: match &typ.args[2].typ {
588                    Type::Fn(ft) => ft.clone(),
589                    t => bail!("expected a function not {t}"),
590                },
591                init: None,
592                t: PhantomData,
593            })),
594            _ => bail!("expected three arguments"),
595        }
596    }
597}
598
599impl<R: Rt, E: UserEvent, T: FoldFn<R, E>> Apply<R, E> for FoldQ<R, E, T> {
600    fn update(
601        &mut self,
602        ctx: &mut ExecCtx<R, E>,
603        from: &mut [Node<R, E>],
604        event: &mut Event<E>,
605    ) -> Option<Value> {
606        let init = match from[0].update(ctx, event).and_then(|v| T::Collection::select(v))
607        {
608            None => self.nodes.len(),
609            Some(a) if a.len() == self.binds.len() => {
610                for (id, v) in self.binds.iter().zip(a.iter_values()) {
611                    ctx.cached.insert(*id, v.clone());
612                    event.variables.insert(*id, v.clone());
613                }
614                self.nodes.len()
615            }
616            Some(a) => {
617                let vals = a.iter_values().collect::<LPooled<Vec<Value>>>();
618                while self.binds.len() < a.len() {
619                    self.binds.push(BindId::new());
620                    self.inits.push(None);
621                    self.initids.push(BindId::new());
622                }
623                while a.len() < self.binds.len() {
624                    if let Some(id) = self.binds.pop() {
625                        ctx.cached.remove(&id);
626                    }
627                    if let Some(id) = self.initids.pop() {
628                        ctx.cached.remove(&id);
629                    }
630                    self.inits.pop();
631                    if let Some(mut n) = self.nodes.pop() {
632                        n.delete(ctx);
633                    }
634                }
635                let init = self.nodes.len();
636                for i in 0..self.binds.len() {
637                    ctx.cached.insert(self.binds[i], vals[i].clone());
638                    event.variables.insert(self.binds[i], vals[i].clone());
639                    if i >= self.nodes.len() {
640                        let n = genn::reference(
641                            ctx,
642                            if i == 0 { self.initid } else { self.initids[i - 1] },
643                            self.ityp.clone(),
644                            self.top_id,
645                        );
646                        let x = genn::reference(
647                            ctx,
648                            self.binds[i],
649                            self.etyp.clone(),
650                            self.top_id,
651                        );
652                        let fnode = genn::reference(
653                            ctx,
654                            self.fid,
655                            Type::Fn(self.mftype.clone()),
656                            self.top_id,
657                        );
658                        let node = genn::apply(
659                            fnode,
660                            self.scope.clone(),
661                            vec![n, x],
662                            &self.mftype,
663                            self.top_id,
664                        );
665                        self.nodes.push(node);
666                    }
667                }
668                init
669            }
670        };
671        if let Some(v) = from[1].update(ctx, event) {
672            ctx.cached.insert(self.initid, v.clone());
673            event.variables.insert(self.initid, v.clone());
674            self.init = Some(v);
675        }
676        if let Some(v) = from[2].update(ctx, event) {
677            ctx.cached.insert(self.fid, v.clone());
678            event.variables.insert(self.fid, v);
679        }
680        let old_init = event.init;
681        for i in 0..self.nodes.len() {
682            if i == init {
683                event.init = true;
684                if let Some(v) = ctx.cached.get(&self.fid)
685                    && let Entry::Vacant(e) = event.variables.entry(self.fid)
686                {
687                    e.insert(v.clone());
688                }
689                if i == 0 {
690                    if let Some(v) = self.init.as_ref()
691                        && let Entry::Vacant(e) = event.variables.entry(self.initid)
692                    {
693                        e.insert(v.clone());
694                    }
695                } else {
696                    if let Some(v) = self.inits[i - 1].clone() {
697                        event.variables.insert(self.initids[i - 1], v);
698                    }
699                }
700            }
701            match self.nodes[i].update(ctx, event) {
702                Some(v) => {
703                    ctx.cached.insert(self.initids[i], v.clone());
704                    event.variables.insert(self.initids[i], v.clone());
705                    self.inits[i] = Some(v);
706                }
707                None => {
708                    ctx.cached.remove(&self.initids[i]);
709                    event.variables.remove(&self.initids[i]);
710                    self.inits[i] = None;
711                }
712            }
713        }
714        event.init = old_init;
715        self.inits.last().and_then(|v| v.clone())
716    }
717
718    fn typecheck(
719        &mut self,
720        ctx: &mut ExecCtx<R, E>,
721        _from: &mut [Node<R, E>],
722    ) -> anyhow::Result<()> {
723        let mut n = genn::reference(ctx, self.initid, self.ityp.clone(), self.top_id);
724        let x = genn::reference(ctx, BindId::new(), self.etyp.clone(), self.top_id);
725        let fnode =
726            genn::reference(ctx, self.fid, Type::Fn(self.mftype.clone()), self.top_id);
727        n = genn::apply(fnode, self.scope.clone(), vec![n, x], &self.mftype, self.top_id);
728        let r = n.typecheck(ctx);
729        n.delete(ctx);
730        r
731    }
732
733    fn refs(&self, refs: &mut Refs) {
734        for n in &self.nodes {
735            n.refs(refs)
736        }
737    }
738
739    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
740        let i =
741            iter::once(&self.initid).chain(self.binds.iter()).chain(self.initids.iter());
742        for id in i {
743            ctx.cached.remove(id);
744        }
745        for n in &mut self.nodes {
746            n.delete(ctx);
747        }
748    }
749
750    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
751        self.init = None;
752        for v in &mut self.inits {
753            *v = None
754        }
755        for n in &mut self.nodes {
756            n.sleep(ctx)
757        }
758    }
759}
760
761// ── Core builtins ──────────────────────────────────────────────────
762
763#[derive(Debug)]
764struct IsErr;
765
766impl<R: Rt, E: UserEvent> BuiltIn<R, E> for IsErr {
767    const NAME: &str = "core_is_err";
768    deftype!("fn(Any) -> bool");
769
770    fn init<'a, 'b, 'c>(
771        _ctx: &'a mut ExecCtx<R, E>,
772        _typ: &'a graphix_compiler::typ::FnType,
773        _scope: &'b Scope,
774        _from: &'c [Node<R, E>],
775        _top_id: ExprId,
776    ) -> Result<Box<dyn Apply<R, E>>> {
777        Ok(Box::new(IsErr))
778    }
779}
780
781impl<R: Rt, E: UserEvent> Apply<R, E> for IsErr {
782    fn update(
783        &mut self,
784        ctx: &mut ExecCtx<R, E>,
785        from: &mut [Node<R, E>],
786        event: &mut Event<E>,
787    ) -> Option<Value> {
788        from[0].update(ctx, event).map(|v| match v {
789            Value::Error(_) => Value::Bool(true),
790            _ => Value::Bool(false),
791        })
792    }
793
794    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
795}
796
797#[derive(Debug)]
798struct FilterErr;
799
800impl<R: Rt, E: UserEvent> BuiltIn<R, E> for FilterErr {
801    const NAME: &str = "core_filter_err";
802    deftype!("fn(Result<'a, 'b>) -> Error<'b>");
803
804    fn init<'a, 'b, 'c>(
805        _ctx: &'a mut ExecCtx<R, E>,
806        _typ: &'a graphix_compiler::typ::FnType,
807        _scope: &'b Scope,
808        _from: &'c [Node<R, E>],
809        _top_id: ExprId,
810    ) -> Result<Box<dyn Apply<R, E>>> {
811        Ok(Box::new(FilterErr))
812    }
813}
814
815impl<R: Rt, E: UserEvent> Apply<R, E> for FilterErr {
816    fn update(
817        &mut self,
818        ctx: &mut ExecCtx<R, E>,
819        from: &mut [Node<R, E>],
820        event: &mut Event<E>,
821    ) -> Option<Value> {
822        from[0].update(ctx, event).and_then(|v| match v {
823            v @ Value::Error(_) => Some(v),
824            _ => None,
825        })
826    }
827
828    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
829}
830
831#[derive(Debug)]
832struct ToError;
833
834impl<R: Rt, E: UserEvent> BuiltIn<R, E> for ToError {
835    const NAME: &str = "core_error";
836    deftype!("fn('a) -> Error<'a>");
837
838    fn init<'a, 'b, 'c>(
839        _ctx: &'a mut ExecCtx<R, E>,
840        _typ: &'a graphix_compiler::typ::FnType,
841        _scope: &'b Scope,
842        _from: &'c [Node<R, E>],
843        _top_id: ExprId,
844    ) -> Result<Box<dyn Apply<R, E>>> {
845        Ok(Box::new(ToError))
846    }
847}
848
849impl<R: Rt, E: UserEvent> Apply<R, E> for ToError {
850    fn update(
851        &mut self,
852        ctx: &mut ExecCtx<R, E>,
853        from: &mut [Node<R, E>],
854        event: &mut Event<E>,
855    ) -> Option<Value> {
856        from[0].update(ctx, event).map(|e| Value::Error(triomphe::Arc::new(e)))
857    }
858
859    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
860}
861
862#[derive(Debug)]
863struct Once {
864    val: bool,
865}
866
867impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Once {
868    const NAME: &str = "core_once";
869    deftype!("fn('a) -> 'a");
870
871    fn init<'a, 'b, 'c>(
872        _ctx: &'a mut ExecCtx<R, E>,
873        _typ: &'a graphix_compiler::typ::FnType,
874        _scope: &'b Scope,
875        _from: &'c [Node<R, E>],
876        _top_id: ExprId,
877    ) -> Result<Box<dyn Apply<R, E>>> {
878        Ok(Box::new(Once { val: false }))
879    }
880}
881
882impl<R: Rt, E: UserEvent> Apply<R, E> for Once {
883    fn update(
884        &mut self,
885        ctx: &mut ExecCtx<R, E>,
886        from: &mut [Node<R, E>],
887        event: &mut Event<E>,
888    ) -> Option<Value> {
889        match from {
890            [s] => s.update(ctx, event).and_then(|v| {
891                if self.val {
892                    None
893                } else {
894                    self.val = true;
895                    Some(v)
896                }
897            }),
898            _ => None,
899        }
900    }
901
902    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
903        self.val = false
904    }
905}
906
907#[derive(Debug)]
908struct Take {
909    n: Option<usize>,
910}
911
912impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Take {
913    const NAME: &str = "core_take";
914    deftype!("fn(#n:Any, 'a) -> 'a");
915
916    fn init<'a, 'b, 'c>(
917        _ctx: &'a mut ExecCtx<R, E>,
918        _typ: &'a graphix_compiler::typ::FnType,
919        _scope: &'b Scope,
920        _from: &'c [Node<R, E>],
921        _top_id: ExprId,
922    ) -> Result<Box<dyn Apply<R, E>>> {
923        Ok(Box::new(Take { n: None }))
924    }
925}
926
927impl<R: Rt, E: UserEvent> Apply<R, E> for Take {
928    fn update(
929        &mut self,
930        ctx: &mut ExecCtx<R, E>,
931        from: &mut [Node<R, E>],
932        event: &mut Event<E>,
933    ) -> Option<Value> {
934        if let Some(n) =
935            from[0].update(ctx, event).and_then(|v| v.cast_to::<usize>().ok())
936        {
937            self.n = Some(n)
938        }
939        match from[1].update(ctx, event) {
940            None => None,
941            Some(v) => match &mut self.n {
942                None => None,
943                Some(n) if *n > 0 => {
944                    *n -= 1;
945                    return Some(v);
946                }
947                Some(_) => None,
948            },
949        }
950    }
951
952    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
953        self.n = None
954    }
955}
956
957#[derive(Debug)]
958struct Skip {
959    n: Option<usize>,
960}
961
962impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Skip {
963    const NAME: &str = "core_skip";
964    deftype!("fn(#n:Any, 'a) -> 'a");
965
966    fn init<'a, 'b, 'c>(
967        _ctx: &'a mut ExecCtx<R, E>,
968        _typ: &'a graphix_compiler::typ::FnType,
969        _scope: &'b Scope,
970        _from: &'c [Node<R, E>],
971        _top_id: ExprId,
972    ) -> Result<Box<dyn Apply<R, E>>> {
973        Ok(Box::new(Skip { n: None }))
974    }
975}
976
977impl<R: Rt, E: UserEvent> Apply<R, E> for Skip {
978    fn update(
979        &mut self,
980        ctx: &mut ExecCtx<R, E>,
981        from: &mut [Node<R, E>],
982        event: &mut Event<E>,
983    ) -> Option<Value> {
984        if let Some(n) =
985            from[0].update(ctx, event).and_then(|v| v.cast_to::<usize>().ok())
986        {
987            self.n = Some(n)
988        }
989        match from[1].update(ctx, event) {
990            None => None,
991            Some(v) => match &mut self.n {
992                None => Some(v),
993                Some(n) if *n > 0 => {
994                    *n -= 1;
995                    None
996                }
997                Some(_) => Some(v),
998            },
999        }
1000    }
1001
1002    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
1003        self.n = None
1004    }
1005}
1006
1007#[derive(Debug, Default)]
1008struct AllEv;
1009
1010impl EvalCached for AllEv {
1011    const NAME: &str = "core_all";
1012    deftype!("fn(@args: Any) -> Any");
1013
1014    fn eval(&mut self, from: &CachedVals) -> Option<Value> {
1015        match &*from.0 {
1016            [] => None,
1017            [hd, tl @ ..] => match hd {
1018                None => None,
1019                v @ Some(_) => {
1020                    if tl.into_iter().all(|v1| v1 == v) {
1021                        v.clone()
1022                    } else {
1023                        None
1024                    }
1025                }
1026            },
1027        }
1028    }
1029}
1030
1031type All = CachedArgs<AllEv>;
1032
1033fn add_vals(lhs: Option<Value>, rhs: Option<Value>) -> Option<Value> {
1034    match (lhs, rhs) {
1035        (None, None) | (Some(_), None) => None,
1036        (None, r @ Some(_)) => r,
1037        (Some(l), Some(r)) => Some(l + r),
1038    }
1039}
1040
1041#[derive(Debug, Default)]
1042struct SumEv;
1043
1044impl EvalCached for SumEv {
1045    const NAME: &str = "core_sum";
1046    deftype!("fn(@args: [Number, Array<[Number, Array<Number>]>]) -> Number");
1047
1048    fn eval(&mut self, from: &CachedVals) -> Option<Value> {
1049        from.flat_iter().fold(None, |res, v| match res {
1050            res @ Some(Value::Error(_)) => res,
1051            res => add_vals(res, v.clone()),
1052        })
1053    }
1054}
1055
1056type Sum = CachedArgs<SumEv>;
1057
1058#[derive(Debug, Default)]
1059struct ProductEv;
1060
1061fn prod_vals(lhs: Option<Value>, rhs: Option<Value>) -> Option<Value> {
1062    match (lhs, rhs) {
1063        (None, None) | (Some(_), None) => None,
1064        (None, r @ Some(_)) => r,
1065        (Some(l), Some(r)) => Some(l * r),
1066    }
1067}
1068
1069impl EvalCached for ProductEv {
1070    const NAME: &str = "core_product";
1071    deftype!("fn(@args: [Number, Array<[Number, Array<Number>]>]) -> Number");
1072
1073    fn eval(&mut self, from: &CachedVals) -> Option<Value> {
1074        from.flat_iter().fold(None, |res, v| match res {
1075            res @ Some(Value::Error(_)) => res,
1076            res => prod_vals(res, v.clone()),
1077        })
1078    }
1079}
1080
1081type Product = CachedArgs<ProductEv>;
1082
1083#[derive(Debug, Default)]
1084struct DivideEv;
1085
1086fn div_vals(lhs: Option<Value>, rhs: Option<Value>) -> Option<Value> {
1087    match (lhs, rhs) {
1088        (None, None) | (Some(_), None) => None,
1089        (None, r @ Some(_)) => r,
1090        (Some(l), Some(r)) => Some(l / r),
1091    }
1092}
1093
1094impl EvalCached for DivideEv {
1095    const NAME: &str = "core_divide";
1096    deftype!("fn(@args: [Number, Array<[Number, Array<Number>]>]) -> Number");
1097
1098    fn eval(&mut self, from: &CachedVals) -> Option<Value> {
1099        from.flat_iter().fold(None, |res, v| match res {
1100            res @ Some(Value::Error(_)) => res,
1101            res => div_vals(res, v.clone()),
1102        })
1103    }
1104}
1105
1106type Divide = CachedArgs<DivideEv>;
1107
1108#[derive(Debug, Default)]
1109struct MinEv;
1110
1111impl EvalCached for MinEv {
1112    const NAME: &str = "core_min";
1113    deftype!("fn('a, @args:'a) -> 'a");
1114
1115    fn eval(&mut self, from: &CachedVals) -> Option<Value> {
1116        let mut res = None;
1117        for v in from.flat_iter() {
1118            match (res, v) {
1119                (None, None) | (Some(_), None) => return None,
1120                (None, Some(v)) => {
1121                    res = Some(v);
1122                }
1123                (Some(v0), Some(v)) => {
1124                    res = if v < v0 { Some(v) } else { Some(v0) };
1125                }
1126            }
1127        }
1128        res
1129    }
1130}
1131
1132type Min = CachedArgs<MinEv>;
1133
1134#[derive(Debug, Default)]
1135struct MaxEv;
1136
1137impl EvalCached for MaxEv {
1138    const NAME: &str = "core_max";
1139    deftype!("fn('a, @args: 'a) -> 'a");
1140
1141    fn eval(&mut self, from: &CachedVals) -> Option<Value> {
1142        let mut res = None;
1143        for v in from.flat_iter() {
1144            match (res, v) {
1145                (None, None) | (Some(_), None) => return None,
1146                (None, Some(v)) => {
1147                    res = Some(v);
1148                }
1149                (Some(v0), Some(v)) => {
1150                    res = if v > v0 { Some(v) } else { Some(v0) };
1151                }
1152            }
1153        }
1154        res
1155    }
1156}
1157
1158type Max = CachedArgs<MaxEv>;
1159
1160#[derive(Debug, Default)]
1161struct AndEv;
1162
1163impl EvalCached for AndEv {
1164    const NAME: &str = "core_and";
1165    deftype!("fn(@args: bool) -> bool");
1166
1167    fn eval(&mut self, from: &CachedVals) -> Option<Value> {
1168        let mut res = Some(Value::Bool(true));
1169        for v in from.flat_iter() {
1170            match v {
1171                None => return None,
1172                Some(Value::Bool(true)) => (),
1173                Some(_) => {
1174                    res = Some(Value::Bool(false));
1175                }
1176            }
1177        }
1178        res
1179    }
1180}
1181
1182type And = CachedArgs<AndEv>;
1183
1184#[derive(Debug, Default)]
1185struct OrEv;
1186
1187impl EvalCached for OrEv {
1188    const NAME: &str = "core_or";
1189    deftype!("fn(@args: bool) -> bool");
1190
1191    fn eval(&mut self, from: &CachedVals) -> Option<Value> {
1192        let mut res = Some(Value::Bool(false));
1193        for v in from.flat_iter() {
1194            match v {
1195                None => return None,
1196                Some(Value::Bool(true)) => {
1197                    res = Some(Value::Bool(true));
1198                }
1199                Some(_) => (),
1200            }
1201        }
1202        res
1203    }
1204}
1205
1206type Or = CachedArgs<OrEv>;
1207
1208#[derive(Debug)]
1209struct Filter<R: Rt, E: UserEvent> {
1210    ready: bool,
1211    queue: VecDeque<Value>,
1212    pred: Node<R, E>,
1213    top_id: ExprId,
1214    fid: BindId,
1215    x: BindId,
1216    out: BindId,
1217}
1218
1219impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Filter<R, E> {
1220    const NAME: &str = "core_filter";
1221    deftype!("fn('a, fn('a) -> bool throws 'e) -> 'a throws 'e");
1222
1223    fn init<'a, 'b, 'c>(
1224        ctx: &'a mut ExecCtx<R, E>,
1225        typ: &'a graphix_compiler::typ::FnType,
1226        scope: &'b Scope,
1227        from: &'c [Node<R, E>],
1228        top_id: ExprId,
1229    ) -> Result<Box<dyn Apply<R, E>>> {
1230        match from {
1231            [_, _] => {
1232                let (x, xn) =
1233                    genn::bind(ctx, &scope.lexical, "x", typ.args[0].typ.clone(), top_id);
1234                let fid = BindId::new();
1235                let ptyp = match &typ.args[1].typ {
1236                    Type::Fn(ft) => ft.clone(),
1237                    t => bail!("expected a function not {t}"),
1238                };
1239                let fnode = genn::reference(ctx, fid, Type::Fn(ptyp.clone()), top_id);
1240                let pred = genn::apply(fnode, scope.clone(), vec![xn], &ptyp, top_id);
1241                let queue = VecDeque::new();
1242                let out = BindId::new();
1243                ctx.rt.ref_var(out, top_id);
1244                Ok(Box::new(Self { ready: true, queue, pred, fid, x, out, top_id }))
1245            }
1246            _ => bail!("expected two arguments"),
1247        }
1248    }
1249}
1250
1251impl<R: Rt, E: UserEvent> Apply<R, E> for Filter<R, E> {
1252    fn update(
1253        &mut self,
1254        ctx: &mut ExecCtx<R, E>,
1255        from: &mut [Node<R, E>],
1256        event: &mut Event<E>,
1257    ) -> Option<Value> {
1258        macro_rules! set {
1259            ($v:expr) => {{
1260                self.ready = false;
1261                ctx.cached.insert(self.x, $v.clone());
1262                event.variables.insert(self.x, $v);
1263            }};
1264        }
1265        macro_rules! maybe_cont {
1266            () => {{
1267                if let Some(v) = self.queue.front().cloned() {
1268                    set!(v);
1269                    continue;
1270                }
1271                break;
1272            }};
1273        }
1274        if let Some(v) = from[0].update(ctx, event) {
1275            self.queue.push_back(v);
1276        }
1277        if let Some(v) = from[1].update(ctx, event) {
1278            ctx.cached.insert(self.fid, v.clone());
1279            event.variables.insert(self.fid, v);
1280        }
1281        if self.ready && self.queue.len() > 0 {
1282            let v = self.queue.front().unwrap().clone();
1283            set!(v);
1284        }
1285        loop {
1286            match self.pred.update(ctx, event) {
1287                None => break,
1288                Some(v) => {
1289                    self.ready = true;
1290                    match v {
1291                        Value::Bool(true) => {
1292                            ctx.rt.set_var(self.out, self.queue.pop_front().unwrap());
1293                            maybe_cont!();
1294                        }
1295                        _ => {
1296                            let _ = self.queue.pop_front();
1297                            maybe_cont!();
1298                        }
1299                    }
1300                }
1301            }
1302        }
1303        event.variables.get(&self.out).map(|v| v.clone())
1304    }
1305
1306    fn typecheck(
1307        &mut self,
1308        ctx: &mut ExecCtx<R, E>,
1309        _from: &mut [Node<R, E>],
1310    ) -> anyhow::Result<()> {
1311        self.pred.typecheck(ctx)
1312    }
1313
1314    fn refs(&self, refs: &mut Refs) {
1315        self.pred.refs(refs)
1316    }
1317
1318    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
1319        ctx.cached.remove(&self.fid);
1320        ctx.cached.remove(&self.out);
1321        ctx.cached.remove(&self.x);
1322        ctx.env.unbind_variable(self.x);
1323        self.pred.delete(ctx);
1324        ctx.rt.unref_var(self.out, self.top_id)
1325    }
1326
1327    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
1328        ctx.rt.unref_var(self.out, self.top_id);
1329        self.out = BindId::new();
1330        ctx.rt.ref_var(self.out, self.top_id);
1331        self.queue.clear();
1332        self.pred.sleep(ctx);
1333    }
1334}
1335
1336#[derive(Debug)]
1337struct Queue {
1338    triggered: usize,
1339    queue: VecDeque<Value>,
1340    id: BindId,
1341    top_id: ExprId,
1342}
1343
1344impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Queue {
1345    const NAME: &str = "core_queue";
1346    deftype!("fn(#clock:Any, 'a) -> 'a");
1347
1348    fn init<'a, 'b, 'c>(
1349        ctx: &'a mut ExecCtx<R, E>,
1350        _typ: &'a graphix_compiler::typ::FnType,
1351        _scope: &'b Scope,
1352        from: &'c [Node<R, E>],
1353        top_id: ExprId,
1354    ) -> Result<Box<dyn Apply<R, E>>> {
1355        match from {
1356            [_, _] => {
1357                let id = BindId::new();
1358                ctx.rt.ref_var(id, top_id);
1359                Ok(Box::new(Self { triggered: 0, queue: VecDeque::new(), id, top_id }))
1360            }
1361            _ => bail!("expected two arguments"),
1362        }
1363    }
1364}
1365
1366impl<R: Rt, E: UserEvent> Apply<R, E> for Queue {
1367    fn update(
1368        &mut self,
1369        ctx: &mut ExecCtx<R, E>,
1370        from: &mut [Node<R, E>],
1371        event: &mut Event<E>,
1372    ) -> Option<Value> {
1373        if from[0].update(ctx, event).is_some() {
1374            self.triggered += 1;
1375        }
1376        if let Some(v) = from[1].update(ctx, event) {
1377            self.queue.push_back(v);
1378        }
1379        while self.triggered > 0 && self.queue.len() > 0 {
1380            self.triggered -= 1;
1381            ctx.rt.set_var(self.id, self.queue.pop_front().unwrap());
1382        }
1383        event.variables.get(&self.id).cloned()
1384    }
1385
1386    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
1387        ctx.rt.unref_var(self.id, self.top_id);
1388    }
1389
1390    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
1391        ctx.rt.unref_var(self.id, self.top_id);
1392        self.id = BindId::new();
1393        ctx.rt.ref_var(self.id, self.top_id);
1394        self.triggered = 0;
1395        self.queue.clear();
1396    }
1397}
1398
1399#[derive(Debug)]
1400struct Hold {
1401    triggered: usize,
1402    current: Option<Value>,
1403}
1404
1405impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Hold {
1406    const NAME: &str = "core_hold";
1407    deftype!("fn(#clock:Any, 'a) -> 'a");
1408
1409    fn init<'a, 'b, 'c>(
1410        _ctx: &'a mut ExecCtx<R, E>,
1411        _typ: &'a graphix_compiler::typ::FnType,
1412        _scope: &'b Scope,
1413        from: &'c [Node<R, E>],
1414        _top_id: ExprId,
1415    ) -> Result<Box<dyn Apply<R, E>>> {
1416        match from {
1417            [_, _] => Ok(Box::new(Self { triggered: 0, current: None })),
1418            _ => bail!("expected two arguments"),
1419        }
1420    }
1421}
1422
1423impl<R: Rt, E: UserEvent> Apply<R, E> for Hold {
1424    fn update(
1425        &mut self,
1426        ctx: &mut ExecCtx<R, E>,
1427        from: &mut [Node<R, E>],
1428        event: &mut Event<E>,
1429    ) -> Option<Value> {
1430        if from[0].update(ctx, event).is_some() {
1431            self.triggered += 1;
1432        }
1433        if let Some(v) = from[1].update(ctx, event) {
1434            self.current = Some(v);
1435        }
1436        if self.triggered > 0
1437            && let Some(v) = self.current.take()
1438        {
1439            self.triggered -= 1;
1440            Some(v)
1441        } else {
1442            None
1443        }
1444    }
1445
1446    fn delete(&mut self, _: &mut ExecCtx<R, E>) {}
1447
1448    fn sleep(&mut self, _: &mut ExecCtx<R, E>) {
1449        self.triggered = 0;
1450        self.current = None;
1451    }
1452}
1453
1454#[derive(Debug)]
1455struct Seq {
1456    id: BindId,
1457    top_id: ExprId,
1458    args: CachedVals,
1459}
1460
1461impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Seq {
1462    const NAME: &str = "core_seq";
1463    deftype!("fn(i64, i64) -> Result<i64, `SeqError(string)>");
1464
1465    fn init<'a, 'b, 'c>(
1466        ctx: &'a mut ExecCtx<R, E>,
1467        _typ: &'a graphix_compiler::typ::FnType,
1468        _scope: &'b Scope,
1469        from: &'c [Node<R, E>],
1470        top_id: ExprId,
1471    ) -> Result<Box<dyn Apply<R, E>>> {
1472        let id = BindId::new();
1473        ctx.rt.ref_var(id, top_id);
1474        let args = CachedVals::new(from);
1475        Ok(Box::new(Self { id, top_id, args }))
1476    }
1477}
1478
1479impl<R: Rt, E: UserEvent> Apply<R, E> for Seq {
1480    fn update(
1481        &mut self,
1482        ctx: &mut ExecCtx<R, E>,
1483        from: &mut [Node<R, E>],
1484        event: &mut Event<E>,
1485    ) -> Option<Value> {
1486        if self.args.update(ctx, from, event) {
1487            match &self.args.0[..] {
1488                [Some(Value::I64(i)), Some(Value::I64(j))] if i <= j => {
1489                    for v in *i..*j {
1490                        ctx.rt.set_var(self.id, Value::I64(v));
1491                    }
1492                }
1493                _ => {
1494                    let e = literal!("SeqError");
1495                    return Some(err!(e, "invalid args i must be <= j"));
1496                }
1497            }
1498        }
1499        event.variables.get(&self.id).cloned()
1500    }
1501
1502    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
1503        ctx.rt.unref_var(self.id, self.top_id);
1504    }
1505
1506    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
1507        ctx.rt.unref_var(self.id, self.top_id);
1508        self.id = BindId::new();
1509        ctx.rt.ref_var(self.id, self.top_id);
1510    }
1511}
1512
1513#[derive(Debug)]
1514struct Throttle {
1515    wait: Duration,
1516    last: Option<Instant>,
1517    tid: Option<BindId>,
1518    top_id: ExprId,
1519    args: CachedVals,
1520}
1521
1522impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Throttle {
1523    const NAME: &str = "core_throttle";
1524    deftype!("fn(?#rate:duration, 'a) -> 'a");
1525
1526    fn init<'a, 'b, 'c>(
1527        _ctx: &'a mut ExecCtx<R, E>,
1528        _typ: &'a graphix_compiler::typ::FnType,
1529        _scope: &'b Scope,
1530        from: &'c [Node<R, E>],
1531        top_id: ExprId,
1532    ) -> Result<Box<dyn Apply<R, E>>> {
1533        let args = CachedVals::new(from);
1534        Ok(Box::new(Self { wait: Duration::ZERO, last: None, tid: None, top_id, args }))
1535    }
1536}
1537
1538impl<R: Rt, E: UserEvent> Apply<R, E> for Throttle {
1539    fn update(
1540        &mut self,
1541        ctx: &mut ExecCtx<R, E>,
1542        from: &mut [Node<R, E>],
1543        event: &mut Event<E>,
1544    ) -> Option<Value> {
1545        macro_rules! maybe_schedule {
1546            ($last:expr) => {{
1547                let now = Instant::now();
1548                if now - *$last >= self.wait {
1549                    *$last = now;
1550                    return self.args.0[1].clone();
1551                } else {
1552                    let id = BindId::new();
1553                    ctx.rt.ref_var(id, self.top_id);
1554                    ctx.rt.set_timer(id, self.wait - (now - *$last));
1555                    self.tid = Some(id);
1556                    return None;
1557                }
1558            }};
1559        }
1560        let mut up = [false; 2];
1561        self.args.update_diff(&mut up, ctx, from, event);
1562        if up[0]
1563            && let Some(Value::Duration(d)) = &self.args.0[0]
1564        {
1565            self.wait = **d;
1566            if let Some(id) = self.tid.take()
1567                && let Some(last) = &mut self.last
1568            {
1569                ctx.rt.unref_var(id, self.top_id);
1570                maybe_schedule!(last)
1571            }
1572        }
1573        if up[1] && self.tid.is_none() {
1574            match &mut self.last {
1575                Some(last) => maybe_schedule!(last),
1576                None => {
1577                    self.last = Some(Instant::now());
1578                    return self.args.0[1].clone();
1579                }
1580            }
1581        }
1582        if let Some(id) = self.tid
1583            && let Some(_) = event.variables.get(&id)
1584        {
1585            ctx.rt.unref_var(id, self.top_id);
1586            self.tid = None;
1587            self.last = Some(Instant::now());
1588            return self.args.0[1].clone();
1589        }
1590        None
1591    }
1592
1593    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
1594        if let Some(id) = self.tid.take() {
1595            ctx.rt.unref_var(id, self.top_id);
1596        }
1597    }
1598
1599    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
1600        self.delete(ctx);
1601        self.last = None;
1602        self.wait = Duration::ZERO;
1603        self.args.clear();
1604    }
1605}
1606
1607#[derive(Debug)]
1608struct Count {
1609    count: i64,
1610}
1611
1612impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Count {
1613    const NAME: &str = "core_count";
1614    deftype!("fn(Any) -> i64");
1615
1616    fn init<'a, 'b, 'c>(
1617        _ctx: &'a mut ExecCtx<R, E>,
1618        _typ: &'a graphix_compiler::typ::FnType,
1619        _scope: &'b Scope,
1620        _from: &'c [Node<R, E>],
1621        _top_id: ExprId,
1622    ) -> Result<Box<dyn Apply<R, E>>> {
1623        Ok(Box::new(Count { count: 0 }))
1624    }
1625}
1626
1627impl<R: Rt, E: UserEvent> Apply<R, E> for Count {
1628    fn update(
1629        &mut self,
1630        ctx: &mut ExecCtx<R, E>,
1631        from: &mut [Node<R, E>],
1632        event: &mut Event<E>,
1633    ) -> Option<Value> {
1634        if from.into_iter().fold(false, |u, n| u || n.update(ctx, event).is_some()) {
1635            self.count += 1;
1636            Some(Value::I64(self.count))
1637        } else {
1638            None
1639        }
1640    }
1641
1642    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
1643        self.count = 0
1644    }
1645}
1646
1647#[derive(Debug, Default)]
1648struct MeanEv;
1649
1650impl EvalCached for MeanEv {
1651    const NAME: &str = "core_mean";
1652    deftype!(
1653        "fn([Number, Array<Number>], @args: [Number, Array<Number>]) -> Result<f64, `MeanError(string)>"
1654    );
1655
1656    fn eval(&mut self, from: &CachedVals) -> Option<Value> {
1657        static TAG: ArcStr = literal!("MeanError");
1658        let mut total = 0.;
1659        let mut samples = 0;
1660        let mut error = None;
1661        for v in from.flat_iter() {
1662            if let Some(v) = v {
1663                match v.cast_to::<f64>() {
1664                    Err(e) => error = Some(errf!(TAG, "{e:?}")),
1665                    Ok(v) => {
1666                        total += v;
1667                        samples += 1;
1668                    }
1669                }
1670            }
1671        }
1672        if let Some(e) = error {
1673            Some(e)
1674        } else if samples == 0 {
1675            Some(err!(TAG, "mean requires at least one argument"))
1676        } else {
1677            Some(Value::F64(total / samples as f64))
1678        }
1679    }
1680}
1681
1682type Mean = CachedArgs<MeanEv>;
1683
1684#[derive(Debug)]
1685struct Uniq(Option<Value>);
1686
1687impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Uniq {
1688    const NAME: &str = "core_uniq";
1689    deftype!("fn('a) -> 'a");
1690
1691    fn init<'a, 'b, 'c>(
1692        _ctx: &'a mut ExecCtx<R, E>,
1693        _typ: &'a graphix_compiler::typ::FnType,
1694        _scope: &'b Scope,
1695        _from: &'c [Node<R, E>],
1696        _top_id: ExprId,
1697    ) -> Result<Box<dyn Apply<R, E>>> {
1698        Ok(Box::new(Uniq(None)))
1699    }
1700}
1701
1702impl<R: Rt, E: UserEvent> Apply<R, E> for Uniq {
1703    fn update(
1704        &mut self,
1705        ctx: &mut ExecCtx<R, E>,
1706        from: &mut [Node<R, E>],
1707        event: &mut Event<E>,
1708    ) -> Option<Value> {
1709        from[0].update(ctx, event).and_then(|v| {
1710            if Some(&v) != self.0.as_ref() {
1711                self.0 = Some(v.clone());
1712                Some(v)
1713            } else {
1714                None
1715            }
1716        })
1717    }
1718
1719    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
1720        self.0 = None
1721    }
1722}
1723
1724#[derive(Debug)]
1725struct Never;
1726
1727impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Never {
1728    const NAME: &str = "core_never";
1729    deftype!("fn(@args: Any) -> 'a");
1730
1731    fn init<'a, 'b, 'c>(
1732        _ctx: &'a mut ExecCtx<R, E>,
1733        _typ: &'a graphix_compiler::typ::FnType,
1734        _scope: &'b Scope,
1735        _from: &'c [Node<R, E>],
1736        _top_id: ExprId,
1737    ) -> Result<Box<dyn Apply<R, E>>> {
1738        Ok(Box::new(Never))
1739    }
1740}
1741
1742impl<R: Rt, E: UserEvent> Apply<R, E> for Never {
1743    fn update(
1744        &mut self,
1745        ctx: &mut ExecCtx<R, E>,
1746        from: &mut [Node<R, E>],
1747        event: &mut Event<E>,
1748    ) -> Option<Value> {
1749        for n in from {
1750            n.update(ctx, event);
1751        }
1752        None
1753    }
1754
1755    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
1756}
1757
1758#[derive(Debug, Clone, Copy)]
1759enum Level {
1760    Trace,
1761    Debug,
1762    Info,
1763    Warn,
1764    Error,
1765}
1766
1767impl FromValue for Level {
1768    fn from_value(v: Value) -> Result<Self> {
1769        match &*v.cast_to::<ArcStr>()? {
1770            "Trace" => Ok(Self::Trace),
1771            "Debug" => Ok(Self::Debug),
1772            "Info" => Ok(Self::Info),
1773            "Warn" => Ok(Self::Warn),
1774            "Error" => Ok(Self::Error),
1775            v => bail!("invalid log level {v}"),
1776        }
1777    }
1778}
1779
1780#[derive(Debug, Clone, Copy)]
1781enum LogDest {
1782    Stdout,
1783    Stderr,
1784    Log(Level),
1785}
1786
1787impl FromValue for LogDest {
1788    fn from_value(v: Value) -> Result<Self> {
1789        match &*v.clone().cast_to::<ArcStr>()? {
1790            "Stdout" => Ok(Self::Stdout),
1791            "Stderr" => Ok(Self::Stderr),
1792            _ => Ok(Self::Log(v.cast_to()?)),
1793        }
1794    }
1795}
1796
1797#[derive(Debug)]
1798struct Dbg {
1799    spec: Expr,
1800    dest: LogDest,
1801    typ: Type,
1802}
1803
1804impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Dbg {
1805    const NAME: &str = "core_dbg";
1806    deftype!("fn(?#dest:[`Stdout, `Stderr, Log], 'a) -> 'a");
1807
1808    fn init<'a, 'b, 'c>(
1809        _ctx: &'a mut ExecCtx<R, E>,
1810        _typ: &'a graphix_compiler::typ::FnType,
1811        _scope: &'b Scope,
1812        from: &'c [Node<R, E>],
1813        _top_id: ExprId,
1814    ) -> Result<Box<dyn Apply<R, E>>> {
1815        Ok(Box::new(Dbg {
1816            spec: from[1].spec().clone(),
1817            dest: LogDest::Stderr,
1818            typ: Type::Bottom,
1819        }))
1820    }
1821}
1822
1823impl<R: Rt, E: UserEvent> Apply<R, E> for Dbg {
1824    fn update(
1825        &mut self,
1826        ctx: &mut ExecCtx<R, E>,
1827        from: &mut [Node<R, E>],
1828        event: &mut Event<E>,
1829    ) -> Option<Value> {
1830        if let Some(v) = from[0].update(ctx, event)
1831            && let Ok(d) = v.cast_to::<LogDest>()
1832        {
1833            self.dest = d;
1834        }
1835        from[1].update(ctx, event).map(|v| {
1836            let tv = TVal { env: &ctx.env, typ: &self.typ, v: &v };
1837            match self.dest {
1838                LogDest::Stderr => {
1839                    eprintln!("{} dbg({}): {}", self.spec.pos, self.spec, tv)
1840                }
1841                LogDest::Stdout => {
1842                    println!("{} dbg({}): {}", self.spec.pos, self.spec, tv)
1843                }
1844                LogDest::Log(level) => match level {
1845                    Level::Trace => {
1846                        log::trace!("{} dbg({}): {}", self.spec.pos, self.spec, tv)
1847                    }
1848                    Level::Debug => {
1849                        log::debug!("{} dbg({}): {}", self.spec.pos, self.spec, tv)
1850                    }
1851                    Level::Info => {
1852                        log::info!("{} dbg({}): {}", self.spec.pos, self.spec, tv)
1853                    }
1854                    Level::Warn => {
1855                        log::warn!("{} dbg({}): {}", self.spec.pos, self.spec, tv)
1856                    }
1857                    Level::Error => {
1858                        log::error!("{} dbg({}): {}", self.spec.pos, self.spec, tv)
1859                    }
1860                },
1861            };
1862            v
1863        })
1864    }
1865
1866    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
1867
1868    fn typecheck(
1869        &mut self,
1870        _ctx: &mut ExecCtx<R, E>,
1871        from: &mut [Node<R, E>],
1872    ) -> Result<()> {
1873        self.typ = from[1].typ().clone();
1874        Ok(())
1875    }
1876}
1877
1878#[derive(Debug)]
1879struct Log {
1880    scope: Scope,
1881    dest: LogDest,
1882}
1883
1884impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Log {
1885    const NAME: &str = "core_log";
1886    deftype!("fn(?#dest:Log, 'a) -> _");
1887
1888    fn init<'a, 'b, 'c>(
1889        _ctx: &'a mut ExecCtx<R, E>,
1890        _typ: &'a graphix_compiler::typ::FnType,
1891        scope: &'b Scope,
1892        _from: &'c [Node<R, E>],
1893        _top_id: ExprId,
1894    ) -> Result<Box<dyn Apply<R, E>>> {
1895        Ok(Box::new(Self { scope: scope.clone(), dest: LogDest::Stdout }))
1896    }
1897}
1898
1899impl<R: Rt, E: UserEvent> Apply<R, E> for Log {
1900    fn update(
1901        &mut self,
1902        ctx: &mut ExecCtx<R, E>,
1903        from: &mut [Node<R, E>],
1904        event: &mut Event<E>,
1905    ) -> Option<Value> {
1906        if let Some(v) = from[0].update(ctx, event)
1907            && let Ok(d) = v.cast_to::<LogDest>()
1908        {
1909            self.dest = d;
1910        }
1911        if let Some(v) = from[1].update(ctx, event) {
1912            let tv = TVal { env: &ctx.env, typ: from[1].typ(), v: &v };
1913            match self.dest {
1914                LogDest::Stdout => println!("{}: {}", self.scope.lexical, tv),
1915                LogDest::Stderr => eprintln!("{}: {}", self.scope.lexical, tv),
1916                LogDest::Log(lvl) => match lvl {
1917                    Level::Trace => log::trace!("{}: {}", self.scope.lexical, tv),
1918                    Level::Debug => log::debug!("{}: {}", self.scope.lexical, tv),
1919                    Level::Info => log::info!("{}: {}", self.scope.lexical, tv),
1920                    Level::Warn => log::warn!("{}: {}", self.scope.lexical, tv),
1921                    Level::Error => log::error!("{}: {}", self.scope.lexical, tv),
1922                },
1923            }
1924        }
1925        None
1926    }
1927
1928    fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
1929}
1930
1931macro_rules! printfn {
1932    ($type:ident, $name:literal, $print:ident, $eprint:ident) => {
1933        #[derive(Debug)]
1934        struct $type {
1935            dest: LogDest,
1936            buf: String,
1937        }
1938
1939        impl<R: Rt, E: UserEvent> BuiltIn<R, E> for $type {
1940            const NAME: &str = $name;
1941            deftype!("fn(?#dest:Log, 'a) -> _");
1942
1943            fn init<'a, 'b, 'c>(
1944                _ctx: &'a mut ExecCtx<R, E>,
1945                _typ: &'a graphix_compiler::typ::FnType,
1946                _scope: &'b Scope,
1947                _from: &'c [Node<R, E>],
1948                _top_id: ExprId,
1949            ) -> Result<Box<dyn Apply<R, E>>> {
1950                Ok(Box::new(Self { dest: LogDest::Stdout, buf: String::new() }))
1951            }
1952        }
1953
1954        impl<R: Rt, E: UserEvent> Apply<R, E> for $type {
1955            fn update(
1956                &mut self,
1957                ctx: &mut ExecCtx<R, E>,
1958                from: &mut [Node<R, E>],
1959                event: &mut Event<E>,
1960            ) -> Option<Value> {
1961                use std::fmt::Write;
1962                if let Some(v) = from[0].update(ctx, event)
1963                    && let Ok(d) = v.cast_to::<LogDest>()
1964                {
1965                    self.dest = d;
1966                }
1967                if let Some(v) = from[1].update(ctx, event) {
1968                    self.buf.clear();
1969                    match v {
1970                        Value::String(s) => write!(self.buf, "{s}"),
1971                        v => write!(
1972                            self.buf,
1973                            "{}",
1974                            TVal { env: &ctx.env, typ: &from[1].typ(), v: &v }
1975                        ),
1976                    }
1977                    .unwrap();
1978                    match self.dest {
1979                        LogDest::Stdout => $print!("{}", self.buf),
1980                        LogDest::Stderr => $eprint!("{}", self.buf),
1981                        LogDest::Log(lvl) => match lvl {
1982                            Level::Trace => log::trace!("{}", self.buf),
1983                            Level::Debug => log::debug!("{}", self.buf),
1984                            Level::Info => log::info!("{}", self.buf),
1985                            Level::Warn => log::warn!("{}", self.buf),
1986                            Level::Error => log::error!("{}", self.buf),
1987                        },
1988                    }
1989                }
1990                None
1991            }
1992
1993            fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
1994        }
1995    };
1996}
1997
1998printfn!(Print, "core_print", print, eprint);
1999printfn!(Println, "core_println", println, eprintln);
2000
2001// ── Package registration ───────────────────────────────────────────
2002
2003graphix_derive::defpackage! {
2004    builtins => [
2005        IsErr,
2006        FilterErr,
2007        ToError,
2008        Once,
2009        Take,
2010        Skip,
2011        All,
2012        Sum,
2013        Product,
2014        Divide,
2015        Min,
2016        Max,
2017        And,
2018        Or,
2019        Filter as Filter<GXRt<X>, X::UserEvent>,
2020        Queue,
2021        Hold,
2022        Seq,
2023        Throttle,
2024        Count,
2025        Mean,
2026        Uniq,
2027        Never,
2028        Dbg,
2029        Log,
2030        Print,
2031        Println,
2032    ],
2033}