mirror of
https://github.com/typst/typst
synced 2025-05-13 12:36:23 +08:00
Find optionally takes function instead of value
This commit is contained in:
parent
757615dc42
commit
ccb4753e24
@ -3,9 +3,9 @@ use std::fmt::{self, Debug, Formatter, Write};
|
|||||||
use std::ops::{Add, AddAssign};
|
use std::ops::{Add, AddAssign};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::{ops, Args, Func, Machine, Value};
|
use super::{ops, Args, Cast, Func, Machine, Value};
|
||||||
use crate::diag::{At, StrResult, TypResult};
|
use crate::diag::{At, StrResult, TypResult};
|
||||||
use crate::syntax::Spanned;
|
use crate::syntax::{Span, Spanned};
|
||||||
use crate::util::ArcExt;
|
use crate::util::ArcExt;
|
||||||
|
|
||||||
/// Create a new [`Array`] from values.
|
/// Create a new [`Array`] from values.
|
||||||
@ -120,10 +120,19 @@ impl Array {
|
|||||||
|
|
||||||
/// Transform each item in the array with a function.
|
/// Transform each item in the array with a function.
|
||||||
pub fn map(&self, vm: &mut Machine, f: Spanned<Func>) -> TypResult<Self> {
|
pub fn map(&self, vm: &mut Machine, f: Spanned<Func>) -> TypResult<Self> {
|
||||||
|
let enumerate = f.v.argc() == Some(2);
|
||||||
Ok(self
|
Ok(self
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|item| f.v.call(vm, Args::new(f.span, [item])))
|
.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<_>>()?)
|
.collect::<TypResult<_>>()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,8 +166,14 @@ impl Array {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return the index of the element if it is part of the array.
|
/// Return the index of the element if it is part of the array.
|
||||||
pub fn find(&self, value: Value) -> Option<i64> {
|
pub fn find(&self, vm: &mut Machine, target: Target) -> TypResult<Option<i64>> {
|
||||||
self.0.iter().position(|x| *x == value).map(|i| i as i64)
|
for (i, item) in self.iter().enumerate() {
|
||||||
|
if target.matches(vm, item)? {
|
||||||
|
return Ok(Some(i as i64));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Join all values in the array, optionally with separator and last
|
/// Join all values in the array, optionally with separator and last
|
||||||
@ -304,3 +319,37 @@ impl<'a> IntoIterator for &'a Array {
|
|||||||
self.iter()
|
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),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -38,7 +38,9 @@ pub fn call(
|
|||||||
"map" => Value::Array(array.map(vm, args.expect("function")?)?),
|
"map" => Value::Array(array.map(vm, args.expect("function")?)?),
|
||||||
"filter" => Value::Array(array.filter(vm, args.expect("function")?)?),
|
"filter" => Value::Array(array.filter(vm, args.expect("function")?)?),
|
||||||
"flatten" => Value::Array(array.flatten()),
|
"flatten" => Value::Array(array.flatten()),
|
||||||
"find" => array.find(args.expect("value")?).map_or(Value::None, Value::Int),
|
"find" => array
|
||||||
|
.find(vm, args.expect("value or function")?)?
|
||||||
|
.map_or(Value::None, Value::Int),
|
||||||
"join" => {
|
"join" => {
|
||||||
let sep = args.eat()?;
|
let sep = args.eat()?;
|
||||||
let last = args.named("last")?;
|
let last = args.named("last")?;
|
||||||
|
@ -713,9 +713,9 @@ castable! {
|
|||||||
castable! {
|
castable! {
|
||||||
Pattern,
|
Pattern,
|
||||||
Expected: "function, string or regular expression",
|
Expected: "function, string or regular expression",
|
||||||
Value::Func(func) => Pattern::Node(func.node()?),
|
Value::Func(func) => Self::Node(func.node()?),
|
||||||
Value::Str(text) => Pattern::text(&text),
|
Value::Str(text) => Self::text(&text),
|
||||||
@regex: Regex => Pattern::Regex(regex.clone()),
|
@regex: Regex => Self::Regex(regex.clone()),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
// Test the `find` method.
|
// Test the `find` method.
|
||||||
#test(("Hi", "❤️", "Love").find("❤️"), 1)
|
#test(("Hi", "❤️", "Love").find("❤️"), 1)
|
||||||
#test(("Bye", "💘", "Apart").find("❤️"), none)
|
#test(("Bye", "💘", "Apart").find("❤️"), none)
|
||||||
|
#test(("A", "B", "CDEF", "G").find(v => v.len() > 2), 2)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test the `slice` method.
|
// Test the `slice` method.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user