1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//! Implementation of the filter() builtin function.
//!
//! This module provides the filter() builtin which filters elements from an iterable
//! based on a predicate function. The implementation supports:
//! - `None` as predicate (filters falsy values)
//! - Builtin functions (len, abs, etc.)
//! - Type constructors (int, str, float, etc.)
//! - User-defined functions (via `vm.evaluate_function`)
use crate::{
args::ArgValues,
bytecode::VM,
defer_drop,
exception_private::RunResult,
heap::{DropGuard, HeapData},
types::{List, PyTrait},
value::Value,
};
/// Implementation of the filter() builtin function.
///
/// Filters elements from an iterable based on a predicate function.
/// If the predicate is None, filters out falsy values.
///
/// Note: In Python this returns an iterator, but we return a list for simplicity.
///
/// Examples:
/// ```python
/// filter(lambda x: x > 0, [-1, 0, 1, 2]) # [1, 2]
/// filter(None, [0, 1, False, True, '']) # [1, True]
/// ```
pub fn builtin_filter(vm: &mut VM<'_>, args: ArgValues) -> RunResult<Value> {
let (function, iterable) = args.get_two_args("filter", vm.heap)?;
defer_drop!(function, vm);
let iter = iterable.into_py_iter(vm)?;
defer_drop!(iter, vm);
let mut iter = iter.read(vm);
let out: Vec<Value> = Vec::new();
let mut out_guard = DropGuard::new(out, vm);
let (out, vm) = out_guard.as_parts_mut();
while let Some(item) = iter.py_next(vm)? {
let mut item_guard = DropGuard::new(item, vm);
let (item, vm) = item_guard.as_parts_mut();
let should_include = if let Value::None = function {
// No predicate - use truthiness of element
item.py_bool(vm)?
} else {
// Clone for predicate call - the clone is consumed by evaluate_function
let item_for_predicate = item.clone_with_heap(vm);
let result = vm.evaluate_function("filter()", function, ArgValues::One(item_for_predicate))?;
let is_truthy = result.py_bool(vm);
result.drop_with(vm);
is_truthy?
};
if should_include {
out.push(item_guard.into_inner());
}
}
let (out, vm) = out_guard.into_parts();
let heap_id = vm.heap.allocate(HeapData::List(List::new(out)));
Ok(Value::Ref(heap_id))
}