#ifndef wren_value_h
#define wren_value_h
#include <stdbool.h>
#include <string.h>
#include "wren_common.h"
#include "wren_math.h"
#include "wren_utils.h"
#define AS_CLASS(value) ((ObjClass*)AS_OBJ(value))
#define AS_CLOSURE(value) ((ObjClosure*)AS_OBJ(value))
#define AS_FIBER(v) ((ObjFiber*)AS_OBJ(v))
#define AS_FN(value) ((ObjFn*)AS_OBJ(value))
#define AS_FOREIGN(v) ((ObjForeign*)AS_OBJ(v))
#define AS_INSTANCE(value) ((ObjInstance*)AS_OBJ(value))
#define AS_LIST(value) ((ObjList*)AS_OBJ(value))
#define AS_MAP(value) ((ObjMap*)AS_OBJ(value))
#define AS_MODULE(value) ((ObjModule*)AS_OBJ(value))
#define AS_NUM(value) (wrenValueToNum(value))
#define AS_RANGE(v) ((ObjRange*)AS_OBJ(v))
#define AS_STRING(v) ((ObjString*)AS_OBJ(v))
#define AS_CSTRING(v) (AS_STRING(v)->value)
#define BOOL_VAL(boolean) ((boolean) ? TRUE_VAL : FALSE_VAL)
#define NUM_VAL(num) (wrenNumToValue(num))
#define OBJ_VAL(obj) (wrenObjectToValue((Obj*)(obj)))
#define IS_BOOL(value) (wrenIsBool(value))
#define IS_CLASS(value) (wrenIsObjType(value, OBJ_CLASS))
#define IS_CLOSURE(value) (wrenIsObjType(value, OBJ_CLOSURE))
#define IS_FIBER(value) (wrenIsObjType(value, OBJ_FIBER))
#define IS_FN(value) (wrenIsObjType(value, OBJ_FN))
#define IS_FOREIGN(value) (wrenIsObjType(value, OBJ_FOREIGN))
#define IS_INSTANCE(value) (wrenIsObjType(value, OBJ_INSTANCE))
#define IS_LIST(value) (wrenIsObjType(value, OBJ_LIST))
#define IS_MAP(value) (wrenIsObjType(value, OBJ_MAP))
#define IS_RANGE(value) (wrenIsObjType(value, OBJ_RANGE))
#define IS_STRING(value) (wrenIsObjType(value, OBJ_STRING))
#define CONST_STRING(vm, text) wrenNewStringLength((vm), (text), sizeof(text) - 1)
typedef enum {
OBJ_CLASS,
OBJ_CLOSURE,
OBJ_FIBER,
OBJ_FN,
OBJ_FOREIGN,
OBJ_INSTANCE,
OBJ_LIST,
OBJ_MAP,
OBJ_MODULE,
OBJ_RANGE,
OBJ_STRING,
OBJ_UPVALUE
} ObjType;
typedef struct sObjClass ObjClass;
typedef struct sObj Obj;
struct sObj
{
ObjType type;
bool isDark;
ObjClass* classObj;
struct sObj* next;
};
#if WREN_NAN_TAGGING
typedef uint64_t Value;
#else
typedef enum
{
VAL_FALSE,
VAL_NULL,
VAL_NUM,
VAL_TRUE,
VAL_UNDEFINED,
VAL_OBJ
} ValueType;
typedef struct
{
ValueType type;
union
{
double num;
Obj* obj;
} as;
} Value;
#endif
DECLARE_BUFFER(Value, Value);
struct sObjString
{
Obj obj;
uint32_t length;
uint32_t hash;
char value[FLEXIBLE_ARRAY];
};
typedef struct sObjUpvalue
{
Obj obj;
Value* value;
Value closed;
struct sObjUpvalue* next;
} ObjUpvalue;
typedef bool (*Primitive)(WrenVM* vm, Value* args);
typedef struct
{
char* name;
IntBuffer sourceLines;
} FnDebug;
typedef struct
{
Obj obj;
ValueBuffer variables;
SymbolTable variableNames;
ObjString* name;
} ObjModule;
typedef struct
{
Obj obj;
ByteBuffer code;
ValueBuffer constants;
ObjModule* module;
int maxSlots;
int numUpvalues;
int arity;
FnDebug* debug;
} ObjFn;
typedef struct
{
Obj obj;
ObjFn* fn;
ObjUpvalue* upvalues[FLEXIBLE_ARRAY];
} ObjClosure;
typedef struct
{
uint8_t* ip;
ObjClosure* closure;
Value* stackStart;
} CallFrame;
typedef enum
{
FIBER_TRY,
FIBER_ROOT,
FIBER_OTHER,
} FiberState;
typedef struct sObjFiber
{
Obj obj;
Value* stack;
Value* stackTop;
int stackCapacity;
CallFrame* frames;
int numFrames;
int frameCapacity;
ObjUpvalue* openUpvalues;
struct sObjFiber* caller;
Value error;
FiberState state;
} ObjFiber;
typedef enum
{
METHOD_PRIMITIVE,
METHOD_FUNCTION_CALL,
METHOD_FOREIGN,
METHOD_BLOCK,
METHOD_NONE
} MethodType;
typedef struct
{
MethodType type;
union
{
Primitive primitive;
WrenForeignMethodFn foreign;
ObjClosure* closure;
} as;
} Method;
DECLARE_BUFFER(Method, Method);
struct sObjClass
{
Obj obj;
ObjClass* superclass;
int numFields;
MethodBuffer methods;
ObjString* name;
Value attributes;
};
typedef struct
{
Obj obj;
uint8_t data[FLEXIBLE_ARRAY];
} ObjForeign;
typedef struct
{
Obj obj;
Value fields[FLEXIBLE_ARRAY];
} ObjInstance;
typedef struct
{
Obj obj;
ValueBuffer elements;
} ObjList;
typedef struct
{
Value key;
Value value;
} MapEntry;
typedef struct
{
Obj obj;
uint32_t capacity;
uint32_t count;
MapEntry* entries;
} ObjMap;
typedef struct
{
Obj obj;
double from;
double to;
bool isInclusive;
} ObjRange;
#if WREN_NAN_TAGGING
#define SIGN_BIT ((uint64_t)1 << 63)
#define QNAN ((uint64_t)0x7ffc000000000000)
#define IS_NUM(value) (((value) & QNAN) != QNAN)
#define IS_OBJ(value) (((value) & (QNAN | SIGN_BIT)) == (QNAN | SIGN_BIT))
#define IS_FALSE(value) ((value) == FALSE_VAL)
#define IS_NULL(value) ((value) == NULL_VAL)
#define IS_UNDEFINED(value) ((value) == UNDEFINED_VAL)
#define MASK_TAG (7)
#define TAG_NAN (0)
#define TAG_NULL (1)
#define TAG_FALSE (2)
#define TAG_TRUE (3)
#define TAG_UNDEFINED (4)
#define TAG_UNUSED2 (5)
#define TAG_UNUSED3 (6)
#define TAG_UNUSED4 (7)
#define AS_BOOL(value) ((value) == TRUE_VAL)
#define AS_OBJ(value) ((Obj*)(uintptr_t)((value) & ~(SIGN_BIT | QNAN)))
#define NULL_VAL ((Value)(uint64_t)(QNAN | TAG_NULL))
#define FALSE_VAL ((Value)(uint64_t)(QNAN | TAG_FALSE))
#define TRUE_VAL ((Value)(uint64_t)(QNAN | TAG_TRUE))
#define UNDEFINED_VAL ((Value)(uint64_t)(QNAN | TAG_UNDEFINED))
#define GET_TAG(value) ((int)((value) & MASK_TAG))
#else
#define AS_BOOL(value) ((value).type == VAL_TRUE)
#define AS_OBJ(v) ((v).as.obj)
#define IS_OBJ(value) ((value).type == VAL_OBJ)
#define IS_FALSE(value) ((value).type == VAL_FALSE)
#define IS_NULL(value) ((value).type == VAL_NULL)
#define IS_NUM(value) ((value).type == VAL_NUM)
#define IS_UNDEFINED(value) ((value).type == VAL_UNDEFINED)
#define FALSE_VAL ((Value){ VAL_FALSE, { 0 } })
#define NULL_VAL ((Value){ VAL_NULL, { 0 } })
#define TRUE_VAL ((Value){ VAL_TRUE, { 0 } })
#define UNDEFINED_VAL ((Value){ VAL_UNDEFINED, { 0 } })
#endif
ObjClass* wrenNewSingleClass(WrenVM* vm, int numFields, ObjString* name);
void wrenBindSuperclass(WrenVM* vm, ObjClass* subclass, ObjClass* superclass);
ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass, int numFields,
ObjString* name);
void wrenBindMethod(WrenVM* vm, ObjClass* classObj, int symbol, Method method);
ObjClosure* wrenNewClosure(WrenVM* vm, ObjFn* fn);
ObjFiber* wrenNewFiber(WrenVM* vm, ObjClosure* closure);
static inline void wrenAppendCallFrame(WrenVM* vm, ObjFiber* fiber,
ObjClosure* closure, Value* stackStart)
{
ASSERT(fiber->frameCapacity > fiber->numFrames, "No memory for call frame.");
CallFrame* frame = &fiber->frames[fiber->numFrames++];
frame->stackStart = stackStart;
frame->closure = closure;
frame->ip = closure->fn->code.data;
}
void wrenEnsureStack(WrenVM* vm, ObjFiber* fiber, int needed);
static inline bool wrenHasError(const ObjFiber* fiber)
{
return !IS_NULL(fiber->error);
}
ObjForeign* wrenNewForeign(WrenVM* vm, ObjClass* classObj, size_t size);
ObjFn* wrenNewFunction(WrenVM* vm, ObjModule* module, int maxSlots);
void wrenFunctionBindName(WrenVM* vm, ObjFn* fn, const char* name, int length);
Value wrenNewInstance(WrenVM* vm, ObjClass* classObj);
ObjList* wrenNewList(WrenVM* vm, uint32_t numElements);
void wrenListInsert(WrenVM* vm, ObjList* list, Value value, uint32_t index);
Value wrenListRemoveAt(WrenVM* vm, ObjList* list, uint32_t index);
int wrenListIndexOf(WrenVM* vm, ObjList* list, Value value);
ObjMap* wrenNewMap(WrenVM* vm);
static inline bool wrenMapIsValidKey(Value arg);
Value wrenMapGet(ObjMap* map, Value key);
void wrenMapSet(WrenVM* vm, ObjMap* map, Value key, Value value);
void wrenMapClear(WrenVM* vm, ObjMap* map);
Value wrenMapRemoveKey(WrenVM* vm, ObjMap* map, Value key);
ObjModule* wrenNewModule(WrenVM* vm, ObjString* name);
Value wrenNewRange(WrenVM* vm, double from, double to, bool isInclusive);
Value wrenNewString(WrenVM* vm, const char* text);
Value wrenNewStringLength(WrenVM* vm, const char* text, size_t length);
Value wrenNewStringFromRange(WrenVM* vm, ObjString* source, int start,
uint32_t count, int step);
Value wrenNumToString(WrenVM* vm, double value);
Value wrenStringFormat(WrenVM* vm, const char* format, ...);
Value wrenStringFromCodePoint(WrenVM* vm, int value);
Value wrenStringFromByte(WrenVM* vm, uint8_t value);
Value wrenStringCodePointAt(WrenVM* vm, ObjString* string, uint32_t index);
uint32_t wrenStringFind(ObjString* haystack, ObjString* needle,
uint32_t startIndex);
static inline bool wrenStringEqualsCString(const ObjString* a,
const char* b, size_t length)
{
return a->length == length && memcmp(a->value, b, length) == 0;
}
ObjUpvalue* wrenNewUpvalue(WrenVM* vm, Value* value);
void wrenGrayObj(WrenVM* vm, Obj* obj);
void wrenGrayValue(WrenVM* vm, Value value);
void wrenGrayBuffer(WrenVM* vm, ValueBuffer* buffer);
void wrenBlackenObjects(WrenVM* vm);
void wrenFreeObj(WrenVM* vm, Obj* obj);
ObjClass* wrenGetClass(WrenVM* vm, Value value);
static inline bool wrenValuesSame(Value a, Value b)
{
#if WREN_NAN_TAGGING
return a == b;
#else
if (a.type != b.type) return false;
if (a.type == VAL_NUM) return a.as.num == b.as.num;
return a.as.obj == b.as.obj;
#endif
}
bool wrenValuesEqual(Value a, Value b);
static inline bool wrenIsBool(Value value)
{
#if WREN_NAN_TAGGING
return value == TRUE_VAL || value == FALSE_VAL;
#else
return value.type == VAL_FALSE || value.type == VAL_TRUE;
#endif
}
static inline bool wrenIsObjType(Value value, ObjType type)
{
return IS_OBJ(value) && AS_OBJ(value)->type == type;
}
static inline Value wrenObjectToValue(Obj* obj)
{
#if WREN_NAN_TAGGING
return (Value)(SIGN_BIT | QNAN | (uint64_t)(uintptr_t)(obj));
#else
Value value;
value.type = VAL_OBJ;
value.as.obj = obj;
return value;
#endif
}
static inline double wrenValueToNum(Value value)
{
#if WREN_NAN_TAGGING
return wrenDoubleFromBits(value);
#else
return value.as.num;
#endif
}
static inline Value wrenNumToValue(double num)
{
#if WREN_NAN_TAGGING
return wrenDoubleToBits(num);
#else
Value value;
value.type = VAL_NUM;
value.as.num = num;
return value;
#endif
}
static inline bool wrenMapIsValidKey(Value arg)
{
return IS_BOOL(arg)
|| IS_CLASS(arg)
|| IS_NULL(arg)
|| IS_NUM(arg)
|| IS_RANGE(arg)
|| IS_STRING(arg);
}
#endif