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:
@ -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;
|
||||
}
|
||||
|
||||
@ -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
145
src/vm.c
@ -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]));
|
||||
}
|
||||
|
||||
71
src/vm.h
71
src/vm.h
@ -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);
|
||||
|
||||
Reference in New Issue
Block a user