summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-03-10 20:47:23 +0100
committerLaurenz <laurmaedje@gmail.com>2023-03-10 21:19:50 +0100
commita9fdff244aef859449a76e5f762ee7c343a8ddcc (patch)
tree172b543183296b4bc30b3008650f594688467914 /src/eval
parent62f35602a87574dcc607f1637aeae1be574981ff (diff)
Expose content representation more
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/args.rs16
-rw-r--r--src/eval/array.rs49
-rw-r--r--src/eval/dict.rs45
-rw-r--r--src/eval/func.rs56
-rw-r--r--src/eval/methods.rs12
-rw-r--r--src/eval/mod.rs16
-rw-r--r--src/eval/ops.rs1
-rw-r--r--src/eval/value.rs7
8 files changed, 92 insertions, 110 deletions
diff --git a/src/eval/args.rs b/src/eval/args.rs
index 159e9a77..17bd9cb4 100644
--- a/src/eval/args.rs
+++ b/src/eval/args.rs
@@ -1,10 +1,11 @@
-use std::fmt::{self, Debug, Formatter, Write};
+use std::fmt::{self, Debug, Formatter};
-use ecow::EcoVec;
+use ecow::{eco_format, EcoVec};
use super::{Array, Cast, Dict, Str, Value};
use crate::diag::{bail, At, SourceResult};
use crate::syntax::{Span, Spanned};
+use crate::util::pretty_array;
/// Evaluated arguments to a function.
#[derive(Clone, PartialEq, Hash)]
@@ -171,14 +172,9 @@ impl Args {
impl Debug for Args {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.write_char('(')?;
- for (i, arg) in self.items.iter().enumerate() {
- arg.fmt(f)?;
- if i + 1 < self.items.len() {
- f.write_str(", ")?;
- }
- }
- f.write_char(')')
+ let pieces: Vec<_> =
+ self.items.iter().map(|arg| eco_format!("{arg:?}")).collect();
+ f.write_str(&pretty_array(&pieces, false))
}
}
diff --git a/src/eval/array.rs b/src/eval/array.rs
index 53bae06f..8da9b3d2 100644
--- a/src/eval/array.rs
+++ b/src/eval/array.rs
@@ -1,11 +1,12 @@
use std::cmp::Ordering;
-use std::fmt::{self, Debug, Formatter, Write};
+use std::fmt::{self, Debug, Formatter};
use std::ops::{Add, AddAssign};
use ecow::{eco_format, EcoString, EcoVec};
use super::{ops, Args, Func, Value, Vm};
use crate::diag::{bail, At, SourceResult, StrResult};
+use crate::util::pretty_array;
/// Create a new [`Array`] from values.
#[macro_export]
@@ -147,7 +148,6 @@ impl Array {
return Ok(Some(item.clone()));
}
}
-
Ok(None)
}
@@ -262,6 +262,14 @@ impl Array {
self.0.iter().cloned().rev().collect()
}
+ /// Split all values in the array.
+ pub fn split(&self, at: Value) -> Array {
+ self.as_slice()
+ .split(|value| *value == at)
+ .map(|subslice| Value::Array(subslice.iter().cloned().collect()))
+ .collect()
+ }
+
/// Join all values in the array, optionally with separator and last
/// separator (between the final two items).
pub fn join(&self, sep: Option<Value>, mut last: Option<Value>) -> StrResult<Value> {
@@ -332,31 +340,10 @@ impl Array {
}
}
-/// The out of bounds access error message.
-#[cold]
-fn out_of_bounds(index: i64, len: i64) -> EcoString {
- eco_format!("array index out of bounds (index: {}, len: {})", index, len)
-}
-
-/// The error message when the array is empty.
-#[cold]
-fn array_is_empty() -> EcoString {
- "array is empty".into()
-}
-
impl Debug for Array {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.write_char('(')?;
- for (i, value) in self.iter().enumerate() {
- value.fmt(f)?;
- if i + 1 < self.0.len() {
- f.write_str(", ")?;
- }
- }
- if self.len() == 1 {
- f.write_char(',')?;
- }
- f.write_char(')')
+ let pieces: Vec<_> = self.iter().map(|value| eco_format!("{value:?}")).collect();
+ f.write_str(&pretty_array(&pieces, self.len() == 1))
}
}
@@ -404,3 +391,15 @@ impl<'a> IntoIterator for &'a Array {
self.iter()
}
}
+
+/// The error message when the array is empty.
+#[cold]
+fn array_is_empty() -> EcoString {
+ "array is empty".into()
+}
+
+/// The out of bounds access error message.
+#[cold]
+fn out_of_bounds(index: i64, len: i64) -> EcoString {
+ eco_format!("array index out of bounds (index: {}, len: {})", index, len)
+}
diff --git a/src/eval/dict.rs b/src/eval/dict.rs
index 6c1934c9..ececce07 100644
--- a/src/eval/dict.rs
+++ b/src/eval/dict.rs
@@ -1,5 +1,5 @@
use std::collections::BTreeMap;
-use std::fmt::{self, Debug, Formatter, Write};
+use std::fmt::{self, Debug, Formatter};
use std::ops::{Add, AddAssign};
use std::sync::Arc;
@@ -8,7 +8,7 @@ use ecow::{eco_format, EcoString};
use super::{array, Array, Str, Value};
use crate::diag::StrResult;
use crate::syntax::is_ident;
-use crate::util::ArcExt;
+use crate::util::{pretty_array, ArcExt};
/// Create a new [`Dict`] from key-value pairs.
#[macro_export]
@@ -132,31 +132,24 @@ impl Dict {
}
}
-/// The missing key access error message.
-#[cold]
-fn missing_key(key: &str) -> EcoString {
- eco_format!("dictionary does not contain key {:?}", Str::from(key))
-}
-
impl Debug for Dict {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- f.write_char('(')?;
if self.is_empty() {
- f.write_char(':')?;
+ return f.write_str("(:)");
}
- for (i, (key, value)) in self.iter().enumerate() {
- if is_ident(key) {
- f.write_str(key)?;
- } else {
- write!(f, "{key:?}")?;
- }
- f.write_str(": ")?;
- value.fmt(f)?;
- if i + 1 < self.0.len() {
- f.write_str(", ")?;
- }
- }
- f.write_char(')')
+
+ let pieces: Vec<_> = self
+ .iter()
+ .map(|(key, value)| {
+ if is_ident(key) {
+ eco_format!("{key}: {value:?}")
+ } else {
+ eco_format!("{key:?}: {value:?}")
+ }
+ })
+ .collect();
+
+ f.write_str(&pretty_array(&pieces, false))
}
}
@@ -207,3 +200,9 @@ impl<'a> IntoIterator for &'a Dict {
self.iter()
}
}
+
+/// The missing key access error message.
+#[cold]
+fn missing_key(key: &str) -> EcoString {
+ eco_format!("dictionary does not contain key {:?}", Str::from(key))
+}
diff --git a/src/eval/func.rs b/src/eval/func.rs
index 79ae142c..8da5c6bc 100644
--- a/src/eval/func.rs
+++ b/src/eval/func.rs
@@ -13,7 +13,7 @@ use super::{
Vm,
};
use crate::diag::{bail, SourceResult, StrResult};
-use crate::model::{Content, NodeId, Selector, StyleMap};
+use crate::model::{NodeId, Selector, StyleMap};
use crate::syntax::ast::{self, AstNode, Expr};
use crate::syntax::{SourceId, Span, SyntaxNode};
use crate::util::hash128;
@@ -29,7 +29,7 @@ enum Repr {
/// A native Rust function.
Native(NativeFunc),
/// A function for a node.
- Node(NodeFunc),
+ Node(NodeId),
/// A user-defined closure.
Closure(Closure),
/// A nested function with pre-applied arguments.
@@ -156,13 +156,13 @@ impl Func {
/// Create a selector for this function's node type.
pub fn select(&self, fields: Option<Dict>) -> StrResult<Selector> {
- match &**self.0 {
- Repr::Node(node) => {
- if node.id == item!(text_id) {
+ match **self.0 {
+ Repr::Node(id) => {
+ if id == item!(text_id) {
Err("to select text, please use a string or regex instead")?;
}
- Ok(Selector::Node(node.id, fields))
+ Ok(Selector::Node(id, fields))
}
_ => Err("this function is not selectable")?,
}
@@ -172,8 +172,8 @@ impl Func {
impl Debug for Func {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self.name() {
- Some(name) => write!(f, "<function {name}>"),
- None => f.write_str("<function>"),
+ Some(name) => write!(f, "{name}"),
+ None => f.write_str("(..) => .."),
}
}
}
@@ -190,6 +190,16 @@ impl From<Repr> for Func {
}
}
+impl From<NodeId> for Func {
+ fn from(id: NodeId) -> Self {
+ Repr::Node(id).into()
+ }
+}
+
+cast_to_value! {
+ v: NodeId => Value::Func(v.into())
+}
+
/// A native Rust function.
pub struct NativeFunc {
/// The function's implementation.
@@ -223,36 +233,6 @@ where
}
}
-/// A function defined by a native Rust node.
-pub struct NodeFunc {
- /// The node's id.
- pub id: NodeId,
- /// The node's constructor.
- pub construct: fn(&Vm, &mut Args) -> SourceResult<Content>,
- /// The node's set rule.
- pub set: fn(&mut Args) -> SourceResult<StyleMap>,
- /// Details about the function.
- pub info: Lazy<FuncInfo>,
-}
-
-impl Hash for NodeFunc {
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.id.hash(state);
- (self.construct as usize).hash(state);
- (self.set as usize).hash(state);
- }
-}
-
-impl From<NodeFunc> for Func {
- fn from(node: NodeFunc) -> Self {
- Repr::Node(node).into()
- }
-}
-
-cast_to_value! {
- v: NodeFunc => Value::Func(v.into())
-}
-
/// Details about a function.
#[derive(Debug, Clone)]
pub struct FuncInfo {
diff --git a/src/eval/methods.rs b/src/eval/methods.rs
index dcb1ca31..197a2f65 100644
--- a/src/eval/methods.rs
+++ b/src/eval/methods.rs
@@ -69,6 +69,13 @@ pub fn call(
_ => return missing(),
},
+ Value::Content(content) => match method {
+ "func" => Value::Func(content.id().into()),
+ "has" => Value::Bool(content.has(&args.expect::<EcoString>("field")?)),
+ "at" => content.at(&args.expect::<EcoString>("field")?).at(span)?.clone(),
+ _ => return missing(),
+ },
+
Value::Array(array) => match method {
"len" => Value::Int(array.len()),
"first" => array.first().at(span)?.clone(),
@@ -96,6 +103,7 @@ pub fn call(
"all" => Value::Bool(array.all(vm, args.expect("function")?)?),
"flatten" => Value::Array(array.flatten()),
"rev" => Value::Array(array.rev()),
+ "split" => Value::Array(array.split(args.expect("separator")?)),
"join" => {
let sep = args.eat()?;
let last = args.named("last")?;
@@ -107,7 +115,7 @@ pub fn call(
Value::Dict(dict) => match method {
"len" => Value::Int(dict.len()),
- "at" => dict.at(&args.expect::<Str>("key")?).cloned().at(span)?,
+ "at" => dict.at(&args.expect::<Str>("key")?).at(span)?.clone(),
"keys" => Value::Array(dict.keys()),
"values" => Value::Array(dict.values()),
"pairs" => Value::Array(dict.pairs()),
@@ -237,6 +245,7 @@ pub fn methods_on(type_name: &str) -> &[(&'static str, bool)] {
("starts-with", true),
("trim", true),
],
+ "content" => &[("func", false), ("has", true), ("at", true)],
"array" => &[
("all", true),
("any", true),
@@ -248,6 +257,7 @@ pub fn methods_on(type_name: &str) -> &[(&'static str, bool)] {
("flatten", false),
("fold", true),
("insert", true),
+ ("split", true),
("join", true),
("last", false),
("len", false),
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index fcfda263..145f961a 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -47,6 +47,7 @@ use unicode_segmentation::UnicodeSegmentation;
use crate::diag::{
bail, error, At, SourceError, SourceResult, StrResult, Trace, Tracepoint,
};
+use crate::model::Unlabellable;
use crate::model::{Content, Label, Recipe, Selector, StyleMap, Transform};
use crate::syntax::ast::AstNode;
use crate::syntax::{
@@ -357,12 +358,12 @@ fn eval_markup(
}
let tail = eval_markup(vm, exprs)?;
- seq.push(tail.apply_recipe(vm.world, recipe)?)
+ seq.push(tail.styled_with_recipe(vm.world, recipe)?)
}
expr => match expr.eval(vm)? {
Value::Label(label) => {
if let Some(node) =
- seq.iter_mut().rev().find(|node| node.labellable())
+ seq.iter_mut().rev().find(|node| !node.can::<dyn Unlabellable>())
{
*node = mem::take(node).labelled(label);
}
@@ -786,7 +787,7 @@ fn eval_code(
}
let tail = eval_code(vm, exprs)?.display();
- Value::Content(tail.apply_recipe(vm.world, recipe)?)
+ Value::Content(tail.styled_with_recipe(vm.world, recipe)?)
}
_ => expr.eval(vm)?,
};
@@ -979,6 +980,10 @@ impl Eval for ast::FuncCall {
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
let span = self.span();
+ if vm.depth >= MAX_CALL_DEPTH {
+ bail!(span, "maximum function call depth exceeded");
+ }
+
let callee = self.callee();
let in_math = in_math(&callee);
let callee_span = callee.span();
@@ -1042,11 +1047,6 @@ impl Eval for ast::FuncCall {
));
}
- // Finally, just a normal function call!
- if vm.depth >= MAX_CALL_DEPTH {
- bail!(span, "maximum function call depth exceeded");
- }
-
let callee = callee.cast::<Func>().at(callee_span)?;
let point = || Tracepoint::Call(callee.name().map(Into::into));
callee.call(vm, args).trace(vm.world, point, span)
diff --git a/src/eval/ops.rs b/src/eval/ops.rs
index 52b9b06a..5efb68c3 100644
--- a/src/eval/ops.rs
+++ b/src/eval/ops.rs
@@ -340,6 +340,7 @@ pub fn equal(lhs: &Value, rhs: &Value) -> bool {
(Symbol(a), Symbol(b)) => a == b,
(Str(a), Str(b)) => a == b,
(Label(a), Label(b)) => a == b,
+ (Content(a), Content(b)) => a == b,
(Array(a), Array(b)) => a == b,
(Dict(a), Dict(b)) => a == b,
(Func(a), Func(b)) => a == b,
diff --git a/src/eval/value.rs b/src/eval/value.rs
index 9b9bc314..61af36f5 100644
--- a/src/eval/value.rs
+++ b/src/eval/value.rs
@@ -120,10 +120,7 @@ impl Value {
match self {
Self::Symbol(symbol) => symbol.clone().modified(&field).map(Self::Symbol),
Self::Dict(dict) => dict.at(&field).cloned(),
- Self::Content(content) => content
- .field(&field)
- .cloned()
- .ok_or_else(|| eco_format!("unknown field `{field}`")),
+ Self::Content(content) => content.at(&field).cloned(),
Self::Module(module) => module.get(&field).cloned(),
v => Err(eco_format!("cannot access fields on type {}", v.type_name())),
}
@@ -190,7 +187,7 @@ impl Debug for Value {
Self::Symbol(v) => Debug::fmt(v, f),
Self::Str(v) => Debug::fmt(v, f),
Self::Label(v) => Debug::fmt(v, f),
- Self::Content(_) => f.pad("[...]"),
+ Self::Content(v) => Debug::fmt(v, f),
Self::Array(v) => Debug::fmt(v, f),
Self::Dict(v) => Debug::fmt(v, f),
Self::Func(v) => Debug::fmt(v, f),