diff options
| author | Laurenz <laurmaedje@gmail.com> | 2020-10-03 15:07:57 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2020-10-03 15:07:57 +0200 |
| commit | 95bae5725cf6495644e2593f8492f1cd0e5bd3c1 (patch) | |
| tree | 919dd90cac7623bcbbc09d9c92399eaa65e537f2 /src/eval/value.rs | |
| parent | 0fc25d732d7cbc37cf801645849d1060f2cec4a3 (diff) | |
Int, Float, Relative and Linear values 🍉
Diffstat (limited to 'src/eval/value.rs')
| -rw-r--r-- | src/eval/value.rs | 143 |
1 files changed, 95 insertions, 48 deletions
diff --git a/src/eval/value.rs b/src/eval/value.rs index 6a63a66f..51bc55ab 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -8,8 +8,8 @@ use fontdock::{FontStretch, FontStyle, FontWeight}; use super::dict::{Dict, SpannedEntry}; use crate::color::RgbaColor; +use crate::geom::Linear; use crate::layout::{Command, Commands, Dir, LayoutContext, SpecAlign}; -use crate::length::{Length, ScaleLength}; use crate::paper::Paper; use crate::syntax::{Ident, Span, SpanWith, Spanned, SynNode, SynTree}; use crate::{DynFuture, Feedback, Pass}; @@ -21,10 +21,21 @@ pub enum Value { Ident(Ident), /// A boolean: `true, false`. Bool(bool), - /// A number: `1.2, 200%`. - Number(f64), + /// An integer: `120`. + Int(i64), + /// A floating-point number: `1.2, 200%`. + Float(f64), /// A length: `2cm, 5.2in`. - Length(Length), + Length(f64), + /// A relative value: `50%`. + /// + /// Note: `50%` is represented as `0.5` here, but as `50.0` in the + /// corresponding [literal]. + /// + /// [literal]: ../syntax/ast/enum.Lit.html#variant.Percent + Relative(f64), + /// A combination of an absolute length and a relative value. + Linear(Linear), /// A color value with alpha channel: `#f79143ff`. Color(RgbaColor), /// A string: `"string"`. @@ -40,14 +51,16 @@ pub enum Value { } impl Value { - /// A natural-language name of the type of this expression, e.g. - /// "identifier". + /// The natural-language name of this value for use in error messages. pub fn name(&self) -> &'static str { match self { Self::Ident(_) => "identifier", Self::Bool(_) => "bool", - Self::Number(_) => "number", + Self::Int(_) => "integer", + Self::Float(_) => "float", + Self::Relative(_) => "relative", Self::Length(_) => "length", + Self::Linear(_) => "linear", Self::Color(_) => "color", Self::Str(_) => "string", Self::Dict(_) => "dict", @@ -71,13 +84,13 @@ impl Spanned<Value> { let mut end = None; for entry in dict.into_values() { if let Some(last_end) = end { - let span = Span::new(last_end, entry.key.start); + let span = Span::new(last_end, entry.key_span.start); let tree = vec![SynNode::Space.span_with(span)]; commands.push(Command::LayoutSyntaxTree(tree)); } - end = Some(entry.val.span.end); - commands.extend(entry.val.into_commands()); + end = Some(entry.value.span.end); + commands.extend(entry.value.into_commands()); } commands } @@ -100,11 +113,14 @@ impl Debug for Value { match self { Self::Ident(i) => i.fmt(f), Self::Bool(b) => b.fmt(f), - Self::Number(n) => n.fmt(f), - Self::Length(s) => s.fmt(f), + Self::Int(i) => i.fmt(f), + Self::Float(n) => n.fmt(f), + Self::Length(l) => l.fmt(f), + Self::Relative(r) => r.fmt(f), + Self::Linear(l) => l.fmt(f), Self::Color(c) => c.fmt(f), Self::Str(s) => s.fmt(f), - Self::Dict(t) => t.fmt(f), + Self::Dict(d) => d.fmt(f), Self::Tree(t) => t.fmt(f), Self::Func(c) => c.fmt(f), Self::Commands(c) => c.fmt(f), @@ -117,18 +133,19 @@ impl Debug for Value { /// The first argument is a dictionary containing the arguments passed to the /// function. The function may be asynchronous (as such it returns a dynamic /// future) and it may emit diagnostics, which are contained in the returned -/// `Pass`. In the end, the function must evaluate to `Value`. Your typical +/// `Pass`. In the end, the function must evaluate to [`Value`]. A typical /// typesetting function will return a `Commands` value which will instruct the /// layouting engine to do what the function pleases. /// -/// The dynamic function object is wrapped in an `Rc` to keep `Value` clonable. +/// The dynamic function object is wrapped in an `Rc` to keep [`Value`] +/// clonable. +/// +/// [`Value`]: enum.Value.html #[derive(Clone)] pub struct FuncValue(pub Rc<FuncType>); -/// The dynamic function type backtick [`FuncValue`]. -/// -/// [`FuncValue`]: struct.FuncValue.html -pub type FuncType = dyn Fn(Span, DictValue, LayoutContext<'_>) -> DynFuture<Pass<Value>>; +/// The signature of executable functions. +type FuncType = dyn Fn(Span, DictValue, LayoutContext<'_>) -> DynFuture<Pass<Value>>; impl FuncValue { /// Create a new function value from a rust function or closure. @@ -175,7 +192,7 @@ impl DictValue { /// skipping and ignoring all non-matching entries with lower keys. pub fn take<T: TryFromValue>(&mut self) -> Option<T> { for (&key, entry) in self.nums() { - let expr = entry.val.as_ref(); + let expr = entry.value.as_ref(); if let Some(val) = T::try_from_value(expr, &mut Feedback::new()) { self.remove(key); return Some(val); @@ -197,7 +214,7 @@ impl DictValue { ) -> Option<T> { while let Some((num, _)) = self.first() { let entry = self.remove(num).unwrap(); - if let Some(val) = T::try_from_value(entry.val.as_ref(), f) { + if let Some(val) = T::try_from_value(entry.value.as_ref(), f) { return Some(val); } } @@ -214,7 +231,7 @@ impl DictValue { T: TryFromValue, { self.remove(key).and_then(|entry| { - let expr = entry.val.as_ref(); + let expr = entry.value.as_ref(); T::try_from_value(expr, f) }) } @@ -230,7 +247,7 @@ impl DictValue { let mut skip = 0; std::iter::from_fn(move || { for (&key, entry) in self.nums().skip(skip) { - let expr = entry.val.as_ref(); + let expr = entry.value.as_ref(); if let Some(val) = T::try_from_value(expr, &mut Feedback::new()) { self.remove(key); return Some((key, val)); @@ -265,7 +282,7 @@ impl DictValue { let mut skip = 0; std::iter::from_fn(move || { for (key, entry) in self.strs().skip(skip) { - let expr = entry.val.as_ref(); + let expr = entry.value.as_ref(); if let Some(val) = T::try_from_value(expr, &mut Feedback::new()) { let key = key.clone(); self.remove(&key); @@ -281,7 +298,7 @@ impl DictValue { /// Generated `"unexpected argument"` errors for all remaining entries. pub fn unexpected(&self, f: &mut Feedback) { for entry in self.values() { - error!(@f, entry.key.join(entry.val.span), "unexpected argument"); + error!(@f, entry.key_span.join(entry.value.span), "unexpected argument"); } } } @@ -351,20 +368,50 @@ impl<T: TryFromValue> TryFromValue for Spanned<T> { impl_match!(Value, "value", v => v.clone()); impl_match!(Ident, "identifier", Value::Ident(i) => i.clone()); -impl_match!(String, "string", Value::Str(s) => s.clone()); impl_match!(bool, "bool", &Value::Bool(b) => b); -impl_match!(f64, "number", &Value::Number(n) => n); -impl_match!(Length, "length", &Value::Length(l) => l); +impl_match!(i64, "integer", &Value::Int(i) => i); +impl_match!(f64, "float", + &Value::Int(i) => i as f64, + &Value::Float(f) => f, +); +impl_match!(Abs, "length", &Value::Length(l) => Abs(l)); +impl_match!(Rel, "relative", &Value::Relative(r) => Rel(r)); +impl_match!(Linear, "linear", + &Value::Linear(l) => l, + &Value::Length(l) => Linear::abs(l), + &Value::Relative(r) => Linear::rel(r), +); +impl_match!(String, "string", Value::Str(s) => s.clone()); impl_match!(SynTree, "tree", Value::Tree(t) => t.clone()); impl_match!(DictValue, "dict", Value::Dict(t) => t.clone()); impl_match!(FuncValue, "function", Value::Func(f) => f.clone()); -impl_match!(ScaleLength, "number or length", - &Value::Length(length) => ScaleLength::Absolute(length), - &Value::Number(scale) => ScaleLength::Scaled(scale), -); -/// A value type that matches identifiers and strings and implements -/// `Into<String>`. +/// A value type that matches [length] values. +/// +/// [length]: enum.Value.html#variant.Length +pub struct Abs(pub f64); + +impl From<Abs> for f64 { + fn from(abs: Abs) -> f64 { + abs.0 + } +} + +/// A value type that matches [relative] values. +/// +/// [relative]: enum.Value.html#variant.Relative +pub struct Rel(pub f64); + +impl From<Rel> for f64 { + fn from(rel: Rel) -> f64 { + rel.0 + } +} + +/// A value type that matches [identifier] and [string] values. +/// +/// [identifier]: enum.Value.html#variant.Ident +/// [string]: enum.Value.html#variant.Str pub struct StringLike(pub String); impl From<StringLike> for String { @@ -410,19 +457,19 @@ impl_ident!(Paper, "paper", Self::from_name); impl TryFromValue for FontWeight { fn try_from_value(value: Spanned<&Value>, f: &mut Feedback) -> Option<Self> { match value.v { - &Value::Number(weight) => { - const MIN: u16 = 100; - const MAX: u16 = 900; - - if weight < MIN as f64 { + &Value::Int(weight) => { + const MIN: i64 = 100; + const MAX: i64 = 900; + let weight = if weight < MIN { error!(@f, value.span, "the minimum font weight is {}", MIN); - Some(Self::THIN) - } else if weight > MAX as f64 { + MIN + } else if weight > MAX { error!(@f, value.span, "the maximum font weight is {}", MAX); - Some(Self::BLACK) + MAX } else { - FontWeight::from_number(weight.round() as u16) - } + weight + }; + Self::from_number(weight as u16) } Value::Ident(ident) => { let weight = Self::from_str(ident); @@ -434,7 +481,7 @@ impl TryFromValue for FontWeight { other => { error!( @f, value.span, - "expected font weight (name or number), found {}", + "expected font weight (name or integer), found {}", other.name(), ); None @@ -490,7 +537,7 @@ mod tests { assert_eq!(dict.take_key::<f64>("hi", &mut f), None); assert_eq!(f.diagnostics, [error!( Span::ZERO, - "expected number, found bool" + "expected float, found bool" )]); assert!(dict.is_empty()); } @@ -499,13 +546,13 @@ mod tests { fn test_dict_take_all_removes_the_correct_entries() { let mut dict = Dict::new(); dict.insert(1, entry(Value::Bool(false))); - dict.insert(3, entry(Value::Number(0.0))); + dict.insert(3, entry(Value::Float(0.0))); dict.insert(7, entry(Value::Bool(true))); assert_eq!(dict.take_all_num::<bool>().collect::<Vec<_>>(), [ (1, false), (7, true) ],); assert_eq!(dict.len(), 1); - assert_eq!(dict[3].val.v, Value::Number(0.0)); + assert_eq!(dict[3].value.v, Value::Float(0.0)); } } |
