diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/eval/function.rs | 42 | ||||
| -rw-r--r-- | src/eval/mod.rs | 1 | ||||
| -rw-r--r-- | src/eval/ops.rs | 8 | ||||
| -rw-r--r-- | src/library/elements.rs | 8 | ||||
| -rw-r--r-- | src/library/layout.rs | 12 | ||||
| -rw-r--r-- | src/library/mod.rs | 1 | ||||
| -rw-r--r-- | src/library/text.rs | 14 | ||||
| -rw-r--r-- | src/library/utility.rs | 29 | ||||
| -rw-r--r-- | src/syntax/expr.rs | 12 |
9 files changed, 74 insertions, 53 deletions
diff --git a/src/eval/function.rs b/src/eval/function.rs index d9f79adf..cbbc0b36 100644 --- a/src/eval/function.rs +++ b/src/eval/function.rs @@ -77,25 +77,22 @@ pub struct Arg { } impl Args { - /// Find and consume the first castable positional argument. - pub fn eat<T>(&mut self) -> Option<T> + /// Consume and cast the first positional argument. + /// + /// Returns a `missing argument: {what}` error if no positional argument is + /// left. + pub fn expect<T>(&mut self, what: &str) -> TypResult<T> where T: Cast<Spanned<Value>>, { - for (i, slot) in self.items.iter().enumerate() { - if slot.name.is_none() { - if T::is(&slot.value) { - let value = self.items.remove(i).value; - return T::cast(value).ok(); - } - } + match self.eat()? { + Some(v) => Ok(v), + None => bail!(self.span, "missing argument: {}", what), } - None } - /// Try to cast the first positional argument ir returning a `missing - /// argument: {what}` error if no positional argument is left. - pub fn expect<T>(&mut self, what: &str) -> TypResult<T> + /// Consume and cast the first positional argument if there is one. + pub fn eat<T>(&mut self) -> TypResult<Option<T>> where T: Cast<Spanned<Value>>, { @@ -103,11 +100,24 @@ impl Args { if slot.name.is_none() { let value = self.items.remove(i).value; let span = value.span; - return T::cast(value).at(span); + return T::cast(value).at(span).map(Some); } } + Ok(None) + } - bail!(self.span, "missing argument: {}", what); + /// Find and consume the first castable positional argument. + pub fn find<T>(&mut self) -> Option<T> + where + T: Cast<Spanned<Value>>, + { + for (i, slot) in self.items.iter().enumerate() { + if slot.name.is_none() && T::is(&slot.value) { + let value = self.items.remove(i).value; + return T::cast(value).ok(); + } + } + None } /// Find and consume all castable positional arguments. @@ -115,7 +125,7 @@ impl Args { where T: Cast<Spanned<Value>>, { - std::iter::from_fn(move || self.eat()) + std::iter::from_fn(move || self.find()) } /// Cast and remove the value for the given named argument, returning an diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 2adf785e..691e3c49 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -317,7 +317,6 @@ impl Eval for BinaryExpr { BinOp::SubAssign => self.assign(ctx, ops::sub), BinOp::MulAssign => self.assign(ctx, ops::mul), BinOp::DivAssign => self.assign(ctx, ops::div), - BinOp::Range => self.apply(ctx, ops::range), } } } diff --git a/src/eval/ops.rs b/src/eval/ops.rs index 8756e8c6..732bfb14 100644 --- a/src/eval/ops.rs +++ b/src/eval/ops.rs @@ -244,14 +244,6 @@ comparison!(leq, "<=", Ordering::Less | Ordering::Equal); comparison!(gt, ">", Ordering::Greater); comparison!(geq, ">=", Ordering::Greater | Ordering::Equal); -/// Compute the range from `lhs` to `rhs`. -pub fn range(lhs: Value, rhs: Value) -> StrResult<Value> { - match (lhs, rhs) { - (Int(a), Int(b)) => Ok(Array((a .. b).map(Int).collect())), - (a, b) => mismatch!("cannot apply '..' to {} and {}", a, b), - } -} - /// Determine whether two values are equal. pub fn equal(lhs: &Value, rhs: &Value) -> bool { match (lhs, rhs) { diff --git a/src/library/elements.rs b/src/library/elements.rs index 5d87d65d..cd8a6f88 100644 --- a/src/library/elements.rs +++ b/src/library/elements.rs @@ -30,7 +30,7 @@ pub fn rect(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { let width = args.named("width")?; let height = args.named("height")?; let fill = args.named("fill")?; - let body = args.eat(); + let body = args.find(); Ok(shape_impl(ShapeKind::Rect, width, height, fill, body)) } @@ -46,7 +46,7 @@ pub fn square(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { size => size, }; let fill = args.named("fill")?; - let body = args.eat(); + let body = args.find(); Ok(shape_impl(ShapeKind::Square, width, height, fill, body)) } @@ -55,7 +55,7 @@ pub fn ellipse(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { let width = args.named("width")?; let height = args.named("height")?; let fill = args.named("fill")?; - let body = args.eat(); + let body = args.find(); Ok(shape_impl(ShapeKind::Ellipse, width, height, fill, body)) } @@ -71,7 +71,7 @@ pub fn circle(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { diameter => diameter, }; let fill = args.named("fill")?; - let body = args.eat(); + let body = args.find(); Ok(shape_impl(ShapeKind::Circle, width, height, fill, body)) } diff --git a/src/library/layout.rs b/src/library/layout.rs index d412af67..4b416156 100644 --- a/src/library/layout.rs +++ b/src/library/layout.rs @@ -6,7 +6,7 @@ use crate::style::{Paper, PaperClass}; /// `page`: Configure pages. pub fn page(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let paper = match args.named::<Spanned<Str>>("paper")?.or_else(|| args.eat()) { + let paper = match args.named::<Spanned<Str>>("paper")?.or_else(|| args.find()) { Some(name) => match Paper::from_name(&name.v) { None => bail!(name.span, "invalid paper name"), paper => paper, @@ -80,9 +80,9 @@ pub fn pagebreak(_: &mut EvalContext, _: &mut Args) -> TypResult<Value> { /// `align`: Configure the alignment along the layouting axes. pub fn align(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let first = args.eat::<Align>(); - let second = args.eat::<Align>(); - let body = args.eat::<Template>(); + let first = args.find::<Align>(); + let second = args.find::<Align>(); + let body = args.find::<Template>(); let mut horizontal = args.named("horizontal")?; let mut vertical = args.named("vertical")?; @@ -149,7 +149,7 @@ pub fn boxed(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { let width = args.named("width")?; let height = args.named("height")?; let fill = args.named("fill")?; - let body: Template = args.eat().unwrap_or_default(); + let body: Template = args.find().unwrap_or_default(); Ok(Value::Template(Template::from_inline(move |style| { ShapeNode { shape: ShapeKind::Rect, @@ -171,7 +171,7 @@ pub fn block(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { /// `pad`: Pad content at the sides. pub fn pad(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let all = args.eat(); + let all = args.find(); let left = args.named("left")?; let top = args.named("top")?; let right = args.named("right")?; diff --git a/src/library/mod.rs b/src/library/mod.rs index 430e260b..919f7532 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -66,6 +66,7 @@ pub fn new() -> Scope { std.def_func("abs", abs); std.def_func("min", min); std.def_func("max", max); + std.def_func("range", range); std.def_func("rgb", rgb); std.def_func("lower", lower); std.def_func("upper", upper); diff --git a/src/library/text.rs b/src/library/text.rs index 8c5b2547..23d74938 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -35,18 +35,18 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { (!families.is_empty()).then(|| FontDef(Rc::new(families))) }); - let size = args.named::<Linear>("size")?.or_else(|| args.eat()); + let size = args.named::<Linear>("size")?.or_else(|| args.find()); let style = args.named("style")?; let weight = args.named("weight")?; let stretch = args.named("stretch")?; - let fill = args.named("fill")?.or_else(|| args.eat()); + let fill = args.named("fill")?.or_else(|| args.find()); let top_edge = args.named("top-edge")?; let bottom_edge = args.named("bottom-edge")?; let serif = args.named("serif")?; let sans_serif = args.named("sans-serif")?; let monospace = args.named("monospace")?; let fallback = args.named("fallback")?; - let body = args.eat::<Template>(); + let body = args.find::<Template>(); let f = move |style_: &mut Style| { let text = style_.text_mut(); @@ -132,7 +132,7 @@ pub fn par(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { /// `lang`: Configure the language. pub fn lang(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Value> { - let iso = args.eat::<Str>(); + let iso = args.find::<Str>(); let dir = if let Some(dir) = args.named::<Spanned<Dir>>("dir")? { if dir.v.axis() == SpecAxis::Horizontal { Some(dir.v) @@ -177,8 +177,8 @@ pub fn overline(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { } fn line_impl(args: &mut Args, kind: LineKind) -> TypResult<Value> { - let stroke = args.named("stroke")?.or_else(|| args.eat()); - let thickness = args.named::<Linear>("thickness")?.or_else(|| args.eat()); + let stroke = args.named("stroke")?.or_else(|| args.find()); + let thickness = args.named::<Linear>("thickness")?.or_else(|| args.find()); let offset = args.named("offset")?; let extent = args.named("extent")?.unwrap_or_default(); let body: Template = args.expect("body")?; @@ -197,7 +197,7 @@ fn line_impl(args: &mut Args, kind: LineKind) -> TypResult<Value> { /// `link`: Typeset text as a link. pub fn link(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { let url = args.expect::<Str>("url")?; - let body = args.eat().unwrap_or_else(|| { + let body = args.find().unwrap_or_else(|| { let mut template = Template::new(); template.text(&url); template diff --git a/src/library/utility.rs b/src/library/utility.rs index 16a6f5db..15f2bcf5 100644 --- a/src/library/utility.rs +++ b/src/library/utility.rs @@ -86,7 +86,7 @@ pub fn str(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { /// `rgb`: Create an RGB(A) color. pub fn rgb(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { Ok(Value::Color(Color::Rgba( - if let Some(string) = args.eat::<Spanned<Str>>() { + if let Some(string) = args.find::<Spanned<Str>>() { match RgbaColor::from_str(&string.v) { Ok(color) => color, Err(_) => bail!(string.span, "invalid color"), @@ -95,7 +95,7 @@ pub fn rgb(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { let r = args.expect("red component")?; let g = args.expect("green component")?; let b = args.expect("blue component")?; - let a = args.eat().unwrap_or(Spanned::new(1.0, Span::detached())); + let a = args.eat()?.unwrap_or(Spanned::new(1.0, Span::detached())); let f = |Spanned { v, span }: Spanned<f64>| { if 0.0 <= v && v <= 1.0 { Ok((v * 255.0).round() as u8) @@ -154,6 +154,31 @@ fn minmax(args: &mut Args, goal: Ordering) -> TypResult<Value> { Ok(extremum) } +/// `range`: Create a sequence of numbers. +pub fn range(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { + let first = args.expect::<i64>("end")?; + let (start, end) = match args.eat::<i64>()? { + Some(second) => (first, second), + None => (0, first), + }; + + let step: i64 = match args.named("step")? { + Some(Spanned { v: 0, span }) => bail!(span, "step must not be zero"), + Some(Spanned { v, .. }) => v, + None => 1, + }; + + let mut x = start; + let mut seq = vec![]; + + while x.cmp(&end) == 0.cmp(&step) { + seq.push(Value::Int(x)); + x += step; + } + + Ok(Value::Array(Array::from_vec(seq))) +} + /// `lower`: Convert a string to lowercase. pub fn lower(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> { Ok(args.expect::<Str>("string")?.to_lowercase().into()) diff --git a/src/syntax/expr.rs b/src/syntax/expr.rs index 887de826..904515ba 100644 --- a/src/syntax/expr.rs +++ b/src/syntax/expr.rs @@ -293,8 +293,6 @@ pub enum BinOp { MulAssign, /// The divide-assign operator: `/=`. DivAssign, - /// The range operator: `..`. - Range, } impl BinOp { @@ -318,7 +316,6 @@ impl BinOp { Token::HyphEq => Self::SubAssign, Token::StarEq => Self::MulAssign, Token::SlashEq => Self::DivAssign, - Token::Dots => Self::Range, _ => return None, }) } @@ -326,9 +323,8 @@ impl BinOp { /// The precedence of this operator. pub fn precedence(self) -> usize { match self { - Self::Mul | Self::Div => 7, - Self::Add | Self::Sub => 6, - Self::Range => 5, + Self::Mul | Self::Div => 6, + Self::Add | Self::Sub => 5, Self::Eq | Self::Neq | Self::Lt | Self::Leq | Self::Gt | Self::Geq => 4, Self::And => 3, Self::Or => 2, @@ -354,8 +350,7 @@ impl BinOp { | Self::Lt | Self::Leq | Self::Gt - | Self::Geq - | Self::Range => Associativity::Left, + | Self::Geq => Associativity::Left, Self::Assign | Self::AddAssign | Self::SubAssign @@ -384,7 +379,6 @@ impl BinOp { Self::SubAssign => "-=", Self::MulAssign => "*=", Self::DivAssign => "/=", - Self::Range => "..", } } } |
