#![doc(
html_logo_url = "https://graphix-lang.github.io/graphix/graphix-icon.svg",
html_favicon_url = "https://graphix-lang.github.io/graphix/graphix-icon.svg"
)]
use anyhow::Result;
use graphix_compiler::{
expr::ExprId, typ::FnType, Apply, BuiltIn, Event, ExecCtx, Node, Rt, Scope, UserEvent,
};
use graphix_package_core::CachedVals;
use netidx::subscriber::Value;
use netidx_value::ValArray;
use rand::{rng, seq::SliceRandom, RngExt};
use smallvec::{smallvec, SmallVec};
#[derive(Debug)]
struct Rand {
args: CachedVals,
}
impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Rand {
const NAME: &str = "rand";
const NEEDS_CALLSITE: bool = false;
fn init<'a, 'b, 'c, 'd>(
_ctx: &'a mut ExecCtx<R, E>,
_typ: &'a FnType,
_resolved: Option<&'d FnType>,
_scope: &'b Scope,
from: &'c [Node<R, E>],
_top_id: ExprId,
) -> Result<Box<dyn Apply<R, E>>> {
Ok(Box::new(Rand { args: CachedVals::new(from) }))
}
}
impl<R: Rt, E: UserEvent> Apply<R, E> for Rand {
fn update(
&mut self,
ctx: &mut ExecCtx<R, E>,
from: &mut [Node<R, E>],
event: &mut Event<E>,
) -> Option<Value> {
macro_rules! gen_cases {
($start:expr, $end:expr, $($typ:ident),+) => {
match ($start, $end) {
$(
(Value::$typ(start), Value::$typ(end)) if start < end => {
Some(Value::$typ(rng().random_range(*start..*end)))
}
),+
_ => None
}
};
}
let up = self.args.update(ctx, from, event);
if up {
match &self.args.0[..] {
[Some(start), Some(end), Some(_)] => gen_cases!(
start, end, F32, F64, I32, I64, Z32, Z64, U32, U64, V32, V64
),
_ => None,
}
} else {
None
}
}
fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
self.args.clear()
}
}
#[derive(Debug)]
struct Pick;
impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Pick {
const NAME: &str = "rand_pick";
const NEEDS_CALLSITE: bool = false;
fn init<'a, 'b, 'c, 'd>(
_ctx: &'a mut ExecCtx<R, E>,
_typ: &'a FnType,
_resolved: Option<&'d FnType>,
_scope: &'b Scope,
_from: &'c [Node<R, E>],
_top_id: ExprId,
) -> Result<Box<dyn Apply<R, E>>> {
Ok(Box::new(Pick))
}
}
impl<R: Rt, E: UserEvent> Apply<R, E> for Pick {
fn update(
&mut self,
ctx: &mut ExecCtx<R, E>,
from: &mut [Node<R, E>],
event: &mut Event<E>,
) -> Option<Value> {
from[0].update(ctx, event).and_then(|a| match a {
Value::Array(a) if a.len() > 0 => {
Some(a[rng().random_range(0..a.len())].clone())
}
_ => None,
})
}
fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {}
}
#[derive(Debug)]
struct Shuffle(SmallVec<[Value; 32]>);
impl<R: Rt, E: UserEvent> BuiltIn<R, E> for Shuffle {
const NAME: &str = "rand_shuffle";
const NEEDS_CALLSITE: bool = false;
fn init<'a, 'b, 'c, 'd>(
_ctx: &'a mut ExecCtx<R, E>,
_typ: &'a FnType,
_resolved: Option<&'d FnType>,
_scope: &'b Scope,
_from: &'c [Node<R, E>],
_top_id: ExprId,
) -> Result<Box<dyn Apply<R, E>>> {
Ok(Box::new(Shuffle(smallvec![])))
}
}
impl<R: Rt, E: UserEvent> Apply<R, E> for Shuffle {
fn update(
&mut self,
ctx: &mut ExecCtx<R, E>,
from: &mut [Node<R, E>],
event: &mut Event<E>,
) -> Option<Value> {
from[0].update(ctx, event).and_then(|a| match a {
Value::Array(a) => {
self.0.extend(a.iter().cloned());
self.0.shuffle(&mut rng());
Some(Value::Array(ValArray::from_iter_exact(self.0.drain(..))))
}
_ => None,
})
}
fn sleep(&mut self, _ctx: &mut ExecCtx<R, E>) {
self.0.clear()
}
}
#[cfg(test)]
mod test;
graphix_derive::defpackage! {
builtins => [
Rand,
Pick,
Shuffle,
],
}