mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Reference-count complex values
Rename some nodes types
This commit is contained in:
parent
36b3067c19
commit
6a4823461f
49
src/eco.rs
49
src/eco.rs
@ -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
148
src/eval/array.rs
Normal 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
129
src/eval/dict.rs
Normal 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
176
src/eval/function.rs
Normal 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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -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
139
src/eval/template.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
@ -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 {
|
||||||
|
@ -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));
|
||||||
|
@ -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();
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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() {
|
||||||
|
@ -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::*;
|
||||||
|
@ -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(|| {
|
||||||
|
@ -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.
|
||||||
|
@ -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");
|
||||||
|
@ -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>;
|
||||||
|
@ -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.
|
||||||
|
@ -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 {
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user