diff --git a/src/color.rs b/src/color.rs index f349fdb47..d74584ae7 100644 --- a/src/color.rs +++ b/src/color.rs @@ -1,6 +1,6 @@ //! Color handling. -use std::fmt::{self, Debug, Formatter}; +use std::fmt::{self, Debug, Display, Formatter}; use std::str::FromStr; /// A color in a dynamic format. @@ -10,10 +10,18 @@ pub enum Color { Rgba(RgbaColor), } +impl Display for Color { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Rgba(c) => Display::fmt(c, f), + } + } +} + impl Debug for Color { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - Self::Rgba(c) => c.fmt(f), + Self::Rgba(c) => Debug::fmt(c, f), } } } @@ -76,6 +84,16 @@ impl FromStr for RgbaColor { } } +impl Display for RgbaColor { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)?; + if self.a != 255 { + write!(f, "{:02x}", self.a)?; + } + Ok(()) + } +} + impl Debug for RgbaColor { fn fmt(&self, f: &mut Formatter) -> fmt::Result { if f.alternate() { @@ -83,14 +101,10 @@ impl Debug for RgbaColor { f, "rgba({:02}, {:02}, {:02}, {:02})", self.r, self.g, self.b, self.a, - )?; + ) } else { - write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)?; - if self.a != 255 { - write!(f, "{:02x}", self.a)?; - } + Display::fmt(self, f) } - Ok(()) } } diff --git a/src/eval/context.rs b/src/eval/context.rs index ece33146e..64a8fbbed 100644 --- a/src/eval/context.rs +++ b/src/eval/context.rs @@ -119,15 +119,17 @@ impl EvalContext { if !children.is_empty() || keep_empty(group.softness) { self.runs.push(NodePages { size: group.size, - child: Node::any(NodePad { + child: NodePad { padding: group.padding, - child: Node::any(NodeStack { + child: NodeStack { dirs: group.dirs, align: group.align, expansion: Gen::uniform(Expansion::Fill), children, - }), - }), + } + .into(), + } + .into(), }) } group.softness diff --git a/src/eval/state.rs b/src/eval/state.rs index 7860c0048..2a8ee2f00 100644 --- a/src/eval/state.rs +++ b/src/eval/state.rs @@ -128,7 +128,22 @@ impl StateFont { impl Default for StateFont { fn default() -> Self { Self { - families: Rc::new(default_font_families()), + /// The default tree of font fallbacks. + families: Rc::new(fallback! { + list: ["sans-serif"], + classes: { + "serif" => ["source serif pro", "noto serif"], + "sans-serif" => ["source sans pro", "noto sans"], + "monospace" => ["source code pro", "noto sans mono"], + }, + base: [ + "source sans pro", + "noto sans", + "segoe ui emoji", + "noto emoji", + "latin modern math", + ], + }), variant: FontVariant { style: FontStyle::Normal, weight: FontWeight::REGULAR, @@ -141,22 +156,3 @@ impl Default for StateFont { } } } - -/// The default tree of font fallbacks. -fn default_font_families() -> FallbackTree { - fallback! { - list: ["sans-serif"], - classes: { - "serif" => ["source serif pro", "noto serif"], - "sans-serif" => ["source sans pro", "noto sans"], - "monospace" => ["source code pro", "noto sans mono"], - }, - base: [ - "source sans pro", - "noto sans", - "segoe ui emoji", - "noto emoji", - "latin modern math", - ], - } -} diff --git a/src/eval/value.rs b/src/eval/value.rs index a91ff137a..80c6b8200 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -1,6 +1,6 @@ use std::any::Any; use std::collections::HashMap; -use std::fmt::{self, Debug, Formatter}; +use std::fmt::{self, Debug, Display, Formatter}; use std::ops::Deref; use std::rc::Rc; @@ -45,14 +45,6 @@ pub enum Value { } impl Value { - /// Create a new dynamic value. - pub fn any(any: T) -> Self - where - T: Type + Debug + Clone + PartialEq + 'static, - { - Self::Any(ValueAny::new(any)) - } - /// Try to cast the value into a specific type. pub fn cast(self) -> CastResult where @@ -68,8 +60,8 @@ impl Value { 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::Relative(_) => Relative::TYPE_NAME, Self::Linear(_) => Linear::TYPE_NAME, Self::Color(_) => Color::TYPE_NAME, Self::Str(_) => String::TYPE_NAME, @@ -88,16 +80,24 @@ impl Eval for &Value { /// Evaluate everything contained in this value. fn eval(self, ctx: &mut EvalContext) -> Self::Output { - match self { - // Don't print out none values. - Value::None => {} - - // Pass through. - Value::Content(tree) => tree.eval(ctx), - - // Format with debug. - val => ctx.push(ctx.make_text_node(format!("{:?}", val))), - } + ctx.push(ctx.make_text_node(match self { + Value::None => return, + Value::Bool(v) => v.to_string(), + Value::Int(v) => v.to_string(), + Value::Float(v) => v.to_string(), + Value::Length(v) => v.to_string(), + Value::Relative(v) => v.to_string(), + Value::Linear(v) => v.to_string(), + Value::Color(v) => v.to_string(), + Value::Str(v) => v.clone(), + // TODO: Find good representation for composite types. + Value::Array(_v) => "(array)".into(), + Value::Dict(_v) => "(dictionary)".into(), + Value::Content(tree) => return tree.eval(ctx), + Value::Func(v) => v.to_string(), + Value::Any(v) => v.to_string(), + Value::Error => "(error)".into(), + })); } } @@ -110,21 +110,21 @@ impl Default for Value { impl Debug for Value { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - Self::None => f.pad("none"), - Self::Bool(v) => v.fmt(f), - Self::Int(v) => v.fmt(f), - Self::Float(v) => v.fmt(f), - Self::Length(v) => v.fmt(f), - Self::Relative(v) => v.fmt(f), - 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(""), + Self::None => f.pad("None"), + Self::Bool(v) => Debug::fmt(v, f), + Self::Int(v) => Debug::fmt(v, f), + Self::Float(v) => Debug::fmt(v, f), + Self::Length(v) => Debug::fmt(v, f), + Self::Relative(v) => Debug::fmt(v, f), + Self::Linear(v) => Debug::fmt(v, f), + Self::Color(v) => Debug::fmt(v, f), + Self::Str(v) => Debug::fmt(v, f), + Self::Array(v) => Debug::fmt(v, f), + Self::Dict(v) => Debug::fmt(v, f), + Self::Content(v) => Debug::fmt(v, f), + Self::Func(v) => Debug::fmt(v, f), + Self::Any(v) => Debug::fmt(v, f), + Self::Error => f.pad("Error"), } } } @@ -140,15 +140,18 @@ pub type ValueContent = Tree; /// A wrapper around a reference-counted executable function. #[derive(Clone)] -pub struct ValueFunc(Rc Value>); +pub struct ValueFunc { + name: String, + f: Rc Value>, +} impl ValueFunc { /// Create a new function value from a rust function or closure. - pub fn new(func: F) -> Self + pub fn new(name: impl Into, f: F) -> Self where F: Fn(&mut EvalContext, &mut Args) -> Value + 'static, { - Self(Rc::new(func)) + Self { name: name.into(), f: Rc::new(f) } } } @@ -162,13 +165,18 @@ impl Deref for ValueFunc { type Target = dyn Fn(&mut EvalContext, &mut Args) -> Value; fn deref(&self) -> &Self::Target { - self.0.as_ref() + self.f.as_ref() } } +impl Display for ValueFunc { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "", self.name) + } +} impl Debug for ValueFunc { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.pad("") + Display::fmt(self, f) } } @@ -179,7 +187,7 @@ impl ValueAny { /// Create a new instance from any value that satisifies the required bounds. pub fn new(any: T) -> Self where - T: Type + Debug + Clone + PartialEq + 'static, + T: Type + Debug + Display + Clone + PartialEq + 'static, { Self(Box::new(any)) } @@ -221,13 +229,19 @@ impl PartialEq for ValueAny { } } -impl Debug for ValueAny { +impl Display for ValueAny { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - self.0.fmt(f) + Display::fmt(&self.0, f) } } -trait Bounds: Debug + 'static { +impl Debug for ValueAny { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Debug::fmt(&self.0, f) + } +} + +trait Bounds: Debug + Display + 'static { fn as_any(&self) -> &dyn Any; fn into_any(self: Box) -> Box; fn dyn_eq(&self, other: &ValueAny) -> bool; @@ -237,7 +251,7 @@ trait Bounds: Debug + 'static { impl Bounds for T where - T: Type + Debug + Clone + PartialEq + 'static, + T: Type + Debug + Display + Clone + PartialEq + 'static, { fn as_any(&self) -> &dyn Any { self @@ -304,6 +318,16 @@ impl CastResult { } } +impl Type for Value { + const TYPE_NAME: &'static str = "value"; +} + +impl Cast for Value { + fn cast(value: Value) -> CastResult { + CastResult::Ok(value) + } +} + impl Cast> for T where T: Cast, @@ -390,15 +414,6 @@ impl From<&str> for Value { } } -impl From for Value -where - F: Fn(&mut EvalContext, &mut Args) -> Value + 'static, -{ - fn from(func: F) -> Self { - Self::Func(ValueFunc::new(func)) - } -} - impl From for Value { fn from(v: ValueAny) -> Self { Self::Any(v) @@ -407,9 +422,8 @@ impl From for Value { /// Make a type usable as a [`Value`]. /// -/// Given a type `T`, this implements the following traits: +/// Given a type `T`, this always implements the following traits: /// - [`Type`] for `T`, -/// - [`From`](From) for [`Value`], /// - [`Cast`](Cast) for `T`. #[macro_export] macro_rules! impl_type { @@ -423,12 +437,6 @@ macro_rules! impl_type { const TYPE_NAME: &'static str = $type_name; } - impl From<$type> for $crate::eval::Value { - fn from(any: $type) -> Self { - $crate::eval::Value::any(any) - } - } - impl $crate::eval::Cast<$crate::eval::Value> for $type { fn cast( value: $crate::eval::Value, diff --git a/src/geom/length.rs b/src/geom/length.rs index 0e4451530..23650e897 100644 --- a/src/geom/length.rs +++ b/src/geom/length.rs @@ -90,7 +90,7 @@ impl Display for Length { } else { (self.to_cm(), Unit::Cm) }; - write!(f, "{:.2}{}", val, unit) + write!(f, "{}{}", (val * 100.0).round() / 100.0, unit) } } @@ -214,8 +214,8 @@ mod tests { #[test] fn test_length_formatting() { - assert_eq!(Length::pt(-28.34).to_string(), "-1.00cm".to_string()); - assert_eq!(Length::pt(23.0).to_string(), "23.00pt".to_string()); + assert_eq!(Length::pt(-28.34).to_string(), "-1cm".to_string()); + assert_eq!(Length::pt(23.0).to_string(), "23pt".to_string()); assert_eq!(Length::cm(12.728).to_string(), "12.73cm".to_string()); } } diff --git a/src/geom/relative.rs b/src/geom/relative.rs index ce473e3d8..fdd1f9bb6 100644 --- a/src/geom/relative.rs +++ b/src/geom/relative.rs @@ -32,7 +32,7 @@ impl Relative { impl Display for Relative { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{:.2}%", self.0) + write!(f, "{}%", (self.0 * 10000.0).round() / 100.0) } } diff --git a/src/layout/fixed.rs b/src/layout/fixed.rs index 2ec46df37..cb69b1788 100644 --- a/src/layout/fixed.rs +++ b/src/layout/fixed.rs @@ -25,8 +25,8 @@ impl Layout for NodeFixed { } } -impl From for Node { +impl From for NodeAny { fn from(fixed: NodeFixed) -> Self { - Self::any(fixed) + Self::new(fixed) } } diff --git a/src/layout/node.rs b/src/layout/node.rs index c945ee19a..fa7fe010f 100644 --- a/src/layout/node.rs +++ b/src/layout/node.rs @@ -14,16 +14,6 @@ pub enum Node { Any(NodeAny), } -impl Node { - /// Create a new dynamic node. - pub fn any(any: T) -> Self - where - T: Layout + Debug + Clone + PartialEq + 'static, - { - Self::Any(NodeAny::new(any)) - } -} - impl Layout for Node { fn layout(&self, ctx: &mut LayoutContext, areas: &Areas) -> Layouted { match self { @@ -81,9 +71,12 @@ impl Debug for NodeAny { } } -impl From for Node { - fn from(dynamic: NodeAny) -> Self { - Self::Any(dynamic) +impl From for Node +where + T: Into, +{ + fn from(t: T) -> Self { + Self::Any(t.into()) } } diff --git a/src/layout/pad.rs b/src/layout/pad.rs index f947a7f5c..9e2718c3e 100644 --- a/src/layout/pad.rs +++ b/src/layout/pad.rs @@ -29,9 +29,9 @@ impl Layout for NodePad { } } -impl From for Node { +impl From for NodeAny { fn from(pad: NodePad) -> Self { - Self::any(pad) + Self::new(pad) } } diff --git a/src/layout/par.rs b/src/layout/par.rs index 3a8e39697..7b2e4e925 100644 --- a/src/layout/par.rs +++ b/src/layout/par.rs @@ -36,9 +36,9 @@ impl Layout for NodePar { } } -impl From for Node { +impl From for NodeAny { fn from(par: NodePar) -> Self { - Self::any(par) + Self::new(par) } } diff --git a/src/layout/stack.rs b/src/layout/stack.rs index e98be7edf..eac631d9d 100644 --- a/src/layout/stack.rs +++ b/src/layout/stack.rs @@ -34,9 +34,9 @@ impl Layout for NodeStack { } } -impl From for Node { +impl From for NodeAny { fn from(stack: NodeStack) -> Self { - Self::any(stack) + Self::new(stack) } } diff --git a/src/library/extend.rs b/src/library/extend.rs new file mode 100644 index 000000000..e388c9c84 --- /dev/null +++ b/src/library/extend.rs @@ -0,0 +1,16 @@ +use crate::prelude::*; + +/// `type`: Find out the name of a value's type. +/// +/// # Positional arguments +/// - Any value. +/// +/// # Return value +/// The name of the value's type as a string. +pub fn type_(ctx: &mut EvalContext, args: &mut Args) -> Value { + if let Some(value) = args.require::(ctx, "value") { + value.type_name().into() + } else { + Value::Error + } +} diff --git a/src/library/insert.rs b/src/library/insert.rs index 7d95afe38..8d60927a3 100644 --- a/src/library/insert.rs +++ b/src/library/insert.rs @@ -83,8 +83,8 @@ impl Layout for NodeImage { } } -impl From for Node { +impl From for NodeAny { fn from(image: NodeImage) -> Self { - Self::any(image) + Self::new(image) } } diff --git a/src/library/layout.rs b/src/library/layout.rs index e469c9be6..774e67817 100644 --- a/src/library/layout.rs +++ b/src/library/layout.rs @@ -1,3 +1,5 @@ +use std::fmt::{self, Display, Formatter}; + use crate::eval::Softness; use crate::layout::{Expansion, NodeFixed, NodeSpacing, NodeStack}; use crate::paper::{Paper, PaperClass}; @@ -153,7 +155,19 @@ impl Switch for Alignment { } impl_type! { - Alignment: "alignment" + Alignment: "alignment", +} + +impl Display for Alignment { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.pad(match self { + Self::Left => "left", + Self::Center => "center", + Self::Right => "right", + Self::Top => "top", + Self::Bottom => "bottom", + }) + } } /// `box`: Layout content into a box. @@ -161,7 +175,7 @@ impl_type! { /// # Named arguments /// - Width of the box: `width`, of type `linear` relative to parent width. /// - Height of the box: `height`, of type `linear` relative to parent height. -pub fn boxed(ctx: &mut EvalContext, args: &mut Args) -> Value { +pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value { let snapshot = ctx.state.clone(); let width = args.get(ctx, "width"); @@ -189,7 +203,7 @@ pub fn boxed(ctx: &mut EvalContext, args: &mut Args) -> Value { ctx.push(NodeFixed { width, height, - child: Node::any(NodeStack { dirs, align, expansion, children }), + child: NodeStack { dirs, align, expansion, children }.into(), }); ctx.state = snapshot; diff --git a/src/library/mod.rs b/src/library/mod.rs index 1cc7b9e95..f9047fc6d 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -1,67 +1,78 @@ //! The standard library. +mod extend; mod insert; mod layout; mod style; +pub use extend::*; pub use insert::*; pub use layout::*; pub use style::*; use fontdock::{FontStretch, FontStyle, FontWeight}; -use crate::eval::Scope; +use crate::eval::{Scope, ValueAny, ValueFunc}; use crate::geom::Dir; /// The scope containing the standard library. pub fn _std() -> Scope { let mut std = Scope::new(); + macro_rules! set { + (func: $name:expr, $func:expr) => { + std.set($name, ValueFunc::new($name, $func)) + }; + (any: $var:expr, $any:expr) => { + std.set($var, ValueAny::new($any)) + }; + } // Functions. - std.set("align", align); - std.set("box", boxed); - std.set("font", font); - std.set("h", h); - std.set("image", image); - std.set("page", page); - std.set("pagebreak", pagebreak); - std.set("rgb", rgb); - std.set("v", v); + set!(func: "align", align); + set!(func: "box", box_); + set!(func: "font", font); + set!(func: "h", h); + set!(func: "image", image); + set!(func: "page", page); + set!(func: "pagebreak", pagebreak); + set!(func: "rgb", rgb); + set!(func: "type", type_); + set!(func: "v", v); // Constants. - std.set("left", Alignment::Left); - std.set("center", Alignment::Center); - std.set("right", Alignment::Right); - std.set("top", Alignment::Top); - std.set("bottom", Alignment::Bottom); - std.set("ltr", Dir::LTR); - std.set("rtl", Dir::RTL); - std.set("ttb", Dir::TTB); - std.set("btt", Dir::BTT); - std.set("serif", FontFamily::Serif); - std.set("sans-serif", FontFamily::SansSerif); - std.set("monospace", FontFamily::Monospace); - std.set("normal", FontStyle::Normal); - std.set("italic", FontStyle::Italic); - std.set("oblique", FontStyle::Oblique); - std.set("thin", FontWeight::THIN); - std.set("extralight", FontWeight::EXTRALIGHT); - std.set("light", FontWeight::LIGHT); - std.set("regular", FontWeight::REGULAR); - std.set("medium", FontWeight::MEDIUM); - std.set("semibold", FontWeight::SEMIBOLD); - std.set("bold", FontWeight::BOLD); - std.set("extrabold", FontWeight::EXTRABOLD); - std.set("black", FontWeight::BLACK); - std.set("ultra-condensed", FontStretch::UltraCondensed); - std.set("extra-condensed", FontStretch::ExtraCondensed); - std.set("condensed", FontStretch::Condensed); - std.set("semi-condensed", FontStretch::SemiCondensed); - std.set("normal", FontStretch::Normal); - std.set("semi-expanded", FontStretch::SemiExpanded); - std.set("expanded", FontStretch::Expanded); - std.set("extra-expanded", FontStretch::ExtraExpanded); - std.set("ultra-expanded", FontStretch::UltraExpanded); + set!(any: "left", Alignment::Left); + set!(any: "center", Alignment::Center); + set!(any: "right", Alignment::Right); + set!(any: "top", Alignment::Top); + set!(any: "bottom", Alignment::Bottom); + set!(any: "ltr", Dir::LTR); + set!(any: "rtl", Dir::RTL); + set!(any: "ttb", Dir::TTB); + set!(any: "btt", Dir::BTT); + set!(any: "serif", FontFamily::Serif); + set!(any: "sans-serif", FontFamily::SansSerif); + set!(any: "monospace", FontFamily::Monospace); + set!(any: "normal", FontStyle::Normal); + set!(any: "italic", FontStyle::Italic); + set!(any: "oblique", FontStyle::Oblique); + set!(any: "thin", FontWeight::THIN); + set!(any: "extralight", FontWeight::EXTRALIGHT); + set!(any: "light", FontWeight::LIGHT); + set!(any: "regular", FontWeight::REGULAR); + set!(any: "medium", FontWeight::MEDIUM); + set!(any: "semibold", FontWeight::SEMIBOLD); + set!(any: "bold", FontWeight::BOLD); + set!(any: "extrabold", FontWeight::EXTRABOLD); + set!(any: "black", FontWeight::BLACK); + set!(any: "ultra-condensed", FontStretch::UltraCondensed); + set!(any: "extra-condensed", FontStretch::ExtraCondensed); + set!(any: "condensed", FontStretch::Condensed); + set!(any: "semi-condensed", FontStretch::SemiCondensed); + set!(any: "normal", FontStretch::Normal); + set!(any: "semi-expanded", FontStretch::SemiExpanded); + set!(any: "expanded", FontStretch::Expanded); + set!(any: "extra-expanded", FontStretch::ExtraExpanded); + set!(any: "ultra-expanded", FontStretch::UltraExpanded); std } diff --git a/src/syntax/span.rs b/src/syntax/span.rs index 3be770b83..e924b03b1 100644 --- a/src/syntax/span.rs +++ b/src/syntax/span.rs @@ -269,14 +269,14 @@ impl Location { } } -impl Debug for Location { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - Display::fmt(self, f) - } -} - impl Display for Location { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}:{}", self.line, self.column) } } + +impl Debug for Location { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + Display::fmt(self, f) + } +}