975 lines
24 KiB
C
975 lines
24 KiB
C
#include "object.h"
|
||
#include "env.h"
|
||
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
size_t bytes = 0;
|
||
|
||
size_t getBytes()
|
||
{
|
||
return bytes;
|
||
}
|
||
|
||
void* smalloc(size_t size)
|
||
{
|
||
bytes += size;
|
||
return malloc(size);
|
||
}
|
||
|
||
void* scalloc(size_t size, size_t count)
|
||
{
|
||
bytes += (size * count);
|
||
return calloc(size, count);
|
||
|
||
}
|
||
|
||
#define malloc(x) smalloc(x)
|
||
#define calloc(x, y) scalloc(x, y)
|
||
|
||
/**
|
||
* 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 || !isListy(*listObj)) {
|
||
return -1;
|
||
}
|
||
|
||
int len = 0;
|
||
FOR_POINTER_IN_LIST(listObj) {
|
||
len++;
|
||
}
|
||
return len;
|
||
}
|
||
|
||
/**
|
||
* 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 || !isListy(*listObj)) {
|
||
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 || !isListy(*listObj)) {
|
||
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:
|
||
case TYPE_SLIST:
|
||
return obj->list == NULL;
|
||
case TYPE_LAMBDA:
|
||
return obj->lambda == NULL;
|
||
case TYPE_STRUCT:
|
||
return obj->structObject == 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 getErrorCode(*obj);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int allocations = 0;
|
||
|
||
int getAllocations()
|
||
{
|
||
return allocations;
|
||
}
|
||
|
||
/**
|
||
* 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)
|
||
{
|
||
#ifdef ALLOCATION_CAP
|
||
if (allocations >= 10000) {
|
||
printf("MAX ALLOCATIONS EXCEEDED\n");
|
||
*spot = NULL;
|
||
return;
|
||
}
|
||
#endif
|
||
allocations++;
|
||
*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 || !isListy(*dest)) {
|
||
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 || isListy(*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 || !isListy(*dest)) {
|
||
return;
|
||
}
|
||
|
||
if (isEmpty(dest)) {
|
||
allocObject(&dest->list, src);
|
||
return;
|
||
}
|
||
|
||
allocObject(&tail(dest)->forward, src);
|
||
}
|
||
|
||
#ifndef SIMPLE_ERRORS
|
||
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",
|
||
"UNEXPECTED_EOF",
|
||
"INDEX_PAST_END"};
|
||
#endif
|
||
|
||
/**
|
||
* 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 || !isListy(*obj)) {
|
||
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 struct Object
|
||
* Blocks out with curly braces
|
||
*/
|
||
void stringStruct(char* dest, const Object* obj)
|
||
{
|
||
struct StructObject* so = obj->structObject;
|
||
dest[0] = '{';
|
||
dest[1] = '\0';
|
||
|
||
for (int i = 0; i < global()->structDefs[so->definition].fieldCount; i++) {
|
||
strcat(dest, " ");
|
||
strcat(dest, global()->structDefs[so->definition].names[i]);
|
||
strcat(dest, ": ");
|
||
int isString = so->fields[i].type == TYPE_STRING;
|
||
if (isString) {
|
||
strcat(dest, "\"");
|
||
}
|
||
|
||
char tok[90] = "";
|
||
stringObj(tok, &so->fields[i]);
|
||
strcat(dest, tok);
|
||
|
||
if (isString) {
|
||
strcat(dest, "\"");
|
||
}
|
||
strcat(dest, ",");
|
||
}
|
||
int i = 0;
|
||
while (dest[i]) {
|
||
i++;
|
||
}
|
||
dest[i - 1] = ' ';
|
||
dest[i + 0] = '}';
|
||
dest[i + 1] = '\0';
|
||
}
|
||
/**
|
||
* 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
|
||
*/
|
||
#ifdef STANDALONE
|
||
#define stringf(_dest, _len, _format, args...) sprintf(_dest, _format, ## args)
|
||
#else
|
||
#define stringf(_dest, _len, _format, args...) snprintf(_dest, _len, _format, ## args)
|
||
#endif
|
||
|
||
char* stringNObj(char* dest, const Object* obj, const size_t len)
|
||
{
|
||
if (!dest || !obj) {
|
||
return NULL;
|
||
}
|
||
|
||
switch (obj->type) {
|
||
case TYPE_NUMBER:
|
||
stringf(dest, len, "%d", obj->number);
|
||
break;
|
||
case TYPE_BOOL:
|
||
stringf(dest, len, "%s", obj->number ? "T" : "F");
|
||
break;
|
||
case TYPE_STRING:
|
||
stringf(dest, len, "%s", obj->string);
|
||
break;
|
||
case TYPE_SYMBOL:
|
||
stringf(dest, len, "`%s`", obj->string);
|
||
break;
|
||
case TYPE_STRUCT:
|
||
//snprintf(dest, len, "{%s}", obj->structObject->definition->names[0]);
|
||
stringStruct(dest, obj);
|
||
break;
|
||
case TYPE_LIST:
|
||
case TYPE_SLIST:
|
||
stringList(dest, obj);
|
||
break;
|
||
case TYPE_ERROR: {
|
||
int code = getErrorCode(*obj);
|
||
#ifdef SIMPLE_ERRORS
|
||
stringf(dest, len, "E[%d]", (int)code);
|
||
#else
|
||
if (obj->error->context && obj->error->context[0] != '\0') {
|
||
stringf(dest, len, "'%s': %s", errorText[code],
|
||
obj->error->context);
|
||
} else if (code >= 0 && code <= INDEX_PAST_END) {
|
||
stringf(dest, len, "%s", errorText[code]);
|
||
} else {
|
||
stringf(dest, len, "BROKEN ERROR CODE: %d", code);
|
||
}
|
||
#endif
|
||
break;
|
||
}
|
||
case TYPE_FUNC:
|
||
stringf(dest, len, "F%d", obj->number);
|
||
break;
|
||
case TYPE_LAMBDA:
|
||
#ifdef STANDALONE
|
||
dest += stringf(dest, len, "\\x%d", obj->number);
|
||
stringNObj(dest, &obj->lambda->params, len);
|
||
dest += strlen(dest);
|
||
strcat(dest, " -> ");
|
||
dest += 4;
|
||
stringNObj(dest, &obj->lambda->body, len);
|
||
dest += strlen(dest);
|
||
strcat(dest, ">");
|
||
#else
|
||
stringf(dest, len, "\\x%d", obj->number);
|
||
#endif
|
||
break;
|
||
case TYPE_OTHER:
|
||
stringf(dest, len, "%p", obj->other->data);
|
||
break;
|
||
}
|
||
|
||
if (!isValidType(*obj)) {
|
||
stringf(dest, len, "BAD_TYPE(%d) X%d", obj->type, obj->number);
|
||
}
|
||
|
||
return dest;
|
||
}
|
||
|
||
char* stringObj(char* dest, const Object* obj)
|
||
{
|
||
return stringNObj(dest, obj, RESULT_LENGTH);
|
||
}
|
||
|
||
/**
|
||
* 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
|
||
}
|
||
|
||
#if defined(DEBUG) || defined(STANDALONE)
|
||
|
||
#define SIMPLE_TYPE(_type) case _type:\
|
||
printf(#_type);\
|
||
return
|
||
|
||
void printType(const Object* obj)
|
||
{
|
||
if (!obj) {
|
||
printf("NULL OBJECT");
|
||
return;
|
||
}
|
||
switch (obj->type) {
|
||
SIMPLE_TYPE(TYPE_NUMBER);
|
||
SIMPLE_TYPE(TYPE_STRUCT);
|
||
SIMPLE_TYPE(TYPE_BOOL);
|
||
SIMPLE_TYPE(TYPE_LIST);
|
||
SIMPLE_TYPE(TYPE_SLIST);
|
||
SIMPLE_TYPE(TYPE_FUNC);
|
||
SIMPLE_TYPE(TYPE_SYMBOL);
|
||
SIMPLE_TYPE(TYPE_STRING);
|
||
SIMPLE_TYPE(TYPE_OTHER);
|
||
SIMPLE_TYPE(TYPE_ERROR);
|
||
case TYPE_LAMBDA:
|
||
printf("TYPE_LAMBDA Params:\n");
|
||
return;
|
||
}
|
||
|
||
if (!isValidType(*obj)) {
|
||
printf("UNKNOWN TYPE (%d)", obj->type);
|
||
}
|
||
}
|
||
|
||
void nestedPrintList(const Object* list, int newline);
|
||
|
||
void _printObj(const Object* obj, int newline)
|
||
{
|
||
#ifdef DEBUG
|
||
printType(obj);
|
||
#endif
|
||
|
||
if (!obj) {
|
||
printf(newline ? "\n" : "");
|
||
return;
|
||
}
|
||
if (obj->type == TYPE_LAMBDA) {
|
||
printObj(&obj->lambda->params);
|
||
printf("->");
|
||
printObj(&obj->lambda->body);
|
||
return;
|
||
}
|
||
|
||
char temp[200] = "";
|
||
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 nestedPrintList(const Object* list, int newline)
|
||
{
|
||
printf("(");
|
||
if (list && isListy(*list)) {
|
||
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)
|
||
{
|
||
nestedPrintList(list, 1);
|
||
}
|
||
|
||
#endif
|
||
|
||
/**
|
||
* 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)
|
||
{
|
||
if (target == NULL) {
|
||
return;
|
||
}
|
||
|
||
switch (target->type) {
|
||
case TYPE_STRING:
|
||
case TYPE_SYMBOL:
|
||
free(target->string);
|
||
target->string = NULL;
|
||
break;
|
||
case TYPE_LIST:
|
||
case TYPE_SLIST:
|
||
deleteList(target);
|
||
break;
|
||
case TYPE_LAMBDA:
|
||
cleanObject(&target->lambda->params);
|
||
cleanObject(&target->lambda->body);
|
||
free(target->lambda);
|
||
break;
|
||
case TYPE_STRUCT:
|
||
for (int i = 0; i < global()->structDefs[target->structObject->definition].fieldCount; i++) {
|
||
cleanObject(&target->structObject->fields[i]);
|
||
}
|
||
free(target->structObject->fields);
|
||
free(target->structObject);
|
||
target->structObject = NULL;
|
||
break;
|
||
case TYPE_ERROR:
|
||
#ifndef SIMPLE_ERRORS
|
||
free(target->error->context);
|
||
free(target->error);
|
||
target->error = NULL;
|
||
#endif
|
||
break;
|
||
case TYPE_OTHER:
|
||
if (target->other->cleanup) {
|
||
target->other->cleanup(target);
|
||
}
|
||
break;
|
||
case TYPE_BOOL:
|
||
case TYPE_NUMBER:
|
||
case TYPE_FUNC:
|
||
break;
|
||
}
|
||
|
||
target->forward = NULL;
|
||
}
|
||
|
||
/**
|
||
* Print the given object with a newline, then clean it
|
||
* @param target The object to print and clean
|
||
*/
|
||
#ifdef STANDALONE
|
||
|
||
void printAndClean(Object* target)
|
||
{
|
||
printObj(target);
|
||
cleanObject(target);
|
||
}
|
||
|
||
#endif
|
||
|
||
/**
|
||
* 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 (!isListy(*dest)) {
|
||
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 (!isListy(*dest) || !isListy(*src)) { printd("NOT A LIST\n");
|
||
return;
|
||
}
|
||
|
||
if (delete) {
|
||
deleteList(dest);
|
||
}
|
||
|
||
FOR_POINTER_IN_LIST(src) {
|
||
if (isListy(*POINTER)) {
|
||
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 an empty struct Object
|
||
inline Object structObject(int definition)
|
||
{
|
||
Object structo = newObject(TYPE_STRUCT);
|
||
structo.structObject = malloc(sizeof(struct StructObject));
|
||
structo.structObject->definition = definition;
|
||
structo.structObject->fields = malloc(sizeof(Object) * global()->structDefs[definition].fieldCount);
|
||
return structo;
|
||
}
|
||
|
||
// 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 isListy(const Object test)
|
||
{
|
||
return test.type == TYPE_LIST || test.type == TYPE_SLIST;
|
||
}
|
||
|
||
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_STRUCT:
|
||
case TYPE_SLIST:
|
||
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, NULL);
|
||
}
|
||
|
||
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);
|
||
list.type = src.type;
|
||
return list;
|
||
}
|
||
|
||
inline Object cloneOther(const Object src)
|
||
{
|
||
return src.other->clone ? src.other->clone(src.other) : src;
|
||
}
|
||
|
||
Object cloneStruct(Object src);
|
||
|
||
inline Object cloneObject(const Object src)
|
||
{
|
||
switch (src.type) {
|
||
case TYPE_SLIST:
|
||
case TYPE_LIST:
|
||
return cloneList(src);
|
||
case TYPE_LAMBDA:
|
||
return cloneLambda(src);
|
||
case TYPE_STRUCT:
|
||
return cloneStruct(src);
|
||
case TYPE_STRING:
|
||
case TYPE_SYMBOL:
|
||
return cloneString(src);
|
||
case TYPE_ERROR:
|
||
return errorWithContext(getErrorCode(src), src.error->context);
|
||
case TYPE_BOOL:
|
||
case TYPE_NUMBER:
|
||
case TYPE_FUNC:
|
||
case TYPE_OTHER:
|
||
return src;
|
||
}
|
||
|
||
return src;
|
||
}
|
||
|
||
Object cloneStruct(const Object src)
|
||
{
|
||
Object structo = structObject(src.structObject->definition);
|
||
struct StructObject* so = structo.structObject;
|
||
for (int i = 0; i < global()->structDefs[so->definition].fieldCount; i++) {
|
||
so->fields[i] = cloneObject(src.structObject->fields[i]);
|
||
}
|
||
return structo;
|
||
}
|
||
|
||
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, struct Environment* env)
|
||
{
|
||
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);
|
||
|
||
if (env) {
|
||
Object* dest = &o.lambda->body;
|
||
FOR_POINTER_IN_LIST(dest) {
|
||
if (POINTER->type == TYPE_SYMBOL) {
|
||
Object fetched = fetchFromEnvironment(POINTER->string, env);
|
||
// TODO: Figure out why lambdas in particular break when doing this.
|
||
if (!isError(fetched, DID_NOT_FIND_SYMBOL) && fetched.type != TYPE_LAMBDA) {
|
||
fetched.forward = POINTER->forward;
|
||
cleanObject(POINTER);
|
||
*POINTER = fetched;
|
||
} else {
|
||
cleanObject(&fetched);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return o;
|
||
}
|
||
|
||
inline int isError(const Object obj, const enum errorCode err)
|
||
{
|
||
return obj.type == TYPE_ERROR && getErrorCode(obj) == err;
|
||
}
|
||
|
||
inline int bothAre(const enum Type type, const Object* obj1, const Object* obj2)
|
||
{
|
||
return (obj1->type == type) && (obj2->type == type);
|
||
}
|
||
|
||
inline int eitherIs(const enum Type type, const Object* o1, const Object* o2)
|
||
{
|
||
return (o1->type == type) || (o2->type == type);
|
||
}
|
||
|
||
inline int areSameType(const Object* obj1, const Object* obj2)
|
||
{
|
||
return obj1->type == obj2->type;
|
||
}
|
||
|
||
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);
|
||
#ifdef SIMPLE_ERRORS
|
||
o.error = err;
|
||
#else
|
||
o.error = malloc(sizeof(struct Error));
|
||
o.error->code = err;
|
||
o.error->context = NULL;
|
||
#endif
|
||
|
||
return o;
|
||
}
|
||
|
||
inline enum errorCode getErrorCode(const Object obj)
|
||
{
|
||
#ifdef SIMPLE_ERRORS
|
||
return obj.error;
|
||
#else
|
||
return obj.error->code;
|
||
#endif
|
||
}
|
||
|
||
#ifndef SIMPLE_ERRORS
|
||
|
||
inline void errorAddContext(Object* o, const char* context, int lineNo, const char* fileName)
|
||
{
|
||
// printf("o: %p\n", o);
|
||
// printf("o->error: %s\n", o->error);
|
||
o->error->context = calloc(sizeof(char), RESULT_LENGTH);
|
||
// printf("context: %p\n", context);
|
||
sprintf(o->error->context, "%s [33m%s:%d[0m", context, fileName, lineNo);
|
||
}
|
||
|
||
inline Object errorWithContextLineNo(enum errorCode code, const char* context, int lineNo, const char* fileName)
|
||
{
|
||
Object o = errorObject(code);
|
||
if (context) {
|
||
errorAddContext(&o, context, lineNo, fileName);
|
||
}
|
||
return o;
|
||
}
|
||
|
||
#endif
|
||
|
||
struct Error noError()
|
||
{
|
||
struct Error err;
|
||
err.context = NULL;
|
||
return err;
|
||
}
|
||
|
||
inline Object toBool(const Object test)
|
||
{
|
||
if (test.number == 0) {
|
||
return boolObject(0);
|
||
}
|
||
return boolObject(1);
|
||
}
|