summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-07-26 23:17:06 +0200
committerLaurenz <laurmaedje@gmail.com>2022-07-26 23:24:30 +0200
commit1e9a5eda48c65096b482b396d550d139a4c2e61d (patch)
treeab4a2ee6aa77d3e812eeb0ed93cd5d5bfc912d47 /src/eval
parentef866b0cd1b3ed0a02e9c0fbee07f4f0e46465cb (diff)
Rework array methods
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/array.rs153
-rw-r--r--src/eval/methods.rs15
2 files changed, 95 insertions, 73 deletions
diff --git a/src/eval/array.rs b/src/eval/array.rs
index aea1b0be..e54e7e19 100644
--- a/src/eval/array.rs
+++ b/src/eval/array.rs
@@ -3,9 +3,9 @@ use std::fmt::{self, Debug, Formatter, Write};
use std::ops::{Add, AddAssign};
use std::sync::Arc;
-use super::{ops, Args, Cast, Func, Machine, Value};
+use super::{ops, Args, Func, Machine, Value};
use crate::diag::{At, StrResult, TypResult};
-use crate::syntax::{Span, Spanned};
+use crate::syntax::Spanned;
use crate::util::ArcExt;
/// Create a new [`Array`] from values.
@@ -35,16 +35,21 @@ impl Array {
Self(Arc::new(vec))
}
- /// Whether the array is empty.
- pub fn is_empty(&self) -> bool {
- self.0.is_empty()
- }
-
/// 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) -> Option<&Value> {
+ self.0.first()
+ }
+
+ /// The last value in the array.
+ pub fn last(&self) -> Option<&Value> {
+ self.0.last()
+ }
+
/// Borrow the value at the given index.
pub fn get(&self, index: i64) -> StrResult<&Value> {
self.locate(index)
@@ -67,7 +72,7 @@ impl Array {
/// Remove the last value in the array.
pub fn pop(&mut self) -> StrResult<()> {
- Arc::make_mut(&mut self.0).pop().ok_or_else(|| "array is empty")?;
+ Arc::make_mut(&mut self.0).pop().ok_or_else(array_is_empty)?;
Ok(())
}
@@ -95,11 +100,6 @@ impl Array {
return Ok(());
}
- /// Whether the array contains a specific value.
- pub fn contains(&self, value: &Value) -> bool {
- self.0.contains(value)
- }
-
/// Extract a contigous subregion of the array.
pub fn slice(&self, start: i64, end: Option<i64>) -> StrResult<Self> {
let len = self.len();
@@ -118,38 +118,87 @@ impl Array {
Ok(Self::from_vec(self.0[start .. end].to_vec()))
}
+ /// 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 Machine, f: Spanned<Func>) -> TypResult<Option<Value>> {
+ for item in self.iter() {
+ let args = Args::new(f.span, [item.clone()]);
+ if f.v.call(vm, args)?.cast::<bool>().at(f.span)? {
+ return Ok(Some(item.clone()));
+ }
+ }
+
+ Ok(None)
+ }
+
+ /// Return the index of the first matching element.
+ pub fn position(&self, vm: &mut Machine, f: Spanned<Func>) -> TypResult<Option<i64>> {
+ for (i, item) in self.iter().enumerate() {
+ let args = Args::new(f.span, [item.clone()]);
+ if f.v.call(vm, args)?.cast::<bool>().at(f.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 Machine, f: Spanned<Func>) -> TypResult<Self> {
+ let mut kept = vec![];
+ for item in self.iter() {
+ let args = Args::new(f.span, [item.clone()]);
+ if f.v.call(vm, args)?.cast::<bool>().at(f.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 Machine, f: Spanned<Func>) -> TypResult<Self> {
let enumerate = f.v.argc() == Some(2);
Ok(self
.iter()
- .cloned()
.enumerate()
.map(|(i, item)| {
let mut args = Args::new(f.span, []);
if enumerate {
args.push(f.span, Value::Int(i as i64));
}
- args.push(f.span, item);
+ args.push(f.span, item.clone());
f.v.call(vm, args)
})
.collect::<TypResult<_>>()?)
}
- /// Return a new array with only those elements for which the function
- /// returns true.
- pub fn filter(&self, vm: &mut Machine, f: Spanned<Func>) -> TypResult<Self> {
- let mut kept = vec![];
+ /// Whether any element matches.
+ pub fn any(&self, vm: &mut Machine, f: Spanned<Func>) -> TypResult<bool> {
for item in self.iter() {
- if f.v
- .call(vm, Args::new(f.span, [item.clone()]))?
- .cast::<bool>()
- .at(f.span)?
- {
- kept.push(item.clone())
+ let args = Args::new(f.span, [item.clone()]);
+ if f.v.call(vm, args)?.cast::<bool>().at(f.span)? {
+ return Ok(true);
}
}
- Ok(Self::from_vec(kept))
+
+ Ok(false)
+ }
+
+ /// Whether all elements match.
+ pub fn all(&self, vm: &mut Machine, f: Spanned<Func>) -> TypResult<bool> {
+ for item in self.iter() {
+ let args = Args::new(f.span, [item.clone()]);
+ if !f.v.call(vm, args)?.cast::<bool>().at(f.span)? {
+ return Ok(false);
+ }
+ }
+
+ Ok(true)
}
/// Return a new array with all items from this and nested arrays.
@@ -165,15 +214,9 @@ impl Array {
Self::from_vec(flat)
}
- /// Return the index of the element if it is part of the array.
- pub fn find(&self, vm: &mut Machine, target: Target) -> TypResult<Option<i64>> {
- for (i, item) in self.iter().enumerate() {
- if target.matches(vm, item)? {
- return Ok(Some(i as i64));
- }
- }
-
- Ok(None)
+ /// 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
@@ -256,6 +299,12 @@ fn out_of_bounds(index: i64, len: i64) -> String {
format!("array index out of bounds (index: {}, len: {})", index, len)
}
+/// The error message when the array is empty.
+#[cold]
+fn array_is_empty() -> String {
+ "array is empty".into()
+}
+
impl Debug for Array {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_char('(')?;
@@ -319,37 +368,3 @@ impl<'a> IntoIterator for &'a Array {
self.iter()
}
}
-
-/// Something that can be found.
-pub enum Target {
- /// A bare value.
- Value(Value),
- /// A function that returns a boolean.
- Func(Func, Span),
-}
-
-impl Target {
- /// Whether the value is the search target.
- pub fn matches(&self, vm: &mut Machine, other: &Value) -> TypResult<bool> {
- match self {
- Self::Value(value) => Ok(value == other),
- Self::Func(f, span) => f
- .call(vm, Args::new(*span, [other.clone()]))?
- .cast::<bool>()
- .at(*span),
- }
- }
-}
-
-impl Cast<Spanned<Value>> for Target {
- fn is(_: &Spanned<Value>) -> bool {
- true
- }
-
- fn cast(value: Spanned<Value>) -> StrResult<Self> {
- Ok(match value.v {
- Value::Func(v) => Self::Func(v, value.span),
- v => Self::Value(v),
- })
- }
-}
diff --git a/src/eval/methods.rs b/src/eval/methods.rs
index 1ac0ce61..0e6f5af4 100644
--- a/src/eval/methods.rs
+++ b/src/eval/methods.rs
@@ -27,6 +27,8 @@ pub fn call(
Value::Array(array) => match method {
"len" => Value::Int(array.len()),
+ "first" => array.first().cloned().unwrap_or(Value::None),
+ "last" => array.last().cloned().unwrap_or(Value::None),
"slice" => {
let start = args.expect("start")?;
let mut end = args.eat()?;
@@ -35,12 +37,17 @@ pub fn call(
}
Value::Array(array.slice(start, end).at(span)?)
}
- "map" => Value::Array(array.map(vm, args.expect("function")?)?),
+ "contains" => Value::Bool(array.contains(&args.expect("value")?)),
+ "find" => array.find(vm, args.expect("function")?)?.unwrap_or(Value::None),
+ "position" => array
+ .position(vm, args.expect("function")?)?
+ .map_or(Value::None, Value::Int),
"filter" => Value::Array(array.filter(vm, args.expect("function")?)?),
+ "map" => Value::Array(array.map(vm, args.expect("function")?)?),
+ "any" => Value::Bool(array.any(vm, args.expect("function")?)?),
+ "all" => Value::Bool(array.all(vm, args.expect("function")?)?),
"flatten" => Value::Array(array.flatten()),
- "find" => array
- .find(vm, args.expect("value or function")?)?
- .map_or(Value::None, Value::Int),
+ "rev" => Value::Array(array.rev()),
"join" => {
let sep = args.eat()?;
let last = args.named("last")?;