Refactor expressions and create tuples and objects 🧮

This commit is contained in:
Laurenz 2020-01-13 14:36:40 +01:00
parent 6527d31dfb
commit dde69276d4
11 changed files with 266 additions and 182 deletions

View File

@ -16,7 +16,7 @@ pub mod prelude {
pub use crate::layout::prelude::*; pub use crate::layout::prelude::*;
pub use crate::syntax::{ pub use crate::syntax::{
ParseContext, ParseResult, ParseContext, ParseResult,
SyntaxTree, FuncCall, FuncArgs, PosArg, KeyArg, SyntaxTree, FuncCall, FuncArgs,
Expression, Ident, ExpressionKind, Expression, Ident, ExpressionKind,
Spanned, Span Spanned, Span
}; };

View File

@ -34,6 +34,13 @@ key!(AxisKey, "axis",
"secondary" | "s" => Generic(Secondary), "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. /// A map for storing extents along axes.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ExtentMap<E: ExpressionKind + Copy>(ConsistentMap<AxisKey, E>); 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> { impl<E: ExpressionKind + Copy> ExtentMap<E> {
/// Parse an extent map from the function args. /// 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. /// 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(); let mut map = ConsistentMap::new();
for arg in args.keys() { for arg in args.iter_keys() {
let key = match arg.v.key.v.as_str() { let key = match arg.key.v.as_str() {
"width" | "w" => AxisKey::Specific(Horizontal), "width" | "w" => AxisKey::Specific(Horizontal),
"height" | "h" => AxisKey::Specific(Vertical), "height" | "h" => AxisKey::Specific(Vertical),
"primary-size" | "ps" => AxisKey::Generic(Primary), "primary-size" | "ps" => AxisKey::Generic(Primary),
"secondary-size" | "ss" => AxisKey::Generic(Secondary), "secondary-size" | "ss" => AxisKey::Generic(Secondary),
_ => if enforce { _ => if all {
error!("expected dimension") error!("expected dimension")
} else { } else {
args.add_key(arg); args.add_key_pair(arg);
continue; continue;
} }
}; };
let e = E::from_expr(arg.v.value)?; let e = E::from_expr(arg.value)?;
map.add(key, e)?; 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::First, args.get_pos_opt::<E>()?)?;
map.add_opt(PosAxisKey::Second, args.get_pos_opt::<E>()?)?; map.add_opt(PosAxisKey::Second, args.get_pos_opt::<E>()?)?;
for arg in args.keys() { for arg in args.iter_keys() {
let axis = AxisKey::from_ident(&arg.v.key)?; let axis = AxisKey::from_ident(&arg.key)?;
let value = E::from_expr(arg.v.value)?; let value = E::from_expr(arg.value)?;
map.add(PosAxisKey::Keyword(axis), value)?; map.add(PosAxisKey::Keyword(axis), value)?;
} }

View File

@ -36,6 +36,37 @@ pub_use_mod!(axis);
pub_use_mod!(alignment); pub_use_mod!(alignment);
pub_use_mod!(padding); 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. /// A deduplicating map type useful for storing possibly redundant arguments.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ConsistentMap<K, V> where K: Hash + Eq { 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() 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,
);

View File

@ -46,11 +46,12 @@ impl PaddingMap {
/// Parse a padding map from the function args. /// Parse a padding map from the function args.
pub fn new(args: &mut FuncArgs) -> ParseResult<PaddingMap> { pub fn new(args: &mut FuncArgs) -> ParseResult<PaddingMap> {
let mut map = ConsistentMap::new(); 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() { for arg in args.iter_keys() {
let key = PaddingKey::from_ident(&arg.v.key)?; let key = PaddingKey::from_ident(&arg.key)?;
let size = Option::<PSize>::from_expr(arg.v.value)?; let size = DefaultKey::<PSize>::from_expr(arg.value)?.into();
map.add(key, size)?; map.add(key, size)?;
} }

View File

@ -62,7 +62,7 @@ function! {
FontFamilyFunc { FontFamilyFunc {
body: parse!(optional: body, ctx), body: parse!(optional: body, ctx),
list: { list: {
args.pos().map(|arg| match arg.v { args.iter_pos().map(|arg| match arg.v {
Expression::Str(s) | Expression::Str(s) |
Expression::Ident(Ident(s)) => Ok(s.to_lowercase()), Expression::Ident(Ident(s)) => Ok(s.to_lowercase()),
_ => error!("expected identifier or string"), _ => error!("expected identifier or string"),
@ -118,7 +118,7 @@ function! {
FontWeightFunc { FontWeightFunc {
body: parse!(optional: body, ctx), body: parse!(optional: body, ctx),
weight: match args.get_pos::<Expression>()? { weight: match args.get_pos::<Expression>()? {
Expression::Num(weight) => { Expression::Number(weight) => {
let weight = weight.round() as i16; let weight = weight.round() as i16;
FontWeight( FontWeight(
if weight < 100 { 100 } if weight < 100 { 100 }
@ -264,13 +264,15 @@ function! {
axis: AxisKey::Specific(axis), axis: AxisKey::Specific(axis),
spacing: FSize::from_expr(args.get_pos::<Spanned<Expression>>()?)?, 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 { } 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") error!("expected axis and spacing")
} }
} }

View File

@ -15,7 +15,7 @@ pub enum ColorToken {
Brace, Brace,
ExprIdent, ExprIdent,
ExprString, ExprStr,
ExprNumber, ExprNumber,
ExprSize, ExprSize,
ExprBool, ExprBool,

View File

@ -1,125 +1,16 @@
use super::*; 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. /// An argument or return value.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub enum Expression { pub enum Expression {
Ident(Ident), Ident(Ident),
Str(String), Str(String),
Num(f64), Number(f64),
Size(Size), Size(Size),
Bool(bool), Bool(bool),
Tuple(Tuple),
Object(Object),
} }
impl Display for Expression { impl Display for Expression {
@ -128,20 +19,17 @@ impl Display for Expression {
match self { match self {
Ident(i) => write!(f, "{}", i), Ident(i) => write!(f, "{}", i),
Str(s) => write!(f, "{:?}", s), Str(s) => write!(f, "{:?}", s),
Num(n) => write!(f, "{}", n), Number(n) => write!(f, "{}", n),
Size(s) => write!(f, "{}", s), Size(s) => write!(f, "{}", s),
Bool(b) => write!(f, "{}", b), 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. /// An identifier.
#[derive(Clone, PartialEq)] #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Ident(pub String); pub struct Ident(pub String);
impl Ident { 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!(Ident);
debug_display!(Expression);
debug_display!(Tuple);
debug_display!(Object);
/// Kinds of expressions. /// Kinds of expressions.
pub trait ExpressionKind: Sized { pub trait ExpressionKind: Sized {
/// The name of the expression in an `expected <name>` error.
const NAME: &'static str; const NAME: &'static str;
/// Create from expression. /// Create from expression.
fn from_expr(expr: Spanned<Expression>) -> ParseResult<Self>; fn from_expr(expr: Spanned<Expression>) -> ParseResult<Self>;
} }
/// Implements the expression kind trait for a type.
macro_rules! kind { macro_rules! kind {
($type:ty, $name:expr, $($patterns:tt)*) => { ($type:ty, $name:expr, $($patterns:tt)*) => {
impl ExpressionKind for $type { impl ExpressionKind for $type {
@ -190,15 +160,18 @@ macro_rules! kind {
}; };
} }
kind!(Expression, "expression", e => e); kind!(Expression, "expression", e => e);
kind!(Ident, "identifier", Expression::Ident(ident) => ident); kind!(Ident, "identifier", Expression::Ident(ident) => ident);
kind!(String, "string", Expression::Str(string) => string); kind!(String, "string", Expression::Str(string) => string);
kind!(f64, "number", Expression::Num(num) => num); kind!(f64, "number", Expression::Number(num) => num);
kind!(bool, "boolean", Expression::Bool(boolean) => boolean); kind!(bool, "boolean", Expression::Bool(boolean) => boolean);
kind!(Size, "size", Expression::Size(size) => size); 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", kind!(ScaleSize, "number or size",
Expression::Size(size) => ScaleSize::Absolute(size), Expression::Size(size) => ScaleSize::Absolute(size),
Expression::Num(scale) => ScaleSize::Scaled(scale as f32) Expression::Number(scale) => ScaleSize::Scaled(scale as f32)
); );
impl<T> ExpressionKind for Spanned<T> where T: ExpressionKind { 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>> { fn from_expr(expr: Spanned<Expression>) -> ParseResult<Spanned<T>> {
let span = expr.span; let span = expr.span;
T::from_expr(expr) T::from_expr(expr).map(|v| Spanned { v, span })
.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))
} }
} }

View File

@ -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. /// Parses source code into a syntax tree given a context.
pub fn parse(src: &str, ctx: ParseContext) -> SyntaxTree { pub fn parse(src: &str, ctx: ParseContext) -> SyntaxTree {
Parser::new(src, ctx).parse() Parser::new(src, ctx).parse()
@ -346,7 +439,7 @@ impl<'s> Parser<'s> {
Comma => Some(ColorToken::Comma), Comma => Some(ColorToken::Comma),
Equals => Some(ColorToken::Equals), Equals => Some(ColorToken::Equals),
ExprIdent(_) => Some(ColorToken::ExprIdent), ExprIdent(_) => Some(ColorToken::ExprIdent),
ExprString(_) => Some(ColorToken::ExprString), ExprStr(_) => Some(ColorToken::ExprStr),
ExprNumber(_) => Some(ColorToken::ExprNumber), ExprNumber(_) => Some(ColorToken::ExprNumber),
ExprSize(_) => Some(ColorToken::ExprSize), ExprSize(_) => Some(ColorToken::ExprSize),
ExprBool(_) => Some(ColorToken::ExprBool), ExprBool(_) => Some(ColorToken::ExprBool),
@ -387,7 +480,7 @@ fn name(token: Token) -> &'static str {
Comma => "comma", Comma => "comma",
Equals => "equals sign", Equals => "equals sign",
ExprIdent(_) => "identifier", ExprIdent(_) => "identifier",
ExprString(_) => "string", ExprStr(_) => "string",
ExprNumber(_) => "number", ExprNumber(_) => "number",
ExprSize(_) => "size", ExprSize(_) => "size",
ExprBool(_) => "bool", ExprBool(_) => "bool",

View File

@ -4,7 +4,7 @@ use std::fmt::{self, Display, Formatter};
/// Annotates a value with the part of the source code it corresponds to. /// 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 struct Spanned<T> {
pub v: T, pub v: T,
pub span: Span, 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); debug_display!(Spanned; T where T: std::fmt::Debug);
/// Describes a slice of source code. /// Describes a slice of source code.
#[derive(Copy, Clone, Eq, PartialEq)] #[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Span { pub struct Span {
pub start: Position, pub start: Position,
pub end: Position, pub end: Position,

View File

@ -45,7 +45,7 @@ pub enum Token<'s> {
/// An identifier in a function header: `center`. /// An identifier in a function header: `center`.
ExprIdent(&'s str), ExprIdent(&'s str),
/// A quoted string in a function header: `"..."`. /// A quoted string in a function header: `"..."`.
ExprString(&'s str), ExprStr(&'s str),
/// A number in a function header: `3.14`. /// A number in a function header: `3.14`.
ExprNumber(f64), ExprNumber(f64),
/// A size in a function header: `12pt`. /// A size in a function header: `12pt`.
@ -220,7 +220,7 @@ impl<'s> Tokens<'s> {
fn parse_string(&mut self) -> Token<'s> { fn parse_string(&mut self) -> Token<'s> {
let mut escaped = false; let mut escaped = false;
ExprString(self.read_string_until(|n| { ExprStr(self.read_string_until(|n| {
if n == '"' && !escaped { if n == '"' && !escaped {
return true; return true;
} else if n == '\\' { } else if n == '\\' {

View File

@ -10,7 +10,7 @@ use Token::{
LeftParen as LP, RightParen as RP, LeftParen as LP, RightParen as RP,
LeftBrace as LBR, RightBrace as RBR, LeftBrace as LBR, RightBrace as RBR,
Colon as CL, Comma as CM, Equals as EQ, Colon as CL, Comma as CM, Equals as EQ,
ExprIdent as ID, ExprString as STR, ExprSize as SIZE, ExprIdent as ID, ExprStr as STR, ExprSize as SIZE,
ExprNumber as NUM, ExprBool as BOOL, ExprNumber as NUM, ExprBool as BOOL,
Star as ST, Underscore as U, Backtick as B, Text as T, Star as ST, Underscore as U, Backtick as B, Text as T,
}; };