Rework array methods
This commit is contained in:
parent
ef866b0cd1
commit
1e9a5eda48
@ -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,22 +118,33 @@ impl Array {
|
||||
Ok(Self::from_vec(self.0[start .. end].to_vec()))
|
||||
}
|
||||
|
||||
/// 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);
|
||||
f.v.call(vm, args)
|
||||
})
|
||||
.collect::<TypResult<_>>()?)
|
||||
/// 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
|
||||
@ -141,17 +152,55 @@ impl Array {
|
||||
pub fn filter(&self, vm: &mut Machine, f: Spanned<Func>) -> TypResult<Self> {
|
||||
let mut kept = vec![];
|
||||
for item in self.iter() {
|
||||
if f.v
|
||||
.call(vm, Args::new(f.span, [item.clone()]))?
|
||||
.cast::<bool>()
|
||||
.at(f.span)?
|
||||
{
|
||||
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()
|
||||
.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.clone());
|
||||
f.v.call(vm, args)
|
||||
})
|
||||
.collect::<TypResult<_>>()?)
|
||||
}
|
||||
|
||||
/// Whether any element matches.
|
||||
pub fn any(&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(true);
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
pub fn flatten(&self) -> Self {
|
||||
let mut flat = Vec::with_capacity(self.0.len());
|
||||
@ -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),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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")?)?),
|
||||
"filter" => Value::Array(array.filter(vm, args.expect("function")?)?),
|
||||
"flatten" => Value::Array(array.flatten()),
|
||||
"find" => array
|
||||
.find(vm, args.expect("value or 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()),
|
||||
"rev" => Value::Array(array.rev()),
|
||||
"join" => {
|
||||
let sep = args.eat()?;
|
||||
let last = args.named("last")?;
|
||||
|
@ -8,6 +8,15 @@
|
||||
#test("Hello World!".len(), 12)
|
||||
#test((a: 1, b: 2).len(), 2)
|
||||
|
||||
---
|
||||
// The the `first` and `last` methods.
|
||||
#test(().first(), none)
|
||||
#test(().last(), none)
|
||||
#test((1,).first(), 1)
|
||||
#test((2,).last(), 2)
|
||||
#test((1, 2, 3).first(), 1)
|
||||
#test((1, 2, 3).last(), 3)
|
||||
|
||||
---
|
||||
// Test the `push` and `pop` methods.
|
||||
{
|
||||
@ -29,16 +38,16 @@
|
||||
}
|
||||
|
||||
---
|
||||
// Test the `find` method.
|
||||
#test(("Hi", "❤️", "Love").find("❤️"), 1)
|
||||
#test(("Bye", "💘", "Apart").find("❤️"), none)
|
||||
#test(("A", "B", "CDEF", "G").find(v => v.len() > 2), 2)
|
||||
// Error: 2:17-2:19 missing argument: index
|
||||
#let numbers = ()
|
||||
{ numbers.insert() }
|
||||
|
||||
---
|
||||
// Test the `slice` method.
|
||||
#test((1, 2, 3, 4).slice(2), (3, 4))
|
||||
#test(range(10).slice(2, 6), (2, 3, 4, 5))
|
||||
#test(range(10).slice(4, count: 3), (4, 5, 6))
|
||||
#test(range(10).slice(-5, count: 2), (5, 6))
|
||||
#test((1, 2, 3).slice(2, -2), ())
|
||||
#test((1, 2, 3).slice(-2, 2), (2,))
|
||||
#test((1, 2, 3).slice(-3, 2), (1, 2))
|
||||
@ -53,9 +62,14 @@
|
||||
{ (1, 2, 3).slice(0, -4) }
|
||||
|
||||
---
|
||||
// Error: 2:17-2:19 missing argument: index
|
||||
#let numbers = ()
|
||||
{ numbers.insert() }
|
||||
// Test the `position` method.
|
||||
#test(("Hi", "❤️", "Love").position(s => s == "❤️"), 1)
|
||||
#test(("Bye", "💘", "Apart").position(s => s == "❤️"), none)
|
||||
#test(("A", "B", "CDEF", "G").position(v => v.len() > 2), 2)
|
||||
|
||||
---
|
||||
// Test the `rev` method.
|
||||
#test(range(3).rev(), (2, 1, 0))
|
||||
|
||||
---
|
||||
// Test the `join` method.
|
||||
|
Loading…
x
Reference in New Issue
Block a user