summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-01-13 14:36:40 +0100
committerLaurenz <laurmaedje@gmail.com>2020-01-13 14:36:40 +0100
commitdde69276d47818174c35523c8ed86b6888b6d02b (patch)
tree68f0f56efd42f47156fddf67158cdcdcde3717b9 /src
parent6527d31dfba78330a39e52d7772f6c8561fb23ef (diff)
Refactor expressions and create tuples and objects 🧮
Diffstat (limited to 'src')
-rw-r--r--src/func/mod.rs2
-rw-r--r--src/library/maps/axis.rs27
-rw-r--r--src/library/maps/mod.rs38
-rw-r--r--src/library/maps/padding.rs9
-rw-r--r--src/library/mod.rs18
-rw-r--r--src/syntax/color.rs2
-rw-r--r--src/syntax/expr.rs245
-rw-r--r--src/syntax/parsing.rs97
-rw-r--r--src/syntax/span.rs4
-rw-r--r--src/syntax/tokens.rs4
10 files changed, 265 insertions, 181 deletions
diff --git a/src/func/mod.rs b/src/func/mod.rs
index 01c77327..5f4918d9 100644
--- a/src/func/mod.rs
+++ b/src/func/mod.rs
@@ -16,7 +16,7 @@ pub mod prelude {
pub use crate::layout::prelude::*;
pub use crate::syntax::{
ParseContext, ParseResult,
- SyntaxTree, FuncCall, FuncArgs, PosArg, KeyArg,
+ SyntaxTree, FuncCall, FuncArgs,
Expression, Ident, ExpressionKind,
Spanned, Span
};
diff --git a/src/library/maps/axis.rs b/src/library/maps/axis.rs
index 238d146a..3c3a8c11 100644
--- a/src/library/maps/axis.rs
+++ b/src/library/maps/axis.rs
@@ -34,6 +34,13 @@ key!(AxisKey, "axis",
"secondary" | "s" => Generic(Secondary),
);
+key!(Direction, "direction",
+ "left-to-right" | "ltr" => LeftToRight,
+ "right-to-left" | "rtl" => RightToLeft,
+ "top-to-bottom" | "ttb" => TopToBottom,
+ "bottom-to-top" | "btt" => BottomToTop,
+);
+
/// A map for storing extents along axes.
#[derive(Debug, Clone, PartialEq)]
pub struct ExtentMap<E: ExpressionKind + Copy>(ConsistentMap<AxisKey, E>);
@@ -41,27 +48,27 @@ pub struct ExtentMap<E: ExpressionKind + Copy>(ConsistentMap<AxisKey, E>);
impl<E: ExpressionKind + Copy> ExtentMap<E> {
/// Parse an extent map from the function args.
///
- /// If `enforce` is true other arguments will create an error, otherwise
+ /// If `all` is true other arguments will create an error, otherwise
/// they are left intact.
- pub fn new(args: &mut FuncArgs, enforce: bool) -> ParseResult<ExtentMap<E>> {
+ pub fn new(args: &mut FuncArgs, all: bool) -> ParseResult<ExtentMap<E>> {
let mut map = ConsistentMap::new();
- for arg in args.keys() {
- let key = match arg.v.key.v.as_str() {
+ for arg in args.iter_keys() {
+ let key = match arg.key.v.as_str() {
"width" | "w" => AxisKey::Specific(Horizontal),
"height" | "h" => AxisKey::Specific(Vertical),
"primary-size" | "ps" => AxisKey::Generic(Primary),
"secondary-size" | "ss" => AxisKey::Generic(Secondary),
- _ => if enforce {
+ _ => if all {
error!("expected dimension")
} else {
- args.add_key(arg);
+ args.add_key_pair(arg);
continue;
}
};
- let e = E::from_expr(arg.v.value)?;
+ let e = E::from_expr(arg.value)?;
map.add(key, e)?;
}
@@ -98,9 +105,9 @@ impl<E: ExpressionKind + Copy> PosAxisMap<E> {
map.add_opt(PosAxisKey::First, args.get_pos_opt::<E>()?)?;
map.add_opt(PosAxisKey::Second, args.get_pos_opt::<E>()?)?;
- for arg in args.keys() {
- let axis = AxisKey::from_ident(&arg.v.key)?;
- let value = E::from_expr(arg.v.value)?;
+ for arg in args.iter_keys() {
+ let axis = AxisKey::from_ident(&arg.key)?;
+ let value = E::from_expr(arg.value)?;
map.add(PosAxisKey::Keyword(axis), value)?;
}
diff --git a/src/library/maps/mod.rs b/src/library/maps/mod.rs
index 5e130d53..a868ce6c 100644
--- a/src/library/maps/mod.rs
+++ b/src/library/maps/mod.rs
@@ -36,6 +36,37 @@ pub_use_mod!(axis);
pub_use_mod!(alignment);
pub_use_mod!(padding);
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum DefaultKey<T> {
+ Some(T),
+ None,
+}
+
+impl<T> Into<Option<T>> for DefaultKey<T> {
+ fn into(self) -> Option<T> {
+ match self {
+ DefaultKey::Some(v) => Some(v),
+ DefaultKey::None => None,
+ }
+ }
+}
+
+impl<T> ExpressionKind for DefaultKey<T> where T: ExpressionKind {
+ const NAME: &'static str = T::NAME;
+
+ fn from_expr(expr: Spanned<Expression>) -> ParseResult<DefaultKey<T>> {
+ if let Expression::Ident(ident) = &expr.v {
+ match ident.as_str() {
+ "default" => return Ok(DefaultKey::None),
+ _ => {},
+ }
+ }
+
+ T::from_expr(expr).map(|v| DefaultKey::Some(v))
+ }
+}
+
/// A deduplicating map type useful for storing possibly redundant arguments.
#[derive(Debug, Clone, PartialEq)]
pub struct ConsistentMap<K, V> where K: Hash + Eq {
@@ -95,10 +126,3 @@ impl<K, V> ConsistentMap<K, V> where K: Hash + Eq {
self.map.iter()
}
}
-
-key!(Direction, "direction",
- "left-to-right" | "ltr" => LeftToRight,
- "right-to-left" | "rtl" => RightToLeft,
- "top-to-bottom" | "ttb" => TopToBottom,
- "bottom-to-top" | "btt" => BottomToTop,
-);
diff --git a/src/library/maps/padding.rs b/src/library/maps/padding.rs
index 4bbbb754..e2d0ea09 100644
--- a/src/library/maps/padding.rs
+++ b/src/library/maps/padding.rs
@@ -46,11 +46,12 @@ impl PaddingMap {
/// Parse a padding map from the function args.
pub fn new(args: &mut FuncArgs) -> ParseResult<PaddingMap> {
let mut map = ConsistentMap::new();
- map.add_opt(PaddingKey::All, args.get_pos_opt::<Option<PSize>>()?)?;
+ map.add_opt(PaddingKey::All,
+ args.get_pos_opt::<DefaultKey<PSize>>()?.map(Into::into))?;
- for arg in args.keys() {
- let key = PaddingKey::from_ident(&arg.v.key)?;
- let size = Option::<PSize>::from_expr(arg.v.value)?;
+ for arg in args.iter_keys() {
+ let key = PaddingKey::from_ident(&arg.key)?;
+ let size = DefaultKey::<PSize>::from_expr(arg.value)?.into();
map.add(key, size)?;
}
diff --git a/src/library/mod.rs b/src/library/mod.rs
index 92c3c948..f8625904 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -62,7 +62,7 @@ function! {
FontFamilyFunc {
body: parse!(optional: body, ctx),
list: {
- args.pos().map(|arg| match arg.v {
+ args.iter_pos().map(|arg| match arg.v {
Expression::Str(s) |
Expression::Ident(Ident(s)) => Ok(s.to_lowercase()),
_ => error!("expected identifier or string"),
@@ -118,7 +118,7 @@ function! {
FontWeightFunc {
body: parse!(optional: body, ctx),
weight: match args.get_pos::<Expression>()? {
- Expression::Num(weight) => {
+ Expression::Number(weight) => {
let weight = weight.round() as i16;
FontWeight(
if weight < 100 { 100 }
@@ -264,13 +264,15 @@ function! {
axis: AxisKey::Specific(axis),
spacing: FSize::from_expr(args.get_pos::<Spanned<Expression>>()?)?,
}
- } else if let Some(arg) = args.get_key_next() {
- let axis = AxisKey::from_ident(&arg.v.key)
- .map_err(|_| error!(@unexpected_argument))?;
-
- let spacing = FSize::from_expr(arg.v.value)?;
- SpacingFunc { axis, spacing }
} else {
+ for arg in args.iter_keys() {
+ let axis = AxisKey::from_ident(&arg.key)
+ .map_err(|_| error!(@unexpected_argument))?;
+
+ let spacing = FSize::from_expr(arg.value)?;
+ return Ok(SpacingFunc { axis, spacing });
+ }
+
error!("expected axis and spacing")
}
}
diff --git a/src/syntax/color.rs b/src/syntax/color.rs
index 716cb688..7f34fad7 100644
--- a/src/syntax/color.rs
+++ b/src/syntax/color.rs
@@ -15,7 +15,7 @@ pub enum ColorToken {
Brace,
ExprIdent,
- ExprString,
+ ExprStr,
ExprNumber,
ExprSize,
ExprBool,
diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs
index ed5e50df..e2df3c4e 100644
--- a/src/syntax/expr.rs
+++ b/src/syntax/expr.rs
@@ -1,125 +1,16 @@
use super::*;
-/// The arguments passed to a function.
-#[derive(Debug, Clone, PartialEq)]
-pub struct FuncArgs {
- pub pos: Vec<Spanned<PosArg>>,
- pub key: Vec<Spanned<KeyArg>>,
-}
-
-impl FuncArgs {
- /// Create an empty collection of arguments.
- pub fn new() -> FuncArgs {
- FuncArgs {
- pos: vec![],
- key: vec![],
- }
- }
-
- /// Add a positional argument.
- pub fn add_pos(&mut self, arg: Spanned<PosArg>) {
- self.pos.push(arg);
- }
-
- /// Add a keyword argument.
- pub fn add_key(&mut self, arg: Spanned<KeyArg>) {
- self.key.push(arg);
- }
-
- /// Force-extract the first positional argument.
- pub fn get_pos<E: ExpressionKind>(&mut self) -> ParseResult<E> {
- expect(self.get_pos_opt())
- }
-
- /// Extract the first positional argument.
- pub fn get_pos_opt<E: ExpressionKind>(&mut self) -> ParseResult<Option<E>> {
- Ok(if !self.pos.is_empty() {
- let spanned = self.pos.remove(0);
- Some(E::from_expr(spanned)?)
- } else {
- None
- })
- }
-
- /// Iterator over positional arguments.
- pub fn pos(&mut self) -> std::vec::IntoIter<Spanned<PosArg>> {
- let vec = std::mem::replace(&mut self.pos, vec![]);
- vec.into_iter()
- }
-
- /// Force-extract a keyword argument.
- pub fn get_key<E: ExpressionKind>(&mut self, name: &str) -> ParseResult<E> {
- expect(self.get_key_opt(name))
- }
-
- /// Extract a keyword argument.
- pub fn get_key_opt<E: ExpressionKind>(&mut self, name: &str) -> ParseResult<Option<E>> {
- Ok(if let Some(index) = self.key.iter().position(|arg| arg.v.key.v.0 == name) {
- let value = self.key.swap_remove(index).v.value;
- Some(E::from_expr(value)?)
- } else {
- None
- })
- }
-
- /// Extract any keyword argument.
- pub fn get_key_next(&mut self) -> Option<Spanned<KeyArg>> {
- self.key.pop()
- }
-
- /// Iterator over all keyword arguments.
- pub fn keys(&mut self) -> std::vec::IntoIter<Spanned<KeyArg>> {
- let vec = std::mem::replace(&mut self.key, vec![]);
- vec.into_iter()
- }
-
- /// Clear the argument lists.
- pub fn clear(&mut self) {
- self.pos.clear();
- self.key.clear();
- }
-
- /// Whether both the positional and keyword argument lists are empty.
- pub fn is_empty(&self) -> bool {
- self.pos.is_empty() && self.key.is_empty()
- }
-}
-
-/// Extract the option expression kind from the option or return an error.
-fn expect<E: ExpressionKind>(opt: ParseResult<Option<E>>) -> ParseResult<E> {
- match opt {
- Ok(Some(spanned)) => Ok(spanned),
- Ok(None) => error!("expected {}", E::NAME),
- Err(e) => Err(e),
- }
-}
-
-/// A positional argument passed to a function.
-pub type PosArg = Expression;
-
-/// A keyword argument passed to a function.
-#[derive(Debug, Clone, PartialEq)]
-pub struct KeyArg {
- pub key: Spanned<Ident>,
- pub value: Spanned<Expression>,
-}
-
-/// Either a positional or keyword argument.
-#[derive(Debug, Clone, PartialEq)]
-pub enum DynArg {
- Pos(Spanned<PosArg>),
- Key(Spanned<KeyArg>),
-}
-
/// An argument or return value.
#[derive(Clone, PartialEq)]
pub enum Expression {
Ident(Ident),
Str(String),
- Num(f64),
+ Number(f64),
Size(Size),
Bool(bool),
+ Tuple(Tuple),
+ Object(Object),
}
impl Display for Expression {
@@ -128,20 +19,17 @@ impl Display for Expression {
match self {
Ident(i) => write!(f, "{}", i),
Str(s) => write!(f, "{:?}", s),
- Num(n) => write!(f, "{}", n),
+ Number(n) => write!(f, "{}", n),
Size(s) => write!(f, "{}", s),
Bool(b) => write!(f, "{}", b),
+ Tuple(t) => write!(f, "{}", t),
+ Object(o) => write!(f, "{}", o),
}
}
}
-debug_display!(Expression);
-
-pub struct Tuple;
-pub struct Object;
-
/// An identifier.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Ident(pub String);
impl Ident {
@@ -164,16 +52,98 @@ impl Display for Ident {
}
}
+/// A sequence of expressions.
+#[derive(Clone, PartialEq)]
+pub struct Tuple {
+ pub items: Vec<Spanned<Expression>>,
+}
+
+impl Tuple {
+ pub fn new() -> Tuple {
+ Tuple { items: vec![] }
+ }
+
+ pub fn add(&mut self, item: Spanned<Expression>) {
+ self.items.push(item);
+ }
+}
+
+impl Display for Tuple {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "(")?;
+
+ let mut first = true;
+ for item in &self.items {
+ if !first {
+ write!(f, ", ")?;
+ }
+ write!(f, "{}", item.v)?;
+ first = false;
+ }
+
+ write!(f, ")")
+ }
+}
+
+/// A key-value collection of identifiers and associated expressions.
+#[derive(Clone, PartialEq)]
+pub struct Object {
+ pub pairs: Vec<Pair>,
+}
+
+#[derive(Clone, PartialEq)]
+pub struct Pair {
+ pub key: Spanned<Ident>,
+ pub value: Spanned<Expression>,
+}
+
+impl Object {
+ pub fn new() -> Object {
+ Object { pairs: vec![] }
+ }
+
+ pub fn add(&mut self, key: Spanned<Ident>, value: Spanned<Expression>) {
+ self.pairs.push(Pair { key, value });
+ }
+
+ pub fn add_pair(&mut self, pair: Pair) {
+ self.pairs.push(pair);
+ }
+}
+
+impl Display for Object {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "{{ ")?;
+
+ let mut first = true;
+ for pair in &self.pairs {
+ if !first {
+ write!(f, ", ")?;
+ }
+ write!(f, "{}: {}", pair.key.v, pair.value.v)?;
+ first = false;
+ }
+
+ write!(f, " }}")
+ }
+}
+
debug_display!(Ident);
+debug_display!(Expression);
+debug_display!(Tuple);
+debug_display!(Object);
+
/// Kinds of expressions.
pub trait ExpressionKind: Sized {
+ /// The name of the expression in an `expected <name>` error.
const NAME: &'static str;
/// Create from expression.
fn from_expr(expr: Spanned<Expression>) -> ParseResult<Self>;
}
+/// Implements the expression kind trait for a type.
macro_rules! kind {
($type:ty, $name:expr, $($patterns:tt)*) => {
impl ExpressionKind for $type {
@@ -190,15 +160,18 @@ macro_rules! kind {
};
}
-kind!(Expression, "expression", e => e);
-kind!(Ident, "identifier", Expression::Ident(ident) => ident);
-kind!(String, "string", Expression::Str(string) => string);
-kind!(f64, "number", Expression::Num(num) => num);
-kind!(bool, "boolean", Expression::Bool(boolean) => boolean);
-kind!(Size, "size", Expression::Size(size) => size);
+kind!(Expression, "expression", e => e);
+kind!(Ident, "identifier", Expression::Ident(ident) => ident);
+kind!(String, "string", Expression::Str(string) => string);
+kind!(f64, "number", Expression::Number(num) => num);
+kind!(bool, "boolean", Expression::Bool(boolean) => boolean);
+kind!(Size, "size", Expression::Size(size) => size);
+kind!(Tuple, "tuple", Expression::Tuple(tuple) => tuple);
+kind!(Object, "object", Expression::Object(object) => object);
+
kind!(ScaleSize, "number or size",
- Expression::Size(size) => ScaleSize::Absolute(size),
- Expression::Num(scale) => ScaleSize::Scaled(scale as f32)
+ Expression::Size(size) => ScaleSize::Absolute(size),
+ Expression::Number(scale) => ScaleSize::Scaled(scale as f32)
);
impl<T> ExpressionKind for Spanned<T> where T: ExpressionKind {
@@ -206,22 +179,6 @@ impl<T> ExpressionKind for Spanned<T> where T: ExpressionKind {
fn from_expr(expr: Spanned<Expression>) -> ParseResult<Spanned<T>> {
let span = expr.span;
- T::from_expr(expr)
- .map(|v| Spanned::new(v, span))
- }
-}
-
-impl<T> ExpressionKind for Option<T> where T: ExpressionKind {
- const NAME: &'static str = T::NAME;
-
- fn from_expr(expr: Spanned<Expression>) -> ParseResult<Option<T>> {
- if let Expression::Ident(ident) = &expr.v {
- match ident.as_str() {
- "default" | "none" => return Ok(None),
- _ => {},
- }
- }
-
- T::from_expr(expr).map(|v| Some(v))
+ T::from_expr(expr).map(|v| Spanned { v, span })
}
}
diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs
index 112c2f65..47322485 100644
--- a/src/syntax/parsing.rs
+++ b/src/syntax/parsing.rs
@@ -47,6 +47,99 @@ impl PartialEq for FuncCall {
}
}
+#[derive(Debug)]
+pub struct FuncArgs {
+ positional: Tuple,
+ keyword: Object,
+}
+
+impl FuncArgs {
+ fn new() -> FuncArgs {
+ FuncArgs {
+ positional: Tuple::new(),
+ keyword: Object::new(),
+ }
+ }
+
+ /// Add a positional argument.
+ pub fn add_pos(&mut self, item: Spanned<Expression>) {
+ self.positional.add(item);
+ }
+
+ /// Force-extract the first positional argument.
+ pub fn get_pos<E: ExpressionKind>(&mut self) -> ParseResult<E> {
+ expect(self.get_pos_opt())
+ }
+
+ /// Extract the first positional argument.
+ pub fn get_pos_opt<E: ExpressionKind>(&mut self) -> ParseResult<Option<E>> {
+ Ok(if !self.positional.items.is_empty() {
+ let spanned = self.positional.items.remove(0);
+ Some(E::from_expr(spanned)?)
+ } else {
+ None
+ })
+ }
+
+ /// Add a keyword argument.
+ pub fn add_key(&mut self, key: Spanned<Ident>, value: Spanned<Expression>) {
+ self.keyword.add(key, value);
+ }
+
+ /// Add a keyword argument from an existing pair.
+ pub fn add_key_pair(&mut self, pair: Pair) {
+ self.keyword.add_pair(pair);
+ }
+
+ /// Force-extract a keyword argument.
+ pub fn get_key<E: ExpressionKind>(&mut self, name: &str) -> ParseResult<E> {
+ expect(self.get_key_opt(name))
+ }
+
+ /// Extract a keyword argument.
+ pub fn get_key_opt<E: ExpressionKind>(&mut self, name: &str) -> ParseResult<Option<E>> {
+ self.keyword.pairs.iter()
+ .position(|p| p.key.v.0 == name)
+ .map(|index| {
+ let value = self.keyword.pairs.swap_remove(index).value;
+ E::from_expr(value)
+ })
+ .transpose()
+ }
+
+ /// Iterator over positional arguments.
+ pub fn iter_pos(&mut self) -> std::vec::IntoIter<Spanned<Expression>> {
+ let tuple = std::mem::replace(&mut self.positional, Tuple::new());
+ tuple.items.into_iter()
+ }
+
+ /// Iterator over all keyword arguments.
+ pub fn iter_keys(&mut self) -> std::vec::IntoIter<Pair> {
+ let object = std::mem::replace(&mut self.keyword, Object::new());
+ object.pairs.into_iter()
+ }
+
+ /// Clear the argument lists.
+ pub fn clear(&mut self) {
+ self.positional.items.clear();
+ self.keyword.pairs.clear();
+ }
+
+ /// Whether both the positional and keyword argument lists are empty.
+ pub fn is_empty(&self) -> bool {
+ self.positional.items.is_empty() && self.keyword.pairs.is_empty()
+ }
+}
+
+/// Extract the option expression kind from the option or return an error.
+fn expect<E: ExpressionKind>(opt: ParseResult<Option<E>>) -> ParseResult<E> {
+ match opt {
+ Ok(Some(spanned)) => Ok(spanned),
+ Ok(None) => error!("expected {}", E::NAME),
+ Err(e) => Err(e),
+ }
+}
+
/// Parses source code into a syntax tree given a context.
pub fn parse(src: &str, ctx: ParseContext) -> SyntaxTree {
Parser::new(src, ctx).parse()
@@ -346,7 +439,7 @@ impl<'s> Parser<'s> {
Comma => Some(ColorToken::Comma),
Equals => Some(ColorToken::Equals),
ExprIdent(_) => Some(ColorToken::ExprIdent),
- ExprString(_) => Some(ColorToken::ExprString),
+ ExprStr(_) => Some(ColorToken::ExprStr),
ExprNumber(_) => Some(ColorToken::ExprNumber),
ExprSize(_) => Some(ColorToken::ExprSize),
ExprBool(_) => Some(ColorToken::ExprBool),
@@ -387,7 +480,7 @@ fn name(token: Token) -> &'static str {
Comma => "comma",
Equals => "equals sign",
ExprIdent(_) => "identifier",
- ExprString(_) => "string",
+ ExprStr(_) => "string",
ExprNumber(_) => "number",
ExprSize(_) => "size",
ExprBool(_) => "bool",
diff --git a/src/syntax/span.rs b/src/syntax/span.rs
index 10188ed4..e5c6912b 100644
--- a/src/syntax/span.rs
+++ b/src/syntax/span.rs
@@ -4,7 +4,7 @@ use std::fmt::{self, Display, Formatter};
/// Annotates a value with the part of the source code it corresponds to.
-#[derive(Copy, Clone, Eq, PartialEq)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Spanned<T> {
pub v: T,
pub span: Span,
@@ -37,7 +37,7 @@ impl<T> Display for Spanned<T> where T: std::fmt::Debug {
debug_display!(Spanned; T where T: std::fmt::Debug);
/// Describes a slice of source code.
-#[derive(Copy, Clone, Eq, PartialEq)]
+#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Span {
pub start: Position,
pub end: Position,
diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs
index ae5cfe48..295a4382 100644
--- a/src/syntax/tokens.rs
+++ b/src/syntax/tokens.rs
@@ -45,7 +45,7 @@ pub enum Token<'s> {
/// An identifier in a function header: `center`.
ExprIdent(&'s str),
/// A quoted string in a function header: `"..."`.
- ExprString(&'s str),
+ ExprStr(&'s str),
/// A number in a function header: `3.14`.
ExprNumber(f64),
/// A size in a function header: `12pt`.
@@ -220,7 +220,7 @@ impl<'s> Tokens<'s> {
fn parse_string(&mut self) -> Token<'s> {
let mut escaped = false;
- ExprString(self.read_string_until(|n| {
+ ExprStr(self.read_string_until(|n| {
if n == '"' && !escaped {
return true;
} else if n == '\\' {