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;
}
// 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 *afterNew = beforeNew->forward;
// Replace the `before` object's pointer
// Replace the `before` Object's pointer
allocObject(&beforeNew->forward, src);
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
* Immediately returns if `list` is NULL or not a list type
* @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);
}
/**
* 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)
{
if(!obj || obj->type != TYPE_ERROR)
@ -197,8 +202,18 @@ void printErr(const Object *obj)
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)
{
if(!dest || obj->type != TYPE_LIST)
return;
dest[0] = '(';
dest[1] = '\0';
@ -211,7 +226,22 @@ void stringList(char *dest, const Object *obj)
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)
{
if(!dest || !obj)
@ -225,19 +255,23 @@ char* stringObj(char *dest, const Object *obj)
snprintf(dest, RESULT_LENGTH, "%s", obj->name);
} else if(t == TYPE_BOOL) {
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) {
snprintf(dest, RESULT_LENGTH, "%s", obj->string);
} else if(t == TYPE_LIST) {
stringList(dest, obj);
} else if(t == TYPE_ERROR) {
snprintf(dest, RESULT_LENGTH, "E%d", obj->err);
} else {
snprintf(dest, RESULT_LENGTH, "%d", obj->number);
snprintf(dest, RESULT_LENGTH, "X%d", obj->number);
}
return dest;
}
/**
* Prints a given Object only if the DEBUG flag is set
* @param obj The Object to debug
*/
void debugObj(const Object *obj)
{
#ifdef DEBUG
@ -279,7 +313,12 @@ void _printObj(const Object *obj, int newline)
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)
{
_printObj(obj, 1);
@ -298,40 +337,60 @@ void _printList(const Object *list, int newline)
printf("\n");
}
/**
* Prints each Object in a given list, surrounded by parentheses
* @param list The list Object to print
*/
void printList(const Object *list)
{
_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)
{
if(target == NULL)
return;
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->body);
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;
}
/**
* Print the given object with a newline, then clean it
* @param target The object to print and clean
*/
void printAndClean(Object *target)
{
printObj(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)
{
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 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
* 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)
{
_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)
{
_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)
{
Object no;
@ -404,7 +479,7 @@ inline Object newObject(Type type)
return no;
}
// Returns an empty list object
// Returns an empty list Object
inline Object listObject()
{
Object list = newObject(TYPE_LIST);
@ -412,6 +487,7 @@ inline Object listObject()
return list;
}
// Returns a list Object starting with the given Object
inline Object startList(const Object start)
{
Object list = listObject();
@ -419,6 +495,19 @@ inline Object startList(const Object start)
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)
{
Object o = newObject(TYPE_NUMBER);

View File

@ -118,6 +118,8 @@ void appendList(Object *dest, const Object *src);
Object newObject(Type type);
Object listObject();
Object startList(const Object start);
Object cloneList(const Object src);
Object cloneObject(const Object src);
Object lambdaObject();
Object symbolObject();
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) {
// Create a new list for each element,
// since lambda evaluation looks for a list
Object tempList;
if(POINTER->type == TYPE_LIST) {
Object tl = listObject();
copyList(&tl, POINTER);
tempList = startList(tl);
} else {
tempList = startList(*POINTER);
}
Object tempList = startList(cloneObject(*POINTER));
Object *params = &lambda.lambda->params;
struct Environment newEnv = envForLambda(params, &tempList, env);
struct Environment newEnv =
envForLambda(&lambda.lambda->params, &tempList, env);
// Add the lambda evaluation to the list
nf_addToList(&list, eval(&lambda.lambda->body, &newEnv));
deleteEnv(&newEnv);
@ -155,10 +149,11 @@ Object eval(const Object *obj, struct Environment *env)
return ret;
} else {
Object newList = listObject();
copyList(&newList, obj);
return newList;
//return *obj;
Object list = listObject();
FOR_POINTER_IN_LIST(obj) {
nf_addToList(&list, eval(POINTER, env));
}
return list;
}
}
case TYPE_LAMBDA:
@ -423,12 +418,9 @@ int main(int argc, const char* argv[])
if(argc >= 2) {
Object r = numberObject(0);
for(int i = 1; i < argc; i++) {
// cleanObject(&r);
r = parseEval(argv[i], &env);
printAndClean(&r);
}
// printObj(&r);
//printAndClean(&r);
} else {
repl(&env);
}

View File

@ -58,6 +58,7 @@ check "EmptyCnd" "(if () T F)" "F"
# check "EtyLstLt" "(if (()) T F)" "T"
echo ""
check "RegLists" "(1 2 3 4 5)" "( 1 2 3 4 5 )"
check "MultiTypeList" "(10 20 \"rascals\")" "( 10 20 rascals )"
check "EmptyLst" "()" "( )"
check "EmptLst2" "( )" "( )"
echo ""
@ -82,6 +83,9 @@ check "ImplicitCat" "(+ \"There are \" (* 5 4) \" bonks\")" "There are 20 bonks"
# Not recommended
check "CatAssocLeft" "(+ 10 20 \" rascals\")" "30 rascals"
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
echo "$FAILS Tests Failed"