#include "object.h" #include #include #include #define RESULT_LENGTH 30 #ifdef DEBUG #define printd(...) printf(__VA_ARGS__) #else #define printd(...) stringObj(NULL, NULL) #endif #ifndef STANDALONE #include #undef printf #define printf(...) APP_LOG(APP_LOG_LEVEL_DEBUG, __VA_ARGS__) #endif /** * Returns the length of a given list Object * @param listObj The list to get the length of * @return Length of the list if non-null and of the list type. Otherwise -1 */ int listLength(const Object *listObj) { if(!listObj || listObj->type != TYPE_LIST) return -1; int len = 0; FOR_POINTER_IN_LIST(listObj) { len++; } return len; } Object len(Object obj1, Object o_ignore, struct Environment *e_ignore) { Object o = numberObject(listLength(&obj1)); if(o.number < 0) { return errorObject(NOT_A_LIST); } return o; } /** * Returns a pointer to the Object at the given index in a given list * @param listObj The list to fetch an Object from * @param n The index to to fetch from the list * @return A pointer to the Object, if it is found, or NULL on an error */ Object *itemAt(const Object *listObj, int n) { if(!listObj || listObj->type != TYPE_LIST) return NULL; FOR_POINTER_IN_LIST(listObj) { if(POINTER == NULL) return NULL; if(n-- == 0) return POINTER; } return NULL; } /** * Returns a pointer to the last Object in the given list. * @param listObj The list to find the tail of * @return A pointer to the last Object if it is found, or NULL on an error */ Object *tail(const Object *listObj) { if(!listObj || listObj->type != TYPE_LIST) return NULL; Object *tail = NULL; FOR_POINTER_IN_LIST(listObj) { tail = POINTER; } return tail; } /** * Checks if a given object is empty. * * BOOL, NUMBER, and ERROR types return true only when 0 * LIST, LAMBDA, and FUNC types return true only when NULL * SYMBOL returns true only when it begins with a nullchar * @param obj A pointer to the object to check * @return True only if the non-null Object is empty/non-zero */ inline int isEmpty(const Object *obj) { if(obj == NULL) return 0; switch(obj->type) { case TYPE_NUMBER: case TYPE_BOOL: return obj->number == 0; case TYPE_LIST: return obj->list == NULL; case TYPE_LAMBDA: return obj->lambda == NULL; case TYPE_SYMBOL: case TYPE_STRING: return obj->string == NULL || obj->string[0] == '\0'; case TYPE_FUNC: return obj->func == NULL; case TYPE_OTHER: return obj->other == NULL; case TYPE_ERROR: return obj->err == 0; } return 0; } /** * Allocate a copy of a given object into the given pointer. * Does nothing if `spot` is NULL * @param spot A pointer to the Object pointer that needs allocating * @param src The Object to copy from */ void allocObject(Object **spot, const Object src) { if(!spot) return; *spot = malloc(sizeof(struct Object)); **spot = src; (*spot)->forward = NULL; } /** * Inserts a given Object into a list at the given index. * Immediately returns if `dest` is NULL or not a list type * Adds to the end of the list if the index is higher than the list length * @param dest The list to insert into * @param ind The index to insert at * @param src The Object to add to the list * @warning UNTESTED */ void insertIntoList(Object *dest, int ind, const Object src) { if(!dest || dest->type != TYPE_LIST) return; // Merely append, when possible if(ind >= listLength(dest)) { nf_addToList(dest, src); return; } // 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 allocObject(&beforeNew->forward, src); beforeNew->forward->forward = afterNew; } /** * 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 * @param i The index of the element to replace * @param src The Object to copy into the list */ void replaceListing(Object *list, int i, const Object src) { if(!list || list->type != TYPE_LIST) return; Object *replace = itemAt(list, i); Object *oldForward = replace->forward; cleanObject(replace); *replace = src; replace->forward = oldForward; } /** * Adds an Object to the end of a list * Does nothing if `dest` is NULL or not a list type * @param dest The list to append to * @param src The Object to copy into the list */ void nf_addToList(Object *dest, const Object src) { if(!dest || dest->type != TYPE_LIST) return; if(isEmpty(dest)) { allocObject(&dest->list, src); return; } allocObject(&tail(dest)->forward, src); } static const char *errorText[] = { "MISMATCHED_PARENS", "BAD_LIST_OF_SYMBOL_STRINGS", "TYPE_LIST_NOT_CAUGHT", "NULL_ENV", "EMPTY_ENV", "BUILT_IN_NOT_FOUND", "NULL_PARSE", "NULL_LAMBDA_LIST", "NULL_MAP_ARGS", "LAMBDA_ARGS_NOT_LIST", "DID_NOT_FIND_SYMBOL", "BAD_TYPE", "UNEXPECTED_FORM", "LISTS_NOT_SAME_SIZE", "BAD_NUMBER", "UNSUPPORTED_NUMBER_TYPE", "NOT_A_SYMBOL", "ONLY_ONE_ARGUMENT", "NOT_A_LIST", "SCRIPT_NOT_FOUND", "NO_CLONE_SPECIFIED", "CAN_ONLY_EVAL_STRINGS" }; /** * 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) return; 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'; FOR_POINTER_IN_LIST(obj) { strcat(dest, " "); char tok[90] = ""; stringObj(tok, POINTER); strcat(dest, tok); } strcat(dest, " )"); } /** * 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) return NULL; switch(obj->type) { case TYPE_NUMBER: snprintf(dest, RESULT_LENGTH, "%d", obj->number); break; case TYPE_BOOL: snprintf(dest, RESULT_LENGTH, "%s", obj->number ? "T" : "F"); break; case TYPE_STRING: snprintf(dest, RESULT_LENGTH, "%s", obj->string); break; case TYPE_SYMBOL: snprintf(dest, RESULT_LENGTH, "`%s`", obj->string); break; case TYPE_LIST: stringList(dest, obj); break; case TYPE_ERROR: #ifdef STANDALONE snprintf(dest, RESULT_LENGTH, "%s", errorText[(int)(obj->err)]); #else snprintf(dest, RESULT_LENGTH, "E%d", obj->err); #endif break; case TYPE_FUNC: case TYPE_LAMBDA: snprintf(dest, RESULT_LENGTH, "X%d", obj->number); break; case TYPE_OTHER: snprintf(dest, RESULT_LENGTH, "%p", obj->other->data); break; } if(!isValidType(*obj)) snprintf(dest, RESULT_LENGTH, "BAD_TYPE(%d) X%d", obj->type, 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 printObj(obj); #endif return; } void printType(const Object *obj) { switch(obj->type) { case TYPE_NUMBER: printf("TYPE_NUMBER"); return; case TYPE_BOOL: printf("TYPE_BOOL"); return; case TYPE_LIST: printf("TYPE_LIST"); return; case TYPE_FUNC: printf("TYPE_FUNC"); return; case TYPE_SYMBOL: printf("TYPE_SYMBOL"); return; case TYPE_STRING: printf("TYPE_STRING"); return; case TYPE_LAMBDA: printf("TYPE_LAMBDA Params:\n"); return; case TYPE_OTHER: printf("TYPE_OTHER: "); case TYPE_ERROR: printf("TYPE_ERROR: "); return; } if(!isValidType(*obj)) printf("UNKNOWN TYPE (%d)", obj->type); } void _printList(const Object *list, int newline); void _printObj(const Object *obj, int newline) { #ifdef DEBUG printType(obj); #endif if (obj->type == TYPE_LAMBDA) { printObj(&obj->lambda->params); printf("->"); printObj(&obj->lambda->body); return; } char temp[100] = ""; stringObj(temp, obj); if(newline) printf("%s\n", temp); else printf("%s", temp); } /** * 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 */ inline void printObj(const Object *obj) { _printObj(obj, 1); } void _printList(const Object *list, int newline) { printf("("); FOR_POINTER_IN_LIST(list) { printf(" "); _printObj(POINTER, 0); } printf(" )"); if(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) { //printf("CLEANING:"); if(target == NULL) return; switch(target->type) { case TYPE_STRING: case TYPE_SYMBOL: free(target->string); target->string = NULL; break; case TYPE_LIST: deleteList(target); break; case TYPE_LAMBDA: cleanObject(&target->lambda->params); cleanObject(&target->lambda->body); free(target->lambda); break; case TYPE_OTHER: if(target->other->cleanup) { target->other->cleanup(target); } //free(target->other); break; case TYPE_BOOL: case TYPE_NUMBER: case TYPE_FUNC: case TYPE_ERROR: break; } 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 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) return; if(dest->type != TYPE_LIST) { printf("Tried to delete something other than a list\n"); return; } Object *march = dest->list; while(march != NULL) { Object *prevMarch = march; march = march->forward; cleanObject(prevMarch); free(prevMarch); } dest->list = NULL; } void _copyList(Object *dest, const Object *src, int delete) { if(!dest || !src) { printd("NULL\n"); return; } if(dest->type != TYPE_LIST || src->type != TYPE_LIST) { printd("NOT A LIST\n"); return; } if(delete) deleteList(dest); FOR_POINTER_IN_LIST(src) { if(POINTER->type == TYPE_LIST) { nf_addToList(dest, *POINTER); tail(dest)->list = NULL; _copyList(tail(dest), POINTER, 0); } else if (isStringy(*POINTER)) { nf_addToList(dest, cloneString(*POINTER)); } else { nf_addToList(dest, *POINTER); } } } /** * Does a deep copy of all items from `src` to `dest` * 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 * @param type The type of Object to create * @return The created Object */ inline Object newObject(Type type) { Object no; no.forward = NULL; no.type = type; return no; } // Returns an empty list Object inline Object listObject() { Object list = newObject(TYPE_LIST); list.list = NULL; return list; } // Returns a list Object starting with the given Object inline Object startList(const Object start) { Object list = listObject(); nf_addToList(&list, start); return list; } inline int isStringy(const Object test) { return test.type == TYPE_STRING || test.type == TYPE_SYMBOL; } inline int isValidType(const Object test) { switch(test.type) { case TYPE_NUMBER: case TYPE_BOOL: case TYPE_LIST: case TYPE_FUNC: case TYPE_SYMBOL: case TYPE_LAMBDA: case TYPE_STRING: case TYPE_OTHER: case TYPE_ERROR: return 1; } return 0; } /** * Clones the given lambda with new allocations * Will behave unexpectedly if given something other than a lambda object! */ inline Object cloneLambda(const Object old) { return constructLambda(&old.lambda->params, &old.lambda->body); } Object cloneString(Object obj) { const char* string = obj.string; obj.string = malloc(sizeof(char) * (strlen(string) + 1)); strcpy(obj.string, string); return obj; } // 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 cloneOther(const Object src) { return src.other->clone? src.other->clone(src.other) : src; } inline Object cloneObject(const Object src) { switch(src.type) { case TYPE_LIST: return cloneList(src); case TYPE_LAMBDA: return cloneLambda(src); case TYPE_STRING: case TYPE_SYMBOL: return cloneString(src); case TYPE_BOOL: case TYPE_NUMBER: case TYPE_FUNC: case TYPE_OTHER: case TYPE_ERROR: ; // Fall through to plain return } return src; } inline Object numberObject(int num) { Object o = newObject(TYPE_NUMBER); o.number = num; return o; } inline Object boolObject(int b) { Object o = newObject(TYPE_BOOL); o.number = !!b; return o; } // Skips first and last chars! Assumed to be '"' inline Object objFromSlice(const char *string, int len) { return stringFromSlice(&string[1], len - 1); } inline Object stringFromSlice(const char *string, int len) { Object o = symFromSlice(string, len); o.type = TYPE_STRING; return o; } inline Object symFromSlice(const char *string, int len) { Object o = newObject(TYPE_SYMBOL); o.string = calloc(sizeof(char), len + 1); strncpy(o.string, string, len); return o; } inline Object constructLambda(const Object *params, const Object *body) { if(!params || !body) return errorObject(NULL_LAMBDA_LIST); if(params->type != TYPE_LIST || body->type != TYPE_LIST) return errorObject(LAMBDA_ARGS_NOT_LIST); Object o = newObject(TYPE_LAMBDA); o.lambda = malloc(sizeof(struct Lambda)); o.lambda->params = listObject(); o.lambda->body = listObject(); copyList(&o.lambda->params, params); copyList(&o.lambda->body, body); return o; } inline int isError(const Object obj, const enum errorCode err) { return obj.type == TYPE_ERROR && obj.err == err; } inline Object otherObject() { Object o = newObject(TYPE_OTHER); o.other = malloc(sizeof(struct Other)); return o; } inline Object errorObject(enum errorCode err) { Object o = newObject(TYPE_ERROR); o.err = err; return o; } inline Object toBool(const Object test) { if(test.number == 0) return boolObject(0); return boolObject(1); }