summaryrefslogtreecommitdiff
path: root/src/model
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-12-31 09:37:55 +0100
committerLaurenz <laurmaedje@gmail.com>2022-12-31 09:52:14 +0100
commit7c683db3673187260ef2fa84d779ff81cb05aba8 (patch)
tree5ecfe3845b528a7d5ec67dfc1c93eb0dee3fa951 /src/model
parenta6d90c1bf1e9fefa0af04206909a40e112d6bb14 (diff)
Merge `MarkupNode` and `MathNode` into `Expr`
Diffstat (limited to 'src/model')
-rw-r--r--src/model/eval.rs310
-rw-r--r--src/model/library.rs2
-rw-r--r--src/model/value.rs9
3 files changed, 178 insertions, 143 deletions
diff --git a/src/model/eval.rs b/src/model/eval.rs
index e9134114..959e4166 100644
--- a/src/model/eval.rs
+++ b/src/model/eval.rs
@@ -17,7 +17,7 @@ use crate::diag::{
use crate::geom::{Abs, Angle, Em, Fr, Ratio};
use crate::syntax::ast::AstNode;
use crate::syntax::{ast, Source, SourceId, Span, Spanned, SyntaxKind, SyntaxNode, Unit};
-use crate::util::{format_eco, EcoString, PathExt};
+use crate::util::{EcoString, PathExt};
use crate::World;
const MAX_ITERATIONS: usize = 10_000;
@@ -98,7 +98,7 @@ impl<'a> Vm<'a> {
}
/// Access the underlying world.
- pub fn world(&self) -> Tracked<dyn World> {
+ pub fn world(&self) -> Tracked<'a, dyn World> {
self.world
}
@@ -203,38 +203,38 @@ impl Eval for ast::Markup {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- eval_markup(vm, &mut self.children())
+ eval_markup(vm, &mut self.exprs())
}
}
-/// Evaluate a stream of markup nodes.
+/// Evaluate a stream of markup.
fn eval_markup(
vm: &mut Vm,
- nodes: &mut impl Iterator<Item = ast::MarkupNode>,
+ exprs: &mut impl Iterator<Item = ast::Expr>,
) -> SourceResult<Content> {
let flow = vm.flow.take();
- let mut seq = Vec::with_capacity(nodes.size_hint().1.unwrap_or_default());
+ let mut seq = Vec::with_capacity(exprs.size_hint().1.unwrap_or_default());
- while let Some(node) = nodes.next() {
- match node {
- ast::MarkupNode::Expr(ast::Expr::Set(set)) => {
+ while let Some(expr) = exprs.next() {
+ match expr {
+ ast::Expr::Set(set) => {
let styles = set.eval(vm)?;
if vm.flow.is_some() {
break;
}
- seq.push(eval_markup(vm, nodes)?.styled_with_map(styles))
+ seq.push(eval_markup(vm, exprs)?.styled_with_map(styles))
}
- ast::MarkupNode::Expr(ast::Expr::Show(show)) => {
+ ast::Expr::Show(show) => {
let recipe = show.eval(vm)?;
if vm.flow.is_some() {
break;
}
- let tail = eval_markup(vm, nodes)?;
+ let tail = eval_markup(vm, exprs)?;
seq.push(tail.styled_with_recipe(vm.world, recipe)?)
}
- ast::MarkupNode::Expr(expr) => match expr.eval(vm)? {
+ expr => match expr.eval(vm)? {
Value::Label(label) => {
if let Some(node) =
seq.iter_mut().rev().find(|node| node.labellable())
@@ -244,7 +244,6 @@ fn eval_markup(
}
value => seq.push(value.display().spanned(expr.span())),
},
- _ => seq.push(node.eval(vm)?),
}
if vm.flow.is_some() {
@@ -259,41 +258,96 @@ fn eval_markup(
Ok(Content::sequence(seq))
}
-impl Eval for ast::MarkupNode {
- type Output = Content;
+impl Eval for ast::Expr {
+ type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
+ let forbidden = |name| {
+ error!(
+ self.span(),
+ "{} is only allowed directly in code and content blocks", name
+ )
+ };
+
+ match self {
+ Self::Space(v) => v.eval(vm).map(Value::Content),
+ Self::Linebreak(v) => v.eval(vm).map(Value::Content),
+ Self::Text(v) => v.eval(vm).map(Value::Content),
+ Self::Escape(v) => v.eval(vm).map(Value::Content),
+ Self::Shorthand(v) => v.eval(vm).map(Value::Content),
+ Self::Symbol(v) => v.eval(vm).map(Value::Content),
+ Self::SmartQuote(v) => v.eval(vm).map(Value::Content),
+ Self::Strong(v) => v.eval(vm).map(Value::Content),
+ Self::Emph(v) => v.eval(vm).map(Value::Content),
+ Self::Link(v) => v.eval(vm).map(Value::Content),
+ Self::Raw(v) => v.eval(vm).map(Value::Content),
+ Self::Ref(v) => v.eval(vm).map(Value::Content),
+ Self::Heading(v) => v.eval(vm).map(Value::Content),
+ Self::List(v) => v.eval(vm).map(Value::Content),
+ Self::Enum(v) => v.eval(vm).map(Value::Content),
+ Self::Term(v) => v.eval(vm).map(Value::Content),
+ Self::Atom(v) => v.eval(vm).map(Value::Content),
+ Self::Script(v) => v.eval(vm).map(Value::Content),
+ Self::Frac(v) => v.eval(vm).map(Value::Content),
+ Self::AlignPoint(v) => v.eval(vm).map(Value::Content),
+ Self::Lit(v) => v.eval(vm),
+ Self::Ident(v) => v.eval(vm),
+ Self::Code(v) => v.eval(vm),
+ Self::Content(v) => v.eval(vm).map(Value::Content),
+ Self::Math(v) => v.eval(vm).map(Value::Content),
+ Self::Array(v) => v.eval(vm).map(Value::Array),
+ Self::Dict(v) => v.eval(vm).map(Value::Dict),
+ Self::Parenthesized(v) => v.eval(vm),
+ Self::FieldAccess(v) => v.eval(vm),
+ Self::FuncCall(v) => v.eval(vm),
+ Self::MethodCall(v) => v.eval(vm),
+ Self::Closure(v) => v.eval(vm),
+ Self::Unary(v) => v.eval(vm),
+ Self::Binary(v) => v.eval(vm),
+ Self::Let(v) => v.eval(vm),
+ Self::Set(_) => bail!(forbidden("set")),
+ Self::Show(_) => bail!(forbidden("show")),
+ Self::Conditional(v) => v.eval(vm),
+ Self::While(v) => v.eval(vm),
+ Self::For(v) => v.eval(vm),
+ Self::Import(v) => v.eval(vm),
+ Self::Include(v) => v.eval(vm).map(Value::Content),
+ Self::Break(v) => v.eval(vm),
+ Self::Continue(v) => v.eval(vm),
+ Self::Return(v) => v.eval(vm),
+ }
+ }
+}
+
+impl ast::Expr {
+ fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
Ok(match self {
- Self::Space(v) => match v.newlines() {
- 0..=1 => (vm.items.space)(),
- _ => (vm.items.parbreak)(),
- },
- Self::Linebreak(v) => v.eval(vm)?,
- Self::Text(v) => v.eval(vm)?,
- Self::Escape(v) => (vm.items.text)(v.get().into()),
- Self::Shorthand(v) => v.eval(vm)?,
- Self::Symbol(v) => v.eval(vm)?,
- Self::SmartQuote(v) => v.eval(vm)?,
- Self::Strong(v) => v.eval(vm)?,
- Self::Emph(v) => v.eval(vm)?,
- Self::Link(v) => v.eval(vm)?,
- Self::Raw(v) => v.eval(vm)?,
- Self::Heading(v) => v.eval(vm)?,
- Self::List(v) => v.eval(vm)?,
- Self::Enum(v) => v.eval(vm)?,
- Self::Term(v) => v.eval(vm)?,
- Self::Ref(v) => v.eval(vm)?,
- Self::Expr(_) => unimplemented!("handled above"),
+ Self::Escape(v) => v.eval_in_math(vm)?,
+ Self::Shorthand(v) => v.eval_in_math(vm)?,
+ Self::Symbol(v) => v.eval_in_math(vm)?,
+ Self::Ident(v) => v.eval_in_math(vm)?,
+ _ => self.eval(vm)?.display_in_math(),
}
.spanned(self.span()))
}
}
+impl Eval for ast::Space {
+ type Output = Content;
+
+ fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
+ Ok(match self.newlines() {
+ 0..=1 => (vm.items.space)(),
+ _ => (vm.items.parbreak)(),
+ })
+ }
+}
+
impl Eval for ast::Linebreak {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- Ok((vm.items.linebreak)(false))
+ Ok((vm.items.linebreak)())
}
}
@@ -305,6 +359,20 @@ impl Eval for ast::Text {
}
}
+impl Eval for ast::Escape {
+ type Output = Content;
+
+ fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
+ Ok((vm.items.text)(self.get().into()))
+ }
+}
+
+impl ast::Escape {
+ fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
+ Ok((vm.items.math_atom)(self.get().into()))
+ }
+}
+
impl Eval for ast::Shorthand {
type Output = Content;
@@ -313,6 +381,12 @@ impl Eval for ast::Shorthand {
}
}
+impl ast::Shorthand {
+ fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
+ Ok((vm.items.math_atom)(self.get().into()))
+ }
+}
+
impl Eval for ast::Symbol {
type Output = Content;
@@ -321,6 +395,12 @@ impl Eval for ast::Symbol {
}
}
+impl ast::Symbol {
+ fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
+ Ok((vm.items.symbol)(self.get().clone() + ":op".into()))
+ }
+}
+
impl Eval for ast::SmartQuote {
type Output = Content;
@@ -414,48 +494,12 @@ impl Eval for ast::Math {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- Ok((vm.items.math)(
- self.children()
- .map(|node| node.eval(vm))
- .collect::<SourceResult<_>>()?,
- self.block(),
- ))
- }
-}
-
-impl Eval for ast::MathNode {
- type Output = Content;
-
- fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- Ok(match self {
- Self::Space(_) => (vm.items.space)(),
- Self::Linebreak(v) => v.eval(vm)?,
- Self::Escape(v) => (vm.items.math_atom)(v.get().into()),
- Self::Shorthand(v) => (vm.items.math_atom)(v.get().into()),
- Self::Atom(v) => v.eval(vm)?,
- Self::Symbol(v) => (vm.items.symbol)(v.get().clone() + ":op".into()),
- Self::Script(v) => v.eval(vm)?,
- Self::Frac(v) => v.eval(vm)?,
- Self::AlignPoint(v) => v.eval(vm)?,
- Self::Group(v) => v.eval(vm)?,
- Self::Expr(v) => {
- if let ast::Expr::Ident(ident) = v {
- if self.as_untyped().len() == ident.len()
- && matches!(vm.scopes.get(ident), Ok(Value::Func(_)) | Err(_))
- {
- let node = (vm.items.symbol)(ident.get().clone() + ":op".into());
- return Ok(node.spanned(self.span()));
- }
- }
-
- match v.eval(vm)? {
- Value::Int(v) => (vm.items.math_atom)(format_eco!("{}", v)),
- Value::Float(v) => (vm.items.math_atom)(format_eco!("{}", v)),
- v => v.display(),
- }
- }
- }
- .spanned(self.span()))
+ let seq = self
+ .exprs()
+ .map(|expr| expr.eval_in_math(vm))
+ .collect::<SourceResult<_>>()?;
+ let block = self.block();
+ Ok((vm.items.math)(seq, block))
}
}
@@ -471,11 +515,10 @@ impl Eval for ast::Script {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- Ok((vm.items.math_script)(
- self.base().eval(vm)?,
- self.sub().map(|node| node.eval(vm)).transpose()?,
- self.sup().map(|node| node.eval(vm)).transpose()?,
- ))
+ let base = self.base().eval_in_math(vm)?;
+ let sub = self.sub().map(|expr| expr.eval_in_math(vm)).transpose()?;
+ let sup = self.sup().map(|expr| expr.eval_in_math(vm)).transpose()?;
+ Ok((vm.items.math_script)(base, sub, sup))
}
}
@@ -483,7 +526,9 @@ impl Eval for ast::Frac {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- Ok((vm.items.math_frac)(self.num().eval(vm)?, self.denom().eval(vm)?))
+ let num = self.num().eval_in_math(vm)?;
+ let denom = self.denom().eval_in_math(vm)?;
+ Ok((vm.items.math_frac)(num, denom))
}
}
@@ -495,47 +540,6 @@ impl Eval for ast::AlignPoint {
}
}
-impl Eval for ast::Expr {
- type Output = Value;
-
- fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- let forbidden = |name| {
- error!(
- self.span(),
- "{} is only allowed directly in code and content blocks", name
- )
- };
-
- match self {
- Self::Lit(v) => v.eval(vm),
- Self::Ident(v) => v.eval(vm),
- Self::Code(v) => v.eval(vm),
- Self::Content(v) => v.eval(vm).map(Value::Content),
- Self::Math(v) => v.eval(vm).map(Value::Content),
- Self::Array(v) => v.eval(vm).map(Value::Array),
- Self::Dict(v) => v.eval(vm).map(Value::Dict),
- Self::Parenthesized(v) => v.eval(vm),
- Self::FieldAccess(v) => v.eval(vm),
- Self::FuncCall(v) => v.eval(vm),
- Self::MethodCall(v) => v.eval(vm),
- Self::Closure(v) => v.eval(vm),
- Self::Unary(v) => v.eval(vm),
- Self::Binary(v) => v.eval(vm),
- Self::Let(v) => v.eval(vm),
- Self::Set(_) => bail!(forbidden("set")),
- Self::Show(_) => bail!(forbidden("show")),
- Self::Conditional(v) => v.eval(vm),
- Self::While(v) => v.eval(vm),
- Self::For(v) => v.eval(vm),
- Self::Import(v) => v.eval(vm),
- Self::Include(v) => v.eval(vm).map(Value::Content),
- Self::Break(v) => v.eval(vm),
- Self::Continue(v) => v.eval(vm),
- Self::Return(v) => v.eval(vm),
- }
- }
-}
-
impl Eval for ast::Lit {
type Output = Value;
@@ -571,6 +575,18 @@ impl Eval for ast::Ident {
}
}
+impl ast::Ident {
+ fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
+ if self.as_untyped().len() == self.len()
+ && matches!(vm.scopes.get(&self), Ok(Value::Func(_)) | Err(_))
+ {
+ Ok((vm.items.symbol)(self.get().clone() + ":op".into()))
+ } else {
+ Ok(self.eval(vm)?.display_in_math())
+ }
+ }
+}
+
impl Eval for ast::CodeBlock {
type Output = Value;
@@ -789,7 +805,11 @@ impl Eval for ast::FieldAccess {
.field(&field)
.ok_or_else(|| format!("unknown field {field:?}"))
.at(span)?,
- v => bail!(self.target().span(), "cannot access field on {}", v.type_name()),
+ v => bail!(
+ self.target().span(),
+ "expected dictionary or content, found {}",
+ v.type_name()
+ ),
})
}
}
@@ -916,7 +936,7 @@ impl Eval for ast::Closure {
}
}
- // Define the closure function.
+ // Define the closure.
let closure = Closure {
location: vm.location,
name,
@@ -966,7 +986,7 @@ impl Eval for ast::ShowRule {
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
let selector = self
.selector()
- .map(|selector| selector.eval(vm)?.cast::<Selector>().at(selector.span()))
+ .map(|sel| sel.eval(vm)?.cast::<Selector>().at(sel.span()))
.transpose()?;
let transform = self.transform();
@@ -1094,7 +1114,6 @@ impl Eval for ast::ForLoop {
}
let iter = self.iter().eval(vm)?;
-
let pattern = self.pattern();
let key = pattern.key().map(ast::Ident::take);
let value = pattern.value().take();
@@ -1266,28 +1285,35 @@ impl Access for ast::Parenthesized {
impl Access for ast::FieldAccess {
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
- Ok(match self.target().access(vm)? {
- Value::Dict(dict) => dict.at_mut(self.field().take().into()),
- v => bail!(
+ let value = self.target().access(vm)?;
+ let Value::Dict(dict) = value else {
+ bail!(
self.target().span(),
"expected dictionary, found {}",
- v.type_name(),
- ),
- })
+ value.type_name(),
+ );
+ };
+
+ Ok(dict.at_mut(self.field().take().into()))
}
}
impl Access for ast::MethodCall {
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
let span = self.span();
- let method = self.method();
- let args = self.args().eval(vm)?;
- if methods::is_accessor(&method) {
- let value = self.target().access(vm)?;
- methods::call_access(value, &method, args, span)
- } else {
+ let method = self.method().take();
+ let world = vm.world();
+
+ if !methods::is_accessor(&method) {
let _ = self.eval(vm)?;
bail!(span, "cannot mutate a temporary value");
}
+
+ let args = self.args().eval(vm)?;
+ let value = self.target().access(vm)?;
+ let result = methods::call_access(value, &method, args, span);
+
+ let point = || Tracepoint::Call(Some(method.clone()));
+ result.trace(world, point, span)
}
}
diff --git a/src/model/library.rs b/src/model/library.rs
index c6449e27..5360b00a 100644
--- a/src/model/library.rs
+++ b/src/model/library.rs
@@ -34,7 +34,7 @@ pub struct LangItems {
/// Whitespace.
pub space: fn() -> Content,
/// A forced line break: `\`.
- pub linebreak: fn(justify: bool) -> Content,
+ pub linebreak: fn() -> Content,
/// Plain text without markup.
pub text: fn(text: EcoString) -> Content,
/// The id of the text node.
diff --git a/src/model/value.rs b/src/model/value.rs
index 1c687d8d..0716985d 100644
--- a/src/model/value.rs
+++ b/src/model/value.rs
@@ -112,6 +112,15 @@ impl Value {
_ => item!(raw)(self.repr().into(), Some("typc".into()), false),
}
}
+
+ /// Return the display representation of the value in math mode.
+ pub fn display_in_math(self) -> Content {
+ match self {
+ Self::Int(v) => item!(math_atom)(format_eco!("{}", v)),
+ Self::Float(v) => item!(math_atom)(format_eco!("{}", v)),
+ _ => self.display(),
+ }
+ }
}
impl Default for Value {