#[macro_use]
extern crate netidx_core;
#[macro_use]
extern crate combine;
#[macro_use]
extern crate serde_derive;
pub mod env;
pub mod expr;
pub mod node;
pub mod rt;
pub mod stdfn;
pub mod typ;
use crate::{
env::Env,
expr::{ExprId, ExprKind, ModPath},
typ::{FnType, Type},
};
use anyhow::{bail, Result};
use arcstr::ArcStr;
use expr::Expr;
use fxhash::FxHashMap;
use netidx::{
path::Path,
publisher::{Id, Val, WriteRequest},
subscriber::{self, Dval, SubId, UpdatesFlags, Value},
};
use netidx_protocols::rpc::server::{ArgSpec, RpcCall};
use node::compiler;
use parking_lot::RwLock;
use std::{
collections::{hash_map::Entry, HashMap},
fmt::Debug,
sync::{self, LazyLock},
time::Duration,
};
use triomphe::Arc;
#[cfg(test)]
#[macro_use]
mod tests;
atomic_id!(BindId);
atomic_id!(LambdaId);
impl BindId {
pub(crate) fn from_u64(v: u64) -> Self {
BindId(v)
}
}
#[macro_export]
macro_rules! errf {
($pat:expr, $($arg:expr),*) => {
Some(Value::Error(ArcStr::from(format_compact!($pat, $($arg),*).as_str())))
};
($pat:expr) => { Some(Value::Error(ArcStr::from(format_compact!($pat).as_str()))) };
}
#[macro_export]
macro_rules! err {
($pat:literal) => {
Some(Value::Error(literal!($pat)))
};
}
pub trait UserEvent: Clone + Debug + 'static {
fn clear(&mut self);
}
#[derive(Debug, Clone)]
pub struct NoUserEvent;
impl UserEvent for NoUserEvent {
fn clear(&mut self) {}
}
#[derive(Debug)]
pub struct Event<E: UserEvent> {
pub init: bool,
pub variables: FxHashMap<BindId, Value>,
pub netidx: FxHashMap<SubId, subscriber::Event>,
pub writes: FxHashMap<Id, WriteRequest>,
pub rpc_calls: FxHashMap<BindId, RpcCall>,
pub user: E,
}
impl<E: UserEvent> Event<E> {
pub fn new(user: E) -> Self {
Event {
init: false,
variables: HashMap::default(),
netidx: HashMap::default(),
writes: HashMap::default(),
rpc_calls: HashMap::default(),
user,
}
}
pub fn clear(&mut self) {
let Self { init, variables, netidx, rpc_calls, writes, user } = self;
*init = false;
variables.clear();
netidx.clear();
rpc_calls.clear();
writes.clear();
user.clear();
}
}
pub type Node<C, E> = Box<dyn Update<C, E>>;
pub type BuiltInInitFn<C, E> = sync::Arc<
dyn for<'a, 'b, 'c> Fn(
&'a mut ExecCtx<C, E>,
&'a FnType,
&'b ModPath,
&'c [Node<C, E>],
ExprId,
) -> Result<Box<dyn Apply<C, E>>>
+ Send
+ Sync
+ 'static,
>;
pub type InitFn<C, E> = sync::Arc<
dyn for<'a, 'b> Fn(
&'a mut ExecCtx<C, E>,
&'b [Node<C, E>],
ExprId,
) -> Result<Box<dyn Apply<C, E>>>
+ Send
+ Sync
+ 'static,
>;
pub trait Apply<C: Ctx, E: UserEvent>: Debug + Send + Sync + 'static {
fn update(
&mut self,
ctx: &mut ExecCtx<C, E>,
from: &mut [Node<C, E>],
event: &mut Event<E>,
) -> Option<Value>;
fn delete(&mut self, _ctx: &mut ExecCtx<C, E>) {
()
}
fn typecheck(
&mut self,
_ctx: &mut ExecCtx<C, E>,
_from: &mut [Node<C, E>],
) -> Result<()> {
Ok(())
}
fn typ(&self) -> Arc<FnType> {
const EMPTY: LazyLock<Arc<FnType>> = LazyLock::new(|| {
Arc::new(FnType {
args: Arc::from_iter([]),
constraints: Arc::new(RwLock::new(vec![])),
rtype: Type::Bottom,
vargs: None,
})
});
Arc::clone(&*EMPTY)
}
fn refs<'a>(&'a self, _f: &'a mut (dyn FnMut(BindId) + 'a)) {
()
}
}
pub trait Update<C: Ctx, E: UserEvent>: Debug + Send + Sync + 'static {
fn update(&mut self, ctx: &mut ExecCtx<C, E>, event: &mut Event<E>) -> Option<Value>;
fn delete(&mut self, ctx: &mut ExecCtx<C, E>);
fn typecheck(&mut self, ctx: &mut ExecCtx<C, E>) -> Result<()>;
fn typ(&self) -> &Type;
fn refs<'a>(&'a self, f: &'a mut (dyn FnMut(BindId) + 'a));
fn spec(&self) -> &Expr;
}
pub trait BuiltIn<C: Ctx, E: UserEvent> {
const NAME: &str;
const TYP: LazyLock<FnType>;
fn init(ctx: &mut ExecCtx<C, E>) -> BuiltInInitFn<C, E>;
}
pub trait Ctx: Debug + 'static {
fn clear(&mut self);
fn subscribe(&mut self, flags: UpdatesFlags, path: Path, ref_by: ExprId) -> Dval;
fn unsubscribe(&mut self, path: Path, dv: Dval, ref_by: ExprId);
fn list(&mut self, id: BindId, path: Path);
fn list_table(&mut self, id: BindId, path: Path);
fn stop_list(&mut self, id: BindId);
fn publish(&mut self, path: Path, value: Value, ref_by: ExprId) -> Result<Val>;
fn update(&mut self, id: &Val, value: Value);
fn unpublish(&mut self, id: Val, ref_by: ExprId);
fn ref_var(&mut self, id: BindId, ref_by: ExprId);
fn unref_var(&mut self, id: BindId, ref_by: ExprId);
fn set_var(&mut self, id: BindId, value: Value);
fn call_rpc(&mut self, name: Path, args: Vec<(ArcStr, Value)>, id: BindId);
fn publish_rpc(
&mut self,
name: Path,
doc: Value,
spec: Vec<ArgSpec>,
id: BindId,
) -> Result<()>;
fn unpublish_rpc(&mut self, name: Path);
fn set_timer(&mut self, id: BindId, timeout: Duration);
}
pub struct ExecCtx<C: Ctx, E: UserEvent> {
builtins: FxHashMap<&'static str, (FnType, BuiltInInitFn<C, E>)>,
std: Vec<Node<C, E>>,
pub env: Env<C, E>,
pub cached: FxHashMap<BindId, Value>,
pub user: C,
}
impl<C: Ctx, E: UserEvent> ExecCtx<C, E> {
pub fn clear(&mut self) {
self.env.clear();
self.user.clear();
}
pub fn new_no_std(user: C) -> Self {
let mut t = ExecCtx {
env: Env::new(),
builtins: FxHashMap::default(),
std: vec![],
cached: HashMap::default(),
user,
};
let core = stdfn::core::register(&mut t);
let root = ModPath(Path::root());
let node = compile(&mut t, &root, core).expect("error compiling core");
t.std.push(node);
let node = compile(
&mut t,
&root,
ExprKind::Use { name: ModPath::from(["core"]) }.to_expr(Default::default()),
)
.expect("error compiling use core");
t.std.push(node);
t
}
pub fn new(user: C) -> Self {
let mut t = Self::new_no_std(user);
let root = ModPath(Path::root());
let str = stdfn::str::register(&mut t);
let node = compile(&mut t, &root, str).expect("failed to compile the str module");
t.std.push(node);
let re = stdfn::re::register(&mut t);
let node = compile(&mut t, &root, re).expect("failed to compile the re module");
t.std.push(node);
let time = stdfn::time::register(&mut t);
let node =
compile(&mut t, &root, time).expect("failed to compile the time module");
t.std.push(node);
let rand = stdfn::rand::register(&mut t);
let node = compile(&mut t, &root, rand).expect("failed to compile rand module");
t.std.push(node);
let net = stdfn::net::register(&mut t);
let node = compile(&mut t, &root, net).expect("failed to compile the net module");
t.std.push(node);
t
}
pub fn register_builtin<T: BuiltIn<C, E>>(&mut self) -> Result<()> {
let f = T::init(self);
match self.builtins.entry(T::NAME) {
Entry::Vacant(e) => {
e.insert((T::TYP.clone(), f));
}
Entry::Occupied(_) => bail!("builtin {} is already registered", T::NAME),
}
Ok(())
}
pub fn set_var(&mut self, id: BindId, v: Value) {
self.cached.insert(id, v.clone());
self.user.set_var(id, v)
}
}
pub fn compile<C: Ctx, E: UserEvent>(
ctx: &mut ExecCtx<C, E>,
scope: &ModPath,
spec: Expr,
) -> Result<Node<C, E>> {
let top_id = spec.id;
let env = ctx.env.clone();
let mut node = match compiler::compile(ctx, spec, scope, top_id) {
Ok(n) => n,
Err(e) => {
ctx.env = env;
return Err(e);
}
};
if let Err(e) = node.typecheck(ctx) {
ctx.env = env;
return Err(e);
}
Ok(node)
}