From ef866b0cd1b3ed0a02e9c0fbee07f4f0e46465cb Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 26 Jul 2022 23:14:09 +0200 Subject: Move casting into separate module --- src/eval/value.rs | 364 +----------------------------------------------------- 1 file changed, 4 insertions(+), 360 deletions(-) (limited to 'src/eval/value.rs') diff --git a/src/eval/value.rs b/src/eval/value.rs index 294aac94..cdd403a7 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -2,18 +2,13 @@ use std::any::Any; use std::cmp::Ordering; use std::fmt::{self, Debug, Formatter}; use std::hash::{Hash, Hasher}; -use std::num::NonZeroUsize; use std::sync::Arc; -use super::{ops, Args, Array, Dict, Func, RawLength, Regex}; -use crate::diag::{with_alternative, StrResult}; -use crate::geom::{ - Angle, Color, Corners, Dir, Em, Fraction, Length, Paint, Ratio, Relative, RgbaColor, - Sides, -}; +use super::{ops, Args, Array, Cast, Dict, Func, RawLength}; +use crate::diag::StrResult; +use crate::geom::{Angle, Color, Em, Fraction, Length, Ratio, Relative, RgbaColor}; use crate::library::text::RawNode; -use crate::model::{Content, Group, Layout, LayoutNode, Pattern}; -use crate::syntax::Spanned; +use crate::model::{Content, Layout}; use crate::util::EcoString; /// A computational value. @@ -340,15 +335,6 @@ pub trait Type { const TYPE_NAME: &'static str; } -/// Cast from a value to a specific type. -pub trait Cast: Sized { - /// Check whether the value is castable to `Self`. - fn is(value: &V) -> bool; - - /// Try to cast the value into an instance of `Self`. - fn cast(value: V) -> StrResult; -} - /// Implement traits for primitives. macro_rules! primitive { ( @@ -389,79 +375,6 @@ macro_rules! primitive { (@$other:ident) => { Value::$other }; } -/// Implement traits for dynamic types. -macro_rules! dynamic { - ($type:ty: $name:literal, $($tts:tt)*) => { - impl $crate::eval::Type for $type { - const TYPE_NAME: &'static str = $name; - } - - castable! { - $type, - Expected: ::TYPE_NAME, - $($tts)* - @this: Self => this.clone(), - } - - impl From<$type> for $crate::eval::Value { - fn from(v: $type) -> Self { - $crate::eval::Value::Dyn($crate::eval::Dynamic::new(v)) - } - } - }; -} - -/// Make a type castable from a value. -macro_rules! castable { - ($type:ty: $inner:ty) => { - impl $crate::eval::Cast<$crate::eval::Value> for $type { - fn is(value: &$crate::eval::Value) -> bool { - <$inner>::is(value) - } - - fn cast(value: $crate::eval::Value) -> $crate::diag::StrResult { - <$inner>::cast(value).map(Self) - } - } - }; - - ( - $type:ty, - Expected: $expected:expr, - $($pattern:pat => $out:expr,)* - $(@$dyn_in:ident: $dyn_type:ty => $dyn_out:expr,)* - ) => { - #[allow(unreachable_patterns)] - impl $crate::eval::Cast<$crate::eval::Value> for $type { - fn is(value: &$crate::eval::Value) -> bool { - #[allow(unused_variables)] - match value { - $($pattern => true,)* - $crate::eval::Value::Dyn(dynamic) => { - false $(|| dynamic.is::<$dyn_type>())* - } - _ => false, - } - } - - fn cast(value: $crate::eval::Value) -> $crate::diag::StrResult { - let found = match value { - $($pattern => return Ok($out),)* - $crate::eval::Value::Dyn(dynamic) => { - $(if let Some($dyn_in) = dynamic.downcast::<$dyn_type>() { - return Ok($dyn_out); - })* - dynamic.type_name() - } - v => v.type_name(), - }; - - Err(format!("expected {}, found {}", $expected, found)) - } - } - }; -} - primitive! { bool: "boolean", Bool } primitive! { i64: "integer", Int } primitive! { f64: "float", Float, Int(v) => v as f64 } @@ -486,275 +399,6 @@ primitive! { Dict: "dictionary", Dict } primitive! { Func: "function", Func } primitive! { Args: "arguments", Args } -impl Cast for Value { - fn is(_: &Value) -> bool { - true - } - - fn cast(value: Value) -> StrResult { - Ok(value) - } -} - -impl Cast> for T { - fn is(value: &Spanned) -> bool { - T::is(&value.v) - } - - fn cast(value: Spanned) -> StrResult { - T::cast(value.v) - } -} - -impl Cast> for Spanned { - fn is(value: &Spanned) -> bool { - T::is(&value.v) - } - - fn cast(value: Spanned) -> StrResult { - let span = value.span; - T::cast(value.v).map(|t| Spanned::new(t, span)) - } -} - -dynamic! { - Dir: "direction", -} - -dynamic! { - Regex: "regular expression", -} - -dynamic! { - Group: "group", -} - -castable! { - usize, - Expected: "non-negative integer", - Value::Int(int) => int.try_into().map_err(|_| { - if int < 0 { - "must be at least zero" - } else { - "number too large" - } - })?, -} - -castable! { - NonZeroUsize, - Expected: "positive integer", - Value::Int(int) => int - .try_into() - .and_then(|int: usize| int.try_into()) - .map_err(|_| if int <= 0 { - "must be positive" - } else { - "number too large" - })?, -} - -castable! { - Paint, - Expected: "color", - Value::Color(color) => Paint::Solid(color), -} - -castable! { - String, - Expected: "string", - Value::Str(string) => string.into(), -} - -castable! { - LayoutNode, - Expected: "content", - Value::None => Self::default(), - Value::Str(text) => Content::Text(text).pack(), - Value::Content(content) => content.pack(), -} - -castable! { - Pattern, - Expected: "function, string or regular expression", - Value::Func(func) => Self::Node(func.node()?), - Value::Str(text) => Self::text(&text), - @regex: Regex => Self::Regex(regex.clone()), -} - -impl Cast for Option { - fn is(value: &Value) -> bool { - matches!(value, Value::None) || T::is(value) - } - - fn cast(value: Value) -> StrResult { - match value { - Value::None => Ok(None), - v => T::cast(v).map(Some).map_err(|msg| with_alternative(msg, "none")), - } - } -} - -/// A value that can be automatically determined. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub enum Smart { - /// The value should be determined smartly based on the circumstances. - Auto, - /// A specific value. - Custom(T), -} - -impl Smart { - /// Map the contained custom value with `f`. - pub fn map(self, f: F) -> Smart - where - F: FnOnce(T) -> U, - { - match self { - Self::Auto => Smart::Auto, - Self::Custom(x) => Smart::Custom(f(x)), - } - } - - /// Keeps `self` if it contains a custom value, otherwise returns `other`. - pub fn or(self, other: Smart) -> Self { - match self { - Self::Custom(x) => Self::Custom(x), - Self::Auto => other, - } - } - - /// Returns the contained custom value or a provided default value. - pub fn unwrap_or(self, default: T) -> T { - match self { - Self::Auto => default, - Self::Custom(x) => x, - } - } - - /// Returns the contained custom value or computes a default value. - pub fn unwrap_or_else(self, f: F) -> T - where - F: FnOnce() -> T, - { - match self { - Self::Auto => f(), - Self::Custom(x) => x, - } - } - - /// Returns the contained custom value or the default value. - pub fn unwrap_or_default(self) -> T - where - T: Default, - { - self.unwrap_or_else(T::default) - } -} - -impl Default for Smart { - fn default() -> Self { - Self::Auto - } -} - -impl Cast for Smart { - fn is(value: &Value) -> bool { - matches!(value, Value::Auto) || T::is(value) - } - - fn cast(value: Value) -> StrResult { - match value { - Value::Auto => Ok(Self::Auto), - v => T::cast(v) - .map(Self::Custom) - .map_err(|msg| with_alternative(msg, "auto")), - } - } -} - -impl Cast for Sides -where - T: Cast + Default + Copy, -{ - fn is(value: &Value) -> bool { - matches!(value, Value::Dict(_)) || T::is(value) - } - - fn cast(mut value: Value) -> StrResult { - if let Value::Dict(dict) = &mut value { - let mut take = |key| dict.take(key).map(T::cast).transpose(); - - let rest = take("rest")?; - let x = take("x")?.or(rest); - let y = take("y")?.or(rest); - let sides = Sides { - left: take("left")?.or(x), - top: take("top")?.or(y), - right: take("right")?.or(x), - bottom: take("bottom")?.or(y), - }; - - if let Some((key, _)) = dict.iter().next() { - return Err(format!("unexpected key {key:?}")); - } - - Ok(sides.map(Option::unwrap_or_default)) - } else { - T::cast(value).map(Self::splat).map_err(|msg| { - with_alternative( - msg, - "dictionary with any of \ - `left`, `top`, `right`, `bottom`, \ - `x`, `y`, or `rest` as keys", - ) - }) - } - } -} - -impl Cast for Corners -where - T: Cast + Default + Copy, -{ - fn is(value: &Value) -> bool { - matches!(value, Value::Dict(_)) || T::is(value) - } - - fn cast(mut value: Value) -> StrResult { - if let Value::Dict(dict) = &mut value { - let mut take = |key| dict.take(key).map(T::cast).transpose(); - - let rest = take("rest")?; - let left = take("left")?.or(rest); - let top = take("top")?.or(rest); - let right = take("right")?.or(rest); - let bottom = take("bottom")?.or(rest); - let corners = Corners { - top_left: take("top-left")?.or(top).or(left), - top_right: take("top-right")?.or(top).or(right), - bottom_right: take("bottom-right")?.or(bottom).or(right), - bottom_left: take("bottom-left")?.or(bottom).or(left), - }; - - if let Some((key, _)) = dict.iter().next() { - return Err(format!("unexpected key {key:?}")); - } - - Ok(corners.map(Option::unwrap_or_default)) - } else { - T::cast(value).map(Self::splat).map_err(|msg| { - with_alternative( - msg, - "dictionary with any of \ - `top-left`, `top-right`, `bottom-right`, `bottom-left`, \ - `left`, `top`, `right`, `bottom`, or `rest` as keys", - ) - }) - } - } -} - #[cfg(test)] mod tests { use super::*; -- cgit v1.2.3