summaryrefslogtreecommitdiff
path: root/src/syntax
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-08-02 22:05:49 +0200
committerLaurenz <laurmaedje@gmail.com>2020-08-02 22:05:49 +0200
commit266d457292e7461d448f9141030028ea68b573d1 (patch)
treeff3ff3cc289d34040db421b6a7faa1f2aa402b05 /src/syntax
parentcbbc46215fe0a0ad8a50e991ec442890b8eadc0a (diff)
Refactor model into tree 🛒
Diffstat (limited to 'src/syntax')
-rw-r--r--src/syntax/expr.rs6
-rw-r--r--src/syntax/mod.rs4
-rw-r--r--src/syntax/model.rs134
-rw-r--r--src/syntax/parsing.rs76
-rw-r--r--src/syntax/scope.rs12
-rw-r--r--src/syntax/test.rs18
-rw-r--r--src/syntax/tokens.rs4
-rw-r--r--src/syntax/tree.rs100
8 files changed, 157 insertions, 197 deletions
diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs
index a551c2b6..0a9ab149 100644
--- a/src/syntax/expr.rs
+++ b/src/syntax/expr.rs
@@ -7,7 +7,7 @@ use std::u8;
use crate::Feedback;
use crate::length::Length;
-use super::span::Spanned;
+use super::span::{Spanned, SpanVec};
use super::tokens::is_identifier;
use super::value::Value;
@@ -237,7 +237,7 @@ impl fmt::Display for ParseColorError {
/// (false, 12cm, "hi")
/// ```
#[derive(Default, Clone, PartialEq)]
-pub struct Tuple(pub Vec<Spanned<Expr>>);
+pub struct Tuple(pub SpanVec<Expr>);
impl Tuple {
/// Create an empty tuple.
@@ -333,7 +333,7 @@ impl Deref for NamedTuple {
/// { fit: false, width: 12cm, items: (1, 2, 3) }
/// ```
#[derive(Default, Clone, PartialEq)]
-pub struct Object(pub Vec<Spanned<Pair>>);
+pub struct Object(pub SpanVec<Pair>);
/// A key-value pair in an object.
#[derive(Debug, Clone, PartialEq)]
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
index e844fdf1..86c2fd24 100644
--- a/src/syntax/mod.rs
+++ b/src/syntax/mod.rs
@@ -1,4 +1,4 @@
-//! Syntax models, parsing and tokenization.
+//! Syntax trees, parsing and tokenization.
#[cfg(test)]
#[macro_use]
@@ -6,7 +6,7 @@ mod test;
pub mod decoration;
pub mod expr;
-pub mod model;
+pub mod tree;
pub mod parsing;
pub mod span;
pub mod scope;
diff --git a/src/syntax/model.rs b/src/syntax/model.rs
deleted file mode 100644
index 4eb2abe0..00000000
--- a/src/syntax/model.rs
+++ /dev/null
@@ -1,134 +0,0 @@
-//! The syntax model.
-
-use std::any::Any;
-use std::fmt::Debug;
-use async_trait::async_trait;
-
-use crate::{Pass, Feedback};
-use crate::layout::{LayoutContext, Commands, Command};
-use super::span::{Spanned, SpanVec};
-
-/// Represents a parsed piece of source that can be layouted and in the future
-/// also be queried for information used for refactorings, autocomplete, etc.
-#[async_trait(?Send)]
-pub trait Model: Debug + ModelBounds {
- /// Layout the model into a sequence of commands processed by a
- /// [`ModelLayouter`](crate::layout::ModelLayouter).
- async fn layout<'a>(&'a self, ctx: LayoutContext<'_>) -> Pass<Commands<'a>>;
-}
-
-/// A tree representation of source code.
-#[derive(Debug, Default, Clone, PartialEq)]
-pub struct SyntaxModel {
- /// The syntactical elements making up this model.
- pub nodes: SpanVec<Node>,
-}
-
-impl SyntaxModel {
- /// Create an empty syntax model.
- pub fn new() -> SyntaxModel {
- SyntaxModel { nodes: vec![] }
- }
-
- /// Add a node to the model.
- pub fn add(&mut self, node: Spanned<Node>) {
- self.nodes.push(node);
- }
-}
-
-#[async_trait(?Send)]
-impl Model for SyntaxModel {
- async fn layout<'a>(&'a self, _: LayoutContext<'_>) -> Pass<Commands<'a>> {
- Pass::new(vec![Command::LayoutSyntaxModel(self)], Feedback::new())
- }
-}
-
-/// A node in the [syntax model](SyntaxModel).
-#[derive(Debug, Clone)]
-pub enum Node {
- /// Whitespace containing less than two newlines.
- Space,
- /// Whitespace with more than two newlines.
- Parbreak,
- /// A forced line break.
- Linebreak,
- /// Plain text.
- Text(String),
- /// Lines of raw text.
- Raw(Vec<String>),
- /// Italics were enabled / disabled.
- ToggleItalic,
- /// Bolder was enabled / disabled.
- ToggleBolder,
- /// A submodel, typically a function invocation.
- Model(Box<dyn Model>),
-}
-
-impl PartialEq for Node {
- fn eq(&self, other: &Node) -> bool {
- use Node::*;
- match (self, other) {
- (Space, Space) => true,
- (Parbreak, Parbreak) => true,
- (Linebreak, Linebreak) => true,
- (Text(a), Text(b)) => a == b,
- (Raw(a), Raw(b)) => a == b,
- (ToggleItalic, ToggleItalic) => true,
- (ToggleBolder, ToggleBolder) => true,
- (Model(a), Model(b)) => a == b,
- _ => false,
- }
- }
-}
-
-impl dyn Model {
- /// Downcast this model to a concrete type implementing [`Model`].
- pub fn downcast<T>(&self) -> Option<&T> where T: Model + 'static {
- self.as_any().downcast_ref::<T>()
- }
-}
-
-impl PartialEq for dyn Model {
- fn eq(&self, other: &dyn Model) -> bool {
- self.bound_eq(other)
- }
-}
-
-impl Clone for Box<dyn Model> {
- fn clone(&self) -> Self {
- self.bound_clone()
- }
-}
-
-/// This trait describes bounds necessary for types implementing [`Model`]. It is
-/// automatically implemented for all types that are [`Model`], [`PartialEq`],
-/// [`Clone`] and `'static`.
-///
-/// It is necessary to make models comparable and clonable.
-pub trait ModelBounds {
- /// Convert into a `dyn Any`.
- fn as_any(&self) -> &dyn Any;
-
- /// Check for equality with another model.
- fn bound_eq(&self, other: &dyn Model) -> bool;
-
- /// Clone into a boxed model trait object.
- fn bound_clone(&self) -> Box<dyn Model>;
-}
-
-impl<T> ModelBounds for T where T: Model + PartialEq + Clone + 'static {
- fn as_any(&self) -> &dyn Any {
- self
- }
-
- fn bound_eq(&self, other: &dyn Model) -> bool {
- match other.as_any().downcast_ref::<Self>() {
- Some(other) => self == other,
- None => false,
- }
- }
-
- fn bound_clone(&self) -> Box<dyn Model> {
- Box::new(self.clone())
- }
-}
diff --git a/src/syntax/parsing.rs b/src/syntax/parsing.rs
index 75e30177..7594c14d 100644
--- a/src/syntax/parsing.rs
+++ b/src/syntax/parsing.rs
@@ -1,4 +1,4 @@
-//! Parsing of source code into syntax models.
+//! Parsing of source code into syntax trees.
use std::str::FromStr;
@@ -8,10 +8,10 @@ use super::expr::*;
use super::scope::Scope;
use super::span::{Pos, Span, Spanned};
use super::tokens::{is_newline_char, Token, Tokens, TokenMode};
-use super::model::{SyntaxModel, Node, Model};
+use super::tree::{SyntaxTree, SyntaxNode, DynamicNode};
-/// A function which parses a function call into a model.
-pub type CallParser = dyn Fn(FuncCall, &ParseState) -> Pass<Box<dyn Model>>;
+/// A function which parses a function call into a tree.
+pub type CallParser = dyn Fn(FuncCall, &ParseState) -> Pass<Box<dyn DynamicNode>>;
/// An invocation of a function.
#[derive(Debug, Clone, PartialEq)]
@@ -73,12 +73,12 @@ pub struct ParseState {
/// Parse a string of source code.
///
-/// All spans in the resulting model and feedback are offset by the given
+/// All spans in the resulting tree and feedback are offset by the given
/// `offset` position. This is used to make spans of a function body relative to
/// the start of the function as a whole as opposed to the start of the
/// function's body.
-pub fn parse(src: &str, offset: Pos, state: &ParseState) -> Pass<SyntaxModel> {
- let mut model = SyntaxModel::new();
+pub fn parse(src: &str, offset: Pos, state: &ParseState) -> Pass<SyntaxTree> {
+ let mut tree = SyntaxTree::new();
let mut feedback = Feedback::new();
for token in Tokens::new(src, offset, TokenMode::Body) {
@@ -87,9 +87,9 @@ pub fn parse(src: &str, offset: Pos, state: &ParseState) -> Pass<SyntaxModel> {
// Starting from two newlines counts as a paragraph break, a single
// newline does not.
Token::Space(newlines) => if newlines >= 2 {
- Node::Parbreak
+ SyntaxNode::Parbreak
} else {
- Node::Space
+ SyntaxNode::Space
}
Token::Function { header, body, terminated } => {
@@ -103,19 +103,19 @@ pub fn parse(src: &str, offset: Pos, state: &ParseState) -> Pass<SyntaxModel> {
parsed.output
}
- Token::Star => Node::ToggleBolder,
- Token::Underscore => Node::ToggleItalic,
- Token::Backslash => Node::Linebreak,
+ Token::Star => SyntaxNode::ToggleBolder,
+ Token::Underscore => SyntaxNode::ToggleItalic,
+ Token::Backslash => SyntaxNode::Linebreak,
Token::Raw { raw, terminated } => {
if !terminated {
error!(@feedback, Span::at(span.end), "expected backtick");
}
- Node::Raw(unescape_raw(raw))
+ SyntaxNode::Raw(unescape_raw(raw))
}
- Token::Text(text) => Node::Text(text.to_string()),
+ Token::Text(text) => SyntaxNode::Text(text.to_string()),
Token::LineComment(_) | Token::BlockComment(_) => continue,
unexpected => {
@@ -124,10 +124,10 @@ pub fn parse(src: &str, offset: Pos, state: &ParseState) -> Pass<SyntaxModel> {
}
};
- model.add(Spanned::new(node, span));
+ tree.push(Spanned::new(node, span));
}
- Pass::new(model, feedback)
+ Pass::new(tree, feedback)
}
struct FuncParser<'s> {
@@ -164,7 +164,7 @@ impl<'s> FuncParser<'s> {
}
}
- fn parse(mut self) -> Pass<Node> {
+ fn parse(mut self) -> Pass<SyntaxNode> {
let (parser, header) = if let Some(header) = self.parse_func_header() {
let name = header.name.v.as_str();
let (parser, deco) = match self.state.scope.get_parser(name) {
@@ -197,9 +197,8 @@ impl<'s> FuncParser<'s> {
let call = FuncCall { header, body: self.body };
let parsed = parser(call, self.state);
-
self.feedback.extend(parsed.feedback);
- Pass::new(Node::Model(parsed.output), self.feedback)
+ Pass::new(SyntaxNode::Dyn(parsed.output), self.feedback)
}
fn parse_func_header(&mut self) -> Option<FuncHeader> {
@@ -662,26 +661,27 @@ fn unescape_raw(raw: &str) -> Vec<String> {
#[allow(non_snake_case)]
mod tests {
use crate::length::Length;
- use super::super::test::{check, DebugFn};
+ use crate::syntax::span::SpanVec;
+ use crate::syntax::test::{check, DebugFn};
use super::*;
use Decoration::*;
use Expr::{Number as Num, Length as Len, Bool};
- use Node::{
+ use SyntaxNode::{
Space as S, ToggleItalic as Italic, ToggleBolder as Bold,
Parbreak, Linebreak,
};
/// Test whether the given string parses into
- /// - the given node list (required).
+ /// - the given SyntaxNode list (required).
/// - the given error list (optional, if omitted checks against empty list).
/// - the given decoration list (optional, if omitted it is not tested).
macro_rules! p {
- ($source:expr => [$($model:tt)*]) => {
- p!($source => [$($model)*], []);
+ ($source:expr => [$($tree:tt)*]) => {
+ p!($source => [$($tree)*], []);
};
- ($source:expr => [$($model:tt)*], [$($diagnostics:tt)*] $(, [$($decos:tt)*])? $(,)?) => {
+ ($source:expr => [$($tree:tt)*], [$($diagnostics:tt)*] $(, [$($decos:tt)*])? $(,)?) => {
let mut scope = Scope::new::<DebugFn>();
scope.add::<DebugFn>("f");
scope.add::<DebugFn>("n");
@@ -691,9 +691,9 @@ mod tests {
let state = ParseState { scope };
let pass = parse($source, Pos::ZERO, &state);
- // Test model.
- let (exp, cmp) = span_vec![$($model)*];
- check($source, exp, pass.output.nodes, cmp);
+ // Test tree.
+ let (exp, cmp) = span_vec![$($tree)*];
+ check($source, exp, pass.output, cmp);
// Test diagnostics.
let (exp, cmp) = span_vec![$($diagnostics)*];
@@ -728,7 +728,7 @@ mod tests {
fn Sub(e1: Expr, e2: Expr) -> Expr { Expr::Sub(Box::new(Z(e1)), Box::new(Z(e2))) }
fn Mul(e1: Expr, e2: Expr) -> Expr { Expr::Mul(Box::new(Z(e1)), Box::new(Z(e2))) }
fn Div(e1: Expr, e2: Expr) -> Expr { Expr::Div(Box::new(Z(e1)), Box::new(Z(e2))) }
- fn T(text: &str) -> Node { Node::Text(text.to_string()) }
+ fn T(text: &str) -> SyntaxNode { SyntaxNode::Text(text.to_string()) }
fn Z<T>(v: T) -> Spanned<T> { Spanned::zero(v) }
macro_rules! tuple {
@@ -757,7 +757,7 @@ mod tests {
macro_rules! raw {
($($line:expr),* $(,)?) => {
- Node::Raw(vec![$($line.to_string()),*])
+ SyntaxNode::Raw(vec![$($line.to_string()),*])
};
}
@@ -769,7 +769,7 @@ mod tests {
#[allow(unused_mut)]
let mut args = FuncArgs::new();
$(
- let items: Vec<Spanned<Expr>> = span_vec![$($pos)*].0;
+ let items: SpanVec<Expr> = span_vec![$($pos)*].0;
for item in items {
args.push(item.map(|v| FuncArg::Pos(v)));
}
@@ -778,7 +778,7 @@ mod tests {
value: Z($value),
})));)*)?
)?
- Node::Model(Box::new(DebugFn {
+ SyntaxNode::Dyn(Box::new(DebugFn {
header: FuncHeader {
name: span_item!($name).map(|s| Ident(s.to_string())),
args,
@@ -786,7 +786,7 @@ mod tests {
body: func!(@body $($($body)*)?),
}))
}};
- (@body [$($body:tt)*]) => { Some(SyntaxModel { nodes: span_vec![$($body)*].0 }) };
+ (@body [$($body:tt)*]) => { Some(span_vec![$($body)*].0) };
(@body) => { None };
}
@@ -818,8 +818,8 @@ mod tests {
#[test]
fn unescape_raws() {
- fn test(raw: &str, expected: Node) {
- let vec = if let Node::Raw(v) = expected { v } else { panic!() };
+ fn test(raw: &str, expected: SyntaxNode) {
+ let vec = if let SyntaxNode::Raw(v) = expected { v } else { panic!() };
assert_eq!(unescape_raw(raw), vec);
}
@@ -834,8 +834,8 @@ mod tests {
}
#[test]
- fn parse_basic_nodes() {
- // Basic nodes.
+ fn parse_basic_SyntaxNodes() {
+ // Basic SyntaxNodes.
p!("" => []);
p!("hi" => [T("hi")]);
p!("*hi" => [Bold, T("hi")]);
@@ -855,7 +855,7 @@ mod tests {
p!("`hi\nyou" => [raw!["hi", "you"]], [(1:3, 1:3, "expected backtick")]);
p!("`hi\\`du`" => [raw!["hi`du"]]);
- // Spanned nodes.
+ // Spanned SyntaxNodes.
p!("Hi" => [(0:0, 0:2, T("Hi"))]);
p!("*Hi*" => [(0:0, 0:1, Bold), (0:1, 0:3, T("Hi")), (0:3, 0:4, Bold)]);
p!("🌎\n*/[n]" =>
diff --git a/src/syntax/scope.rs b/src/syntax/scope.rs
index 83b9fdee..d3092944 100644
--- a/src/syntax/scope.rs
+++ b/src/syntax/scope.rs
@@ -5,7 +5,7 @@ use std::fmt::{self, Debug, Formatter};
use crate::func::ParseFunc;
use super::parsing::CallParser;
-use super::model::Model;
+use super::tree::DynamicNode;
/// A map from identifiers to function parsers.
pub struct Scope {
@@ -17,7 +17,7 @@ impl Scope {
/// Create a new empty scope with a fallback parser that is invoked when no
/// match is found.
pub fn new<F>() -> Scope
- where F: ParseFunc<Meta=()> + Model + 'static {
+ where F: ParseFunc<Meta=()> + DynamicNode + 'static {
Scope {
parsers: HashMap::new(),
fallback: make_parser::<F>(()),
@@ -31,14 +31,14 @@ impl Scope {
/// Associate the given name with a type that is parseable into a function.
pub fn add<F>(&mut self, name: &str)
- where F: ParseFunc<Meta=()> + Model + 'static {
+ where F: ParseFunc<Meta=()> + DynamicNode + 'static {
self.add_with_meta::<F>(name, ());
}
/// Add a parseable type with additional metadata that is given to the
/// parser (other than the default of `()`).
pub fn add_with_meta<F>(&mut self, name: &str, metadata: <F as ParseFunc>::Meta)
- where F: ParseFunc + Model + 'static {
+ where F: ParseFunc + DynamicNode + 'static {
self.parsers.insert(
name.to_string(),
make_parser::<F>(metadata),
@@ -65,9 +65,9 @@ impl Debug for Scope {
}
fn make_parser<F>(metadata: <F as ParseFunc>::Meta) -> Box<CallParser>
-where F: ParseFunc + Model + 'static {
+where F: ParseFunc + DynamicNode + 'static {
Box::new(move |f, s| {
F::parse(f, s, metadata.clone())
- .map(|model| Box::new(model) as Box<dyn Model>)
+ .map(|tree| Box::new(tree) as Box<dyn DynamicNode>)
})
}
diff --git a/src/syntax/test.rs b/src/syntax/test.rs
index 38a124aa..b701e577 100644
--- a/src/syntax/test.rs
+++ b/src/syntax/test.rs
@@ -5,7 +5,7 @@ use super::expr::{Expr, Ident, Tuple, NamedTuple, Object, Pair};
use super::parsing::{FuncHeader, FuncArgs, FuncArg};
use super::span::Spanned;
use super::tokens::Token;
-use super::model::{SyntaxModel, Model, Node};
+use super::tree::{SyntaxTree, SyntaxNode, DynamicNode};
/// Check whether the expected and found results are the same.
pub fn check<T>(src: &str, exp: T, found: T, cmp_spans: bool)
@@ -62,7 +62,7 @@ function! {
#[derive(Debug, Clone, PartialEq)]
pub struct DebugFn {
pub header: FuncHeader,
- pub body: Option<SyntaxModel>,
+ pub body: Option<SyntaxTree>,
}
parse(header, body, state, f) {
@@ -83,20 +83,14 @@ pub trait SpanlessEq<Rhs=Self> {
fn spanless_eq(&self, other: &Rhs) -> bool;
}
-impl SpanlessEq for SyntaxModel {
- fn spanless_eq(&self, other: &SyntaxModel) -> bool {
- self.nodes.spanless_eq(&other.nodes)
- }
-}
-
-impl SpanlessEq for Node {
- fn spanless_eq(&self, other: &Node) -> bool {
- fn downcast<'a>(func: &'a (dyn Model + 'static)) -> &'a DebugFn {
+impl SpanlessEq for SyntaxNode {
+ fn spanless_eq(&self, other: &SyntaxNode) -> bool {
+ fn downcast<'a>(func: &'a (dyn DynamicNode + 'static)) -> &'a DebugFn {
func.downcast::<DebugFn>().expect("not a debug fn")
}
match (self, other) {
- (Node::Model(a), Node::Model(b)) => {
+ (SyntaxNode::Dyn(a), SyntaxNode::Dyn(b)) => {
downcast(a.as_ref()).spanless_eq(downcast(b.as_ref()))
}
(a, b) => a == b,
diff --git a/src/syntax/tokens.rs b/src/syntax/tokens.rs
index a0af5a02..1ea11449 100644
--- a/src/syntax/tokens.rs
+++ b/src/syntax/tokens.rs
@@ -83,8 +83,8 @@ pub enum Token<'s> {
ExprHex(&'s str),
/// A plus in a function header, signifying the addition of expressions.
Plus,
- /// A hyphen in a function header,
- /// signifying the subtraction of expressions.
+ /// A hyphen in a function header, signifying the subtraction of
+ /// expressions.
Hyphen,
/// A slash in a function header, signifying the division of expressions.
Slash,
diff --git a/src/syntax/tree.rs b/src/syntax/tree.rs
new file mode 100644
index 00000000..41a03fae
--- /dev/null
+++ b/src/syntax/tree.rs
@@ -0,0 +1,100 @@
+//! The syntax tree.
+
+use std::any::Any;
+use std::fmt::Debug;
+
+use crate::layout::Layout;
+use super::span::SpanVec;
+
+/// A list of nodes which forms 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)]
+pub enum SyntaxNode {
+ /// Whitespace containing less than two newlines.
+ Space,
+ /// Whitespace with more than two newlines.
+ Parbreak,
+ /// A forced line break.
+ Linebreak,
+ /// Plain text.
+ Text(String),
+ /// Lines of raw text.
+ Raw(Vec<String>),
+ /// Italics were enabled / disabled.
+ ToggleItalic,
+ /// Bolder was enabled / disabled.
+ ToggleBolder,
+ /// A subtree, typically a function invocation.
+ Dyn(Box<dyn DynamicNode>),
+}
+
+impl PartialEq for SyntaxNode {
+ fn eq(&self, other: &SyntaxNode) -> bool {
+ use SyntaxNode::*;
+ match (self, other) {
+ (Space, Space) => true,
+ (Parbreak, Parbreak) => true,
+ (Linebreak, Linebreak) => true,
+ (Text(a), Text(b)) => a == b,
+ (Raw(a), Raw(b)) => a == b,
+ (ToggleItalic, ToggleItalic) => true,
+ (ToggleBolder, ToggleBolder) => true,
+ (Dyn(a), Dyn(b)) => a == b,
+ _ => false,
+ }
+ }
+}
+
+/// 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<N>(&self) -> Option<&N> where N: DynamicNode + 'static {
+ self.as_any().downcast_ref::<N>()
+ }
+}
+
+impl PartialEq for dyn DynamicNode {
+ fn eq(&self, other: &dyn DynamicNode) -> bool {
+ self.dyn_eq(other)
+ }
+}
+
+impl Clone for Box<dyn DynamicNode> {
+ fn clone(&self) -> Self {
+ self.box_clone()
+ }
+}
+
+impl<T> DynamicNode for T where T: Debug + PartialEq + Clone + Layout + 'static {
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
+
+ fn dyn_eq(&self, other: &dyn DynamicNode) -> bool {
+ match other.as_any().downcast_ref::<Self>() {
+ Some(other) => self == other,
+ None => false,
+ }
+ }
+
+ fn box_clone(&self) -> Box<dyn DynamicNode> {
+ Box::new(self.clone())
+ }
+}