summaryrefslogtreecommitdiff
path: root/src/syntax/tree.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-08-16 22:14:27 +0200
committerLaurenz <laurmaedje@gmail.com>2020-08-16 22:39:21 +0200
commit30f16bbf6431ca0c174ca0a1abaa6a13ef50ab06 (patch)
treef5a5c0adad15840ebe24b39e77ff467862067c91 /src/syntax/tree.rs
parent9f6137d8a829fe8f34554623495fa620252a0184 (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.rs191
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,
+}