pebblisp/src/object.c

741 lines
17 KiB
C

#include "object.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define RESULT_LENGTH 30
#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
/**
* 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);
}