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)]
5#[macro_use]
6extern crate netidx_core;
7#[macro_use]
8extern crate combine;
9#[macro_use]
10extern crate serde_derive;
11
12pub mod env;
13pub mod expr;
14pub mod node;
15pub mod typ;
16
17use crate::{
18 env::Env,
19 expr::{ExprId, ModPath},
20 node::lambda::LambdaDef,
21 typ::{FnType, Type},
22};
23use anyhow::{bail, Result};
24use arcstr::ArcStr;
25use enumflags2::{bitflags, BitFlags};
26use expr::Expr;
27use futures::channel::mpsc;
28use fxhash::{FxHashMap, FxHashSet};
29use log::info;
30use netidx::{
31 path::Path,
32 publisher::{Id, Val, WriteRequest},
33 subscriber::{self, Dval, SubId, UpdatesFlags, Value},
34};
35use netidx_protocols::rpc::server::{ArgSpec, RpcCall};
36use netidx_value::{abstract_type::AbstractWrapper, Abstract};
37use node::compiler;
38use parking_lot::{Mutex, RwLock};
39use poolshark::{
40 global::{GPooled, Pool},
41 local::LPooled,
42};
43use std::{
44 any::{Any, TypeId},
45 cell::Cell,
46 collections::{
47 hash_map::{self, Entry},
48 HashMap,
49 },
50 fmt::Debug,
51 mem,
52 sync::{
53 self,
54 atomic::{AtomicBool, Ordering},
55 LazyLock,
56 },
57 time::Duration,
58};
59use tokio::{task, time::Instant};
60use triomphe::Arc;
61use uuid::Uuid;
62
63#[derive(Debug, Clone, Copy)]
64#[bitflags]
65#[repr(u64)]
66pub enum CFlag {
67 WarnUnhandled,
68 WarnUnhandledArith,
69 WarnUnused,
70 WarningsAreErrors,
71}
72
73#[allow(dead_code)]
74static TRACE: AtomicBool = AtomicBool::new(false);
75
76#[allow(dead_code)]
77fn set_trace(b: bool) {
78 TRACE.store(b, Ordering::Relaxed)
79}
80
81#[allow(dead_code)]
82fn with_trace<F: FnOnce() -> Result<R>, R>(enable: bool, spec: &Expr, f: F) -> Result<R> {
83 let set = if enable {
84 eprintln!("trace enabled at {}, spec: {}", spec.pos, spec);
85 let prev = trace();
86 set_trace(true);
87 !prev
88 } else {
89 false
90 };
91 let r = match f() {
92 Err(e) => {
93 eprintln!("traced at {} failed with {e:?}", spec.pos);
94 Err(e)
95 }
96 r => r,
97 };
98 if set {
99 eprintln!("trace disabled at {}", spec.pos);
100 set_trace(false)
101 }
102 r
103}
104
105#[allow(dead_code)]
106fn trace() -> bool {
107 TRACE.load(Ordering::Relaxed)
108}
109
110#[macro_export]
111macro_rules! tdbg {
112 ($e:expr) => {
113 if $crate::trace() {
114 dbg!($e)
115 } else {
116 $e
117 }
118 };
119}
120
121#[macro_export]
122macro_rules! err {
123 ($tag:expr, $err:literal) => {{
124 let e: Value = ($tag.clone(), ::arcstr::literal!($err)).into();
125 Value::Error(::triomphe::Arc::new(e))
126 }};
127}
128
129#[macro_export]
130macro_rules! errf {
131 ($tag:expr, $fmt:expr, $($args:expr),*) => {{
132 let msg: ArcStr = ::compact_str::format_compact!($fmt, $($args),*).as_str().into();
133 let e: Value = ($tag.clone(), msg).into();
134 Value::Error(::triomphe::Arc::new(e))
135 }};
136 ($tag:expr, $fmt:expr) => {{
137 let msg: ArcStr = ::compact_str::format_compact!($fmt).as_str().into();
138 let e: Value = ($tag.clone(), msg).into();
139 Value::Error(::triomphe::Arc::new(e))
140 }};
141}
142
143#[macro_export]
144macro_rules! defetyp {
145 ($name:ident, $tag_name:ident, $tag:literal, $typ:expr) => {
146 static $tag_name: ArcStr = ::arcstr::literal!($tag);
147 static $name: ::std::sync::LazyLock<$crate::typ::Type> =
148 ::std::sync::LazyLock::new(|| {
149 let scope = $crate::expr::ModPath::root();
150 $crate::expr::parser::parse_type(&format!($typ, $tag))
151 .expect("failed to parse type")
152 .scope_refs(&scope)
153 });
154 };
155}
156
157defetyp!(CAST_ERR, CAST_ERR_TAG, "InvalidCast", "Error<`{}(string)>");
158
159atomic_id!(LambdaId);
160
161impl From<u64> for LambdaId {
162 fn from(v: u64) -> Self {
163 LambdaId(v)
164 }
165}
166
167atomic_id!(BindId);
168
169impl From<u64> for BindId {
170 fn from(v: u64) -> Self {
171 BindId(v)
172 }
173}
174
175impl TryFrom<Value> for BindId {
176 type Error = anyhow::Error;
177
178 fn try_from(value: Value) -> Result<Self> {
179 match value {
180 Value::U64(id) => Ok(BindId(id)),
181 v => bail!("invalid bind id {v}"),
182 }
183 }
184}
185
186pub trait UserEvent: Clone + Debug + Any {
187 fn clear(&mut self);
188}
189
190pub trait CustomBuiltinType: Debug + Any + Send + Sync {}
191
192impl CustomBuiltinType for Value {}
193impl CustomBuiltinType for Option<Value> {}
194
195#[derive(Debug, Clone)]
196pub struct NoUserEvent;
197
198impl UserEvent for NoUserEvent {
199 fn clear(&mut self) {}
200}
201
202#[derive(Debug, Clone, Copy)]
203#[bitflags]
204#[repr(u64)]
205pub enum PrintFlag {
206 DerefTVars,
209 ReplacePrims,
212 NoSource,
214 NoParents,
216}
217
218thread_local! {
219 static PRINT_FLAGS: Cell<BitFlags<PrintFlag>> = Cell::new(PrintFlag::ReplacePrims.into());
220}
221
222pub static CBATCH_POOL: LazyLock<Pool<Vec<(BindId, Box<dyn CustomBuiltinType>)>>> =
224 LazyLock::new(|| Pool::new(10000, 1000));
225
226pub fn format_with_flags<G: Into<BitFlags<PrintFlag>>, R, F: FnOnce() -> R>(
230 flags: G,
231 f: F,
232) -> R {
233 let prev = PRINT_FLAGS.replace(flags.into());
234 let res = f();
235 PRINT_FLAGS.set(prev);
236 res
237}
238
239#[derive(Debug)]
245pub struct Event<E: UserEvent> {
246 pub init: bool,
247 pub variables: FxHashMap<BindId, Value>,
248 pub netidx: FxHashMap<SubId, subscriber::Event>,
249 pub writes: FxHashMap<Id, WriteRequest>,
250 pub rpc_calls: FxHashMap<BindId, RpcCall>,
251 pub custom: FxHashMap<BindId, Box<dyn CustomBuiltinType>>,
252 pub user: E,
253}
254
255impl<E: UserEvent> Event<E> {
256 pub fn new(user: E) -> Self {
257 Event {
258 init: false,
259 variables: HashMap::default(),
260 netidx: HashMap::default(),
261 writes: HashMap::default(),
262 rpc_calls: HashMap::default(),
263 custom: HashMap::default(),
264 user,
265 }
266 }
267
268 pub fn clear(&mut self) {
269 let Self { init, variables, netidx, rpc_calls, writes, custom, user } = self;
270 *init = false;
271 variables.clear();
272 netidx.clear();
273 rpc_calls.clear();
274 custom.clear();
275 writes.clear();
276 user.clear();
277 }
278}
279
280#[derive(Debug, Clone, Default)]
281pub struct Refs {
282 refed: LPooled<FxHashSet<BindId>>,
283 bound: LPooled<FxHashSet<BindId>>,
284}
285
286impl Refs {
287 pub fn clear(&mut self) {
288 self.refed.clear();
289 self.bound.clear();
290 }
291
292 pub fn with_external_refs(&self, mut f: impl FnMut(BindId)) {
293 for id in &*self.refed {
294 if !self.bound.contains(id) {
295 f(*id);
296 }
297 }
298 }
299}
300
301pub type Node<R, E> = Box<dyn Update<R, E>>;
302
303pub type InitFn<R, E> = sync::Arc<
304 dyn for<'a, 'b, 'c> Fn(
305 &'a Scope,
306 &'b mut ExecCtx<R, E>,
307 &'c mut [Node<R, E>],
308 ExprId,
309 bool,
310 ) -> Result<Box<dyn Apply<R, E>>>
311 + Send
312 + Sync
313 + 'static,
314>;
315
316pub trait Apply<R: Rt, E: UserEvent>: Debug + Send + Sync + Any {
321 fn update(
322 &mut self,
323 ctx: &mut ExecCtx<R, E>,
324 from: &mut [Node<R, E>],
325 event: &mut Event<E>,
326 ) -> Option<Value>;
327
328 fn delete(&mut self, _ctx: &mut ExecCtx<R, E>) {
331 ()
332 }
333
334 fn typecheck(
337 &mut self,
338 _ctx: &mut ExecCtx<R, E>,
339 _from: &mut [Node<R, E>],
340 ) -> Result<()> {
341 Ok(())
342 }
343
344 fn typ(&self) -> Arc<FnType> {
347 static EMPTY: LazyLock<Arc<FnType>> = LazyLock::new(|| {
348 Arc::new(FnType {
349 args: Arc::from_iter([]),
350 constraints: Arc::new(RwLock::new(LPooled::take())),
351 rtype: Type::Bottom,
352 throws: Type::Bottom,
353 vargs: None,
354 explicit_throws: false,
355 })
356 });
357 Arc::clone(&*EMPTY)
358 }
359
360 fn refs<'a>(&self, _refs: &mut Refs) {}
364
365 fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>);
368}
369
370pub trait Update<R: Rt, E: UserEvent>: Debug + Send + Sync + Any + 'static {
374 fn update(&mut self, ctx: &mut ExecCtx<R, E>, event: &mut Event<E>) -> Option<Value>;
377
378 fn delete(&mut self, ctx: &mut ExecCtx<R, E>);
380
381 fn typecheck(&mut self, ctx: &mut ExecCtx<R, E>) -> Result<()>;
383
384 fn typ(&self) -> &Type;
386
387 fn refs(&self, refs: &mut Refs);
390
391 fn spec(&self) -> &Expr;
393
394 fn sleep(&mut self, ctx: &mut ExecCtx<R, E>);
396}
397
398pub type BuiltInInitFn<R, E> = for<'a, 'b, 'c> fn(
399 &'a mut ExecCtx<R, E>,
400 &'a FnType,
401 &'b Scope,
402 &'c [Node<R, E>],
403 ExprId,
404) -> Result<Box<dyn Apply<R, E>>>;
405
406pub trait BuiltIn<R: Rt, E: UserEvent> {
410 const NAME: &str;
411 const TYP: LazyLock<FnType>;
412
413 fn init<'a, 'b, 'c>(
414 ctx: &'a mut ExecCtx<R, E>,
415 typ: &'a FnType,
416 scope: &'b Scope,
417 from: &'c [Node<R, E>],
418 top_id: ExprId,
419 ) -> Result<Box<dyn Apply<R, E>>>;
420}
421
422pub trait Abortable {
423 fn abort(&self);
424}
425
426impl Abortable for task::AbortHandle {
427 fn abort(&self) {
428 task::AbortHandle::abort(self)
429 }
430}
431
432pub trait Rt: Debug + Any {
433 type AbortHandle: Abortable;
434
435 fn clear(&mut self);
436
437 fn subscribe(&mut self, flags: UpdatesFlags, path: Path, ref_by: ExprId) -> Dval;
442
443 fn unsubscribe(&mut self, path: Path, dv: Dval, ref_by: ExprId);
445
446 fn list(&mut self, id: BindId, path: Path);
451
452 fn list_table(&mut self, id: BindId, path: Path);
455
456 fn stop_list(&mut self, id: BindId);
459
460 fn publish(&mut self, path: Path, value: Value, ref_by: ExprId) -> Result<Val>;
464
465 fn update(&mut self, id: &Val, value: Value);
467
468 fn unpublish(&mut self, id: Val, ref_by: ExprId);
470
471 fn ref_var(&mut self, id: BindId, ref_by: ExprId);
481 fn unref_var(&mut self, id: BindId, ref_by: ExprId);
482
483 fn set_var(&mut self, id: BindId, value: Value);
494
495 fn notify_set(&mut self, id: BindId);
502
503 fn call_rpc(&mut self, name: Path, args: Vec<(ArcStr, Value)>, id: BindId);
509
510 fn publish_rpc(
519 &mut self,
520 name: Path,
521 doc: Value,
522 spec: Vec<ArgSpec>,
523 id: BindId,
524 ) -> Result<()>;
525
526 fn unpublish_rpc(&mut self, name: Path);
528
529 fn set_timer(&mut self, id: BindId, timeout: Duration);
533
534 fn spawn<F: Future<Output = (BindId, Box<dyn CustomBuiltinType>)> + Send + 'static>(
542 &mut self,
543 f: F,
544 ) -> Self::AbortHandle;
545
546 fn spawn_var<F: Future<Output = (BindId, Value)> + Send + 'static>(
554 &mut self,
555 f: F,
556 ) -> Self::AbortHandle;
557
558 fn watch(
563 &mut self,
564 s: mpsc::Receiver<GPooled<Vec<(BindId, Box<dyn CustomBuiltinType>)>>>,
565 );
566
567 fn watch_var(&mut self, s: mpsc::Receiver<GPooled<Vec<(BindId, Value)>>>);
572}
573
574#[derive(Default)]
575pub struct LibState(FxHashMap<TypeId, Box<dyn Any + Send + Sync>>);
576
577impl LibState {
578 pub fn get_or_default<T>(&mut self) -> &mut T
584 where
585 T: Default + Any + Send + Sync,
586 {
587 self.0
588 .entry(TypeId::of::<T>())
589 .or_insert_with(|| Box::new(T::default()) as Box<dyn Any + Send + Sync>)
590 .downcast_mut::<T>()
591 .unwrap()
592 }
593
594 pub fn get_or_else<T, F>(&mut self, f: F) -> &mut T
600 where
601 T: Any + Send + Sync,
602 F: FnOnce() -> T,
603 {
604 self.0
605 .entry(TypeId::of::<T>())
606 .or_insert_with(|| Box::new(f()) as Box<dyn Any + Send + Sync>)
607 .downcast_mut::<T>()
608 .unwrap()
609 }
610
611 pub fn entry<'a, T>(
612 &'a mut self,
613 ) -> hash_map::Entry<'a, TypeId, Box<dyn Any + Send + Sync>>
614 where
615 T: Any + Send + Sync,
616 {
617 self.0.entry(TypeId::of::<T>())
618 }
619
620 pub fn contains<T>(&self) -> bool
622 where
623 T: Any + Send + Sync,
624 {
625 self.0.contains_key(&TypeId::of::<T>())
626 }
627
628 pub fn get<T>(&mut self) -> Option<&T>
633 where
634 T: Any + Send + Sync,
635 {
636 self.0.get(&TypeId::of::<T>()).map(|t| t.downcast_ref::<T>().unwrap())
637 }
638
639 pub fn get_mut<T>(&mut self) -> Option<&mut T>
644 where
645 T: Any + Send + Sync,
646 {
647 self.0.get_mut(&TypeId::of::<T>()).map(|t| t.downcast_mut::<T>().unwrap())
648 }
649
650 pub fn set<T>(&mut self, t: T) -> Option<Box<T>>
654 where
655 T: Any + Send + Sync,
656 {
657 self.0
658 .insert(TypeId::of::<T>(), Box::new(t) as Box<dyn Any + Send + Sync>)
659 .map(|t| t.downcast::<T>().unwrap())
660 }
661
662 pub fn remove<T>(&mut self) -> Option<Box<T>>
664 where
665 T: Any + Send + Sync,
666 {
667 self.0.remove(&TypeId::of::<T>()).map(|t| t.downcast::<T>().unwrap())
668 }
669}
670
671#[derive(Default)]
682pub struct AbstractTypeRegistry {
683 by_tid: FxHashMap<TypeId, Uuid>,
684 by_uuid: FxHashMap<Uuid, &'static str>,
685}
686
687impl AbstractTypeRegistry {
688 fn with<V, F: FnMut(&mut AbstractTypeRegistry) -> V>(mut f: F) -> V {
689 static REG: LazyLock<Mutex<AbstractTypeRegistry>> =
690 LazyLock::new(|| Mutex::new(AbstractTypeRegistry::default()));
691 let mut g = REG.lock();
692 f(&mut *g)
693 }
694
695 pub(crate) fn uuid<T: Any>(tag: &'static str) -> Uuid {
697 Self::with(|rg| {
698 *rg.by_tid.entry(TypeId::of::<T>()).or_insert_with(|| {
699 let id = Uuid::new_v4();
700 rg.by_uuid.insert(id, tag);
701 id
702 })
703 })
704 }
705
706 pub fn tag(a: &Abstract) -> Option<&'static str> {
708 Self::with(|rg| rg.by_uuid.get(&a.id()).map(|r| *r))
709 }
710
711 pub fn is_a(a: &Abstract, tag: &str) -> bool {
713 match Self::tag(a) {
714 Some(t) => t == tag,
715 None => false,
716 }
717 }
718}
719
720pub struct ExecCtx<R: Rt, E: UserEvent> {
721 lambdawrap: AbstractWrapper<LambdaDef<R, E>>,
723 builtins: FxHashMap<&'static str, (FnType, BuiltInInitFn<R, E>)>,
725 builtins_allowed: bool,
728 tags: FxHashSet<ArcStr>,
730 pub libstate: LibState,
732 pub env: Env,
734 pub cached: FxHashMap<BindId, Value>,
736 pub rt: R,
738}
739
740impl<R: Rt, E: UserEvent> ExecCtx<R, E> {
741 pub fn clear(&mut self) {
742 self.env.clear();
743 self.rt.clear();
744 }
745
746 pub fn new(user: R) -> Result<Self> {
755 let id = AbstractTypeRegistry::uuid::<LambdaDef<R, E>>("lambda");
756 Ok(Self {
757 lambdawrap: Abstract::register(id)?,
758 env: Env::default(),
759 builtins: FxHashMap::default(),
760 builtins_allowed: true,
761 libstate: LibState::default(),
762 tags: FxHashSet::default(),
763 cached: HashMap::default(),
764 rt: user,
765 })
766 }
767
768 pub fn register_builtin<T: BuiltIn<R, E>>(&mut self) -> Result<()> {
769 match self.builtins.entry(T::NAME) {
770 Entry::Vacant(e) => {
771 e.insert((T::TYP.clone(), T::init));
772 }
773 Entry::Occupied(_) => bail!("builtin {} is already registered", T::NAME),
774 }
775 Ok(())
776 }
777
778 pub fn set_var(&mut self, id: BindId, v: Value) {
782 self.cached.insert(id, v.clone());
783 self.rt.set_var(id, v)
784 }
785
786 fn tag(&mut self, s: &ArcStr) -> ArcStr {
787 match self.tags.get(s) {
788 Some(s) => s.clone(),
789 None => {
790 self.tags.insert(s.clone());
791 s.clone()
792 }
793 }
794 }
795
796 pub fn with_restored<T, F: FnOnce(&mut Self) -> T>(&mut self, env: Env, f: F) -> T {
800 let snap = self.env.restore_lexical_env(env);
801 let orig = mem::replace(&mut self.env, snap);
802 let r = f(self);
803 self.env = self.env.restore_lexical_env(orig);
804 r
805 }
806
807 pub fn with_restored_mut<T, F: FnOnce(&mut Self) -> T>(
813 &mut self,
814 env: &mut Env,
815 f: F,
816 ) -> T {
817 let snap = self.env.restore_lexical_env_mut(env);
818 let orig = mem::replace(&mut self.env, snap);
819 let r = f(self);
820 *env = self.env.clone();
821 self.env = self.env.restore_lexical_env(orig);
822 r
823 }
824}
825
826#[derive(Debug, Clone)]
827pub struct Scope {
828 pub lexical: ModPath,
829 pub dynamic: ModPath,
830}
831
832impl Scope {
833 pub fn append<S: AsRef<str> + ?Sized>(&self, s: &S) -> Self {
834 Self {
835 lexical: ModPath(self.lexical.append(s)),
836 dynamic: ModPath(self.dynamic.append(s)),
837 }
838 }
839
840 pub fn root() -> Self {
841 Self { lexical: ModPath::root(), dynamic: ModPath::root() }
842 }
843}
844
845pub fn compile<R: Rt, E: UserEvent>(
848 ctx: &mut ExecCtx<R, E>,
849 flags: BitFlags<CFlag>,
850 scope: &Scope,
851 spec: Expr,
852) -> Result<Node<R, E>> {
853 let top_id = spec.id;
854 let env = ctx.env.clone();
855 let st = Instant::now();
856 let mut node = match compiler::compile(ctx, flags, spec, scope, top_id) {
857 Ok(n) => n,
858 Err(e) => {
859 ctx.env = env;
860 return Err(e);
861 }
862 };
863 info!("compile time {:?}", st.elapsed());
864 let st = Instant::now();
865 if let Err(e) = node.typecheck(ctx) {
866 ctx.env = env;
867 return Err(e);
868 }
869 info!("typecheck time {:?}", st.elapsed());
870 Ok(node)
871}