1
0
forked from Mirror/wren

First phase of changing object representation.

This code is full of hacks and isn't the right representation, but
it gets it so that Value is no longer just a raw Obj*.
This commit is contained in:
Bob Nystrom
2013-11-16 11:26:32 -08:00
parent 1266578a4e
commit 01b61c83d7
4 changed files with 181 additions and 107 deletions

View File

@ -717,7 +717,7 @@ static void function(Compiler* compiler, int allowAssignment)
// Add the function to the constant table. Do this immediately so that it's
// reachable by the GC.
compiler->fn->constants[compiler->fn->numConstants++] = (Value)fnCompiler.fn;
compiler->fn->constants[compiler->fn->numConstants++] = OBJ_VAL(fnCompiler.fn);
// TODO(bob): Hackish.
// Define a fake local slot for the receiver (the function object itself) so
@ -837,7 +837,7 @@ static void number(Compiler* compiler, int allowAssignment)
// Define a constant for the literal.
int constant = addConstant(compiler,
(Value)newNum(compiler->parser->vm, (double)value));
newNum(compiler->parser->vm, (double)value));
// Compile the code to load the constant.
emit(compiler, CODE_CONSTANT);
@ -1167,7 +1167,7 @@ void method(Compiler* compiler, int isStatic, SignatureFn signature)
// Add the block to the constant table. Do this eagerly so it's reachable by
// the GC.
int constant = addConstant(compiler, (Value)methodCompiler.fn);
int constant = addConstant(compiler, OBJ_VAL(methodCompiler.fn));
// TODO(bob): Hackish.
// Define a fake local slot for the receiver so that later locals have the
@ -1314,7 +1314,7 @@ ObjFn* compile(VM* vm, const char* source)
Compiler compiler;
initCompiler(&compiler, &parser, NULL, 0);
pinObj(vm, (Value)compiler.fn);
pinObj(vm, OBJ_VAL(compiler.fn));
for (;;)
{
@ -1335,7 +1335,7 @@ ObjFn* compile(VM* vm, const char* source)
emit(&compiler, CODE_END);
unpinObj(vm, (Value)compiler.fn);
unpinObj(vm, OBJ_VAL(compiler.fn));
return parser.hasError ? NULL : compiler.fn;
}

View File

@ -23,7 +23,7 @@ DEF_PRIMITIVE(bool_not)
DEF_PRIMITIVE(bool_eqeq)
{
if (args[1]->type != OBJ_FALSE && args[1]->type != OBJ_TRUE)
if (!(IS_BOOL(args[1])))
{
return newBool(vm, 0);
}
@ -33,7 +33,7 @@ DEF_PRIMITIVE(bool_eqeq)
DEF_PRIMITIVE(bool_bangeq)
{
if (args[1]->type != OBJ_FALSE && args[1]->type != OBJ_TRUE)
if (!(IS_BOOL(args[1])))
{
return newBool(vm, 1);
}
@ -62,66 +62,66 @@ DEF_PRIMITIVE(bool_toString)
DEF_PRIMITIVE(fn_call0)
{
callFunction(fiber, AS_FN(args[0]), 1);
return NULL;
return NO_VAL;
}
DEF_PRIMITIVE(fn_call1)
{
callFunction(fiber, AS_FN(args[0]), 2);
return NULL;
return NO_VAL;
}
DEF_PRIMITIVE(fn_call2)
{
callFunction(fiber, AS_FN(args[0]), 3);
return NULL;
return NO_VAL;
}
DEF_PRIMITIVE(fn_call3)
{
callFunction(fiber, AS_FN(args[0]), 4);
return NULL;
return NO_VAL;
}
DEF_PRIMITIVE(fn_call4)
{
callFunction(fiber, AS_FN(args[0]), 5);
return NULL;
return NO_VAL;
}
DEF_PRIMITIVE(fn_call5)
{
callFunction(fiber, AS_FN(args[0]), 6);
return NULL;
return NO_VAL;
}
DEF_PRIMITIVE(fn_call6)
{
callFunction(fiber, AS_FN(args[0]), 7);
return NULL;
return NO_VAL;
}
DEF_PRIMITIVE(fn_call7)
{
callFunction(fiber, AS_FN(args[0]), 8);
return NULL;
return NO_VAL;
}
DEF_PRIMITIVE(fn_call8)
{
callFunction(fiber, AS_FN(args[0]), 9);
return NULL;
return NO_VAL;
}
DEF_PRIMITIVE(fn_eqeq)
{
if (args[1]->type != OBJ_FN) return newBool(vm, 0);
if (!IS_FN(args[1])) return newBool(vm, 0);
return newBool(vm, AS_FN(args[0]) == AS_FN(args[1]));
}
DEF_PRIMITIVE(fn_bangeq)
{
if (args[1]->type != OBJ_FN) return newBool(vm, 1);
if (!IS_FN(args[1])) return newBool(vm, 1);
return newBool(vm, AS_FN(args[0]) != AS_FN(args[1]));
}
@ -145,68 +145,68 @@ DEF_PRIMITIVE(num_negate)
DEF_PRIMITIVE(num_minus)
{
if (args[1]->type != OBJ_NUM) return vm->unsupported;
if (!IS_NUM(args[1])) return vm->unsupported;
return (Value)newNum(vm, AS_NUM(args[0]) - AS_NUM(args[1]));
}
DEF_PRIMITIVE(num_plus)
{
if (args[1]->type != OBJ_NUM) return vm->unsupported;
if (!IS_NUM(args[1])) return vm->unsupported;
// TODO(bob): Handle coercion to string if RHS is a string.
return (Value)newNum(vm, AS_NUM(args[0]) + AS_NUM(args[1]));
}
DEF_PRIMITIVE(num_multiply)
{
if (args[1]->type != OBJ_NUM) return vm->unsupported;
if (!IS_NUM(args[1])) return vm->unsupported;
return (Value)newNum(vm, AS_NUM(args[0]) * AS_NUM(args[1]));
}
DEF_PRIMITIVE(num_divide)
{
if (args[1]->type != OBJ_NUM) return vm->unsupported;
if (!IS_NUM(args[1])) return vm->unsupported;
return (Value)newNum(vm, AS_NUM(args[0]) / AS_NUM(args[1]));
}
DEF_PRIMITIVE(num_mod)
{
if (args[1]->type != OBJ_NUM) return vm->unsupported;
if (!IS_NUM(args[1])) return vm->unsupported;
return (Value)newNum(vm, fmod(AS_NUM(args[0]), AS_NUM(args[1])));
}
DEF_PRIMITIVE(num_lt)
{
if (args[1]->type != OBJ_NUM) return vm->unsupported;
if (!IS_NUM(args[1])) return vm->unsupported;
return newBool(vm, AS_NUM(args[0]) < AS_NUM(args[1]));
}
DEF_PRIMITIVE(num_gt)
{
if (args[1]->type != OBJ_NUM) return vm->unsupported;
if (!IS_NUM(args[1])) return vm->unsupported;
return newBool(vm, AS_NUM(args[0]) > AS_NUM(args[1]));
}
DEF_PRIMITIVE(num_lte)
{
if (args[1]->type != OBJ_NUM) return vm->unsupported;
if (!IS_NUM(args[1])) return vm->unsupported;
return newBool(vm, AS_NUM(args[0]) <= AS_NUM(args[1]));
}
DEF_PRIMITIVE(num_gte)
{
if (args[1]->type != OBJ_NUM) return vm->unsupported;
if (!IS_NUM(args[1])) return vm->unsupported;
return newBool(vm, AS_NUM(args[0]) >= AS_NUM(args[1]));
}
DEF_PRIMITIVE(num_eqeq)
{
if (args[1]->type != OBJ_NUM) return newBool(vm, 0);
if (!IS_NUM(args[1])) return newBool(vm, 0);
return newBool(vm, AS_NUM(args[0]) == AS_NUM(args[1]));
}
DEF_PRIMITIVE(num_bangeq)
{
if (args[1]->type != OBJ_NUM) return newBool(vm, 1);
if (!IS_NUM(args[1])) return newBool(vm, 1);
return newBool(vm, AS_NUM(args[0]) != AS_NUM(args[1]));
}
@ -236,7 +236,7 @@ DEF_PRIMITIVE(string_toString)
DEF_PRIMITIVE(string_plus)
{
if (args[1]->type != OBJ_STRING) return vm->unsupported;
if (!IS_STRING(args[1])) return vm->unsupported;
// TODO(bob): Handle coercion to string of RHS.
const char* left = AS_STRING(args[0]);
@ -245,17 +245,19 @@ DEF_PRIMITIVE(string_plus)
size_t leftLength = strlen(left);
size_t rightLength = strlen(right);
ObjString* string = newString(vm, NULL, leftLength + rightLength);
Value value = newString(vm, NULL, leftLength + rightLength);
// TODO(bob): Cast here is lame.
ObjString* string = (ObjString*)(value.obj);
strcpy(string->value, left);
strcpy(string->value + leftLength, right);
string->value[leftLength + rightLength] = '\0';
return (Value)string;
return value;
}
DEF_PRIMITIVE(string_eqeq)
{
if (args[1]->type != OBJ_STRING) return newBool(vm, 0);
if (!IS_STRING(args[1])) return newBool(vm, 0);
const char* a = AS_STRING(args[0]);
const char* b = AS_STRING(args[1]);
return newBool(vm, strcmp(a, b) == 0);
@ -263,7 +265,7 @@ DEF_PRIMITIVE(string_eqeq)
DEF_PRIMITIVE(string_bangeq)
{
if (args[1]->type != OBJ_STRING) return newBool(vm, 1);
if (!IS_STRING(args[1])) return newBool(vm, 1);
const char* a = AS_STRING(args[0]);
const char* b = AS_STRING(args[1]);
return newBool(vm, strcmp(a, b) != 0);
@ -331,8 +333,6 @@ void loadCore(VM* vm)
PRIMITIVE(vm->numClass, "== ", num_eqeq);
PRIMITIVE(vm->numClass, "!= ", num_bangeq);
vm->objectClass = AS_CLASS(findGlobal(vm, "Object"));
vm->stringClass = AS_CLASS(findGlobal(vm, "String"));
PRIMITIVE(vm->stringClass, "contains ", string_contains);
PRIMITIVE(vm->stringClass, "count", string_count);

145
src/vm.c
View File

@ -8,6 +8,14 @@
static Value primitive_metaclass_new(VM* vm, Fiber* fiber, Value* args);
Value objectToValue(Obj* obj)
{
Value value;
value.type = VAL_OBJ;
value.obj = obj;
return value;
}
VM* newVM()
{
// TODO(bob): Get rid of explicit malloc() here.
@ -28,14 +36,7 @@ VM* newVM()
// initialized in case we do a garbage collection before one gets initialized.
for (int i = 0; i < MAX_SYMBOLS; i++)
{
vm->globals[i] = NULL;
}
// Clear out the pinned list. We look for NULL empty slots explicitly.
vm->numPinned = 0;
for (int j = 0; j < MAX_PINNED; j++)
{
vm->pinned[j] = NULL;
vm->globals[i] = NULL_VAL;
}
loadCore(vm);
@ -50,10 +51,10 @@ void freeVM(VM* vm)
free(vm);
}
void markObj(Value value)
{
Obj* obj = (Obj*)value;
static void markValue(Value value);
static void markObj(Obj* obj)
{
// Don't recurse if already marked. Avoids getting stuck in a loop on cycles.
if (obj->flags & FLAG_MARKED) return;
@ -73,17 +74,17 @@ void markObj(Value value)
{
case OBJ_CLASS:
{
ObjClass* classObj = AS_CLASS(obj);
ObjClass* classObj = (ObjClass*)obj;
// The metaclass.
if (classObj->metaclass != NULL) markObj((Value)classObj->metaclass);
if (classObj->metaclass != NULL) markObj((Obj*)classObj->metaclass);
// Method function objects.
for (int i = 0; i < MAX_SYMBOLS; i++)
{
if (classObj->methods[i].type == METHOD_BLOCK)
{
markObj((Value)classObj->methods[i].fn);
markObj((Obj*)classObj->methods[i].fn);
}
}
break;
@ -92,10 +93,10 @@ void markObj(Value value)
case OBJ_FN:
{
// Mark the constants.
ObjFn* fn = AS_FN(obj);
ObjFn* fn = (ObjFn*)obj;
for (int i = 0; i < fn->numConstants; i++)
{
markObj(fn->constants[i]);
markValue(fn->constants[i]);
}
break;
}
@ -118,6 +119,15 @@ void markObj(Value value)
#endif
}
void markValue(Value value)
{
// TODO(bob): Temp-ish hack. NULL_VAL doesn't have an Obj.
if (value.obj == NULL) return;
//if (!IS_OBJ(value)) return;
markObj(value.obj);
}
void freeObj(VM* vm, Obj* obj)
{
#ifdef TRACE_MEMORY
@ -134,17 +144,20 @@ void freeObj(VM* vm, Obj* obj)
{
// TODO(bob): Don't hardcode array sizes.
size = sizeof(ObjFn) + sizeof(Code) * 1024 + sizeof(Value) * 256;
ObjFn* fn = AS_FN(obj);
ObjFn* fn = (ObjFn*)obj;
free(fn->bytecode);
free(fn->constants);
break;
}
case OBJ_STRING:
{
// TODO(bob): O(n) calculation here is lame!
size = sizeof(ObjString) + strlen(AS_STRING(obj));
free(AS_STRING(obj));
ObjString* string = (ObjString*)obj;
size = sizeof(ObjString) + strlen(string->value);
free(string->value);
break;
}
case OBJ_CLASS:
size = sizeof(ObjClass);
@ -171,6 +184,9 @@ void freeObj(VM* vm, Obj* obj)
void collectGarbage(VM* vm)
{
// TODO(bob): Instead of casting to Obj* and calling markObj(), split out
// marking functions for different types.
// Mark all reachable objects.
#ifdef TRACE_MEMORY
printf("-- gc --\n");
@ -181,25 +197,25 @@ void collectGarbage(VM* vm)
{
// Check for NULL to handle globals that have been defined (at compile time)
// but not yet initialized.
if (vm->globals[i] != NULL) markObj(vm->globals[i]);
if (!IS_NULL(vm->globals[i])) markValue(vm->globals[i]);
}
// Pinned objects.
for (int j = 0; j < vm->numPinned; j++)
{
if (vm->pinned[j] != NULL) markObj(vm->pinned[j]);
if (!IS_NULL(vm->pinned[j])) markValue(vm->pinned[j]);
}
// Stack functions.
for (int k = 0; k < vm->fiber->numFrames; k++)
{
markObj((Value)vm->fiber->frames[k].fn);
markObj((Obj*)vm->fiber->frames[k].fn);
}
// Stack variables.
for (int l = 0; l < vm->fiber->stackSize; l++)
{
markObj(vm->fiber->stack[l]);
markValue(vm->fiber->stack[l]);
}
// Collect any unmarked objects.
@ -258,11 +274,14 @@ void initObj(VM* vm, Obj* obj, ObjType type)
vm->first = obj;
}
Value newBool(VM* vm, int value)
Value newBool(VM* vm, int b)
{
Obj* obj = allocate(vm, sizeof(Obj));
initObj(vm, obj, value ? OBJ_TRUE : OBJ_FALSE);
return obj;
// TODO(bob): Get rid of Obj here.
Value value;
value.type = b ? VAL_TRUE : VAL_FALSE;
value.obj = allocate(vm, sizeof(Obj));
initObj(vm, value.obj, b ? OBJ_TRUE : OBJ_FALSE);
return value;
}
static ObjClass* newSingleClass(VM* vm, ObjClass* metaclass,
@ -288,11 +307,12 @@ ObjClass* newClass(VM* vm, ObjClass* superclass)
ObjClass* metaclass = newSingleClass(vm, NULL, NULL);
// Make sure it isn't collected when we allocate the metaclass.
pinObj(vm, (Value)metaclass);
pinObj(vm, OBJ_VAL(metaclass));
ObjClass* classObj = newSingleClass(vm, metaclass, superclass);
unpinObj(vm, (Value)metaclass);
// TODO(bob): Make pin list just Obj* instead of Value.
unpinObj(vm, OBJ_VAL(metaclass));
// Inherit methods from its superclass (unless it's Object, which has none).
// TODO(bob): If we want BETA-style inheritance, we'll need to do this after
@ -325,31 +345,40 @@ ObjFn* newFunction(VM* vm)
return fn;
}
ObjInstance* newInstance(VM* vm, ObjClass* classObj)
Value newInstance(VM* vm, ObjClass* classObj)
{
Value value;
value.type = VAL_OBJ;
ObjInstance* instance = allocate(vm, sizeof(ObjInstance));
value.obj = (Obj*)instance;
initObj(vm, &instance->obj, OBJ_INSTANCE);
instance->classObj = classObj;
return instance;
return value;
}
Value newNull(VM* vm)
{
Obj* obj = allocate(vm, sizeof(Obj));
initObj(vm, obj, OBJ_NULL);
return obj;
// TODO(bob): Get rid of Obj here.
Value value;
value.type = VAL_NULL;
value.obj = allocate(vm, sizeof(Obj));
initObj(vm, value.obj, OBJ_NULL);
return value;
}
ObjNum* newNum(VM* vm, double number)
Value newNum(VM* vm, double number)
{
Value value;
value.type = VAL_NUM;
ObjNum* num = allocate(vm, sizeof(ObjNum));
value.obj = (Obj*)num;
initObj(vm, &num->obj, OBJ_NUM);
num->value = number;
return num;
return value;
}
ObjString* newString(VM* vm, const char* text, size_t length)
Value newString(VM* vm, const char* text, size_t length)
{
// Allocate before the string object in case this triggers a GC which would
// free the string object.
@ -366,7 +395,10 @@ ObjString* newString(VM* vm, const char* text, size_t length)
}
string->value = heapText;
return string;
Value value;
value.type = VAL_OBJ;
value.obj = (Obj*)string;
return value;
}
void initSymbolTable(SymbolTable* symbols)
@ -474,7 +506,7 @@ void dumpCode(VM* vm, ObjFn* fn)
case CODE_CLASS:
printf("CLASS\n");
break;
case CODE_SUBCLASS:
printf("SUBCLASS\n");
break;
@ -590,11 +622,11 @@ void dumpCode(VM* vm, ObjFn* fn)
*/
// Returns the class of [object].
static ObjClass* getClass(VM* vm, Value object)
static ObjClass* getClass(VM* vm, Value value)
{
switch (object->type)
switch (value.obj->type)
{
case OBJ_CLASS: return AS_CLASS(object)->metaclass;
case OBJ_CLASS: return AS_CLASS(value)->metaclass;
case OBJ_FALSE:
case OBJ_TRUE:
return vm->boolClass;
@ -603,7 +635,7 @@ static ObjClass* getClass(VM* vm, Value object)
case OBJ_NULL: return vm->nullClass;
case OBJ_NUM: return vm->numClass;
case OBJ_STRING: return vm->stringClass;
case OBJ_INSTANCE: return AS_INSTANCE(object)->classObj;
case OBJ_INSTANCE: return AS_INSTANCE(value)->classObj;
}
}
@ -655,6 +687,12 @@ Value interpret(VM* vm, ObjFn* fn)
ObjClass* classObj = newClass(vm, superclass);
// Assume the first class being defined is Object.
if (vm->objectClass == NULL)
{
vm->objectClass = classObj;
}
// Define a "new" method on the metaclass.
// TODO(bob): Can this be inherited?
int newSymbol = ensureSymbol(&vm->methods, "new", strlen("new"));
@ -662,14 +700,14 @@ Value interpret(VM* vm, ObjFn* fn)
classObj->metaclass->methods[newSymbol].primitive =
primitive_metaclass_new;
PUSH((Value)classObj);
PUSH(OBJ_VAL(classObj));
break;
}
case CODE_METACLASS:
{
ObjClass* classObj = AS_CLASS(PEEK());
PUSH((Value)classObj->metaclass);
PUSH(OBJ_VAL(classObj->metaclass));
break;
}
@ -753,7 +791,7 @@ Value interpret(VM* vm, ObjFn* fn)
Value result = method->primitive(vm, fiber, args);
// If the primitive pushed a call frame, it returns NULL.
if (result != NULL)
if (result.type != VAL_NO_VALUE)
{
fiber->stack[fiber->stackSize - numArgs] = result;
@ -779,7 +817,7 @@ Value interpret(VM* vm, ObjFn* fn)
Value condition = POP();
// False is the only falsey value.
if (condition->type == OBJ_FALSE)
if (!AS_BOOL(condition))
{
frame->ip += offset;
}
@ -831,10 +869,10 @@ void callFunction(Fiber* fiber, ObjFn* fn, int numArgs)
void printValue(Value value)
{
// TODO(bob): Do more useful stuff here.
switch (value->type)
switch (value.obj->type)
{
case OBJ_CLASS:
printf("[class %p]", value);
printf("[class %p]", value.obj);
break;
case OBJ_FALSE:
@ -842,11 +880,11 @@ void printValue(Value value)
break;
case OBJ_FN:
printf("[fn %p]", value);
printf("[fn %p]", value.obj);
break;
case OBJ_INSTANCE:
printf("[instance %p]", value);
printf("[instance %p]", value.obj);
break;
case OBJ_NULL:
@ -875,7 +913,8 @@ void pinObj(VM* vm, Value value)
void unpinObj(VM* vm, Value value)
{
ASSERT(vm->pinned[vm->numPinned - 1] == value,
// TODO(bob): Do real equivalance check here.
ASSERT(vm->pinned[vm->numPinned - 1].type == value.type,
"Unpinning object out of stack order.");
vm->numPinned--;
}
@ -883,5 +922,5 @@ void unpinObj(VM* vm, Value value)
Value primitive_metaclass_new(VM* vm, Fiber* fiber, Value* args)
{
// TODO(bob): Invoke initializer method.
return (Value)newInstance(vm, AS_CLASS(args[0]));
return newInstance(vm, AS_CLASS(args[0]));
}

View File

@ -7,33 +7,54 @@
#define MAX_SYMBOLS 256
#define MAX_PINNED 16
// Get the class value of [obj] (0 or 1), which must be a boolean.
#define AS_CLASS(obj) ((ObjClass*)obj)
// Get the class value of [value] (0 or 1), which must be a boolean.
#define AS_CLASS(value) ((ObjClass*)(value).obj)
// Get the bool value of [obj] (0 or 1), which must be a boolean.
#define AS_BOOL(obj) (((Obj*)obj)->type == OBJ_TRUE)
#define AS_BOOL(value) (((Obj*)(value).obj)->type == OBJ_TRUE)
// Get the function value of [obj] (0 or 1), which must be a function.
#define AS_FN(obj) ((ObjFn*)obj)
#define AS_FN(value) ((ObjFn*)(value).obj)
// Get the double value of [obj], which must be a number.
#define AS_INSTANCE(obj) ((ObjInstance*)obj)
#define AS_INSTANCE(value) ((ObjInstance*)(value).obj)
// Get the double value of [obj], which must be a number.
#define AS_NUM(obj) (((ObjNum*)obj)->value)
// Get the double value of [value], which must be a number.
#define AS_NUM(v) (((ObjNum*)(v).obj)->value)
// Get the const char* value of [obj], which must be a string.
#define AS_STRING(obj) (((ObjString*)obj)->value)
// Get the const char* value of [v], which must be a string.
#define AS_STRING(v) (((ObjString*)(v).obj)->value)
// Determines if [value] is a garbage-collected object or not.
#define IS_OBJ(value) ((value).type == VAL_OBJ)
#define IS_NULL(value) ((value).type == VAL_NULL)
#define IS_NUM(value) ((value).type == VAL_NUM)
#define IS_BOOL(value) ((value).type == VAL_FALSE || (value).type == VAL_TRUE)
// TODO(bob): Evaluating value here twice sucks.
#define IS_FN(value) ((value).type == VAL_OBJ && (value).obj->type == OBJ_FN)
#define IS_STRING(value) ((value).type == VAL_OBJ && (value).obj->type == OBJ_STRING)
typedef enum
{
VAL_FALSE,
VAL_NULL,
VAL_NUM,
VAL_TRUE,
VAL_NO_VALUE, // TODO(bob): Hack remove.
VAL_OBJ
} ValueType;
typedef enum {
OBJ_CLASS,
OBJ_FALSE,
OBJ_FN,
OBJ_INSTANCE,
OBJ_NULL,
OBJ_NUM,
OBJ_STRING,
OBJ_TRUE
OBJ_TRUE,
OBJ_CLASS,
OBJ_FN,
OBJ_INSTANCE,
OBJ_STRING
} ObjType;
typedef enum
@ -51,7 +72,12 @@ typedef struct sObj
struct sObj* next;
} Obj;
typedef Obj* Value;
// TODO(bob): Temp.
typedef struct
{
ValueType type;
Obj* obj;
} Value;
typedef struct sVM VM;
typedef struct sFiber Fiber;
@ -257,6 +283,15 @@ struct sFiber
int numFrames;
};
// TODO(bob): Temp.
#define OBJ_VAL(obj) (objectToValue((Obj*)(obj)))
Value objectToValue(Obj* obj);
// TODO(bob): Not C89!
#define NULL_VAL ((Value){ VAL_NULL, NULL })
// TODO(bob): Gross.
#define NO_VAL ((Value){ VAL_NO_VALUE, NULL })
VM* newVM();
void freeVM(VM* vm);
@ -272,16 +307,16 @@ ObjFn* newFunction(VM* vm);
ObjClass* newClass(VM* vm, ObjClass* superclass);
// Creates a new instance of the given [classObj].
ObjInstance* newInstance(VM* vm, ObjClass* classObj);
Value newInstance(VM* vm, ObjClass* classObj);
// Creates a new null object.
Value newNull(VM* vm);
// Creates a new number object.
ObjNum* newNum(VM* vm, double number);
Value newNum(VM* vm, double number);
// Creates a new string object and copies [text] into it.
ObjString* newString(VM* vm, const char* text, size_t length);
Value newString(VM* vm, const char* text, size_t length);
// Initializes the symbol table.
void initSymbolTable(SymbolTable* symbols);