summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/eco.rs45
-rw-r--r--src/eval/array.rs148
-rw-r--r--src/eval/dict.rs129
-rw-r--r--src/eval/function.rs176
-rw-r--r--src/eval/mod.rs33
-rw-r--r--src/eval/ops.rs37
-rw-r--r--src/eval/scope.rs4
-rw-r--r--src/eval/template.rs139
-rw-r--r--src/eval/value.rs310
-rw-r--r--src/exec/context.rs10
-rw-r--r--src/exec/mod.rs27
-rw-r--r--src/layout/background.rs4
-rw-r--r--src/layout/fixed.rs4
-rw-r--r--src/layout/grid.rs8
-rw-r--r--src/layout/image.rs2
-rw-r--r--src/layout/mod.rs30
-rw-r--r--src/layout/pad.rs4
-rw-r--r--src/layout/par.rs6
-rw-r--r--src/layout/stack.rs4
-rw-r--r--src/library/elements.rs4
-rw-r--r--src/library/layout.rs4
-rw-r--r--src/library/mod.rs2
-rw-r--r--src/library/text.rs8
-rw-r--r--src/parse/mod.rs52
-rw-r--r--src/pretty.rs40
-rw-r--r--src/syntax/mod.rs2
-rw-r--r--src/syntax/node.rs2
-rw-r--r--src/syntax/span.rs50
-rw-r--r--src/syntax/visit.rs24
29 files changed, 831 insertions, 477 deletions
diff --git a/src/eco.rs b/src/eco.rs
index 2d2ab2dc..a52a163c 100644
--- a/src/eco.rs
+++ b/src/eco.rs
@@ -7,7 +7,7 @@ use std::hash::{Hash, Hasher};
use std::ops::{Add, AddAssign, Deref};
use std::rc::Rc;
-/// A economical string with inline storage and clone-on-write value semantics.
+/// An economical string with inline storage and clone-on-write value semantics.
#[derive(Clone)]
pub struct EcoString(Repr);
@@ -22,8 +22,9 @@ enum Repr {
/// The maximum number of bytes that can be stored inline.
///
-/// The value is chosen such that `Repr` fits exactly into 16 bytes
-/// (which are needed anyway due to `Rc`s alignment).
+/// The value is chosen such that an `EcoString` fits exactly into 16 bytes
+/// (which are needed anyway due to the `Rc`s alignment, at least on 64-bit
+/// platforms).
///
/// Must be at least 4 to hold any char.
const LIMIT: usize = 14;
@@ -77,7 +78,7 @@ impl EcoString {
self
}
- /// Appends the given character at the end.
+ /// Append the given character at the end.
pub fn push(&mut self, c: char) {
match &mut self.0 {
Repr::Small { buf, len } => {
@@ -93,7 +94,7 @@ impl EcoString {
}
}
- /// Appends the given string slice at the end.
+ /// Append the given string slice at the end.
pub fn push_str(&mut self, string: &str) {
match &mut self.0 {
Repr::Small { buf, len } => {
@@ -113,7 +114,7 @@ impl EcoString {
}
}
- /// Removes the last character from the string.
+ /// Remove the last character from the string.
pub fn pop(&mut self) -> Option<char> {
let c = self.as_str().chars().rev().next()?;
match &mut self.0 {
@@ -127,7 +128,21 @@ impl EcoString {
Some(c)
}
- /// Repeats this string `n` times.
+ /// Clear the string.
+ pub fn clear(&mut self) {
+ match &mut self.0 {
+ Repr::Small { len, .. } => *len = 0,
+ Repr::Large(rc) => {
+ if Rc::strong_count(rc) == 1 {
+ Rc::make_mut(rc).clear();
+ } else {
+ *self = Self::new();
+ }
+ }
+ }
+ }
+
+ /// Repeat this string `n` times.
pub fn repeat(&self, n: usize) -> Self {
if let Repr::Small { buf, len } = &self.0 {
let prev = usize::from(*len);
@@ -209,15 +224,15 @@ impl Default for EcoString {
}
}
-impl Debug for EcoString {
+impl Display for EcoString {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- Debug::fmt(self.as_str(), f)
+ Display::fmt(self.as_str(), f)
}
}
-impl Display for EcoString {
+impl Debug for EcoString {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- Display::fmt(self.as_str(), f)
+ Debug::fmt(self.as_str(), f)
}
}
@@ -259,6 +274,14 @@ impl PartialOrd for EcoString {
}
}
+impl Add<&Self> for EcoString {
+ type Output = Self;
+
+ fn add(self, rhs: &Self) -> Self::Output {
+ self + rhs.as_str()
+ }
+}
+
impl Add<&str> for EcoString {
type Output = Self;
diff --git a/src/eval/array.rs b/src/eval/array.rs
new file mode 100644
index 00000000..f62bda9f
--- /dev/null
+++ b/src/eval/array.rs
@@ -0,0 +1,148 @@
+use std::fmt::{self, Debug, Formatter};
+use std::iter::FromIterator;
+use std::ops::{Add, AddAssign};
+use std::rc::Rc;
+
+use super::Value;
+
+/// Create a new [`Array`] from values.
+#[macro_export]
+macro_rules! array {
+ ($value:expr; $count:expr) => {
+ $crate::eval::Array::from_vec(vec![$crate::eval::Value::from($value); $count])
+ };
+
+ ($($value:expr),* $(,)?) => {
+ $crate::eval::Array::from_vec(vec![$($crate::eval::Value::from($value)),*])
+ };
+}
+
+/// A variably-typed array with clone-on-write value semantics.
+#[derive(Clone, PartialEq)]
+pub struct Array {
+ vec: Rc<Vec<Value>>,
+}
+
+impl Array {
+ /// Create a new, empty array.
+ pub fn new() -> Self {
+ Self { vec: Rc::new(vec![]) }
+ }
+
+ /// Create a new array from a vector of values.
+ pub fn from_vec(vec: Vec<Value>) -> Self {
+ Self { vec: Rc::new(vec) }
+ }
+
+ /// Create a new, empty array with the given `capacity`.
+ pub fn with_capacity(capacity: usize) -> Self {
+ Self {
+ vec: Rc::new(Vec::with_capacity(capacity)),
+ }
+ }
+
+ /// Whether the array is empty.
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ /// The length of the array.
+ pub fn len(&self) -> usize {
+ self.vec.len()
+ }
+
+ /// Borrow the value at the given index.
+ pub fn get(&self, index: usize) -> Option<&Value> {
+ self.vec.get(index)
+ }
+
+ /// Mutably borrow the value at the given index.
+ pub fn get_mut(&mut self, index: usize) -> Option<&mut Value> {
+ Rc::make_mut(&mut self.vec).get_mut(index)
+ }
+
+ /// Set the value at the given index.
+ ///
+ /// This panics the `index` is out of range.
+ pub fn set(&mut self, index: usize, value: Value) {
+ Rc::make_mut(&mut self.vec)[index] = value;
+ }
+
+ /// Push a value to the end of the array.
+ pub fn push(&mut self, value: Value) {
+ Rc::make_mut(&mut self.vec).push(value);
+ }
+
+ /// Extend the array with the values from another array.
+ pub fn extend(&mut self, other: &Array) {
+ Rc::make_mut(&mut self.vec).extend(other.into_iter())
+ }
+
+ /// Clear the array.
+ pub fn clear(&mut self) {
+ if Rc::strong_count(&mut self.vec) == 1 {
+ Rc::make_mut(&mut self.vec).clear();
+ } else {
+ *self = Self::new();
+ }
+ }
+
+ /// Repeat this array `n` times.
+ pub fn repeat(&self, n: usize) -> Self {
+ let len = self.len().checked_mul(n).expect("capacity overflow");
+ self.into_iter().cycle().take(len).collect()
+ }
+
+ /// Iterate over references to the contained values.
+ pub fn iter(&self) -> std::slice::Iter<Value> {
+ self.vec.iter()
+ }
+
+ /// Iterate over the contained values.
+ pub fn into_iter(&self) -> impl Iterator<Item = Value> + Clone + '_ {
+ // TODO: Actually consume the vector if the ref-count is 1?
+ self.iter().cloned()
+ }
+}
+
+impl Default for Array {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl Debug for Array {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.debug_list().entries(self.vec.iter()).finish()
+ }
+}
+
+impl FromIterator<Value> for Array {
+ fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
+ Array { vec: Rc::new(iter.into_iter().collect()) }
+ }
+}
+
+impl<'a> IntoIterator for &'a Array {
+ type Item = &'a Value;
+ type IntoIter = std::slice::Iter<'a, Value>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+}
+
+impl Add<&Array> for Array {
+ type Output = Self;
+
+ fn add(mut self, rhs: &Array) -> Self::Output {
+ self.extend(rhs);
+ self
+ }
+}
+
+impl AddAssign<&Array> for Array {
+ fn add_assign(&mut self, rhs: &Array) {
+ self.extend(rhs);
+ }
+}
diff --git a/src/eval/dict.rs b/src/eval/dict.rs
new file mode 100644
index 00000000..bf99ea17
--- /dev/null
+++ b/src/eval/dict.rs
@@ -0,0 +1,129 @@
+use std::collections::BTreeMap;
+use std::fmt::{self, Debug, Formatter};
+use std::iter::FromIterator;
+use std::ops::{Add, AddAssign};
+use std::rc::Rc;
+
+use super::Value;
+use crate::eco::EcoString;
+
+/// Create a new [`Dict`] from key-value pairs.
+#[macro_export]
+macro_rules! dict {
+ ($($key:expr => $value:expr),* $(,)?) => {{
+ #[allow(unused_mut)]
+ let mut map = std::collections::BTreeMap::new();
+ $(map.insert($crate::eco::EcoString::from($key), $crate::eval::Value::from($value));)*
+ $crate::eval::Dict::from_map(map)
+ }};
+}
+
+/// A variably-typed dictionary with clone-on-write value semantics.
+#[derive(Clone, PartialEq)]
+pub struct Dict {
+ map: Rc<BTreeMap<EcoString, Value>>,
+}
+
+impl Dict {
+ /// Create a new, empty dictionary.
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Create a new dictionary from a mapping of strings to values.
+ pub fn from_map(map: BTreeMap<EcoString, Value>) -> Self {
+ Self { map: Rc::new(map) }
+ }
+
+ /// Whether the dictionary is empty.
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ /// The number of pairs in the dictionary.
+ pub fn len(&self) -> usize {
+ self.map.len()
+ }
+
+ /// Borrow the value the given `key` maps to.
+ pub fn get(&self, key: &str) -> Option<&Value> {
+ self.map.get(key)
+ }
+
+ /// Mutably borrow the value the given `key` maps to.
+ pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
+ Rc::make_mut(&mut self.map).get_mut(key)
+ }
+
+ /// Insert a mapping from the given `key` to the given `value`.
+ pub fn insert(&mut self, key: EcoString, value: Value) {
+ Rc::make_mut(&mut self.map).insert(key, value);
+ }
+
+ /// Extend the dictionary with the values from another dictionary.
+ pub fn extend(&mut self, other: &Dict) {
+ Rc::make_mut(&mut self.map).extend(other.into_iter())
+ }
+
+ /// Clear the dictionary.
+ pub fn clear(&mut self) {
+ if Rc::strong_count(&mut self.map) == 1 {
+ Rc::make_mut(&mut self.map).clear();
+ } else {
+ *self = Self::new();
+ }
+ }
+
+ /// Iterate over pairs of the contained keys and values.
+ pub fn into_iter(&self) -> impl Iterator<Item = (EcoString, Value)> + Clone + '_ {
+ // TODO: Actually consume the map if the ref-count is 1?
+ self.iter().map(|(k, v)| (k.clone(), v.clone()))
+ }
+
+ /// Iterate over pairs of references to the contained keys and values.
+ pub fn iter(&self) -> std::collections::btree_map::Iter<EcoString, Value> {
+ self.map.iter()
+ }
+}
+
+impl Default for Dict {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl Debug for Dict {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.debug_map().entries(self.map.iter()).finish()
+ }
+}
+
+impl FromIterator<(EcoString, Value)> for Dict {
+ fn from_iter<T: IntoIterator<Item = (EcoString, Value)>>(iter: T) -> Self {
+ Dict { map: Rc::new(iter.into_iter().collect()) }
+ }
+}
+
+impl<'a> IntoIterator for &'a Dict {
+ type Item = (&'a EcoString, &'a Value);
+ type IntoIter = std::collections::btree_map::Iter<'a, EcoString, Value>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+}
+
+impl Add<&Dict> for Dict {
+ type Output = Self;
+
+ fn add(mut self, rhs: &Dict) -> Self::Output {
+ self.extend(rhs);
+ self
+ }
+}
+
+impl AddAssign<&Dict> for Dict {
+ fn add_assign(&mut self, rhs: &Dict) {
+ self.extend(rhs);
+ }
+}
diff --git a/src/eval/function.rs b/src/eval/function.rs
new file mode 100644
index 00000000..c986d71a
--- /dev/null
+++ b/src/eval/function.rs
@@ -0,0 +1,176 @@
+use std::fmt::{self, Debug, Formatter};
+use std::ops::Deref;
+use std::rc::Rc;
+
+use super::{Cast, CastResult, EvalContext, Value};
+use crate::eco::EcoString;
+use crate::syntax::{Span, Spanned};
+
+/// An evaluatable function.
+#[derive(Clone)]
+pub struct Function {
+ /// The name of the function.
+ ///
+ /// The string is boxed to make the whole struct fit into 24 bytes, so that
+ /// a value fits into 32 bytes.
+ name: Option<Box<EcoString>>,
+ /// The closure that defines the function.
+ f: Rc<dyn Fn(&mut EvalContext, &mut FuncArgs) -> Value>,
+}
+
+impl Function {
+ /// Create a new function from a rust closure.
+ pub fn new<F>(name: Option<EcoString>, f: F) -> Self
+ where
+ F: Fn(&mut EvalContext, &mut FuncArgs) -> Value + 'static,
+ {
+ Self { name: name.map(Box::new), f: Rc::new(f) }
+ }
+
+ /// The name of the function.
+ pub fn name(&self) -> Option<&EcoString> {
+ self.name.as_ref().map(|s| &**s)
+ }
+}
+
+impl Deref for Function {
+ type Target = dyn Fn(&mut EvalContext, &mut FuncArgs) -> Value;
+
+ fn deref(&self) -> &Self::Target {
+ self.f.as_ref()
+ }
+}
+
+impl Debug for Function {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.debug_struct("ValueFunc").field("name", &self.name).finish()
+ }
+}
+
+impl PartialEq for Function {
+ fn eq(&self, other: &Self) -> bool {
+ // We cast to thin pointers because we don't want to compare vtables.
+ Rc::as_ptr(&self.f) as *const () == Rc::as_ptr(&other.f) as *const ()
+ }
+}
+
+/// Evaluated arguments to a function.
+#[derive(Debug, Clone, PartialEq)]
+pub struct FuncArgs {
+ /// The span of the whole argument list.
+ pub span: Span,
+ /// The positional arguments.
+ pub items: Vec<FuncArg>,
+}
+
+/// An argument to a function call: `12` or `draw: false`.
+#[derive(Debug, Clone, PartialEq)]
+pub struct FuncArg {
+ /// The span of the whole argument.
+ pub span: Span,
+ /// The name of the argument (`None` for positional arguments).
+ pub name: Option<EcoString>,
+ /// The value of the argument.
+ pub value: Spanned<Value>,
+}
+
+impl FuncArgs {
+ /// Find and consume the first castable positional argument.
+ pub fn eat<T>(&mut self, ctx: &mut EvalContext) -> Option<T>
+ where
+ T: Cast<Spanned<Value>>,
+ {
+ (0 .. self.items.len()).find_map(|index| {
+ let slot = &mut self.items[index];
+ if slot.name.is_some() {
+ return None;
+ }
+
+ let value = std::mem::replace(&mut slot.value, Spanned::zero(Value::None));
+ let span = value.span;
+
+ match T::cast(value) {
+ CastResult::Ok(t) => {
+ self.items.remove(index);
+ Some(t)
+ }
+ CastResult::Warn(t, m) => {
+ self.items.remove(index);
+ ctx.diag(warning!(span, "{}", m));
+ Some(t)
+ }
+ CastResult::Err(value) => {
+ slot.value = value;
+ None
+ }
+ }
+ })
+ }
+
+ /// Find and consume the first castable positional argument, producing a
+ /// `missing argument: {what}` error if no match was found.
+ pub fn expect<T>(&mut self, ctx: &mut EvalContext, what: &str) -> Option<T>
+ where
+ T: Cast<Spanned<Value>>,
+ {
+ let found = self.eat(ctx);
+ if found.is_none() {
+ ctx.diag(error!(self.span, "missing argument: {}", what));
+ }
+ found
+ }
+
+ /// Find, consume and collect all castable positional arguments.
+ ///
+ /// This function returns a vector instead of an iterator because the
+ /// iterator would require unique access to the context, rendering it rather
+ /// unusable. If you need to process arguments one-by-one, you probably want
+ /// to use a while-let loop together with [`eat()`](Self::eat).
+ pub fn all<T>(&mut self, ctx: &mut EvalContext) -> Vec<T>
+ where
+ T: Cast<Spanned<Value>>,
+ {
+ std::iter::from_fn(|| self.eat(ctx)).collect()
+ }
+
+ /// Cast and remove the value for the given named argument, producing an
+ /// error if the conversion fails.
+ pub fn named<T>(&mut self, ctx: &mut EvalContext, name: &str) -> Option<T>
+ where
+ T: Cast<Spanned<Value>>,
+ {
+ let index = self
+ .items
+ .iter()
+ .position(|arg| arg.name.as_ref().map_or(false, |other| other == name))?;
+
+ let value = self.items.remove(index).value;
+ let span = value.span;
+
+ match T::cast(value) {
+ CastResult::Ok(t) => Some(t),
+ CastResult::Warn(t, m) => {
+ ctx.diag(warning!(span, "{}", m));
+ Some(t)
+ }
+ CastResult::Err(value) => {
+ ctx.diag(error!(
+ span,
+ "expected {}, found {}",
+ T::TYPE_NAME,
+ value.v.type_name(),
+ ));
+ None
+ }
+ }
+ }
+
+ /// Produce "unexpected argument" errors for all remaining arguments.
+ pub fn finish(self, ctx: &mut EvalContext) {
+ for arg in &self.items {
+ if arg.value.v != Value::Error {
+ ctx.diag(error!(arg.span, "unexpected argument"));
+ }
+ }
+ }
+}
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index dc4ab7ee..689234bd 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -1,13 +1,23 @@
//! Evaluation of syntax trees.
#[macro_use]
+mod array;
+#[macro_use]
+mod dict;
+#[macro_use]
mod value;
mod capture;
+mod function;
mod ops;
mod scope;
+mod template;
+pub use array::*;
pub use capture::*;
+pub use dict::*;
+pub use function::*;
pub use scope::*;
+pub use template::*;
pub use value::*;
use std::collections::HashMap;
@@ -45,7 +55,7 @@ pub struct Module {
/// The top-level definitions that were bound in this module.
pub scope: Scope,
/// The template defined by this module.
- pub template: TemplateValue,
+ pub template: Template,
}
/// The context for evaluation.
@@ -213,7 +223,7 @@ pub trait Eval {
}
impl Eval for Rc<SyntaxTree> {
- type Output = TemplateValue;
+ type Output = Template;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
struct ExprVisitor<'a, 'b> {
@@ -230,10 +240,7 @@ impl Eval for Rc<SyntaxTree> {
let mut visitor = ExprVisitor { ctx, map: ExprMap::new() };
visitor.visit_tree(self);
- Rc::new(vec![TemplateNode::Tree {
- tree: Rc::clone(self),
- map: visitor.map,
- }])
+ TemplateTree { tree: Rc::clone(self), map: visitor.map }.into()
}
}
@@ -280,7 +287,7 @@ impl Eval for Expr {
}
impl Eval for ArrayExpr {
- type Output = ArrayValue;
+ type Output = Array;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
self.items.iter().map(|expr| expr.eval(ctx)).collect()
@@ -288,7 +295,7 @@ impl Eval for ArrayExpr {
}
impl Eval for DictExpr {
- type Output = DictValue;
+ type Output = Dict;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
self.items
@@ -299,7 +306,7 @@ impl Eval for DictExpr {
}
impl Eval for TemplateExpr {
- type Output = TemplateValue;
+ type Output = Template;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
self.tree.eval(ctx)
@@ -476,7 +483,7 @@ impl Eval for CallExpr {
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
let callee = self.callee.eval(ctx);
- if let Some(func) = ctx.cast::<FuncValue>(callee, self.callee.span()) {
+ if let Some(func) = ctx.cast::<Function>(callee, self.callee.span()) {
let mut args = self.args.eval(ctx);
let returned = func(ctx, &mut args);
args.finish(ctx);
@@ -530,7 +537,7 @@ impl Eval for ClosureExpr {
};
let name = self.name.as_ref().map(|name| name.string.clone());
- Value::Func(FuncValue::new(name, move |ctx, args| {
+ Value::Func(Function::new(name, move |ctx, args| {
// Don't leak the scopes from the call site. Instead, we use the
// scope of captured variables we collected earlier.
let prev = mem::take(&mut ctx.scopes);
@@ -554,10 +561,10 @@ impl Eval for WithExpr {
fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
let callee = self.callee.eval(ctx);
- if let Some(func) = ctx.cast::<FuncValue>(callee, self.callee.span()) {
+ if let Some(func) = ctx.cast::<Function>(callee, self.callee.span()) {
let applied = self.args.eval(ctx);
let name = func.name().cloned();
- Value::Func(FuncValue::new(name, move |ctx, args| {
+ Value::Func(Function::new(name, move |ctx, args| {
// Remove named arguments that were overridden.
let kept: Vec<_> = applied
.items
diff --git a/src/eval/ops.rs b/src/eval/ops.rs
index 3b48140c..c1dd726b 100644
--- a/src/eval/ops.rs
+++ b/src/eval/ops.rs
@@ -1,7 +1,6 @@
use std::cmp::Ordering::*;
-use std::rc::Rc;
-use super::{TemplateNode, Value};
+use super::Value;
use Value::*;
/// Apply the plus operator to a value.
@@ -90,11 +89,6 @@ pub fn sub(lhs: Value, rhs: Value) -> Value {
/// Compute the product of two values.
pub fn mul(lhs: Value, rhs: Value) -> Value {
- fn repeat<T: Clone>(vec: Vec<T>, n: usize) -> Vec<T> {
- let len = n * vec.len();
- vec.into_iter().cycle().take(len).collect()
- }
-
match (lhs, rhs) {
(Int(a), Int(b)) => Int(a * b),
(Int(a), Float(b)) => Float(a as f64 * b),
@@ -128,8 +122,8 @@ pub fn mul(lhs: Value, rhs: Value) -> Value {
(Str(a), Int(b)) => Str(a.repeat(b.max(0) as usize)),
(Int(a), Str(b)) => Str(b.repeat(a.max(0) as usize)),
- (Array(a), Int(b)) => Array(repeat(a, b.max(0) as usize)),
- (Int(a), Array(b)) => Array(repeat(b, a.max(0) as usize)),
+ (Array(a), Int(b)) => Array(a.repeat(b.max(0) as usize)),
+ (Int(a), Array(b)) => Array(b.repeat(a.max(0) as usize)),
_ => Error,
}
@@ -240,26 +234,11 @@ pub fn join(lhs: Value, rhs: Value) -> Result<Value, Value> {
fn concat(lhs: Value, rhs: Value) -> Result<Value, Value> {
Ok(match (lhs, rhs) {
(Str(a), Str(b)) => Str(a + &b),
- (Array(mut a), Array(b)) => Array({
- a.extend(b);
- a
- }),
- (Dict(mut a), Dict(b)) => Dict({
- a.extend(b);
- a
- }),
- (Template(mut a), Template(b)) => Template({
- Rc::make_mut(&mut a).extend(b.iter().cloned());
- a
- }),
- (Template(mut a), Str(b)) => Template({
- Rc::make_mut(&mut a).push(TemplateNode::Str(b));
- a
- }),
- (Str(a), Template(mut b)) => Template({
- Rc::make_mut(&mut b).insert(0, TemplateNode::Str(a));
- b
- }),
+ (Array(a), Array(b)) => Array(a + &b),
+ (Dict(a), Dict(b)) => Dict(a + &b),
+ (Template(a), Template(b)) => Template(a + &b),
+ (Template(a), Str(b)) => Template(a + b),
+ (Str(a), Template(b)) => Template(a + b),
(a, _) => return Err(a),
})
}
diff --git a/src/eval/scope.rs b/src/eval/scope.rs
index 05bbeda2..4a9d5970 100644
--- a/src/eval/scope.rs
+++ b/src/eval/scope.rs
@@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Display, Formatter};
use std::iter;
use std::rc::Rc;
-use super::{AnyValue, EcoString, EvalContext, FuncArgs, FuncValue, Type, Value};
+use super::{AnyValue, EcoString, EvalContext, FuncArgs, Function, Type, Value};
/// A slot where a variable is stored.
pub type Slot = Rc<RefCell<Value>>;
@@ -92,7 +92,7 @@ impl Scope {
F: Fn(&mut EvalContext, &mut FuncArgs) -> Value + 'static,
{
let name = name.into();
- self.def_const(name.clone(), FuncValue::new(Some(name), f));
+ self.def_const(name.clone(), Function::new(Some(name), f));
}
/// Define a constant variable with a value of variant `Value::Any`.
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()
+ }
+}
diff --git a/src/eval/value.rs b/src/eval/value.rs
index 2881399b..fe9494b1 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -1,15 +1,13 @@
use std::any::Any;
use std::cmp::Ordering;
-use std::collections::{BTreeMap, HashMap};
use std::fmt::{self, Debug, Display, Formatter};
-use std::ops::Deref;
-use std::rc::Rc;
-use super::*;
+use super::{ops, Array, Dict, EvalContext, Function, Template, TemplateFunc};
use crate::color::{Color, RgbaColor};
+use crate::eco::EcoString;
use crate::exec::ExecContext;
use crate::geom::{Angle, Fractional, Length, Linear, Relative};
-use crate::syntax::{Expr, Span, Spanned, SyntaxTree};
+use crate::syntax::{Span, Spanned};
/// A computational value.
#[derive(Debug, Clone, PartialEq)]
@@ -38,14 +36,14 @@ pub enum Value {
Color(Color),
/// A string: `"string"`.
Str(EcoString),
- /// An array value: `(1, "hi", 12cm)`.
- Array(ArrayValue),
+ /// An array of values: `(1, "hi", 12cm)`.
+ Array(Array),
/// A dictionary value: `(color: #f79143, pattern: dashed)`.
- Dict(DictValue),
+ Dict(Dict),
/// A template value: `[*Hi* there]`.
- Template(TemplateValue),
+ Template(Template),
/// An executable function.
- Func(FuncValue),
+ Func(Function),
/// Any object.
Any(AnyValue),
/// The result of invalid operations.
@@ -53,12 +51,12 @@ pub enum Value {
}
impl Value {
- /// Create a new template value consisting of a single dynamic node.
+ /// Create a new template consisting of a single function node.
pub fn template<F>(f: F) -> Self
where
F: Fn(&mut ExecContext) + 'static,
{
- Self::Template(Rc::new(vec![TemplateNode::Func(TemplateFunc::new(f))]))
+ Self::Template(TemplateFunc::new(f).into())
}
/// The name of the stored value's type.
@@ -76,10 +74,10 @@ impl Value {
Self::Fractional(_) => Fractional::TYPE_NAME,
Self::Color(_) => Color::TYPE_NAME,
Self::Str(_) => EcoString::TYPE_NAME,
- Self::Array(_) => ArrayValue::TYPE_NAME,
- Self::Dict(_) => DictValue::TYPE_NAME,
- Self::Template(_) => TemplateValue::TYPE_NAME,
- Self::Func(_) => FuncValue::TYPE_NAME,
+ Self::Array(_) => Array::TYPE_NAME,
+ Self::Dict(_) => Dict::TYPE_NAME,
+ Self::Template(_) => Template::TYPE_NAME,
+ Self::Func(_) => Function::TYPE_NAME,
Self::Any(v) => v.type_name(),
Self::Error => "error",
}
@@ -101,7 +99,6 @@ impl Value {
a.len() == b.len()
&& a.iter().all(|(k, x)| b.get(k).map_or(false, |y| x.eq(y)))
}
- (Self::Template(a), Self::Template(b)) => Rc::ptr_eq(a, b),
(a, b) => a == b,
}
}
@@ -146,245 +143,6 @@ impl Default for Value {
}
}
-/// An array value: `(1, "hi", 12cm)`.
-pub type ArrayValue = Vec<Value>;
-
-/// A dictionary value: `(color: #f79143, pattern: dashed)`.
-pub type DictValue = BTreeMap<EcoString, Value>;
-
-/// A template value: `[*Hi* there]`.
-pub type TemplateValue = Rc<Vec<TemplateNode>>;
-
-/// One chunk 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 consists of a syntax tree plus already evaluated
- /// expression.
- Tree {
- /// The syntax tree of the corresponding template expression.
- tree: Rc<SyntaxTree>,
- /// The evaluated expressions in the syntax tree.
- map: ExprMap,
- },
- /// A template that was converted from a string.
- Str(EcoString),
- /// A function template that can implement custom behaviour.
- Func(TemplateFunc),
-}
-
-impl PartialEq for TemplateNode {
- fn eq(&self, _: &Self) -> bool {
- false
- }
-}
-
-/// 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("TemplateAny").finish()
- }
-}
-
-/// A wrapper around a reference-counted executable function.
-#[derive(Clone)]
-pub struct FuncValue {
- /// The string is boxed to make the whole struct fit into 24 bytes, so that
- /// a [`Value`] fits into 32 bytes.
- name: Option<Box<EcoString>>,
- /// The closure that defines the function.
- f: Rc<dyn Fn(&mut EvalContext, &mut FuncArgs) -> Value>,
-}
-
-impl FuncValue {
- /// Create a new function value from a rust function or closure.
- pub fn new<F>(name: Option<EcoString>, f: F) -> Self
- where
- F: Fn(&mut EvalContext, &mut FuncArgs) -> Value + 'static,
- {
- Self { name: name.map(Box::new), f: Rc::new(f) }
- }
-
- /// The name of the function.
- pub fn name(&self) -> Option<&EcoString> {
- self.name.as_ref().map(|s| &**s)
- }
-}
-
-impl PartialEq for FuncValue {
- fn eq(&self, other: &Self) -> bool {
- // We cast to thin pointers because we don't want to compare vtables.
- Rc::as_ptr(&self.f) as *const () == Rc::as_ptr(&other.f) as *const ()
- }
-}
-
-impl Deref for FuncValue {
- type Target = dyn Fn(&mut EvalContext, &mut FuncArgs) -> Value;
-
- fn deref(&self) -> &Self::Target {
- self.f.as_ref()
- }
-}
-
-impl Debug for FuncValue {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.debug_struct("ValueFunc").field("name", &self.name).finish()
- }
-}
-
-/// Evaluated arguments to a function.
-#[derive(Debug, Clone, PartialEq)]
-pub struct FuncArgs {
- /// The span of the whole argument list.
- pub span: Span,
- /// The positional arguments.
- pub items: Vec<FuncArg>,
-}
-
-impl FuncArgs {
- /// Find and consume the first castable positional argument.
- pub fn eat<T>(&mut self, ctx: &mut EvalContext) -> Option<T>
- where
- T: Cast<Spanned<Value>>,
- {
- (0 .. self.items.len()).find_map(|index| {
- let slot = &mut self.items[index];
- if slot.name.is_some() {
- return None;
- }
-
- let value = std::mem::replace(&mut slot.value, Spanned::zero(Value::None));
- let span = value.span;
-
- match T::cast(value) {
- CastResult::Ok(t) => {
- self.items.remove(index);
- Some(t)
- }
- CastResult::Warn(t, m) => {
- self.items.remove(index);
- ctx.diag(warning!(span, "{}", m));
- Some(t)
- }
- CastResult::Err(value) => {
- slot.value = value;
- None
- }
- }
- })
- }
-
- /// Find and consume the first castable positional argument, producing a
- /// `missing argument: {what}` error if no match was found.
- pub fn expect<T>(&mut self, ctx: &mut EvalContext, what: &str) -> Option<T>
- where
- T: Cast<Spanned<Value>>,
- {
- let found = self.eat(ctx);
- if found.is_none() {
- ctx.diag(error!(self.span, "missing argument: {}", what));
- }
- found
- }
-
- /// Find, consume and collect all castable positional arguments.
- ///
- /// This function returns a vector instead of an iterator because the
- /// iterator would require unique access to the context, rendering it rather
- /// unusable. If you need to process arguments one-by-one, you probably want
- /// to use a while-let loop together with [`eat()`](Self::eat).
- pub fn all<T>(&mut self, ctx: &mut EvalContext) -> Vec<T>
- where
- T: Cast<Spanned<Value>>,
- {
- std::iter::from_fn(|| self.eat(ctx)).collect()
- }
-
- /// Cast and remove the value for the given named argument, producing an
- /// error if the conversion fails.
- pub fn named<T>(&mut self, ctx: &mut EvalContext, name: &str) -> Option<T>
- where
- T: Cast<Spanned<Value>>,
- {
- let index = self
- .items
- .iter()
- .position(|arg| arg.name.as_ref().map_or(false, |other| other == name))?;
-
- let value = self.items.remove(index).value;
- let span = value.span;
-
- match T::cast(value) {
- CastResult::Ok(t) => Some(t),
- CastResult::Warn(t, m) => {
- ctx.diag(warning!(span, "{}", m));
- Some(t)
- }
- CastResult::Err(value) => {
- ctx.diag(error!(
- span,
- "expected {}, found {}",
- T::TYPE_NAME,
- value.v.type_name(),
- ));
- None
- }
- }
- }
-
- /// Produce "unexpected argument" errors for all remaining arguments.
- pub fn finish(self, ctx: &mut EvalContext) {
- for arg in &self.items {
- if arg.value.v != Value::Error {
- ctx.diag(error!(arg.span, "unexpected argument"));
- }
- }
- }
-}
-
-/// An argument to a function call: `12` or `draw: false`.
-#[derive(Debug, Clone, PartialEq)]
-pub struct FuncArg {
- /// The span of the whole argument.
- pub span: Span,
- /// The name of the argument (`None` for positional arguments).
- pub name: Option<EcoString>,
- /// The value of the argument.
- pub value: Spanned<Value>,
-}
-
/// A wrapper around a dynamic value.
pub struct AnyValue(Box<dyn Bounds>);
@@ -422,15 +180,9 @@ impl AnyValue {
}
}
-impl Clone for AnyValue {
- fn clone(&self) -> Self {
- Self(self.0.dyn_clone())
- }
-}
-
-impl PartialEq for AnyValue {
- fn eq(&self, other: &Self) -> bool {
- self.0.dyn_eq(other)
+impl Display for AnyValue {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ Display::fmt(&self.0, f)
}
}
@@ -440,9 +192,15 @@ impl Debug for AnyValue {
}
}
-impl Display for AnyValue {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- Display::fmt(&self.0, f)
+impl Clone for AnyValue {
+ fn clone(&self) -> Self {
+ Self(self.0.dyn_clone())
+ }
+}
+
+impl PartialEq for AnyValue {
+ fn eq(&self, other: &Self) -> bool {
+ self.0.dyn_eq(other)
}
}
@@ -608,14 +366,20 @@ primitive! {
primitive! { Fractional: "fractional", Value::Fractional }
primitive! { Color: "color", Value::Color }
primitive! { EcoString: "string", Value::Str }
-primitive! { ArrayValue: "array", Value::Array }
-primitive! { DictValue: "dictionary", Value::Dict }
+primitive! { Array: "array", Value::Array }
+primitive! { Dict: "dictionary", Value::Dict }
primitive! {
- TemplateValue: "template",
+ Template: "template",
Value::Template,
- Value::Str(v) => Rc::new(vec![TemplateNode::Str(v)]),
+ Value::Str(v) => v.into(),
+}
+primitive! { Function: "function", Value::Func }
+
+impl From<i32> for Value {
+ fn from(v: i32) -> Self {
+ Self::Int(v as i64)
+ }
}
-primitive! { FuncValue: "function", Value::Func }
impl From<usize> for Value {
fn from(v: usize) -> Self {
diff --git a/src/exec/context.rs b/src/exec/context.rs
index 4764a808..04c0169d 100644
--- a/src/exec/context.rs
+++ b/src/exec/context.rs
@@ -4,10 +4,10 @@ use std::rc::Rc;
use super::{Exec, ExecWithMap, FontFamily, State};
use crate::diag::{Diag, DiagSet, Pass};
use crate::eco::EcoString;
-use crate::eval::{ExprMap, TemplateValue};
+use crate::eval::{ExprMap, Template};
use crate::geom::{Align, Dir, Gen, GenAxis, Length, Linear, Sides, Size};
use crate::layout::{
- AnyNode, LayoutTree, PadNode, PageRun, ParChild, ParNode, StackChild, StackNode,
+ LayoutNode, LayoutTree, PadNode, PageRun, ParChild, ParNode, StackChild, StackNode,
};
use crate::syntax::{Span, SyntaxTree};
@@ -53,7 +53,7 @@ impl ExecContext {
}
/// Execute a template and return the result as a stack node.
- pub fn exec_template_stack(&mut self, template: &TemplateValue) -> StackNode {
+ pub fn exec_template_stack(&mut self, template: &Template) -> StackNode {
self.exec_stack(|ctx| template.exec(ctx))
}
@@ -88,13 +88,13 @@ impl ExecContext {
}
/// Push any node into the active paragraph.
- pub fn push_into_par(&mut self, node: impl Into<AnyNode>) {
+ pub fn push_into_par(&mut self, node: impl Into<LayoutNode>) {
let align = self.state.aligns.cross;
self.stack.par.push(ParChild::Any(node.into(), align));
}
/// Push any node into the active stack.
- pub fn push_into_stack(&mut self, node: impl Into<AnyNode>) {
+ pub fn push_into_stack(&mut self, node: impl Into<LayoutNode>) {
self.parbreak();
let aligns = self.state.aligns;
self.stack.push(StackChild::Any(node.into(), aligns));
diff --git a/src/exec/mod.rs b/src/exec/mod.rs
index 2a145fbc..752bdba5 100644
--- a/src/exec/mod.rs
+++ b/src/exec/mod.rs
@@ -10,15 +10,15 @@ use std::fmt::Write;
use std::rc::Rc;
use crate::diag::Pass;
-use crate::eval::{ExprMap, TemplateFunc, TemplateNode, TemplateValue, Value};
+use crate::eco::EcoString;
+use crate::eval::{ExprMap, Template, TemplateFunc, TemplateNode, TemplateTree, Value};
use crate::geom::{Dir, Gen};
use crate::layout::{LayoutTree, StackChild, StackNode};
use crate::pretty::pretty;
-use crate::eco::EcoString;
use crate::syntax::*;
/// Execute a template to produce a layout tree.
-pub fn exec(template: &TemplateValue, state: State) -> Pass<LayoutTree> {
+pub fn exec(template: &Template, state: State) -> Pass<LayoutTree> {
let mut ctx = ExecContext::new(state);
template.exec(&mut ctx);
ctx.finish()
@@ -50,7 +50,7 @@ impl ExecWithMap for SyntaxTree {
}
}
-impl ExecWithMap for Node {
+impl ExecWithMap for SyntaxNode {
fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) {
match self {
Self::Text(text) => ctx.push_text(text),
@@ -117,12 +117,7 @@ impl ExecWithMap for EnumItem {
}
}
-fn exec_item(
- ctx: &mut ExecContext,
- label: EcoString,
- body: &SyntaxTree,
- map: &ExprMap,
-) {
+fn exec_item(ctx: &mut ExecContext, label: EcoString, body: &SyntaxTree, map: &ExprMap) {
let label = ctx.exec_stack(|ctx| ctx.push_text(label));
let body = ctx.exec_tree_stack(body, map);
let stack = StackNode {
@@ -159,7 +154,7 @@ impl Exec for Value {
}
}
-impl Exec for TemplateValue {
+impl Exec for Template {
fn exec(&self, ctx: &mut ExecContext) {
for node in self.iter() {
node.exec(ctx);
@@ -170,13 +165,19 @@ impl Exec for TemplateValue {
impl Exec for TemplateNode {
fn exec(&self, ctx: &mut ExecContext) {
match self {
- Self::Tree { tree, map } => tree.exec_with_map(ctx, &map),
- Self::Str(v) => ctx.push_text(v),
+ Self::Tree(v) => v.exec(ctx),
Self::Func(v) => v.exec(ctx),
+ Self::Str(v) => ctx.push_text(v),
}
}
}
+impl Exec for TemplateTree {
+ fn exec(&self, ctx: &mut ExecContext) {
+ self.tree.exec_with_map(ctx, &self.map)
+ }
+}
+
impl Exec for TemplateFunc {
fn exec(&self, ctx: &mut ExecContext) {
let snapshot = ctx.state.clone();
diff --git a/src/layout/background.rs b/src/layout/background.rs
index 867783bf..76ce431b 100644
--- a/src/layout/background.rs
+++ b/src/layout/background.rs
@@ -9,7 +9,7 @@ pub struct BackgroundNode {
/// Background color / texture.
pub fill: Paint,
/// The child node to be filled.
- pub child: AnyNode,
+ pub child: LayoutNode,
}
/// The kind of shape to use as a background.
@@ -45,7 +45,7 @@ impl Layout for BackgroundNode {
}
}
-impl From<BackgroundNode> for AnyNode {
+impl From<BackgroundNode> for LayoutNode {
fn from(background: BackgroundNode) -> Self {
Self::new(background)
}
diff --git a/src/layout/fixed.rs b/src/layout/fixed.rs
index dfcd4038..9fa2af8a 100644
--- a/src/layout/fixed.rs
+++ b/src/layout/fixed.rs
@@ -9,7 +9,7 @@ pub struct FixedNode {
/// The fixed height, if any.
pub height: Option<Linear>,
/// The child node whose size to fix.
- pub child: AnyNode,
+ pub child: LayoutNode,
}
impl Layout for FixedNode {
@@ -47,7 +47,7 @@ impl Layout for FixedNode {
}
}
-impl From<FixedNode> for AnyNode {
+impl From<FixedNode> for LayoutNode {
fn from(fixed: FixedNode) -> Self {
Self::new(fixed)
}
diff --git a/src/layout/grid.rs b/src/layout/grid.rs
index 06b6596c..bccdf381 100644
--- a/src/layout/grid.rs
+++ b/src/layout/grid.rs
@@ -14,7 +14,7 @@ pub struct GridNode {
/// Defines sizing of gutter rows and columns between content.
pub gutter: Gen<Vec<TrackSizing>>,
/// The nodes to be arranged in a grid.
- pub children: Vec<AnyNode>,
+ pub children: Vec<LayoutNode>,
}
/// Defines how to size a grid cell along an axis.
@@ -45,7 +45,7 @@ impl Layout for GridNode {
}
}
-impl From<GridNode> for AnyNode {
+impl From<GridNode> for LayoutNode {
fn from(grid: GridNode) -> Self {
Self::new(grid)
}
@@ -64,7 +64,7 @@ struct GridLayouter<'a> {
/// The row tracks including gutter tracks.
rows: Vec<TrackSizing>,
/// The children of the grid.
- children: &'a [AnyNode],
+ children: &'a [LayoutNode],
/// The region to layout into.
regions: Regions,
/// Resolved column sizes.
@@ -517,7 +517,7 @@ impl<'a> GridLayouter<'a> {
/// Get the node in the cell in column `x` and row `y`.
///
/// Returns `None` if it's a gutter cell.
- fn cell(&self, x: usize, y: usize) -> Option<&'a AnyNode> {
+ fn cell(&self, x: usize, y: usize) -> Option<&'a LayoutNode> {
assert!(x < self.cols.len());
assert!(y < self.rows.len());
diff --git a/src/layout/image.rs b/src/layout/image.rs
index 9ea9db55..07d7799c 100644
--- a/src/layout/image.rs
+++ b/src/layout/image.rs
@@ -59,7 +59,7 @@ impl Layout for ImageNode {
}
}
-impl From<ImageNode> for AnyNode {
+impl From<ImageNode> for LayoutNode {
fn from(image: ImageNode) -> Self {
Self::new(image)
}
diff --git a/src/layout/mod.rs b/src/layout/mod.rs
index a07ccdc5..523d1a92 100644
--- a/src/layout/mod.rs
+++ b/src/layout/mod.rs
@@ -71,7 +71,7 @@ pub struct PageRun {
pub size: Size,
/// The layout node that produces the actual pages (typically a
/// [`StackNode`]).
- pub child: AnyNode,
+ pub child: LayoutNode,
}
impl PageRun {
@@ -86,14 +86,14 @@ impl PageRun {
}
}
-/// A wrapper around a dynamic layouting node.
-pub struct AnyNode {
+/// A dynamic layouting node.
+pub struct LayoutNode {
node: Box<dyn Bounds>,
#[cfg(feature = "layout-cache")]
hash: u64,
}
-impl AnyNode {
+impl LayoutNode {
/// Create a new instance from any node that satisifies the required bounds.
#[cfg(feature = "layout-cache")]
pub fn new<T>(node: T) -> Self
@@ -120,7 +120,7 @@ impl AnyNode {
}
}
-impl Layout for AnyNode {
+impl Layout for LayoutNode {
fn layout(
&self,
ctx: &mut LayoutContext,
@@ -143,7 +143,13 @@ impl Layout for AnyNode {
}
}
-impl Clone for AnyNode {
+impl Debug for LayoutNode {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ self.node.fmt(f)
+ }
+}
+
+impl Clone for LayoutNode {
fn clone(&self) -> Self {
Self {
node: self.node.dyn_clone(),
@@ -153,27 +159,21 @@ impl Clone for AnyNode {
}
}
-impl Eq for AnyNode {}
+impl Eq for LayoutNode {}
-impl PartialEq for AnyNode {
+impl PartialEq for LayoutNode {
fn eq(&self, other: &Self) -> bool {
self.node.dyn_eq(other.node.as_ref())
}
}
#[cfg(feature = "layout-cache")]
-impl Hash for AnyNode {
+impl Hash for LayoutNode {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.hash);
}
}
-impl Debug for AnyNode {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- self.node.fmt(f)
- }
-}
-
trait Bounds: Layout + Debug + 'static {
fn as_any(&self) -> &dyn Any;
fn dyn_eq(&self, other: &dyn Bounds) -> bool;
diff --git a/src/layout/pad.rs b/src/layout/pad.rs
index 3770c754..619bee41 100644
--- a/src/layout/pad.rs
+++ b/src/layout/pad.rs
@@ -7,7 +7,7 @@ pub struct PadNode {
/// The amount of padding.
pub padding: Sides<Linear>,
/// The child node whose sides to pad.
- pub child: AnyNode,
+ pub child: LayoutNode,
}
impl Layout for PadNode {
@@ -61,7 +61,7 @@ fn solve(padding: Sides<Linear>, size: Size) -> Size {
)
}
-impl From<PadNode> for AnyNode {
+impl From<PadNode> for LayoutNode {
fn from(pad: PadNode) -> Self {
Self::new(pad)
}
diff --git a/src/layout/par.rs b/src/layout/par.rs
index bd744201..72b3edfb 100644
--- a/src/layout/par.rs
+++ b/src/layout/par.rs
@@ -5,8 +5,8 @@ use unicode_bidi::{BidiInfo, Level};
use xi_unicode::LineBreakIterator;
use super::*;
-use crate::exec::FontState;
use crate::eco::EcoString;
+use crate::exec::FontState;
use crate::util::{RangeExt, SliceExt};
type Range = std::ops::Range<usize>;
@@ -32,7 +32,7 @@ pub enum ParChild {
/// A run of text and how to align it in its line.
Text(EcoString, Align, Rc<FontState>),
/// Any child node and how to align it in its line.
- Any(AnyNode, Align),
+ Any(LayoutNode, Align),
}
impl Layout for ParNode {
@@ -89,7 +89,7 @@ impl ParNode {
}
}
-impl From<ParNode> for AnyNode {
+impl From<ParNode> for LayoutNode {
fn from(par: ParNode) -> Self {
Self::new(par)
}
diff --git a/src/layout/stack.rs b/src/layout/stack.rs
index 516a2284..ed053dd7 100644
--- a/src/layout/stack.rs
+++ b/src/layout/stack.rs
@@ -26,7 +26,7 @@ pub enum StackChild {
/// Spacing between other nodes.
Spacing(Length),
/// Any child node and how to align it in the stack.
- Any(AnyNode, Gen<Align>),
+ Any(LayoutNode, Gen<Align>),
}
impl Layout for StackNode {
@@ -39,7 +39,7 @@ impl Layout for StackNode {
}
}
-impl From<StackNode> for AnyNode {
+impl From<StackNode> for LayoutNode {
fn from(stack: StackNode) -> Self {
Self::new(stack)
}
diff --git a/src/library/elements.rs b/src/library/elements.rs
index e669369a..6c6c66db 100644
--- a/src/library/elements.rs
+++ b/src/library/elements.rs
@@ -55,7 +55,7 @@ fn rect_impl(
height: Option<Linear>,
aspect: Option<N64>,
fill: Option<Color>,
- body: TemplateValue,
+ body: Template,
) -> Value {
Value::template(move |ctx| {
let mut stack = ctx.exec_template_stack(&body);
@@ -99,7 +99,7 @@ fn ellipse_impl(
height: Option<Linear>,
aspect: Option<N64>,
fill: Option<Color>,
- body: TemplateValue,
+ body: Template,
) -> Value {
Value::template(move |ctx| {
// This padding ratio ensures that the rectangular padded region fits
diff --git a/src/library/layout.rs b/src/library/layout.rs
index 28ee27e1..d7ada806 100644
--- a/src/library/layout.rs
+++ b/src/library/layout.rs
@@ -20,7 +20,7 @@ pub fn page(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let right = args.named(ctx, "right");
let bottom = args.named(ctx, "bottom");
let flip = args.named(ctx, "flip");
- let body = args.expect::<TemplateValue>(ctx, "body").unwrap_or_default();
+ let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
Value::template(move |ctx| {
let snapshot = ctx.state.clone();
@@ -108,7 +108,7 @@ pub fn align(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let second = args.eat::<AlignValue>(ctx);
let mut horizontal = args.named::<AlignValue>(ctx, "horizontal");
let mut vertical = args.named::<AlignValue>(ctx, "vertical");
- let body = args.expect::<TemplateValue>(ctx, "body").unwrap_or_default();
+ let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
for value in first.into_iter().chain(second) {
match value.axis() {
diff --git a/src/library/mod.rs b/src/library/mod.rs
index 7c3e0a71..28387218 100644
--- a/src/library/mod.rs
+++ b/src/library/mod.rs
@@ -18,7 +18,7 @@ use std::rc::Rc;
use crate::color::{Color, RgbaColor};
use crate::eco::EcoString;
-use crate::eval::{EvalContext, FuncArgs, Scope, TemplateValue, Value};
+use crate::eval::{EvalContext, FuncArgs, Scope, Template, Value};
use crate::exec::{Exec, FontFamily};
use crate::font::{FontStyle, FontWeight, VerticalFontMetric};
use crate::geom::*;
diff --git a/src/library/text.rs b/src/library/text.rs
index a0ffc56c..e6de04a1 100644
--- a/src/library/text.rs
+++ b/src/library/text.rs
@@ -23,7 +23,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let serif = args.named(ctx, "serif");
let sans_serif = args.named(ctx, "sans-serif");
let monospace = args.named(ctx, "monospace");
- let body = args.expect::<TemplateValue>(ctx, "body").unwrap_or_default();
+ let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
Value::template(move |ctx| {
let font = ctx.state.font_mut();
@@ -163,7 +163,7 @@ pub fn par(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let spacing = args.named(ctx, "spacing");
let leading = args.named(ctx, "leading");
let word_spacing = args.named(ctx, "word-spacing");
- let body = args.expect::<TemplateValue>(ctx, "body").unwrap_or_default();
+ let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
Value::template(move |ctx| {
if let Some(spacing) = spacing {
@@ -194,7 +194,7 @@ pub fn lang(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
}
None => None,
};
- let body = args.expect::<TemplateValue>(ctx, "body").unwrap_or_default();
+ let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
Value::template(move |ctx| {
if let Some(dir) = dir.or(iso) {
@@ -239,7 +239,7 @@ fn line_impl(
let thickness = args.eat(ctx).or_else(|| args.named::<Linear>(ctx, "thickness"));
let offset = args.named(ctx, "offset");
let extent = args.named(ctx, "extent").unwrap_or_default();
- let body = args.expect::<TemplateValue>(ctx, "body").unwrap_or_default();
+ let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
// Suppress any existing strikethrough if strength is explicitly zero.
let state = thickness.map_or(true, |s| !s.is_zero()).then(|| {
diff --git a/src/parse/mod.rs b/src/parse/mod.rs
index 4c2fd129..ecce0138 100644
--- a/src/parse/mod.rs
+++ b/src/parse/mod.rs
@@ -55,10 +55,10 @@ where
let mut tree = vec![];
while !p.eof() && f(p) {
if let Some(mut node) = node(p, &mut at_start) {
- at_start &= matches!(node, Node::Space | Node::Parbreak(_));
+ at_start &= matches!(node, SyntaxNode::Space | SyntaxNode::Parbreak(_));
// Look for wide call.
- if let Node::Expr(Expr::Call(call)) = &mut node {
+ if let SyntaxNode::Expr(Expr::Call(call)) = &mut node {
if call.wide {
let start = p.next_start();
let tree = tree_while(p, true, f);
@@ -77,7 +77,7 @@ where
}
/// Parse a syntax node.
-fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
+fn node(p: &mut Parser, at_start: &mut bool) -> Option<SyntaxNode> {
let token = p.peek()?;
let span = p.peek_span();
let node = match token {
@@ -85,30 +85,32 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
Token::Space(newlines) => {
*at_start |= newlines > 0;
if newlines < 2 {
- Node::Space
+ SyntaxNode::Space
} else {
- Node::Parbreak(span)
+ SyntaxNode::Parbreak(span)
}
}
// Text.
- Token::Text(text) => Node::Text(text.into()),
- Token::Tilde => Node::Text("\u{00A0}".into()),
- Token::HyphHyph => Node::Text("\u{2013}".into()),
- Token::HyphHyphHyph => Node::Text("\u{2014}".into()),
- Token::UnicodeEscape(t) => Node::Text(unicode_escape(p, t)),
+ Token::Text(text) => SyntaxNode::Text(text.into()),
+ Token::Tilde => SyntaxNode::Text("\u{00A0}".into()),
+ Token::HyphHyph => SyntaxNode::Text("\u{2013}".into()),
+ Token::HyphHyphHyph => SyntaxNode::Text("\u{2014}".into()),
+ Token::UnicodeEscape(t) => SyntaxNode::Text(unicode_escape(p, t)),
// Markup.
- Token::Backslash => Node::Linebreak(span),
- Token::Star => Node::Strong(span),
- Token::Underscore => Node::Emph(span),
+ Token::Backslash => SyntaxNode::Linebreak(span),
+ Token::Star => SyntaxNode::Strong(span),
+ Token::Underscore => SyntaxNode::Emph(span),
Token::Raw(t) => raw(p, t),
Token::Eq if *at_start => return Some(heading(p)),
Token::Hyph if *at_start => return Some(list_item(p)),
Token::Numbering(number) if *at_start => return Some(enum_item(p, number)),
// Line-based markup that is not currently at the start of the line.
- Token::Eq | Token::Hyph | Token::Numbering(_) => Node::Text(p.peek_src().into()),
+ Token::Eq | Token::Hyph | Token::Numbering(_) => {
+ SyntaxNode::Text(p.peek_src().into())
+ }
// Hashtag + keyword / identifier.
Token::Ident(_)
@@ -128,12 +130,12 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
}
p.end_group();
- return expr.map(Node::Expr);
+ return expr.map(SyntaxNode::Expr);
}
// Block and template.
- Token::LeftBrace => return Some(Node::Expr(block(p, false))),
- Token::LeftBracket => return Some(Node::Expr(template(p))),
+ Token::LeftBrace => return Some(SyntaxNode::Expr(block(p, false))),
+ Token::LeftBracket => return Some(SyntaxNode::Expr(template(p))),
// Comments.
Token::LineComment(_) | Token::BlockComment(_) => {
@@ -170,17 +172,17 @@ fn unicode_escape(p: &mut Parser, token: UnicodeEscapeToken) -> EcoString {
}
/// Handle a raw block.
-fn raw(p: &mut Parser, token: RawToken) -> Node {
+fn raw(p: &mut Parser, token: RawToken) -> SyntaxNode {
let span = p.peek_span();
let raw = resolve::resolve_raw(span, token.text, token.backticks);
if !token.terminated {
p.diag(error!(p.peek_span().end, "expected backtick(s)"));
}
- Node::Raw(raw)
+ SyntaxNode::Raw(raw)
}
/// Parse a heading.
-fn heading(p: &mut Parser) -> Node {
+fn heading(p: &mut Parser) -> SyntaxNode {
let start = p.next_start();
p.assert(Token::Eq);
@@ -197,7 +199,7 @@ fn heading(p: &mut Parser) -> Node {
let body = tree_indented(p);
- Node::Heading(HeadingNode {
+ SyntaxNode::Heading(HeadingNode {
span: p.span(start),
level,
body: Rc::new(body),
@@ -205,19 +207,19 @@ fn heading(p: &mut Parser) -> Node {
}
/// Parse a single list item.
-fn list_item(p: &mut Parser) -> Node {
+fn list_item(p: &mut Parser) -> SyntaxNode {
let start = p.next_start();
p.assert(Token::Hyph);
let body = tree_indented(p);
- Node::List(ListItem { span: p.span(start), body })
+ SyntaxNode::List(ListItem { span: p.span(start), body })
}
/// Parse a single enum item.
-fn enum_item(p: &mut Parser, number: Option<usize>) -> Node {
+fn enum_item(p: &mut Parser, number: Option<usize>) -> SyntaxNode {
let start = p.next_start();
p.assert(Token::Numbering(number));
let body = tree_indented(p);
- Node::Enum(EnumItem { span: p.span(start), number, body })
+ SyntaxNode::Enum(EnumItem { span: p.span(start), number, body })
}
/// Parse an expression.
diff --git a/src/pretty.rs b/src/pretty.rs
index 83c207b5..2a825b14 100644
--- a/src/pretty.rs
+++ b/src/pretty.rs
@@ -86,7 +86,7 @@ impl Pretty for SyntaxTree {
}
}
-impl Pretty for Node {
+impl Pretty for SyntaxNode {
fn pretty(&self, p: &mut Printer) {
match self {
// TODO: Handle escaping.
@@ -493,7 +493,7 @@ impl Pretty for Value {
}
}
-impl Pretty for ArrayValue {
+impl Pretty for Array {
fn pretty(&self, p: &mut Printer) {
p.push('(');
p.join(self, ", ", |item, p| item.pretty(p));
@@ -504,7 +504,7 @@ impl Pretty for ArrayValue {
}
}
-impl Pretty for DictValue {
+impl Pretty for Dict {
fn pretty(&self, p: &mut Printer) {
p.push('(');
if self.is_empty() {
@@ -520,13 +520,13 @@ impl Pretty for DictValue {
}
}
-impl Pretty for TemplateValue {
+impl Pretty for Template {
fn pretty(&self, p: &mut Printer) {
p.push_str("<template>");
}
}
-impl Pretty for FuncValue {
+impl Pretty for Function {
fn pretty(&self, p: &mut Printer) {
p.push_str("<function");
if let Some(name) = self.name() {
@@ -608,20 +608,9 @@ pretty_display! {
#[cfg(test)]
mod tests {
- use std::collections::BTreeMap;
-
use super::*;
use crate::parse::parse;
- macro_rules! map {
- ($($k:ident: $v:expr),* $(,)?) => {{
- #[allow(unused_mut)]
- let mut m = BTreeMap::new();
- $(m.insert(stringify!($k).into(), $v);)*
- m
- }};
- }
-
#[track_caller]
fn roundtrip(src: &str) {
test_parse(src, src);
@@ -757,18 +746,15 @@ mod tests {
test_value("\n", r#""\n""#);
test_value("\\", r#""\\""#);
test_value("\"", r#""\"""#);
- test_value(Value::Array(vec![]), "()");
- test_value(vec![Value::None], "(none,)");
- test_value(vec![Value::Int(1), Value::Int(2)], "(1, 2)");
- test_value(map![], "(:)");
- test_value(map![one: Value::Int(1)], "(one: 1)");
- test_value(
- map![two: Value::Bool(false), one: Value::Int(1)],
- "(one: 1, two: false)",
- );
- test_value(FuncValue::new(None, |_, _| Value::None), "<function>");
+ test_value(array![], "()");
+ test_value(array![Value::None], "(none,)");
+ test_value(array![1, 2], "(1, 2)");
+ test_value(dict![], "(:)");
+ test_value(dict!["one" => 1], "(one: 1)");
+ test_value(dict!["two" => false, "one" => 1], "(one: 1, two: false)");
+ test_value(Function::new(None, |_, _| Value::None), "<function>");
test_value(
- FuncValue::new(Some("nil".into()), |_, _| Value::None),
+ Function::new(Some("nil".into()), |_, _| Value::None),
"<function nil>",
);
test_value(AnyValue::new(1), "1");
diff --git a/src/syntax/mod.rs b/src/syntax/mod.rs
index 1de5c1dd..895a5bc5 100644
--- a/src/syntax/mod.rs
+++ b/src/syntax/mod.rs
@@ -18,4 +18,4 @@ use crate::eco::EcoString;
/// The abstract syntax tree.
///
/// This type can represent a full parsed document.
-pub type SyntaxTree = Vec<Node>;
+pub type SyntaxTree = Vec<SyntaxNode>;
diff --git a/src/syntax/node.rs b/src/syntax/node.rs
index bb9ff098..9294fecd 100644
--- a/src/syntax/node.rs
+++ b/src/syntax/node.rs
@@ -4,7 +4,7 @@ use super::*;
/// A syntax node, encompassing a single logical entity of parsed source code.
#[derive(Debug, Clone, PartialEq)]
-pub enum Node {
+pub enum SyntaxNode {
/// Plain text.
Text(EcoString),
/// Whitespace containing less than two newlines.
diff --git a/src/syntax/span.rs b/src/syntax/span.rs
index f9b1d312..8a630faa 100644
--- a/src/syntax/span.rs
+++ b/src/syntax/span.rs
@@ -120,20 +120,6 @@ impl Span {
}
}
-impl Eq for Span {}
-
-impl PartialEq for Span {
- fn eq(&self, other: &Self) -> bool {
- !Self::cmp() || (self.start == other.start && self.end == other.end)
- }
-}
-
-impl Default for Span {
- fn default() -> Self {
- Span::ZERO
- }
-}
-
impl<T> From<T> for Span
where
T: Into<Pos> + Copy,
@@ -152,12 +138,26 @@ where
}
}
+impl Default for Span {
+ fn default() -> Self {
+ Span::ZERO
+ }
+}
+
impl Debug for Span {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "<{:?}-{:?}>", self.start, self.end)
}
}
+impl Eq for Span {}
+
+impl PartialEq for Span {
+ fn eq(&self, other: &Self) -> bool {
+ !Self::cmp() || (self.start == other.start && self.end == other.end)
+ }
+}
+
/// A byte position in source code.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct Pos(pub u32);
@@ -172,17 +172,6 @@ impl Pos {
}
}
-impl<T> Add<T> for Pos
-where
- T: Into<Pos>,
-{
- type Output = Self;
-
- fn add(self, rhs: T) -> Self {
- Pos(self.0 + rhs.into().0)
- }
-}
-
impl From<u32> for Pos {
fn from(index: u32) -> Self {
Self(index)
@@ -207,6 +196,17 @@ impl Debug for Pos {
}
}
+impl<T> Add<T> for Pos
+where
+ T: Into<Pos>,
+{
+ type Output = Self;
+
+ fn add(self, rhs: T) -> Self {
+ Pos(self.0 + rhs.into().0)
+ }
+}
+
/// A one-indexed line-column position in source code.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct Location {
diff --git a/src/syntax/visit.rs b/src/syntax/visit.rs
index 1184010b..b6ee657a 100644
--- a/src/syntax/visit.rs
+++ b/src/syntax/visit.rs
@@ -85,19 +85,19 @@ impl_visitors! {
}
}
- visit_node(v, node: Node) {
+ visit_node(v, node: SyntaxNode) {
match node {
- Node::Text(_) => {}
- Node::Space => {}
- Node::Linebreak(_) => {}
- Node::Parbreak(_) => {}
- Node::Strong(_) => {}
- Node::Emph(_) => {}
- Node::Raw(_) => {}
- Node::Heading(n) => v.visit_heading(n),
- Node::List(n) => v.visit_list(n),
- Node::Enum(n) => v.visit_enum(n),
- Node::Expr(n) => v.visit_expr(n),
+ SyntaxNode::Text(_) => {}
+ SyntaxNode::Space => {}
+ SyntaxNode::Linebreak(_) => {}
+ SyntaxNode::Parbreak(_) => {}
+ SyntaxNode::Strong(_) => {}
+ SyntaxNode::Emph(_) => {}
+ SyntaxNode::Raw(_) => {}
+ SyntaxNode::Heading(n) => v.visit_heading(n),
+ SyntaxNode::List(n) => v.visit_list(n),
+ SyntaxNode::Enum(n) => v.visit_enum(n),
+ SyntaxNode::Expr(n) => v.visit_expr(n),
}
}