mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Range operator
This commit is contained in:
parent
c5635d8a3f
commit
5a500fb8a7
@ -383,6 +383,7 @@ 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -250,6 +250,14 @@ comparison!(leq, Less | Equal);
|
||||
comparison!(gt, Greater);
|
||||
comparison!(geq, Greater | Equal);
|
||||
|
||||
/// Compute the range from `lhs` to `rhs`.
|
||||
pub fn range(lhs: Value, rhs: Value) -> Value {
|
||||
match (lhs, rhs) {
|
||||
(Int(a), Int(b)) => Array((a ..= b).map(Int).collect()),
|
||||
_ => Error,
|
||||
}
|
||||
}
|
||||
|
||||
/// Concatenate two collections.
|
||||
fn concat<T, A>(mut a: T, b: T) -> T
|
||||
where
|
||||
|
@ -86,11 +86,6 @@ impl<'s> Scanner<'s> {
|
||||
self.rest().chars().next()
|
||||
}
|
||||
|
||||
/// Peek at the nth-next char without consuming anything.
|
||||
pub fn peek_nth(&self, n: usize) -> Option<char> {
|
||||
self.rest().chars().nth(n)
|
||||
}
|
||||
|
||||
/// Checks whether the next char fulfills a condition.
|
||||
///
|
||||
/// Returns `false` if there is no next char.
|
||||
@ -101,6 +96,11 @@ impl<'s> Scanner<'s> {
|
||||
self.peek().map(f).unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Checks whether the remaining source starts with the given string.
|
||||
pub fn starts_with(&self, string: &str) -> bool {
|
||||
self.rest().starts_with(string)
|
||||
}
|
||||
|
||||
/// The previous index in the source string.
|
||||
pub fn last_index(&self) -> usize {
|
||||
self.eaten()
|
||||
|
@ -216,7 +216,7 @@ impl<'s> Tokens<'s> {
|
||||
self.s.eat_assert(c);
|
||||
Token::Text(&self.s.eaten_from(start))
|
||||
}
|
||||
'u' if self.s.peek_nth(1) == Some('{') => {
|
||||
'u' if self.s.starts_with("u{") => {
|
||||
self.s.eat_assert('u');
|
||||
self.s.eat_assert('{');
|
||||
Token::UnicodeEscape(UnicodeEscapeToken {
|
||||
@ -366,7 +366,8 @@ impl<'s> Tokens<'s> {
|
||||
self.s.eat_while(|c| c.is_ascii_digit());
|
||||
|
||||
// Read the fractional part if not already done.
|
||||
if c != '.' && self.s.eat_if('.') {
|
||||
// Make sure not to confuse a range for the decimal separator.
|
||||
if c != '.' && !self.s.starts_with("..") && self.s.eat_if('.') {
|
||||
self.s.eat_while(|c| c.is_ascii_digit());
|
||||
}
|
||||
|
||||
@ -905,6 +906,11 @@ mod tests {
|
||||
t!(Code[" /"]: format!("{}{}", s, suffix) => build(v));
|
||||
}
|
||||
}
|
||||
|
||||
// Multiple dots close the number.
|
||||
t!(Code[" /"]: "1..2" => Int(1), Dots, Int(2));
|
||||
t!(Code[" /"]: "1..2.3" => Int(1), Dots, Float(2.3));
|
||||
t!(Code[" /"]: "1.2..3" => Float(1.2), Dots, Int(3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -278,6 +278,8 @@ pub enum BinOp {
|
||||
MulAssign,
|
||||
/// The divide-assign operator: `/=`.
|
||||
DivAssign,
|
||||
/// The inclusive range operator: `..`.
|
||||
Range,
|
||||
}
|
||||
|
||||
impl BinOp {
|
||||
@ -301,6 +303,7 @@ impl BinOp {
|
||||
Token::HyphEq => Self::SubAssign,
|
||||
Token::StarEq => Self::MulAssign,
|
||||
Token::SlashEq => Self::DivAssign,
|
||||
Token::Dots => Self::Range,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
@ -311,8 +314,9 @@ impl BinOp {
|
||||
Self::Mul | Self::Div => 7,
|
||||
Self::Add | Self::Sub => 6,
|
||||
Self::Eq | Self::Neq | Self::Lt | Self::Leq | Self::Gt | Self::Geq => 5,
|
||||
Self::And => 3,
|
||||
Self::Or => 2,
|
||||
Self::And => 4,
|
||||
Self::Or => 3,
|
||||
Self::Range => 2,
|
||||
Self::Assign
|
||||
| Self::AddAssign
|
||||
| Self::SubAssign
|
||||
@ -335,7 +339,8 @@ impl BinOp {
|
||||
| Self::Lt
|
||||
| Self::Leq
|
||||
| Self::Gt
|
||||
| Self::Geq => Associativity::Left,
|
||||
| Self::Geq
|
||||
| Self::Range => Associativity::Left,
|
||||
Self::Assign
|
||||
| Self::AddAssign
|
||||
| Self::SubAssign
|
||||
@ -364,6 +369,7 @@ impl BinOp {
|
||||
Self::SubAssign => "-=",
|
||||
Self::MulAssign => "*=",
|
||||
Self::DivAssign => "/=",
|
||||
Self::Range => "..",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,6 +44,9 @@
|
||||
// Error: 3-4 expected function, found integer
|
||||
{ 1 with () }
|
||||
|
||||
// Error: 3-10 cannot apply '..' to integer and string
|
||||
{ 1 .. "" }
|
||||
|
||||
---
|
||||
// Bad left-hand sides of assignment.
|
||||
|
||||
|
@ -146,6 +146,18 @@
|
||||
{ x = "some" } #test(x, "some")
|
||||
{ x += "thing" } #test(x, "something")
|
||||
|
||||
---
|
||||
// Test range operator.
|
||||
|
||||
#let array = (1, 2, 3)
|
||||
#test(1..3, array)
|
||||
#test(1.. 3, array)
|
||||
#test(1 ..3, array)
|
||||
#test(1 .. 3, array)
|
||||
|
||||
#test(-4..2, (-4, -3, -2, -1, 0, 1, 2))
|
||||
#test(10..5, ())
|
||||
|
||||
---
|
||||
// Test with operator.
|
||||
// Ref: true
|
||||
|
Loading…
x
Reference in New Issue
Block a user