diff options
| author | Laurenz <laurmaedje@gmail.com> | 2020-08-16 22:14:27 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2020-08-16 22:39:21 +0200 |
| commit | 30f16bbf6431ca0c174ca0a1abaa6a13ef50ab06 (patch) | |
| tree | f5a5c0adad15840ebe24b39e77ff467862067c91 /src/syntax/tree.rs | |
| parent | 9f6137d8a829fe8f34554623495fa620252a0184 (diff) | |
Add Value type and replace dyn-nodes with call-exprs 🏗
- In addition to syntax trees there are now `Value`s, which syntax trees can be evaluated into (e.g. the tree is `5+5` and the value is `10`)
- Parsing is completely pure, function calls are not parsed into nodes, but into simple call expressions, which are resolved later
- Functions aren't dynamic nodes anymore, but simply functions which receive their arguments as a table and the layouting context
- Functions may return any `Value`
- Layouting is powered by functions which return the new `Commands` value, which informs the layouting engine what to do
- When a function returns a non-`Commands` value, the layouter simply dumps the value into the document in monospace
Diffstat (limited to 'src/syntax/tree.rs')
| -rw-r--r-- | src/syntax/tree.rs | 191 |
1 files changed, 124 insertions, 67 deletions
diff --git a/src/syntax/tree.rs b/src/syntax/tree.rs index 4ce39cd4..e7a1eaf1 100644 --- a/src/syntax/tree.rs +++ b/src/syntax/tree.rs @@ -1,17 +1,20 @@ //! The syntax tree. -use std::any::Any; -use std::fmt::Debug; +use std::fmt::{self, Debug, Formatter}; -use crate::layout::Layout; -use super::span::SpanVec; +use crate::color::RgbaColor; +use crate::compute::table::{SpannedEntry, Table}; +use crate::compute::value::{TableValue, Value}; +use crate::length::Length; +use super::span::{Spanned, SpanVec}; +use super::Ident; /// A collection of nodes which form a tree together with the nodes' children. pub type SyntaxTree = SpanVec<SyntaxNode>; /// A syntax node, which encompasses a single logical entity of parsed source /// code. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum SyntaxNode { /// Whitespace containing less than two newlines. Spacing, @@ -27,84 +30,138 @@ pub enum SyntaxNode { Raw(Vec<String>), /// A paragraph of child nodes. Par(SyntaxTree), - /// A dynamic node, created through function invocations in source code. - Dyn(Box<dyn DynamicNode>), + /// A function call. + Call(CallExpr), } -impl SyntaxNode { - /// Create a `Dyn` variant from an unboxed dynamic node. - pub fn boxed<T: DynamicNode + 'static>(node: T) -> SyntaxNode { - SyntaxNode::Dyn(Box::new(node)) - } +/// An expression. +#[derive(Clone, PartialEq)] +pub enum Expr { + /// An identifier: `ident`. + Ident(Ident), + /// A string: `"string"`. + Str(String), + /// A boolean: `true, false`. + Bool(bool), + /// A number: `1.2, 200%`. + Number(f64), + /// A length: `2cm, 5.2in`. + Length(Length), + /// A color value with alpha channel: `#f79143ff`. + Color(RgbaColor), + /// A table expression: `(false, 12cm, greeting="hi")`. + Table(TableExpr), + /// A syntax tree containing typesetting content. + Tree(SyntaxTree), + /// A function call expression: `cmyk(37.7, 0, 3.9, 1.1)`. + Call(CallExpr), + /// An operation that negates the contained expression. + Neg(Box<Spanned<Expr>>), + /// An operation that adds the contained expressions. + Add(Box<Spanned<Expr>>, Box<Spanned<Expr>>), + /// An operation that subtracts the contained expressions. + Sub(Box<Spanned<Expr>>, Box<Spanned<Expr>>), + /// An operation that multiplies the contained expressions. + Mul(Box<Spanned<Expr>>, Box<Spanned<Expr>>), + /// An operation that divides the contained expressions. + Div(Box<Spanned<Expr>>, Box<Spanned<Expr>>), } -impl PartialEq for SyntaxNode { - fn eq(&self, other: &SyntaxNode) -> bool { - use SyntaxNode::*; - match (self, other) { - (Spacing, Spacing) => true, - (Linebreak, Linebreak) => true, - (ToggleItalic, ToggleItalic) => true, - (ToggleBolder, ToggleBolder) => true, - (Text(a), Text(b)) => a == b, - (Raw(a), Raw(b)) => a == b, - (Par(a), Par(b)) => a == b, - (Dyn(a), Dyn(b)) => a == b, - _ => false, +impl Expr { + /// A natural-language name of the type of this expression, e.g. + /// "identifier". + pub fn name(&self) -> &'static str { + use Expr::*; + match self { + Ident(_) => "identifier", + Str(_) => "string", + Bool(_) => "bool", + Number(_) => "number", + Length(_) => "length", + Color(_) => "color", + Table(_) => "table", + Tree(_) => "syntax tree", + Call(_) => "function call", + Neg(_) => "negation", + Add(_, _) => "addition", + Sub(_, _) => "subtraction", + Mul(_, _) => "multiplication", + Div(_, _) => "division", } } -} - -/// Dynamic syntax nodes. -/// -/// *Note*: This is automatically implemented for all types which are -/// `Debug + Clone + PartialEq`, `Layout` and `'static`. -pub trait DynamicNode: Debug + Layout { - /// Convert into a `dyn Any`. - fn as_any(&self) -> &dyn Any; - - /// Check for equality with another dynamic node. - fn dyn_eq(&self, other: &dyn DynamicNode) -> bool; - /// Clone into a boxed node trait object. - fn box_clone(&self) -> Box<dyn DynamicNode>; -} - -impl dyn DynamicNode { - /// Downcast this dynamic node to a concrete node. - pub fn downcast<T: DynamicNode + 'static>(&self) -> Option<&T> { - self.as_any().downcast_ref::<T>() + /// Evaluate the expression to a value. + pub fn eval(&self) -> Value { + use Expr::*; + match self { + Ident(i) => Value::Ident(i.clone()), + Str(s) => Value::Str(s.clone()), + &Bool(b) => Value::Bool(b), + &Number(n) => Value::Number(n), + &Length(s) => Value::Length(s), + &Color(c) => Value::Color(c), + Table(t) => Value::Table(t.eval()), + Tree(t) => Value::Tree(t.clone()), + Call(_) => todo!("eval call"), + Neg(_) => todo!("eval neg"), + Add(_, _) => todo!("eval add"), + Sub(_, _) => todo!("eval sub"), + Mul(_, _) => todo!("eval mul"), + Div(_, _) => todo!("eval div"), + } } } -impl PartialEq for dyn DynamicNode { - fn eq(&self, other: &Self) -> bool { - self.dyn_eq(other) +impl Debug for Expr { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + use Expr::*; + match self { + Ident(i) => i.fmt(f), + Str(s) => s.fmt(f), + Bool(b) => b.fmt(f), + Number(n) => n.fmt(f), + Length(s) => s.fmt(f), + Color(c) => c.fmt(f), + Table(t) => t.fmt(f), + Tree(t) => t.fmt(f), + Call(c) => c.fmt(f), + Neg(e) => write!(f, "-{:?}", e), + Add(a, b) => write!(f, "({:?} + {:?})", a, b), + Sub(a, b) => write!(f, "({:?} - {:?})", a, b), + Mul(a, b) => write!(f, "({:?} * {:?})", a, b), + Div(a, b) => write!(f, "({:?} / {:?})", a, b), + } } } -impl Clone for Box<dyn DynamicNode> { - fn clone(&self) -> Self { - self.box_clone() - } -} +/// A table of expressions. +/// +/// # Example +/// ```typst +/// (false, 12cm, greeting="hi") +/// ``` +pub type TableExpr = Table<SpannedEntry<Expr>>; -impl<T> DynamicNode for T -where - T: Debug + PartialEq + Clone + Layout + 'static, -{ - fn as_any(&self) -> &dyn Any { - self - } +impl TableExpr { + /// Evaluate the table expression to a table value. + pub fn eval(&self) -> TableValue { + let mut table = TableValue::new(); - fn dyn_eq(&self, other: &dyn DynamicNode) -> bool { - match other.as_any().downcast_ref::<Self>() { - Some(other) => self == other, - None => false, + for (&key, entry) in self.nums() { + table.insert(key, entry.as_ref().map(|val| val.eval())); } - } - fn box_clone(&self) -> Box<dyn DynamicNode> { - Box::new(self.clone()) + for (key, entry) in self.strs() { + table.insert(key.clone(), entry.as_ref().map(|val| val.eval())); + } + + table } } + +/// An invocation of a function. +#[derive(Debug, Clone, PartialEq)] +pub struct CallExpr { + pub name: Spanned<Ident>, + pub args: TableExpr, +} |
