Skip to main content

graphix_package_array/
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 compact_str::format_compact;
7use graphix_compiler::{
8    expr::ExprId,
9    node::genn,
10    typ::{FnType, Type},
11    Apply, BindId, BuiltIn, Event, ExecCtx, LambdaId, Node, Refs, Rt, Scope,
12    TypecheckPhase, UserEvent,
13};
14use graphix_package_core::{
15    CachedArgs, CachedVals, EvalCached, FoldFn, FoldQ, MapFn, MapQ, Slot,
16};
17use graphix_rt::GXRt;
18use netidx::{publisher::Typ, subscriber::Value, utils::Either};
19use netidx_value::ValArray;
20use smallvec::{smallvec, SmallVec};
21use std::{collections::hash_map::Entry, collections::VecDeque, fmt::Debug, iter};
22use triomphe::Arc as TArc;
23
24#[derive(Debug, Default)]
25struct MapImpl;
26
27impl<R: Rt, E: UserEvent> MapFn<R, E> for MapImpl {
28    type Collection = ValArray;
29
30    const NAME: &str = "array_map";
31
32    fn finish(&mut self, slots: &[Slot<R, E>], _: &ValArray) -> Option<Value> {
33        Some(Value::Array(ValArray::from_iter_exact(
34            slots.iter().map(|s| s.cur.clone().unwrap()),
35        )))
36    }
37}
38
39type Map<R, E> = MapQ<R, E, MapImpl>;
40
41#[derive(Debug, Default)]
42struct FilterImpl;
43
44impl<R: Rt, E: UserEvent> MapFn<R, E> for FilterImpl {
45    type Collection = ValArray;
46
47    const NAME: &str = "array_filter";
48
49    fn finish(&mut self, slots: &[Slot<R, E>], a: &ValArray) -> Option<Value> {
50        Some(Value::Array(ValArray::from_iter(slots.iter().zip(a.iter()).filter_map(
51            |(p, v)| match p.cur {
52                Some(Value::Bool(true)) => Some(v.clone()),
53                _ => None,
54            },
55        ))))
56    }
57}
58
59type Filter<R, E> = MapQ<R, E, FilterImpl>;
60
61#[derive(Debug, Default)]
62struct FlatMapImpl;
63
64impl<R: Rt, E: UserEvent> MapFn<R, E> for FlatMapImpl {
65    type Collection = ValArray;
66
67    const NAME: &str = "array_flat_map";
68
69    fn finish(&mut self, slots: &[Slot<R, E>], _: &ValArray) -> Option<Value> {
70        Some(Value::Array(ValArray::from_iter(slots.iter().flat_map(|s| {
71            match s.cur.as_ref().unwrap() {
72                Value::Array(a) => Either::Left(a.clone().into_iter()),
73                v => Either::Right(iter::once(v.clone())),
74            }
75        }))))
76    }
77}
78
79type FlatMap<R, E> = MapQ<R, E, FlatMapImpl>;
80
81#[derive(Debug, Default)]
82struct FilterMapImpl;
83
84impl<R: Rt, E: UserEvent> MapFn<R, E> for FilterMapImpl {
85    type Collection = ValArray;
86
87    const NAME: &str = "array_filter_map";
88
89    fn finish(&mut self, slots: &[Slot<R, E>], _: &ValArray) -> Option<Value> {
90        Some(Value::Array(ValArray::from_iter(slots.iter().filter_map(|s| {
91            match s.cur.as_ref().unwrap() {
92                Value::Null => None,
93                v => Some(v.clone()),
94            }
95        }))))
96    }
97}
98
99type FilterMap<R, E> = MapQ<R, E, FilterMapImpl>;
100
101#[derive(Debug, Default)]
102struct FindImpl;
103
104impl<R: Rt, E: UserEvent> MapFn<R, E> for FindImpl {
105    type Collection = ValArray;
106
107    const NAME: &str = "array_find";
108
109    fn finish(&mut self, slots: &[Slot<R, E>], a: &ValArray) -> Option<Value> {
110        let r = slots
111            .iter()
112            .enumerate()
113            .find(|(_, s)| match s.cur.as_ref() {
114                Some(Value::Bool(true)) => true,
115                _ => false,
116            })
117            .map(|(i, _)| a[i].clone())
118            .unwrap_or(Value::Null);
119        Some(r)
120    }
121}
122
123type Find<R, E> = MapQ<R, E, FindImpl>;
124
125#[derive(Debug, Default)]
126struct FindMapImpl;
127
128impl<R: Rt, E: UserEvent> MapFn<R, E> for FindMapImpl {
129    type Collection = ValArray;
130
131    const NAME: &str = "array_find_map";
132
133    fn finish(&mut self, slots: &[Slot<R, E>], _: &ValArray) -> Option<Value> {
134        let r = slots
135            .iter()
136            .find_map(|s| match s.cur.as_ref().unwrap() {
137                Value::Null => None,
138                v => Some(v.clone()),
139            })
140            .unwrap_or(Value::Null);
141        Some(r)
142    }
143}
144
145type FindMap<R, E> = MapQ<R, E, FindMapImpl>;
146
147#[derive(Debug)]
148struct FoldImpl;
149
150impl<R: Rt, E: UserEvent> FoldFn<R, E> for FoldImpl {
151    type Collection = ValArray;
152
153    const NAME: &str = "array_fold";
154}
155
156type Fold<R, E> = FoldQ<R, E, FoldImpl>;
157
158#[derive(Debug, Default)]
159struct ConcatEv(SmallVec<[Value; 32]>);
160
161impl<R: Rt, E: UserEvent> EvalCached<R, E> for ConcatEv {
162    const NAME: &str = "array_concat";
163    const NEEDS_CALLSITE: bool = false;
164
165    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
166        let mut present = true;
167        for v in from.0.iter() {
168            match v {
169                Some(Value::Array(a)) => {
170                    for v in a.iter() {
171                        self.0.push(v.clone())
172                    }
173                }
174                Some(v) => self.0.push(v.clone()),
175                None => present = false,
176            }
177        }
178        if present {
179            let a = ValArray::from_iter_exact(self.0.drain(..));
180            Some(Value::Array(a))
181        } else {
182            self.0.clear();
183            None
184        }
185    }
186}
187
188type Concat = CachedArgs<ConcatEv>;
189
190#[derive(Debug, Default)]
191struct PushBackEv(SmallVec<[Value; 32]>);
192
193impl<R: Rt, E: UserEvent> EvalCached<R, E> for PushBackEv {
194    const NAME: &str = "array_push_back";
195    const NEEDS_CALLSITE: bool = false;
196
197    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
198        let mut present = true;
199        match &from.0[..] {
200            [Some(Value::Array(a)), tl @ ..] => {
201                self.0.extend(a.iter().map(|v| v.clone()));
202                for v in tl {
203                    match v {
204                        Some(v) => self.0.push(v.clone()),
205                        None => present = false,
206                    }
207                }
208            }
209            [] | [None, ..] | [Some(_), ..] => present = false,
210        }
211        if present {
212            let a = ValArray::from_iter_exact(self.0.drain(..));
213            Some(Value::Array(a))
214        } else {
215            self.0.clear();
216            None
217        }
218    }
219}
220
221type PushBack = CachedArgs<PushBackEv>;
222
223#[derive(Debug, Default)]
224struct PushFrontEv(SmallVec<[Value; 32]>);
225
226impl<R: Rt, E: UserEvent> EvalCached<R, E> for PushFrontEv {
227    const NAME: &str = "array_push_front";
228    const NEEDS_CALLSITE: bool = false;
229
230    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
231        let mut present = true;
232        match &from.0[..] {
233            [Some(Value::Array(a)), tl @ ..] => {
234                for v in tl {
235                    match v {
236                        Some(v) => self.0.push(v.clone()),
237                        None => present = false,
238                    }
239                }
240                self.0.extend(a.iter().map(|v| v.clone()));
241            }
242            [] | [None, ..] | [Some(_), ..] => present = false,
243        }
244        if present {
245            let a = ValArray::from_iter_exact(self.0.drain(..));
246            Some(Value::Array(a))
247        } else {
248            self.0.clear();
249            None
250        }
251    }
252}
253
254type PushFront = CachedArgs<PushFrontEv>;
255
256#[derive(Debug, Default)]
257struct WindowEv(SmallVec<[Value; 32]>);
258
259impl<R: Rt, E: UserEvent> EvalCached<R, E> for WindowEv {
260    const NAME: &str = "array_window";
261    const NEEDS_CALLSITE: bool = false;
262
263    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
264        let mut present = true;
265        match &from.0[..] {
266            [Some(Value::I64(window)), Some(Value::Array(a)), tl @ ..] => {
267                let window = *window as usize;
268                let total = a.len() + tl.len();
269                if total <= window {
270                    self.0.extend(a.iter().cloned());
271                    for v in tl {
272                        match v {
273                            Some(v) => self.0.push(v.clone()),
274                            None => present = false,
275                        }
276                    }
277                } else if a.len() >= (total - window) {
278                    self.0.extend(a[(total - window)..].iter().cloned());
279                    for v in tl {
280                        match v {
281                            Some(v) => self.0.push(v.clone()),
282                            None => present = false,
283                        }
284                    }
285                } else {
286                    for v in &tl[tl.len() - window..] {
287                        match v {
288                            Some(v) => self.0.push(v.clone()),
289                            None => present = false,
290                        }
291                    }
292                }
293            }
294            [] | [_] | [_, None, ..] | [None, _, ..] | [Some(_), Some(_), ..] => {
295                present = false
296            }
297        }
298        if present {
299            let a = ValArray::from_iter_exact(self.0.drain(..));
300            Some(Value::Array(a))
301        } else {
302            self.0.clear();
303            None
304        }
305    }
306}
307
308type Window = CachedArgs<WindowEv>;
309
310#[derive(Debug, Default)]
311struct LenEv;
312
313impl<R: Rt, E: UserEvent> EvalCached<R, E> for LenEv {
314    const NAME: &str = "array_len";
315    const NEEDS_CALLSITE: bool = false;
316
317    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
318        match &from.0[0] {
319            Some(Value::Array(a)) => Some(Value::I64(a.len() as i64)),
320            Some(_) | None => None,
321        }
322    }
323}
324
325type Len = CachedArgs<LenEv>;
326
327#[derive(Debug, Default)]
328struct FlattenEv(SmallVec<[Value; 32]>);
329
330impl<R: Rt, E: UserEvent> EvalCached<R, E> for FlattenEv {
331    const NAME: &str = "array_flatten";
332    const NEEDS_CALLSITE: bool = false;
333
334    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
335        match &from.0[0] {
336            Some(Value::Array(a)) => {
337                for v in a.iter() {
338                    match v {
339                        Value::Array(a) => self.0.extend(a.iter().map(|v| v.clone())),
340                        v => self.0.push(v.clone()),
341                    }
342                }
343                let a = ValArray::from_iter_exact(self.0.drain(..));
344                Some(Value::Array(a))
345            }
346            Some(_) | None => None,
347        }
348    }
349}
350
351type Flatten = CachedArgs<FlattenEv>;
352
353#[derive(Debug, Default)]
354struct SortEv(SmallVec<[Value; 32]>);
355
356impl<R: Rt, E: UserEvent> EvalCached<R, E> for SortEv {
357    const NAME: &str = "array_sort";
358    const NEEDS_CALLSITE: bool = false;
359
360    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
361        fn cn(v: &Value) -> Value {
362            v.clone().cast(Typ::F64).unwrap_or_else(|| v.clone())
363        }
364        match &from.0[..] {
365            [Some(Value::String(dir)), Some(Value::Bool(numeric)), Some(Value::Array(a))] => {
366                match &**dir {
367                    "Ascending" => {
368                        self.0.extend(a.iter().cloned());
369                        if *numeric {
370                            self.0.sort_by(|v0, v1| cn(v0).cmp(&cn(v1)))
371                        } else {
372                            self.0.sort();
373                        }
374                        Some(Value::Array(ValArray::from_iter_exact(self.0.drain(..))))
375                    }
376                    "Descending" => {
377                        self.0.extend(a.iter().cloned());
378                        if *numeric {
379                            self.0.sort_by(|a0, a1| cn(a1).cmp(&cn(a0)))
380                        } else {
381                            self.0.sort_by(|a0, a1| a1.cmp(a0));
382                        }
383                        Some(Value::Array(ValArray::from_iter_exact(self.0.drain(..))))
384                    }
385                    _ => None,
386                }
387            }
388            _ => None,
389        }
390    }
391}
392
393type Sort = CachedArgs<SortEv>;
394
395#[derive(Debug, Default)]
396struct EnumerateEv;
397
398impl<R: Rt, E: UserEvent> EvalCached<R, E> for EnumerateEv {
399    const NAME: &str = "array_enumerate";
400    const NEEDS_CALLSITE: bool = false;
401
402    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
403        if let Some(Value::Array(a)) = &from.0[0] {
404            let a = ValArray::from_iter_exact(
405                a.iter().enumerate().map(|(i, v)| (i, v.clone()).into()),
406            );
407            return Some(Value::Array(a));
408        }
409        None
410    }
411}
412
413type Enumerate = CachedArgs<EnumerateEv>;
414
415#[derive(Debug, Default)]
416struct ZipEv;
417
418impl<R: Rt, E: UserEvent> EvalCached<R, E> for ZipEv {
419    const NAME: &str = "array_zip";
420    const NEEDS_CALLSITE: bool = false;
421
422    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
423        match &from.0[..] {
424            [Some(Value::Array(a0)), Some(Value::Array(a1))] => {
425                Some(Value::Array(ValArray::from_iter_exact(
426                    a0.iter().cloned().zip(a1.iter().cloned()).map(|p| p.into()),
427                )))
428            }
429            _ => None,
430        }
431    }
432}
433
434type Zip = CachedArgs<ZipEv>;
435
436#[derive(Debug, Default)]
437struct UnzipEv {
438    t0: Vec<Value>,
439    t1: Vec<Value>,
440}
441
442impl<R: Rt, E: UserEvent> EvalCached<R, E> for UnzipEv {
443    const NAME: &str = "array_unzip";
444    const NEEDS_CALLSITE: bool = false;
445
446    fn eval(&mut self, _ctx: &mut ExecCtx<R, E>, from: &CachedVals) -> Option<Value> {
447        match &from.0[..] {
448            [Some(Value::Array(a))] => {
449                for v in a {
450                    if let Value::Array(a) = v {
451                        match &a[..] {
452                            [v0, v1] => {
453                                self.t0.push(v0.clone());
454                                self.t1.push(v1.clone());
455                            }
456                            _ => (),
457                        }
458                    }
459                }
460                let v0 = Value::Array(ValArray::from_iter_exact(self.t0.drain(..)));
461                let v1 = Value::Array(ValArray::from_iter_exact(self.t1.drain(..)));
462                Some(Value::Array(ValArray::from_iter_exact([v0, v1].into_iter())))
463            }
464            _ => None,
465        }
466    }
467}
468
469type Unzip = CachedArgs<UnzipEv>;
470
471#[derive(Debug)]
472struct Group<R: Rt, E: UserEvent> {
473    queue: VecDeque<Value>,
474    buf: SmallVec<[Value; 16]>,
475    pred: Node<R, E>,
476    ready: bool,
477    pid: BindId,
478    nid: BindId,
479    xid: BindId,
480}
481
482impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Group<R, E> {
483    const NAME: &str = "array_group";
484    const NEEDS_CALLSITE: bool = false;
485
486    fn init<'a, 'b, 'c, 'd>(
487        ctx: &'a mut ExecCtx<R, E>,
488        typ: &'a FnType,
489        resolved: Option<&'d FnType>,
490        scope: &'b graphix_compiler::Scope,
491        from: &'c [Node<R, E>],
492        top_id: ExprId,
493    ) -> Result<Box<dyn Apply<R, E>>> {
494        match from {
495            [_, _] => {
496                let typ = resolved.unwrap_or(typ);
497                let scope =
498                    scope.append(&format_compact!("fn{}", LambdaId::new().inner()));
499                let n_typ = Type::Primitive(Typ::I64.into());
500                let etyp = typ.args[0].typ.clone();
501                let mftyp = match &typ.args[1].typ {
502                    Type::Fn(ft) => ft.clone(),
503                    t => bail!("expected function not {t}"),
504                };
505                let (nid, n) =
506                    genn::bind(ctx, &scope.lexical, "n", n_typ.clone(), top_id);
507                let (xid, x) = genn::bind(ctx, &scope.lexical, "x", etyp.clone(), top_id);
508                let pid = BindId::new();
509                let fnode = genn::reference(ctx, pid, Type::Fn(mftyp.clone()), top_id);
510                let pred = genn::apply(fnode, scope, vec![n, x], &mftyp, top_id);
511                Ok(Box::new(Self {
512                    queue: VecDeque::new(),
513                    buf: smallvec![],
514                    pred,
515                    ready: true,
516                    pid,
517                    nid,
518                    xid,
519                }))
520            }
521            _ => bail!("expected two arguments"),
522        }
523    }
524}
525
526impl<R: Rt, E: UserEvent> Apply<R, E> for Group<R, E> {
527    fn update(
528        &mut self,
529        ctx: &mut ExecCtx<R, E>,
530        from: &mut [Node<R, E>],
531        event: &mut Event<E>,
532    ) -> Option<Value> {
533        macro_rules! set {
534            ($v:expr) => {{
535                self.ready = false;
536                self.buf.push($v.clone());
537                let len = Value::I64(self.buf.len() as i64);
538                ctx.cached.insert(self.nid, len.clone());
539                event.variables.insert(self.nid, len);
540                ctx.cached.insert(self.xid, $v.clone());
541                event.variables.insert(self.xid, $v);
542            }};
543        }
544        if let Some(v) = from[0].update(ctx, event) {
545            self.queue.push_back(v);
546        }
547        if let Some(v) = from[1].update(ctx, event) {
548            ctx.cached.insert(self.pid, v.clone());
549            event.variables.insert(self.pid, v);
550        }
551        if self.ready && self.queue.len() > 0 {
552            let v = self.queue.pop_front().unwrap();
553            set!(v);
554        }
555        loop {
556            match self.pred.update(ctx, event) {
557                None => break None,
558                Some(v) => {
559                    self.ready = true;
560                    match v {
561                        Value::Bool(true) => {
562                            break Some(Value::Array(ValArray::from_iter_exact(
563                                self.buf.drain(..),
564                            )))
565                        }
566                        _ => match self.queue.pop_front() {
567                            None => break None,
568                            Some(v) => set!(v),
569                        },
570                    }
571                }
572            }
573        }
574    }
575
576    fn typecheck(
577        &mut self,
578        ctx: &mut ExecCtx<R, E>,
579        _from: &mut [Node<R, E>],
580        _phase: TypecheckPhase<'_>,
581    ) -> anyhow::Result<()> {
582        self.pred.typecheck(ctx)?;
583        Ok(())
584    }
585
586    fn refs(&self, refs: &mut Refs) {
587        self.pred.refs(refs)
588    }
589
590    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
591        ctx.cached.remove(&self.nid);
592        ctx.cached.remove(&self.pid);
593        ctx.cached.remove(&self.xid);
594        self.pred.delete(ctx);
595    }
596
597    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
598        self.pred.sleep(ctx);
599    }
600}
601
602#[derive(Debug)]
603struct Iter(BindId, ExprId);
604
605impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Iter {
606    const NAME: &str = "array_iter";
607    const NEEDS_CALLSITE: bool = false;
608
609    fn init<'a, 'b, 'c, 'd>(
610        ctx: &'a mut ExecCtx<R, E>,
611        _typ: &'a FnType,
612        _resolved: Option<&'d FnType>,
613        _scope: &'b graphix_compiler::Scope,
614        _from: &'c [Node<R, E>],
615        top_id: ExprId,
616    ) -> Result<Box<dyn Apply<R, E>>> {
617        let id = BindId::new();
618        ctx.rt.ref_var(id, top_id);
619        Ok(Box::new(Iter(id, top_id)))
620    }
621}
622
623impl<R: Rt, E: UserEvent> Apply<R, E> for Iter {
624    fn update(
625        &mut self,
626        ctx: &mut ExecCtx<R, E>,
627        from: &mut [Node<R, E>],
628        event: &mut Event<E>,
629    ) -> Option<Value> {
630        if let Some(Value::Array(a)) = from[0].update(ctx, event) {
631            for v in a.iter() {
632                ctx.rt.set_var(self.0, v.clone());
633            }
634        }
635        event.variables.get(&self.0).map(|v| v.clone())
636    }
637
638    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
639        ctx.rt.unref_var(self.0, self.1)
640    }
641
642    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
643        ctx.rt.unref_var(self.0, self.1);
644        self.0 = BindId::new();
645        ctx.rt.ref_var(self.0, self.1);
646    }
647}
648
649#[derive(Debug)]
650struct IterQ {
651    triggered: usize,
652    queue: VecDeque<(usize, ValArray)>,
653    id: BindId,
654    top_id: ExprId,
655}
656
657impl<R: Rt, E: UserEvent> BuiltIn<R, E> for IterQ {
658    const NAME: &str = "array_iterq";
659    const NEEDS_CALLSITE: bool = false;
660
661    fn init<'a, 'b, 'c, 'd>(
662        ctx: &'a mut ExecCtx<R, E>,
663        _typ: &'a FnType,
664        _resolved: Option<&'d FnType>,
665        _scope: &'b graphix_compiler::Scope,
666        _from: &'c [Node<R, E>],
667        top_id: ExprId,
668    ) -> Result<Box<dyn Apply<R, E>>> {
669        let id = BindId::new();
670        ctx.rt.ref_var(id, top_id);
671        Ok(Box::new(IterQ { triggered: 0, queue: VecDeque::new(), id, top_id }))
672    }
673}
674
675impl<R: Rt, E: UserEvent> Apply<R, E> for IterQ {
676    fn update(
677        &mut self,
678        ctx: &mut ExecCtx<R, E>,
679        from: &mut [Node<R, E>],
680        event: &mut Event<E>,
681    ) -> Option<Value> {
682        if from[0].update(ctx, event).is_some() {
683            self.triggered += 1;
684        }
685        if let Some(Value::Array(a)) = from[1].update(ctx, event) {
686            if a.len() > 0 {
687                self.queue.push_back((0, a));
688            }
689        }
690        while self.triggered > 0 && self.queue.len() > 0 {
691            let (i, a) = self.queue.front_mut().unwrap();
692            while self.triggered > 0 && *i < a.len() {
693                ctx.rt.set_var(self.id, a[*i].clone());
694                *i += 1;
695                self.triggered -= 1;
696            }
697            if *i == a.len() {
698                self.queue.pop_front();
699            }
700        }
701        event.variables.get(&self.id).cloned()
702    }
703
704    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
705        ctx.rt.unref_var(self.id, self.top_id)
706    }
707
708    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
709        ctx.rt.unref_var(self.id, self.top_id);
710        self.id = BindId::new();
711        self.queue.clear();
712        self.triggered = 0;
713    }
714}
715
716#[derive(Debug)]
717struct Init<R: Rt, E: UserEvent> {
718    scope: Scope,
719    fid: BindId,
720    top_id: ExprId,
721    mftyp: TArc<FnType>,
722    slots: Vec<Slot<R, E>>,
723}
724
725impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Init<R, E> {
726    const NAME: &str = "array_init";
727    const NEEDS_CALLSITE: bool = false;
728
729    fn init<'a, 'b, 'c, 'd>(
730        _ctx: &'a mut ExecCtx<R, E>,
731        typ: &'a FnType,
732        resolved: Option<&'c FnType>,
733        scope: &'b Scope,
734        from: &'c [Node<R, E>],
735        top_id: ExprId,
736    ) -> Result<Box<dyn Apply<R, E>>> {
737        match from {
738            [_, _] => {
739                let typ = resolved.unwrap_or(typ);
740                Ok(Box::new(Self {
741                    scope: scope
742                        .append(&format_compact!("fn{}", LambdaId::new().inner())),
743                    fid: BindId::new(),
744                    top_id,
745                    mftyp: match &typ.args[1].typ {
746                        Type::Fn(ft) => ft.clone(),
747                        t => bail!("expected a function not {t}"),
748                    },
749                    slots: vec![],
750                }))
751            }
752            _ => bail!("expected two arguments"),
753        }
754    }
755}
756
757impl<R: Rt, E: UserEvent> Apply<R, E> for Init<R, E> {
758    fn update(
759        &mut self,
760        ctx: &mut ExecCtx<R, E>,
761        from: &mut [Node<R, E>],
762        event: &mut Event<E>,
763    ) -> Option<Value> {
764        let slen = self.slots.len();
765        if let Some(v) = from[1].update(ctx, event) {
766            ctx.cached.insert(self.fid, v.clone());
767            event.variables.insert(self.fid, v);
768        }
769        let (size_fired, resized) = match from[0].update(ctx, event) {
770            Some(Value::I64(n)) => {
771                let n = n.max(0) as usize;
772                if n == slen {
773                    (true, false)
774                } else if n < slen {
775                    while self.slots.len() > n {
776                        if let Some(mut s) = self.slots.pop() {
777                            s.delete(ctx)
778                        }
779                    }
780                    (true, true)
781                } else {
782                    let i_typ = Type::Primitive(Typ::I64.into());
783                    while self.slots.len() < n {
784                        let i = self.slots.len();
785                        let (id, node) = genn::bind(
786                            ctx,
787                            &self.scope.lexical,
788                            "i",
789                            i_typ.clone(),
790                            self.top_id,
791                        );
792                        ctx.cached.insert(id, Value::I64(i as i64));
793                        let fnode = genn::reference(
794                            ctx,
795                            self.fid,
796                            Type::Fn(self.mftyp.clone()),
797                            self.top_id,
798                        );
799                        let pred = genn::apply(
800                            fnode,
801                            self.scope.clone(),
802                            vec![node],
803                            &self.mftyp,
804                            self.top_id,
805                        );
806                        self.slots.push(Slot { id, pred, cur: None });
807                    }
808                    (true, true)
809                }
810            }
811            _ => (false, false),
812        };
813        // set index bindings for new slots
814        if resized && self.slots.len() > slen {
815            for i in slen..self.slots.len() {
816                let id = self.slots[i].id;
817                event.variables.insert(id, Value::I64(i as i64));
818            }
819        }
820        if size_fired && self.slots.is_empty() {
821            return Some(Value::Array(ValArray::default()));
822        }
823        let init = event.init;
824        let mut up = resized;
825        for (i, s) in self.slots.iter_mut().enumerate() {
826            if i == slen {
827                event.init = true;
828                if let Entry::Vacant(e) = event.variables.entry(self.fid)
829                    && let Some(v) = ctx.cached.get(&self.fid)
830                {
831                    e.insert(v.clone());
832                }
833            }
834            if let Some(v) = s.pred.update(ctx, event) {
835                s.cur = Some(v);
836                up = true;
837            }
838        }
839        event.init = init;
840        if up && self.slots.iter().all(|s| s.cur.is_some()) {
841            Some(Value::Array(ValArray::from_iter_exact(
842                self.slots.iter().map(|s| s.cur.clone().unwrap()),
843            )))
844        } else {
845            None
846        }
847    }
848
849    fn typecheck(
850        &mut self,
851        ctx: &mut ExecCtx<R, E>,
852        _from: &mut [Node<R, E>],
853        _phase: TypecheckPhase<'_>,
854    ) -> anyhow::Result<()> {
855        let i_typ = Type::Primitive(Typ::I64.into());
856        let (_, node) = genn::bind(ctx, &self.scope.lexical, "i", i_typ, self.top_id);
857        let ft = self.mftyp.clone();
858        let fnode = genn::reference(ctx, self.fid, Type::Fn(ft.clone()), self.top_id);
859        let mut node =
860            genn::apply(fnode, self.scope.clone(), vec![node], &ft, self.top_id);
861        let r = node.typecheck(ctx);
862        node.delete(ctx);
863        r?;
864        Ok(())
865    }
866
867    fn refs(&self, refs: &mut Refs) {
868        for s in &self.slots {
869            s.pred.refs(refs)
870        }
871    }
872
873    fn delete(&mut self, ctx: &mut ExecCtx<R, E>) {
874        ctx.cached.remove(&self.fid);
875        for sl in &mut self.slots {
876            sl.delete(ctx)
877        }
878    }
879
880    fn sleep(&mut self, ctx: &mut ExecCtx<R, E>) {
881        for sl in &mut self.slots {
882            sl.cur = None;
883            sl.pred.sleep(ctx);
884        }
885    }
886}
887
888graphix_derive::defpackage! {
889    builtins => [
890        Concat,
891        Filter as Filter<GXRt<X>, X::UserEvent>,
892        FilterMap as FilterMap<GXRt<X>, X::UserEvent>,
893        Find as Find<GXRt<X>, X::UserEvent>,
894        FindMap as FindMap<GXRt<X>, X::UserEvent>,
895        FlatMap as FlatMap<GXRt<X>, X::UserEvent>,
896        Enumerate,
897        Zip,
898        Unzip,
899        Flatten,
900        Fold as Fold<GXRt<X>, X::UserEvent>,
901        Group as Group<GXRt<X>, X::UserEvent>,
902        Init as Init<GXRt<X>, X::UserEvent>,
903        Iter,
904        IterQ,
905        Len,
906        Map as Map<GXRt<X>, X::UserEvent>,
907        PushBack,
908        PushFront,
909        Sort,
910        Window,
911    ],
912}