From 1a0608930cea9c939319ae4b6e2c793eaf2f10d0 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sat, 15 Aug 2020 11:44:18 +0200 Subject: [PATCH] =?UTF-8?q?Rename=20Value=20trait=20into=20TryFromExpr=20?= =?UTF-8?q?=E2=9C=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/func.rs | 1 - src/syntax/expr.rs | 208 +++++++++++++++++++++++++++++++++++++++++--- src/syntax/mod.rs | 1 - src/syntax/value.rs | 197 ----------------------------------------- 4 files changed, 195 insertions(+), 212 deletions(-) delete mode 100644 src/syntax/value.rs diff --git a/src/func.rs b/src/func.rs index abff559f4..bd273b4a3 100644 --- a/src/func.rs +++ b/src/func.rs @@ -11,7 +11,6 @@ pub mod prelude { pub use crate::syntax::parsing::{parse, FuncArgs, FuncCall, ParseState}; pub use crate::syntax::span::{Span, SpanVec, Spanned}; pub use crate::syntax::tree::{DynamicNode, SyntaxNode, SyntaxTree}; - pub use crate::syntax::value::*; pub use crate::{Pass, Feedback}; pub use super::*; } diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index e7e262bc0..2ab433ae6 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -5,12 +5,15 @@ use std::ops::Deref; use std::str::FromStr; use std::u8; -use crate::length::Length; +use fontdock::{FontStyle, FontWeight, FontWidth}; + +use crate::layout::{Dir, SpecAlign}; +use crate::length::{Length, ScaleLength}; +use crate::paper::Paper; use crate::Feedback; use super::span::{SpanVec, Spanned}; use super::tokens::is_identifier; use super::tree::SyntaxTree; -use super::value::Value; /// An expression. #[derive(Clone, PartialEq)] @@ -249,10 +252,10 @@ impl Tuple { /// Expect a specific value type and generate errors for every argument /// until an argument of the value type is found. - pub fn expect(&mut self, f: &mut Feedback) -> Option { + pub fn expect(&mut self, f: &mut Feedback) -> Option { while !self.0.is_empty() { let item = self.0.remove(0); - if let Some(val) = V::parse(item, f) { + if let Some(val) = T::try_from_expr(item, f) { return Some(val); } } @@ -260,9 +263,9 @@ impl Tuple { } /// Extract the first argument of the value type if there is any. - pub fn get(&mut self) -> Option { + pub fn get(&mut self) -> Option { for (i, item) in self.0.iter().enumerate() { - if let Some(val) = V::parse(item.clone(), &mut Feedback::new()) { + if let Some(val) = T::try_from_expr(item.clone(), &mut Feedback::new()) { self.0.remove(i); return Some(val); } @@ -271,11 +274,11 @@ impl Tuple { } /// Extract all arguments of the value type. - pub fn all<'a, V: Value>(&'a mut self) -> impl Iterator + 'a { + pub fn all<'a, T: TryFromExpr>(&'a mut self) -> impl Iterator + 'a { let mut i = 0; std::iter::from_fn(move || { while i < self.0.len() { - let val = V::parse(self.0[i].clone(), &mut Feedback::new()); + let val = T::try_from_expr(self.0[i].clone(), &mut Feedback::new()); if val.is_some() { self.0.remove(i); return val; @@ -354,24 +357,24 @@ impl Object { /// /// Generates an error if there is a matching key, but the value is of the /// wrong type. - pub fn get(&mut self, key: &str, f: &mut Feedback) -> Option { + pub fn get(&mut self, key: &str, f: &mut Feedback) -> Option { for (i, pair) in self.0.iter().enumerate() { if pair.v.key.v.as_str() == key { let pair = self.0.remove(i); - return V::parse(pair.v.value, f); + return T::try_from_expr(pair.v.value, f); } } None } /// Extract all key-value pairs where the value is of the given type. - pub fn all<'a, V: Value>(&'a mut self) - -> impl Iterator, V)> + 'a + pub fn all<'a, T: TryFromExpr>(&'a mut self) + -> impl Iterator, T)> + 'a { let mut i = 0; std::iter::from_fn(move || { while i < self.0.len() { - let val = V::parse(self.0[i].v.value.clone(), &mut Feedback::new()); + let val = T::try_from_expr(self.0[i].v.value.clone(), &mut Feedback::new()); if let Some(val) = val { let pair = self.0.remove(i); return Some((pair.v.key, val)); @@ -391,3 +394,182 @@ impl Debug for Object { .finish() } } + +/// A trait for converting expression into specific types. +pub trait TryFromExpr: Sized { + /// Try to convert an expression into this type. + /// + /// Returns `None` and generates an appropriate error if the expression is + /// not valid for this type. + fn try_from_expr(expr: Spanned, f: &mut Feedback) -> Option; +} + +macro_rules! impl_match { + ($type:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => { + impl TryFromExpr for $type { + fn try_from_expr(expr: Spanned, f: &mut Feedback) -> Option { + #[allow(unreachable_patterns)] + match expr.v { + $($p => Some($r)),*, + other => { + error!( + @f, expr.span, + "expected {}, found {}", $name, other.name() + ); + None + } + } + } + } + }; +} + +macro_rules! impl_ident { + ($type:ty, $name:expr, $parse:expr) => { + impl TryFromExpr for $type { + fn try_from_expr(expr: Spanned, f: &mut Feedback) -> Option { + if let Expr::Ident(ident) = expr.v { + let val = $parse(ident.as_str()); + if val.is_none() { + error!(@f, expr.span, "invalid {}", $name); + } + val + } else { + error!( + @f, expr.span, + "expected {}, found {}", $name, expr.v.name() + ); + None + } + } + } + }; +} + +impl TryFromExpr for Spanned { + fn try_from_expr(expr: Spanned, f: &mut Feedback) -> Option { + let span = expr.span; + T::try_from_expr(expr, f).map(|v| Spanned { v, span }) + } +} + +impl_match!(Expr, "expression", e => e); +impl_match!(Ident, "identifier", Expr::Ident(i) => i); +impl_match!(String, "string", Expr::Str(s) => s); +impl_match!(bool, "bool", Expr::Bool(b) => b); +impl_match!(f64, "number", Expr::Number(n) => n); +impl_match!(Length, "length", Expr::Length(l) => l); +impl_match!(SyntaxTree, "tree", Expr::Tree(t) => t); +impl_match!(Tuple, "tuple", Expr::Tuple(t) => t); +impl_match!(Object, "object", Expr::Object(o) => o); +impl_match!(ScaleLength, "number or length", + Expr::Length(length) => ScaleLength::Absolute(length), + Expr::Number(scale) => ScaleLength::Scaled(scale), +); + +/// A value type that matches identifiers and strings and implements +/// `Into`. +pub struct StringLike(pub String); + +impl From for String { + fn from(like: StringLike) -> String { + like.0 + } +} + +impl_match!(StringLike, "identifier or string", + Expr::Ident(Ident(s)) => StringLike(s), + Expr::Str(s) => StringLike(s), +); + +impl_ident!(Dir, "direction", |s| match s { + "ltr" => Some(Self::LTR), + "rtl" => Some(Self::RTL), + "ttb" => Some(Self::TTB), + "btt" => Some(Self::BTT), + _ => None, +}); + +impl_ident!(SpecAlign, "alignment", |s| match s { + "left" => Some(Self::Left), + "right" => Some(Self::Right), + "top" => Some(Self::Top), + "bottom" => Some(Self::Bottom), + "center" => Some(Self::Center), + _ => None, +}); + +impl_ident!(FontStyle, "font style", FontStyle::from_name); +impl_ident!(Paper, "paper", Paper::from_name); + +impl TryFromExpr for FontWeight { + fn try_from_expr(expr: Spanned, f: &mut Feedback) -> Option { + match expr.v { + Expr::Number(weight) => { + const MIN: u16 = 100; + const MAX: u16 = 900; + + Some(Self(if weight < MIN as f64 { + error!(@f, expr.span, "the minimum font weight is {}", MIN); + MIN + } else if weight > MAX as f64 { + error!(@f, expr.span, "the maximum font weight is {}", MAX); + MAX + } else { + weight.round() as u16 + })) + } + Expr::Ident(ident) => { + let weight = Self::from_name(ident.as_str()); + if weight.is_none() { + error!(@f, expr.span, "invalid font weight"); + } + weight + } + other => { + error!( + @f, expr.span, + "expected font weight (name or number), found {}", + other.name(), + ); + None + } + } + } +} + +impl TryFromExpr for FontWidth { + fn try_from_expr(expr: Spanned, f: &mut Feedback) -> Option { + match expr.v { + Expr::Number(width) => { + const MIN: u16 = 1; + const MAX: u16 = 9; + + Self::new(if width < MIN as f64 { + error!(@f, expr.span, "the minimum font width is {}", MIN); + MIN + } else if width > MAX as f64 { + error!(@f, expr.span, "the maximum font width is {}", MAX); + MAX + } else { + width.round() as u16 + }) + } + Expr::Ident(ident) => { + let width = Self::from_name(ident.as_str()); + if width.is_none() { + error!(@f, expr.span, "invalid font width"); + } + width + } + other => { + error!( + @f, expr.span, + "expected font width (name or number), found {}", + other.name(), + ); + None + } + } + } +} diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index e0f4e4c85..fe0bf6b55 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -11,4 +11,3 @@ pub mod scope; pub mod span; pub mod tokens; pub mod tree; -pub mod value; diff --git a/src/syntax/value.rs b/src/syntax/value.rs deleted file mode 100644 index aa5049e02..000000000 --- a/src/syntax/value.rs +++ /dev/null @@ -1,197 +0,0 @@ -//! Value types for extracting function arguments. - -use fontdock::{FontStyle, FontWeight, FontWidth}; - -use crate::layout::{Dir, SpecAlign}; -use crate::length::{Length, ScaleLength}; -use crate::paper::Paper; -use crate::Feedback; -use super::expr::*; -use super::span::Spanned; -use super::tree::SyntaxTree; - -/// Value types are used to extract values from functions, tuples and -/// objects. They represent the value part of an argument. -/// -/// # Example -/// ```typst -/// [func: 12pt, key="these are both values"] -/// ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ -/// ``` -pub trait Value: Sized { - /// Try to parse this value from an expression. - /// - /// Returns `None` and generates an appropriate error if the expression is - /// not valid for this value type - fn parse(expr: Spanned, f: &mut Feedback) -> Option; -} - -impl Value for Spanned { - fn parse(expr: Spanned, f: &mut Feedback) -> Option { - let span = expr.span; - V::parse(expr, f).map(|v| Spanned { v, span }) - } -} - -macro_rules! match_value { - ($type:ty, $name:expr, $($p:pat => $r:expr),* $(,)?) => { - impl Value for $type { - fn parse(expr: Spanned, f: &mut Feedback) -> Option { - #[allow(unreachable_patterns)] - match expr.v { - $($p => Some($r)),*, - other => { - error!( - @f, expr.span, - "expected {}, found {}", $name, other.name() - ); - None - } - } - } - } - }; -} - -match_value!(Expr, "expression", e => e); -match_value!(Ident, "identifier", Expr::Ident(i) => i); -match_value!(String, "string", Expr::Str(s) => s); -match_value!(bool, "bool", Expr::Bool(b) => b); -match_value!(f64, "number", Expr::Number(n) => n); -match_value!(Length, "length", Expr::Length(l) => l); -match_value!(SyntaxTree, "tree", Expr::Tree(t) => t); -match_value!(Tuple, "tuple", Expr::Tuple(t) => t); -match_value!(Object, "object", Expr::Object(o) => o); -match_value!(ScaleLength, "number or length", - Expr::Length(length) => ScaleLength::Absolute(length), - Expr::Number(scale) => ScaleLength::Scaled(scale), -); - -/// A value type that matches identifiers and strings and implements -/// `Into`. -pub struct StringLike(pub String); - -impl From for String { - fn from(like: StringLike) -> String { - like.0 - } -} - -match_value!(StringLike, "identifier or string", - Expr::Ident(Ident(s)) => StringLike(s), - Expr::Str(s) => StringLike(s), -); - -macro_rules! ident_value { - ($type:ty, $name:expr, $parse:expr) => { - impl Value for $type { - fn parse(expr: Spanned, f: &mut Feedback) -> Option { - if let Expr::Ident(ident) = expr.v { - let val = $parse(ident.as_str()); - if val.is_none() { - error!(@f, expr.span, "invalid {}", $name); - } - val - } else { - error!( - @f, expr.span, - "expected {}, found {}", $name, expr.v.name() - ); - None - } - } - } - }; -} - -ident_value!(Dir, "direction", |s| match s { - "ltr" => Some(Self::LTR), - "rtl" => Some(Self::RTL), - "ttb" => Some(Self::TTB), - "btt" => Some(Self::BTT), - _ => None, -}); - -ident_value!(SpecAlign, "alignment", |s| match s { - "left" => Some(Self::Left), - "right" => Some(Self::Right), - "top" => Some(Self::Top), - "bottom" => Some(Self::Bottom), - "center" => Some(Self::Center), - _ => None, -}); - -ident_value!(FontStyle, "font style", FontStyle::from_name); -ident_value!(Paper, "paper", Paper::from_name); - -impl Value for FontWeight { - fn parse(expr: Spanned, f: &mut Feedback) -> Option { - match expr.v { - Expr::Number(weight) => { - const MIN: u16 = 100; - const MAX: u16 = 900; - - Some(Self(if weight < MIN as f64 { - error!(@f, expr.span, "the minimum font weight is {}", MIN); - MIN - } else if weight > MAX as f64 { - error!(@f, expr.span, "the maximum font weight is {}", MAX); - MAX - } else { - weight.round() as u16 - })) - } - Expr::Ident(ident) => { - let weight = Self::from_name(ident.as_str()); - if weight.is_none() { - error!(@f, expr.span, "invalid font weight"); - } - weight - } - other => { - error!( - @f, expr.span, - "expected font weight (name or number), found {}", - other.name(), - ); - None - } - } - } -} - -impl Value for FontWidth { - fn parse(expr: Spanned, f: &mut Feedback) -> Option { - match expr.v { - Expr::Number(width) => { - const MIN: u16 = 1; - const MAX: u16 = 9; - - Self::new(if width < MIN as f64 { - error!(@f, expr.span, "the minimum font width is {}", MIN); - MIN - } else if width > MAX as f64 { - error!(@f, expr.span, "the maximum font width is {}", MAX); - MAX - } else { - width.round() as u16 - }) - } - Expr::Ident(ident) => { - let width = Self::from_name(ident.as_str()); - if width.is_none() { - error!(@f, expr.span, "invalid font width"); - } - width - } - other => { - error!( - @f, expr.span, - "expected font width (name or number), found {}", - other.name(), - ); - None - } - } - } -}