diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-01-02 19:37:10 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-01-02 19:37:10 +0100 |
| commit | 1c40dc42e7bc7b799b77f06d25414aca59a044ba (patch) | |
| tree | ea8bdedaebf59f5bc601346b0108236c7264a29d /src/eval | |
| parent | 8cad78481cd52680317032c3bb84cacda5666489 (diff) | |
Dynamic values, Types, Arrays, and Dictionaries 🚀
- Identifiers are now evaluated as variables instead of being plain values
- Constants like `left` or `bold` are stored as dynamic values containing the respective rust types
- We now distinguish between arrays and dictionaries to make things more intuitive (at the cost of a bit more complex parsing)
- Spans were removed from collections (arrays, dictionaries), function arguments still have spans for the top-level values to enable good diagnostics
Diffstat (limited to 'src/eval')
| -rw-r--r-- | src/eval/args.rs | 246 | ||||
| -rw-r--r-- | src/eval/dict.rs | 522 | ||||
| -rw-r--r-- | src/eval/mod.rs | 95 | ||||
| -rw-r--r-- | src/eval/scope.rs | 18 | ||||
| -rw-r--r-- | src/eval/value.rs | 481 |
5 files changed, 473 insertions, 889 deletions
diff --git a/src/eval/args.rs b/src/eval/args.rs index 43c30daf..ea248ec4 100644 --- a/src/eval/args.rs +++ b/src/eval/args.rs @@ -1,184 +1,138 @@ -//! Simplifies argument parsing. +use super::*; + +/// Evaluated arguments to a function. +#[derive(Debug)] +pub struct Args { + span: Span, + pos: SpanVec<Value>, + named: Vec<(Spanned<String>, Spanned<Value>)>, +} -use std::mem; +impl Eval for Spanned<&Arguments> { + type Output = Args; -use super::{Conv, EvalContext, RefKey, TryFromValue, Value, ValueDict}; -use crate::diag::Diag; -use crate::syntax::{Span, SpanVec, Spanned, WithSpan}; + fn eval(self, ctx: &mut EvalContext) -> Self::Output { + let mut pos = vec![]; + let mut named = vec![]; -/// A wrapper around a dictionary value that simplifies argument parsing in -/// functions. -pub struct Args(pub Spanned<ValueDict>); + for arg in self.v { + match arg { + Argument::Pos(expr) => { + pos.push(expr.as_ref().eval(ctx).with_span(expr.span)); + } + Argument::Named(Named { name, expr }) => { + named.push(( + name.as_ref().map(|id| id.0.clone()), + expr.as_ref().eval(ctx).with_span(expr.span), + )); + } + } + } -impl Args { - /// Retrieve and remove the argument associated with the given key if there - /// is any. - /// - /// Generates an error if the key exists, but the value can't be converted - /// into the type `T`. - pub fn get<'a, K, T>(&mut self, ctx: &mut EvalContext, key: K) -> Option<T> - where - K: Into<RefKey<'a>>, - T: TryFromValue, - { - self.0.v.remove(key).and_then(|entry| { - let span = entry.value.span; - conv_diag( - T::try_from_value(entry.value), - &mut ctx.feedback.diags, - span, - ) - }) + Args { span: self.span, pos, named } } +} - /// This is the same as [`get`](Self::get), except that it generates an error about a - /// missing argument with the given `name` if the key does not exist. - pub fn need<'a, K, T>( - &mut self, - ctx: &mut EvalContext, - key: K, - name: &str, - ) -> Option<T> +impl Args { + /// Find the first convertible positional argument. + pub fn find<T>(&mut self, ctx: &mut EvalContext) -> Option<T> where - K: Into<RefKey<'a>>, - T: TryFromValue, + T: Cast<Spanned<Value>>, { - if let Some(entry) = self.0.v.remove(key) { - let span = entry.value.span; - conv_diag( - T::try_from_value(entry.value), - &mut ctx.feedback.diags, - span, - ) - } else { - ctx.diag(error!(self.0.span, "missing argument: {}", name)); - None - } + self.pos.iter_mut().find_map(move |slot| try_cast(ctx, slot)) } - /// Retrieve and remove the first matching positional argument. - pub fn find<T>(&mut self) -> Option<T> + /// Find the first convertible positional argument, producing an error if + /// no match was found. + pub fn require<T>(&mut self, ctx: &mut EvalContext, what: &str) -> Option<T> where - T: TryFromValue, + T: Cast<Spanned<Value>>, { - for (&key, entry) in self.0.v.nums_mut() { - let span = entry.value.span; - let slot = &mut entry.value; - let conv = conv_put_back(T::try_from_value(mem::take(slot)), slot, span); - if let Some(t) = conv { - self.0.v.remove(key); - return Some(t); - } + let found = self.find(ctx); + if found.is_none() { + ctx.diag(error!(self.span, "missing argument: {}", what)); } - None + found } - /// Retrieve and remove all matching positional arguments. - pub fn find_all<T>(&mut self) -> impl Iterator<Item = T> + '_ + /// Filter out all convertible positional arguments. + pub fn filter<'a, T>( + &'a mut self, + ctx: &'a mut EvalContext, + ) -> impl Iterator<Item = T> + 'a where - T: TryFromValue, + T: Cast<Spanned<Value>>, { - let mut skip = 0; - std::iter::from_fn(move || { - for (&key, entry) in self.0.v.nums_mut().skip(skip) { - let span = entry.value.span; - let slot = &mut entry.value; - let conv = conv_put_back(T::try_from_value(mem::take(slot)), slot, span); - if let Some(t) = conv { - self.0.v.remove(key); - return Some(t); - } - skip += 1; - } - None - }) + self.pos.iter_mut().filter_map(move |slot| try_cast(ctx, slot)) } - /// Retrieve and remove all matching named arguments. - pub fn find_all_str<T>(&mut self) -> impl Iterator<Item = (String, T)> + '_ + /// Convert the value for the given named argument. + /// + /// Generates an error if the conversion fails. + pub fn get<'a, T>(&mut self, ctx: &mut EvalContext, name: &str) -> Option<T> where - T: TryFromValue, + T: Cast<Spanned<Value>>, { - let mut skip = 0; - std::iter::from_fn(move || { - for (key, entry) in self.0.v.strs_mut().skip(skip) { - let span = entry.value.span; - let slot = &mut entry.value; - let conv = conv_put_back(T::try_from_value(mem::take(slot)), slot, span); - if let Some(t) = conv { - let key = key.clone(); - self.0.v.remove(&key); - return Some((key, t)); - } - skip += 1; - } - - None - }) + let index = self.named.iter().position(|(k, _)| k.v.as_str() == name)?; + let value = self.named.remove(index).1; + cast(ctx, value) } - /// Generated _unexpected argument_ errors for all remaining entries. - pub fn done(&self, ctx: &mut EvalContext) { - for entry in self.0.v.values() { - let span = entry.key_span.join(entry.value.span); - ctx.diag(error!(span, "unexpected argument")); + /// Generate "unexpected argument" errors for all remaining arguments. + pub fn finish(self, ctx: &mut EvalContext) { + let a = self.pos.iter().map(|v| v.as_ref()); + let b = self.named.iter().map(|(k, v)| (&v.v).with_span(k.span.join(v.span))); + for value in a.chain(b) { + if value.v != &Value::Error { + ctx.diag(error!(value.span, "unexpected argument")); + } } } } -fn conv_diag<T>(conv: Conv<T>, diags: &mut SpanVec<Diag>, span: Span) -> Option<T> { - match conv { - Conv::Ok(t) => Some(t), - Conv::Warn(t, warn) => { - diags.push(warn.with_span(span)); +/// Cast the value into `T`, generating an error if the conversion fails. +fn cast<T>(ctx: &mut EvalContext, value: Spanned<Value>) -> Option<T> +where + T: Cast<Spanned<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) } - Conv::Err(_, err) => { - diags.push(err.with_span(span)); + CastResult::Err(value) => { + ctx.diag(error!( + span, + "expected {}, found {}", + T::TYPE_NAME, + value.v.type_name() + )); None } } } -fn conv_put_back<T>(conv: Conv<T>, slot: &mut Spanned<Value>, span: Span) -> Option<T> { - match conv { - Conv::Ok(t) => Some(t), - Conv::Warn(t, _) => Some(t), - Conv::Err(v, _) => { - *slot = v.with_span(span); +/// Try to cast the value in the slot into `T`, putting it back if the +/// conversion fails. +fn try_cast<T>(ctx: &mut EvalContext, slot: &mut Spanned<Value>) -> Option<T> +where + T: Cast<Spanned<Value>>, +{ + // Replace with error placeholder when conversion works since error values + // are ignored when generating "unexpected argument" errors. + let value = std::mem::replace(slot, Spanned::zero(Value::Error)); + 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) => { + *slot = value; None } } } - -#[cfg(test)] -mod tests { - use super::super::{Dict, SpannedEntry, Value}; - use super::*; - - fn entry(value: Value) -> SpannedEntry<Value> { - SpannedEntry::value(Spanned::zero(value)) - } - - #[test] - fn test_args_find() { - let mut args = Args(Spanned::zero(Dict::new())); - args.0.v.insert(1, entry(Value::Bool(false))); - args.0.v.insert(2, entry(Value::Str("hi".to_string()))); - assert_eq!(args.find::<String>(), Some("hi".to_string())); - assert_eq!(args.0.v.len(), 1); - assert_eq!(args.find::<bool>(), Some(false)); - assert!(args.0.v.is_empty()); - } - - #[test] - fn test_args_find_all() { - let mut args = Args(Spanned::zero(Dict::new())); - args.0.v.insert(1, entry(Value::Bool(false))); - args.0.v.insert(3, entry(Value::Float(0.0))); - args.0.v.insert(7, entry(Value::Bool(true))); - assert_eq!(args.find_all::<bool>().collect::<Vec<_>>(), [false, true]); - assert_eq!(args.0.v.len(), 1); - assert_eq!(args.0.v[3].value.v, Value::Float(0.0)); - } -} diff --git a/src/eval/dict.rs b/src/eval/dict.rs deleted file mode 100644 index efa5cb37..00000000 --- a/src/eval/dict.rs +++ /dev/null @@ -1,522 +0,0 @@ -//! A key-value map that can also model array-like structures. - -use std::collections::BTreeMap; -use std::fmt::{self, Debug, Display, Formatter}; -use std::iter::{Extend, FromIterator}; -use std::ops::Index; - -use crate::syntax::{Span, Spanned}; - -/// A dictionary data structure, which maps from integers and strings to a -/// generic value type. -/// -/// The dictionary can be used to model arrays by assigning values to successive -/// indices from `0..n`. The `push` method offers special support for this -/// pattern. -#[derive(Clone)] -pub struct Dict<V> { - nums: BTreeMap<u64, V>, - strs: BTreeMap<String, V>, - lowest_free: u64, -} - -impl<V> Dict<V> { - /// Create a new empty dictionary. - pub fn new() -> Self { - Self { - nums: BTreeMap::new(), - strs: BTreeMap::new(), - lowest_free: 0, - } - } - - /// The total number of entries in the dictionary. - pub fn len(&self) -> usize { - self.nums.len() + self.strs.len() - } - - /// Whether the dictionary contains no entries. - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// The first number key-value pair (with lowest number). - pub fn first(&self) -> Option<(u64, &V)> { - self.nums.iter().next().map(|(&k, v)| (k, v)) - } - - /// The last number key-value pair (with highest number). - pub fn last(&self) -> Option<(u64, &V)> { - self.nums.iter().next_back().map(|(&k, v)| (k, v)) - } - - /// Get a reference to the value with the given key. - pub fn get<'a, K>(&self, key: K) -> Option<&V> - where - K: Into<RefKey<'a>>, - { - match key.into() { - RefKey::Num(num) => self.nums.get(&num), - RefKey::Str(string) => self.strs.get(string), - } - } - - /// Borrow the value with the given key mutably. - pub fn get_mut<'a, K>(&mut self, key: K) -> Option<&mut V> - where - K: Into<RefKey<'a>>, - { - match key.into() { - RefKey::Num(num) => self.nums.get_mut(&num), - RefKey::Str(string) => self.strs.get_mut(string), - } - } - - /// Insert a value into the dictionary. - pub fn insert<K>(&mut self, key: K, value: V) - where - K: Into<DictKey>, - { - match key.into() { - DictKey::Num(num) => { - self.nums.insert(num, value); - if self.lowest_free == num { - self.lowest_free += 1; - } - } - DictKey::Str(string) => { - self.strs.insert(string, value); - } - } - } - - /// Remove the value with the given key from the dictionary. - pub fn remove<'a, K>(&mut self, key: K) -> Option<V> - where - K: Into<RefKey<'a>>, - { - match key.into() { - RefKey::Num(num) => { - self.lowest_free = self.lowest_free.min(num); - self.nums.remove(&num) - } - RefKey::Str(string) => self.strs.remove(string), - } - } - - /// Append a value to the dictionary. - /// - /// This will associate the `value` with the lowest free number key (zero if - /// there is no number key so far). - pub fn push(&mut self, value: V) { - while self.nums.contains_key(&self.lowest_free) { - self.lowest_free += 1; - } - self.nums.insert(self.lowest_free, value); - self.lowest_free += 1; - } -} - -impl<'a, K, V> Index<K> for Dict<V> -where - K: Into<RefKey<'a>>, -{ - type Output = V; - - fn index(&self, index: K) -> &Self::Output { - self.get(index).expect("key not in dict") - } -} - -impl<V: Eq> Eq for Dict<V> {} - -impl<V: PartialEq> PartialEq for Dict<V> { - fn eq(&self, other: &Self) -> bool { - self.iter().eq(other.iter()) - } -} - -impl<V> Default for Dict<V> { - fn default() -> Self { - Self::new() - } -} - -impl<V: Debug> Debug for Dict<V> { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - if self.is_empty() { - return f.write_str("()"); - } - - let mut builder = f.debug_tuple(""); - - struct Entry<'a>(bool, &'a dyn Display, &'a dyn Debug); - impl<'a> Debug for Entry<'a> { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - if self.0 { - f.write_str("\"")?; - } - self.1.fmt(f)?; - if self.0 { - f.write_str("\"")?; - } - - f.write_str(": ")?; - - self.2.fmt(f) - } - } - - for (key, value) in self.nums() { - builder.field(&Entry(false, &key, &value)); - } - - for (key, value) in self.strs() { - builder.field(&Entry(key.contains(' '), &key, &value)); - } - - builder.finish() - } -} - -/// Iteration. -impl<V> Dict<V> { - /// Iterator over all borrowed keys and values. - pub fn iter(&self) -> impl Iterator<Item = (RefKey, &V)> { - self.into_iter() - } - - /// Iterator over all borrowed keys and values. - pub fn iter_mut(&mut self) -> impl Iterator<Item = (RefKey, &mut V)> { - self.into_iter() - } - - /// Iterate over all values in the dictionary. - pub fn values(&self) -> impl Iterator<Item = &V> { - self.nums().map(|(_, v)| v).chain(self.strs().map(|(_, v)| v)) - } - - /// Iterate over all values in the dictionary. - pub fn values_mut(&mut self) -> impl Iterator<Item = &mut V> { - self.nums - .iter_mut() - .map(|(_, v)| v) - .chain(self.strs.iter_mut().map(|(_, v)| v)) - } - - /// Move into an owned iterator over all values in the dictionary. - pub fn into_values(self) -> impl Iterator<Item = V> { - self.nums - .into_iter() - .map(|(_, v)| v) - .chain(self.strs.into_iter().map(|(_, v)| v)) - } - - /// Iterate over the number key-value pairs. - pub fn nums(&self) -> std::collections::btree_map::Iter<u64, V> { - self.nums.iter() - } - - /// Iterate mutably over the number key-value pairs. - pub fn nums_mut(&mut self) -> std::collections::btree_map::IterMut<u64, V> { - self.nums.iter_mut() - } - - /// Iterate over the number key-value pairs. - pub fn into_nums(self) -> std::collections::btree_map::IntoIter<u64, V> { - self.nums.into_iter() - } - - /// Iterate over the string key-value pairs. - pub fn strs(&self) -> std::collections::btree_map::Iter<String, V> { - self.strs.iter() - } - - /// Iterate mutably over the string key-value pairs. - pub fn strs_mut(&mut self) -> std::collections::btree_map::IterMut<String, V> { - self.strs.iter_mut() - } - - /// Iterate over the string key-value pairs. - pub fn into_strs(self) -> std::collections::btree_map::IntoIter<String, V> { - self.strs.into_iter() - } -} - -impl<V> Extend<(DictKey, V)> for Dict<V> { - fn extend<T: IntoIterator<Item = (DictKey, V)>>(&mut self, iter: T) { - for (key, value) in iter.into_iter() { - self.insert(key, value); - } - } -} - -impl<V> FromIterator<(DictKey, V)> for Dict<V> { - fn from_iter<T: IntoIterator<Item = (DictKey, V)>>(iter: T) -> Self { - let mut v = Self::new(); - v.extend(iter); - v - } -} - -impl<V> IntoIterator for Dict<V> { - type Item = (DictKey, V); - type IntoIter = std::iter::Chain< - std::iter::Map< - std::collections::btree_map::IntoIter<u64, V>, - fn((u64, V)) -> (DictKey, V), - >, - std::iter::Map< - std::collections::btree_map::IntoIter<String, V>, - fn((String, V)) -> (DictKey, V), - >, - >; - - fn into_iter(self) -> Self::IntoIter { - let nums = self.nums.into_iter().map((|(k, v)| (DictKey::Num(k), v)) as _); - let strs = self.strs.into_iter().map((|(k, v)| (DictKey::Str(k), v)) as _); - nums.chain(strs) - } -} - -impl<'a, V> IntoIterator for &'a Dict<V> { - type Item = (RefKey<'a>, &'a V); - type IntoIter = std::iter::Chain< - std::iter::Map< - std::collections::btree_map::Iter<'a, u64, V>, - fn((&'a u64, &'a V)) -> (RefKey<'a>, &'a V), - >, - std::iter::Map< - std::collections::btree_map::Iter<'a, String, V>, - fn((&'a String, &'a V)) -> (RefKey<'a>, &'a V), - >, - >; - - fn into_iter(self) -> Self::IntoIter { - let nums = self.nums().map((|(k, v): (&u64, _)| (RefKey::Num(*k), v)) as _); - let strs = self.strs().map((|(k, v): (&'a String, _)| (RefKey::Str(k), v)) as _); - nums.chain(strs) - } -} - -impl<'a, V> IntoIterator for &'a mut Dict<V> { - type Item = (RefKey<'a>, &'a mut V); - type IntoIter = std::iter::Chain< - std::iter::Map< - std::collections::btree_map::IterMut<'a, u64, V>, - fn((&'a u64, &'a mut V)) -> (RefKey<'a>, &'a mut V), - >, - std::iter::Map< - std::collections::btree_map::IterMut<'a, String, V>, - fn((&'a String, &'a mut V)) -> (RefKey<'a>, &'a mut V), - >, - >; - - fn into_iter(self) -> Self::IntoIter { - let nums = self - .nums - .iter_mut() - .map((|(k, v): (&u64, _)| (RefKey::Num(*k), v)) as _); - let strs = self - .strs - .iter_mut() - .map((|(k, v): (&'a String, _)| (RefKey::Str(k), v)) as _); - nums.chain(strs) - } -} - -/// The owned variant of a dictionary key. -#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] -pub enum DictKey { - Num(u64), - Str(String), -} - -impl From<&Self> for DictKey { - fn from(key: &Self) -> Self { - key.clone() - } -} - -impl From<RefKey<'_>> for DictKey { - fn from(key: RefKey<'_>) -> Self { - match key { - RefKey::Num(num) => Self::Num(num), - RefKey::Str(string) => Self::Str(string.to_string()), - } - } -} - -impl From<u64> for DictKey { - fn from(num: u64) -> Self { - Self::Num(num) - } -} - -impl From<String> for DictKey { - fn from(string: String) -> Self { - Self::Str(string) - } -} - -impl From<&'static str> for DictKey { - fn from(string: &'static str) -> Self { - Self::Str(string.to_string()) - } -} - -/// The borrowed variant of a dictionary key. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] -pub enum RefKey<'a> { - Num(u64), - Str(&'a str), -} - -impl From<u64> for RefKey<'static> { - fn from(num: u64) -> Self { - Self::Num(num) - } -} - -impl<'a> From<&'a String> for RefKey<'a> { - fn from(string: &'a String) -> Self { - Self::Str(&string) - } -} - -impl<'a> From<&'a str> for RefKey<'a> { - fn from(string: &'a str) -> Self { - Self::Str(string) - } -} - -/// A dictionary entry which combines key span and value. -/// -/// This exists because a key in a directory can't track its span by itself. -#[derive(Clone, PartialEq)] -pub struct SpannedEntry<V> { - pub key_span: Span, - pub value: Spanned<V>, -} - -impl<V> SpannedEntry<V> { - /// Create a new entry. - pub fn new(key: Span, val: Spanned<V>) -> Self { - Self { key_span: key, value: val } - } - - /// Create an entry with the same span for key and value. - pub fn value(val: Spanned<V>) -> Self { - Self { key_span: val.span, value: val } - } - - /// Convert from `&SpannedEntry<T>` to `SpannedEntry<&T>` - pub fn as_ref(&self) -> SpannedEntry<&V> { - SpannedEntry { - key_span: self.key_span, - value: self.value.as_ref(), - } - } - - /// Map the entry to a different value type. - pub fn map<U>(self, f: impl FnOnce(V) -> U) -> SpannedEntry<U> { - SpannedEntry { - key_span: self.key_span, - value: self.value.map(f), - } - } -} - -impl<V: Debug> Debug for SpannedEntry<V> { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - if f.alternate() { - f.write_str("key")?; - self.key_span.fmt(f)?; - f.write_str(" ")?; - } - self.value.fmt(f) - } -} - -#[cfg(test)] -mod tests { - use super::Dict; - - #[test] - fn test_dict_different_key_types_dont_interfere() { - let mut dict = Dict::new(); - dict.insert(10, "hello"); - dict.insert("twenty", "there"); - assert_eq!(dict.len(), 2); - assert_eq!(dict[10], "hello"); - assert_eq!(dict["twenty"], "there"); - } - - #[test] - fn test_dict_push_skips_already_inserted_keys() { - let mut dict = Dict::new(); - dict.insert(2, "2"); - dict.push("0"); - dict.insert(3, "3"); - dict.push("1"); - dict.push("4"); - assert_eq!(dict.len(), 5); - assert_eq!(dict[0], "0"); - assert_eq!(dict[1], "1"); - assert_eq!(dict[2], "2"); - assert_eq!(dict[3], "3"); - assert_eq!(dict[4], "4"); - } - - #[test] - fn test_dict_push_remove_push_reuses_index() { - let mut dict = Dict::new(); - dict.push("0"); - dict.push("1"); - dict.push("2"); - dict.remove(1); - dict.push("a"); - dict.push("3"); - assert_eq!(dict.len(), 4); - assert_eq!(dict[0], "0"); - assert_eq!(dict[1], "a"); - assert_eq!(dict[2], "2"); - assert_eq!(dict[3], "3"); - } - - #[test] - fn test_dict_first_and_last_are_correct() { - let mut dict = Dict::new(); - assert_eq!(dict.first(), None); - assert_eq!(dict.last(), None); - dict.insert(4, "hi"); - dict.insert("string", "hi"); - assert_eq!(dict.first(), Some((4, &"hi"))); - assert_eq!(dict.last(), Some((4, &"hi"))); - dict.insert(2, "bye"); - assert_eq!(dict.first(), Some((2, &"bye"))); - assert_eq!(dict.last(), Some((4, &"hi"))); - } - - #[test] - fn test_dict_format_debug() { - let mut dict = Dict::new(); - assert_eq!(format!("{:?}", dict), "()"); - assert_eq!(format!("{:#?}", dict), "()"); - - dict.insert(10, "hello"); - dict.insert("twenty", "there"); - dict.insert("sp ace", "quotes"); - assert_eq!( - format!("{:?}", dict), - r#"(10: "hello", "sp ace": "quotes", twenty: "there")"#, - ); - assert_eq!(format!("{:#?}", dict).lines().collect::<Vec<_>>(), [ - "(", - r#" 10: "hello","#, - r#" "sp ace": "quotes","#, - r#" twenty: "there","#, - ")", - ]); - } -} diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 56946210..62bd444c 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -3,12 +3,10 @@ #[macro_use] mod value; mod args; -mod dict; mod scope; mod state; pub use args::*; -pub use dict::*; pub use scope::*; pub use state::*; pub use value::*; @@ -451,7 +449,13 @@ impl Eval for Spanned<&Lit> { fn eval(self, ctx: &mut EvalContext) -> Self::Output { match *self.v { - Lit::Ident(ref v) => Value::Ident(v.clone()), + Lit::Ident(ref v) => match ctx.state.scope.get(v.as_str()) { + Some(value) => value.clone(), + None => { + ctx.diag(error!(self.span, "unknown variable")); + Value::Error + } + }, Lit::Bool(v) => Value::Bool(v), Lit::Int(v) => Value::Int(v), Lit::Float(v) => Value::Float(v), @@ -459,28 +463,29 @@ impl Eval for Spanned<&Lit> { Lit::Percent(v) => Value::Relative(Relative::new(v / 100.0)), Lit::Color(v) => Value::Color(Color::Rgba(v)), Lit::Str(ref v) => Value::Str(v.clone()), + Lit::Array(ref v) => Value::Array(v.with_span(self.span).eval(ctx)), Lit::Dict(ref v) => Value::Dict(v.with_span(self.span).eval(ctx)), Lit::Content(ref v) => Value::Content(v.clone()), } } } -impl Eval for Spanned<&LitDict> { - type Output = ValueDict; + +impl Eval for Spanned<&Array> { + type Output = ValueArray; fn eval(self, ctx: &mut EvalContext) -> Self::Output { - let mut dict = ValueDict::new(); + self.v.iter().map(|expr| expr.as_ref().eval(ctx)).collect() + } +} - for entry in &self.v.0 { - let val = entry.expr.as_ref().eval(ctx); - let spanned = val.with_span(entry.expr.span); - if let Some(key) = &entry.key { - dict.insert(&key.v, SpannedEntry::new(key.span, spanned)); - } else { - dict.push(SpannedEntry::value(spanned)); - } - } +impl Eval for Spanned<&Dict> { + type Output = ValueDict; - dict + fn eval(self, ctx: &mut EvalContext) -> Self::Output { + self.v + .iter() + .map(|Named { name, expr }| (name.v.0.clone(), expr.as_ref().eval(ctx))) + .collect() } } @@ -490,19 +495,27 @@ impl Eval for Spanned<&ExprCall> { fn eval(self, ctx: &mut EvalContext) -> Self::Output { let name = &self.v.name.v; let span = self.v.name.span; - let dict = self.v.args.as_ref().eval(ctx); - if let Some(func) = ctx.state.scope.get(name) { - let args = Args(dict.with_span(self.v.args.span)); - ctx.feedback.decos.push(Deco::Resolved.with_span(span)); - (func.clone())(args, ctx) - } else { - if !name.is_empty() { - ctx.diag(error!(span, "unknown function")); - ctx.feedback.decos.push(Deco::Unresolved.with_span(span)); + if let Some(value) = ctx.state.scope.get(name) { + if let Value::Func(func) = value { + let func = func.clone(); + ctx.feedback.decos.push(Deco::Resolved.with_span(span)); + + let mut args = self.v.args.as_ref().eval(ctx); + let returned = func(ctx, &mut args); + args.finish(ctx); + + return returned; + } else { + let ty = value.type_name(); + ctx.diag(error!(span, "a value of type {} is not callable", ty)); } - Value::Dict(dict) + } else if !name.is_empty() { + ctx.diag(error!(span, "unknown function")); } + + ctx.feedback.decos.push(Deco::Unresolved.with_span(span)); + Value::Error } } @@ -554,7 +567,7 @@ fn neg(ctx: &mut EvalContext, span: Span, value: Value) -> Value { Relative(v) => Relative(-v), Linear(v) => Linear(-v), v => { - ctx.diag(error!(span, "cannot negate {}", v.ty())); + ctx.diag(error!(span, "cannot negate {}", v.type_name())); Value::Error } } @@ -589,7 +602,12 @@ fn add(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value { (Content(a), Content(b)) => Content(concat(a, b)), (a, b) => { - ctx.diag(error!(span, "cannot add {} and {}", a.ty(), b.ty())); + ctx.diag(error!( + span, + "cannot add {} and {}", + a.type_name(), + b.type_name() + )); Value::Error } } @@ -617,7 +635,12 @@ fn sub(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value { (Linear(a), Linear(b)) => Linear(a - b), (a, b) => { - ctx.diag(error!(span, "cannot subtract {1} from {0}", a.ty(), b.ty())); + ctx.diag(error!( + span, + "cannot subtract {1} from {0}", + a.type_name(), + b.type_name() + )); Value::Error } } @@ -652,7 +675,12 @@ fn mul(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value { (Str(a), Int(b)) => Str(a.repeat(0.max(b) as usize)), (a, b) => { - ctx.diag(error!(span, "cannot multiply {} with {}", a.ty(), b.ty())); + ctx.diag(error!( + span, + "cannot multiply {} with {}", + a.type_name(), + b.type_name() + )); Value::Error } } @@ -677,7 +705,12 @@ fn div(ctx: &mut EvalContext, span: Span, lhs: Value, rhs: Value) -> Value { (Linear(a), Float(b)) => Linear(a / b), (a, b) => { - ctx.diag(error!(span, "cannot divide {} by {}", a.ty(), b.ty())); + ctx.diag(error!( + span, + "cannot divide {} by {}", + a.type_name(), + b.type_name() + )); Value::Error } } diff --git a/src/eval/scope.rs b/src/eval/scope.rs index c0624393..c9ce1423 100644 --- a/src/eval/scope.rs +++ b/src/eval/scope.rs @@ -3,12 +3,12 @@ use std::collections::HashMap; use std::fmt::{self, Debug, Formatter}; -use super::value::ValueFunc; +use super::Value; /// A map from identifiers to functions. #[derive(Default, Clone, PartialEq)] pub struct Scope { - functions: HashMap<String, ValueFunc>, + values: HashMap<String, Value>, } impl Scope { @@ -18,19 +18,19 @@ impl Scope { Self::default() } - /// Return the function with the given name if there is one. - pub fn get(&self, name: &str) -> Option<&ValueFunc> { - self.functions.get(name) + /// Return the value of the given variable. + pub fn get(&self, var: &str) -> Option<&Value> { + self.values.get(var) } - /// Associate the given name with the function. - pub fn set(&mut self, name: impl Into<String>, function: ValueFunc) { - self.functions.insert(name.into(), function); + /// Store the value for the given variable. + pub fn set(&mut self, var: impl Into<String>, value: impl Into<Value>) { + self.values.insert(var.into(), value.into()); } } impl Debug for Scope { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.debug_set().entries(self.functions.keys()).finish() + self.values.fmt(f) } } diff --git a/src/eval/value.rs b/src/eval/value.rs index a009e891..d1dcdcfa 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -1,25 +1,21 @@ //! Computational values. +use std::any::Any; +use std::collections::HashMap; use std::fmt::{self, Debug, Formatter}; use std::ops::Deref; use std::rc::Rc; -use fontdock::{FontStretch, FontStyle, FontWeight}; - -use super::{Args, Dict, Eval, EvalContext, SpannedEntry}; +use super::{Args, Eval, EvalContext}; use crate::color::Color; -use crate::diag::Diag; -use crate::geom::{Dir, Length, Linear, Relative}; -use crate::paper::Paper; -use crate::syntax::{Ident, Spanned, SynTree, WithSpan}; +use crate::geom::{Length, Linear, Relative}; +use crate::syntax::{Spanned, SynTree, WithSpan}; /// A computational value. #[derive(Clone, PartialEq)] pub enum Value { /// The value that indicates the absence of a meaningful value. None, - /// An identifier: `ident`. - Ident(Ident), /// A boolean: `true, false`. Bool(bool), /// An integer: `120`. @@ -36,34 +32,46 @@ pub enum Value { Color(Color), /// A string: `"string"`. Str(String), - /// A dictionary value: `(false, 12cm, greeting: "hi")`. + /// An array value: `(1, "hi", 12cm)`. + Array(ValueArray), + /// A dictionary value: `(color: #f79143, pattern: dashed)`. Dict(ValueDict), /// A content value: `{*Hi* there}`. - Content(SynTree), + Content(ValueContent), /// An executable function. Func(ValueFunc), + /// Any object. + Any(ValueAny), /// The result of invalid operations. Error, } impl Value { - /// The natural-language name of this value's type for use in error - /// messages. - pub fn ty(&self) -> &'static str { + /// Try to cast the value into a specific type. + pub fn cast<T>(self) -> CastResult<T, Self> + where + T: Cast<Value>, + { + T::cast(self) + } + + /// The name of the stored value's type. + pub fn type_name(&self) -> &'static str { match self { Self::None => "none", - Self::Ident(_) => "identifier", - Self::Bool(_) => "bool", - Self::Int(_) => "integer", - Self::Float(_) => "float", - Self::Relative(_) => "relative", - Self::Length(_) => "length", - Self::Linear(_) => "linear", - Self::Color(_) => "color", - Self::Str(_) => "string", - Self::Dict(_) => "dict", - Self::Content(_) => "content", - Self::Func(_) => "function", + Self::Bool(_) => bool::TYPE_NAME, + Self::Int(_) => i64::TYPE_NAME, + Self::Float(_) => f64::TYPE_NAME, + Self::Relative(_) => Relative::TYPE_NAME, + Self::Length(_) => Length::TYPE_NAME, + Self::Linear(_) => Linear::TYPE_NAME, + Self::Color(_) => Color::TYPE_NAME, + Self::Str(_) => String::TYPE_NAME, + Self::Array(_) => ValueArray::TYPE_NAME, + Self::Dict(_) => ValueDict::TYPE_NAME, + Self::Content(_) => ValueContent::TYPE_NAME, + Self::Func(_) => ValueFunc::TYPE_NAME, + Self::Any(v) => v.type_name(), Self::Error => "error", } } @@ -81,13 +89,6 @@ impl Eval for &Value { // Pass through. Value::Content(tree) => tree.eval(ctx), - // Forward to each dictionary entry. - Value::Dict(dict) => { - for entry in dict.values() { - entry.value.v.eval(ctx); - } - } - // Format with debug. val => ctx.push(ctx.make_text_node(format!("{:?}", val))), } @@ -104,7 +105,6 @@ impl Debug for Value { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Self::None => f.pad("none"), - Self::Ident(v) => v.fmt(f), Self::Bool(v) => v.fmt(f), Self::Int(v) => v.fmt(f), Self::Float(v) => v.fmt(f), @@ -113,45 +113,36 @@ impl Debug for Value { Self::Linear(v) => v.fmt(f), Self::Color(v) => v.fmt(f), Self::Str(v) => v.fmt(f), + Self::Array(v) => v.fmt(f), Self::Dict(v) => v.fmt(f), Self::Content(v) => v.fmt(f), Self::Func(v) => v.fmt(f), + Self::Any(v) => v.fmt(f), Self::Error => f.pad("<error>"), } } } -/// A dictionary of values. -/// -/// # Example -/// ```typst -/// (false, 12cm, greeting: "hi") -/// ``` -pub type ValueDict = Dict<SpannedEntry<Value>>; +/// An array value: `(1, "hi", 12cm)`. +pub type ValueArray = Vec<Value>; -/// An wrapper around a reference-counted function trait object. -/// -/// The dynamic function object is wrapped in an `Rc` to keep [`Value`] -/// cloneable. -/// -/// _Note_: This is needed because the compiler can't `derive(PartialEq)` for -/// [`Value`] when directly putting the `Rc` in there, see the [Rust -/// Issue]. -/// -/// [Rust Issue]: https://github.com/rust-lang/rust/issues/31740 -#[derive(Clone)] -pub struct ValueFunc(pub Rc<Func>); +/// A dictionary value: `(color: #f79143, pattern: dashed)`. +pub type ValueDict = HashMap<String, Value>; + +/// A content value: `{*Hi* there}`. +pub type ValueContent = SynTree; -/// The signature of executable functions. -type Func = dyn Fn(Args, &mut EvalContext) -> Value; +/// A wrapper around a reference-counted executable function. +#[derive(Clone)] +pub struct ValueFunc(Rc<dyn Fn(&mut EvalContext, &mut Args) -> Value>); impl ValueFunc { /// Create a new function value from a rust function or closure. - pub fn new<F>(f: F) -> Self + pub fn new<F>(func: F) -> Self where - F: Fn(Args, &mut EvalContext) -> Value + 'static, + F: Fn(&mut EvalContext, &mut Args) -> Value + 'static, { - Self(Rc::new(f)) + Self(Rc::new(func)) } } @@ -162,7 +153,7 @@ impl PartialEq for ValueFunc { } impl Deref for ValueFunc { - type Target = Func; + type Target = dyn Fn(&mut EvalContext, &mut Args) -> Value; fn deref(&self) -> &Self::Target { self.0.as_ref() @@ -175,160 +166,288 @@ impl Debug for ValueFunc { } } -/// Try to convert a value into a more specific type. -pub trait TryFromValue: Sized { - /// Try to convert the value into yourself. - fn try_from_value(value: Spanned<Value>) -> Conv<Self>; +/// A wrapper around a dynamic value. +pub struct ValueAny(Box<dyn Bounds>); + +impl ValueAny { + /// Create a new instance from any value that satisifies the required bounds. + pub fn new<T>(any: T) -> Self + where + T: Type + Debug + Clone + PartialEq + 'static, + { + Self(Box::new(any)) + } + + /// Whether the wrapped type is `T`. + pub fn is<T: 'static>(&self) -> bool { + self.0.as_any().is::<T>() + } + + /// Try to downcast to a specific type. + pub fn downcast<T: 'static>(self) -> Result<T, Self> { + if self.is::<T>() { + Ok(*self.0.into_any().downcast().unwrap()) + } else { + Err(self) + } + } + + /// Try to downcast to a reference to a specific type. + pub fn downcast_ref<T: 'static>(&self) -> Option<&T> { + self.0.as_any().downcast_ref() + } + + /// The name of the stored object's type. + pub fn type_name(&self) -> &'static str { + self.0.dyn_type_name() + } } -/// The result of a conversion. -#[derive(Debug, Clone, PartialEq)] -pub enum Conv<T> { - /// Success conversion. - Ok(T), - /// Sucessful conversion with a warning. - Warn(T, Diag), - /// Unsucessful conversion, gives back the value alongside the error. - Err(Value, Diag), +impl Clone for ValueAny { + fn clone(&self) -> Self { + Self(self.0.dyn_clone()) + } } -impl<T> Conv<T> { - /// Map the conversion result. - pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Conv<U> { - match self { - Conv::Ok(t) => Conv::Ok(f(t)), - Conv::Warn(t, warn) => Conv::Warn(f(t), warn), - Conv::Err(v, err) => Conv::Err(v, err), - } +impl PartialEq for ValueAny { + fn eq(&self, other: &Self) -> bool { + self.0.dyn_eq(other) } } -impl<T: TryFromValue> TryFromValue for Spanned<T> { - fn try_from_value(value: Spanned<Value>) -> Conv<Self> { - let span = value.span; - T::try_from_value(value).map(|v| v.with_span(span)) +impl Debug for ValueAny { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.0.fmt(f) } } -/// A value type that matches [identifier](Value::Ident) and [string](Value::Str) values. -pub struct StringLike(pub String); +trait Bounds: Debug + 'static { + fn as_any(&self) -> &dyn Any; + fn into_any(self: Box<Self>) -> Box<dyn Any>; + fn dyn_eq(&self, other: &ValueAny) -> bool; + fn dyn_clone(&self) -> Box<dyn Bounds>; + fn dyn_type_name(&self) -> &'static str; +} -impl From<StringLike> for String { - fn from(like: StringLike) -> String { - like.0 +impl<T> Bounds for T +where + T: Type + Debug + Clone + PartialEq + 'static, +{ + fn as_any(&self) -> &dyn Any { + self + } + + fn into_any(self: Box<Self>) -> Box<dyn Any> { + self + } + + fn dyn_eq(&self, other: &ValueAny) -> bool { + if let Some(other) = other.downcast_ref::<Self>() { + self == other + } else { + false + } + } + + fn dyn_clone(&self) -> Box<dyn Bounds> { + Box::new(self.clone()) + } + + fn dyn_type_name(&self) -> &'static str { + T::TYPE_NAME } } -impl Deref for StringLike { - type Target = str; +/// Types that can be stored in values. +pub trait Type { + /// The name of the type. + const TYPE_NAME: &'static str; +} - fn deref(&self) -> &str { - self.0.as_str() +impl<T> Type for Spanned<T> +where + T: Type, +{ + const TYPE_NAME: &'static str = T::TYPE_NAME; +} + +/// Cast from a value to a specific type. +pub trait Cast<V>: Type + Sized { + /// Try to cast the value into an instance of `Self`. + fn cast(value: V) -> CastResult<Self, V>; +} + +/// The result of casting a value to a specific type. +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum CastResult<T, V> { + /// The value was cast successfully. + Ok(T), + /// The value was cast successfully, but with a warning message. + Warn(T, String), + /// The value could not be cast into the specified type. + Err(V), +} + +impl<T, V> CastResult<T, V> { + /// Access the conversion resulting, discarding a possibly existing warning. + pub fn ok(self) -> Option<T> { + match self { + CastResult::Ok(t) | CastResult::Warn(t, _) => Some(t), + CastResult::Err(_) => None, + } } } -/// Implement [`TryFromValue`] through a match. -macro_rules! try_from_match { - ($type:ty[$name:literal] $(@ $span:ident)?: $($pattern:pat => $output:expr),* $(,)?) => { - impl $crate::eval::TryFromValue for $type { - fn try_from_value(value: Spanned<Value>) -> $crate::eval::Conv<Self> { - use $crate::eval::Conv; - #[allow(unused)] - $(let $span = value.span;)? - #[allow(unreachable_patterns)] - match value.v { - $($pattern => Conv::Ok($output)),*, - v => { - let e = error!("expected {}, found {}", $name, v.ty()); - Conv::Err(v, e) - } - } - } +impl<T> Cast<Spanned<Value>> for T +where + T: Cast<Value>, +{ + fn cast(value: Spanned<Value>) -> CastResult<Self, Spanned<Value>> { + let span = value.span; + match T::cast(value.v) { + CastResult::Ok(t) => CastResult::Ok(t), + CastResult::Warn(t, m) => CastResult::Warn(t, m), + CastResult::Err(v) => CastResult::Err(v.with_span(span)), } - }; + } +} + +impl<T> Cast<Spanned<Value>> for Spanned<T> +where + T: Cast<Value>, +{ + fn cast(value: Spanned<Value>) -> CastResult<Self, Spanned<Value>> { + let span = value.span; + match T::cast(value.v) { + CastResult::Ok(t) => CastResult::Ok(t.with_span(span)), + CastResult::Warn(t, m) => CastResult::Warn(t.with_span(span), m), + CastResult::Err(v) => CastResult::Err(v.with_span(span)), + } + } } -/// Implement [`TryFromValue`] through a function parsing an identifier. -macro_rules! try_from_id { - ($type:ty[$name:literal]: $from_str:expr) => { - impl $crate::eval::TryFromValue for $type { - fn try_from_value(value: Spanned<Value>) -> $crate::eval::Conv<Self> { - use $crate::eval::Conv; - let v = value.v; - if let Value::Ident(id) = v { - if let Some(v) = $from_str(&id) { - Conv::Ok(v) - } else { - Conv::Err(Value::Ident(id), error!("invalid {}", $name)) - } - } else { - let e = error!("expected identifier, found {}", v.ty()); - Conv::Err(v, e) +macro_rules! impl_primitive { + ($type:ty: + $type_name:literal, + $variant:path + $(, $pattern:pat => $out:expr)* $(,)? + ) => { + impl Type for $type { + const TYPE_NAME: &'static str = $type_name; + } + + impl From<$type> for Value { + fn from(v: $type) -> Self { + $variant(v) + } + } + + impl Cast<Value> for $type { + fn cast(value: Value) -> CastResult<Self, Value> { + match value { + $variant(v) => CastResult::Ok(v), + $($pattern => CastResult::Ok($out),)* + v => CastResult::Err(v), } } } }; } -try_from_match!(Value["value"]: v => v); -try_from_match!(Ident["identifier"]: Value::Ident(v) => v); -try_from_match!(bool["bool"]: Value::Bool(v) => v); -try_from_match!(i64["integer"]: Value::Int(v) => v); -try_from_match!(f64["float"]: +impl_primitive! { bool: "boolean", Value::Bool } +impl_primitive! { i64: "integer", Value::Int } +impl_primitive! { Length: "length", Value::Length } +impl_primitive! { Relative: "relative", Value::Relative } +impl_primitive! { Color: "color", Value::Color } +impl_primitive! { String: "string", Value::Str } +impl_primitive! { ValueArray: "array", Value::Array } +impl_primitive! { ValueDict: "dictionary", Value::Dict } +impl_primitive! { ValueContent: "content", Value::Content } +impl_primitive! { ValueFunc: "function", Value::Func } + +impl_primitive! { + f64: "float", + Value::Float, Value::Int(v) => v as f64, - Value::Float(v) => v, -); -try_from_match!(Length["length"]: Value::Length(v) => v); -try_from_match!(Relative["relative"]: Value::Relative(v) => v); -try_from_match!(Linear["linear"]: - Value::Linear(v) => v, +} + +impl_primitive! { + Linear: "linear", + Value::Linear, Value::Length(v) => v.into(), Value::Relative(v) => v.into(), -); -try_from_match!(Color["color"]: Value::Color(v) => v); -try_from_match!(String["string"]: Value::Str(v) => v); -try_from_match!(SynTree["tree"]: Value::Content(v) => v); -try_from_match!(ValueDict["dictionary"]: Value::Dict(v) => v); -try_from_match!(ValueFunc["function"]: Value::Func(v) => v); -try_from_match!(StringLike["identifier or string"]: - Value::Ident(Ident(v)) => Self(v), - Value::Str(v) => Self(v), -); -try_from_id!(Dir["direction"]: |v| match v { - "ltr" | "left-to-right" => Some(Self::LTR), - "rtl" | "right-to-left" => Some(Self::RTL), - "ttb" | "top-to-bottom" => Some(Self::TTB), - "btt" | "bottom-to-top" => Some(Self::BTT), - _ => None, -}); -try_from_id!(FontStyle["font style"]: Self::from_str); -try_from_id!(FontStretch["font stretch"]: Self::from_str); -try_from_id!(Paper["paper"]: Self::from_name); - -impl TryFromValue for FontWeight { - fn try_from_value(value: Spanned<Value>) -> Conv<Self> { - match value.v { - Value::Int(number) => { - let [min, max] = [Self::THIN, Self::BLACK]; - if number < i64::from(min.to_number()) { - Conv::Warn(min, warning!("the minimum font weight is {:#?}", min)) - } else if number > i64::from(max.to_number()) { - Conv::Warn(max, warning!("the maximum font weight is {:#?}", max)) - } else { - Conv::Ok(Self::from_number(number as u16)) - } +} + +impl From<&str> for Value { + fn from(v: &str) -> Self { + Self::Str(v.to_string()) + } +} + +impl<F> From<F> for Value +where + F: Fn(&mut EvalContext, &mut Args) -> Value + 'static, +{ + fn from(func: F) -> Self { + Self::Func(ValueFunc::new(func)) + } +} + +impl From<ValueAny> for Value { + fn from(v: ValueAny) -> Self { + Self::Any(v) + } +} + +/// Make a type usable with [`ValueAny`]. +/// +/// Given a type `T`, this implements the following traits: +/// - [`Type`] for `T`, +/// - [`From<T>`](From) for [`Value`], +/// - [`Cast<Value>`](Cast) for `T`. +#[macro_export] +macro_rules! impl_type { + ($type:ty: + $type_name:literal + $(, $pattern:pat => $out:expr)* + $(, #($anyvar:ident: $anytype:ty) => $anyout:expr)* + $(,)? + ) => { + impl $crate::eval::Type for $type { + const TYPE_NAME: &'static str = $type_name; + } + + impl From<$type> for $crate::eval::Value { + fn from(any: $type) -> Self { + $crate::eval::Value::Any($crate::eval::ValueAny::new(any)) } - Value::Ident(id) => { - if let Some(weight) = Self::from_str(&id) { - Conv::Ok(weight) - } else { - Conv::Err(Value::Ident(id), error!("invalid font weight")) + } + + impl $crate::eval::Cast<$crate::eval::Value> for $type { + fn cast( + value: $crate::eval::Value, + ) -> $crate::eval::CastResult<Self, $crate::eval::Value> { + use $crate::eval::*; + + #[allow(unreachable_code)] + match value { + $($pattern => CastResult::Ok($out),)* + Value::Any(mut any) => { + any = match any.downcast::<Self>() { + Ok(t) => return CastResult::Ok(t), + Err(any) => any, + }; + + $(any = match any.downcast::<$anytype>() { + Ok($anyvar) => return CastResult::Ok($anyout), + Err(any) => any, + };)* + + CastResult::Err(Value::Any(any)) + }, + v => CastResult::Err(v), } } - v => { - let e = error!("expected font weight, found {}", v.ty()); - Conv::Err(v, e) - } } - } + }; } |
