summaryrefslogtreecommitdiff
path: root/src/model
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-01-27 11:54:30 +0100
committerLaurenz <laurmaedje@gmail.com>2023-01-27 11:54:30 +0100
commita8fd64f9289b92614b9e6c16e909ec0c45429027 (patch)
tree76ce8797a6fe9c8b8c0bb1783910ba4b9e22da8b /src/model
parent33585d9a3fbab8a76d3fd8e9c2560f929202a518 (diff)
Hashtags everywhere!
Diffstat (limited to 'src/model')
-rw-r--r--src/model/eval.rs291
-rw-r--r--src/model/func.rs16
-rw-r--r--src/model/library.rs14
3 files changed, 129 insertions, 192 deletions
diff --git a/src/model/eval.rs b/src/model/eval.rs
index 6de328bc..6a1884eb 100644
--- a/src/model/eval.rs
+++ b/src/model/eval.rs
@@ -234,7 +234,7 @@ fn eval_markup(
*node = mem::take(node).labelled(label);
}
}
- value => seq.push(value.display().spanned(expr.span())),
+ value => seq.push(value.display()),
},
}
@@ -254,11 +254,9 @@ impl Eval for ast::Expr {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
+ let span = self.span();
let forbidden = |name| {
- error!(
- self.span(),
- "{} is only allowed directly in code and content blocks", name
- )
+ error!(span, "{} is only allowed directly in code and content blocks", name)
};
match self {
@@ -266,9 +264,9 @@ impl Eval for ast::Expr {
Self::Space(v) => v.eval(vm).map(Value::Content),
Self::Linebreak(v) => v.eval(vm).map(Value::Content),
Self::Parbreak(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::Escape(v) => v.eval(vm),
+ Self::Shorthand(v) => v.eval(vm),
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),
@@ -280,11 +278,14 @@ impl Eval for ast::Expr {
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::Delimited(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::Formula(v) => v.eval(vm).map(Value::Content),
+ Self::Math(v) => v.eval(vm).map(Value::Content),
+ Self::MathAtom(v) => v.eval(vm).map(Value::Content),
+ Self::MathIdent(v) => v.eval(vm),
+ Self::MathAlignPoint(v) => v.eval(vm).map(Value::Content),
+ Self::MathDelimited(v) => v.eval(vm).map(Value::Content),
+ Self::MathScript(v) => v.eval(vm).map(Value::Content),
+ Self::MathFrac(v) => v.eval(vm).map(Value::Content),
Self::Ident(v) => v.eval(vm),
Self::None(v) => v.eval(vm),
Self::Auto(v) => v.eval(vm),
@@ -295,7 +296,6 @@ impl Eval for ast::Expr {
Self::Str(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),
@@ -316,29 +316,12 @@ impl Eval for ast::Expr {
Self::Break(v) => v.eval(vm),
Self::Continue(v) => v.eval(vm),
Self::Return(v) => v.eval(vm),
- }
- }
-}
+ }?
+ .spanned(span);
-impl ast::Expr {
- fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
- Ok(match self {
- 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::FuncCall(v) => v.eval_in_math(vm)?,
- _ => self.eval(vm)?.display_in_math(),
}
- .spanned(self.span()))
- }
- fn eval_without_parens(&self, vm: &mut Vm) -> SourceResult<Content> {
- Ok(match self {
- Self::Delimited(v) => v.eval_without_parens(vm)?,
- _ => self.eval_in_math(vm)?,
- }
- .spanned(self.span()))
+ Ok(v)
}
}
@@ -375,44 +358,22 @@ impl Eval for ast::Parbreak {
}
impl Eval for ast::Escape {
- type Output = Content;
-
- fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- Ok((vm.items.text)(self.get().into()))
- }
-}
+ type Output = Value;
-impl ast::Escape {
- fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
- Ok((vm.items.math_atom)(self.get().into()))
+ fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
+ // This can be in markup and math, going through a string ensure
+ // that either text or atom is picked.
+ Ok(Value::Str(self.get().into()))
}
}
impl Eval for ast::Shorthand {
- type Output = Content;
-
- fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- Ok((vm.items.text)(self.get().into()))
- }
-}
-
-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;
-
- fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- Ok((vm.items.symbol)(self.get().into()))
- }
-}
+ type Output = Value;
-impl ast::Symbol {
- fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
- Ok((vm.items.symbol)(EcoString::from(self.get()) + ":op:square".into()))
+ fn eval(&self, _: &mut Vm) -> SourceResult<Self::Output> {
+ // This can be in markup and math, going through a string ensure
+ // that either text or atom is picked.
+ Ok(Value::Str(self.get().into()))
}
}
@@ -513,108 +474,95 @@ impl Eval for ast::TermItem {
}
}
-impl Eval for ast::Math {
+impl Eval for ast::Formula {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- let seq = self
- .exprs()
- .map(|expr| expr.eval_in_math(vm))
- .collect::<SourceResult<_>>()?;
+ let body = self.body().eval(vm)?;
let block = self.block();
- Ok((vm.items.math_formula)(Content::sequence(seq), block))
+ Ok((vm.items.formula)(body, block))
}
}
-impl Eval for ast::Atom {
+impl Eval for ast::Math {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- Ok((vm.items.math_atom)(self.get().clone()))
+ Ok(Content::sequence(
+ self.exprs()
+ .map(|expr| Ok(expr.eval(vm)?.display_in_math()))
+ .collect::<SourceResult<_>>()?,
+ ))
}
}
-impl Eval for ast::Delimited {
+impl Eval for ast::MathAtom {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- let seq = self
- .exprs()
- .map(|expr| expr.eval_in_math(vm))
- .collect::<SourceResult<_>>()?;
- Ok((vm.items.math_delimited)(Content::sequence(seq)))
+ Ok((vm.items.math_atom)(self.get().clone()))
}
}
-impl ast::Delimited {
- fn eval_without_parens(&self, vm: &mut Vm) -> SourceResult<Content> {
- let exprs: Vec<_> = self.exprs().collect();
- let mut slice = exprs.as_slice();
- if let (Some(ast::Expr::Atom(first)), Some(ast::Expr::Atom(last))) =
- (exprs.first(), exprs.last())
- {
- if first.get() == "(" && last.get() == ")" {
- slice = &exprs[1..exprs.len() - 1];
- }
- }
- let seq = slice
- .iter()
- .map(|expr| expr.eval_in_math(vm))
- .collect::<SourceResult<_>>()?;
- Ok((vm.items.math_delimited)(Content::sequence(seq)))
+impl Eval for ast::MathIdent {
+ type Output = Value;
+
+ fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
+ Ok(vm.scopes.get_in_math(self).cloned().at(self.span())?)
}
}
-impl Eval for ast::Script {
+impl Eval for ast::MathAlignPoint {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- let base = self.base().eval_in_math(vm)?;
- let sub = self.sub().map(|expr| expr.eval_without_parens(vm)).transpose()?;
- let sup = self.sup().map(|expr| expr.eval_without_parens(vm)).transpose()?;
- Ok((vm.items.math_script)(base, sub, sup))
+ Ok((vm.items.math_align_point)())
}
}
-impl Eval for ast::Frac {
+impl Eval for ast::MathDelimited {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- let num = self.num().eval_without_parens(vm)?;
- let denom = self.denom().eval_without_parens(vm)?;
- Ok((vm.items.math_frac)(num, denom))
+ let open = self.open().eval(vm)?;
+ let body = self.body().eval(vm)?;
+ let close = self.close().eval(vm)?;
+ Ok((vm.items.math_delimited)(open, body, close))
}
}
-impl Eval for ast::AlignPoint {
+impl Eval for ast::MathScript {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- Ok((vm.items.math_align_point)())
+ let base = self.base().eval(vm)?.display_in_math();
+ let sub = self
+ .sub()
+ .map(|expr| expr.eval(vm).map(Value::display_in_math))
+ .transpose()?;
+ let sup = self
+ .sup()
+ .map(|expr| expr.eval(vm).map(Value::display_in_math))
+ .transpose()?;
+ Ok((vm.items.math_script)(base, sub, sup))
}
}
-impl Eval for ast::Ident {
- type Output = Value;
+impl Eval for ast::MathFrac {
+ type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- let value = vm.scopes.get(self).cloned().at(self.span())?;
- Ok(match value {
- Value::Func(func) => Value::Func(func.spanned(self.span())),
- value => value,
- })
+ let num = self.num().eval(vm)?.display_in_math();
+ let denom = self.denom().eval(vm)?.display_in_math();
+ Ok((vm.items.math_frac)(num, denom))
}
}
-impl ast::Ident {
- fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
- if self.as_untyped().len() == self.len()
- && matches!(vm.scopes.get_in_math(self), Ok(Value::Func(_)) | Err(_))
- {
- Ok((vm.items.symbol)(EcoString::from(self.get()) + ":op".into()))
- } else {
- Ok(vm.scopes.get_in_math(self).at(self.span())?.clone().display_in_math())
- }
+impl Eval for ast::Ident {
+ type Output = Value;
+
+ fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
+ Ok(vm.scopes.get(self).cloned().at(self.span())?)
}
}
@@ -686,12 +634,20 @@ impl Eval for ast::CodeBlock {
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
vm.scopes.enter();
- let output = eval_code(vm, &mut self.exprs())?;
+ let output = self.body().eval(vm)?;
vm.scopes.exit();
Ok(output)
}
}
+impl Eval for ast::Code {
+ type Output = Value;
+
+ fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
+ eval_code(vm, &mut self.exprs())
+ }
+}
+
/// Evaluate a stream of expressions.
fn eval_code(
vm: &mut Vm,
@@ -901,23 +857,9 @@ impl Eval for ast::FieldAccess {
type Output = Value;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- let object = self.target().eval(vm)?;
- let span = self.field().span();
- let field = self.field().take();
-
- Ok(match object {
- Value::Dict(dict) => dict.at(&field).at(span)?.clone(),
- Value::Content(content) => content
- .field(&field)
- .ok_or_else(|| format!("unknown field `{field}`"))
- .at(span)?,
- Value::Module(module) => module.get(&field).cloned().at(span)?,
- v => bail!(
- self.target().span(),
- "expected dictionary or content, found {}",
- v.type_name()
- ),
- })
+ let value = self.target().eval(vm)?;
+ let field = self.field();
+ value.field(&field).at(field.span())
}
}
@@ -926,27 +868,13 @@ impl Eval for ast::FuncCall {
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
let callee = self.callee();
- let callee = callee.eval(vm)?.cast::<Func>().at(callee.span())?;
- let args = self.args().eval(vm)?;
- Self::eval_call(vm, &callee, args, self.span())
- }
-}
-
-impl ast::FuncCall {
- fn eval_in_math(&self, vm: &mut Vm) -> SourceResult<Content> {
- let callee = match self.callee() {
- ast::Expr::Ident(ident) => {
- vm.scopes.get_in_math(&ident).at(ident.span())?.clone()
- }
- expr => expr.eval(vm)?,
- };
+ let callee_span = callee.span();
+ let in_math = matches!(callee, ast::Expr::MathIdent(_));
+ let callee = callee.eval(vm)?;
+ let mut args = self.args().eval(vm)?;
- if let Value::Func(callee) = callee {
- let args = self.args().eval(vm)?;
- Ok(Self::eval_call(vm, &callee, args, self.span())?.display_in_math())
- } else {
+ if in_math && !matches!(callee, Value::Func(_)) {
let mut body = (vm.items.math_atom)('('.into());
- let mut args = self.args().eval(vm)?;
for (i, arg) in args.all::<Content>()?.into_iter().enumerate() {
if i > 0 {
body += (vm.items.math_atom)(','.into());
@@ -954,23 +882,26 @@ impl ast::FuncCall {
body += arg;
}
body += (vm.items.math_atom)(')'.into());
- Ok(callee.display_in_math() + body)
+ return Ok(Value::Content(callee.display_in_math() + body));
}
- }
- fn eval_call(
- vm: &mut Vm,
- callee: &Func,
- args: Args,
- span: Span,
- ) -> SourceResult<Value> {
- if vm.depth >= MAX_CALL_DEPTH {
- bail!(span, "maximum function call depth exceeded");
- }
+ let callee = callee.cast::<Func>().at(callee_span)?;
+ complete_call(vm, &callee, args, self.span())
+ }
+}
- let point = || Tracepoint::Call(callee.name().map(Into::into));
- callee.call(vm, args).trace(vm.world, point, span)
+fn complete_call(
+ vm: &mut Vm,
+ callee: &Func,
+ args: Args,
+ span: Span,
+) -> SourceResult<Value> {
+ if vm.depth >= MAX_CALL_DEPTH {
+ bail!(span, "maximum function call depth exceeded");
}
+
+ let point = || Tracepoint::Call(callee.name().map(Into::into));
+ callee.call(vm, args).trace(vm.world, point, span)
}
impl Eval for ast::MethodCall {
@@ -988,7 +919,7 @@ impl Eval for ast::MethodCall {
if let Value::Func(callee) =
module.get(&method).cloned().at(method.span())?
{
- return ast::FuncCall::eval_call(vm, &callee, args, self.span());
+ return complete_call(vm, &callee, args, self.span());
}
}
@@ -999,7 +930,7 @@ impl Eval for ast::MethodCall {
if let Value::Module(module) = &value {
if let Value::Func(callee) = module.get(&method).at(method.span())? {
- return ast::FuncCall::eval_call(vm, callee, args, self.span());
+ return complete_call(vm, callee, args, self.span());
}
}
@@ -1367,7 +1298,7 @@ impl Eval for ast::ModuleInclude {
}
/// Process an import of a module relative to the current location.
-fn import(vm: &Vm, source: Value, span: Span) -> SourceResult<Module> {
+fn import(vm: &mut Vm, source: Value, span: Span) -> SourceResult<Module> {
let path = match source {
Value::Str(path) => path,
Value::Module(module) => return Ok(module),
@@ -1386,7 +1317,8 @@ fn import(vm: &Vm, source: Value, span: Span) -> SourceResult<Module> {
// Evaluate the file.
let source = vm.world.source(id);
let point = || Tracepoint::Import;
- eval(vm.world, vm.route, source).trace(vm.world, point, span)
+ eval(vm.world, vm.route, TrackedMut::reborrow_mut(&mut vm.tracer), source)
+ .trace(vm.world, point, span)
}
impl Eval for ast::LoopBreak {
@@ -1446,7 +1378,12 @@ impl Access for ast::Expr {
impl Access for ast::Ident {
fn access<'a>(&self, vm: &'a mut Vm) -> SourceResult<&'a mut Value> {
- vm.scopes.get_mut(self).at(self.span())
+ let span = self.span();
+ let value = vm.scopes.get_mut(self).at(span)?;
+ if vm.traced == Some(span) {
+ vm.tracer.trace(value.clone());
+ }
+ Ok(value)
}
}
diff --git a/src/model/func.rs b/src/model/func.rs
index 73b7f1c7..00e59bbd 100644
--- a/src/model/func.rs
+++ b/src/model/func.rs
@@ -505,17 +505,17 @@ mod tests {
fn test_captures() {
// Let binding and function definition.
test("#let x = x", &["x"]);
- test("#let x; {x + y}", &["y"]);
+ test("#let x; #{x + y}", &["y"]);
test("#let f(x, y) = x + y", &[]);
test("#let f(x, y) = f", &[]);
test("#let f = (x, y) => f", &["f"]);
// Closure with different kinds of params.
- test("{(x, y) => x + z}", &["z"]);
- test("{(x: y, z) => x + z}", &["y"]);
- test("{(..x) => x + y}", &["y"]);
- test("{(x, y: x + z) => x + y}", &["x", "z"]);
- test("{x => x; x}", &["x"]);
+ test("#{(x, y) => x + z}", &["z"]);
+ test("#{(x: y, z) => x + z}", &["y"]);
+ test("#{(..x) => x + y}", &["y"]);
+ test("#{(x, y: x + z) => x + y}", &["x", "z"]);
+ test("#{x => x; x}", &["x"]);
// Show rule.
test("#show y: x => x", &["y"]);
@@ -532,7 +532,7 @@ mod tests {
test("#import x + y: x, y, z", &["x", "y"]);
// Blocks.
- test("{ let x = 1; { let y = 2; y }; x + y }", &["y"]);
- test("[#let x = 1]#x", &["x"]);
+ test("#{ let x = 1; { let y = 2; y }; x + y }", &["y"]);
+ test("#[#let x = 1]#x", &["x"]);
}
}
diff --git a/src/model/library.rs b/src/model/library.rs
index a64b0263..cd9db10f 100644
--- a/src/model/library.rs
+++ b/src/model/library.rs
@@ -68,19 +68,19 @@ pub struct LangItems {
/// An item in a term list: `/ Term: Details`.
pub term_item: fn(term: Content, description: Content) -> Content,
/// A mathematical formula: `$x$`, `$ x^2 $`.
- pub math_formula: fn(body: Content, block: bool) -> Content,
- /// A subsection in a math formula that is surrounded by matched delimiters:
- /// `[x + y]`.
- pub math_delimited: fn(body: Content) -> Content,
+ pub formula: fn(body: Content, block: bool) -> Content,
/// An atom in a formula: `x`, `+`, `12`.
pub math_atom: fn(atom: EcoString) -> Content,
+ /// An alignment point in a formula: `&`.
+ pub math_align_point: fn() -> Content,
+ /// A subsection in a math formula that is surrounded by matched delimiters:
+ /// `[x + y]`.
+ pub math_delimited: fn(open: Content, body: Content, close: Content) -> Content,
/// A base with optional sub- and superscripts in a formula: `a_1^2`.
pub math_script:
fn(base: Content, sub: Option<Content>, sup: Option<Content>) -> Content,
/// A fraction in a formula: `x/2`.
pub math_frac: fn(num: Content, denom: Content) -> Content,
- /// An alignment point in a formula: `&`.
- pub math_align_point: fn() -> Content,
}
impl Debug for LangItems {
@@ -108,7 +108,7 @@ impl Hash for LangItems {
self.list_item.hash(state);
self.enum_item.hash(state);
self.term_item.hash(state);
- self.math_formula.hash(state);
+ self.formula.hash(state);
self.math_atom.hash(state);
self.math_script.hash(state);
self.math_frac.hash(state);