From f9197dcfef11c4c054a460c80ff6023dae6f1f2a Mon Sep 17 00:00:00 2001 From: Laurenz Date: Tue, 9 Feb 2021 22:56:44 +0100 Subject: [PATCH] =?UTF-8?q?Add=20arguments=20value=20=F0=9F=8F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/eval/call.rs | 197 --------------------- src/eval/mod.rs | 50 +++++- src/eval/value.rs | 298 ++++++++++++++++++++++++++++---- src/exec/mod.rs | 28 +-- src/library/extend.rs | 2 +- src/library/insert.rs | 4 +- src/library/layout.rs | 28 +-- src/library/style.rs | 16 +- src/parse/collection.rs | 36 ++-- src/parse/mod.rs | 4 +- src/prelude.rs | 4 +- src/pretty.rs | 4 +- src/syntax/expr.rs | 16 +- src/syntax/node.rs | 1 + src/syntax/visit.rs | 6 +- tests/lang/ref/call-args.png | Bin 3713 -> 3295 bytes tests/lang/ref/call-bracket.png | Bin 1897 -> 1700 bytes tests/lang/ref/call-chain.png | Bin 10171 -> 8692 bytes tests/lang/ref/call-invalid.png | Bin 1905 -> 1706 bytes tests/lang/ref/comment.png | Bin 1215 -> 1168 bytes tests/lang/ref/if-branch.png | Bin 2040 -> 1959 bytes tests/lang/ref/repr.png | Bin 8440 -> 8355 bytes tests/typeset.rs | 28 +-- 23 files changed, 396 insertions(+), 326 deletions(-) delete mode 100644 src/eval/call.rs diff --git a/src/eval/call.rs b/src/eval/call.rs deleted file mode 100644 index 1a80e15a3..000000000 --- a/src/eval/call.rs +++ /dev/null @@ -1,197 +0,0 @@ -use super::*; - -impl Eval for ExprCall { - type Output = Value; - - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - let callee = self.callee.eval(ctx); - - if let Value::Func(func) = callee { - let func = func.clone(); - let mut args = self.args.eval(ctx); - let returned = func(ctx, &mut args); - args.finish(ctx); - - return returned; - } else if callee != Value::Error { - ctx.diag(error!( - self.callee.span(), - "expected function, found {}", - callee.type_name(), - )); - } - - Value::Error - } -} - -impl Eval for ExprArgs { - type Output = Args; - - fn eval(&self, ctx: &mut EvalContext) -> Self::Output { - let mut pos = vec![]; - let mut named = vec![]; - - for arg in &self.items { - match arg { - Argument::Pos(expr) => { - pos.push(expr.eval(ctx).with_span(expr.span())); - } - Argument::Named(Named { name, expr }) => { - named.push(( - name.string.clone().with_span(name.span), - expr.eval(ctx).with_span(expr.span()), - )); - } - } - } - - Args { span: self.span, pos, named } - } -} - -/// Evaluated arguments to a function. -#[derive(Debug)] -pub struct Args { - /// The span of the whole argument list. - pub span: Span, - /// The positional arguments. - pub pos: SpanVec, - /// The named arguments. - pub named: Vec<(Spanned, Spanned)>, -} - -impl Args { - /// Find and remove the first convertible positional argument. - pub fn find(&mut self, ctx: &mut EvalContext) -> Option - where - T: Cast>, - { - (0 .. self.pos.len()).find_map(move |i| try_cast(ctx, &mut self.pos, i)) - } - - /// Find and remove the first convertible positional argument, producing an - /// error if no match was found. - pub fn require(&mut self, ctx: &mut EvalContext, what: &str) -> Option - where - T: Cast>, - { - let found = self.find(ctx); - if found.is_none() { - ctx.diag(error!(self.span, "missing argument: {}", what)); - } - found - } - - /// Filter out and remove all convertible positional arguments. - pub fn filter<'a, 'b: 'a, T>( - &'a mut self, - ctx: &'a mut EvalContext<'b>, - ) -> impl Iterator + Captures<'a> + Captures<'b> - where - T: Cast>, - { - let mut i = 0; - std::iter::from_fn(move || { - while i < self.pos.len() { - if let Some(val) = try_cast(ctx, &mut self.pos, i) { - return Some(val); - } - i += 1; - } - None - }) - } - - /// Convert and remove the value for the given named argument, producing an - /// error if the conversion fails. - pub fn get(&mut self, ctx: &mut EvalContext, name: &str) -> Option - where - T: Cast>, - { - let index = self.named.iter().position(|(k, _)| k.v.as_str() == name)?; - let value = self.named.remove(index).1; - cast(ctx, value) - } - - /// Drain all remainings arguments into an array and a dictionary. - pub fn drain(&mut self) -> (ValueArray, ValueDict) { - let array = self.pos.drain(..).map(|s| s.v).collect(); - let dict = self.named.drain(..).map(|(k, v)| (k.v, v.v)).collect(); - (array, dict) - } - - /// Produce "unexpected argument" errors for all remaining arguments. - pub fn finish(self, ctx: &mut EvalContext) { - let a = self.pos.iter().map(|v| v.as_ref()); - let b = self.named.iter().map(|(k, v)| (&v.v).with_span(k.span.join(v.span))); - for value in a.chain(b) { - if value.v != &Value::Error { - ctx.diag(error!(value.span, "unexpected argument")); - } - } - } -} - -// This is a workaround because `-> impl Trait + 'a + 'b` does not work. -// -// See also: https://github.com/rust-lang/rust/issues/49431 -#[doc(hidden)] -pub trait Captures<'a> {} -impl<'a, T: ?Sized> Captures<'a> for T {} - -/// Cast the value into `T`, generating an error if the conversion fails. -fn cast(ctx: &mut EvalContext, value: Spanned) -> Option -where - T: Cast>, -{ - let span = value.span; - match T::cast(value) { - CastResult::Ok(t) => Some(t), - CastResult::Warn(t, m) => { - ctx.diag(warning!(span, "{}", m)); - Some(t) - } - CastResult::Err(value) => { - ctx.diag(error!( - span, - "expected {}, found {}", - T::TYPE_NAME, - value.v.type_name() - )); - None - } - } -} - -/// Try to cast the value in the slot into `T`, putting it back if the -/// conversion fails. -fn try_cast( - ctx: &mut EvalContext, - vec: &mut Vec>, - i: usize, -) -> Option -where - T: Cast>, -{ - // Replace with error placeholder when conversion works since error values - // are ignored when generating "unexpected argument" errors. - let slot = &mut vec[i]; - let value = std::mem::replace(slot, Spanned::zero(Value::None)); - let span = value.span; - match T::cast(value) { - CastResult::Ok(t) => { - vec.remove(i); - Some(t) - } - CastResult::Warn(t, m) => { - vec.remove(i); - ctx.diag(warning!(span, "{}", m)); - Some(t) - } - CastResult::Err(value) => { - *slot = value; - None - } - } -} diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 2390a84f8..4f0beb34b 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -2,12 +2,10 @@ #[macro_use] mod value; -mod call; mod capture; mod ops; mod scope; -pub use call::*; pub use capture::*; pub use scope::*; pub use value::*; @@ -344,6 +342,54 @@ impl ExprBinary { } } +impl Eval for ExprCall { + type Output = Value; + + fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + let callee = self.callee.eval(ctx); + + if let Value::Func(func) = callee { + let func = func.clone(); + let mut args = self.args.eval(ctx); + let returned = func(ctx, &mut args); + args.finish(ctx); + + return returned; + } else if callee != Value::Error { + ctx.diag(error!( + self.callee.span(), + "expected function, found {}", + callee.type_name(), + )); + } + + Value::Error + } +} + +impl Eval for ExprArgs { + type Output = ValueArgs; + + fn eval(&self, ctx: &mut EvalContext) -> Self::Output { + let items = self + .items + .iter() + .map(|arg| match arg { + ExprArg::Pos(expr) => ValueArg { + name: None, + value: expr.eval(ctx).with_span(expr.span()), + }, + ExprArg::Named(Named { name, expr }) => ValueArg { + name: Some(name.string.clone().with_span(name.span)), + value: expr.eval(ctx).with_span(expr.span()), + }, + }) + .collect(); + + ValueArgs { span: self.span, items } + } +} + impl Eval for ExprLet { type Output = Value; diff --git a/src/eval/value.rs b/src/eval/value.rs index dd1221e5b..94477aa5b 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -42,6 +42,8 @@ pub enum Value { Template(ValueTemplate), /// An executable function. Func(ValueFunc), + /// Arguments to a function. + Args(ValueArgs), /// Any object. Any(ValueAny), /// The result of invalid operations. @@ -50,11 +52,11 @@ pub enum Value { impl Value { /// Create a new template value consisting of a single dynamic node. - pub fn template(f: F) -> Self + pub fn template(name: impl Into, f: F) -> Self where F: Fn(&mut ExecContext) + 'static, { - Self::Template(vec![TemplateNode::Any(TemplateAny::new(f))]) + Self::Template(vec![TemplateNode::Any(TemplateAny::new(name, f))]) } /// The name of the stored value's type. @@ -74,6 +76,7 @@ impl Value { Self::Dict(_) => ValueDict::TYPE_NAME, Self::Template(_) => ValueTemplate::TYPE_NAME, Self::Func(_) => ValueFunc::TYPE_NAME, + Self::Args(_) => ValueArgs::TYPE_NAME, Self::Any(v) => v.type_name(), Self::Error => "error", } @@ -111,8 +114,9 @@ impl Pretty for Value { Value::Dict(v) => v.pretty(p), Value::Template(v) => v.pretty(p), Value::Func(v) => v.pretty(p), + Value::Args(v) => v.pretty(p), Value::Any(v) => v.pretty(p), - Value::Error => p.push_str("(error)"), + Value::Error => p.push_str(""), } } } @@ -194,16 +198,17 @@ impl Pretty for TemplateNode { /// A reference-counted dynamic template node (can implement custom behaviour). #[derive(Clone)] pub struct TemplateAny { + name: String, f: Rc, } impl TemplateAny { /// Create a new dynamic template value from a rust function or closure. - pub fn new(f: F) -> Self + pub fn new(name: impl Into, f: F) -> Self where F: Fn(&mut ExecContext) + 'static, { - Self { f: Rc::new(f) } + Self { name: name.into(), f: Rc::new(f) } } } @@ -224,7 +229,9 @@ impl Deref for TemplateAny { impl Pretty for TemplateAny { fn pretty(&self, p: &mut Printer) { - p.push_str(""); + p.push('<'); + p.push_str(&self.name); + p.push('>'); } } @@ -238,14 +245,14 @@ impl Debug for TemplateAny { #[derive(Clone)] pub struct ValueFunc { name: String, - f: Rc Value>, + f: Rc Value>, } impl ValueFunc { /// Create a new function value from a rust function or closure. pub fn new(name: impl Into, f: F) -> Self where - F: Fn(&mut EvalContext, &mut Args) -> Value + 'static, + F: Fn(&mut EvalContext, &mut ValueArgs) -> Value + 'static, { Self { name: name.into(), f: Rc::new(f) } } @@ -259,7 +266,7 @@ impl PartialEq for ValueFunc { } impl Deref for ValueFunc { - type Target = dyn Fn(&mut EvalContext, &mut Args) -> Value; + type Target = dyn Fn(&mut EvalContext, &mut ValueArgs) -> Value; fn deref(&self) -> &Self::Target { self.f.as_ref() @@ -268,7 +275,9 @@ impl Deref for ValueFunc { impl Pretty for ValueFunc { fn pretty(&self, p: &mut Printer) { + p.push('<'); p.push_str(&self.name); + p.push('>'); } } @@ -278,6 +287,180 @@ impl Debug for ValueFunc { } } +/// Evaluated arguments to a function. +#[derive(Debug, Clone, PartialEq)] +pub struct ValueArgs { + /// The span of the whole argument list. + pub span: Span, + /// The arguments. + pub items: Vec, +} + +impl ValueArgs { + /// Find and remove the first convertible positional argument. + pub fn find(&mut self, ctx: &mut EvalContext) -> Option + where + T: Cast>, + { + (0 .. self.items.len()).find_map(move |i| self.try_take(ctx, i)) + } + + /// Find and remove the first convertible positional argument, producing an + /// error if no match was found. + pub fn require(&mut self, ctx: &mut EvalContext, what: &str) -> Option + where + T: Cast>, + { + let found = self.find(ctx); + if found.is_none() { + ctx.diag(error!(self.span, "missing argument: {}", what)); + } + found + } + + /// Filter out and remove all convertible positional arguments. + pub fn filter<'a, 'b: 'a, T>( + &'a mut self, + ctx: &'a mut EvalContext<'b>, + ) -> impl Iterator + Captures<'a> + Captures<'b> + where + T: Cast>, + { + let mut i = 0; + std::iter::from_fn(move || { + while i < self.items.len() { + if let Some(val) = self.try_take(ctx, i) { + return Some(val); + } + i += 1; + } + None + }) + } + + /// Convert and remove the value for the given named argument, producing an + /// error if the conversion fails. + pub fn get(&mut self, ctx: &mut EvalContext, name: &str) -> Option + where + T: Cast>, + { + let index = self + .items + .iter() + .position(|arg| arg.name.as_ref().map(|s| s.v.as_str()) == Some(name))?; + + let value = self.items.remove(index).value; + self.cast(ctx, value) + } + + /// Produce "unexpected argument" errors for all remaining arguments. + pub fn finish(self, ctx: &mut EvalContext) { + for arg in &self.items { + if arg.value.v != Value::Error { + ctx.diag(error!(arg.span(), "unexpected argument")); + } + } + } + + /// Cast the value into `T`, generating an error if the conversion fails. + fn cast(&self, ctx: &mut EvalContext, value: Spanned) -> Option + where + T: Cast>, + { + let span = value.span; + match T::cast(value) { + CastResult::Ok(t) => Some(t), + CastResult::Warn(t, m) => { + ctx.diag(warning!(span, "{}", m)); + Some(t) + } + CastResult::Err(value) => { + ctx.diag(error!( + span, + "expected {}, found {}", + T::TYPE_NAME, + value.v.type_name() + )); + None + } + } + } + + /// Try to take and cast a positional argument in the i'th slot into `T`, + /// putting it back if the conversion fails. + fn try_take(&mut self, ctx: &mut EvalContext, i: usize) -> Option + where + T: Cast>, + { + let slot = &mut self.items[i]; + if slot.name.is_some() { + return None; + } + + let value = std::mem::replace(&mut slot.value, Spanned::zero(Value::None)); + let span = value.span; + match T::cast(value) { + CastResult::Ok(t) => { + self.items.remove(i); + Some(t) + } + CastResult::Warn(t, m) => { + self.items.remove(i); + ctx.diag(warning!(span, "{}", m)); + Some(t) + } + CastResult::Err(value) => { + slot.value = value; + None + } + } + } +} + +impl Pretty for ValueArgs { + fn pretty(&self, p: &mut Printer) { + p.push('<'); + p.join(&self.items, ", ", |item, p| item.pretty(p)); + p.push('>'); + } +} + +// This is a workaround because `-> impl Trait + 'a + 'b` does not work. +// +// See also: https://github.com/rust-lang/rust/issues/49431 +#[doc(hidden)] +pub trait Captures<'a> {} +impl<'a, T: ?Sized> Captures<'a> for T {} + +/// An argument to a function call: `12` or `draw: false`. +#[derive(Debug, Clone, PartialEq)] +pub struct ValueArg { + /// The name of the argument (`None` for positional arguments). + pub name: Option>, + /// The value of the argument. + pub value: Spanned, +} + +impl ValueArg { + /// The source code location. + pub fn span(&self) -> Span { + match &self.name { + Some(name) => name.span.join(self.value.span), + None => self.value.span, + } + } +} + +impl Pretty for ValueArg { + fn pretty(&self, p: &mut Printer) { + if let Some(name) = &self.name { + p.push_str(&name.v); + p.push_str(": "); + } + self.value.v.pretty(p); + } +} + /// A wrapper around a dynamic value. pub struct ValueAny(Box); @@ -454,7 +637,7 @@ where } } -macro_rules! impl_primitive { +macro_rules! primitive { ($type:ty: $type_name:literal, $variant:path @@ -482,28 +665,29 @@ macro_rules! impl_primitive { }; } -impl_primitive! { bool: "boolean", Value::Bool } -impl_primitive! { i64: "integer", Value::Int } -impl_primitive! { +primitive! { bool: "boolean", Value::Bool } +primitive! { i64: "integer", Value::Int } +primitive! { f64: "float", Value::Float, Value::Int(v) => v as f64, } -impl_primitive! { Length: "length", Value::Length } -impl_primitive! { Angle: "angle", Value::Angle } -impl_primitive! { Relative: "relative", Value::Relative } -impl_primitive! { +primitive! { Length: "length", Value::Length } +primitive! { Angle: "angle", Value::Angle } +primitive! { Relative: "relative", Value::Relative } +primitive! { Linear: "linear", Value::Linear, Value::Length(v) => v.into(), Value::Relative(v) => v.into(), } -impl_primitive! { Color: "color", Value::Color } -impl_primitive! { String: "string", Value::Str } -impl_primitive! { ValueArray: "array", Value::Array } -impl_primitive! { ValueDict: "dictionary", Value::Dict } -impl_primitive! { ValueTemplate: "template", Value::Template } -impl_primitive! { ValueFunc: "function", Value::Func } +primitive! { Color: "color", Value::Color } +primitive! { String: "string", Value::Str } +primitive! { ValueArray: "array", Value::Array } +primitive! { ValueDict: "dictionary", Value::Dict } +primitive! { ValueTemplate: "template", Value::Template } +primitive! { ValueFunc: "function", Value::Func } +primitive! { ValueArgs: "arguments", Value::Args } impl From<&str> for Value { fn from(v: &str) -> Self { @@ -519,11 +703,23 @@ impl From for Value { /// Make a type usable as a [`Value`]. /// -/// Given a type `T`, this always implements the following traits: +/// Given a type `T`, this implements the following traits: /// - [`Type`] for `T`, /// - [`Cast`](Cast) for `T`. +/// +/// # Example +/// Make a type `FontFamily` that can be cast from a [`Value::Any`] variant +/// containing a `FontFamily` or from a string. +/// ``` +/// # use typst::typify; +/// # enum FontFamily { Named(String) } +/// typify! { +/// FontFamily: "font family", +/// Value::Str(string) => Self::Named(string.to_lowercase()) +/// } +/// ``` #[macro_export] -macro_rules! impl_type { +macro_rules! typify { ($type:ty: $type_name:literal $(, $pattern:pat => $out:expr)* @@ -575,23 +771,19 @@ mod tests { } #[test] - fn test_pretty_print_simple_values() { + fn test_pretty_print_value() { + // Simple values. test_pretty(Value::None, "none"); test_pretty(false, "false"); - test_pretty(12.4, "12.4"); + test_pretty(12, "12"); + test_pretty(3.14, "3.14"); test_pretty(Length::pt(5.5), "5.5pt"); test_pretty(Angle::deg(90.0), "90.0deg"); test_pretty(Relative::ONE / 2.0, "50.0%"); test_pretty(Relative::new(0.3) + Length::cm(2.0), "30.0% + 2.0cm"); test_pretty(Color::Rgba(RgbaColor::new(1, 1, 1, 0xff)), "#010101"); test_pretty("hello", r#""hello""#); - test_pretty(ValueFunc::new("nil", |_, _| Value::None), "nil"); - test_pretty(ValueAny::new(1), "1"); - test_pretty(Value::Error, "(error)"); - } - #[test] - fn test_pretty_print_collections() { // Array. test_pretty(Value::Array(vec![]), "()"); test_pretty(vec![Value::None], "(none,)"); @@ -599,9 +791,47 @@ mod tests { // Dictionary. let mut dict = BTreeMap::new(); + test_pretty(dict.clone(), "(:)"); dict.insert("one".into(), Value::Int(1)); + test_pretty(dict.clone(), "(one: 1)"); dict.insert("two".into(), Value::Bool(false)); - test_pretty(BTreeMap::new(), "(:)"); test_pretty(dict, "(one: 1, two: false)"); + + // Template. + test_pretty( + vec![ + TemplateNode::Tree { + tree: Rc::new(vec![Node::Strong]), + map: HashMap::new(), + }, + TemplateNode::Any(TemplateAny::new("example", |_| {})), + ], + "[*]", + ); + + // Function and arguments. + test_pretty(ValueFunc::new("nil", |_, _| Value::None), ""); + test_pretty( + ValueArgs { + span: Span::ZERO, + items: vec![ + ValueArg { + name: Some(Spanned::zero("a".into())), + value: Spanned::zero(Value::Int(1)), + }, + ValueArg { + name: None, + value: Spanned::zero(Value::Int(2)), + }, + ], + }, + "", + ); + + // Any. + test_pretty(ValueAny::new(1), "1"); + + // Error. + test_pretty(Value::Error, ""); } } diff --git a/src/exec/mod.rs b/src/exec/mod.rs index 25edcce3c..c323cf491 100644 --- a/src/exec/mod.rs +++ b/src/exec/mod.rs @@ -59,17 +59,23 @@ pub trait ExecWith { impl ExecWith for Tree { fn exec_with(&self, ctx: &mut ExecContext, map: &ExprMap) { for node in self { - match node { - Node::Text(text) => ctx.push_text(text), - Node::Space => ctx.push_space(), - Node::Linebreak => ctx.apply_linebreak(), - Node::Parbreak => ctx.apply_parbreak(), - Node::Strong => ctx.state.font.strong ^= true, - Node::Emph => ctx.state.font.emph ^= true, - Node::Heading(heading) => heading.exec_with(ctx, map), - Node::Raw(raw) => raw.exec(ctx), - Node::Expr(expr) => map[&(expr as *const _)].exec(ctx), - } + node.exec_with(ctx, map); + } + } +} + +impl ExecWith for Node { + fn exec_with(&self, ctx: &mut ExecContext, map: &ExprMap) { + match self { + Node::Text(text) => ctx.push_text(text), + Node::Space => ctx.push_space(), + Node::Linebreak => ctx.apply_linebreak(), + Node::Parbreak => ctx.apply_parbreak(), + Node::Strong => ctx.state.font.strong ^= true, + Node::Emph => ctx.state.font.emph ^= true, + Node::Heading(heading) => heading.exec_with(ctx, map), + Node::Raw(raw) => raw.exec(ctx), + Node::Expr(expr) => map[&(expr as *const _)].exec(ctx), } } } diff --git a/src/library/extend.rs b/src/library/extend.rs index e388c9c84..6396274e0 100644 --- a/src/library/extend.rs +++ b/src/library/extend.rs @@ -7,7 +7,7 @@ use crate::prelude::*; /// /// # Return value /// The name of the value's type as a string. -pub fn type_(ctx: &mut EvalContext, args: &mut Args) -> Value { +pub fn type_(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { if let Some(value) = args.require::(ctx, "value") { value.type_name().into() } else { diff --git a/src/library/insert.rs b/src/library/insert.rs index eff54e914..4f0f64898 100644 --- a/src/library/insert.rs +++ b/src/library/insert.rs @@ -10,12 +10,12 @@ use crate::prelude::*; /// /// # Positional arguments /// - Path to image file: of type `string`. -pub fn image(ctx: &mut EvalContext, args: &mut Args) -> Value { +pub fn image(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { let path = args.require::>(ctx, "path to image file"); let width = args.get(ctx, "width"); let height = args.get(ctx, "height"); - Value::template(move |ctx| { + Value::template("image", move |ctx| { if let Some(path) = &path { let loaded = ctx.env.resources.load(&path.v, ImageResource::parse); if let Some((res, img)) = loaded { diff --git a/src/library/layout.rs b/src/library/layout.rs index 44c985362..3577ee366 100644 --- a/src/library/layout.rs +++ b/src/library/layout.rs @@ -26,14 +26,14 @@ use crate::prelude::*; /// - `top` /// - `bottom` /// - `center` -pub fn align(ctx: &mut EvalContext, args: &mut Args) -> Value { +pub fn align(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { let first = args.find(ctx); let second = args.find(ctx); let hor = args.get(ctx, "horizontal"); let ver = args.get(ctx, "vertical"); let body = args.find::(ctx); - Value::template(move |ctx| { + Value::template("align", move |ctx| { let snapshot = ctx.state.clone(); let mut had = Gen::uniform(false); @@ -157,7 +157,7 @@ impl Switch for Alignment { } } -impl_type! { +typify! { Alignment: "alignment", } @@ -188,7 +188,7 @@ impl Display for Alignment { /// - `rtl` (right to left) /// - `ttb` (top to bottom) /// - `btt` (bottom to top) -pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value { +pub fn box_(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { let width = args.get(ctx, "width"); let height = args.get(ctx, "height"); let main = args.get(ctx, "main-dir"); @@ -196,7 +196,7 @@ pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value { let color = args.get(ctx, "color"); let body = args.find::(ctx); - Value::template(move |ctx| { + Value::template("box", move |ctx| { let snapshot = ctx.state.clone(); ctx.set_dirs(Gen::new(main, cross)); @@ -230,7 +230,7 @@ pub fn box_(ctx: &mut EvalContext, args: &mut Args) -> Value { }) } -impl_type! { +typify! { Dir: "direction" } @@ -238,7 +238,7 @@ impl_type! { /// /// # Positional arguments /// - Amount of spacing: of type `linear` relative to current font size. -pub fn h(ctx: &mut EvalContext, args: &mut Args) -> Value { +pub fn h(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { spacing(ctx, args, SpecAxis::Horizontal) } @@ -246,15 +246,15 @@ pub fn h(ctx: &mut EvalContext, args: &mut Args) -> Value { /// /// # Positional arguments /// - Amount of spacing: of type `linear` relative to current font size. -pub fn v(ctx: &mut EvalContext, args: &mut Args) -> Value { +pub fn v(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { spacing(ctx, args, SpecAxis::Vertical) } /// Apply spacing along a specific axis. -fn spacing(ctx: &mut EvalContext, args: &mut Args, axis: SpecAxis) -> Value { +fn spacing(ctx: &mut EvalContext, args: &mut ValueArgs, axis: SpecAxis) -> Value { let spacing: Option = args.require(ctx, "spacing"); - Value::template(move |ctx| { + Value::template("spacing", move |ctx| { if let Some(linear) = spacing { let amount = linear.resolve(ctx.state.font.font_size()); let spacing = NodeSpacing { amount, softness: Softness::Hard }; @@ -286,7 +286,7 @@ fn spacing(ctx: &mut EvalContext, args: &mut Args, axis: SpecAxis) -> Value { /// - Flip width and height: `flip`, of type `bool`. /// - Main layouting direction: `main-dir`, of type `direction`. /// - Cross layouting direction: `cross-dir`, of type `direction`. -pub fn page(ctx: &mut EvalContext, args: &mut Args) -> Value { +pub fn page(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { let paper = args.find::>(ctx).and_then(|name| { Paper::from_name(&name.v).or_else(|| { ctx.diag(error!(name.span, "invalid paper name")); @@ -305,7 +305,7 @@ pub fn page(ctx: &mut EvalContext, args: &mut Args) -> Value { let cross = args.get(ctx, "cross-dir"); let body = args.find::(ctx); - Value::template(move |ctx| { + Value::template("page", move |ctx| { let snapshot = ctx.state.clone(); if let Some(paper) = paper { @@ -370,8 +370,8 @@ pub fn page(ctx: &mut EvalContext, args: &mut Args) -> Value { } /// `pagebreak`: Start a new page. -pub fn pagebreak(_: &mut EvalContext, _: &mut Args) -> Value { - Value::template(move |ctx| { +pub fn pagebreak(_: &mut EvalContext, _: &mut ValueArgs) -> Value { + Value::template("pagebreak", move |ctx| { ctx.end_page_group(|_| true); ctx.start_page_group(Softness::Hard); }) diff --git a/src/library/style.rs b/src/library/style.rs index 23bd5298e..357275157 100644 --- a/src/library/style.rs +++ b/src/library/style.rs @@ -54,7 +54,7 @@ use crate::prelude::*; /// - `expanded` /// - `extra-expanded` /// - `ultra-expanded` -pub fn font(ctx: &mut EvalContext, args: &mut Args) -> Value { +pub fn font(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { let size = args.find::(ctx); let list: Vec<_> = args.filter::(ctx).map(|f| f.to_string()).collect(); let style = args.get(ctx, "style"); @@ -65,7 +65,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> Value { let monospace = args.get(ctx, "monospace"); let body = args.find::(ctx); - Value::template(move |ctx| { + Value::template("font", move |ctx| { let snapshot = ctx.state.clone(); if let Some(linear) = size { @@ -145,7 +145,7 @@ impl Display for FontFamily { } } -impl_type! { +typify! { FontFamilies: "font family or array of font families", Value::Str(string) => Self(vec![FontFamily::Named(string.to_lowercase())]), Value::Array(values) => Self(values @@ -156,16 +156,16 @@ impl_type! { #(family: FontFamily) => Self(vec![family]), } -impl_type! { +typify! { FontFamily: "font family", Value::Str(string) => Self::Named(string.to_lowercase()) } -impl_type! { +typify! { FontStyle: "font style" } -impl_type! { +typify! { FontWeight: "font weight", Value::Int(number) => { let [min, max] = [Self::THIN, Self::BLACK]; @@ -180,7 +180,7 @@ impl_type! { }, } -impl_type! { +typify! { FontStretch: "font stretch" } @@ -191,7 +191,7 @@ impl_type! { /// - Green component: of type `float`, between 0.0 and 1.0. /// - Blue component: of type `float`, between 0.0 and 1.0. /// - Alpha component: optional, of type `float`, between 0.0 and 1.0. -pub fn rgb(ctx: &mut EvalContext, args: &mut Args) -> Value { +pub fn rgb(ctx: &mut EvalContext, args: &mut ValueArgs) -> Value { let r = args.require(ctx, "red component"); let g = args.require(ctx, "green component"); let b = args.require(ctx, "blue component"); diff --git a/src/parse/collection.rs b/src/parse/collection.rs index 162a8bd5b..7ffc4539f 100644 --- a/src/parse/collection.rs +++ b/src/parse/collection.rs @@ -51,29 +51,29 @@ fn collection(p: &mut Parser, mut collection: T) -> T { } /// Parse an expression or a named pair. -fn argument(p: &mut Parser) -> Option { +fn argument(p: &mut Parser) -> Option { let first = expr(p)?; if p.eat_if(Token::Colon) { if let Expr::Ident(name) = first { - Some(Argument::Named(Named { name, expr: expr(p)? })) + Some(ExprArg::Named(Named { name, expr: expr(p)? })) } else { p.diag(error!(first.span(), "expected identifier")); expr(p); None } } else { - Some(Argument::Pos(first)) + Some(ExprArg::Pos(first)) } } /// Abstraction for comma-separated list of expression / named pairs. trait Collection { - fn push_arg(&mut self, p: &mut Parser, arg: Argument); + fn push_arg(&mut self, p: &mut Parser, arg: ExprArg); fn push_comma(&mut self) {} } -impl Collection for Vec { - fn push_arg(&mut self, _: &mut Parser, arg: Argument) { +impl Collection for Vec { + fn push_arg(&mut self, _: &mut Parser, arg: ExprArg) { self.push(arg); } } @@ -99,23 +99,23 @@ impl State { } impl Collection for State { - fn push_arg(&mut self, p: &mut Parser, arg: Argument) { + fn push_arg(&mut self, p: &mut Parser, arg: ExprArg) { match self { Self::Unknown => match arg { - Argument::Pos(expr) => *self = Self::Expr(expr), - Argument::Named(named) => *self = Self::Dict(vec![named]), + ExprArg::Pos(expr) => *self = Self::Expr(expr), + ExprArg::Named(named) => *self = Self::Dict(vec![named]), }, Self::Expr(prev) => match arg { - Argument::Pos(expr) => *self = Self::Array(vec![take(prev), expr]), - Argument::Named(_) => diag(p, arg), + ExprArg::Pos(expr) => *self = Self::Array(vec![take(prev), expr]), + ExprArg::Named(_) => diag(p, arg), }, Self::Array(array) => match arg { - Argument::Pos(expr) => array.push(expr), - Argument::Named(_) => diag(p, arg), + ExprArg::Pos(expr) => array.push(expr), + ExprArg::Named(_) => diag(p, arg), }, Self::Dict(dict) => match arg { - Argument::Pos(_) => diag(p, arg), - Argument::Named(named) => dict.push(named), + ExprArg::Pos(_) => diag(p, arg), + ExprArg::Named(named) => dict.push(named), }, } } @@ -135,9 +135,9 @@ fn take(expr: &mut Expr) -> Expr { ) } -fn diag(p: &mut Parser, arg: Argument) { +fn diag(p: &mut Parser, arg: ExprArg) { p.diag(error!(arg.span(), "{}", match arg { - Argument::Pos(_) => "expected named pair, found expression", - Argument::Named(_) => "expected expression, found named pair", + ExprArg::Pos(_) => "expected named pair, found expression", + ExprArg::Named(_) => "expected expression, found named pair", })); } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 2c34d7b82..e7e25a1d8 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -198,11 +198,11 @@ fn bracket_call(p: &mut Parser) -> Option { let mut inner = inner?; if let Some(body) = body { inner.span.expand(body.span()); - inner.args.items.push(Argument::Pos(body)); + inner.args.items.push(ExprArg::Pos(body)); } while let Some(mut top) = outer.pop() { - top.args.items.push(Argument::Pos(Expr::Call(inner))); + top.args.items.push(ExprArg::Pos(Expr::Call(inner))); inner = top; } diff --git a/src/prelude.rs b/src/prelude.rs index 6bab80e67..3f5bcab74 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -3,7 +3,7 @@ pub use crate::diag::{Feedback, Pass}; #[doc(no_inline)] pub use crate::eval::{ - Args, CastResult, Eval, EvalContext, TemplateAny, TemplateNode, Value, ValueAny, + CastResult, Eval, EvalContext, TemplateAny, TemplateNode, Value, ValueAny, ValueArgs, ValueArray, ValueDict, ValueTemplate, }; #[doc(no_inline)] @@ -13,4 +13,4 @@ pub use crate::geom::*; pub use crate::layout::Node; #[doc(no_inline)] pub use crate::syntax::{Span, Spanned, WithSpan}; -pub use crate::{error, impl_type, warning}; +pub use crate::{error, typify, warning}; diff --git a/src/pretty.rs b/src/pretty.rs index 0123e9a75..e01955cd8 100644 --- a/src/pretty.rs +++ b/src/pretty.rs @@ -105,7 +105,7 @@ impl Pretty for str { } } -macro_rules! impl_pretty_display { +macro_rules! pretty_display { ($($type:ty),* $(,)?) => { $(impl Pretty for $type { fn pretty(&self, p: &mut Printer) { @@ -115,7 +115,7 @@ macro_rules! impl_pretty_display { }; } -impl_pretty_display! { +pretty_display! { bool, Length, Angle, diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index f431ba8dc..8a11ebc41 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -531,7 +531,7 @@ impl ExprCall { // Function name. self.callee.pretty(p); - let mut write_args = |items: &[Argument]| { + let mut write_args = |items: &[ExprArg]| { if !items.is_empty() { p.push(' '); p.join(items, ", ", |item, p| item.pretty(p)); @@ -542,7 +542,7 @@ impl ExprCall { // This can written as a chain. // // Example: Transforms "#[v][[f]]" => "#[v | f]". - [head @ .., Argument::Pos(Expr::Call(call))] => { + [head @ .., ExprArg::Pos(Expr::Call(call))] => { write_args(head); call.pretty_bracketed(p, true); } @@ -550,7 +550,7 @@ impl ExprCall { // This can be written with a body. // // Example: Transforms "#[v [Hi]]" => "#[v][Hi]". - [head @ .., Argument::Pos(Expr::Template(template))] => { + [head @ .., ExprArg::Pos(Expr::Template(template))] => { write_args(head); p.push(']'); template.pretty(p); @@ -573,7 +573,7 @@ pub struct ExprArgs { /// The source code location. pub span: Span, /// The positional and named arguments. - pub items: Vec, + pub items: Vec, } impl Pretty for ExprArgs { @@ -584,14 +584,14 @@ impl Pretty for ExprArgs { /// An argument to a function call: `12` or `draw: false`. #[derive(Debug, Clone, PartialEq)] -pub enum Argument { - /// A positional arguments. +pub enum ExprArg { + /// A positional argument. Pos(Expr), /// A named argument. Named(Named), } -impl Argument { +impl ExprArg { /// The source code location. pub fn span(&self) -> Span { match self { @@ -601,7 +601,7 @@ impl Argument { } } -impl Pretty for Argument { +impl Pretty for ExprArg { fn pretty(&self, p: &mut Printer) { match self { Self::Pos(expr) => expr.pretty(p), diff --git a/src/syntax/node.rs b/src/syntax/node.rs index fe9767a18..246790f6a 100644 --- a/src/syntax/node.rs +++ b/src/syntax/node.rs @@ -31,6 +31,7 @@ impl Pretty for Node { Self::Space => p.push(' '), Self::Linebreak => p.push_str(r"\"), Self::Parbreak => p.push_str("\n\n"), + // TODO: Handle escaping. Self::Text(text) => p.push_str(&text), Self::Heading(heading) => heading.pretty(p), Self::Raw(raw) => raw.pretty(p), diff --git a/src/syntax/visit.rs b/src/syntax/visit.rs index 70159d2d8..2d3c683f8 100644 --- a/src/syntax/visit.rs +++ b/src/syntax/visit.rs @@ -111,10 +111,10 @@ visit! { } } - fn visit_arg(v, node: &Argument) { + fn visit_arg(v, node: &ExprArg) { match node { - Argument::Pos(expr) => v.visit_expr(&expr), - Argument::Named(named) => v.visit_expr(&named.expr), + ExprArg::Pos(expr) => v.visit_expr(&expr), + ExprArg::Named(named) => v.visit_expr(&named.expr), } } diff --git a/tests/lang/ref/call-args.png b/tests/lang/ref/call-args.png index 0ce9000a67f8651b1e072e1822239c3e2021d702..99acd84b34451cdb399dba8bd84fd25d8828a317 100644 GIT binary patch literal 3295 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 literal 3713 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 diff --git a/tests/lang/ref/call-bracket.png b/tests/lang/ref/call-bracket.png index 2f020629ddbf91f0467f97fc717e5feececfeff4..a1214b3d9b61c27359f6f3a89f27e653afe725c6 100644 GIT binary patch literal 1700 zcmaKtYgp0;8^-@AGeGbHGcGkrIhff(#XO`-#Y01;d1`AYB6^wnI=7`UHPIvpQ=1^* z-Zb-2^H6WCGE9+WXtGSR(Ag^lOHC845V6z_rnUBE`?9Mq&vidv@8`LHcX><{1p%{% z0RVuYhKI!g00{g+&qF>8KxSSl004_DYFO}>hjqsB$()Pb=uZ~MKtBK&@`j(yAA*Xz z4|bXV4q5>6$^J;z<{0SqVCP@n0=4A)i}pDW1~HGKakQDjQ*SshZXGB;4?H?GkLS+Zsta%tRY#VK^P)}Y_cndlkbkvh zYBHF1GOd8^v}4FRy#My%Ym;=x^Oc$=tSBt4A&A-Gk&a76JPK;%;RUWQJ)4Wm+Kys{L#eE%TC3P^6GguKrYXp%l0V!*$#6=e4>H zne^CUX4D2*=@$x-v58>k1Z*x~jLLxDBm5fb@44NxkfN&>7Fa=}R!i7O~G%8Mi@>TpZX?p;BC^q|xOSwY9Vjh)|JBto)DX2_0{I==b4KMAg ztJ&jAeCi7NK;8DA@{eZ;eR8L9O4^x;cuR$;1M?|UQBn^|_{sfLvW}j#t`2Xz_14eP zZ_&z29$^Yk!52uYnBNH;{&vRMh;-+nX(l9`>eX zQz&O{J*i`Q=HIuz(XVGrFRXHHKw&>?MZ1G=L zkAoTC3?CQeXIf|b5B)$M_u6~k1#B5A!O!l~^Y7TETALZ!FR??iL+w@0{fLzDSD(Dd z+U3`mkUKXlB&+@tEokdNc%A#a%S+hZ-|1q5KClRAXad)LV`SSjkE6?Il1JL*kdb8vh3pW!_u>1A!ez3&K_?FSA zPQ)suzbz;0*zB6(=kt|EAH}xr_4EM_Rr_%1x;q;r*UEg8>GdNTpw@tGJr-OQg;#5B z>w=w9%O~WQ@u^V5EX*M7O!K~OMI5YA`fH|PG^x&3GZNE$K;ZSi1U51OyHR%|dTFBDjQ|qVxBYbOiZ$fWzwC(`*92M!l>W@L7*G zk8fc`WN1^^pAR5~yk2xD;qfNS)0hns;Y@|UCs^dPEWAAZRl;x%yE^P*6^u~k)bt-Hfip-pJ|RbA5oJ(DH4Dkjt; zA|q&$CK3bUK0ZG~eIP;;;Il?uiP!9FolY6Rs2J Ay8r+H literal 1897 zcmai#`8S)19>!m)^-}81)Ka&*wh8qj)8|Vp7C74+{>KO7G?TEu0-%yFraFu%9AT>z7cHT z8ag%tgmw<&UZZfJEMEQJBD{NkN%73d=D4+UJ!T3&Dh#(gi8?KB_gDc5|0aJqv~uRJ z;QvRMVkM^jOS1jw=HR;X;_jFw5DG^AQ;D2IDU*okyxtdAv}CLc%^n`|)Mn(I3{X+1 zH~aB$r9|f(okW~aYR?2>KZsu+a_U}~yX<5haU{%_1#ZXh@*b8qxqwL@(H`EKR$^43 z;I4`Sw47ie#`{D(XGK{~#fyKCY=h}jR&wT_{eAR?qwXF3fbn0!hSwI~zq?e18cy$4 zca}kf7AqzzRT!1Uh@lFjeK<(m|88O04V|Hv<&6^u6I*(tqXquR%~wj0at6?72z}a> zP#?YD_T;<<|3l!}e%Y+?lh8&8p`u#x=UHDLnQR&r-*1|=cM*uc;-)!glO06eSr;Um z`*}_=p`H~ikmPF`a79CBxX89!doaR7I#+_p#u_VAkNx2>XWzkqdOP#X85lUQ>U6?o z0v20Af+^w;VqxAEoL|Gz<5T=hxa`P7(`zDZbL6ag`&1?_%x33a{^n5 zzP82*o}5mI23*)`U3rJQ?}Yj6Z$od%xOzzk8%&X=>VW+D<;dBiUHVBC%2K*WWSNbm z-Yolf_A0*&mc?YA1vUF>jZ@#-aI#yD1jGoI-VC?6CU#k){1YV!39l`*@|NV*$F@h` zrAC~l+GNNqR)bLUT0`W2aw2!-Dli^pyAk02ipOu0#~l)PNAJsc6Z*XYssmJx@GNV| z{|(5xnXoTcF{vYB;Qo-*b7Y96$n6gf^HgBI;UUF}zVr}X5EtE!$kXYA!)V_p?U0Wd zJ^DC`%YUzMAJ{1HDxkMS-jv{1QIY<5Vfs~4&;ZM*O`nn$U3XVFZnZk!1dZf#9@PvV zoNpl(4?+rqDE;J++GRe7`{{V=@^X`i;?_^Jq&0zf!)i8(Q@jqO~8}n)#G+HlY}n`0Vam5POl>L zN8A4OFp0Fsj*J+KOSSd$1m-Y}irE#ep9jmv1#GoB%%C z8w|dHCh;3vA4#x)9186rXD6(}9O9xR$(i0Z0LOwziW5P~7|ChK*jfa% zE_dwM+alPD95t3~>h$etHCOk0%;CIK*GdDGrF%-Gq77_$s64~)ZKW6Ew_~-(ShT`k z+$D$ju>x6>x~s!MsWjVjAl(G1Z5P)uzb3*n-DSDI_$R2ALj$T!h)T6hV7yR~HR6`Q zzFzf{Cse5j)gJxX7MHd`N|3RuN<5EMNH2y0sW*-E3Pz1^{#-8~vlkm#W0eME5+aM1 z4b2Bj>RJO9PX{0b`m2$1%0g4qr$I-h4y4GhhwHTi>Wp(mc&~talU#O&@R!MB<9unU ztVVXkj`_RP5Ch84m(r~lh5B<2+b;{v^1RWK@hU(;to*tjMfUkEA5?FO`N949zooh` fPZ;|qF=GGd;#5-N!wmBFe>c?LhDfNh3Sj&NtTA4m diff --git a/tests/lang/ref/call-chain.png b/tests/lang/ref/call-chain.png index 5c90dbd806899c8d682e276908f5ae19bf03d944..de5520b8484db5c44d185aec6aa130c1d3647e6e 100644 GIT binary patch literal 8692 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 MtsE`uu)mT214$dVk^lez literal 1905 zcmb7Fe>~IqAOFn8R!k;8mKtrYQ+M;DNb{p%Hk7D#m97wZpbn8=qr$kgu!SC9-{Wwj zR-!aSnP}>xC}d6+v2ioPb&Fw5le%5^xc}&N5ydcB^{SI$0vZ{%vT z)c^n>clnU1004n$UO7VZ0hECYhX4Rh*hTg@kTNV6D`Lid&`d=wR18T3FhiIE{W5*N zK2v{4zZJ6&420V}+{9;0eLx=_ZXg-P-#mJp6EG}d*8TLp-QGO^(fUyK)(&)7k5&7B zC@>V+z~hUC;l`Nb=IA_W$xKsSMFd6rTUAtJigIIV9?Yz^& z{-}+;6&|z&%pj-y?u0!k%mWg)YVG@lV;q{ENXPPoxj@~{l`|f$>%qc#qcm3@*Gz1&K+b(34Od7QXKc-7mzGT5E{T5)aQ(fp5d}LW+DC$1D2EQK1IJ; z;YXW%xmlmD{lO^fuV%Bi&US`3{ExC9*MQ11Q@oT!61y?wEry(b}&?-yMT=|kHXW|k{{lwiOyr6Sczr$NWQU54)5pSFo2j84c`^q(qr7I;e&c?o*!$iPn zrIgPyLrSy!v`XkOVy;mC-}VSBcSj4nee?2Z z3^21*cZSFU2CnH;G(rXvY_;S^FGepArkm{duMRoa<11}IB%C+cpn9(>mABipS-UB7 zT_YfanxymU!zI^7z0gEbN)I(hi0iY!;pWBb8p%MqIc~gGh3ZIoq?r32#x%OrZ#OuJ zM{Rtp=ofug)MX8G%pdBa&Bj$o#@x|o=`l(o=y_KzX4#cLzLisNW9+J7YI}YW6-?0$ z6*SctLpq9vDtRkFC?)_@n2^>=#NeG|#!(u)qkoSI1xook3?82L6N3F~q3F>vBD`8D zK~)-#Z_@fhrhIRQXx<}ZlvH*m0WZ42H|orEM$-6qSIK(I6A(Ug}GXmz*Ni^ zm$FYav%a7MZR;O?nsD)&@!Q{jp4I<|CPSlMZH^fbPy398wbQ**_7gPLkN){D`ENHd zHte^^yvy9M-RTE{Xqg^`cM;K}zM@|BjtN{g1M3~E^e$yqJ-y~)39oAwoOi^sOTDA> zmdwD*t?9#512q-@Unt>jtYx8BM{<)1qk31^4fpH=ANC-ElqO)(553deV{kAok5CR* z_ph$oyYI|EJRnOfX-`FGIM{~Ak-sh)w7dnydXveosIMM8Hy%#+qN^S5zrH>42e&Ly z(tf~aG5ay;7)Iay$lWVOjP6sFyZbtiCzjnv!s6WqR)ub+V4eJXkkJXRY%blg1eSH5 zV<0C?lW@YUIOiTj!t9F%ERe<)W&zhFBZ%mhNRJQ!#RgTebCh&=#5(>Z$I%iPhNwAjY4m3I!A*_hrnWubd807H~DjvF=CkndH?_b diff --git a/tests/lang/ref/comment.png b/tests/lang/ref/comment.png index 79f438b2fcf1b1008c18376c89011d86031290d2..0ab9a988c0c7bcef02e451919f292a3b338b5d60 100644 GIT binary patch delta 1025 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 G+9Esl@ckruuw8{?B<^PQIo^Lsq-Mc4@QO@@8 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 diff --git a/tests/typeset.rs b/tests/typeset.rs index c894b2d48..d64294d1c 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -16,7 +16,7 @@ use walkdir::WalkDir; use typst::diag::{Diag, Feedback, Level, Pass}; use typst::env::{Env, ImageResource, ResourceLoader}; -use typst::eval::{Args, EvalContext, Scope, Value, ValueFunc}; +use typst::eval::{EvalContext, Scope, Value, ValueArgs, ValueFunc}; use typst::exec::State; use typst::export::pdf; use typst::font::FsIndexExt; @@ -24,7 +24,6 @@ use typst::geom::{Length, Point, Sides, Size, Spec}; use typst::layout::{Element, Expansion, Fill, Frame, Geometry, Image, Shape}; use typst::library; use typst::parse::{LineMap, Scanner}; -use typst::pretty::{Pretty, Printer}; use typst::shaping::Shaped; use typst::syntax::{Location, Pos, SpanVec, Spanned, WithSpan}; use typst::typeset; @@ -320,28 +319,13 @@ struct Panic { } fn register_helpers(scope: &mut Scope, panics: Rc>>) { - pub fn f(_: &mut EvalContext, args: &mut Args) -> Value { - let (array, dict) = args.drain(); - let iter = array - .into_iter() - .map(|v| (None, v)) - .chain(dict.into_iter().map(|(k, v)| (Some(k), v))); - - let mut p = Printer::new(); - p.push_str("f("); - p.join(iter, ", ", |(key, value), p| { - if let Some(key) = key { - p.push_str(&key); - p.push_str(": "); - } - value.pretty(p); - }); - p.push(')'); - - Value::Str(p.finish()) + pub fn f(_: &mut EvalContext, args: &mut ValueArgs) -> Value { + let value = args.clone().into(); + args.items.clear(); + value } - let test = move |ctx: &mut EvalContext, args: &mut Args| -> Value { + let test = move |ctx: &mut EvalContext, args: &mut ValueArgs| -> Value { let lhs = args.require::(ctx, "left-hand side"); let rhs = args.require::(ctx, "right-hand side"); if lhs != rhs {