mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +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::SubAssign => self.assign(ctx, ops::sub),
|
||||||
BinOp::MulAssign => self.assign(ctx, ops::mul),
|
BinOp::MulAssign => self.assign(ctx, ops::mul),
|
||||||
BinOp::DivAssign => self.assign(ctx, ops::div),
|
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!(gt, Greater);
|
||||||
comparison!(geq, Greater | Equal);
|
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.
|
/// Concatenate two collections.
|
||||||
fn concat<T, A>(mut a: T, b: T) -> T
|
fn concat<T, A>(mut a: T, b: T) -> T
|
||||||
where
|
where
|
||||||
|
@ -86,11 +86,6 @@ impl<'s> Scanner<'s> {
|
|||||||
self.rest().chars().next()
|
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.
|
/// Checks whether the next char fulfills a condition.
|
||||||
///
|
///
|
||||||
/// Returns `false` if there is no next char.
|
/// Returns `false` if there is no next char.
|
||||||
@ -101,6 +96,11 @@ impl<'s> Scanner<'s> {
|
|||||||
self.peek().map(f).unwrap_or(false)
|
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.
|
/// The previous index in the source string.
|
||||||
pub fn last_index(&self) -> usize {
|
pub fn last_index(&self) -> usize {
|
||||||
self.eaten()
|
self.eaten()
|
||||||
|
@ -216,7 +216,7 @@ impl<'s> Tokens<'s> {
|
|||||||
self.s.eat_assert(c);
|
self.s.eat_assert(c);
|
||||||
Token::Text(&self.s.eaten_from(start))
|
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('u');
|
||||||
self.s.eat_assert('{');
|
self.s.eat_assert('{');
|
||||||
Token::UnicodeEscape(UnicodeEscapeToken {
|
Token::UnicodeEscape(UnicodeEscapeToken {
|
||||||
@ -366,7 +366,8 @@ impl<'s> Tokens<'s> {
|
|||||||
self.s.eat_while(|c| c.is_ascii_digit());
|
self.s.eat_while(|c| c.is_ascii_digit());
|
||||||
|
|
||||||
// Read the fractional part if not already done.
|
// 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());
|
self.s.eat_while(|c| c.is_ascii_digit());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -905,6 +906,11 @@ mod tests {
|
|||||||
t!(Code[" /"]: format!("{}{}", s, suffix) => build(v));
|
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]
|
#[test]
|
||||||
|
@ -278,6 +278,8 @@ pub enum BinOp {
|
|||||||
MulAssign,
|
MulAssign,
|
||||||
/// The divide-assign operator: `/=`.
|
/// The divide-assign operator: `/=`.
|
||||||
DivAssign,
|
DivAssign,
|
||||||
|
/// The inclusive range operator: `..`.
|
||||||
|
Range,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BinOp {
|
impl BinOp {
|
||||||
@ -301,6 +303,7 @@ impl BinOp {
|
|||||||
Token::HyphEq => Self::SubAssign,
|
Token::HyphEq => Self::SubAssign,
|
||||||
Token::StarEq => Self::MulAssign,
|
Token::StarEq => Self::MulAssign,
|
||||||
Token::SlashEq => Self::DivAssign,
|
Token::SlashEq => Self::DivAssign,
|
||||||
|
Token::Dots => Self::Range,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -311,8 +314,9 @@ impl BinOp {
|
|||||||
Self::Mul | Self::Div => 7,
|
Self::Mul | Self::Div => 7,
|
||||||
Self::Add | Self::Sub => 6,
|
Self::Add | Self::Sub => 6,
|
||||||
Self::Eq | Self::Neq | Self::Lt | Self::Leq | Self::Gt | Self::Geq => 5,
|
Self::Eq | Self::Neq | Self::Lt | Self::Leq | Self::Gt | Self::Geq => 5,
|
||||||
Self::And => 3,
|
Self::And => 4,
|
||||||
Self::Or => 2,
|
Self::Or => 3,
|
||||||
|
Self::Range => 2,
|
||||||
Self::Assign
|
Self::Assign
|
||||||
| Self::AddAssign
|
| Self::AddAssign
|
||||||
| Self::SubAssign
|
| Self::SubAssign
|
||||||
@ -335,7 +339,8 @@ impl BinOp {
|
|||||||
| Self::Lt
|
| Self::Lt
|
||||||
| Self::Leq
|
| Self::Leq
|
||||||
| Self::Gt
|
| Self::Gt
|
||||||
| Self::Geq => Associativity::Left,
|
| Self::Geq
|
||||||
|
| Self::Range => Associativity::Left,
|
||||||
Self::Assign
|
Self::Assign
|
||||||
| Self::AddAssign
|
| Self::AddAssign
|
||||||
| Self::SubAssign
|
| Self::SubAssign
|
||||||
@ -364,6 +369,7 @@ impl BinOp {
|
|||||||
Self::SubAssign => "-=",
|
Self::SubAssign => "-=",
|
||||||
Self::MulAssign => "*=",
|
Self::MulAssign => "*=",
|
||||||
Self::DivAssign => "/=",
|
Self::DivAssign => "/=",
|
||||||
|
Self::Range => "..",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,9 @@
|
|||||||
// Error: 3-4 expected function, found integer
|
// Error: 3-4 expected function, found integer
|
||||||
{ 1 with () }
|
{ 1 with () }
|
||||||
|
|
||||||
|
// Error: 3-10 cannot apply '..' to integer and string
|
||||||
|
{ 1 .. "" }
|
||||||
|
|
||||||
---
|
---
|
||||||
// Bad left-hand sides of assignment.
|
// Bad left-hand sides of assignment.
|
||||||
|
|
||||||
|
@ -146,6 +146,18 @@
|
|||||||
{ x = "some" } #test(x, "some")
|
{ x = "some" } #test(x, "some")
|
||||||
{ x += "thing" } #test(x, "something")
|
{ 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.
|
// Test with operator.
|
||||||
// Ref: true
|
// Ref: true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user