diff --git a/src/pebblisp.c b/src/pebblisp.c index 58d8cb8..cbab1cc 100644 --- a/src/pebblisp.c +++ b/src/pebblisp.c @@ -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,52 +245,23 @@ 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); + + default: { // Return list with each element evaluated + Object list = listObject(); + FOR_POINTER_IN_LIST(obj) { + nf_addToList(&list, eval(POINTER, env)); } + return list; } - - 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 { - Object list = listObject(); - FOR_POINTER_IN_LIST(obj) { - nf_addToList(&list, eval(POINTER, env)); - } - return list; } }