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