pebblisp/src/object.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;
}