1use anyhow::Result;
2use arcstr::ArcStr;
3use enumflags2::{bitflags, BitFlags};
4use fxhash::FxHashMap;
5use graphix_compiler::{
6 expr::ModuleResolver, typ::FnType, Apply, BuiltIn, BuiltInInitFn, Event, ExecCtx,
7 Node, Rt, UserEvent,
8};
9use netidx::{path::Path, subscriber::Value};
10use netidx_core::utils::Either;
11use std::{
12 fmt::Debug,
13 iter,
14 sync::{Arc, LazyLock},
15};
16
17mod array;
18mod core;
19mod net;
20mod rand;
21mod re;
22mod str;
23#[cfg(test)]
24mod test;
25mod time;
26
27#[macro_export]
28macro_rules! deftype {
29 ($scope:literal, $s:literal) => {
30 const TYP: ::std::sync::LazyLock<graphix_compiler::typ::FnType> =
31 ::std::sync::LazyLock::new(|| {
32 let scope =
33 graphix_compiler::expr::ModPath(::netidx::path::Path::from($scope));
34 graphix_compiler::expr::parser::parse_fn_type($s)
35 .expect("failed to parse fn type {s}")
36 .scope_refs(&scope)
37 });
38 };
39}
40
41#[macro_export]
42macro_rules! arity1 {
43 ($from:expr, $updates:expr) => {
44 match (&*$from, &*$updates) {
45 ([arg], [arg_up]) => (arg, arg_up),
46 (_, _) => unreachable!(),
47 }
48 };
49}
50
51#[macro_export]
52macro_rules! arity2 {
53 ($from:expr, $updates:expr) => {
54 match (&*$from, &*$updates) {
55 ([arg0, arg1], [arg0_up, arg1_up]) => ((arg0, arg1), (arg0_up, arg1_up)),
56 (_, _) => unreachable!(),
57 }
58 };
59}
60
61#[derive(Debug)]
62pub struct CachedVals(pub Box<[Option<Value>]>);
63
64impl CachedVals {
65 pub fn new<R: Rt, E: UserEvent>(from: &[Node<R, E>]) -> CachedVals {
66 CachedVals(from.into_iter().map(|_| None).collect())
67 }
68
69 pub fn clear(&mut self) {
70 for v in &mut self.0 {
71 *v = None
72 }
73 }
74
75 pub fn update<R: Rt, E: UserEvent>(
76 &mut self,
77 ctx: &mut ExecCtx<R, E>,
78 from: &mut [Node<R, E>],
79 event: &mut Event<E>,
80 ) -> bool {
81 from.into_iter().enumerate().fold(false, |res, (i, src)| {
82 match src.update(ctx, event) {
83 None => res,
84 v @ Some(_) => {
85 self.0[i] = v;
86 true
87 }
88 }
89 })
90 }
91
92 pub fn update_diff<R: Rt, E: UserEvent>(
95 &mut self,
96 up: &mut [bool],
97 ctx: &mut ExecCtx<R, E>,
98 from: &mut [Node<R, E>],
99 event: &mut Event<E>,
100 ) {
101 for (i, n) in from.iter_mut().enumerate() {
102 match n.update(ctx, event) {
103 None => (),
104 v => {
105 self.0[i] = v;
106 up[i] = true
107 }
108 }
109 }
110 }
111
112 pub fn flat_iter<'a>(&'a self) -> impl Iterator<Item = Option<Value>> + 'a {
113 self.0.iter().flat_map(|v| match v {
114 None => Either::Left(iter::once(None)),
115 Some(v) => Either::Right(v.clone().flatten().map(Some)),
116 })
117 }
118}
119
120pub trait EvalCached: Debug + Default + Send + Sync + 'static {
121 const NAME: &str;
122 const TYP: LazyLock<FnType>;
123
124 fn eval(&mut self, from: &CachedVals) -> Option<Value>;
125}
126
127#[derive(Debug)]
128pub struct CachedArgs<T: EvalCached> {
129 cached: CachedVals,
130 t: T,
131}
132
133impl<R: Rt, E: UserEvent, T: EvalCached> BuiltIn<R, E> for CachedArgs<T> {
134 const NAME: &str = T::NAME;
135 const TYP: LazyLock<FnType> = T::TYP;
136
137 fn init(_: &mut ExecCtx<R, E>) -> BuiltInInitFn<R, E> {
138 Arc::new(|_, _, _, from, _| {
139 let t = CachedArgs::<T> { cached: CachedVals::new(from), t: T::default() };
140 Ok(Box::new(t))
141 })
142 }
143}
144
145impl<R: Rt, E: UserEvent, T: EvalCached> Apply<R, E> for CachedArgs<T> {
146 fn update(
147 &mut self,
148 ctx: &mut ExecCtx<R, E>,
149 from: &mut [Node<R, E>],
150 event: &mut Event<E>,
151 ) -> Option<Value> {
152 if self.cached.update(ctx, from, event) {
153 self.t.eval(&self.cached)
154 } else {
155 None
156 }
157 }
158
159 fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
160 self.cached.clear()
161 }
162}
163
164#[bitflags]
165#[derive(Clone, Copy)]
166#[repr(u64)]
167pub enum Module {
168 Array,
169 NetAndTime,
170 Rand,
171 Re,
172 Str,
173}
174
175pub fn register<R: Rt, E: UserEvent>(
213 ctx: &mut ExecCtx<R, E>,
214 modules: BitFlags<Module>,
215) -> Result<(ArcStr, ModuleResolver)> {
216 let mut tbl = FxHashMap::default();
217 tbl.insert(Path::from("/core"), core::register(ctx)?);
218 let mut root = String::from("pub mod core;\nuse core;\n");
219 for module in modules {
220 match module {
221 Module::Array => {
222 root.push_str("pub mod array;\n");
223 tbl.insert(Path::from("/array"), array::register(ctx)?);
224 }
225 Module::NetAndTime => {
226 root.push_str("pub mod time;\n");
227 tbl.insert(Path::from("/time"), time::register(ctx)?);
228 root.push_str("pub mod net;\n");
229 tbl.insert(Path::from("/net"), net::register(ctx)?);
230 }
231 Module::Rand => {
232 root.push_str("pub mod rand;\n");
233 tbl.insert(Path::from("/rand"), rand::register(ctx)?);
234 }
235 Module::Re => {
236 root.push_str("pub mod re;\n");
237 tbl.insert(Path::from("/re"), re::register(ctx)?);
238 }
239 Module::Str => {
240 root.push_str("pub mod str;\n");
241 tbl.insert(Path::from("/str"), str::register(ctx)?);
242 }
243 }
244 }
245 root.pop();
246 root.pop();
247 Ok((ArcStr::from(root), ModuleResolver::VFS(tbl)))
248}