diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-07-10 20:01:18 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-07-10 23:10:17 +0200 |
| commit | 6a4823461f491aef63451f097ddfe5602e0b2157 (patch) | |
| tree | ad11b0ad169d030942d950573c729d50f7b3291b /src/eval/template.rs | |
| parent | 36b3067c19c8743032a44f888ee48702b88d135b (diff) | |
Reference-count complex values
Rename some nodes types
Diffstat (limited to 'src/eval/template.rs')
| -rw-r--r-- | src/eval/template.rs | 139 |
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() + } +} |
