Add stepping to ranges & enable reverse ranges

Follows the following syntax: <start>..<next-value>..<end>
This commit is contained in:
Jakub Žádník
2021-09-11 14:13:04 +03:00
parent 2d3a56f0d3
commit 4749776984
8 changed files with 207 additions and 102 deletions

View File

@ -7,8 +7,9 @@ pub enum Expr {
Int(i64),
Float(f64),
Range(
Option<Box<Expression>>,
Option<Box<Expression>>,
Option<Box<Expression>>, // from
Option<Box<Expression>>, // next value after "from"
Option<Box<Expression>>, // to
RangeOperator,
),
Var(VarId),

View File

@ -59,6 +59,7 @@ pub enum RangeInclusion {
pub struct RangeOperator {
pub inclusion: RangeInclusion,
pub span: Span,
pub next_op_span: Span,
}
impl Display for RangeOperator {

View File

@ -8,7 +8,7 @@ pub use stream::*;
use std::fmt::Debug;
use crate::ast::{PathMember, RangeInclusion};
use crate::ast::PathMember;
use crate::{span, BlockId, Span, Type};
use crate::ShellError;
@ -131,20 +131,10 @@ impl Value {
Value::Int { val, .. } => val.to_string(),
Value::Float { val, .. } => val.to_string(),
Value::Range { val, .. } => {
let vals: Vec<i64> = match (&val.from, &val.to) {
(Value::Int { val: from, .. }, Value::Int { val: to, .. }) => {
match val.inclusion {
RangeInclusion::Inclusive => (*from..=*to).collect(),
RangeInclusion::RightExclusive => (*from..*to).collect(),
}
}
_ => Vec::new(),
};
format!(
"range: [{}]",
vals.iter()
.map(|x| x.to_string())
val.into_iter()
.map(|x| x.into_string())
.collect::<Vec<String>>()
.join(", ")
)

View File

@ -1,12 +1,106 @@
use crate::{ast::RangeInclusion, *};
use crate::{
ast::{RangeInclusion, RangeOperator},
*,
};
#[derive(Debug, Clone, PartialEq)]
pub struct Range {
pub from: Value,
pub incr: Value,
pub to: Value,
pub inclusion: RangeInclusion,
}
impl Range {
pub fn new(
expr_span: Span,
from: Value,
next: Value,
to: Value,
operator: &RangeOperator,
) -> Result<Range, ShellError> {
// Select from & to values if they're not specified
// TODO: Replace the placeholder values with proper min/max based on data type
let from = if let Value::Nothing { .. } = from {
Value::Int {
val: 0i64,
span: Span::unknown(),
}
} else {
from
};
let to = if let Value::Nothing { .. } = to {
if let Ok(Value::Bool { val: true, .. }) = next.lt(expr_span, &from) {
Value::Int {
val: -100i64,
span: Span::unknown(),
}
} else {
Value::Int {
val: 100i64,
span: Span::unknown(),
}
}
} else {
to
};
// Check if the range counts up or down
let moves_up = matches!(from.lte(expr_span, &to), Ok(Value::Bool { val: true, .. }));
// Convert the next value into the inctement
let incr = if let Value::Nothing { .. } = next {
if moves_up {
Value::Int {
val: 1i64,
span: Span::unknown(),
}
} else {
Value::Int {
val: -1i64,
span: Span::unknown(),
}
}
} else {
next.sub(operator.next_op_span, &from)?
};
let zero = Value::Int {
val: 0i64,
span: Span::unknown(),
};
// Increment must be non-zero, otherwise we iterate forever
if matches!(incr.eq(expr_span, &zero), Ok(Value::Bool { val: true, .. })) {
return Err(ShellError::CannotCreateRange(expr_span));
}
// If to > from, then incr > 0, otherwise we iterate forever
if let (Value::Bool { val: true, .. }, Value::Bool { val: false, .. }) = (
to.gt(operator.span, &from)?,
incr.gt(operator.next_op_span, &zero)?,
) {
return Err(ShellError::CannotCreateRange(expr_span));
}
// If to < from, then incr < 0, otherwise we iterate forever
if let (Value::Bool { val: true, .. }, Value::Bool { val: false, .. }) = (
to.lt(operator.span, &from)?,
incr.lt(operator.next_op_span, &zero)?,
) {
return Err(ShellError::CannotCreateRange(expr_span));
}
Ok(Range {
from,
incr,
to,
inclusion: operator.inclusion,
})
}
}
impl IntoIterator for Range {
type Item = Value;
@ -25,8 +119,7 @@ pub struct RangeIterator {
span: Span,
is_end_inclusive: bool,
moves_up: bool,
one: Value,
negative_one: Value,
incr: Value,
done: bool,
}
@ -52,8 +145,7 @@ impl RangeIterator {
span,
is_end_inclusive: matches!(range.inclusion, RangeInclusion::Inclusive),
done: false,
one: Value::Int { val: 1, span },
negative_one: Value::Int { val: -1, span },
incr: range.incr,
}
}
}
@ -70,10 +162,10 @@ impl Iterator for RangeIterator {
Ordering::Less
} else {
match (&self.curr, &self.end) {
(Value::Int { val: x, .. }, Value::Int { val: y, .. }) => x.cmp(y),
// (Value::Float { val: x, .. }, Value::Float { val: y, .. }) => x.cmp(y),
// (Value::Float { val: x, .. }, Value::Int { val: y, .. }) => x.cmp(y),
// (Value::Int { val: x, .. }, Value::Float { val: y, .. }) => x.cmp(y),
(Value::Int { val: curr, .. }, Value::Int { val: end, .. }) => curr.cmp(end),
// (Value::Float { val: curr, .. }, Value::Float { val: end, .. }) => curr.cmp(end),
// (Value::Float { val: curr, .. }, Value::Int { val: end, .. }) => curr.cmp(end),
// (Value::Int { val: curr, .. }, Value::Float { val: end, .. }) => curr.cmp(end),
_ => {
self.done = true;
return Some(Value::Error {
@ -83,10 +175,15 @@ impl Iterator for RangeIterator {
}
};
if self.moves_up
&& (ordering == Ordering::Less || self.is_end_inclusive && ordering == Ordering::Equal)
let desired_ordering = if self.moves_up {
Ordering::Less
} else {
Ordering::Greater
};
if (ordering == desired_ordering) || (self.is_end_inclusive && ordering == Ordering::Equal)
{
let next_value = self.curr.add(self.span, &self.one);
let next_value = self.curr.add(self.span, &self.incr);
let mut next = match next_value {
Ok(result) => result,
@ -98,22 +195,6 @@ impl Iterator for RangeIterator {
};
std::mem::swap(&mut self.curr, &mut next);
Some(next)
} else if !self.moves_up
&& (ordering == Ordering::Greater
|| self.is_end_inclusive && ordering == Ordering::Equal)
{
let next_value = self.curr.add(self.span, &self.negative_one);
let mut next = match next_value {
Ok(result) => result,
Err(error) => {
self.done = true;
return Some(Value::Error { error });
}
};
std::mem::swap(&mut self.curr, &mut next);
Some(next)
} else {
None