From 28c3a797ec7a6fbe3f5069604e5b19a00ae52344 Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Tue, 14 Jul 2020 19:09:58 +0200 Subject: Add named tuples and hex color tokens --- src/syntax/expr.rs | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) (limited to 'src/syntax/expr.rs') diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index b5bce2cd..b7256c84 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -2,6 +2,8 @@ use std::fmt::{self, Write, Debug, Formatter}; use std::iter::FromIterator; +use std::ops::Deref; +use std::u8; use crate::error::Errors; use crate::size::Size; @@ -23,8 +25,12 @@ pub enum Expr { Size(Size), /// A bool: `true, false`. Bool(bool), + /// A color value, including the alpha channel: `#f79143ff` + Color(RgbaColor), /// A tuple: `(false, 12cm, "hi")`. Tuple(Tuple), + /// A named tuple: `cmyk(37.7, 0, 3.9, 1.1)`. + NamedTuple(NamedTuple), /// An object: `{ fit: false, size: 12pt }`. Object(Object), } @@ -39,7 +45,9 @@ impl Expr { Number(_) => "number", Size(_) => "size", Bool(_) => "bool", + Color(_) => "color", Tuple(_) => "tuple", + NamedTuple(_) => "named tuple", Object(_) => "object", } } @@ -54,7 +62,9 @@ impl Debug for Expr { Number(n) => n.fmt(f), Size(s) => s.fmt(f), Bool(b) => b.fmt(f), + Color(c) => c.fmt(f), Tuple(t) => t.fmt(f), + NamedTuple(t) => t.fmt(f), Object(o) => o.fmt(f), } } @@ -97,6 +107,81 @@ impl Debug for Ident { } } +/// An 8-bit RGBA color. +/// +/// # Example +/// ```typst +/// [box: background=#423abaff] +/// ^^^^^^^^ +/// ``` +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct RgbaColor { + r: u8, + g: u8, + b: u8, + a: u8, +} + +impl RgbaColor { + pub fn new(r: u8, g: u8, b: u8, a: u8) -> RgbaColor { + RgbaColor { r, g, b, a } + } + + pub fn from_str(hex_str: &str) -> Option { + let len = hex_str.len(); + let permissable = &[3, 4, 6, 8]; + + if !permissable.contains(&len) { + return None; + } + + let long = len == 6 || len == 8; + let alpha = len == 4 || len == 8; + let mut values: [u8; 4] = [255; 4]; + + for elem in if alpha { 0..4 } else { 0..3 } { + let item_len = if long { 2 } else { 1 }; + let pos = elem * item_len; + + if let Ok(val) = u8::from_str_radix( + &hex_str[pos..(pos+item_len)], 16) { + values[elem] = val; + } else { + // Some non-hexadecimal characters slipped into the color + return None; + } + + if !long { + // Duplicate number for shorthand notation, i.e. `a` -> `aa` + values[elem] += values[elem] * 16; + } + } + + Some( + RgbaColor::new(values[0], values[1], values[2], values[3]) + ) + } +} + +impl Debug for RgbaColor { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + if f.alternate() { + f.write_str("rgba(")?; + f.write_fmt(format_args!("r: {:02}, ", self.r))?; + f.write_fmt(format_args!("g: {:02}, ", self.g))?; + f.write_fmt(format_args!("b: {:02}, ", self.b))?; + f.write_fmt(format_args!("a: {:02}", self.a))?; + f.write_char(')') + } else { + f.write_char('#')?; + f.write_fmt(format_args!("{:02x}", self.r))?; + f.write_fmt(format_args!("{:02x}", self.g))?; + f.write_fmt(format_args!("{:02x}", self.b))?; + f.write_fmt(format_args!("{:02x}", self.a)) + } + } +} + /// An untyped sequence of expressions. /// /// # Example @@ -185,6 +270,43 @@ impl Debug for Tuple { } } +/// A named, untyped sequence of expressions. +/// +/// # Example +/// ```typst +/// hsl(93, 10, 19.4) +/// ``` +#[derive(Clone, PartialEq)] +pub struct NamedTuple { + pub name: Spanned, + /// The elements of the tuple. + pub tuple: Spanned, +} + +impl NamedTuple { + /// Create a named tuple from a tuple. + pub fn new(name: Spanned, tuple: Spanned) -> NamedTuple { + NamedTuple { name, tuple } + } +} + +impl Deref for NamedTuple { + type Target = Tuple; + + fn deref(&self) -> &Self::Target { + &self.tuple.v + } +} + +impl Debug for NamedTuple { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.debug_struct("named tuple") + .field("name", &self.name) + .field("values", &self.tuple) + .finish() + } +} + /// A key-value collection of identifiers and associated expressions. /// /// The pairs themselves are not spanned, but the combined spans can easily be -- cgit v1.2.3 From 1683ca87f797a6262f0a4f75bde0e23eca31798c Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Wed, 15 Jul 2020 20:57:26 +0200 Subject: =?UTF-8?q?Healed=20field=20for=20RgbaColors,=20CodeRev=20feedback?= =?UTF-8?q?=20=F0=9F=A4=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/syntax/expr.rs | 72 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 30 deletions(-) (limited to 'src/syntax/expr.rs') diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index b7256c84..52efbe53 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -116,42 +116,57 @@ impl Debug for Ident { /// ``` #[derive(Clone, Eq, PartialEq, Hash)] pub struct RgbaColor { - r: u8, - g: u8, - b: u8, - a: u8, + /// Red channel. + pub r: u8, + /// Green channel. + pub g: u8, + /// Blue channel. + pub b: u8, + /// Alpha channel. + pub a: u8, + /// Indicates whether this is a user-provided value or a + /// default value provided as a fail-over by the parser. + /// This color may be overwritten if this property is true. + pub healed: bool, } impl RgbaColor { + /// Constructs a new color. pub fn new(r: u8, g: u8, b: u8, a: u8) -> RgbaColor { - RgbaColor { r, g, b, a } + RgbaColor { r, g, b, a, healed: false } } + /// Constructs a new color with the healed property set to true. + pub fn new_healed(r: u8, g: u8, b: u8, a: u8) -> RgbaColor { + RgbaColor { r, g, b, a, healed: true } + } + + /// Constructs a new color from a hex string like `7a03c2`. + /// Do not specify a leading `#`. pub fn from_str(hex_str: &str) -> Option { - let len = hex_str.len(); - let permissable = &[3, 4, 6, 8]; - - if !permissable.contains(&len) { + if !hex_str.is_ascii() { return None; } - let long = len == 6 || len == 8; + let len = hex_str.len(); + let long = len == 6 || len == 8; + let short = len == 3 || len == 4; let alpha = len == 4 || len == 8; + + if !long && !short { + return None; + } + let mut values: [u8; 4] = [255; 4]; for elem in if alpha { 0..4 } else { 0..3 } { let item_len = if long { 2 } else { 1 }; let pos = elem * item_len; - if let Ok(val) = u8::from_str_radix( - &hex_str[pos..(pos+item_len)], 16) { - values[elem] = val; - } else { - // Some non-hexadecimal characters slipped into the color - return None; - } + let item = &hex_str[pos..(pos+item_len)]; + values[elem] = u8::from_str_radix(item, 16).ok()?; - if !long { + if short { // Duplicate number for shorthand notation, i.e. `a` -> `aa` values[elem] += values[elem] * 16; } @@ -171,13 +186,18 @@ impl Debug for RgbaColor { f.write_fmt(format_args!("g: {:02}, ", self.g))?; f.write_fmt(format_args!("b: {:02}, ", self.b))?; f.write_fmt(format_args!("a: {:02}", self.a))?; - f.write_char(')') + f.write_char(')')?; } else { f.write_char('#')?; f.write_fmt(format_args!("{:02x}", self.r))?; f.write_fmt(format_args!("{:02x}", self.g))?; f.write_fmt(format_args!("{:02x}", self.b))?; - f.write_fmt(format_args!("{:02x}", self.a)) + f.write_fmt(format_args!("{:02x}", self.a))?; + } + if self.healed { + f.write_fmt(format_args!(" [healed]")) + } else { + Ok(()) } } } @@ -276,8 +296,9 @@ impl Debug for Tuple { /// ```typst /// hsl(93, 10, 19.4) /// ``` -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Debug)] pub struct NamedTuple { + /// The name of the tuple and where it is in the user source. pub name: Spanned, /// The elements of the tuple. pub tuple: Spanned, @@ -298,15 +319,6 @@ impl Deref for NamedTuple { } } -impl Debug for NamedTuple { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.debug_struct("named tuple") - .field("name", &self.name) - .field("values", &self.tuple) - .finish() - } -} - /// A key-value collection of identifiers and associated expressions. /// /// The pairs themselves are not spanned, but the combined spans can easily be -- cgit v1.2.3 From e96f3830f19ed63ae8b43f8ce33f824237b34d82 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 15 Jul 2020 23:49:10 +0200 Subject: =?UTF-8?q?Use=20FromStr=20trait=20and=20formatting=20=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/syntax/expr.rs | 56 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 20 deletions(-) (limited to 'src/syntax/expr.rs') diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index 52efbe53..3f535729 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -3,6 +3,7 @@ use std::fmt::{self, Write, Debug, Formatter}; use std::iter::FromIterator; use std::ops::Deref; +use std::str::FromStr; use std::u8; use crate::error::Errors; @@ -141,11 +142,16 @@ impl RgbaColor { RgbaColor { r, g, b, a, healed: true } } +} + +impl FromStr for RgbaColor { + type Err = ParseColorError; + /// Constructs a new color from a hex string like `7a03c2`. /// Do not specify a leading `#`. - pub fn from_str(hex_str: &str) -> Option { + fn from_str(hex_str: &str) -> Result { if !hex_str.is_ascii() { - return None; + return Err(ParseColorError); } let len = hex_str.len(); @@ -154,7 +160,7 @@ impl RgbaColor { let alpha = len == 4 || len == 8; if !long && !short { - return None; + return Err(ParseColorError); } let mut values: [u8; 4] = [255; 4]; @@ -164,17 +170,16 @@ impl RgbaColor { let pos = elem * item_len; let item = &hex_str[pos..(pos+item_len)]; - values[elem] = u8::from_str_radix(item, 16).ok()?; - + values[elem] = u8::from_str_radix(item, 16) + .map_err(|_| ParseColorError)?; + if short { // Duplicate number for shorthand notation, i.e. `a` -> `aa` values[elem] += values[elem] * 16; } } - Some( - RgbaColor::new(values[0], values[1], values[2], values[3]) - ) + Ok(RgbaColor::new(values[0], values[1], values[2], values[3])) } } @@ -182,23 +187,34 @@ impl Debug for RgbaColor { fn fmt(&self, f: &mut Formatter) -> fmt::Result { if f.alternate() { f.write_str("rgba(")?; - f.write_fmt(format_args!("r: {:02}, ", self.r))?; - f.write_fmt(format_args!("g: {:02}, ", self.g))?; - f.write_fmt(format_args!("b: {:02}, ", self.b))?; - f.write_fmt(format_args!("a: {:02}", self.a))?; + write!(f, "r: {:02}, ", self.r)?; + write!(f, "g: {:02}, ", self.g)?; + write!(f, "b: {:02}, ", self.b)?; + write!(f, "a: {:02}", self.a)?; f.write_char(')')?; } else { f.write_char('#')?; - f.write_fmt(format_args!("{:02x}", self.r))?; - f.write_fmt(format_args!("{:02x}", self.g))?; - f.write_fmt(format_args!("{:02x}", self.b))?; - f.write_fmt(format_args!("{:02x}", self.a))?; + write!(f, "{:02x}", self.r)?; + write!(f, "{:02x}", self.g)?; + write!(f, "{:02x}", self.b)?; + write!(f, "{:02x}", self.a)?; } if self.healed { - f.write_fmt(format_args!(" [healed]")) - } else { - Ok(()) + f.write_fmt(format_args!(" [healed]"))?; } + Ok(()) + } +} + +/// The error returned when parsing a [`RgbaColor`] from a string fails. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct ParseColorError; + +impl std::error::Error for ParseColorError {} + +impl fmt::Display for ParseColorError { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.write_str("invalid color") } } @@ -296,7 +312,7 @@ impl Debug for Tuple { /// ```typst /// hsl(93, 10, 19.4) /// ``` -#[derive(Clone, PartialEq, Debug)] +#[derive(Debug, Clone, PartialEq)] pub struct NamedTuple { /// The name of the tuple and where it is in the user source. pub name: Spanned, -- cgit v1.2.3