From 72a9631b038d1a60e4e4a78e92cd69e6f8ce4316 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Thu, 5 Dec 2019 19:48:37 +0100 Subject: =?UTF-8?q?Move=20arg=20parser=20into=20`FuncArgs`=20and=20create?= =?UTF-8?q?=20(incomplete)=20consistent=20map=20=F0=9F=A7=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/syntax/mod.rs | 204 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 181 insertions(+), 23 deletions(-) (limited to 'src/syntax/mod.rs') 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, - pub body: Spanned>, + pub call: Box, } 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, - pub args: FuncArgs, -} - /// The arguments passed to a function. #[derive(Debug, Clone, PartialEq)] pub struct FuncArgs { - pub positional: Vec>, - pub keyword: Vec, Spanned)>> + pub pos: Vec>, + pub key: Vec>, } 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) { + self.pos.push(arg); + } + + /// Add a keyword argument. + pub fn add_key(&mut self, arg: Spanned) { + self.key.push(arg); + } + + /// Force-extract the first positional argument. + pub fn get_pos(&mut self) -> ParseResult> { + expect(self.get_pos_opt()) + } + + /// Extract the first positional argument. + pub fn get_pos_opt(&mut self) -> ParseResult>> { + 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> { + let vec = std::mem::replace(&mut self.pos, vec![]); + vec.into_iter() + } + + /// Force-extract a keyword argument. + pub fn get_key(&mut self, name: &str) -> ParseResult> { + expect(self.get_key_opt(name)) + } + + /// Extract a keyword argument. + pub fn get_key_opt(&mut self, name: &str) -> ParseResult>> { + 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> { + self.key.pop() + } + + /// Iterator over all keyword arguments. + pub fn keys(&mut self) -> std::vec::IntoIter> { + 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(opt: ParseResult>>) -> ParseResult> { + 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), - Keyword(Spanned<(Spanned, Spanned)>), +pub struct KeyArg { + pub key: Spanned, + pub value: Spanned, +} + +/// Either a positional or keyword argument. +#[derive(Debug, Clone, PartialEq)] +pub enum DynArg { + Pos(Spanned), + Key(Spanned), } /// 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 { + 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) -> ParseResult; +} + +macro_rules! kind { + ($type:ty, $name:expr, $($patterns:tt)*) => { + impl ExpressionKind for $type { + const NAME: &'static str = $name; + + fn from_expr(expr: Spanned) -> ParseResult { + #[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) +); -- cgit v1.2.3