use std::collections::HashMap;
use derive_more::{Deref, DerefMut};
use strum::IntoEnumIterator;
use crate::model::{BinaryOp, Callable, Expr, Function, Ident, UnaryOp, Value, Type};
use super::error::Error;
#[derive(Debug, Clone, Default, Deref, DerefMut)]
pub struct Namespace(HashMap<Ident, Value>);
#[derive(Debug)]
pub struct Context<'ctx> {
parent: Option<&'ctx Context<'ctx>>,
namespace: Namespace,
#[cfg(rng)]
rng: fastrand::Rng,
}
assert_impl_all!(Context: Send, Sync);
impl<'ctx> Context<'ctx> {
pub fn empty() -> Self {
Self {
parent: None,
namespace: Default::default(),
#[cfg(rng)]
rng: fastrand::Rng::new(),
}
}
pub fn new() -> Self {
let mut ctx = Self::empty();
for func in Function::iter() {
for name in func.names() {
ctx.set(name, func);
}
}
ctx.set("pi", std::f32::consts::PI);
ctx.set("e", std::f32::consts::E);
ctx
}
pub fn with_parent(parent: &'ctx Context<'ctx>) -> Self {
let mut ctx = Self::empty();
ctx.parent = Some(parent);
ctx
}
}
impl Default for Context<'_> {
fn default() -> Self {
Self::new()
}
}
impl<'ctx> Context<'ctx> {
pub fn parent(&self) -> Option<&Context> {
self.parent
}
pub fn child(&'ctx self) -> Context<'ctx> {
Self::with_parent(self)
}
}
impl Context<'_> {
pub fn set(&mut self, name: impl Into<Ident>, value: impl Into<Value>) -> &mut Self {
self.namespace.insert(name.into(), value.into());
self
}
pub fn unset(&mut self, name: impl AsRef<str>) -> &mut Self {
self.namespace.remove(name.as_ref());
self
}
pub fn with(mut self, name: impl Into<Ident>, value: impl Into<Value>) -> Self {
self.set(name, value);
self
}
pub fn without(mut self, name: impl AsRef<str>) -> Self {
self.unset(name);
self
}
}
impl Context<'_> {
pub fn get(&self, name: impl AsRef<str>) -> Option<&Value> {
self.namespace.get(name.as_ref())
}
pub fn get_mut(&mut self, name: impl AsRef<str>) -> Option<&mut Value> {
self.namespace.get_mut(name.as_ref())
}
pub fn resolve(&self, name: impl AsRef<str>) -> Result<&Value, Error> {
let name = name.as_ref();
let mut ctx: &Context = self;
loop {
if let Some(value) = ctx.get(name) {
return Ok(value);
}
match ctx.parent() {
Some(p) => { ctx = p; },
None => { return Err(Error::Name { ident: name.to_owned().into() })},
}
}
}
pub fn iter(&self) -> impl Iterator<Item=(&str, &Value)> {
self.namespace.iter().map(|(name, value)| (name.as_str(), value))
}
pub fn iter_names(&self) -> impl Iterator<Item=&str> {
self.namespace.keys().map(|name| name.as_str())
}
pub fn name_count(&self) -> usize {
self.namespace.len()
}
}
impl Context<'_> {
pub fn eval(&mut self, expr: &Expr) -> Result<Value, Error> {
match expr {
Expr::Literal(lit) => Ok(Value::from(lit)),
Expr::Ref(ident) => self.resolve(ident).map(|v| v.clone()),
#[cfg(glam)]
Expr::Vector(items) => {
let items = items.iter().map(|arg| self.eval(arg)).collect::<Result<Vec<_>, _>>()?;
self.eval_vector(&items)
},
Expr::Call(target, args) => {
let callable = self.eval(target)?.try_to_callable()?;
let args = args.iter().map(|arg| self.eval(arg)).collect::<Result<Vec<_>, _>>()?;
self.eval_call(callable, &args)
},
Expr::Subscript(value, idx) => {
let value = self.eval(value)?;
let idx = self.eval(idx)?;
self.eval_subscript(&value, &idx)
},
Expr::Access(value, member) => {
let value = self.eval(value)?;
self.eval_access(&value, member)
},
Expr::Unary(op, arg) => {
let arg = self.eval(arg)?;
self.eval_unary_expr(*op, &arg)
},
Expr::Binary(op, lhs, rhs) => {
let (lhs, rhs) = (&self.eval(lhs)?, &self.eval(rhs)?);
self.eval_binary_expr(*op, lhs, rhs)
},
}
}
}
impl Context<'_> {
#[cfg(glam)]
pub(crate) fn eval_vector(&mut self, items: &[Value]) -> Result<Value, Error> {
if ![2, 3, 4].contains(&items.len()) {
return Err(Error::bounds(2..(4+1), items.len()));
}
dispatch!((self; items) => {
(_; x: Float, y: Float) => Ok(glam::Vec2::new(x, y).into()),
(_; x: Float, y: Float, z: Float) => Ok(glam::Vec3::new(x, y, z).into()),
(_; x: Float, y: Float, z: Float, w: Float) => Ok(glam::Vec4::new(x, y, z, w).into()),
})
}
pub(crate) fn eval_unary_expr(&mut self, op: UnaryOp, arg: &Value) -> Result<Value, Error> {
match op {
UnaryOp::Neg => dispatch!((self; [arg]) => {
(_; arg: Integer) => Ok(Value::Integer(-arg)),
(_; arg: Float) => Ok(Value::Float(-arg)),
}),
UnaryOp::Not => dispatch!((self; [arg]) => {
(_; arg: Bool) => Ok(Value::Bool(!arg),)
}),
}
}
pub(crate) fn eval_binary_expr(
&mut self, op: BinaryOp, lhs: &Value, rhs: &Value,
) -> Result<Value, Error> {
macro_rules! additive_op {
($op:tt) => {
dispatch!((self; [lhs, rhs]) => {
(_; lhs: Integer, rhs: Integer) => Ok(Value::Integer(lhs $op rhs)),
(_; lhs: Integer, rhs: Float) => Ok(Value::Float(lhs as f32 $op rhs)),
(_; lhs: Float, rhs: Integer) => Ok(Value::Float(lhs $op rhs as f32)),
(_; lhs: Float, rhs: Float) => Ok(Value::Float(lhs $op rhs)),
#[cfg(glam)] (_; lhs: Vec2, rhs: Vec2) => Ok(Value::Vec2(lhs $op rhs)),
#[cfg(glam)] (_; lhs: Vec3, rhs: Vec3) => Ok(Value::Vec3(lhs $op rhs)),
#[cfg(glam)] (_; lhs: Vec4, rhs: Vec4) => Ok(Value::Vec4(lhs $op rhs)),
})
};
}
use BinaryOp::*;
match op {
Add => additive_op!(+),
Sub => additive_op!(-),
Mul => dispatch!((self; [lhs, rhs]) => {
(_; lhs: Integer, rhs: Integer) => Ok(Value::Integer(lhs * rhs)),
(_; lhs: Integer, rhs: Float) => Ok(Value::Float(lhs as f32 * rhs)),
(_; lhs: Float, rhs: Integer) => Ok(Value::Float(lhs * rhs as f32)),
(_; lhs: Float, rhs: Float) => Ok(Value::Float(lhs * rhs)),
#[cfg(glam)] (_; lhs: Vec2, rhs: Vec2) => Ok(Value::Vec2(lhs * rhs)),
#[cfg(glam)] (_; lhs: Vec3, rhs: Vec3) => Ok(Value::Vec3(lhs * rhs)),
#[cfg(glam)] (_; lhs: Vec4, rhs: Vec4) => Ok(Value::Vec4(lhs * rhs)),
#[cfg(glam)] (_; lhs: Vec2, rhs: Integer) => Ok(Value::Vec2(lhs * rhs as f32)),
#[cfg(glam)] (_; lhs: Vec3, rhs: Integer) => Ok(Value::Vec3(lhs * rhs as f32)),
#[cfg(glam)] (_; lhs: Vec4, rhs: Integer) => Ok(Value::Vec4(lhs * rhs as f32)),
#[cfg(glam)] (_; lhs: Vec2, rhs: Float) => Ok(Value::Vec2(lhs * rhs)),
#[cfg(glam)] (_; lhs: Vec3, rhs: Float) => Ok(Value::Vec3(lhs * rhs)),
#[cfg(glam)] (_; lhs: Vec4, rhs: Float) => Ok(Value::Vec4(lhs * rhs)),
#[cfg(glam)] (_; lhs: Integer, rhs: Vec2) => Ok(Value::Vec2(lhs as f32 * rhs)),
#[cfg(glam)] (_; lhs: Integer, rhs: Vec3) => Ok(Value::Vec3(lhs as f32 * rhs)),
#[cfg(glam)] (_; lhs: Integer, rhs: Vec4) => Ok(Value::Vec4(lhs as f32 * rhs)),
#[cfg(glam)] (_; lhs: Float, rhs: Vec2) => Ok(Value::Vec2(lhs * rhs)),
#[cfg(glam)] (_; lhs: Float, rhs: Vec3) => Ok(Value::Vec3(lhs * rhs)),
#[cfg(glam)] (_; lhs: Float, rhs: Vec4) => Ok(Value::Vec4(lhs * rhs)),
}),
Div => dispatch!((self; [lhs, rhs]) => {
(_; lhs: Integer, rhs: Integer) => Ok(Value::Integer(lhs / rhs)),
(_; lhs: Integer, rhs: Float) => Ok(Value::Float(lhs as f32 / rhs)),
(_; lhs: Float, rhs: Integer) => Ok(Value::Float(lhs / rhs as f32)),
(_; lhs: Float, rhs: Float) => Ok(Value::Float(lhs / rhs)),
#[cfg(glam)] (_; lhs: Vec2, rhs: Vec2) => Ok(Value::Vec2(lhs / rhs)),
#[cfg(glam)] (_; lhs: Vec3, rhs: Vec3) => Ok(Value::Vec3(lhs / rhs)),
#[cfg(glam)] (_; lhs: Vec4, rhs: Vec4) => Ok(Value::Vec4(lhs / rhs)),
#[cfg(glam)] (_; lhs: Vec2, rhs: Integer) => Ok(Value::Vec2(lhs / rhs as f32)),
#[cfg(glam)] (_; lhs: Vec3, rhs: Integer) => Ok(Value::Vec3(lhs / rhs as f32)),
#[cfg(glam)] (_; lhs: Vec4, rhs: Integer) => Ok(Value::Vec4(lhs / rhs as f32)),
#[cfg(glam)] (_; lhs: Vec2, rhs: Float) => Ok(Value::Vec2(lhs / rhs)),
#[cfg(glam)] (_; lhs: Vec3, rhs: Float) => Ok(Value::Vec3(lhs / rhs)),
#[cfg(glam)] (_; lhs: Vec4, rhs: Float) => Ok(Value::Vec4(lhs / rhs)),
}),
Pow => dispatch!((self; [lhs, rhs]) => {
(_; lhs: Integer, rhs: Integer) => Ok(Value::Integer(lhs.pow(rhs.try_into()?))),
(_; lhs: Integer, rhs: Float) => Ok(Value::Float(f32::powf(lhs as _, rhs))),
(_; lhs: Float, rhs: Integer) => Ok(Value::Float(f32::powf(lhs, rhs as _))),
(_; lhs: Float, rhs: Float) => Ok(Value::Float(lhs.powf(rhs))),
#[cfg(glam)] (_; lhs: Vec2, rhs: Integer) => Ok(Value::Vec2(lhs.powf(rhs as _))),
#[cfg(glam)] (_; lhs: Vec2, rhs: Float) => Ok(Value::Vec2(lhs.powf(rhs))),
#[cfg(glam)] (_; lhs: Vec3, rhs: Integer) => Ok(Value::Vec3(lhs.powf(rhs as _))),
#[cfg(glam)] (_; lhs: Vec3, rhs: Float) => Ok(Value::Vec3(lhs.powf(rhs))),
#[cfg(glam)] (_; lhs: Vec4, rhs: Integer) => Ok(Value::Vec4(lhs.powf(rhs as _))),
#[cfg(glam)] (_; lhs: Vec4, rhs: Float) => Ok(Value::Vec4(lhs.powf(rhs))),
}),
op @ (Eq | NotEq) => {
dispatch_ref!((self; [lhs, rhs]) => {
(_; a: Bool, b: Bool) => Ok(eval_eq_expr(a, op, b).unwrap()),
(_; a: Integer, b: Integer) => Ok(eval_eq_expr(a, op, b).unwrap()),
(_; a: Integer, b: Float) => Ok(eval_eq_expr(*a as f32, op, *b).unwrap()),
(_; a: Float, b: Integer) => Ok(eval_eq_expr(*a, op, *b as f32).unwrap()),
(_; a: Float, b: Float) => Ok(eval_eq_expr(a, op, b).unwrap()),
(_; a: Symbol, b: Symbol) => Ok(eval_eq_expr(a, op, b).unwrap()),
#[cfg(glam)] (_; a: Vec2, b: Vec2) => Ok(eval_eq_expr(a, op, b).unwrap()),
#[cfg(glam)] (_; a: Vec3, b: Vec3) => Ok(eval_eq_expr(a, op, b).unwrap()),
#[cfg(glam)] (_; a: Vec4, b: Vec4) => Ok(eval_eq_expr(a, op, b).unwrap()),
(_; a: Callable, b: Callable) => Ok(eval_eq_expr(a, op, b).unwrap()),
})
},
op @ (Less | LessOrEq | Greater | GreaterOrEq) => {
dispatch!((self; [lhs, rhs]) => {
(_; a: Integer, b: Integer) => Ok(eval_ord_expr(a, op, b).unwrap()),
(_; a: Integer, b: Float) => Ok(eval_ord_expr(a as f32, op, b).unwrap()),
(_; a: Float, b: Integer) => Ok(eval_ord_expr(a, op, b as f32).unwrap()),
(_; a: Float, b: Float) => Ok(eval_ord_expr(a, op, b).unwrap()),
})
},
And => dispatch!((self; [lhs, rhs]) => {
(_; lhs: Bool, rhs: Bool) => Ok(Value::Bool(lhs && rhs)),
}),
Or => dispatch!((self; [lhs, rhs]) => {
(_; lhs: Bool, rhs: Bool) => Ok(Value::Bool(lhs || rhs)),
}),
}
}
pub(crate) fn eval_call(&mut self, target: Callable, args: &[Value]) -> Result<Value, Error> {
match target {
Callable::Native(func) => self.eval_func(func, args),
Callable::Custom(func) => func(self, args),
}
}
pub(crate) fn eval_subscript(&mut self, value: &Value, idx: &Value) -> Result<Value, Error> {
#[cfg(glam)]
macro_rules! glam_vec_index {
($dim:expr; $vec:ident[$idx:ident]) => ({
let idx: usize = $idx.try_into()?;
$vec.to_array().get(idx)
.copied().map(Into::into)
.ok_or_else(|| Error::bounds(0..$dim, idx))
});
}
dispatch!((self; [value, idx]) => {
#[cfg(glam)] (_; vec2: Vec2, i: Integer) => glam_vec_index!(2; vec2[i]),
#[cfg(glam)] (_; vec3: Vec3, i: Integer) => glam_vec_index!(3; vec3[i]),
#[cfg(glam)] (_; vec4: Vec4, i: Integer) => glam_vec_index!(4; vec4[i]),
})
}
#[allow(unused)] pub(crate) fn eval_access(&mut self, value: &Value, member: &Ident) -> Result<Value, Error> {
let member = member.as_str();
macro_rules! fields {
($value:expr; $member:expr => { $($name:ident : $ty:ident),* $(,)* }) => {
match $member {
$( stringify!($name) => Ok(Value::$ty($value.$name)), )*
name => Err(Error::name(name.to_owned())),
}
};
}
dispatch!((self; [value]) => {
#[cfg(glam)] (_; vec2: Vec2) => fields!(vec2; member => { x: Float, y: Float }),
#[cfg(glam)] (_; vec3: Vec3) => fields!(vec3; member => {
x: Float, y: Float, z: Float,
}),
#[cfg(glam)] (_; vec4: Vec4) => fields!(vec4; member => {
x: Float, y: Float, z: Float, w: Float,
}),
})
}
}
fn eval_eq_expr<A, B>(a: A, op: BinaryOp, b: B) -> Option<Value>
where A: PartialEq<B>, B: PartialEq<A>
{
Some(Value::from(match op {
BinaryOp::Eq => a == b,
BinaryOp::NotEq => a != b,
_ => return None,
}))
}
fn eval_ord_expr<A, B>(a: A, op: BinaryOp, b: B) -> Option<Value>
where A: PartialOrd<B>, B: PartialOrd<A>
{
Some(Value::from(match op {
BinaryOp::Less => a < b,
BinaryOp::LessOrEq => a <= b,
BinaryOp::Greater => a > b,
BinaryOp::GreaterOrEq => a >= b,
_ => return None,
}))
}
impl Context<'_> {
pub(crate) fn eval_func(&mut self, func: Function, args: &[Value]) -> Result<Value, Error> {
macro_rules! f32_func1 {
($func:ident) => {
dispatch!((self; args) => {
(_; x: Integer) => Ok(Value::Float(f32::$func(x as _))),
(_; x: Float) => Ok(Value::Float(x.$func())),
})
}
}
macro_rules! f32_or_vec_func1 {
($func:ident) => {
dispatch!((self; args) => {
(_; x: Integer) => Ok(Value::Float(f32::$func(x as _))),
(_; x: Float) => Ok(Value::Float(x.$func())),
#[cfg(glam)] (_; v: Vec2) => Ok(Value::Vec2(v.$func())),
#[cfg(glam)] (_; v: Vec3) => Ok(Value::Vec3(v.$func())),
#[cfg(glam)] (_; v: Vec4) => Ok(Value::Vec4(v.$func())),
})
}
}
#[cfg(glam)]
macro_rules! vec_to_float_func1 {
($func:ident) => {
dispatch!((self; args) => {
(_; v: Vec2) => Ok(Value::Float(v.$func())),
(_; v: Vec3) => Ok(Value::Float(v.$func())),
(_; v: Vec4) => Ok(Value::Float(v.$func())),
})
};
}
#[cfg(glam)]
macro_rules! vec_to_float_func2 {
($func:ident) => {
dispatch!((self; args) => {
(_; a: Vec2, b: Vec2) => Ok(Value::Float(glam::Vec2::$func(a, b))),
(_; a: Vec3, b: Vec3) => Ok(Value::Float(glam::Vec3::$func(a, b))),
(_; a: Vec4, b: Vec4) => Ok(Value::Float(glam::Vec4::$func(a, b))),
})
};
}
#[cfg(glam)]
macro_rules! vec_func1 {
($func:ident) => {
dispatch!((self; args) => {
(_; v: Vec2) => Ok(Value::Vec2(v.$func())),
(_; v: Vec3) => Ok(Value::Vec3(v.$func())),
(_; v: Vec4) => Ok(Value::Vec4(v.$func())),
})
};
}
use Function::*;
match func {
Identity => {
if args.len() != 1 {
return Err(Error::type_(
Type::iter().map(|ty| [ty]), args.iter().map(|v| v.ty())));
}
Ok(args[0].clone())
},
ToBool => dispatch!((self; args) => {
(_; x: Bool) => Ok(Value::Bool(x)), (_; x: Integer) => Ok(Value::Bool(x != 0)), (_; x: Float) => Ok(Value::Bool(x != 0.)),
#[cfg(glam)] (_; x: Vec2) => Ok(Value::Bool(x != glam::Vec2::ZERO)),
#[cfg(glam)] (_; x: Vec3) => Ok(Value::Bool(x != glam::Vec3::ZERO)),
#[cfg(glam)] (_; x: Vec4) => Ok(Value::Bool(x != glam::Vec4::ZERO)),
}),
ToInteger => dispatch!((self; args) => {
(_; x: Bool) => Ok(Value::Integer(if x { 1 } else { 0 })),
(_; x: Integer) => Ok(Value::Integer(x)), (_; x: Float) => Ok(Value::Integer(x as _)),
}),
ToFloat => dispatch!((self; args) => {
(_; x: Integer) => Ok(Value::Float(x as _)),
(_; x: Integer) => Ok(Value::Integer(x)), }),
Abs => dispatch!((self; args) => {
(_; x: Integer) => Ok(Value::Integer(x.abs())),
(_; x: Float) => Ok(Value::Float(x.abs())),
#[cfg(glam)] (_; v: Vec2) => Ok(Value::Vec2(v.abs())),
#[cfg(glam)] (_; v: Vec3) => Ok(Value::Vec3(v.abs())),
#[cfg(glam)] (_; v: Vec4) => Ok(Value::Vec4(v.abs())),
}),
Frac => f32_or_vec_func1!(fract),
Trunc =>f32_or_vec_func1!(trunc),
Floor => f32_or_vec_func1!(floor),
Ceil => f32_or_vec_func1!(ceil),
Round => f32_or_vec_func1!(round),
SquareRoot => f32_func1!(sqrt),
CubeRoot => f32_func1!(cbrt),
Exp => f32_or_vec_func1!(exp),
Ln => f32_func1!(ln),
Log2 => f32_func1!(log2),
Log10 => f32_func1!(log10),
Sine => f32_func1!(sin),
Cosine => f32_func1!(cos),
Tangent => f32_func1!(tan),
#[cfg(rng)]
Rand => dispatch!((self; args) => {
(ctx; ) => Ok(Value::Float(ctx.rng.f32())),
(ctx; max: Integer) => if max > 0 {
Ok(Value::Integer(ctx.rng.i64(0..max)))
} else {
Err(Error::argument("positive bound", max))
},
(ctx; max: Float) => if max > 0. {
Ok(Value::Float(ctx.rng.f32() * max))
} else {
Err(Error::argument("positive bound", max))
},
(ctx; min: Integer, max: Integer) => if min <= max {
Ok(Value::Integer(ctx.rng.i64(min..=max)))
} else {
Err(Error::argument("maximum bound that's greater or equal to minimum", max))
},
(ctx; min: Float, max: Float) => if min <= max {
Ok(Value::Float(min + ctx.rng.f32() * (max - min)))
} else {
Err(Error::argument("maximum bound that's greater or equal to minimum", max))
},
}),
#[cfg(glam)] Length => vec_to_float_func1!(length),
#[cfg(glam)] NormalizeOrZero => vec_func1!(normalize_or_zero),
#[cfg(glam)] LengthSquared => vec_to_float_func1!(length_squared),
#[cfg(glam)] Distance => vec_to_float_func2!(distance),
#[cfg(glam)] DistanceSquared => vec_to_float_func2!(distance_squared),
#[cfg(glam)] DotProduct => vec_to_float_func2!(dot),
}
}
}
#[cfg(test)]
#[path = "context_test.rs"]
mod tests;