Skip to content

Commit

Permalink
Partial evaluation support for array slicing (#1546)
Browse files Browse the repository at this point in the history
This change adds support to partial evaluation for array slicing in
index expressions, copy expressions and copy & update expressions.

---------

Co-authored-by: Stefan J. Wernli <[email protected]>
  • Loading branch information
cesarzc and swernli authored May 21, 2024
1 parent 481d436 commit 58a187a
Show file tree
Hide file tree
Showing 6 changed files with 1,346 additions and 135 deletions.
84 changes: 9 additions & 75 deletions compiler/qsc_eval/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ pub mod output;
pub mod state;
pub mod val;

use crate::val::Value;
use crate::val::{
index_array, make_range, slice_array, update_index_range, update_index_single, Value,
};
use backend::Backend;
use debug::{CallStack, Frame};
use error::PackageSpan;
pub use error::PackageSpan;
use miette::Diagnostic;
use num_bigint::BigInt;
use output::Receiver;
Expand Down Expand Up @@ -288,7 +290,7 @@ pub struct VariableInfo {
pub span: Span,
}

struct Range {
pub struct Range {
step: i64,
end: i64,
curr: i64,
Expand Down Expand Up @@ -1105,18 +1107,8 @@ impl State {
update: Value,
span: PackageSpan,
) -> Result<(), Error> {
if index < 0 {
return Err(Error::InvalidNegativeInt(index, span));
}
let i = index.as_index(span)?;
let mut values = values.to_vec();
match values.get_mut(i) {
Some(value) => {
*value = update;
}
None => return Err(Error::IndexOutOfRange(index, span)),
}
self.set_val_register(Value::Array(values.into()));
let updated_array = update_index_single(values, index, update, span)?;
self.set_val_register(updated_array);
Ok(())
}

Expand All @@ -1129,19 +1121,8 @@ impl State {
update: Value,
span: PackageSpan,
) -> Result<(), Error> {
let range = make_range(values, start, step, end, span)?;
let mut values = values.to_vec();
let update = update.unwrap_array();
for (idx, update) in range.into_iter().zip(update.iter()) {
let i = idx.as_index(span)?;
match values.get_mut(i) {
Some(value) => {
*value = update.clone();
}
None => return Err(Error::IndexOutOfRange(idx, span)),
}
}
self.set_val_register(Value::Array(values.into()));
let updated_array = update_index_range(values, start, step, end, update, span)?;
self.set_val_register(updated_array);
Ok(())
}

Expand Down Expand Up @@ -1483,53 +1464,6 @@ fn lit_to_val(lit: &Lit) -> Value {
}
}

fn index_array(arr: &[Value], index: i64, span: PackageSpan) -> Result<Value, Error> {
let i = index.as_index(span)?;
match arr.get(i) {
Some(v) => Ok(v.clone()),
None => Err(Error::IndexOutOfRange(index, span)),
}
}

fn slice_array(
arr: &[Value],
start: Option<i64>,
step: i64,
end: Option<i64>,
span: PackageSpan,
) -> Result<Value, Error> {
let range = make_range(arr, start, step, end, span)?;
let mut slice = vec![];
for i in range {
slice.push(index_array(arr, i, span)?);
}

Ok(Value::Array(slice.into()))
}

fn make_range(
arr: &[Value],
start: Option<i64>,
step: i64,
end: Option<i64>,
span: PackageSpan,
) -> Result<Range, Error> {
if step == 0 {
Err(Error::RangeStepZero(span))
} else {
let len: i64 = match arr.len().try_into() {
Ok(len) => Ok(len),
Err(_) => Err(Error::ArrayTooLarge(span)),
}?;
let (start, end) = if step > 0 {
(start.unwrap_or(0), end.unwrap_or(len - 1))
} else {
(start.unwrap_or(len - 1), end.unwrap_or(0))
};
Ok(Range::new(start, step, end))
}
}

fn eval_binop_eq(lhs_val: Value, rhs_val: Value, rhs_span: PackageSpan) -> Result<Value, Error> {
match (lhs_val, rhs_val) {
(Value::Result(val::Result::Id(_)), _) | (_, Value::Result(val::Result::Id(_))) => {
Expand Down
96 changes: 96 additions & 0 deletions compiler/qsc_eval/src/val.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use std::{
rc::Rc,
};

use crate::{error::PackageSpan, AsIndex, Error, Range as EvalRange};

pub(super) const DEFAULT_RANGE_STEP: i64 = 1;

#[derive(Clone, Debug, PartialEq)]
Expand Down Expand Up @@ -380,3 +382,97 @@ impl Value {
}
}
}

pub fn index_array(
arr: &[Value],
index: i64,
span: PackageSpan,
) -> std::result::Result<Value, Error> {
let i = index.as_index(span)?;
match arr.get(i) {
Some(v) => Ok(v.clone()),
None => Err(Error::IndexOutOfRange(index, span)),
}
}

pub fn make_range(
arr: &[Value],
start: Option<i64>,
step: i64,
end: Option<i64>,
span: PackageSpan,
) -> std::result::Result<EvalRange, Error> {
if step == 0 {
Err(Error::RangeStepZero(span))
} else {
let len: i64 = match arr.len().try_into() {
Ok(len) => Ok(len),
Err(_) => Err(Error::ArrayTooLarge(span)),
}?;
let (start, end) = if step > 0 {
(start.unwrap_or(0), end.unwrap_or(len - 1))
} else {
(start.unwrap_or(len - 1), end.unwrap_or(0))
};
Ok(EvalRange::new(start, step, end))
}
}

pub fn slice_array(
arr: &[Value],
start: Option<i64>,
step: i64,
end: Option<i64>,
span: PackageSpan,
) -> std::result::Result<Value, Error> {
let range = make_range(arr, start, step, end, span)?;
let mut slice = vec![];
for i in range {
slice.push(index_array(arr, i, span)?);
}

Ok(Value::Array(slice.into()))
}

pub fn update_index_single(
values: &[Value],
index: i64,
update: Value,
span: PackageSpan,
) -> std::result::Result<Value, Error> {
if index < 0 {
return Err(Error::InvalidNegativeInt(index, span));
}
let i = index.as_index(span)?;
let mut values = values.to_vec();
match values.get_mut(i) {
Some(value) => {
*value = update;
}
None => return Err(Error::IndexOutOfRange(index, span)),
}
Ok(Value::Array(values.into()))
}

pub fn update_index_range(
values: &[Value],
start: Option<i64>,
step: i64,
end: Option<i64>,
update: Value,
span: PackageSpan,
) -> std::result::Result<Value, Error> {
let range = make_range(values, start, step, end, span)?;
let mut values = values.to_vec();
let update = update.unwrap_array();
for (idx, update) in range.into_iter().zip(update.iter()) {
let i = idx.as_index(span)?;
match values.get_mut(i) {
Some(value) => {
*value = update.clone();
}
None => return Err(Error::IndexOutOfRange(idx, span)),
}
}
Ok(Value::Array(values.into()))
}
Loading

0 comments on commit 58a187a

Please sign in to comment.