741 lines
17 KiB
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);
|
|
}
|