diff options
| author | Laurenz <laurmaedje@gmail.com> | 2020-01-13 14:36:40 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2020-01-13 14:36:40 +0100 |
| commit | dde69276d47818174c35523c8ed86b6888b6d02b (patch) | |
| tree | 68f0f56efd42f47156fddf67158cdcdcde3717b9 /src/syntax | |
| parent | 6527d31dfba78330a39e52d7772f6c8561fb23ef (diff) | |
Refactor expressions and create tuples and objects 🧮
Diffstat (limited to 'src/syntax')
| -rw-r--r-- | src/syntax/color.rs | 2 | ||||
| -rw-r--r-- | src/syntax/expr.rs | 245 | ||||
| -rw-r--r-- | src/syntax/parsing.rs | 97 | ||||
| -rw-r--r-- | src/syntax/span.rs | 4 | ||||
| -rw-r--r-- | src/syntax/tokens.rs | 4 |
5 files changed, 201 insertions, 151 deletions
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 == '\\' { |
