From 6ab7760822ccd24b4ef126d4737d41f1be15fe19 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 1 Mar 2023 16:30:58 +0100 Subject: Split up `model` module --- src/eval/array.rs | 406 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 406 insertions(+) create mode 100644 src/eval/array.rs (limited to 'src/eval/array.rs') diff --git a/src/eval/array.rs b/src/eval/array.rs new file mode 100644 index 00000000..53bae06f --- /dev/null +++ b/src/eval/array.rs @@ -0,0 +1,406 @@ +use std::cmp::Ordering; +use std::fmt::{self, Debug, Formatter, Write}; +use std::ops::{Add, AddAssign}; + +use ecow::{eco_format, EcoString, EcoVec}; + +use super::{ops, Args, Func, Value, Vm}; +use crate::diag::{bail, At, SourceResult, StrResult}; + +/// Create a new [`Array`] from values. +#[macro_export] +#[doc(hidden)] +macro_rules! __array { + ($value:expr; $count:expr) => { + $crate::eval::Array::from_vec($crate::eval::eco_vec![$value.into(); $count]) + }; + + ($($value:expr),* $(,)?) => { + $crate::eval::Array::from_vec($crate::eval::eco_vec![$($value.into()),*]) + }; +} + +#[doc(inline)] +pub use crate::__array as array; +#[doc(hidden)] +pub use ecow::eco_vec; + +/// A reference counted array with value semantics. +#[derive(Default, Clone, PartialEq, Hash)] +pub struct Array(EcoVec); + +impl Array { + /// Create a new, empty array. + pub fn new() -> Self { + Self::default() + } + + /// Create a new array from an eco vector of values. + pub fn from_vec(vec: EcoVec) -> Self { + Self(vec) + } + + /// The length of the array. + pub fn len(&self) -> i64 { + self.0.len() as i64 + } + + /// The first value in the array. + pub fn first(&self) -> StrResult<&Value> { + self.0.first().ok_or_else(array_is_empty) + } + + /// Mutably borrow the first value in the array. + pub fn first_mut(&mut self) -> StrResult<&mut Value> { + self.0.make_mut().first_mut().ok_or_else(array_is_empty) + } + + /// The last value in the array. + pub fn last(&self) -> StrResult<&Value> { + self.0.last().ok_or_else(array_is_empty) + } + + /// Mutably borrow the last value in the array. + pub fn last_mut(&mut self) -> StrResult<&mut Value> { + self.0.make_mut().last_mut().ok_or_else(array_is_empty) + } + + /// Borrow the value at the given index. + pub fn at(&self, index: i64) -> StrResult<&Value> { + self.locate(index) + .and_then(|i| self.0.get(i)) + .ok_or_else(|| out_of_bounds(index, self.len())) + } + + /// Mutably borrow the value at the given index. + pub fn at_mut(&mut self, index: i64) -> StrResult<&mut Value> { + let len = self.len(); + self.locate(index) + .and_then(move |i| self.0.make_mut().get_mut(i)) + .ok_or_else(|| out_of_bounds(index, len)) + } + + /// Push a value to the end of the array. + pub fn push(&mut self, value: Value) { + self.0.push(value); + } + + /// Remove the last value in the array. + pub fn pop(&mut self) -> StrResult { + self.0.pop().ok_or_else(array_is_empty) + } + + /// Insert a value at the specified index. + pub fn insert(&mut self, index: i64, value: Value) -> StrResult<()> { + let len = self.len(); + let i = self + .locate(index) + .filter(|&i| i <= self.0.len()) + .ok_or_else(|| out_of_bounds(index, len))?; + + self.0.insert(i, value); + Ok(()) + } + + /// Remove and return the value at the specified index. + pub fn remove(&mut self, index: i64) -> StrResult { + let len = self.len(); + let i = self + .locate(index) + .filter(|&i| i < self.0.len()) + .ok_or_else(|| out_of_bounds(index, len))?; + + Ok(self.0.remove(i)) + } + + /// Extract a contigous subregion of the array. + pub fn slice(&self, start: i64, end: Option) -> StrResult { + let len = self.len(); + let start = self + .locate(start) + .filter(|&start| start <= self.0.len()) + .ok_or_else(|| out_of_bounds(start, len))?; + + let end = end.unwrap_or(self.len()); + let end = self + .locate(end) + .filter(|&end| end <= self.0.len()) + .ok_or_else(|| out_of_bounds(end, len))? + .max(start); + + Ok(Self::from_vec(self.0[start..end].into())) + } + + /// Whether the array contains a specific value. + pub fn contains(&self, value: &Value) -> bool { + self.0.contains(value) + } + + /// Return the first matching element. + pub fn find(&self, vm: &mut Vm, func: Func) -> SourceResult> { + if func.argc().map_or(false, |count| count != 1) { + bail!(func.span(), "function must have exactly one parameter"); + } + for item in self.iter() { + let args = Args::new(func.span(), [item.clone()]); + if func.call(vm, args)?.cast::().at(func.span())? { + return Ok(Some(item.clone())); + } + } + + Ok(None) + } + + /// Return the index of the first matching element. + pub fn position(&self, vm: &mut Vm, func: Func) -> SourceResult> { + if func.argc().map_or(false, |count| count != 1) { + bail!(func.span(), "function must have exactly one parameter"); + } + for (i, item) in self.iter().enumerate() { + let args = Args::new(func.span(), [item.clone()]); + if func.call(vm, args)?.cast::().at(func.span())? { + return Ok(Some(i as i64)); + } + } + + Ok(None) + } + + /// Return a new array with only those elements for which the function + /// returns true. + pub fn filter(&self, vm: &mut Vm, func: Func) -> SourceResult { + if func.argc().map_or(false, |count| count != 1) { + bail!(func.span(), "function must have exactly one parameter"); + } + let mut kept = EcoVec::new(); + for item in self.iter() { + let args = Args::new(func.span(), [item.clone()]); + if func.call(vm, args)?.cast::().at(func.span())? { + kept.push(item.clone()) + } + } + Ok(Self::from_vec(kept)) + } + + /// Transform each item in the array with a function. + pub fn map(&self, vm: &mut Vm, func: Func) -> SourceResult { + if func.argc().map_or(false, |count| !(1..=2).contains(&count)) { + bail!(func.span(), "function must have one or two parameters"); + } + let enumerate = func.argc() == Some(2); + self.iter() + .enumerate() + .map(|(i, item)| { + let mut args = Args::new(func.span(), []); + if enumerate { + args.push(func.span(), Value::Int(i as i64)); + } + args.push(func.span(), item.clone()); + func.call(vm, args) + }) + .collect() + } + + /// Fold all of the array's elements into one with a function. + pub fn fold(&self, vm: &mut Vm, init: Value, func: Func) -> SourceResult { + if func.argc().map_or(false, |count| count != 2) { + bail!(func.span(), "function must have exactly two parameters"); + } + let mut acc = init; + for item in self.iter() { + let args = Args::new(func.span(), [acc, item.clone()]); + acc = func.call(vm, args)?; + } + Ok(acc) + } + + /// Whether any element matches. + pub fn any(&self, vm: &mut Vm, func: Func) -> SourceResult { + if func.argc().map_or(false, |count| count != 1) { + bail!(func.span(), "function must have exactly one parameter"); + } + for item in self.iter() { + let args = Args::new(func.span(), [item.clone()]); + if func.call(vm, args)?.cast::().at(func.span())? { + return Ok(true); + } + } + + Ok(false) + } + + /// Whether all elements match. + pub fn all(&self, vm: &mut Vm, func: Func) -> SourceResult { + if func.argc().map_or(false, |count| count != 1) { + bail!(func.span(), "function must have exactly one parameter"); + } + for item in self.iter() { + let args = Args::new(func.span(), [item.clone()]); + if !func.call(vm, args)?.cast::().at(func.span())? { + return Ok(false); + } + } + + Ok(true) + } + + /// Return a new array with all items from this and nested arrays. + pub fn flatten(&self) -> Self { + let mut flat = EcoVec::with_capacity(self.0.len()); + for item in self.iter() { + if let Value::Array(nested) = item { + flat.extend(nested.flatten().into_iter()); + } else { + flat.push(item.clone()); + } + } + Self::from_vec(flat) + } + + /// Returns a new array with reversed order. + pub fn rev(&self) -> Self { + self.0.iter().cloned().rev().collect() + } + + /// Join all values in the array, optionally with separator and last + /// separator (between the final two items). + pub fn join(&self, sep: Option, mut last: Option) -> StrResult { + let len = self.0.len(); + let sep = sep.unwrap_or(Value::None); + + let mut result = Value::None; + for (i, value) in self.iter().cloned().enumerate() { + if i > 0 { + if i + 1 == len && last.is_some() { + result = ops::join(result, last.take().unwrap())?; + } else { + result = ops::join(result, sep.clone())?; + } + } + + result = ops::join(result, value)?; + } + + Ok(result) + } + + /// Return a sorted version of this array. + /// + /// Returns an error if two values could not be compared. + pub fn sorted(&self) -> StrResult { + let mut result = Ok(()); + let mut vec = self.0.clone(); + vec.make_mut().sort_by(|a, b| { + a.partial_cmp(b).unwrap_or_else(|| { + if result.is_ok() { + result = Err(eco_format!( + "cannot order {} and {}", + a.type_name(), + b.type_name(), + )); + } + Ordering::Equal + }) + }); + result.map(|_| Self::from_vec(vec)) + } + + /// Repeat this array `n` times. + pub fn repeat(&self, n: i64) -> StrResult { + let count = usize::try_from(n) + .ok() + .and_then(|n| self.0.len().checked_mul(n)) + .ok_or_else(|| format!("cannot repeat this array {} times", n))?; + + Ok(self.iter().cloned().cycle().take(count).collect()) + } + + /// Extract a slice of the whole array. + pub fn as_slice(&self) -> &[Value] { + self.0.as_slice() + } + + /// Iterate over references to the contained values. + pub fn iter(&self) -> std::slice::Iter { + self.0.iter() + } + + /// Resolve an index. + fn locate(&self, index: i64) -> Option { + usize::try_from(if index >= 0 { index } else { self.len().checked_add(index)? }) + .ok() + } +} + +/// The out of bounds access error message. +#[cold] +fn out_of_bounds(index: i64, len: i64) -> EcoString { + eco_format!("array index out of bounds (index: {}, len: {})", index, len) +} + +/// The error message when the array is empty. +#[cold] +fn array_is_empty() -> EcoString { + "array is empty".into() +} + +impl Debug for Array { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.write_char('(')?; + for (i, value) in self.iter().enumerate() { + value.fmt(f)?; + if i + 1 < self.0.len() { + f.write_str(", ")?; + } + } + if self.len() == 1 { + f.write_char(',')?; + } + f.write_char(')') + } +} + +impl Add for Array { + type Output = Self; + + fn add(mut self, rhs: Array) -> Self::Output { + self += rhs; + self + } +} + +impl AddAssign for Array { + fn add_assign(&mut self, rhs: Array) { + self.0.extend(rhs.0); + } +} + +impl Extend for Array { + fn extend>(&mut self, iter: T) { + self.0.extend(iter); + } +} + +impl FromIterator for Array { + fn from_iter>(iter: T) -> Self { + Self(iter.into_iter().collect()) + } +} + +impl IntoIterator for Array { + type Item = Value; + type IntoIter = ecow::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> IntoIterator for &'a Array { + type Item = &'a Value; + type IntoIter = std::slice::Iter<'a, Value>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} -- cgit v1.2.3