344 lines
8.0 KiB
C
344 lines
8.0 KiB
C
#ifndef OBJECT_H
|
||
#define OBJECT_H
|
||
|
||
#include <stdlib.h>
|
||
|
||
#ifdef STANDALONE
|
||
#define ERR_LEN 256
|
||
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
|
||
|
||
#else // not STANDALONE
|
||
#include <pebble.h>
|
||
#undef sprintf
|
||
#define sprintf(_dest, args...) snprintf(_dest, 999, args)
|
||
#endif // STANDALONE
|
||
|
||
#ifdef DEBUG
|
||
#define printd(...) printf(__VA_ARGS__)
|
||
#else
|
||
#define printd(...) do { } while (0)
|
||
#endif
|
||
|
||
#ifdef RELEASE
|
||
#define assert(...) do { } while (0)
|
||
#else
|
||
#define assert(...) do {if (!(__VA_ARGS__)) {eprintf("\n[31;1massertion '" # __VA_ARGS__ "' at %s:%d failed!\nBailing![0m\n", __FILE__, __LINE__); int* X = NULL; unused int Y = *X;}} while (0)
|
||
#endif
|
||
|
||
#define MAX_TOK_CNT 2048
|
||
|
||
#define FOR_POINTER_IN_LIST(_list) \
|
||
for(Object *_element = (_list)->list; \
|
||
_element != NULL;\
|
||
_element = _element->forward)
|
||
#define POINTER _element
|
||
|
||
#define BuildListNamed(LIST) LIST = listObject(); Object** LIST ## _LIST_ITEM = &(LIST.list)
|
||
#define addToList(LIST, OBJECT) allocObject(LIST ## _LIST_ITEM, OBJECT); LIST ## _LIST_ITEM = &(*LIST ## _LIST_ITEM)->forward
|
||
|
||
#ifdef PBL_PLATFORM_APLITE
|
||
#define LOW_MEM
|
||
#endif
|
||
|
||
#ifdef LOW_MEM
|
||
#define SIMPLE_ERRORS
|
||
#endif
|
||
|
||
#define unused __attribute__((unused))
|
||
|
||
#define array_length(_array) (sizeof(_array) / sizeof((_array)[0]))
|
||
|
||
#define UNPACK(...) __VA_ARGS__
|
||
|
||
#ifdef STANDALONE
|
||
#define fnn(_name, _docs, ...) \
|
||
static const char * const _name ## Doc = _docs; \
|
||
unused static const char * const _name ## Tests[] = {__VA_ARGS__}; \
|
||
_Static_assert(array_length(_name ## Tests) % 2 == 0, "Array of test strings must have exactly one expected result for each test."); \
|
||
Object _name(Object* params, int length, struct Environment* env)
|
||
|
||
#define expect(_checker) {.checkFunc = (_checker), .name = #_checker}
|
||
#define returns(_checker) {.checkFunc = (_checker), .name = #_checker}
|
||
#define anyType {.checkFunc = NULL, .name = "AnyType"}
|
||
|
||
#define tfn(_name, _symbol, _type, _docs, ...) \
|
||
unused static struct TypeCheck _name ## TypeChecks[] = UNPACK _type; \
|
||
static const char * const _name ## Symbol = _symbol; \
|
||
fnn(_name, _docs, __VA_ARGS__)
|
||
|
||
#define fn(_name, _symbol, _docs, ...) \
|
||
static const char * const _name ## Symbol = _symbol; \
|
||
fnn(_name, _docs, __VA_ARGS__)
|
||
|
||
#else
|
||
#define fnn(_name, _docs, ...) \
|
||
Object _name(Object* params, int length, struct Environment* env)
|
||
|
||
#define tfn(_name, _symbol, _type, _docs, ...) \
|
||
static const char * const _name ## Symbol = _symbol; \
|
||
fnn(_name, _docs, __VA_ARGS__)
|
||
|
||
#define fn(_name, _symbol, _docs, ...) \
|
||
static const char * const _name ## Symbol = _symbol; \
|
||
fnn(_name, _docs, __VA_ARGS__)
|
||
#endif
|
||
|
||
#define stringRefs(STRING_OBJECT) (STRING_OBJECT)->string[-1]
|
||
|
||
enum errorCode {
|
||
MISMATCHED_PARENS,
|
||
NULL_ENV,
|
||
EMPTY_ENV,
|
||
NULL_PARSE,
|
||
NULL_LAMBDA_LIST,
|
||
NULL_MAP_ARGS,
|
||
LAMBDA_ARGS_NOT_LIST,
|
||
DID_NOT_FIND_SYMBOL,
|
||
BAD_TYPE,
|
||
BAD_PARAMS,
|
||
BAD_NUMBER,
|
||
UNSUPPORTED_NUMBER_TYPE,
|
||
NOT_ENOUGH_ARGUMENTS,
|
||
NOT_A_LIST,
|
||
SCRIPT_NOT_FOUND,
|
||
NO_CLONE_SPECIFIED,
|
||
CAN_ONLY_EVAL_STRINGS,
|
||
UNEXPECTED_EOF,
|
||
INDEX_PAST_END // INDEX_PAST_END should always be the highest value
|
||
};
|
||
|
||
typedef enum Type {
|
||
TYPE_NUMBER,
|
||
TYPE_BOOL,
|
||
TYPE_LIST,
|
||
TYPE_SLIST,
|
||
TYPE_STRUCT,
|
||
TYPE_FUNC,
|
||
TYPE_STATIC_FUNC,
|
||
TYPE_SYMBOL,
|
||
TYPE_LAMBDA,
|
||
TYPE_STRING,
|
||
TYPE_HASH_TABLE,
|
||
TYPE_PROMISE,
|
||
TYPE_OTHER,
|
||
TYPE_ERROR // TYPE_ERROR should always be the highest value
|
||
} Type;
|
||
|
||
typedef struct Object Object;
|
||
|
||
struct Lambda;
|
||
struct Environment;
|
||
struct Promise;
|
||
struct Other;
|
||
struct StaticFunction;
|
||
struct ObjectTableObject;
|
||
struct Error {
|
||
enum errorCode code;
|
||
char* context;
|
||
struct Slice* plContext;
|
||
};
|
||
|
||
struct Object {
|
||
Type type;
|
||
|
||
union {
|
||
long number;
|
||
Object* list;
|
||
char* string;
|
||
|
||
Object (* func)(Object*, int, struct Environment*);
|
||
|
||
struct StructObject* structObject;
|
||
struct Lambda* lambda;
|
||
struct StaticFunction* staticF;
|
||
struct Other* other;
|
||
struct Promise* promise;
|
||
struct ObjectTableObject* table;
|
||
#ifdef SIMPLE_ERRORS
|
||
enum errorCode error;
|
||
#else
|
||
struct Error* error;
|
||
#endif
|
||
void* data;
|
||
};
|
||
|
||
union {
|
||
Object* forward;
|
||
char** docStrings;
|
||
int refs;
|
||
};
|
||
};
|
||
|
||
struct TypeCheck {
|
||
int (* checkFunc)(Object);
|
||
|
||
const char* name;
|
||
};
|
||
|
||
struct StructDef {
|
||
int fieldCount;
|
||
char* name;
|
||
char** names;
|
||
};
|
||
|
||
struct StructObject {
|
||
int definition;
|
||
Object* fields; // Order should match that in the definition.
|
||
};
|
||
|
||
#define lambdaDocs(LAMBDA_OBJECT) (LAMBDA_OBJECT)->lambda->params.docStrings
|
||
#define lambdaRefs(LAMBDA_OBJECT) (LAMBDA_OBJECT)->lambda->body.refs
|
||
struct Lambda {
|
||
/// params.docstring can contain details about the constructed lambda.
|
||
Object params;
|
||
/// body.refs contains the reference count
|
||
Object body;
|
||
};
|
||
|
||
struct StaticFunction {
|
||
Object (* func)(Object*, int, struct Environment*);
|
||
|
||
Object* arguments;
|
||
int argCount;
|
||
int refs;
|
||
};
|
||
|
||
struct Other {
|
||
void (* cleanup)(Object*);
|
||
|
||
Object (* clone)(struct Other*);
|
||
|
||
void* data;
|
||
};
|
||
|
||
struct string {
|
||
char* allocPoint;
|
||
char* cursor;
|
||
size_t capacity;
|
||
};
|
||
|
||
const char* getTypeName(const Object* obj);
|
||
|
||
/**
|
||
* Returns a dynamically-sized string representation of the given object.
|
||
*
|
||
* _Adds_ the length of the new string to the `length` parameter.
|
||
*/
|
||
char* stringObj(const Object* obj, size_t* length);
|
||
|
||
const char* getTypeName(const Object* obj);
|
||
|
||
void printObj(const Object* obj);
|
||
|
||
void _printObj(const Object* obj, int newline);
|
||
|
||
Object* tail(const Object* listObj);
|
||
|
||
Object* nf_addToList(Object* dest, Object src);
|
||
|
||
void deleteList(Object* dest);
|
||
|
||
int listLength(const Object* listObj);
|
||
|
||
void cleanObject(Object* target);
|
||
|
||
void printAndClean(Object* target);
|
||
|
||
void allocObject(Object** spot, Object src);
|
||
|
||
int isNumber(Object test);
|
||
|
||
int isListy(Object test);
|
||
|
||
int isStringy(Object test);
|
||
|
||
int isBool(Object test);
|
||
|
||
int isStruct(Object test);
|
||
|
||
int isFuncy(Object test);
|
||
|
||
int isValidType(Object test);
|
||
|
||
int isError(Object obj, enum errorCode err);
|
||
|
||
int bothAre(enum Type type, const Object* obj1, const Object* obj2);
|
||
|
||
int areSameType(const Object* obj1, const Object* obj2);
|
||
|
||
Object cloneList(const Object* src);
|
||
|
||
Object cloneString(Object obj);
|
||
|
||
Object cloneObject(Object src);
|
||
|
||
Object newObject(Type type);
|
||
|
||
Object listObject();
|
||
|
||
Object startList(Object start);
|
||
|
||
Object nullTerminated(const char* string);
|
||
|
||
Object stringFromSlice(const char* string, int len);
|
||
|
||
Object escapedStringFromSlice(const char* string, int len);
|
||
|
||
Object symFromSlice(const char* string, int len);
|
||
|
||
/// Creates a stringy object with room for a reference-count prefix and trailing null byte.
|
||
/// Thus, withLen(3, TYPE_STRING) will actually allocate a 5-byte "string".
|
||
Object withLen(size_t len, enum Type type);
|
||
|
||
Object boolObject(int b);
|
||
|
||
Object numberObject(int num);
|
||
|
||
Object structObject(int definition);
|
||
|
||
Object otherObject();
|
||
|
||
Object errorObject(enum errorCode err);
|
||
|
||
#ifdef SIMPLE_ERRORS
|
||
#define getErrorCode(OBJ) ((OBJ).error)
|
||
#else
|
||
#define getErrorCode(OBJ) ((OBJ).error->code)
|
||
#endif
|
||
|
||
Object errorWithContextLineNo(enum errorCode code, const char* context, int lineNo, const char* fileName);
|
||
|
||
Object errorWithAllocatedContextLineNo(enum errorCode code, char* context, int lineNo, const char* fileName);
|
||
|
||
#ifdef SIMPLE_ERRORS
|
||
#define errorWithContext(code, context) errorObject(code)
|
||
#define errorAddContext(x, y, z, a) ;
|
||
#define throw(_code, ...) return errorObject(_code)
|
||
#else
|
||
#define throw(_code, ...) do { char* ERROR_CONTEXT = malloc(sizeof(char) * ERR_LEN); sprintf(ERROR_CONTEXT, __VA_ARGS__); \
|
||
return errorWithAllocatedContextLineNo(_code, ERROR_CONTEXT, __LINE__, __FILE__); } while (0)
|
||
#define errorWithContext(_code, _context) errorWithContextLineNo(_code, _context, __LINE__, __FILE__)
|
||
|
||
#endif
|
||
|
||
struct Error noError();
|
||
|
||
Object constructLambda(const Object* params, const Object* body, struct Environment* env);
|
||
|
||
#ifdef STANDALONE
|
||
|
||
int getAllocations();
|
||
|
||
size_t getBytes();
|
||
|
||
void* scalloc(size_t size, size_t count);
|
||
|
||
void* smalloc(size_t size);
|
||
|
||
#define malloc(x) smalloc(x)
|
||
#define calloc(x, y) scalloc(x, y)
|
||
|
||
#endif // STANDALONE
|
||
|
||
#endif
|