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:
parent
efb151d0ef
commit
c97cbe850c
131
src/object.c
131
src/object.c
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 "[1;31m$FAILS Tests Failed[0m"
|
echo "[1;31m$FAILS Tests Failed[0m"
|
||||||
|
|
Loading…
Reference in New Issue