diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-07-10 20:01:18 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-07-10 23:10:17 +0200 |
| commit | 6a4823461f491aef63451f097ddfe5602e0b2157 (patch) | |
| tree | ad11b0ad169d030942d950573c729d50f7b3291b /src/eval | |
| parent | 36b3067c19c8743032a44f888ee48702b88d135b (diff) | |
Reference-count complex values
Rename some nodes types
Diffstat (limited to 'src/eval')
| -rw-r--r-- | src/eval/array.rs | 148 | ||||
| -rw-r--r-- | src/eval/dict.rs | 129 | ||||
| -rw-r--r-- | src/eval/function.rs | 176 | ||||
| -rw-r--r-- | src/eval/mod.rs | 33 | ||||
| -rw-r--r-- | src/eval/ops.rs | 37 | ||||
| -rw-r--r-- | src/eval/scope.rs | 4 | ||||
| -rw-r--r-- | src/eval/template.rs | 139 | ||||
| -rw-r--r-- | src/eval/value.rs | 310 |
8 files changed, 659 insertions, 317 deletions
diff --git a/src/eval/array.rs b/src/eval/array.rs new file mode 100644 index 00000000..f62bda9f --- /dev/null +++ b/src/eval/array.rs @@ -0,0 +1,148 @@ +use std::fmt::{self, Debug, Formatter}; +use std::iter::FromIterator; +use std::ops::{Add, AddAssign}; +use std::rc::Rc; + +use super::Value; + +/// Create a new [`Array`] from values. +#[macro_export] +macro_rules! array { + ($value:expr; $count:expr) => { + $crate::eval::Array::from_vec(vec![$crate::eval::Value::from($value); $count]) + }; + + ($($value:expr),* $(,)?) => { + $crate::eval::Array::from_vec(vec![$($crate::eval::Value::from($value)),*]) + }; +} + +/// A variably-typed array with clone-on-write value semantics. +#[derive(Clone, PartialEq)] +pub struct Array { + vec: Rc<Vec<Value>>, +} + +impl Array { + /// Create a new, empty array. + pub fn new() -> Self { + Self { vec: Rc::new(vec![]) } + } + + /// Create a new array from a vector of values. + pub fn from_vec(vec: Vec<Value>) -> Self { + Self { vec: Rc::new(vec) } + } + + /// Create a new, empty array with the given `capacity`. + pub fn with_capacity(capacity: usize) -> Self { + Self { + vec: Rc::new(Vec::with_capacity(capacity)), + } + } + + /// Whether the array is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// The length of the array. + pub fn len(&self) -> usize { + self.vec.len() + } + + /// Borrow the value at the given index. + pub fn get(&self, index: usize) -> Option<&Value> { + self.vec.get(index) + } + + /// Mutably borrow the value at the given index. + pub fn get_mut(&mut self, index: usize) -> Option<&mut Value> { + Rc::make_mut(&mut self.vec).get_mut(index) + } + + /// Set the value at the given index. + /// + /// This panics the `index` is out of range. + pub fn set(&mut self, index: usize, value: Value) { + Rc::make_mut(&mut self.vec)[index] = value; + } + + /// Push a value to the end of the array. + pub fn push(&mut self, value: Value) { + Rc::make_mut(&mut self.vec).push(value); + } + + /// Extend the array with the values from another array. + pub fn extend(&mut self, other: &Array) { + Rc::make_mut(&mut self.vec).extend(other.into_iter()) + } + + /// Clear the array. + pub fn clear(&mut self) { + if Rc::strong_count(&mut self.vec) == 1 { + Rc::make_mut(&mut self.vec).clear(); + } else { + *self = Self::new(); + } + } + + /// Repeat this array `n` times. + pub fn repeat(&self, n: usize) -> Self { + let len = self.len().checked_mul(n).expect("capacity overflow"); + self.into_iter().cycle().take(len).collect() + } + + /// Iterate over references to the contained values. + pub fn iter(&self) -> std::slice::Iter<Value> { + self.vec.iter() + } + + /// Iterate over the contained values. + pub fn into_iter(&self) -> impl Iterator<Item = Value> + Clone + '_ { + // TODO: Actually consume the vector if the ref-count is 1? + self.iter().cloned() + } +} + +impl Default for Array { + fn default() -> Self { + Self::new() + } +} + +impl Debug for Array { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_list().entries(self.vec.iter()).finish() + } +} + +impl FromIterator<Value> for Array { + fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self { + Array { vec: Rc::new(iter.into_iter().collect()) } + } +} + +impl<'a> IntoIterator for &'a Array { + type Item = &'a Value; + type IntoIter = std::slice::Iter<'a, Value>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl Add<&Array> for Array { + type Output = Self; + + fn add(mut self, rhs: &Array) -> Self::Output { + self.extend(rhs); + self + } +} + +impl AddAssign<&Array> for Array { + fn add_assign(&mut self, rhs: &Array) { + self.extend(rhs); + } +} diff --git a/src/eval/dict.rs b/src/eval/dict.rs new file mode 100644 index 00000000..bf99ea17 --- /dev/null +++ b/src/eval/dict.rs @@ -0,0 +1,129 @@ +use std::collections::BTreeMap; +use std::fmt::{self, Debug, Formatter}; +use std::iter::FromIterator; +use std::ops::{Add, AddAssign}; +use std::rc::Rc; + +use super::Value; +use crate::eco::EcoString; + +/// Create a new [`Dict`] from key-value pairs. +#[macro_export] +macro_rules! dict { + ($($key:expr => $value:expr),* $(,)?) => {{ + #[allow(unused_mut)] + let mut map = std::collections::BTreeMap::new(); + $(map.insert($crate::eco::EcoString::from($key), $crate::eval::Value::from($value));)* + $crate::eval::Dict::from_map(map) + }}; +} + +/// A variably-typed dictionary with clone-on-write value semantics. +#[derive(Clone, PartialEq)] +pub struct Dict { + map: Rc<BTreeMap<EcoString, Value>>, +} + +impl Dict { + /// Create a new, empty dictionary. + pub fn new() -> Self { + Self::default() + } + + /// Create a new dictionary from a mapping of strings to values. + pub fn from_map(map: BTreeMap<EcoString, Value>) -> Self { + Self { map: Rc::new(map) } + } + + /// Whether the dictionary is empty. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// The number of pairs in the dictionary. + pub fn len(&self) -> usize { + self.map.len() + } + + /// Borrow the value the given `key` maps to. + pub fn get(&self, key: &str) -> Option<&Value> { + self.map.get(key) + } + + /// Mutably borrow the value the given `key` maps to. + pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> { + Rc::make_mut(&mut self.map).get_mut(key) + } + + /// Insert a mapping from the given `key` to the given `value`. + pub fn insert(&mut self, key: EcoString, value: Value) { + Rc::make_mut(&mut self.map).insert(key, value); + } + + /// Extend the dictionary with the values from another dictionary. + pub fn extend(&mut self, other: &Dict) { + Rc::make_mut(&mut self.map).extend(other.into_iter()) + } + + /// Clear the dictionary. + pub fn clear(&mut self) { + if Rc::strong_count(&mut self.map) == 1 { + Rc::make_mut(&mut self.map).clear(); + } else { + *self = Self::new(); + } + } + + /// Iterate over pairs of the contained keys and values. + pub fn into_iter(&self) -> impl Iterator<Item = (EcoString, Value)> + Clone + '_ { + // TODO: Actually consume the map if the ref-count is 1? + self.iter().map(|(k, v)| (k.clone(), v.clone())) + } + + /// Iterate over pairs of references to the contained keys and values. + pub fn iter(&self) -> std::collections::btree_map::Iter<EcoString, Value> { + self.map.iter() + } +} + +impl Default for Dict { + fn default() -> Self { + Self::new() + } +} + +impl Debug for Dict { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_map().entries(self.map.iter()).finish() + } +} + +impl FromIterator<(EcoString, Value)> for Dict { + fn from_iter<T: IntoIterator<Item = (EcoString, Value)>>(iter: T) -> Self { + Dict { map: Rc::new(iter.into_iter().collect()) } + } +} + +impl<'a> IntoIterator for &'a Dict { + type Item = (&'a EcoString, &'a Value); + type IntoIter = std::collections::btree_map::Iter<'a, EcoString, Value>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl Add<&Dict> for Dict { + type Output = Self; + + fn add(mut self, rhs: &Dict) -> Self::Output { + self.extend(rhs); + self + } +} + +impl AddAssign<&Dict> for Dict { + fn add_assign(&mut self, rhs: &Dict) { + self.extend(rhs); + } +} diff --git a/src/eval/function.rs b/src/eval/function.rs new file mode 100644 index 00000000..c986d71a --- /dev/null +++ b/src/eval/function.rs @@ -0,0 +1,176 @@ +use std::fmt::{self, Debug, Formatter}; +use std::ops::Deref; +use std::rc::Rc; + +use super::{Cast, CastResult, EvalContext, Value}; +use crate::eco::EcoString; +use crate::syntax::{Span, Spanned}; + +/// An evaluatable function. +#[derive(Clone)] +pub struct Function { + /// The name of the function. + /// + /// The string is boxed to make the whole struct fit into 24 bytes, so that + /// a value fits into 32 bytes. + name: Option<Box<EcoString>>, + /// The closure that defines the function. + f: Rc<dyn Fn(&mut EvalContext, &mut FuncArgs) -> Value>, +} + +impl Function { + /// Create a new function from a rust closure. + pub fn new<F>(name: Option<EcoString>, f: F) -> Self + where + F: Fn(&mut EvalContext, &mut FuncArgs) -> Value + 'static, + { + Self { name: name.map(Box::new), f: Rc::new(f) } + } + + /// The name of the function. + pub fn name(&self) -> Option<&EcoString> { + self.name.as_ref().map(|s| &**s) + } +} + +impl Deref for Function { + type Target = dyn Fn(&mut EvalContext, &mut FuncArgs) -> Value; + + fn deref(&self) -> &Self::Target { + self.f.as_ref() + } +} + +impl Debug for Function { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_struct("ValueFunc").field("name", &self.name).finish() + } +} + +impl PartialEq for Function { + fn eq(&self, other: &Self) -> bool { + // We cast to thin pointers because we don't want to compare vtables. + Rc::as_ptr(&self.f) as *const () == Rc::as_ptr(&other.f) as *const () + } +} + +/// Evaluated arguments to a function. +#[derive(Debug, Clone, PartialEq)] +pub struct FuncArgs { + /// The span of the whole argument list. + pub span: Span, + /// The positional arguments. + pub items: Vec<FuncArg>, +} + +/// An argument to a function call: `12` or `draw: false`. +#[derive(Debug, Clone, PartialEq)] +pub struct FuncArg { + /// The span of the whole argument. + pub span: Span, + /// The name of the argument (`None` for positional arguments). + pub name: Option<EcoString>, + /// The value of the argument. + pub value: Spanned<Value>, +} + +impl FuncArgs { + /// Find and consume the first castable positional argument. + pub fn eat<T>(&mut self, ctx: &mut EvalContext) -> Option<T> + where + T: Cast<Spanned<Value>>, + { + (0 .. self.items.len()).find_map(|index| { + let slot = &mut self.items[index]; + if slot.name.is_some() { + return None; + } + + let value = std::mem::replace(&mut slot.value, Spanned::zero(Value::None)); + let span = value.span; + + match T::cast(value) { + CastResult::Ok(t) => { + self.items.remove(index); + Some(t) + } + CastResult::Warn(t, m) => { + self.items.remove(index); + ctx.diag(warning!(span, "{}", m)); + Some(t) + } + CastResult::Err(value) => { + slot.value = value; + None + } + } + }) + } + + /// Find and consume the first castable positional argument, producing a + /// `missing argument: {what}` error if no match was found. + pub fn expect<T>(&mut self, ctx: &mut EvalContext, what: &str) -> Option<T> + where + T: Cast<Spanned<Value>>, + { + let found = self.eat(ctx); + if found.is_none() { + ctx.diag(error!(self.span, "missing argument: {}", what)); + } + found + } + + /// Find, consume and collect all castable positional arguments. + /// + /// This function returns a vector instead of an iterator because the + /// iterator would require unique access to the context, rendering it rather + /// unusable. If you need to process arguments one-by-one, you probably want + /// to use a while-let loop together with [`eat()`](Self::eat). + pub fn all<T>(&mut self, ctx: &mut EvalContext) -> Vec<T> + where + T: Cast<Spanned<Value>>, + { + std::iter::from_fn(|| self.eat(ctx)).collect() + } + + /// Cast and remove the value for the given named argument, producing an + /// error if the conversion fails. + pub fn named<T>(&mut self, ctx: &mut EvalContext, name: &str) -> Option<T> + where + T: Cast<Spanned<Value>>, + { + let index = self + .items + .iter() + .position(|arg| arg.name.as_ref().map_or(false, |other| other == name))?; + + let value = self.items.remove(index).value; + let span = value.span; + + match T::cast(value) { + CastResult::Ok(t) => Some(t), + CastResult::Warn(t, m) => { + ctx.diag(warning!(span, "{}", m)); + Some(t) + } + CastResult::Err(value) => { + ctx.diag(error!( + span, + "expected {}, found {}", + T::TYPE_NAME, + value.v.type_name(), + )); + None + } + } + } + + /// Produce "unexpected argument" errors for all remaining arguments. + pub fn finish(self, ctx: &mut EvalContext) { + for arg in &self.items { + if arg.value.v != Value::Error { + ctx.diag(error!(arg.span, "unexpected argument")); + } + } + } +} diff --git a/src/eval/mod.rs b/src/eval/mod.rs index dc4ab7ee..689234bd 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -1,13 +1,23 @@ //! Evaluation of syntax trees. #[macro_use] +mod array; +#[macro_use] +mod dict; +#[macro_use] mod value; mod capture; +mod function; mod ops; mod scope; +mod template; +pub use array::*; pub use capture::*; +pub use dict::*; +pub use function::*; pub use scope::*; +pub use template::*; pub use value::*; use std::collections::HashMap; @@ -45,7 +55,7 @@ pub struct Module { /// The top-level definitions that were bound in this module. pub scope: Scope, /// The template defined by this module. - pub template: TemplateValue, + pub template: Template, } /// The context for evaluation. @@ -213,7 +223,7 @@ pub trait Eval { } impl Eval for Rc<SyntaxTree> { - type Output = TemplateValue; + type Output = Template; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { struct ExprVisitor<'a, 'b> { @@ -230,10 +240,7 @@ impl Eval for Rc<SyntaxTree> { let mut visitor = ExprVisitor { ctx, map: ExprMap::new() }; visitor.visit_tree(self); - Rc::new(vec![TemplateNode::Tree { - tree: Rc::clone(self), - map: visitor.map, - }]) + TemplateTree { tree: Rc::clone(self), map: visitor.map }.into() } } @@ -280,7 +287,7 @@ impl Eval for Expr { } impl Eval for ArrayExpr { - type Output = ArrayValue; + type Output = Array; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { self.items.iter().map(|expr| expr.eval(ctx)).collect() @@ -288,7 +295,7 @@ impl Eval for ArrayExpr { } impl Eval for DictExpr { - type Output = DictValue; + type Output = Dict; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { self.items @@ -299,7 +306,7 @@ impl Eval for DictExpr { } impl Eval for TemplateExpr { - type Output = TemplateValue; + type Output = Template; fn eval(&self, ctx: &mut EvalContext) -> Self::Output { self.tree.eval(ctx) @@ -476,7 +483,7 @@ impl Eval for CallExpr { fn eval(&self, ctx: &mut EvalContext) -> Self::Output { let callee = self.callee.eval(ctx); - if let Some(func) = ctx.cast::<FuncValue>(callee, self.callee.span()) { + if let Some(func) = ctx.cast::<Function>(callee, self.callee.span()) { let mut args = self.args.eval(ctx); let returned = func(ctx, &mut args); args.finish(ctx); @@ -530,7 +537,7 @@ impl Eval for ClosureExpr { }; let name = self.name.as_ref().map(|name| name.string.clone()); - Value::Func(FuncValue::new(name, move |ctx, args| { + Value::Func(Function::new(name, move |ctx, args| { // Don't leak the scopes from the call site. Instead, we use the // scope of captured variables we collected earlier. let prev = mem::take(&mut ctx.scopes); @@ -554,10 +561,10 @@ impl Eval for WithExpr { fn eval(&self, ctx: &mut EvalContext) -> Self::Output { let callee = self.callee.eval(ctx); - if let Some(func) = ctx.cast::<FuncValue>(callee, self.callee.span()) { + if let Some(func) = ctx.cast::<Function>(callee, self.callee.span()) { let applied = self.args.eval(ctx); let name = func.name().cloned(); - Value::Func(FuncValue::new(name, move |ctx, args| { + Value::Func(Function::new(name, move |ctx, args| { // Remove named arguments that were overridden. let kept: Vec<_> = applied .items diff --git a/src/eval/ops.rs b/src/eval/ops.rs index 3b48140c..c1dd726b 100644 --- a/src/eval/ops.rs +++ b/src/eval/ops.rs @@ -1,7 +1,6 @@ use std::cmp::Ordering::*; -use std::rc::Rc; -use super::{TemplateNode, Value}; +use super::Value; use Value::*; /// Apply the plus operator to a value. @@ -90,11 +89,6 @@ pub fn sub(lhs: Value, rhs: Value) -> Value { /// Compute the product of two values. pub fn mul(lhs: Value, rhs: Value) -> Value { - fn repeat<T: Clone>(vec: Vec<T>, n: usize) -> Vec<T> { - let len = n * vec.len(); - vec.into_iter().cycle().take(len).collect() - } - match (lhs, rhs) { (Int(a), Int(b)) => Int(a * b), (Int(a), Float(b)) => Float(a as f64 * b), @@ -128,8 +122,8 @@ pub fn mul(lhs: Value, rhs: Value) -> Value { (Str(a), Int(b)) => Str(a.repeat(b.max(0) as usize)), (Int(a), Str(b)) => Str(b.repeat(a.max(0) as usize)), - (Array(a), Int(b)) => Array(repeat(a, b.max(0) as usize)), - (Int(a), Array(b)) => Array(repeat(b, a.max(0) as usize)), + (Array(a), Int(b)) => Array(a.repeat(b.max(0) as usize)), + (Int(a), Array(b)) => Array(b.repeat(a.max(0) as usize)), _ => Error, } @@ -240,26 +234,11 @@ pub fn join(lhs: Value, rhs: Value) -> Result<Value, Value> { fn concat(lhs: Value, rhs: Value) -> Result<Value, Value> { Ok(match (lhs, rhs) { (Str(a), Str(b)) => Str(a + &b), - (Array(mut a), Array(b)) => Array({ - a.extend(b); - a - }), - (Dict(mut a), Dict(b)) => Dict({ - a.extend(b); - a - }), - (Template(mut a), Template(b)) => Template({ - Rc::make_mut(&mut a).extend(b.iter().cloned()); - a - }), - (Template(mut a), Str(b)) => Template({ - Rc::make_mut(&mut a).push(TemplateNode::Str(b)); - a - }), - (Str(a), Template(mut b)) => Template({ - Rc::make_mut(&mut b).insert(0, TemplateNode::Str(a)); - b - }), + (Array(a), Array(b)) => Array(a + &b), + (Dict(a), Dict(b)) => Dict(a + &b), + (Template(a), Template(b)) => Template(a + &b), + (Template(a), Str(b)) => Template(a + b), + (Str(a), Template(b)) => Template(a + b), (a, _) => return Err(a), }) } diff --git a/src/eval/scope.rs b/src/eval/scope.rs index 05bbeda2..4a9d5970 100644 --- a/src/eval/scope.rs +++ b/src/eval/scope.rs @@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Display, Formatter}; use std::iter; use std::rc::Rc; -use super::{AnyValue, EcoString, EvalContext, FuncArgs, FuncValue, Type, Value}; +use super::{AnyValue, EcoString, EvalContext, FuncArgs, Function, Type, Value}; /// A slot where a variable is stored. pub type Slot = Rc<RefCell<Value>>; @@ -92,7 +92,7 @@ impl Scope { F: Fn(&mut EvalContext, &mut FuncArgs) -> Value + 'static, { let name = name.into(); - self.def_const(name.clone(), FuncValue::new(Some(name), f)); + self.def_const(name.clone(), Function::new(Some(name), f)); } /// Define a constant variable with a value of variant `Value::Any`. diff --git a/src/eval/template.rs b/src/eval/template.rs new file mode 100644 index 00000000..4d003998 --- /dev/null +++ b/src/eval/template.rs @@ -0,0 +1,139 @@ +use std::collections::HashMap; +use std::fmt::{self, Debug, Formatter}; +use std::ops::{Add, Deref}; +use std::rc::Rc; + +use super::Value; +use crate::eco::EcoString; +use crate::exec::ExecContext; +use crate::syntax::{Expr, SyntaxTree}; + +/// A template value: `[*Hi* there]`. +#[derive(Default, Debug, Clone)] +pub struct Template { + nodes: Rc<Vec<TemplateNode>>, +} + +impl Template { + /// Create a new template from a vector of nodes. + pub fn new(nodes: Vec<TemplateNode>) -> Self { + Self { nodes: Rc::new(nodes) } + } + + /// Iterate over the contained template nodes. + pub fn iter(&self) -> impl Iterator<Item = &TemplateNode> + '_ { + self.nodes.iter() + } +} + +impl From<TemplateTree> for Template { + fn from(tree: TemplateTree) -> Self { + Self::new(vec![TemplateNode::Tree(tree)]) + } +} + +impl From<TemplateFunc> for Template { + fn from(func: TemplateFunc) -> Self { + Self::new(vec![TemplateNode::Func(func)]) + } +} + +impl From<EcoString> for Template { + fn from(string: EcoString) -> Self { + Self::new(vec![TemplateNode::Str(string)]) + } +} + +impl PartialEq for Template { + fn eq(&self, other: &Self) -> bool { + Rc::ptr_eq(&self.nodes, &other.nodes) + } +} + +impl Add<&Template> for Template { + type Output = Self; + + fn add(mut self, rhs: &Self) -> Self::Output { + Rc::make_mut(&mut self.nodes).extend(rhs.nodes.iter().cloned()); + self + } +} + +impl Add<EcoString> for Template { + type Output = Self; + + fn add(mut self, rhs: EcoString) -> Self::Output { + Rc::make_mut(&mut self.nodes).push(TemplateNode::Str(rhs)); + self + } +} + +impl Add<Template> for EcoString { + type Output = Template; + + fn add(self, mut rhs: Template) -> Self::Output { + Rc::make_mut(&mut rhs.nodes).insert(0, TemplateNode::Str(self)); + rhs + } +} + +/// One node of a template. +/// +/// Evaluating a template expression creates only a single node. Adding multiple +/// templates can yield multi-node templates. +#[derive(Debug, Clone)] +pub enum TemplateNode { + /// A template that was evaluated from a template expression. + Tree(TemplateTree), + /// A function template that can implement custom behaviour. + Func(TemplateFunc), + /// A template that was converted from a string. + Str(EcoString), +} + +/// A template that consists of a syntax tree plus already evaluated +/// expressions. +#[derive(Debug, Clone)] +pub struct TemplateTree { + /// The syntax tree of the corresponding template expression. + pub tree: Rc<SyntaxTree>, + /// The evaluated expressions in the syntax tree. + pub map: ExprMap, +} + +/// A map from expressions to the values they evaluated to. +/// +/// The raw pointers point into the expressions contained in some +/// [`SyntaxTree`]. Since the lifetime is erased, the tree could go out of scope +/// while the hash map still lives. Although this could lead to lookup panics, +/// it is not unsafe since the pointers are never dereferenced. +pub type ExprMap = HashMap<*const Expr, Value>; + +/// A reference-counted dynamic template node that can implement custom +/// behaviour. +#[derive(Clone)] +pub struct TemplateFunc(Rc<dyn Fn(&mut ExecContext)>); + +impl TemplateFunc { + /// Create a new function template from a rust function or closure. + pub fn new<F>(f: F) -> Self + where + F: Fn(&mut ExecContext) + 'static, + { + Self(Rc::new(f)) + } +} + +impl Deref for TemplateFunc { + type Target = dyn Fn(&mut ExecContext); + + fn deref(&self) -> &Self::Target { + self.0.as_ref() + } +} + +impl Debug for TemplateFunc { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_struct("TemplateFunc").finish() + } +} diff --git a/src/eval/value.rs b/src/eval/value.rs index 2881399b..fe9494b1 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -1,15 +1,13 @@ use std::any::Any; use std::cmp::Ordering; -use std::collections::{BTreeMap, HashMap}; use std::fmt::{self, Debug, Display, Formatter}; -use std::ops::Deref; -use std::rc::Rc; -use super::*; +use super::{ops, Array, Dict, EvalContext, Function, Template, TemplateFunc}; use crate::color::{Color, RgbaColor}; +use crate::eco::EcoString; use crate::exec::ExecContext; use crate::geom::{Angle, Fractional, Length, Linear, Relative}; -use crate::syntax::{Expr, Span, Spanned, SyntaxTree}; +use crate::syntax::{Span, Spanned}; /// A computational value. #[derive(Debug, Clone, PartialEq)] @@ -38,14 +36,14 @@ pub enum Value { Color(Color), /// A string: `"string"`. Str(EcoString), - /// An array value: `(1, "hi", 12cm)`. - Array(ArrayValue), + /// An array of values: `(1, "hi", 12cm)`. + Array(Array), /// A dictionary value: `(color: #f79143, pattern: dashed)`. - Dict(DictValue), + Dict(Dict), /// A template value: `[*Hi* there]`. - Template(TemplateValue), + Template(Template), /// An executable function. - Func(FuncValue), + Func(Function), /// Any object. Any(AnyValue), /// The result of invalid operations. @@ -53,12 +51,12 @@ pub enum Value { } impl Value { - /// Create a new template value consisting of a single dynamic node. + /// Create a new template consisting of a single function node. pub fn template<F>(f: F) -> Self where F: Fn(&mut ExecContext) + 'static, { - Self::Template(Rc::new(vec![TemplateNode::Func(TemplateFunc::new(f))])) + Self::Template(TemplateFunc::new(f).into()) } /// The name of the stored value's type. @@ -76,10 +74,10 @@ impl Value { Self::Fractional(_) => Fractional::TYPE_NAME, Self::Color(_) => Color::TYPE_NAME, Self::Str(_) => EcoString::TYPE_NAME, - Self::Array(_) => ArrayValue::TYPE_NAME, - Self::Dict(_) => DictValue::TYPE_NAME, - Self::Template(_) => TemplateValue::TYPE_NAME, - Self::Func(_) => FuncValue::TYPE_NAME, + Self::Array(_) => Array::TYPE_NAME, + Self::Dict(_) => Dict::TYPE_NAME, + Self::Template(_) => Template::TYPE_NAME, + Self::Func(_) => Function::TYPE_NAME, Self::Any(v) => v.type_name(), Self::Error => "error", } @@ -101,7 +99,6 @@ impl Value { a.len() == b.len() && a.iter().all(|(k, x)| b.get(k).map_or(false, |y| x.eq(y))) } - (Self::Template(a), Self::Template(b)) => Rc::ptr_eq(a, b), (a, b) => a == b, } } @@ -146,245 +143,6 @@ impl Default for Value { } } -/// An array value: `(1, "hi", 12cm)`. -pub type ArrayValue = Vec<Value>; - -/// A dictionary value: `(color: #f79143, pattern: dashed)`. -pub type DictValue = BTreeMap<EcoString, Value>; - -/// A template value: `[*Hi* there]`. -pub type TemplateValue = Rc<Vec<TemplateNode>>; - -/// One chunk of a template. -/// -/// Evaluating a template expression creates only a single node. Adding multiple -/// templates can yield multi-node templates. -#[derive(Debug, Clone)] -pub enum TemplateNode { - /// A template that consists of a syntax tree plus already evaluated - /// expression. - Tree { - /// The syntax tree of the corresponding template expression. - tree: Rc<SyntaxTree>, - /// The evaluated expressions in the syntax tree. - map: ExprMap, - }, - /// A template that was converted from a string. - Str(EcoString), - /// A function template that can implement custom behaviour. - Func(TemplateFunc), -} - -impl PartialEq for TemplateNode { - fn eq(&self, _: &Self) -> bool { - false - } -} - -/// A map from expressions to the values they evaluated to. -/// -/// The raw pointers point into the expressions contained in some -/// [`SyntaxTree`]. Since the lifetime is erased, the tree could go out of scope -/// while the hash map still lives. Although this could lead to lookup panics, -/// it is not unsafe since the pointers are never dereferenced. -pub type ExprMap = HashMap<*const Expr, Value>; - -/// A reference-counted dynamic template node that can implement custom -/// behaviour. -#[derive(Clone)] -pub struct TemplateFunc(Rc<dyn Fn(&mut ExecContext)>); - -impl TemplateFunc { - /// Create a new function template from a rust function or closure. - pub fn new<F>(f: F) -> Self - where - F: Fn(&mut ExecContext) + 'static, - { - Self(Rc::new(f)) - } -} - -impl Deref for TemplateFunc { - type Target = dyn Fn(&mut ExecContext); - - fn deref(&self) -> &Self::Target { - self.0.as_ref() - } -} - -impl Debug for TemplateFunc { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.debug_struct("TemplateAny").finish() - } -} - -/// A wrapper around a reference-counted executable function. -#[derive(Clone)] -pub struct FuncValue { - /// The string is boxed to make the whole struct fit into 24 bytes, so that - /// a [`Value`] fits into 32 bytes. - name: Option<Box<EcoString>>, - /// The closure that defines the function. - f: Rc<dyn Fn(&mut EvalContext, &mut FuncArgs) -> Value>, -} - -impl FuncValue { - /// Create a new function value from a rust function or closure. - pub fn new<F>(name: Option<EcoString>, f: F) -> Self - where - F: Fn(&mut EvalContext, &mut FuncArgs) -> Value + 'static, - { - Self { name: name.map(Box::new), f: Rc::new(f) } - } - - /// The name of the function. - pub fn name(&self) -> Option<&EcoString> { - self.name.as_ref().map(|s| &**s) - } -} - -impl PartialEq for FuncValue { - fn eq(&self, other: &Self) -> bool { - // We cast to thin pointers because we don't want to compare vtables. - Rc::as_ptr(&self.f) as *const () == Rc::as_ptr(&other.f) as *const () - } -} - -impl Deref for FuncValue { - type Target = dyn Fn(&mut EvalContext, &mut FuncArgs) -> Value; - - fn deref(&self) -> &Self::Target { - self.f.as_ref() - } -} - -impl Debug for FuncValue { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.debug_struct("ValueFunc").field("name", &self.name).finish() - } -} - -/// Evaluated arguments to a function. -#[derive(Debug, Clone, PartialEq)] -pub struct FuncArgs { - /// The span of the whole argument list. - pub span: Span, - /// The positional arguments. - pub items: Vec<FuncArg>, -} - -impl FuncArgs { - /// Find and consume the first castable positional argument. - pub fn eat<T>(&mut self, ctx: &mut EvalContext) -> Option<T> - where - T: Cast<Spanned<Value>>, - { - (0 .. self.items.len()).find_map(|index| { - let slot = &mut self.items[index]; - if slot.name.is_some() { - return None; - } - - let value = std::mem::replace(&mut slot.value, Spanned::zero(Value::None)); - let span = value.span; - - match T::cast(value) { - CastResult::Ok(t) => { - self.items.remove(index); - Some(t) - } - CastResult::Warn(t, m) => { - self.items.remove(index); - ctx.diag(warning!(span, "{}", m)); - Some(t) - } - CastResult::Err(value) => { - slot.value = value; - None - } - } - }) - } - - /// Find and consume the first castable positional argument, producing a - /// `missing argument: {what}` error if no match was found. - pub fn expect<T>(&mut self, ctx: &mut EvalContext, what: &str) -> Option<T> - where - T: Cast<Spanned<Value>>, - { - let found = self.eat(ctx); - if found.is_none() { - ctx.diag(error!(self.span, "missing argument: {}", what)); - } - found - } - - /// Find, consume and collect all castable positional arguments. - /// - /// This function returns a vector instead of an iterator because the - /// iterator would require unique access to the context, rendering it rather - /// unusable. If you need to process arguments one-by-one, you probably want - /// to use a while-let loop together with [`eat()`](Self::eat). - pub fn all<T>(&mut self, ctx: &mut EvalContext) -> Vec<T> - where - T: Cast<Spanned<Value>>, - { - std::iter::from_fn(|| self.eat(ctx)).collect() - } - - /// Cast and remove the value for the given named argument, producing an - /// error if the conversion fails. - pub fn named<T>(&mut self, ctx: &mut EvalContext, name: &str) -> Option<T> - where - T: Cast<Spanned<Value>>, - { - let index = self - .items - .iter() - .position(|arg| arg.name.as_ref().map_or(false, |other| other == name))?; - - let value = self.items.remove(index).value; - let span = value.span; - - match T::cast(value) { - CastResult::Ok(t) => Some(t), - CastResult::Warn(t, m) => { - ctx.diag(warning!(span, "{}", m)); - Some(t) - } - CastResult::Err(value) => { - ctx.diag(error!( - span, - "expected {}, found {}", - T::TYPE_NAME, - value.v.type_name(), - )); - None - } - } - } - - /// Produce "unexpected argument" errors for all remaining arguments. - pub fn finish(self, ctx: &mut EvalContext) { - for arg in &self.items { - if arg.value.v != Value::Error { - ctx.diag(error!(arg.span, "unexpected argument")); - } - } - } -} - -/// An argument to a function call: `12` or `draw: false`. -#[derive(Debug, Clone, PartialEq)] -pub struct FuncArg { - /// The span of the whole argument. - pub span: Span, - /// The name of the argument (`None` for positional arguments). - pub name: Option<EcoString>, - /// The value of the argument. - pub value: Spanned<Value>, -} - /// A wrapper around a dynamic value. pub struct AnyValue(Box<dyn Bounds>); @@ -422,15 +180,9 @@ impl AnyValue { } } -impl Clone for AnyValue { - fn clone(&self) -> Self { - Self(self.0.dyn_clone()) - } -} - -impl PartialEq for AnyValue { - fn eq(&self, other: &Self) -> bool { - self.0.dyn_eq(other) +impl Display for AnyValue { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(&self.0, f) } } @@ -440,9 +192,15 @@ impl Debug for AnyValue { } } -impl Display for AnyValue { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Display::fmt(&self.0, f) +impl Clone for AnyValue { + fn clone(&self) -> Self { + Self(self.0.dyn_clone()) + } +} + +impl PartialEq for AnyValue { + fn eq(&self, other: &Self) -> bool { + self.0.dyn_eq(other) } } @@ -608,14 +366,20 @@ primitive! { primitive! { Fractional: "fractional", Value::Fractional } primitive! { Color: "color", Value::Color } primitive! { EcoString: "string", Value::Str } -primitive! { ArrayValue: "array", Value::Array } -primitive! { DictValue: "dictionary", Value::Dict } +primitive! { Array: "array", Value::Array } +primitive! { Dict: "dictionary", Value::Dict } primitive! { - TemplateValue: "template", + Template: "template", Value::Template, - Value::Str(v) => Rc::new(vec![TemplateNode::Str(v)]), + Value::Str(v) => v.into(), +} +primitive! { Function: "function", Value::Func } + +impl From<i32> for Value { + fn from(v: i32) -> Self { + Self::Int(v as i64) + } } -primitive! { FuncValue: "function", Value::Func } impl From<usize> for Value { fn from(v: usize) -> Self { |
