Reference-count complex values

Rename some nodes types
This commit is contained in:
Laurenz 2021-07-10 20:01:18 +02:00
parent 36b3067c19
commit 6a4823461f
29 changed files with 833 additions and 479 deletions

View File

@ -7,7 +7,7 @@ use std::hash::{Hash, Hasher};
use std::ops::{Add, AddAssign, Deref}; use std::ops::{Add, AddAssign, Deref};
use std::rc::Rc; 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)] #[derive(Clone)]
pub struct EcoString(Repr); pub struct EcoString(Repr);
@ -22,8 +22,9 @@ enum Repr {
/// The maximum number of bytes that can be stored inline. /// The maximum number of bytes that can be stored inline.
/// ///
/// The value is chosen such that `Repr` fits exactly into 16 bytes /// The value is chosen such that an `EcoString` fits exactly into 16 bytes
/// (which are needed anyway due to `Rc`s alignment). /// (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. /// Must be at least 4 to hold any char.
const LIMIT: usize = 14; const LIMIT: usize = 14;
@ -77,7 +78,7 @@ impl EcoString {
self self
} }
/// Appends the given character at the end. /// Append the given character at the end.
pub fn push(&mut self, c: char) { pub fn push(&mut self, c: char) {
match &mut self.0 { match &mut self.0 {
Repr::Small { buf, len } => { 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) { pub fn push_str(&mut self, string: &str) {
match &mut self.0 { match &mut self.0 {
Repr::Small { buf, len } => { 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> { pub fn pop(&mut self) -> Option<char> {
let c = self.as_str().chars().rev().next()?; let c = self.as_str().chars().rev().next()?;
match &mut self.0 { match &mut self.0 {
@ -127,7 +128,21 @@ impl EcoString {
Some(c) 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 { pub fn repeat(&self, n: usize) -> Self {
if let Repr::Small { buf, len } = &self.0 { if let Repr::Small { buf, len } = &self.0 {
let prev = usize::from(*len); let prev = usize::from(*len);
@ -209,18 +224,18 @@ impl Default for EcoString {
} }
} }
impl Debug for EcoString {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(self.as_str(), f)
}
}
impl Display for EcoString { impl Display for EcoString {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self.as_str(), f) Display::fmt(self.as_str(), f)
} }
} }
impl Debug for EcoString {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Debug::fmt(self.as_str(), f)
}
}
impl Eq for EcoString {} impl Eq for EcoString {}
impl PartialEq for EcoString { impl PartialEq for EcoString {
@ -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 { impl Add<&str> for EcoString {
type Output = Self; type Output = Self;

148
src/eval/array.rs Normal file
View File

@ -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);
}
}

129
src/eval/dict.rs Normal file
View File

@ -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);
}
}

176
src/eval/function.rs Normal file
View File

@ -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"));
}
}
}
}

View File

@ -1,13 +1,23 @@
//! Evaluation of syntax trees. //! Evaluation of syntax trees.
#[macro_use]
mod array;
#[macro_use]
mod dict;
#[macro_use] #[macro_use]
mod value; mod value;
mod capture; mod capture;
mod function;
mod ops; mod ops;
mod scope; mod scope;
mod template;
pub use array::*;
pub use capture::*; pub use capture::*;
pub use dict::*;
pub use function::*;
pub use scope::*; pub use scope::*;
pub use template::*;
pub use value::*; pub use value::*;
use std::collections::HashMap; use std::collections::HashMap;
@ -45,7 +55,7 @@ pub struct Module {
/// The top-level definitions that were bound in this module. /// The top-level definitions that were bound in this module.
pub scope: Scope, pub scope: Scope,
/// The template defined by this module. /// The template defined by this module.
pub template: TemplateValue, pub template: Template,
} }
/// The context for evaluation. /// The context for evaluation.
@ -213,7 +223,7 @@ pub trait Eval {
} }
impl Eval for Rc<SyntaxTree> { impl Eval for Rc<SyntaxTree> {
type Output = TemplateValue; type Output = Template;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
struct ExprVisitor<'a, 'b> { struct ExprVisitor<'a, 'b> {
@ -230,10 +240,7 @@ impl Eval for Rc<SyntaxTree> {
let mut visitor = ExprVisitor { ctx, map: ExprMap::new() }; let mut visitor = ExprVisitor { ctx, map: ExprMap::new() };
visitor.visit_tree(self); visitor.visit_tree(self);
Rc::new(vec![TemplateNode::Tree { TemplateTree { tree: Rc::clone(self), map: visitor.map }.into()
tree: Rc::clone(self),
map: visitor.map,
}])
} }
} }
@ -280,7 +287,7 @@ impl Eval for Expr {
} }
impl Eval for ArrayExpr { impl Eval for ArrayExpr {
type Output = ArrayValue; type Output = Array;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
self.items.iter().map(|expr| expr.eval(ctx)).collect() self.items.iter().map(|expr| expr.eval(ctx)).collect()
@ -288,7 +295,7 @@ impl Eval for ArrayExpr {
} }
impl Eval for DictExpr { impl Eval for DictExpr {
type Output = DictValue; type Output = Dict;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
self.items self.items
@ -299,7 +306,7 @@ impl Eval for DictExpr {
} }
impl Eval for TemplateExpr { impl Eval for TemplateExpr {
type Output = TemplateValue; type Output = Template;
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
self.tree.eval(ctx) self.tree.eval(ctx)
@ -476,7 +483,7 @@ impl Eval for CallExpr {
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
let callee = self.callee.eval(ctx); 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 mut args = self.args.eval(ctx);
let returned = func(ctx, &mut args); let returned = func(ctx, &mut args);
args.finish(ctx); args.finish(ctx);
@ -530,7 +537,7 @@ impl Eval for ClosureExpr {
}; };
let name = self.name.as_ref().map(|name| name.string.clone()); 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 // Don't leak the scopes from the call site. Instead, we use the
// scope of captured variables we collected earlier. // scope of captured variables we collected earlier.
let prev = mem::take(&mut ctx.scopes); let prev = mem::take(&mut ctx.scopes);
@ -554,10 +561,10 @@ impl Eval for WithExpr {
fn eval(&self, ctx: &mut EvalContext) -> Self::Output { fn eval(&self, ctx: &mut EvalContext) -> Self::Output {
let callee = self.callee.eval(ctx); 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 applied = self.args.eval(ctx);
let name = func.name().cloned(); 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. // Remove named arguments that were overridden.
let kept: Vec<_> = applied let kept: Vec<_> = applied
.items .items

View File

@ -1,7 +1,6 @@
use std::cmp::Ordering::*; use std::cmp::Ordering::*;
use std::rc::Rc;
use super::{TemplateNode, Value}; use super::Value;
use Value::*; use Value::*;
/// Apply the plus operator to a 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. /// Compute the product of two values.
pub fn mul(lhs: Value, rhs: Value) -> Value { 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) { match (lhs, rhs) {
(Int(a), Int(b)) => Int(a * b), (Int(a), Int(b)) => Int(a * b),
(Int(a), Float(b)) => Float(a as f64 * 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)), (Str(a), Int(b)) => Str(a.repeat(b.max(0) as usize)),
(Int(a), Str(b)) => Str(b.repeat(a.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)), (Array(a), Int(b)) => Array(a.repeat(b.max(0) as usize)),
(Int(a), Array(b)) => Array(repeat(b, a.max(0) as usize)), (Int(a), Array(b)) => Array(b.repeat(a.max(0) as usize)),
_ => Error, _ => Error,
} }
@ -240,26 +234,11 @@ pub fn join(lhs: Value, rhs: Value) -> Result<Value, Value> {
fn concat(lhs: Value, rhs: Value) -> Result<Value, Value> { fn concat(lhs: Value, rhs: Value) -> Result<Value, Value> {
Ok(match (lhs, rhs) { Ok(match (lhs, rhs) {
(Str(a), Str(b)) => Str(a + &b), (Str(a), Str(b)) => Str(a + &b),
(Array(mut a), Array(b)) => Array({ (Array(a), Array(b)) => Array(a + &b),
a.extend(b); (Dict(a), Dict(b)) => Dict(a + &b),
a (Template(a), Template(b)) => Template(a + &b),
}), (Template(a), Str(b)) => Template(a + b),
(Dict(mut a), Dict(b)) => Dict({ (Str(a), Template(b)) => Template(a + b),
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
}),
(a, _) => return Err(a), (a, _) => return Err(a),
}) })
} }

View File

@ -4,7 +4,7 @@ use std::fmt::{self, Debug, Display, Formatter};
use std::iter; use std::iter;
use std::rc::Rc; 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. /// A slot where a variable is stored.
pub type Slot = Rc<RefCell<Value>>; pub type Slot = Rc<RefCell<Value>>;
@ -92,7 +92,7 @@ impl Scope {
F: Fn(&mut EvalContext, &mut FuncArgs) -> Value + 'static, F: Fn(&mut EvalContext, &mut FuncArgs) -> Value + 'static,
{ {
let name = name.into(); 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`. /// Define a constant variable with a value of variant `Value::Any`.

139
src/eval/template.rs Normal file
View File

@ -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()
}
}

View File

@ -1,15 +1,13 @@
use std::any::Any; use std::any::Any;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap};
use std::fmt::{self, Debug, Display, Formatter}; 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::color::{Color, RgbaColor};
use crate::eco::EcoString;
use crate::exec::ExecContext; use crate::exec::ExecContext;
use crate::geom::{Angle, Fractional, Length, Linear, Relative}; use crate::geom::{Angle, Fractional, Length, Linear, Relative};
use crate::syntax::{Expr, Span, Spanned, SyntaxTree}; use crate::syntax::{Span, Spanned};
/// A computational value. /// A computational value.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -38,14 +36,14 @@ pub enum Value {
Color(Color), Color(Color),
/// A string: `"string"`. /// A string: `"string"`.
Str(EcoString), Str(EcoString),
/// An array value: `(1, "hi", 12cm)`. /// An array of values: `(1, "hi", 12cm)`.
Array(ArrayValue), Array(Array),
/// A dictionary value: `(color: #f79143, pattern: dashed)`. /// A dictionary value: `(color: #f79143, pattern: dashed)`.
Dict(DictValue), Dict(Dict),
/// A template value: `[*Hi* there]`. /// A template value: `[*Hi* there]`.
Template(TemplateValue), Template(Template),
/// An executable function. /// An executable function.
Func(FuncValue), Func(Function),
/// Any object. /// Any object.
Any(AnyValue), Any(AnyValue),
/// The result of invalid operations. /// The result of invalid operations.
@ -53,12 +51,12 @@ pub enum Value {
} }
impl 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 pub fn template<F>(f: F) -> Self
where where
F: Fn(&mut ExecContext) + 'static, 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. /// The name of the stored value's type.
@ -76,10 +74,10 @@ impl Value {
Self::Fractional(_) => Fractional::TYPE_NAME, Self::Fractional(_) => Fractional::TYPE_NAME,
Self::Color(_) => Color::TYPE_NAME, Self::Color(_) => Color::TYPE_NAME,
Self::Str(_) => EcoString::TYPE_NAME, Self::Str(_) => EcoString::TYPE_NAME,
Self::Array(_) => ArrayValue::TYPE_NAME, Self::Array(_) => Array::TYPE_NAME,
Self::Dict(_) => DictValue::TYPE_NAME, Self::Dict(_) => Dict::TYPE_NAME,
Self::Template(_) => TemplateValue::TYPE_NAME, Self::Template(_) => Template::TYPE_NAME,
Self::Func(_) => FuncValue::TYPE_NAME, Self::Func(_) => Function::TYPE_NAME,
Self::Any(v) => v.type_name(), Self::Any(v) => v.type_name(),
Self::Error => "error", Self::Error => "error",
} }
@ -101,7 +99,6 @@ impl Value {
a.len() == b.len() a.len() == b.len()
&& a.iter().all(|(k, x)| b.get(k).map_or(false, |y| x.eq(y))) && 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, (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. /// A wrapper around a dynamic value.
pub struct AnyValue(Box<dyn Bounds>); pub struct AnyValue(Box<dyn Bounds>);
@ -422,15 +180,9 @@ impl AnyValue {
} }
} }
impl Clone for AnyValue { impl Display for AnyValue {
fn clone(&self) -> Self { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Self(self.0.dyn_clone()) Display::fmt(&self.0, f)
}
}
impl PartialEq for AnyValue {
fn eq(&self, other: &Self) -> bool {
self.0.dyn_eq(other)
} }
} }
@ -440,9 +192,15 @@ impl Debug for AnyValue {
} }
} }
impl Display for AnyValue { impl Clone for AnyValue {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn clone(&self) -> Self {
Display::fmt(&self.0, f) 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! { Fractional: "fractional", Value::Fractional }
primitive! { Color: "color", Value::Color } primitive! { Color: "color", Value::Color }
primitive! { EcoString: "string", Value::Str } primitive! { EcoString: "string", Value::Str }
primitive! { ArrayValue: "array", Value::Array } primitive! { Array: "array", Value::Array }
primitive! { DictValue: "dictionary", Value::Dict } primitive! { Dict: "dictionary", Value::Dict }
primitive! { primitive! {
TemplateValue: "template", Template: "template",
Value::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 { impl From<usize> for Value {
fn from(v: usize) -> Self { fn from(v: usize) -> Self {

View File

@ -4,10 +4,10 @@ use std::rc::Rc;
use super::{Exec, ExecWithMap, FontFamily, State}; use super::{Exec, ExecWithMap, FontFamily, State};
use crate::diag::{Diag, DiagSet, Pass}; use crate::diag::{Diag, DiagSet, Pass};
use crate::eco::EcoString; 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::geom::{Align, Dir, Gen, GenAxis, Length, Linear, Sides, Size};
use crate::layout::{ use crate::layout::{
AnyNode, LayoutTree, PadNode, PageRun, ParChild, ParNode, StackChild, StackNode, LayoutNode, LayoutTree, PadNode, PageRun, ParChild, ParNode, StackChild, StackNode,
}; };
use crate::syntax::{Span, SyntaxTree}; use crate::syntax::{Span, SyntaxTree};
@ -53,7 +53,7 @@ impl ExecContext {
} }
/// Execute a template and return the result as a stack node. /// 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)) self.exec_stack(|ctx| template.exec(ctx))
} }
@ -88,13 +88,13 @@ impl ExecContext {
} }
/// Push any node into the active paragraph. /// 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; let align = self.state.aligns.cross;
self.stack.par.push(ParChild::Any(node.into(), align)); self.stack.par.push(ParChild::Any(node.into(), align));
} }
/// Push any node into the active stack. /// 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(); self.parbreak();
let aligns = self.state.aligns; let aligns = self.state.aligns;
self.stack.push(StackChild::Any(node.into(), aligns)); self.stack.push(StackChild::Any(node.into(), aligns));

View File

@ -10,15 +10,15 @@ use std::fmt::Write;
use std::rc::Rc; use std::rc::Rc;
use crate::diag::Pass; 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::geom::{Dir, Gen};
use crate::layout::{LayoutTree, StackChild, StackNode}; use crate::layout::{LayoutTree, StackChild, StackNode};
use crate::pretty::pretty; use crate::pretty::pretty;
use crate::eco::EcoString;
use crate::syntax::*; use crate::syntax::*;
/// Execute a template to produce a layout tree. /// 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); let mut ctx = ExecContext::new(state);
template.exec(&mut ctx); template.exec(&mut ctx);
ctx.finish() 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) { fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) {
match self { match self {
Self::Text(text) => ctx.push_text(text), Self::Text(text) => ctx.push_text(text),
@ -117,12 +117,7 @@ impl ExecWithMap for EnumItem {
} }
} }
fn exec_item( fn exec_item(ctx: &mut ExecContext, label: EcoString, body: &SyntaxTree, map: &ExprMap) {
ctx: &mut ExecContext,
label: EcoString,
body: &SyntaxTree,
map: &ExprMap,
) {
let label = ctx.exec_stack(|ctx| ctx.push_text(label)); let label = ctx.exec_stack(|ctx| ctx.push_text(label));
let body = ctx.exec_tree_stack(body, map); let body = ctx.exec_tree_stack(body, map);
let stack = StackNode { 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) { fn exec(&self, ctx: &mut ExecContext) {
for node in self.iter() { for node in self.iter() {
node.exec(ctx); node.exec(ctx);
@ -170,13 +165,19 @@ impl Exec for TemplateValue {
impl Exec for TemplateNode { impl Exec for TemplateNode {
fn exec(&self, ctx: &mut ExecContext) { fn exec(&self, ctx: &mut ExecContext) {
match self { match self {
Self::Tree { tree, map } => tree.exec_with_map(ctx, &map), Self::Tree(v) => v.exec(ctx),
Self::Str(v) => ctx.push_text(v),
Self::Func(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 { impl Exec for TemplateFunc {
fn exec(&self, ctx: &mut ExecContext) { fn exec(&self, ctx: &mut ExecContext) {
let snapshot = ctx.state.clone(); let snapshot = ctx.state.clone();

View File

@ -9,7 +9,7 @@ pub struct BackgroundNode {
/// Background color / texture. /// Background color / texture.
pub fill: Paint, pub fill: Paint,
/// The child node to be filled. /// The child node to be filled.
pub child: AnyNode, pub child: LayoutNode,
} }
/// The kind of shape to use as a background. /// 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 { fn from(background: BackgroundNode) -> Self {
Self::new(background) Self::new(background)
} }

View File

@ -9,7 +9,7 @@ pub struct FixedNode {
/// The fixed height, if any. /// The fixed height, if any.
pub height: Option<Linear>, pub height: Option<Linear>,
/// The child node whose size to fix. /// The child node whose size to fix.
pub child: AnyNode, pub child: LayoutNode,
} }
impl Layout for FixedNode { 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 { fn from(fixed: FixedNode) -> Self {
Self::new(fixed) Self::new(fixed)
} }

View File

@ -14,7 +14,7 @@ pub struct GridNode {
/// Defines sizing of gutter rows and columns between content. /// Defines sizing of gutter rows and columns between content.
pub gutter: Gen<Vec<TrackSizing>>, pub gutter: Gen<Vec<TrackSizing>>,
/// The nodes to be arranged in a grid. /// 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. /// 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 { fn from(grid: GridNode) -> Self {
Self::new(grid) Self::new(grid)
} }
@ -64,7 +64,7 @@ struct GridLayouter<'a> {
/// The row tracks including gutter tracks. /// The row tracks including gutter tracks.
rows: Vec<TrackSizing>, rows: Vec<TrackSizing>,
/// The children of the grid. /// The children of the grid.
children: &'a [AnyNode], children: &'a [LayoutNode],
/// The region to layout into. /// The region to layout into.
regions: Regions, regions: Regions,
/// Resolved column sizes. /// Resolved column sizes.
@ -517,7 +517,7 @@ impl<'a> GridLayouter<'a> {
/// Get the node in the cell in column `x` and row `y`. /// Get the node in the cell in column `x` and row `y`.
/// ///
/// Returns `None` if it's a gutter cell. /// 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!(x < self.cols.len());
assert!(y < self.rows.len()); assert!(y < self.rows.len());

View File

@ -59,7 +59,7 @@ impl Layout for ImageNode {
} }
} }
impl From<ImageNode> for AnyNode { impl From<ImageNode> for LayoutNode {
fn from(image: ImageNode) -> Self { fn from(image: ImageNode) -> Self {
Self::new(image) Self::new(image)
} }

View File

@ -71,7 +71,7 @@ pub struct PageRun {
pub size: Size, pub size: Size,
/// The layout node that produces the actual pages (typically a /// The layout node that produces the actual pages (typically a
/// [`StackNode`]). /// [`StackNode`]).
pub child: AnyNode, pub child: LayoutNode,
} }
impl PageRun { impl PageRun {
@ -86,14 +86,14 @@ impl PageRun {
} }
} }
/// A wrapper around a dynamic layouting node. /// A dynamic layouting node.
pub struct AnyNode { pub struct LayoutNode {
node: Box<dyn Bounds>, node: Box<dyn Bounds>,
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]
hash: u64, hash: u64,
} }
impl AnyNode { impl LayoutNode {
/// Create a new instance from any node that satisifies the required bounds. /// Create a new instance from any node that satisifies the required bounds.
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]
pub fn new<T>(node: T) -> Self pub fn new<T>(node: T) -> Self
@ -120,7 +120,7 @@ impl AnyNode {
} }
} }
impl Layout for AnyNode { impl Layout for LayoutNode {
fn layout( fn layout(
&self, &self,
ctx: &mut LayoutContext, 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 { fn clone(&self) -> Self {
Self { Self {
node: self.node.dyn_clone(), 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 { fn eq(&self, other: &Self) -> bool {
self.node.dyn_eq(other.node.as_ref()) self.node.dyn_eq(other.node.as_ref())
} }
} }
#[cfg(feature = "layout-cache")] #[cfg(feature = "layout-cache")]
impl Hash for AnyNode { impl Hash for LayoutNode {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.hash); 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 { trait Bounds: Layout + Debug + 'static {
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
fn dyn_eq(&self, other: &dyn Bounds) -> bool; fn dyn_eq(&self, other: &dyn Bounds) -> bool;

View File

@ -7,7 +7,7 @@ pub struct PadNode {
/// The amount of padding. /// The amount of padding.
pub padding: Sides<Linear>, pub padding: Sides<Linear>,
/// The child node whose sides to pad. /// The child node whose sides to pad.
pub child: AnyNode, pub child: LayoutNode,
} }
impl Layout for PadNode { 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 { fn from(pad: PadNode) -> Self {
Self::new(pad) Self::new(pad)
} }

View File

@ -5,8 +5,8 @@ use unicode_bidi::{BidiInfo, Level};
use xi_unicode::LineBreakIterator; use xi_unicode::LineBreakIterator;
use super::*; use super::*;
use crate::exec::FontState;
use crate::eco::EcoString; use crate::eco::EcoString;
use crate::exec::FontState;
use crate::util::{RangeExt, SliceExt}; use crate::util::{RangeExt, SliceExt};
type Range = std::ops::Range<usize>; 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. /// A run of text and how to align it in its line.
Text(EcoString, Align, Rc<FontState>), Text(EcoString, Align, Rc<FontState>),
/// Any child node and how to align it in its line. /// Any child node and how to align it in its line.
Any(AnyNode, Align), Any(LayoutNode, Align),
} }
impl Layout for ParNode { 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 { fn from(par: ParNode) -> Self {
Self::new(par) Self::new(par)
} }

View File

@ -26,7 +26,7 @@ pub enum StackChild {
/// Spacing between other nodes. /// Spacing between other nodes.
Spacing(Length), Spacing(Length),
/// Any child node and how to align it in the stack. /// Any child node and how to align it in the stack.
Any(AnyNode, Gen<Align>), Any(LayoutNode, Gen<Align>),
} }
impl Layout for StackNode { 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 { fn from(stack: StackNode) -> Self {
Self::new(stack) Self::new(stack)
} }

View File

@ -55,7 +55,7 @@ fn rect_impl(
height: Option<Linear>, height: Option<Linear>,
aspect: Option<N64>, aspect: Option<N64>,
fill: Option<Color>, fill: Option<Color>,
body: TemplateValue, body: Template,
) -> Value { ) -> Value {
Value::template(move |ctx| { Value::template(move |ctx| {
let mut stack = ctx.exec_template_stack(&body); let mut stack = ctx.exec_template_stack(&body);
@ -99,7 +99,7 @@ fn ellipse_impl(
height: Option<Linear>, height: Option<Linear>,
aspect: Option<N64>, aspect: Option<N64>,
fill: Option<Color>, fill: Option<Color>,
body: TemplateValue, body: Template,
) -> Value { ) -> Value {
Value::template(move |ctx| { Value::template(move |ctx| {
// This padding ratio ensures that the rectangular padded region fits // This padding ratio ensures that the rectangular padded region fits

View File

@ -20,7 +20,7 @@ pub fn page(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let right = args.named(ctx, "right"); let right = args.named(ctx, "right");
let bottom = args.named(ctx, "bottom"); let bottom = args.named(ctx, "bottom");
let flip = args.named(ctx, "flip"); 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| { Value::template(move |ctx| {
let snapshot = ctx.state.clone(); 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 second = args.eat::<AlignValue>(ctx);
let mut horizontal = args.named::<AlignValue>(ctx, "horizontal"); let mut horizontal = args.named::<AlignValue>(ctx, "horizontal");
let mut vertical = args.named::<AlignValue>(ctx, "vertical"); 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) { for value in first.into_iter().chain(second) {
match value.axis() { match value.axis() {

View File

@ -18,7 +18,7 @@ use std::rc::Rc;
use crate::color::{Color, RgbaColor}; use crate::color::{Color, RgbaColor};
use crate::eco::EcoString; 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::exec::{Exec, FontFamily};
use crate::font::{FontStyle, FontWeight, VerticalFontMetric}; use crate::font::{FontStyle, FontWeight, VerticalFontMetric};
use crate::geom::*; use crate::geom::*;

View File

@ -23,7 +23,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let serif = args.named(ctx, "serif"); let serif = args.named(ctx, "serif");
let sans_serif = args.named(ctx, "sans-serif"); let sans_serif = args.named(ctx, "sans-serif");
let monospace = args.named(ctx, "monospace"); 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| { Value::template(move |ctx| {
let font = ctx.state.font_mut(); 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 spacing = args.named(ctx, "spacing");
let leading = args.named(ctx, "leading"); let leading = args.named(ctx, "leading");
let word_spacing = args.named(ctx, "word-spacing"); 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| { Value::template(move |ctx| {
if let Some(spacing) = spacing { if let Some(spacing) = spacing {
@ -194,7 +194,7 @@ pub fn lang(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
} }
None => None, 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| { Value::template(move |ctx| {
if let Some(dir) = dir.or(iso) { 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 thickness = args.eat(ctx).or_else(|| args.named::<Linear>(ctx, "thickness"));
let offset = args.named(ctx, "offset"); let offset = args.named(ctx, "offset");
let extent = args.named(ctx, "extent").unwrap_or_default(); 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. // Suppress any existing strikethrough if strength is explicitly zero.
let state = thickness.map_or(true, |s| !s.is_zero()).then(|| { let state = thickness.map_or(true, |s| !s.is_zero()).then(|| {

View File

@ -55,10 +55,10 @@ where
let mut tree = vec![]; let mut tree = vec![];
while !p.eof() && f(p) { while !p.eof() && f(p) {
if let Some(mut node) = node(p, &mut at_start) { 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. // 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 { if call.wide {
let start = p.next_start(); let start = p.next_start();
let tree = tree_while(p, true, f); let tree = tree_while(p, true, f);
@ -77,7 +77,7 @@ where
} }
/// Parse a syntax node. /// 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 token = p.peek()?;
let span = p.peek_span(); let span = p.peek_span();
let node = match token { let node = match token {
@ -85,30 +85,32 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
Token::Space(newlines) => { Token::Space(newlines) => {
*at_start |= newlines > 0; *at_start |= newlines > 0;
if newlines < 2 { if newlines < 2 {
Node::Space SyntaxNode::Space
} else { } else {
Node::Parbreak(span) SyntaxNode::Parbreak(span)
} }
} }
// Text. // Text.
Token::Text(text) => Node::Text(text.into()), Token::Text(text) => SyntaxNode::Text(text.into()),
Token::Tilde => Node::Text("\u{00A0}".into()), Token::Tilde => SyntaxNode::Text("\u{00A0}".into()),
Token::HyphHyph => Node::Text("\u{2013}".into()), Token::HyphHyph => SyntaxNode::Text("\u{2013}".into()),
Token::HyphHyphHyph => Node::Text("\u{2014}".into()), Token::HyphHyphHyph => SyntaxNode::Text("\u{2014}".into()),
Token::UnicodeEscape(t) => Node::Text(unicode_escape(p, t)), Token::UnicodeEscape(t) => SyntaxNode::Text(unicode_escape(p, t)),
// Markup. // Markup.
Token::Backslash => Node::Linebreak(span), Token::Backslash => SyntaxNode::Linebreak(span),
Token::Star => Node::Strong(span), Token::Star => SyntaxNode::Strong(span),
Token::Underscore => Node::Emph(span), Token::Underscore => SyntaxNode::Emph(span),
Token::Raw(t) => raw(p, t), Token::Raw(t) => raw(p, t),
Token::Eq if *at_start => return Some(heading(p)), Token::Eq if *at_start => return Some(heading(p)),
Token::Hyph if *at_start => return Some(list_item(p)), Token::Hyph if *at_start => return Some(list_item(p)),
Token::Numbering(number) if *at_start => return Some(enum_item(p, number)), 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. // 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. // Hashtag + keyword / identifier.
Token::Ident(_) Token::Ident(_)
@ -128,12 +130,12 @@ fn node(p: &mut Parser, at_start: &mut bool) -> Option<Node> {
} }
p.end_group(); p.end_group();
return expr.map(Node::Expr); return expr.map(SyntaxNode::Expr);
} }
// Block and template. // Block and template.
Token::LeftBrace => return Some(Node::Expr(block(p, false))), Token::LeftBrace => return Some(SyntaxNode::Expr(block(p, false))),
Token::LeftBracket => return Some(Node::Expr(template(p))), Token::LeftBracket => return Some(SyntaxNode::Expr(template(p))),
// Comments. // Comments.
Token::LineComment(_) | Token::BlockComment(_) => { Token::LineComment(_) | Token::BlockComment(_) => {
@ -170,17 +172,17 @@ fn unicode_escape(p: &mut Parser, token: UnicodeEscapeToken) -> EcoString {
} }
/// Handle a raw block. /// 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 span = p.peek_span();
let raw = resolve::resolve_raw(span, token.text, token.backticks); let raw = resolve::resolve_raw(span, token.text, token.backticks);
if !token.terminated { if !token.terminated {
p.diag(error!(p.peek_span().end, "expected backtick(s)")); p.diag(error!(p.peek_span().end, "expected backtick(s)"));
} }
Node::Raw(raw) SyntaxNode::Raw(raw)
} }
/// Parse a heading. /// Parse a heading.
fn heading(p: &mut Parser) -> Node { fn heading(p: &mut Parser) -> SyntaxNode {
let start = p.next_start(); let start = p.next_start();
p.assert(Token::Eq); p.assert(Token::Eq);
@ -197,7 +199,7 @@ fn heading(p: &mut Parser) -> Node {
let body = tree_indented(p); let body = tree_indented(p);
Node::Heading(HeadingNode { SyntaxNode::Heading(HeadingNode {
span: p.span(start), span: p.span(start),
level, level,
body: Rc::new(body), body: Rc::new(body),
@ -205,19 +207,19 @@ fn heading(p: &mut Parser) -> Node {
} }
/// Parse a single list item. /// Parse a single list item.
fn list_item(p: &mut Parser) -> Node { fn list_item(p: &mut Parser) -> SyntaxNode {
let start = p.next_start(); let start = p.next_start();
p.assert(Token::Hyph); p.assert(Token::Hyph);
let body = tree_indented(p); 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. /// 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(); let start = p.next_start();
p.assert(Token::Numbering(number)); p.assert(Token::Numbering(number));
let body = tree_indented(p); 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. /// Parse an expression.

View File

@ -86,7 +86,7 @@ impl Pretty for SyntaxTree {
} }
} }
impl Pretty for Node { impl Pretty for SyntaxNode {
fn pretty(&self, p: &mut Printer) { fn pretty(&self, p: &mut Printer) {
match self { match self {
// TODO: Handle escaping. // 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) { fn pretty(&self, p: &mut Printer) {
p.push('('); p.push('(');
p.join(self, ", ", |item, p| item.pretty(p)); 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) { fn pretty(&self, p: &mut Printer) {
p.push('('); p.push('(');
if self.is_empty() { 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) { fn pretty(&self, p: &mut Printer) {
p.push_str("<template>"); p.push_str("<template>");
} }
} }
impl Pretty for FuncValue { impl Pretty for Function {
fn pretty(&self, p: &mut Printer) { fn pretty(&self, p: &mut Printer) {
p.push_str("<function"); p.push_str("<function");
if let Some(name) = self.name() { if let Some(name) = self.name() {
@ -608,20 +608,9 @@ pretty_display! {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::BTreeMap;
use super::*; use super::*;
use crate::parse::parse; 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] #[track_caller]
fn roundtrip(src: &str) { fn roundtrip(src: &str) {
test_parse(src, src); test_parse(src, src);
@ -757,18 +746,15 @@ mod tests {
test_value("\n", r#""\n""#); test_value("\n", r#""\n""#);
test_value("\\", r#""\\""#); test_value("\\", r#""\\""#);
test_value("\"", r#""\"""#); test_value("\"", r#""\"""#);
test_value(Value::Array(vec![]), "()"); test_value(array![], "()");
test_value(vec![Value::None], "(none,)"); test_value(array![Value::None], "(none,)");
test_value(vec![Value::Int(1), Value::Int(2)], "(1, 2)"); test_value(array![1, 2], "(1, 2)");
test_value(map![], "(:)"); test_value(dict![], "(:)");
test_value(map![one: Value::Int(1)], "(one: 1)"); 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( test_value(
map![two: Value::Bool(false), one: Value::Int(1)], Function::new(Some("nil".into()), |_, _| Value::None),
"(one: 1, two: false)",
);
test_value(FuncValue::new(None, |_, _| Value::None), "<function>");
test_value(
FuncValue::new(Some("nil".into()), |_, _| Value::None),
"<function nil>", "<function nil>",
); );
test_value(AnyValue::new(1), "1"); test_value(AnyValue::new(1), "1");

View File

@ -18,4 +18,4 @@ use crate::eco::EcoString;
/// The abstract syntax tree. /// The abstract syntax tree.
/// ///
/// This type can represent a full parsed document. /// This type can represent a full parsed document.
pub type SyntaxTree = Vec<Node>; pub type SyntaxTree = Vec<SyntaxNode>;

View File

@ -4,7 +4,7 @@ use super::*;
/// A syntax node, encompassing a single logical entity of parsed source code. /// A syntax node, encompassing a single logical entity of parsed source code.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Node { pub enum SyntaxNode {
/// Plain text. /// Plain text.
Text(EcoString), Text(EcoString),
/// Whitespace containing less than two newlines. /// Whitespace containing less than two newlines.

View File

@ -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 impl<T> From<T> for Span
where where
T: Into<Pos> + Copy, T: Into<Pos> + Copy,
@ -152,12 +138,26 @@ where
} }
} }
impl Default for Span {
fn default() -> Self {
Span::ZERO
}
}
impl Debug for Span { impl Debug for Span {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "<{:?}-{:?}>", self.start, self.end) 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. /// A byte position in source code.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct Pos(pub u32); 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 { impl From<u32> for Pos {
fn from(index: u32) -> Self { fn from(index: u32) -> Self {
Self(index) 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. /// A one-indexed line-column position in source code.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct Location { pub struct Location {

View File

@ -85,19 +85,19 @@ impl_visitors! {
} }
} }
visit_node(v, node: Node) { visit_node(v, node: SyntaxNode) {
match node { match node {
Node::Text(_) => {} SyntaxNode::Text(_) => {}
Node::Space => {} SyntaxNode::Space => {}
Node::Linebreak(_) => {} SyntaxNode::Linebreak(_) => {}
Node::Parbreak(_) => {} SyntaxNode::Parbreak(_) => {}
Node::Strong(_) => {} SyntaxNode::Strong(_) => {}
Node::Emph(_) => {} SyntaxNode::Emph(_) => {}
Node::Raw(_) => {} SyntaxNode::Raw(_) => {}
Node::Heading(n) => v.visit_heading(n), SyntaxNode::Heading(n) => v.visit_heading(n),
Node::List(n) => v.visit_list(n), SyntaxNode::List(n) => v.visit_list(n),
Node::Enum(n) => v.visit_enum(n), SyntaxNode::Enum(n) => v.visit_enum(n),
Node::Expr(n) => v.visit_expr(n), SyntaxNode::Expr(n) => v.visit_expr(n),
} }
} }