summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-07-10 20:01:18 +0200
committerLaurenz <laurmaedje@gmail.com>2021-07-10 23:10:17 +0200
commit6a4823461f491aef63451f097ddfe5602e0b2157 (patch)
treead11b0ad169d030942d950573c729d50f7b3291b /src/eval
parent36b3067c19c8743032a44f888ee48702b88d135b (diff)
Reference-count complex values
Rename some nodes types
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/array.rs148
-rw-r--r--src/eval/dict.rs129
-rw-r--r--src/eval/function.rs176
-rw-r--r--src/eval/mod.rs33
-rw-r--r--src/eval/ops.rs37
-rw-r--r--src/eval/scope.rs4
-rw-r--r--src/eval/template.rs139
-rw-r--r--src/eval/value.rs310
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 {