summaryrefslogtreecommitdiff
path: root/src/syntax/expr.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/syntax/expr.rs')
-rw-r--r--src/syntax/expr.rs122
1 files changed, 122 insertions, 0 deletions
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<RgbaColor> {
+ 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<Ident>,
+ /// The elements of the tuple.
+ pub tuple: Spanned<Tuple>,
+}
+
+impl NamedTuple {
+ /// Create a named tuple from a tuple.
+ pub fn new(name: Spanned<Ident>, tuple: Spanned<Tuple>) -> 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