Ensure plain lists have elements evaluated

Add relevant tests. Add new doc comments. Some code shifting.
Tests pass. Valgrind is clean.
This commit is contained in:
= 2020-05-16 15:46:19 +01:00
parent efb151d0ef
commit c97cbe850c
4 changed files with 125 additions and 38 deletions

View File

@ -142,17 +142,17 @@ void insertIntoList(Object *dest, int ind, const Object src)
return; return;
} }
// The objects to preceed and follow the new one // The Objects to preceed and follow the new one
Object *beforeNew = itemAt(dest, ind - 1); Object *beforeNew = itemAt(dest, ind - 1);
Object *afterNew = beforeNew->forward; Object *afterNew = beforeNew->forward;
// Replace the `before` object's pointer // Replace the `before` Object's pointer
allocObject(&beforeNew->forward, src); allocObject(&beforeNew->forward, src);
beforeNew->forward->forward = afterNew; beforeNew->forward->forward = afterNew;
} }
/** /**
* Replace an object in a list at a given index with a given object * Replace an Object in a list at a given index with a given Object
* Attempts to clean the replaced Object before adding the new one * Attempts to clean the replaced Object before adding the new one
* Immediately returns if `list` is NULL or not a list type * Immediately returns if `list` is NULL or not a list type
* @param list The list Object to replace an element of * @param list The list Object to replace an element of
@ -189,6 +189,11 @@ void nf_addToList(Object *dest, const Object src)
allocObject(&tail(dest)->forward, src); allocObject(&tail(dest)->forward, src);
} }
/**
* Prints out the error of a given error Object
* Doesn't print anything if `obj` is NULL or not an error type
* @param obj A pointer to the Object to print
*/
void printErr(const Object *obj) void printErr(const Object *obj)
{ {
if(!obj || obj->type != TYPE_ERROR) if(!obj || obj->type != TYPE_ERROR)
@ -197,8 +202,18 @@ void printErr(const Object *obj)
printf("%s\n", errorText[(int)(obj->err)]); printf("%s\n", errorText[(int)(obj->err)]);
} }
/**
* Creates a string from a given list Object
* Blocks out with parens
* Returns immediately if `dest` is NULL, or `obj` is not a list type
* @param dest The string to copy the list text into
* @param obj The list Object to make a string from
*/
void stringList(char *dest, const Object *obj) void stringList(char *dest, const Object *obj)
{ {
if(!dest || obj->type != TYPE_LIST)
return;
dest[0] = '('; dest[0] = '(';
dest[1] = '\0'; dest[1] = '\0';
@ -211,7 +226,22 @@ void stringList(char *dest, const Object *obj)
strcat(dest, " )"); strcat(dest, " )");
} }
// Puts a string version of the given object into a given string /**
* Creates a string from a given Object
* Returns NULL if either param is NULL
*
* Prints
* -# Numbers without spaces
* -# Symbols as their name
* -# Bools as 'T' or 'F'
* -# Strings as their string value
* -# Lists using stringList()
* -# Errors as the integer value, prepended with 'E'
* -# Otherwise as the raw number, prepended with 'X'
*
* @param dest The string to copy the list text into
* @param obj The list Object to make a string from
*/
char* stringObj(char *dest, const Object *obj) char* stringObj(char *dest, const Object *obj)
{ {
if(!dest || !obj) if(!dest || !obj)
@ -225,19 +255,23 @@ char* stringObj(char *dest, const Object *obj)
snprintf(dest, RESULT_LENGTH, "%s", obj->name); snprintf(dest, RESULT_LENGTH, "%s", obj->name);
} else if(t == TYPE_BOOL) { } else if(t == TYPE_BOOL) {
snprintf(dest, RESULT_LENGTH, "%s", obj->number ? "T" : "F"); snprintf(dest, RESULT_LENGTH, "%s", obj->number ? "T" : "F");
} else if(t == TYPE_ERROR) {
snprintf(dest, RESULT_LENGTH, "E%d", obj->err);
} else if(t == TYPE_STRING) { } else if(t == TYPE_STRING) {
snprintf(dest, RESULT_LENGTH, "%s", obj->string); snprintf(dest, RESULT_LENGTH, "%s", obj->string);
} else if(t == TYPE_LIST) { } else if(t == TYPE_LIST) {
stringList(dest, obj); stringList(dest, obj);
} else if(t == TYPE_ERROR) {
snprintf(dest, RESULT_LENGTH, "E%d", obj->err);
} else { } else {
snprintf(dest, RESULT_LENGTH, "%d", obj->number); snprintf(dest, RESULT_LENGTH, "X%d", obj->number);
} }
return dest; return dest;
} }
/**
* Prints a given Object only if the DEBUG flag is set
* @param obj The Object to debug
*/
void debugObj(const Object *obj) void debugObj(const Object *obj)
{ {
#ifdef DEBUG #ifdef DEBUG
@ -279,7 +313,12 @@ void _printObj(const Object *obj, int newline)
printErr(obj); printErr(obj);
} }
// Prints the given object /**
* Prints the given Object and a newline.
* Uses stringObj() to create the printed string
* If the DEBUG flag is set, the Object's type will be printed first
* @param obj The Object to print
*/
void printObj(const Object *obj) void printObj(const Object *obj)
{ {
_printObj(obj, 1); _printObj(obj, 1);
@ -298,40 +337,60 @@ void _printList(const Object *list, int newline)
printf("\n"); printf("\n");
} }
/**
* Prints each Object in a given list, surrounded by parentheses
* @param list The list Object to print
*/
void printList(const Object *list) void printList(const Object *list)
{ {
_printList(list, 1); _printList(list, 1);
} }
/**
* Performs appropriate free() on a given Object and NULLs its ->forward
* Returns immediately if param is NULL
*
* Strings: The Object's ->string is freed and NULLed
* Lists: deleteList() is called on the Object (may recurse)
* Lambdas: The body and params are cleaned, and the lambda itself is freed
*
* @param target The object to clean
*/
void cleanObject(Object *target) void cleanObject(Object *target)
{ {
if(target == NULL) if(target == NULL)
return; return;
const Type t = target->type; const Type t = target->type;
if(t == TYPE_LAMBDA) { if(t == TYPE_STRING) {
free(target->string);
target->string = NULL;
} else if(t == TYPE_LIST) {
deleteList(target);
} else if(t == TYPE_LAMBDA) {
cleanObject(&target->lambda->params); cleanObject(&target->lambda->params);
cleanObject(&target->lambda->body); cleanObject(&target->lambda->body);
free(target->lambda); free(target->lambda);
} else if(t == TYPE_LIST) {
deleteList(target);
} else if(t == TYPE_STRING) {
// TODO Why does this break?
// Probably multiple objects referencing the same string
free(target->string);
target->string = NULL;
} }
target->forward = NULL; target->forward = NULL;
} }
/**
* Print the given object with a newline, then clean it
* @param target The object to print and clean
*/
void printAndClean(Object *target) void printAndClean(Object *target)
{ {
printObj(target); printObj(target);
cleanObject(target); cleanObject(target);
} }
// Frees all objects in a list /**
* Frees all objects in a given list
* Runs cleanObject() on every item in the list (may recurse)
*
* @param dest The list object to clean up
*/
void deleteList(Object *dest) void deleteList(Object *dest)
{ {
if(!dest) if(!dest)
@ -382,20 +441,36 @@ void _copyList(Object *dest, const Object *src, int delete)
/** /**
* Does a deep copy of all items from `src` to `dest` * Does a deep copy of all items from `src` to `dest`
* Does nothing if either is NULL, or not a list type * Does nothing if either param is NULL, or not a list type
* Does a shallow delete of items from `dest` before copying * Does a shallow delete of items from `dest` before copying
* May recurse into lists in the list
*
* @param dest The list to copy to
* @param src The list to copy from
*/ */
void copyList(Object *dest, const Object *src) void copyList(Object *dest, const Object *src)
{ {
_copyList(dest, src, 1); _copyList(dest, src, 1);
} }
/**
* Does a deep copy of all items from `src` to the end of `dest`
* Does nothing if either param is NULL, or not a list type
* May recurse into lists in the list
*
* @param dest The list to copy to
* @param src The list to copy from
*/
void appendList(Object *dest, const Object *src) void appendList(Object *dest, const Object *src)
{ {
_copyList(dest, src, 0); _copyList(dest, src, 0);
} }
// Returns a basic object with NULL forward and the given `type` /**
* Returns a basic Object with NULL forward and the given type
* @param type The type of Object to create
* @return The created Object
*/
inline Object newObject(Type type) inline Object newObject(Type type)
{ {
Object no; Object no;
@ -404,7 +479,7 @@ inline Object newObject(Type type)
return no; return no;
} }
// Returns an empty list object // Returns an empty list Object
inline Object listObject() inline Object listObject()
{ {
Object list = newObject(TYPE_LIST); Object list = newObject(TYPE_LIST);
@ -412,6 +487,7 @@ inline Object listObject()
return list; return list;
} }
// Returns a list Object starting with the given Object
inline Object startList(const Object start) inline Object startList(const Object start)
{ {
Object list = listObject(); Object list = listObject();
@ -419,6 +495,19 @@ inline Object startList(const Object start)
return list; return list;
} }
// Returns an Object with a deep copy of the given Object
inline Object cloneList(const Object src)
{
Object list = listObject();
copyList(&list, &src);
return list;
}
inline Object cloneObject(const Object src)
{
return src.type == TYPE_LIST? cloneList(src) : src;
}
inline Object numberObject(int num) inline Object numberObject(int num)
{ {
Object o = newObject(TYPE_NUMBER); Object o = newObject(TYPE_NUMBER);

View File

@ -118,6 +118,8 @@ void appendList(Object *dest, const Object *src);
Object newObject(Type type); Object newObject(Type type);
Object listObject(); Object listObject();
Object startList(const Object start); Object startList(const Object start);
Object cloneList(const Object src);
Object cloneObject(const Object src);
Object lambdaObject(); Object lambdaObject();
Object symbolObject(); Object symbolObject();
Object objFromSlice(const char *string, int len); Object objFromSlice(const char *string, int len);

View File

@ -50,17 +50,11 @@ Object evalMapArgs(const Object *arg_forms, struct Environment *env)
FOR_POINTER_IN_LIST(inputList) { FOR_POINTER_IN_LIST(inputList) {
// Create a new list for each element, // Create a new list for each element,
// since lambda evaluation looks for a list // since lambda evaluation looks for a list
Object tempList; Object tempList = startList(cloneObject(*POINTER));
if(POINTER->type == TYPE_LIST) {
Object tl = listObject(); Object *params = &lambda.lambda->params;
copyList(&tl, POINTER); struct Environment newEnv = envForLambda(params, &tempList, env);
tempList = startList(tl);
} else {
tempList = startList(*POINTER);
}
struct Environment newEnv =
envForLambda(&lambda.lambda->params, &tempList, env);
// Add the lambda evaluation to the list // Add the lambda evaluation to the list
nf_addToList(&list, eval(&lambda.lambda->body, &newEnv)); nf_addToList(&list, eval(&lambda.lambda->body, &newEnv));
deleteEnv(&newEnv); deleteEnv(&newEnv);
@ -155,10 +149,11 @@ Object eval(const Object *obj, struct Environment *env)
return ret; return ret;
} else { } else {
Object newList = listObject(); Object list = listObject();
copyList(&newList, obj); FOR_POINTER_IN_LIST(obj) {
return newList; nf_addToList(&list, eval(POINTER, env));
//return *obj; }
return list;
} }
} }
case TYPE_LAMBDA: case TYPE_LAMBDA:
@ -423,12 +418,9 @@ int main(int argc, const char* argv[])
if(argc >= 2) { if(argc >= 2) {
Object r = numberObject(0); Object r = numberObject(0);
for(int i = 1; i < argc; i++) { for(int i = 1; i < argc; i++) {
// cleanObject(&r);
r = parseEval(argv[i], &env); r = parseEval(argv[i], &env);
printAndClean(&r); printAndClean(&r);
} }
// printObj(&r);
//printAndClean(&r);
} else { } else {
repl(&env); repl(&env);
} }

View File

@ -58,6 +58,7 @@ check "EmptyCnd" "(if () T F)" "F"
# check "EtyLstLt" "(if (()) T F)" "T" # check "EtyLstLt" "(if (()) T F)" "T"
echo "" echo ""
check "RegLists" "(1 2 3 4 5)" "( 1 2 3 4 5 )" check "RegLists" "(1 2 3 4 5)" "( 1 2 3 4 5 )"
check "MultiTypeList" "(10 20 \"rascals\")" "( 10 20 rascals )"
check "EmptyLst" "()" "( )" check "EmptyLst" "()" "( )"
check "EmptLst2" "( )" "( )" check "EmptLst2" "( )" "( )"
echo "" echo ""
@ -82,6 +83,9 @@ check "ImplicitCat" "(+ \"There are \" (* 5 4) \" bonks\")" "There are 20 bonks"
# Not recommended # Not recommended
check "CatAssocLeft" "(+ 10 20 \" rascals\")" "30 rascals" check "CatAssocLeft" "(+ 10 20 \" rascals\")" "30 rascals"
echo"" echo""
check "EvalElems" "((* 10 10) 7)" "( 100 7 )"
check "Duplicate" "(def dupe (fn (a) (a a a))) (dupe (*10 10))" "( 100 100 100 )"
echo""
if [ "$FAILS" -ne "0" ]; then if [ "$FAILS" -ne "0" ]; then
echo "$FAILS Tests Failed" echo "$FAILS Tests Failed"