Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/subst2 #215

Merged
merged 7 commits into from
Oct 27, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
...
  • Loading branch information
sebffischer committed Oct 27, 2024
commit 0ba583d6235b60631b32e26936c67ccb7c16c8f7
11 changes: 11 additions & 0 deletions src/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -16,7 +16,12 @@

## Noteable Bugs Addressed:

<<<<<<< Updated upstream
* `substitute()` now works on datatypes such as literals or calls.
=======
* `substitute()` now works on datatypes such as literals or calls (#199).
* accessing variable collected via 'rest-args' does now force evaluation of calls (#216).
>>>>>>> Stashed changes

## Internals

@@ -26,9 +31,15 @@
and unused ones removed.
* The `RepType` struct that was introduced in 0.4.0 was removed again (#189).
* `eval_list_eager()` was removed from the `Context` trait and added as a member method for `CallStack`.
<<<<<<< Updated upstream
* `eval_list_lazy()` now boxes all expressions in promises (including literals)
This is necessary to box `..a`-style ellipsis arguments in a list-call promise, which requires
access to the underlying expression.
=======
* `eval_list_lazy()` now boxes all expressions in promises (including literals).
This is necessary to box `..a`-style ellipsis arguments in a list-call promise, which requires
access to the underlying expression (needed to solve #216).
>>>>>>> Stashed changes

## Notable Bugs Addressed

53 changes: 27 additions & 26 deletions src/callable/core.rs
Original file line number Diff line number Diff line change
@@ -38,9 +38,6 @@ pub trait CallableFormals {
pub trait Callable: CallableFormals {
fn match_args(&self, args: List, stack: &mut CallStack) -> Result<(List, List), Signal> {
let mut formals = self.formals();

// this collects the expressions
let mut expr_ellipsis: ExprList = ExprList::new();
let mut ellipsis: List = List::new();
let mut matched_args: List = List::new();

@@ -70,52 +67,56 @@ pub trait Callable: CallableFormals {

// backfill unnamed args, populating ellipsis with overflow
for (key, value) in args.iter_pairs() {
match (key, value.clone()) {
match key {
// named args go directly to ellipsis, they did not match a formal
(Character::Some(arg), Obj::Promise(_, e, _)) => {
expr_ellipsis.push_named(Character::Some(arg.clone()).as_option(), e);
ellipsis.push_named(Character::Some(arg), value);
}
Character::Some(arg) => ellipsis.push_named(Character::Some(arg), value),

// unnamed args populate next formal, or ellipsis if formals exhausted
(Character::NA, value) => {
Character::NA => {
let next_unassigned_formal = formals.remove(0);
if let Some((Some(param), _)) = next_unassigned_formal {
matched_args.push_named(Character::Some(param), value);
} else {
let Obj::Promise(_, e, _) = value.clone() else {
unreachable!()
};
expr_ellipsis.push_named(Character::NA.as_option(), e);
ellipsis.push_named(Character::NA, value);
}
}
_ => unreachable!(),
}
}

// add back in parameter defaults that weren't filled with args
for (param, default) in formals.into_iter() {
matched_args.push_named(
param.into(),
Obj::Promise(None, default, stack.last_frame().env().clone()),
)
}
let mut ellipsis_expr = ExprList::new();

use crate::callable::builtins::BUILTIN;
for (k, v) in ellipsis.iter_pairs() {
if let Obj::Promise(_, e, _) = v {
ellipsis_expr.push_named(k.as_option(), e)
} else {
// all arguments must be boxed in promises to allow for NSE
unreachable!()
}
}

let list = BUILTIN.get("list").cloned().unwrap();
let list = crate::callable::builtins::BUILTIN
.get("list")
.cloned()
.unwrap();

// convert the expr_ellipsis to an Obj::Promise where the expression is a call into List

let a = Obj::Promise(
let ellipsis_promise = Obj::Promise(
None,
Expr::Call(Box::new(Expr::Primitive(list)), expr_ellipsis),
Expr::Call(Box::new(Expr::Primitive(list)), ellipsis_expr),
stack.last_frame().env().clone(),
);

// add back in parameter defaults that weren't filled with args
for (param, default) in formals.into_iter() {
matched_args.push_named(
param.into(),
Obj::Promise(None, default, stack.last_frame().env().clone()),
)
}

if let Some(Expr::Ellipsis(Some(name))) = remainder.get(0) {
matched_args.push_named(Character::Some(name), a);
matched_args.push_named(Character::Some(name), ellipsis_promise);
} else if !remainder.is_empty() {
matched_args.push_named(
Character::Some("...".to_string()),
Loading