diff options
| author | Laurenz <laurmaedje@gmail.com> | 2023-01-28 15:35:56 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2023-01-28 15:36:32 +0100 |
| commit | 4809e685a231a3ade2c78b75685ee859196c38c1 (patch) | |
| tree | e3141236cca536c31c6ef4a6df6d218c16ba5a94 /src/model | |
| parent | 28c554ec2185a15e22f0408ce485ed4afe035e03 (diff) | |
More capable math calls
Diffstat (limited to 'src/model')
| -rw-r--r-- | src/model/content.rs | 10 | ||||
| -rw-r--r-- | src/model/dict.rs | 21 | ||||
| -rw-r--r-- | src/model/eval.rs | 40 | ||||
| -rw-r--r-- | src/model/func.rs | 10 | ||||
| -rw-r--r-- | src/model/library.rs | 3 | ||||
| -rw-r--r-- | src/model/methods.rs | 76 | ||||
| -rw-r--r-- | src/model/value.rs | 14 |
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 } |
