summaryrefslogtreecommitdiff
path: root/src/syntax/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/syntax/mod.rs')
-rw-r--r--src/syntax/mod.rs204
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)
+);