Skip to content

Commit bfdec35

Browse files
committed
add filter buildin
1 parent 9863496 commit bfdec35

File tree

2 files changed

+63
-5
lines changed

2 files changed

+63
-5
lines changed

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
### Standard Library:
2020

2121
> [!NOTE]
22-
> `filter` and `sort` are not implemented yet.
2322
2423
- **I/O**:
2524
- `print` - Prints a value to the console.
@@ -29,8 +28,7 @@
2928
- `tail` - Returns all elements of an array except the first.
3029
- `get` - Returns the element of an array specified by its index.
3130
- `map` - Applies a function to each element of an array and returns a new array.
32-
- `filter` - Filters an array based on a predicate function.
33-
- `sort` - Sorts an array.
31+
- `filter` - Filters an array based on a predicate function and returns a new (filtered)array.
3432
- `reverse` - Reverses an array.
3533
- **Functional Utilities**:
3634
- `fold` - Reduces an array to a single value using a function.

src/evaluator/builtins.rs

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,69 @@ fn lang_map(args: Vec<Object>) -> Object {
281281
}
282282
}
283283

284-
// TODO
285284
fn lang_filter(args: Vec<Object>) -> Object {
286-
Object::Error(String::from("TODO: filter is not implemented yet"))
285+
if args.len() != 2 {
286+
return Object::Error(format!(
287+
"wrong number of arguments to filter: got={}, want=2",
288+
args.len()
289+
));
290+
}
291+
292+
match (&args[0], &args[1]) {
293+
(Object::Array(arr), Object::Func(params, body, env)) => {
294+
let mut new_array: Vec<Object> = Vec::new();
295+
296+
// We need to make sure the function accepts one argument
297+
if params.len() != 1 {
298+
return Object::Error(format!(
299+
"filter function expects a function with exactly one parameter, got {} parameters",
300+
params.len()
301+
));
302+
}
303+
304+
for item in arr {
305+
// Create a new environment for each function call, with the closure env as outer
306+
let mut scoped_env = Env::new_with_outer(Rc::clone(env));
307+
308+
// Bind the current array item to the function's parameter
309+
let Ident(param_name) = params[0].clone();
310+
scoped_env.set(param_name, item);
311+
312+
// Create a new evaluator with this scoped environment
313+
let mut evaluator = Evaluator::new(Rc::new(RefCell::new(scoped_env)));
314+
315+
// Evaluate the function body
316+
let result = match evaluator.eval_block_stmt(body.clone()) {
317+
Some(Object::ReturnValue(value)) => *value,
318+
Some(obj) => {
319+
if let Object::Error(_) = obj {
320+
return obj;
321+
}
322+
obj
323+
}
324+
None => Object::Null,
325+
};
326+
327+
// Only include the item if the function returns a truthy value
328+
if Evaluator::is_truthy(result) {
329+
new_array.push(item.clone());
330+
}
331+
}
332+
333+
Object::Array(new_array)
334+
}
335+
(Object::Array(_), Object::Builtin(_, _)) => Object::Error(format!(
336+
"cannot use builtin functions with filter yet; use a function literal"
337+
)),
338+
(Object::Array(_), not_func) => Object::Error(format!(
339+
"second argument to `filter` must be a function, got {}",
340+
not_func
341+
)),
342+
(not_array, _) => Object::Error(format!(
343+
"first argument to `filter` must be an array, got {}",
344+
not_array
345+
)),
346+
}
287347
}
288348

289349
// TODO

0 commit comments

Comments
 (0)