diff options
| author | Laurenz <laurmaedje@gmail.com> | 2019-12-05 19:48:37 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2019-12-05 19:48:37 +0100 |
| commit | 72a9631b038d1a60e4e4a78e92cd69e6f8ce4316 (patch) | |
| tree | 17614efc2e21dd0b8caa24beaaaee7c40c150281 /src/syntax/mod.rs | |
| parent | f72b1505bebf8d2fe1a60d386a3a3c3b67d4f903 (diff) | |
Move arg parser into `FuncArgs` and create (incomplete) consistent map ðŸ§
Diffstat (limited to 'src/syntax/mod.rs')
| -rw-r--r-- | src/syntax/mod.rs | 204 |
1 files changed, 181 insertions, 23 deletions
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs index 1b55fb4e..21088b83 100644 --- a/src/syntax/mod.rs +++ b/src/syntax/mod.rs @@ -1,9 +1,10 @@ //! Tokenization and parsing of source code. use std::fmt::{self, Display, Formatter}; +use unicode_xid::UnicodeXID; use crate::func::LayoutFunc; -use crate::size::Size; +use crate::size::{Size, ScaleSize}; mod tokens; #[macro_use] @@ -88,54 +89,133 @@ pub enum Node { Func(FuncCall), } -/// A function invocation, consisting of header and a dynamically parsed body. +/// An invocation of a function. #[derive(Debug)] pub struct FuncCall { - pub header: Spanned<FuncHeader>, - pub body: Spanned<Box<dyn LayoutFunc>>, + pub call: Box<dyn LayoutFunc>, } impl PartialEq for FuncCall { fn eq(&self, other: &FuncCall) -> bool { - (self.header == other.header) && (&self.body == &other.body) + &self.call == &other.call } } -/// Contains header information of a function invocation. -#[derive(Debug, Clone, PartialEq)] -pub struct FuncHeader { - pub name: Spanned<String>, - pub args: FuncArgs, -} - /// The arguments passed to a function. #[derive(Debug, Clone, PartialEq)] pub struct FuncArgs { - pub positional: Vec<Spanned<Expression>>, - pub keyword: Vec<Spanned<(Spanned<String>, Spanned<Expression>)>> + pub pos: Vec<Spanned<PosArg>>, + pub key: Vec<Spanned<KeyArg>>, } impl FuncArgs { /// Create an empty collection of arguments. - fn new() -> FuncArgs { + pub fn new() -> FuncArgs { FuncArgs { - positional: vec![], - keyword: vec![], + 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<Spanned<E>> { + expect(self.get_pos_opt()) + } + + /// Extract the first positional argument. + pub fn get_pos_opt<E: ExpressionKind>(&mut self) -> ParseResult<Option<Spanned<E>>> { + Ok(if !self.pos.is_empty() { + let spanned = self.pos.remove(0); + let span = spanned.span; + Some(Spanned::new(E::from_expr(spanned)?, span)) + } 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<Spanned<E>> { + expect(self.get_key_opt(name)) + } + + /// Extract a keyword argument. + pub fn get_key_opt<E: ExpressionKind>(&mut self, name: &str) -> ParseResult<Option<Spanned<E>>> { + Ok(if let Some(index) = self.key.iter().position(|arg| arg.v.key.v.0 == name) { + let Spanned { v, span } = self.key.swap_remove(index); + Some(Spanned::new(E::from_expr(v.value)?, span)) + } 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() + } } -/// One argument passed to a function. +fn expect<E: ExpressionKind>(opt: ParseResult<Option<Spanned<E>>>) -> ParseResult<Spanned<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 enum FuncArg { - Positional(Spanned<Expression>), - Keyword(Spanned<(Spanned<String>, Spanned<Expression>)>), +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(String), + Ident(Ident), Str(String), Num(f64), Size(Size), @@ -146,7 +226,7 @@ impl Display for Expression { fn fmt(&self, f: &mut Formatter) -> fmt::Result { use Expression::*; match self { - Ident(s) => write!(f, "{}", s), + Ident(i) => write!(f, "{}", i), Str(s) => write!(f, "{:?}", s), Num(n) => write!(f, "{}", n), Size(s) => write!(f, "{}", s), @@ -156,3 +236,81 @@ impl Display for Expression { } debug_display!(Expression); + +/// An identifier. +#[derive(Clone, PartialEq)] +pub struct Ident(pub String); + +impl Ident { + fn new(string: String) -> ParseResult<Ident> { + if is_identifier(&string) { + Ok(Ident(string)) + } else { + error!("invalid identifier: `{}`", string); + } + } +} + +impl Display for Ident { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +debug_display!(Ident); + +/// Whether this word is a valid unicode identifier. +fn is_identifier(string: &str) -> bool { + let mut chars = string.chars(); + + match chars.next() { + Some('-') => (), + Some(c) if UnicodeXID::is_xid_start(c) => (), + _ => return false, + } + + while let Some(c) = chars.next() { + match c { + '.' | '-' => (), + c if UnicodeXID::is_xid_continue(c) => (), + _ => return false, + } + } + + true +} + +/// Kinds of expressions. +pub trait ExpressionKind: Sized { + const NAME: &'static str; + + /// Create from expression. + fn from_expr(expr: Spanned<Expression>) -> ParseResult<Self>; +} + +macro_rules! kind { + ($type:ty, $name:expr, $($patterns:tt)*) => { + impl ExpressionKind for $type { + const NAME: &'static str = $name; + + fn from_expr(expr: Spanned<Expression>) -> ParseResult<Self> { + #[allow(unreachable_patterns)] + Ok(match expr.v { + $($patterns)*, + _ => error!("expected {}", Self::NAME), + }) + } + } + }; +} + +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!(ScaleSize, "number or size", + Expression::Size(size) => ScaleSize::Absolute(size), + Expression::Num(scale) => ScaleSize::Scaled(scale as f32) +); |
