summaryrefslogtreecommitdiff
path: root/src/eval/template.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-07-10 20:01:18 +0200
committerLaurenz <laurmaedje@gmail.com>2021-07-10 23:10:17 +0200
commit6a4823461f491aef63451f097ddfe5602e0b2157 (patch)
treead11b0ad169d030942d950573c729d50f7b3291b /src/eval/template.rs
parent36b3067c19c8743032a44f888ee48702b88d135b (diff)
Reference-count complex values
Rename some nodes types
Diffstat (limited to 'src/eval/template.rs')
-rw-r--r--src/eval/template.rs139
1 files changed, 139 insertions, 0 deletions
diff --git a/src/eval/template.rs b/src/eval/template.rs
new file mode 100644
index 00000000..4d003998
--- /dev/null
+++ b/src/eval/template.rs
@@ -0,0 +1,139 @@
+use std::collections::HashMap;
+use std::fmt::{self, Debug, Formatter};
+use std::ops::{Add, Deref};
+use std::rc::Rc;
+
+use super::Value;
+use crate::eco::EcoString;
+use crate::exec::ExecContext;
+use crate::syntax::{Expr, SyntaxTree};
+
+/// A template value: `[*Hi* there]`.
+#[derive(Default, Debug, Clone)]
+pub struct Template {
+ nodes: Rc<Vec<TemplateNode>>,
+}
+
+impl Template {
+ /// Create a new template from a vector of nodes.
+ pub fn new(nodes: Vec<TemplateNode>) -> Self {
+ Self { nodes: Rc::new(nodes) }
+ }
+
+ /// Iterate over the contained template nodes.
+ pub fn iter(&self) -> impl Iterator<Item = &TemplateNode> + '_ {
+ self.nodes.iter()
+ }
+}
+
+impl From<TemplateTree> for Template {
+ fn from(tree: TemplateTree) -> Self {
+ Self::new(vec![TemplateNode::Tree(tree)])
+ }
+}
+
+impl From<TemplateFunc> for Template {
+ fn from(func: TemplateFunc) -> Self {
+ Self::new(vec![TemplateNode::Func(func)])
+ }
+}
+
+impl From<EcoString> for Template {
+ fn from(string: EcoString) -> Self {
+ Self::new(vec![TemplateNode::Str(string)])
+ }
+}
+
+impl PartialEq for Template {
+ fn eq(&self, other: &Self) -> bool {
+ Rc::ptr_eq(&self.nodes, &other.nodes)
+ }
+}
+
+impl Add<&Template> for Template {
+ type Output = Self;
+
+ fn add(mut self, rhs: &Self) -> Self::Output {
+ Rc::make_mut(&mut self.nodes).extend(rhs.nodes.iter().cloned());
+ self
+ }
+}
+
+impl Add<EcoString> for Template {
+ type Output = Self;
+
+ fn add(mut self, rhs: EcoString) -> Self::Output {
+ Rc::make_mut(&mut self.nodes).push(TemplateNode::Str(rhs));
+ self
+ }
+}
+
+impl Add<Template> for EcoString {
+ type Output = Template;
+
+ fn add(self, mut rhs: Template) -> Self::Output {
+ Rc::make_mut(&mut rhs.nodes).insert(0, TemplateNode::Str(self));
+ rhs
+ }
+}
+
+/// One node of a template.
+///
+/// Evaluating a template expression creates only a single node. Adding multiple
+/// templates can yield multi-node templates.
+#[derive(Debug, Clone)]
+pub enum TemplateNode {
+ /// A template that was evaluated from a template expression.
+ Tree(TemplateTree),
+ /// A function template that can implement custom behaviour.
+ Func(TemplateFunc),
+ /// A template that was converted from a string.
+ Str(EcoString),
+}
+
+/// A template that consists of a syntax tree plus already evaluated
+/// expressions.
+#[derive(Debug, Clone)]
+pub struct TemplateTree {
+ /// The syntax tree of the corresponding template expression.
+ pub tree: Rc<SyntaxTree>,
+ /// The evaluated expressions in the syntax tree.
+ pub map: ExprMap,
+}
+
+/// A map from expressions to the values they evaluated to.
+///
+/// The raw pointers point into the expressions contained in some
+/// [`SyntaxTree`]. Since the lifetime is erased, the tree could go out of scope
+/// while the hash map still lives. Although this could lead to lookup panics,
+/// it is not unsafe since the pointers are never dereferenced.
+pub type ExprMap = HashMap<*const Expr, Value>;
+
+/// A reference-counted dynamic template node that can implement custom
+/// behaviour.
+#[derive(Clone)]
+pub struct TemplateFunc(Rc<dyn Fn(&mut ExecContext)>);
+
+impl TemplateFunc {
+ /// Create a new function template from a rust function or closure.
+ pub fn new<F>(f: F) -> Self
+ where
+ F: Fn(&mut ExecContext) + 'static,
+ {
+ Self(Rc::new(f))
+ }
+}
+
+impl Deref for TemplateFunc {
+ type Target = dyn Fn(&mut ExecContext);
+
+ fn deref(&self) -> &Self::Target {
+ self.0.as_ref()
+ }
+}
+
+impl Debug for TemplateFunc {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.debug_struct("TemplateFunc").finish()
+ }
+}