468 lines
11 KiB
C
468 lines
11 KiB
C
#include "object.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#define RESULT_LENGTH 20
|
|
|
|
#ifdef DEBUG
|
|
#define printd(...) printf(__VA_ARGS__)
|
|
#else
|
|
#define printd(...) stringObj(NULL, NULL)
|
|
#endif
|
|
|
|
#ifndef STANDALONE
|
|
#include <pebble.h>
|
|
#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;
|
|
}
|