Break up evalList() and document the new functions

This commit is contained in:
Sage Vaillancourt 2020-11-03 14:35:47 -05:00
parent a39c6b2c53
commit d8d8231f0d
1 changed files with 119 additions and 47 deletions

View File

@ -110,15 +110,116 @@ Object evalBuiltIns(const Object *first, const Object *rest,
return errorObject(BUILT_IN_NOT_FOUND);
}
void evalForms(Object *destList, const Object *src, struct Environment *env)
/**
* Bulk evaluator of Objects in a given list. Puts the results in a given array
*
* Note that `destArr` is a raw array, not an Object list, and that `start` is
* not itself a list, but the first element *in* a list to be evaluated. This
* allows more speed and flexibility in what should be evaluated.
*
* @param destArr The raw array to put the results into. Not an Object list!
* @param start The Object element to start with
* @param env The environment to use while evaluating
*/
void evalForms(Object *destArr, const Object *start, struct Environment *env)
{
int length = listLength(src) - 1; // Not counting first_form
for(int i = 0; i < length; i++) { // Evaluates all in list
Object *ptr = itemAt(src, i + 1); // Skip the first
destList[i] = eval(ptr, env);
int i = 0;
while(start) {
destArr[i] = eval(start, env);
start = start->forward;
i++;
}
}
/**
* Evaluates a list whose first element is a function, applying that function
*
* Tries to either apply the function to its parameters, or create a partial
* function, if not enough parameters were passed.
*
* @param list The list being evaluated
* @param function First element of the list, already evaluated to be a function
* @param length Length of `list` - 1, to exclude the already-evaluated element
* @param env The environment to evaluate in
*/
Object listEvalFunc(
const Object *list,
const Object *function,
const int length,
struct Environment *env)
{
Object rest[length];
evalForms(rest, list->list->forward, env);
Object func_result = rest[0];
if(length == 1) {
func_result = function->func(
func_result, errorObject(ONLY_ONE_ARGUMENT), env);
// Return a partial function if more parameters are required
// Otherwise, return the function result
return isError(func_result, ONLY_ONE_ARGUMENT) ?
cloneObject(*list) :
func_result;
} else {
// With two args, will apply function once
// With more than two args, apply the function repeatedly
for(int i = 1; i < length; i++) {
Object toClean = func_result;
func_result = function->func(func_result, rest[i], env);
cleanObject(&toClean);
cleanObject(&rest[i]);
}
return func_result;
}
}
/**
* Evaluates a list whose first element is a lambda, applying that lambda
*
* Tries to apply the lambda to its parameters. Doesn't attempt partial
* application.
*
* @param lambda First element of the list, already evaluated to be a lambda
* @param remaining The first element after `lambda`
* @param env The environment to evaluate in
*/
Object listEvalLambda(
Object *lambda,
const Object *remaining,
struct Environment *env)
{
struct Environment newEnv = envForLambda(
&lambda->lambda->params,
remaining,
env
);
Object ret = eval(&lambda->lambda->body, &newEnv);
deleteEnv(&newEnv);
cleanObject(lambda);
Object *t = tail(&ret);
if(t) {
// cleanObject(&ret);
return cloneObject(*t);
}
return ret;
}
/**
* Evaluates a given list, including the application of functions and lambdas
*
* Engages in several behaviors, depending on list contents:
* - () => ()
* - (1item) => eval_1item // Questionable behavior!
* - (function x y) => evaluated function(x, y)
* - (function ...) => evaluated function(...) applied to each arg
* - (function x) => functionx() partial function
* - (lambda x) => evaluated lambda(x)
* - (x y z) => (eval_x eval_y eval_z)
*
* @param obj The list to be evaluated
* @param env The environment to evaluate in
*/
Object evalList(const Object *obj, struct Environment *env)
{
const int evalLength = listLength(obj);
@ -144,53 +245,24 @@ Object evalList(const Object *obj, struct Environment *env)
}
}
// Evaluate the list based on the first element's type
Object first_eval = eval(first_form, env);
if(first_eval.type == TYPE_FUNC) {
int length = evalLength - 1; // Not counting first_form
Object rest[length];
evalForms(rest, obj, env);
switch(first_eval.type) {
case TYPE_FUNC:
// Passes evalLength - 1, because we skip the first form
return listEvalFunc(obj, &first_eval, evalLength - 1, env);
Object func_eval = rest[0];
if(length == 1) {
func_eval = first_eval.func(
func_eval, errorObject(ONLY_ONE_ARGUMENT), env);
// Consider it a partial function if only one arg
if(isError(func_eval, ONLY_ONE_ARGUMENT))
return cloneObject(*obj);
} else {
for(int i = 1; i < length; i++) {
Object toClean = func_eval;
func_eval = first_eval.func(func_eval, rest[i], env);
cleanObject(&toClean);
cleanObject(&rest[i]);
}
}
case TYPE_LAMBDA:
return listEvalLambda(&first_eval, first_form->forward, env);
return func_eval;
} else if (first_eval.type == TYPE_LAMBDA) {
struct Environment newEnv = envForLambda(
&first_eval.lambda->params,
first_form->forward,
env
);
Object ret = eval(&first_eval.lambda->body, &newEnv);
deleteEnv(&newEnv);
cleanObject(&first_eval);
Object *t = tail(&ret);
if(t) {
// cleanObject(&ret);
return cloneObject(*t);
}
return ret;
} else {
default: { // Return list with each element evaluated
Object list = listObject();
FOR_POINTER_IN_LIST(obj) {
nf_addToList(&list, eval(POINTER, env));
}
return list;
}
}
}
Object eval(const Object *obj, struct Environment *env)