#include "object.h" #include #include #include #define RESULT_LENGTH 20 #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 #define FOR_POINTER_IN_LIST(_list) \ for(Object *_element = _list->list; \ _element != NULL;\ _element = _element->forward) #define POINTER _element /** * 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 ignore) { return numberObject(listLength(&obj1)); } /** * 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; Object *march = listObj->list; for(int i = 0; i < n; i++) { if(march == NULL) return NULL; march = march->forward; } return march; } /** * 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 *march = listObj->list; while(march->forward != NULL) { march = march->forward; } return march; } /** * 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: return obj->name[0] == '\0'; case TYPE_FUNC: return obj->func == NULL; case TYPE_ERROR: return obj->err == 0; default: 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, unsigned ind, const Object src) { // Only work with non-null list types if(!dest || dest->type != TYPE_LIST) return; if(isEmpty(dest)) { allocObject(&dest->list, src); return; } // TODO Check for off-by-one errors // ensure pointers connect old and new Object *march = dest->list; for(unsigned i = 1; i < ind; i++) { if(march->forward == NULL) { allocObject(&march->forward, src); return; } march = march->forward; } // Save and re-apply current march->forward Object *oldForward = march->forward; allocObject(&march->forward, src); march->forward->forward = oldForward; } /** * 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); cleanObject(replace); *replace = src; replace->forward = NULL; } /** * 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); } void printErr(const Object *obj) { if(!obj || obj->type != TYPE_ERROR) return; printf("%s\n", errorText[(int)(obj->err)]); } void stringList(char *dest, const Object *obj) { dest[0] = '('; dest[1] = '\0'; const Object *tail = obj->list; while(tail != NULL) { strcat(dest, " "); char tok[90] = ""; stringObj(tok, tail); strcat(dest, tok); tail = tail->forward; } strcat(dest, " )"); } // Puts a string version of the given object into a given string char* stringObj(char *dest, const Object *obj) { if(!dest || !obj) return NULL; const Type t = obj->type; if(t == TYPE_NUMBER) { snprintf(dest, RESULT_LENGTH, "%d", obj->number); } else if(t == TYPE_SYMBOL) { 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_LIST) { stringList(dest, obj); } else { snprintf(dest, RESULT_LENGTH, "%d", obj->number); } return dest; } void debugObj(const Object *obj) { #ifdef DEBUG printObj(obj); #endif return; } void _printList(const Object *list, int newline); void _printObj(const Object *obj, int newline) { if(obj->type == TYPE_NUMBER) { printd("TYPE_NUMBER"); } else if(obj->type == TYPE_BOOL) { printd("TYPE_BOOL"); } else if(obj->type == TYPE_LIST) { printd("TYPE_LIST\n"); } else if(obj->type == TYPE_FUNC) { printd("TYPE_FUNC"); } else if(obj->type == TYPE_SYMBOL) { printd("TYPE_SYMBOL"); } else if(obj->type == TYPE_LAMBDA) { printd("TYPE_LAMBDA Params:\n"); printObj(&obj->lambda->params); printd("Lambda Body: \n"); printObj(&obj->lambda->body); return; } else if(obj->type == TYPE_ERROR) { printd("TYPE_ERROR: "); } else { printd("TYPE_OTHER (as int)"); } char temp[100] = ""; stringObj(temp, obj); if(newline) printf("%s\n", temp); else printf("%s", temp); printErr(obj); } // Prints the given object 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"); } void printList(const Object *list) { _printList(list, 1); } void cleanObject(Object *target) { if(target == NULL) return; const Type t = target->type; if(t == TYPE_LAMBDA) { cleanObject(&target->lambda->params); cleanObject(&target->lambda->body); free(target->lambda); } else if(t == TYPE_LIST) { deleteList(target); } target->forward = NULL; } void printAndClean(Object *target) { printObj(target); cleanObject(target); } // Frees all objects in a list 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) { debugObj(POINTER); nf_addToList(dest, *POINTER); if(POINTER->type == TYPE_LIST) { tail(dest)->list = NULL; _copyList(tail(dest), POINTER, 0); } } } /** * Does a deep copy of all items from `src` to `dest` * Does nothing if either is NULL, or not a list type * Deletes all items from `dest` before copying */ void copyList(Object *dest, const Object *src) { _copyList(dest, src, 1); } // Returns a basic object with NULL forward and the given `type` 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; } inline Object startList(const Object start) { Object list = listObject(); nf_addToList(&list, start); return list; } 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; } inline Object symbolObject() { return newObject(TYPE_SYMBOL); } inline Object lambdaObject() { Object o = newObject(TYPE_LAMBDA); o.lambda = malloc(sizeof(struct Lambda)); 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 = lambdaObject(); o.lambda->params = listObject(); o.lambda->body = listObject(); copyList(&o.lambda->params, params); copyList(&o.lambda->body, body); return o; } inline Object errorObject(enum errorCode err) { Object o = newObject(TYPE_ERROR); o.err = err; return o; }