Replace ..
syntax with range
function
This commit is contained in:
parent
adf52a873f
commit
3968181622
@ -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
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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))
|
||||
}
|
||||
|
||||
|
@ -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")?;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
|
@ -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 => "..",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@
|
||||
---
|
||||
{
|
||||
let x = 2
|
||||
for _ in 0..61 {
|
||||
for _ in range(61) {
|
||||
x *= 2
|
||||
}
|
||||
// Error: 4-18 cannot repeat this string 4611686018427387904 times
|
||||
@ -72,10 +72,6 @@
|
||||
// Error: 3-4 expected function, found integer
|
||||
{ 1 with () }
|
||||
|
||||
---
|
||||
// Error: 3-10 cannot apply '..' to integer and string
|
||||
{ 1 .. "" }
|
||||
|
||||
---
|
||||
// Error: 3-6 cannot access this expression mutably
|
||||
{ (x) = "" }
|
||||
|
@ -161,18 +161,6 @@
|
||||
{ x = "some" } #test(x, "some")
|
||||
{ x += "thing" } #test(x, "something")
|
||||
|
||||
---
|
||||
// Test range operator.
|
||||
|
||||
#let array = (1, 2, 3)
|
||||
#test(1..4, array)
|
||||
#test(1.. 4, array)
|
||||
#test(1 ..4, array)
|
||||
#test(1 .. 4, array)
|
||||
|
||||
#test(-4..2, (-4, -3, -2, -1, 0, 1))
|
||||
#test(10..5, ())
|
||||
|
||||
---
|
||||
// Test with operator.
|
||||
// Ref: true
|
||||
|
@ -4,7 +4,7 @@
|
||||
---
|
||||
// Test the `assert` function.
|
||||
#assert(1 + 1 == 2)
|
||||
#assert(2..5 == (2, 3, 4))
|
||||
#assert(range(2, 5) == (2, 3, 4))
|
||||
#assert(not false)
|
||||
|
||||
---
|
||||
|
@ -29,3 +29,7 @@
|
||||
---
|
||||
// Error: 5-11 missing argument: blue component
|
||||
#rgb(0, 1)
|
||||
|
||||
---
|
||||
// Error: 21-26 expected float, found boolean
|
||||
#rgb(0.1, 0.2, 0.3, false)
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Ref: false
|
||||
|
||||
---
|
||||
// Test `abs` function.
|
||||
// Test the `abs` function.
|
||||
#test(abs(-3), 3)
|
||||
#test(abs(3), 3)
|
||||
#test(abs(-0.0), 0.0)
|
||||
@ -20,7 +20,7 @@
|
||||
#abs("no number")
|
||||
|
||||
---
|
||||
// Test `min` and `max` functions.
|
||||
// Test the `min` and `max` functions.
|
||||
#test(min(2, -4), -4)
|
||||
#test(min(3.5, 1e2, -0.1, 3), -0.1)
|
||||
#test(max(-3, 11), 11)
|
||||
@ -31,5 +31,33 @@
|
||||
#min()
|
||||
|
||||
---
|
||||
// Error: 14-18 cannot compare integer with string
|
||||
#test(min(1, "hi"), error)
|
||||
// Error: 9-13 cannot compare integer with string
|
||||
#min(1, "hi")
|
||||
|
||||
---
|
||||
// Test the `range` function.
|
||||
#test(range(4), (0, 1, 2, 3))
|
||||
#test(range(1, 4), (1, 2, 3))
|
||||
#test(range(-4, 2), (-4, -3, -2, -1, 0, 1))
|
||||
#test(range(10, 5), ())
|
||||
#test(range(10, step: 3), (0, 3, 6, 9))
|
||||
#test(range(1, 4, step: 1), (1, 2, 3))
|
||||
#test(range(1, 8, step: 2), (1, 3, 5, 7))
|
||||
#test(range(5, 2, step: -1), (5, 4, 3))
|
||||
#test(range(10, 0, step: -3), (10, 7, 4, 1))
|
||||
|
||||
---
|
||||
// Error: 7-9 missing argument: end
|
||||
#range()
|
||||
|
||||
---
|
||||
// Error: 11-14 expected integer, found float
|
||||
#range(1, 2.0)
|
||||
|
||||
---
|
||||
// Error: 17-22 expected integer, found string
|
||||
#range(4, step: "one")
|
||||
|
||||
---
|
||||
// Error: 18-19 step must not be zero
|
||||
#range(10, step: 0)
|
||||
|
Loading…
Reference in New Issue
Block a user