summaryrefslogtreecommitdiff
path: root/src/model
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2023-01-28 15:35:56 +0100
committerLaurenz <laurmaedje@gmail.com>2023-01-28 15:36:32 +0100
commit4809e685a231a3ade2c78b75685ee859196c38c1 (patch)
treee3141236cca536c31c6ef4a6df6d218c16ba5a94 /src/model
parent28c554ec2185a15e22f0408ce485ed4afe035e03 (diff)
More capable math calls
Diffstat (limited to 'src/model')
-rw-r--r--src/model/content.rs10
-rw-r--r--src/model/dict.rs21
-rw-r--r--src/model/eval.rs40
-rw-r--r--src/model/func.rs10
-rw-r--r--src/model/library.rs3
-rw-r--r--src/model/methods.rs76
-rw-r--r--src/model/value.rs14
7 files changed, 93 insertions, 81 deletions
diff --git a/src/model/content.rs b/src/model/content.rs
index df910a58..143f97aa 100644
--- a/src/model/content.rs
+++ b/src/model/content.rs
@@ -183,12 +183,18 @@ impl Content {
}
/// Whether the contained node is of type `T`.
- pub fn is<T: 'static>(&self) -> bool {
+ pub fn is<T>(&self) -> bool
+ where
+ T: Capable + 'static,
+ {
(*self.obj).as_any().is::<T>()
}
/// Cast to `T` if the contained node is of type `T`.
- pub fn to<T: 'static>(&self) -> Option<&T> {
+ pub fn to<T>(&self) -> Option<&T>
+ where
+ T: Capable + 'static,
+ {
(*self.obj).as_any().downcast_ref::<T>()
}
diff --git a/src/model/dict.rs b/src/model/dict.rs
index 76d194a8..7165fbbe 100644
--- a/src/model/dict.rs
+++ b/src/model/dict.rs
@@ -3,8 +3,8 @@ use std::fmt::{self, Debug, Formatter, Write};
use std::ops::{Add, AddAssign};
use std::sync::Arc;
-use super::{Args, Array, Func, Str, Value, Vm};
-use crate::diag::{bail, SourceResult, StrResult};
+use super::{array, Array, Str, Value};
+use crate::diag::StrResult;
use crate::syntax::is_ident;
use crate::util::{format_eco, ArcExt, EcoString};
@@ -104,17 +104,12 @@ impl Dict {
self.0.values().cloned().collect()
}
- /// Transform each pair in the dictionary with a function.
- pub fn map(&self, vm: &mut Vm, func: Func) -> SourceResult<Array> {
- if func.argc().map_or(false, |count| count != 2) {
- bail!(func.span(), "function must have exactly two parameters");
- }
- self.iter()
- .map(|(key, value)| {
- let args =
- Args::new(func.span(), [Value::Str(key.clone()), value.clone()]);
- func.call(vm, args)
- })
+ /// Return the values of the dictionary as an array of pairs (arrays of
+ /// length two).
+ pub fn pairs(&self) -> Array {
+ self.0
+ .iter()
+ .map(|(k, v)| Value::Array(array![k.clone(), v.clone()]))
.collect()
}
diff --git a/src/model/eval.rs b/src/model/eval.rs
index 44f08a76..b63069bf 100644
--- a/src/model/eval.rs
+++ b/src/model/eval.rs
@@ -344,7 +344,6 @@ impl Eval for ast::Expr {
Self::Term(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),
@@ -552,21 +551,13 @@ impl Eval for ast::Math {
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
Ok(Content::sequence(
self.exprs()
- .map(|expr| Ok(expr.eval(vm)?.display_in_math()))
+ .map(|expr| Ok(expr.eval(vm)?.display()))
.collect::<SourceResult<_>>()?,
)
.spanned(self.span()))
}
}
-impl Eval for ast::MathAtom {
- type Output = Content;
-
- fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- Ok((vm.items.math_atom)(self.get().clone()))
- }
-}
-
impl Eval for ast::MathIdent {
type Output = Value;
@@ -587,9 +578,9 @@ impl Eval for ast::MathDelimited {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- let open = self.open().eval(vm)?.display_in_math();
+ let open = self.open().eval(vm)?.display();
let body = self.body().eval(vm)?;
- let close = self.close().eval(vm)?.display_in_math();
+ let close = self.close().eval(vm)?.display();
Ok((vm.items.math_delimited)(open, body, close))
}
}
@@ -598,16 +589,13 @@ impl Eval for ast::MathAttach {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- let base = self.base().eval(vm)?.display_in_math();
- let sub = self
+ let base = self.base().eval(vm)?.display();
+ let bottom = self
.bottom()
- .map(|expr| expr.eval(vm).map(Value::display_in_math))
- .transpose()?;
- let sup = self
- .top()
- .map(|expr| expr.eval(vm).map(Value::display_in_math))
+ .map(|expr| expr.eval(vm).map(Value::display))
.transpose()?;
- Ok((vm.items.math_attach)(base, sub, sup))
+ let top = self.top().map(|expr| expr.eval(vm).map(Value::display)).transpose()?;
+ Ok((vm.items.math_attach)(base, bottom, top))
}
}
@@ -615,8 +603,8 @@ impl Eval for ast::MathFrac {
type Output = Content;
fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
- let num = self.num().eval(vm)?.display_in_math();
- let denom = self.denom().eval(vm)?.display_in_math();
+ let num = self.num().eval(vm)?.display();
+ let denom = self.denom().eval(vm)?.display();
Ok((vm.items.math_frac)(num, denom))
}
}
@@ -945,15 +933,15 @@ impl Eval for ast::FuncCall {
}
}
- let mut body = (vm.items.math_atom)('('.into());
+ let mut body = (vm.items.text)('('.into());
for (i, arg) in args.all::<Content>()?.into_iter().enumerate() {
if i > 0 {
- body += (vm.items.math_atom)(','.into());
+ body += (vm.items.text)(','.into());
}
body += arg;
}
- body += (vm.items.math_atom)(')'.into());
- return Ok(Value::Content(callee.display_in_math() + body));
+ body += (vm.items.text)(')'.into());
+ return Ok(Value::Content(callee.display() + body));
}
let callee = callee.cast::<Func>().at(callee_span)?;
diff --git a/src/model/func.rs b/src/model/func.rs
index 8cf3ea99..1ccb0107 100644
--- a/src/model/func.rs
+++ b/src/model/func.rs
@@ -389,6 +389,7 @@ impl<'a> CapturesVisitor<'a> {
// actually bind a new name are handled below (individually through
// the expressions that contain them).
Some(ast::Expr::Ident(ident)) => self.capture(ident),
+ Some(ast::Expr::MathIdent(ident)) => self.capture_in_math(ident),
// Code and content blocks create a scope.
Some(ast::Expr::Code(_) | ast::Expr::Content(_)) => {
@@ -483,6 +484,15 @@ impl<'a> CapturesVisitor<'a> {
}
}
}
+
+ /// Capture a variable in math mode if it isn't internal.
+ fn capture_in_math(&mut self, ident: ast::MathIdent) {
+ if self.internal.get(&ident).is_err() {
+ if let Ok(value) = self.external.get_in_math(&ident) {
+ self.captures.define_captured(ident.take(), value.clone());
+ }
+ }
+ }
}
#[cfg(test)]
diff --git a/src/model/library.rs b/src/model/library.rs
index 4208a4c7..c87ca095 100644
--- a/src/model/library.rs
+++ b/src/model/library.rs
@@ -67,8 +67,6 @@ pub struct LangItems {
pub term_item: fn(term: Content, description: Content) -> Content,
/// A mathematical formula: `$x$`, `$ x^2 $`.
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,
/// Matched delimiters surrounding math in a formula: `[x + y]`.
@@ -110,7 +108,6 @@ impl Hash for LangItems {
self.enum_item.hash(state);
self.term_item.hash(state);
self.formula.hash(state);
- self.math_atom.hash(state);
self.math_align_point.hash(state);
self.math_delimited.hash(state);
self.math_attach.hash(state);
diff --git a/src/model/methods.rs b/src/model/methods.rs
index 1671a5c4..5da64fa2 100644
--- a/src/model/methods.rs
+++ b/src/model/methods.rs
@@ -107,7 +107,7 @@ pub fn call(
"at" => dict.at(&args.expect::<Str>("key")?).cloned().at(span)?,
"keys" => Value::Array(dict.keys()),
"values" => Value::Array(dict.values()),
- "pairs" => Value::Array(dict.map(vm, args.expect("function")?)?),
+ "pairs" => Value::Array(dict.pairs()),
_ => return missing(),
},
@@ -211,35 +211,61 @@ fn missing_method(type_name: &str, method: &str) -> String {
format!("type {type_name} has no method `{method}`")
}
-/// List the available methods for a type.
-pub fn methods_on(type_name: &str) -> &[&'static str] {
+/// List the available methods for a type and whether they take arguments.
+pub fn methods_on(type_name: &str) -> &[(&'static str, bool)] {
match type_name {
- "color" => &["lighten", "darken", "negate"],
+ "color" => &[("lighten", true), ("darken", true), ("negate", false)],
"string" => &[
- "len",
- "at",
- "contains",
- "ends-with",
- "find",
- "first",
- "last",
- "match",
- "matches",
- "position",
- "replace",
- "slice",
- "split",
- "starts-with",
- "trim",
+ ("len", false),
+ ("at", true),
+ ("contains", true),
+ ("ends-with", true),
+ ("find", true),
+ ("first", false),
+ ("last", false),
+ ("match", true),
+ ("matches", true),
+ ("position", true),
+ ("replace", true),
+ ("slice", true),
+ ("split", true),
+ ("starts-with", true),
+ ("trim", true),
],
"array" => &[
- "all", "any", "at", "contains", "filter", "find", "first", "flatten", "fold",
- "insert", "join", "last", "len", "map", "pop", "position", "push", "remove",
- "rev", "slice", "sorted",
+ ("all", true),
+ ("any", true),
+ ("at", true),
+ ("contains", true),
+ ("filter", true),
+ ("find", true),
+ ("first", false),
+ ("flatten", false),
+ ("fold", true),
+ ("insert", true),
+ ("join", true),
+ ("last", false),
+ ("len", false),
+ ("map", true),
+ ("pop", false),
+ ("position", true),
+ ("push", true),
+ ("remove", true),
+ ("rev", false),
+ ("slice", true),
+ ("sorted", false),
],
- "dictionary" => &["at", "insert", "keys", "len", "pairs", "remove", "values"],
- "function" => &["where", "with"],
- "arguments" => &["named", "pos"],
+ "dictionary" => &[
+ ("at", true),
+ ("insert", true),
+ ("keys", false),
+ ("len", false),
+ ("pairs", false),
+ ("remove", true),
+ ("values", false),
+ ],
+ "function" => &[("where", true), ("with", true)],
+ "arguments" => &[("named", false), ("pos", false)],
_ => &[],
}
}
diff --git a/src/model/value.rs b/src/model/value.rs
index 4b9fa5f7..ea17349e 100644
--- a/src/model/value.rs
+++ b/src/model/value.rs
@@ -145,16 +145,6 @@ impl Value {
}
}
- /// 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::Symbol(v) => item!(math_atom)(v.get().into()),
- _ => self.display(),
- }
- }
-
/// Try to extract documentation for the value.
pub fn docs(&self) -> Option<&'static str> {
match self {
@@ -447,8 +437,8 @@ primitive! { Label: "label", Label }
primitive! { Content: "content",
Content,
None => Content::empty(),
- Symbol(symbol) => item!(text)(symbol.get().into()),
- Str(text) => item!(text)(text.into())
+ Symbol(v) => item!(text)(v.get().into()),
+ Str(v) => item!(text)(v.into())
}
primitive! { Array: "array", Array }
primitive! { Dict: "dictionary", Dict }