that's a lot of stuff and pretty much finishes the book; need to do optimizations
This commit is contained in:
parent
1f754ebc01
commit
4982881235
3
chunk.h
3
chunk.h
@ -60,6 +60,9 @@ typedef enum {
|
||||
OP_METHOD_LONG,
|
||||
OP_IMPORT,
|
||||
OP_IMPORT_LONG,
|
||||
OP_INHERIT,
|
||||
OP_GET_SUPER,
|
||||
OP_GET_SUPER_LONG,
|
||||
} KrkOpCode;
|
||||
|
||||
/**
|
||||
|
87
compiler.c
87
compiler.c
@ -70,6 +70,7 @@ typedef struct Compiler {
|
||||
typedef struct ClassCompiler {
|
||||
struct ClassCompiler * enclosing;
|
||||
KrkToken name;
|
||||
int hasSuperClass;
|
||||
} ClassCompiler;
|
||||
|
||||
Parser parser;
|
||||
@ -111,6 +112,7 @@ static void initCompiler(Compiler * compiler, FunctionType type) {
|
||||
|
||||
static void parsePrecedence(Precedence precedence);
|
||||
static ssize_t parseVariable(const char * errorMessage);
|
||||
static void variable(int canAssign);
|
||||
static void defineVariable(size_t global);
|
||||
static uint8_t argumentList();
|
||||
static ssize_t identifierConstant(KrkToken * name);
|
||||
@ -125,6 +127,7 @@ static void and_(int canAssign);
|
||||
static void classDeclaration();
|
||||
static void declareVariable();
|
||||
static void namedVariable(KrkToken name, int canAssign);
|
||||
static void addLocal(KrkToken name);
|
||||
|
||||
static void errorAt(KrkToken * token, const char * message) {
|
||||
if (parser.panicMode) return;
|
||||
@ -182,6 +185,17 @@ static int match(KrkTokenType type) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int identifiersEqual(KrkToken * a, KrkToken * b) {
|
||||
return (a->length == b->length && memcmp(a->start, b->start, a->length) == 0);
|
||||
}
|
||||
|
||||
static KrkToken syntheticToken(const char * text) {
|
||||
KrkToken token;
|
||||
token.start = text;
|
||||
token.length = (int)strlen(text);
|
||||
return token;
|
||||
}
|
||||
|
||||
static void emitByte(uint8_t byte) {
|
||||
krk_writeChunk(currentChunk(), byte, parser.previous.line);
|
||||
}
|
||||
@ -203,7 +217,7 @@ static void emitReturn() {
|
||||
static KrkFunction * endCompiler() {
|
||||
emitReturn();
|
||||
KrkFunction * function = current->function;
|
||||
#define DEBUG
|
||||
#undef DEBUG
|
||||
#ifdef DEBUG
|
||||
if (!parser.hadError) {
|
||||
krk_disassembleChunk(currentChunk(), function->name != NULL ? function->name->chars : "<module>");
|
||||
@ -282,6 +296,25 @@ static void call(int canAssign) {
|
||||
emitBytes(OP_CALL, argCount);
|
||||
}
|
||||
|
||||
static void get_(int canAssign) {
|
||||
/* Synthesize get */
|
||||
KrkToken _get = syntheticToken("__get__");
|
||||
KrkToken _set = syntheticToken("__set__");
|
||||
size_t indGet = identifierConstant(&_get);
|
||||
size_t indSet = identifierConstant(&_set);
|
||||
size_t offset = currentChunk()->count + 1;
|
||||
emitBytes(OP_GET_PROPERTY, indGet); /* TODO what if it's > 256 */
|
||||
expression();
|
||||
consume(TOKEN_RIGHT_SQUARE, "Expected ending square bracket...");
|
||||
if (canAssign && match(TOKEN_EQUAL)) {
|
||||
expression();
|
||||
currentChunk()->code[offset] = indSet;
|
||||
emitBytes(OP_CALL, 2);
|
||||
} else {
|
||||
emitBytes(OP_CALL, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void dot(int canAssign) {
|
||||
consume(TOKEN_IDENTIFIER, "Expected propert name");
|
||||
size_t ind = identifierConstant(&parser.previous);
|
||||
@ -307,7 +340,7 @@ static void expression() {
|
||||
}
|
||||
|
||||
static void varDeclaration() {
|
||||
ssize_t global = parseVariable("Expected variable name.");
|
||||
ssize_t ind = parseVariable("Expected variable name.");
|
||||
|
||||
if (match(TOKEN_EQUAL)) {
|
||||
expression();
|
||||
@ -315,7 +348,7 @@ static void varDeclaration() {
|
||||
emitByte(OP_NONE);
|
||||
}
|
||||
|
||||
defineVariable(global);
|
||||
defineVariable(ind);
|
||||
}
|
||||
|
||||
static void printStatement() {
|
||||
@ -375,8 +408,10 @@ static void endScope() {
|
||||
while (current->localCount > 0 &&
|
||||
current->locals[current->localCount - 1].depth > current->scopeDepth) {
|
||||
if (current->locals[current->localCount - 1].isCaptured) {
|
||||
fprintf(stderr, "Emitting close\n");
|
||||
emitByte(OP_CLOSE_UPVALUE);
|
||||
} else {
|
||||
fprintf(stderr, "emitting pop\n");
|
||||
emitByte(OP_POP);
|
||||
}
|
||||
current->localCount--;
|
||||
@ -480,9 +515,28 @@ static void classDeclaration() {
|
||||
|
||||
ClassCompiler classCompiler;
|
||||
classCompiler.name = parser.previous;
|
||||
classCompiler.hasSuperClass = 0;
|
||||
classCompiler.enclosing = currentClass;
|
||||
currentClass = &classCompiler;
|
||||
|
||||
if (match(TOKEN_LEFT_PAREN)) {
|
||||
if (match(TOKEN_IDENTIFIER)) {
|
||||
variable(0);
|
||||
if (identifiersEqual(&className, &parser.previous)) {
|
||||
error("A class can not inherit from itself.");
|
||||
}
|
||||
|
||||
beginScope();
|
||||
addLocal(syntheticToken("super"));
|
||||
defineVariable(0);
|
||||
|
||||
namedVariable(className, 0);
|
||||
emitByte(OP_INHERIT);
|
||||
classCompiler.hasSuperClass = 1;
|
||||
}
|
||||
consume(TOKEN_RIGHT_PAREN, "Expected closing brace after superclass.");
|
||||
}
|
||||
|
||||
namedVariable(className, 0);
|
||||
|
||||
consume(TOKEN_COLON, "Expected colon after class");
|
||||
@ -502,6 +556,9 @@ static void classDeclaration() {
|
||||
}
|
||||
} /* else empty class (and at end of file?) we'll allow it for now... */
|
||||
emitByte(OP_POP);
|
||||
if (classCompiler.hasSuperClass) {
|
||||
endScope();
|
||||
}
|
||||
currentClass = currentClass->enclosing;
|
||||
}
|
||||
|
||||
@ -796,12 +853,28 @@ static void self(int canAssign) {
|
||||
variable(0);
|
||||
}
|
||||
|
||||
static void super_(int canAssign) {
|
||||
if (currentClass == NULL) {
|
||||
error("Invalid reference to `super` outside of a class.");
|
||||
} else if (!currentClass->hasSuperClass) {
|
||||
error("Invalid reference to `super` from a base class.");
|
||||
}
|
||||
consume(TOKEN_LEFT_PAREN, "Expected `super` to be called.");
|
||||
consume(TOKEN_RIGHT_PAREN, "`super` can not take arguments.");
|
||||
consume(TOKEN_DOT, "Expected a field of `super()` to be referenced.");
|
||||
consume(TOKEN_IDENTIFIER, "Expected a field name.");
|
||||
size_t ind = identifierConstant(&parser.previous);
|
||||
namedVariable(syntheticToken("self"), 0);
|
||||
namedVariable(syntheticToken("super"), 0);
|
||||
EMIT_CONSTANT_OP(OP_GET_SUPER, ind);
|
||||
}
|
||||
|
||||
ParseRule rules[] = {
|
||||
[TOKEN_LEFT_PAREN] = {grouping, call, PREC_CALL},
|
||||
[TOKEN_RIGHT_PAREN] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_LEFT_BRACE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_RIGHT_BRACE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_LEFT_SQUARE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_LEFT_SQUARE] = {NULL, get_, PREC_CALL},
|
||||
[TOKEN_RIGHT_SQUARE] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_COLON] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_COMMA] = {NULL, NULL, PREC_NONE},
|
||||
@ -838,7 +911,7 @@ ParseRule rules[] = {
|
||||
[TOKEN_PRINT] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_RETURN] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_SELF] = {self, NULL, PREC_NONE},
|
||||
[TOKEN_SUPER] = {NULL, NULL, PREC_NONE},
|
||||
[TOKEN_SUPER] = {super_, NULL, PREC_NONE},
|
||||
[TOKEN_TRUE] = {literal, NULL, PREC_NONE},
|
||||
[TOKEN_WHILE] = {NULL, NULL, PREC_NONE},
|
||||
|
||||
@ -872,10 +945,6 @@ static ssize_t identifierConstant(KrkToken * name) {
|
||||
return krk_addConstant(currentChunk(), OBJECT_VAL(copyString(name->start, name->length)));
|
||||
}
|
||||
|
||||
static int identifiersEqual(KrkToken * a, KrkToken * b) {
|
||||
return (a->length == b->length && memcmp(a->start, b->start, a->length) == 0);
|
||||
}
|
||||
|
||||
static ssize_t resolveLocal(Compiler * compiler, KrkToken * name) {
|
||||
for (ssize_t i = compiler->localCount - 1; i >= 0; i--) {
|
||||
Local * local = &compiler->locals[i];
|
||||
|
6
debug.c
6
debug.c
@ -40,8 +40,8 @@ void krk_disassembleChunk(KrkChunk * chunk, const char * name) {
|
||||
#define CLOSURE_MORE \
|
||||
KrkFunction * function = AS_FUNCTION(chunk->constants.values[constant]); \
|
||||
for (size_t j = 0; j < function->upvalueCount; ++j) { \
|
||||
int isLocal = chunk->code[offset++]; \
|
||||
int index = chunk->code[offset++]; \
|
||||
int isLocal = chunk->code[offset++ + 2]; \
|
||||
int index = chunk->code[offset++ + 2]; \
|
||||
fprintf(stderr, "%04d | %s %d\n", \
|
||||
(int)offset - 2, isLocal ? "local" : "upvalue", index); \
|
||||
}
|
||||
@ -71,6 +71,7 @@ size_t krk_disassembleInstruction(KrkChunk * chunk, size_t offset) {
|
||||
SIMPLE(OP_LESS)
|
||||
SIMPLE(OP_PRINT)
|
||||
SIMPLE(OP_POP)
|
||||
SIMPLE(OP_INHERIT)
|
||||
CONSTANT(OP_DEFINE_GLOBAL,(void)0)
|
||||
CONSTANT(OP_CONSTANT,(void)0)
|
||||
CONSTANT(OP_GET_GLOBAL,(void)0)
|
||||
@ -81,6 +82,7 @@ size_t krk_disassembleInstruction(KrkChunk * chunk, size_t offset) {
|
||||
CONSTANT(OP_METHOD, (void)0)
|
||||
CONSTANT(OP_CLOSURE, CLOSURE_MORE)
|
||||
CONSTANT(OP_IMPORT, (void)0)
|
||||
CONSTANT(OP_GET_SUPER, (void)0)
|
||||
OPERANDL(OP_SET_LOCAL)
|
||||
OPERANDL(OP_GET_LOCAL)
|
||||
OPERANDL(OP_SET_UPVALUE)
|
||||
|
4
kuroko.c
4
kuroko.c
@ -6,9 +6,11 @@
|
||||
#include "vm.h"
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
if (argc < 2) return 1;
|
||||
|
||||
krk_initVM();
|
||||
|
||||
KrkValue result = krk_runfile("test.krk",0);
|
||||
KrkValue result = krk_runfile(argv[1],0);
|
||||
|
||||
krk_freeVM();
|
||||
|
||||
|
4
memory.c
4
memory.c
@ -197,7 +197,9 @@ static void markRoots() {
|
||||
}
|
||||
krk_markTable(&vm.globals);
|
||||
krk_markCompilerRoots();
|
||||
krk_markObject((KrkObj*)vm.__init__);
|
||||
for (int i = 0; i < METHOD__MAX; ++i) {
|
||||
krk_markValue(vm.specialMethodNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void krk_collectGarbage(void) {
|
||||
|
31
system.krk
31
system.krk
@ -1,11 +1,36 @@
|
||||
# This is a module
|
||||
class SystemModule:
|
||||
|
||||
class HashMap:
|
||||
def __init__(self):
|
||||
self._get = __krk_builtin_hash_get
|
||||
self._set = __krk_builtin_hash_set
|
||||
self._map = __krk_builtin_hash_new()
|
||||
def __get__(self, ind):
|
||||
return self._get(self._map, ind)
|
||||
def __set__(self, ind, val):
|
||||
self._set(self._map, ind, val)
|
||||
|
||||
class List:
|
||||
def __init__(self):
|
||||
self._get = __krk_builtin_list_get
|
||||
self._set = __krk_builtin_list_set
|
||||
self._app = __krk_builtin_list_append
|
||||
self._len = __krk_builtin_list_length
|
||||
self._list = __krk_builtin_list_new()
|
||||
def __get__(self, ind):
|
||||
return self._get(self._list, ind)
|
||||
def __set__(self, ind, val):
|
||||
return self._set(self._list, ind, val)
|
||||
def append(self, val):
|
||||
return self._app(self._list, val)
|
||||
def length(self):
|
||||
return self._len(self._list)
|
||||
|
||||
let module = SystemModule()
|
||||
module.sleep = __krk_builtin_sleep
|
||||
|
||||
print "You imported the system module."
|
||||
print "It has a module: " + module
|
||||
print "Which has a function: " + module.sleep
|
||||
module.HashMap = HashMap
|
||||
module.List = List
|
||||
|
||||
return module
|
||||
|
42
test.krk
42
test.krk
@ -82,11 +82,9 @@ if True:
|
||||
print c
|
||||
funcs.f = f
|
||||
funcs.g = g
|
||||
funcs.h = h
|
||||
|
||||
funcs.f()
|
||||
funcs.g()
|
||||
funcs.h()
|
||||
|
||||
def outer(): # test
|
||||
#foo
|
||||
@ -123,6 +121,26 @@ print test.foo
|
||||
print test.doAThing
|
||||
test.doAThing()
|
||||
|
||||
|
||||
class SuperClass():
|
||||
def __init__(self):
|
||||
self.a = "class"
|
||||
def aMethod(self):
|
||||
print "This is a great " + self.a + "!"
|
||||
def __str__(self):
|
||||
return "(I am a " + self.a + ")"
|
||||
def __get__(self, ind):
|
||||
return "(get[" + ind + "])"
|
||||
def __set__(self, ind, val):
|
||||
print "(set[" + ind + "] = " + val + ")"
|
||||
|
||||
class SubClass(SuperClass):
|
||||
def __init__(self):
|
||||
self.a = "teapot"
|
||||
|
||||
let subclass = SubClass()
|
||||
subclass.aMethod()
|
||||
|
||||
# Nope
|
||||
#print self
|
||||
# Also nope
|
||||
@ -132,3 +150,23 @@ test.doAThing()
|
||||
#def notAMethoDeither(self):
|
||||
# print self
|
||||
|
||||
print "Subclass says: " + subclass
|
||||
|
||||
subclass.__get__(123)
|
||||
print subclass[123]
|
||||
subclass[456] = "test"
|
||||
|
||||
print "Let's make a hashmap:"
|
||||
let hash = system.HashMap()
|
||||
hash["hello"] = "world"
|
||||
print hash["hello"]
|
||||
|
||||
print "Let's make some lists:"
|
||||
let list = system.List()
|
||||
print "Length before: " + list.length()
|
||||
list.append(1)
|
||||
list.append(2)
|
||||
list.append(3)
|
||||
print "Length after: " + list.length()
|
||||
for j = 0, j < list.length(), j = j + 1:
|
||||
print "j=" + j + ", list[j]=" + list[j]
|
||||
|
142
vm.c
142
vm.c
@ -11,6 +11,8 @@
|
||||
/* Why is this static... why do we do this to ourselves... */
|
||||
KrkVM vm;
|
||||
|
||||
static KrkValue run();
|
||||
|
||||
static void resetStack() {
|
||||
vm.stackTop = vm.stack;
|
||||
vm.frameCount = 0;
|
||||
@ -88,6 +90,64 @@ static KrkValue krk_sleep(int argc, KrkValue argv[]) {
|
||||
return BOOLEAN_VAL(1);
|
||||
}
|
||||
|
||||
static KrkValue krk_expose_hash_new(int argc, KrkValue argv[]) {
|
||||
/* This is absuing the existing object system so it can work without
|
||||
* having to add any new types to the garbage collector, and yes
|
||||
* it is absolute terrible, do not use it. */
|
||||
KrkClass * map = newClass(NULL);
|
||||
return OBJECT_VAL(map);
|
||||
}
|
||||
|
||||
static KrkValue krk_expose_hash_get(int argc, KrkValue argv[]) {
|
||||
if (argc < 2 || !IS_CLASS(argv[0])) return NONE_VAL();
|
||||
KrkClass * map = AS_CLASS(argv[0]);
|
||||
KrkValue out = NONE_VAL();
|
||||
krk_tableGet(&map->methods, argv[1], &out);
|
||||
return out;
|
||||
}
|
||||
|
||||
static KrkValue krk_expose_hash_set(int argc, KrkValue argv[]) {
|
||||
if (argc < 3 || !IS_CLASS(argv[0])) return NONE_VAL();
|
||||
KrkClass * map = AS_CLASS(argv[0]);
|
||||
krk_tableSet(&map->methods, argv[1], argv[2]);
|
||||
return BOOLEAN_VAL(1);
|
||||
}
|
||||
|
||||
static KrkValue krk_expose_list_new(int argc, KrkValue argv[]) {
|
||||
KrkFunction * list = newFunction(NULL);
|
||||
return OBJECT_VAL(list);
|
||||
}
|
||||
|
||||
static KrkValue krk_expose_list_get(int argc, KrkValue argv[]) {
|
||||
if (argc < 2 || !IS_FUNCTION(argv[0]) || !IS_INTEGER(argv[1])) return NONE_VAL();
|
||||
KrkFunction * list = AS_FUNCTION(argv[0]);
|
||||
int index = AS_INTEGER(argv[1]);
|
||||
if (index < 0 || index >= list->chunk.constants.count) return NONE_VAL();
|
||||
return list->chunk.constants.values[index];
|
||||
}
|
||||
|
||||
static KrkValue krk_expose_list_set(int argc, KrkValue argv[]) {
|
||||
if (argc < 3 || !IS_FUNCTION(argv[0]) || !IS_INTEGER(argv[1])) return NONE_VAL();
|
||||
KrkFunction * list = AS_FUNCTION(argv[0]);
|
||||
int index = AS_INTEGER(argv[1]);
|
||||
if (index < 0 || index >= list->chunk.constants.count) return NONE_VAL();
|
||||
list->chunk.constants.values[index] = argv[1];
|
||||
return BOOLEAN_VAL(1);
|
||||
}
|
||||
|
||||
static KrkValue krk_expose_list_append(int argc, KrkValue argv[]) {
|
||||
if (argc < 2 || !IS_FUNCTION(argv[0])) return NONE_VAL();
|
||||
KrkFunction * list = AS_FUNCTION(argv[0]);
|
||||
krk_writeValueArray(&list->chunk.constants, argv[1]);
|
||||
return INTEGER_VAL(list->chunk.constants.count-1);
|
||||
}
|
||||
|
||||
static KrkValue krk_expose_list_length(int argc, KrkValue argv[]) {
|
||||
if (argc < 1 || !IS_FUNCTION(argv[0])) return NONE_VAL();
|
||||
KrkFunction * list = AS_FUNCTION(argv[0]);
|
||||
return INTEGER_VAL(list->chunk.constants.count);
|
||||
}
|
||||
|
||||
static int call(KrkClosure * closure, int argCount) {
|
||||
if (argCount != closure->function->arity) {
|
||||
runtimeError("Wrong number of arguments (%d expected, got %d)", closure->function->arity, argCount);
|
||||
@ -100,7 +160,7 @@ static int call(KrkClosure * closure, int argCount) {
|
||||
CallFrame * frame = &vm.frames[vm.frameCount++];
|
||||
frame->closure = closure;
|
||||
frame->ip = closure->function->chunk.code;
|
||||
frame->slots = vm.stackTop - argCount - 1;
|
||||
frame->slots = (vm.stackTop - argCount - 1) - vm.stack;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -120,7 +180,7 @@ static int callValue(KrkValue callee, int argCount) {
|
||||
KrkClass * _class = AS_CLASS(callee);
|
||||
vm.stackTop[-argCount - 1] = OBJECT_VAL(newInstance(_class));
|
||||
KrkValue initializer;
|
||||
if (krk_tableGet(&_class->methods, OBJECT_VAL(vm.__init__), &initializer)) {
|
||||
if (krk_tableGet(&_class->methods, vm.specialMethodNames[METHOD_INIT], &initializer)) {
|
||||
return call(AS_CLOSURE(initializer), argCount);
|
||||
} else if (argCount != 0) {
|
||||
runtimeError("Class does not have an __init__ but arguments were passed to initializer: %d\n", argCount);
|
||||
@ -157,7 +217,7 @@ static KrkUpvalue * captureUpvalue(KrkValue * local) {
|
||||
prevUpvalue = upvalue;
|
||||
upvalue = upvalue->next;
|
||||
}
|
||||
if (upvalue && upvalue->location == local) {
|
||||
if (upvalue != NULL && upvalue->location == local) {
|
||||
return upvalue;
|
||||
}
|
||||
KrkUpvalue * createdUpvalue = newUpvalue(local);
|
||||
@ -196,15 +256,30 @@ void krk_initVM() {
|
||||
vm.grayStack = NULL;
|
||||
krk_initTable(&vm.globals);
|
||||
krk_initTable(&vm.strings);
|
||||
vm.__init__ = NULL;
|
||||
vm.__init__ = copyString("__init__", 8);
|
||||
memset(vm.specialMethodNames,0,sizeof(vm.specialMethodNames));
|
||||
|
||||
vm.specialMethodNames[METHOD_INIT] = OBJECT_VAL(copyString("__init__", 8));
|
||||
vm.specialMethodNames[METHOD_STR] = OBJECT_VAL(copyString("__str__", 7));
|
||||
|
||||
defineNative("__krk_builtin_sleep", krk_sleep);
|
||||
|
||||
/* Hash maps */
|
||||
defineNative("__krk_builtin_hash_new", krk_expose_hash_new);
|
||||
defineNative("__krk_builtin_hash_set", krk_expose_hash_set);
|
||||
defineNative("__krk_builtin_hash_get", krk_expose_hash_get);
|
||||
|
||||
/* Lists */
|
||||
defineNative("__krk_builtin_list_new", krk_expose_list_new);
|
||||
defineNative("__krk_builtin_list_get", krk_expose_list_get);
|
||||
defineNative("__krk_builtin_list_set", krk_expose_list_set);
|
||||
defineNative("__krk_builtin_list_append", krk_expose_list_append);
|
||||
defineNative("__krk_builtin_list_length", krk_expose_list_length);
|
||||
}
|
||||
|
||||
void krk_freeVM() {
|
||||
krk_freeTable(&vm.globals);
|
||||
krk_freeTable(&vm.strings);
|
||||
vm.__init__ = NULL;
|
||||
memset(vm.specialMethodNames,0,sizeof(vm.specialMethodNames));
|
||||
krk_freeObjects();
|
||||
FREE_ARRAY(size_t, vm.stack, vm.stackSize);
|
||||
}
|
||||
@ -226,6 +301,8 @@ const char * typeName(KrkValue value) {
|
||||
if (IS_FUNCTION(value)) return "Function";
|
||||
if (IS_NATIVE(value)) return "Native";
|
||||
if (IS_CLOSURE(value)) return "Closure";
|
||||
if (IS_CLASS(value)) return "Class";
|
||||
if (IS_INSTANCE(value)) return "Instance";
|
||||
return "(Unspecified Object)";
|
||||
}
|
||||
return "???";
|
||||
@ -289,7 +366,7 @@ static void addObjects() {
|
||||
concatenate(a->chars,b->chars,a->length,b->length);
|
||||
return;
|
||||
}
|
||||
char tmp[256];
|
||||
char tmp[256] = {0};
|
||||
if (IS_INTEGER(_b)) {
|
||||
sprintf(tmp, "%d", AS_INTEGER(_b));
|
||||
} else if (IS_FLOATING(_b)) {
|
||||
@ -298,6 +375,24 @@ static void addObjects() {
|
||||
sprintf(tmp, "%s", AS_BOOLEAN(_b) ? "True" : "False");
|
||||
} else if (IS_NONE(_b)) {
|
||||
sprintf(tmp, "None");
|
||||
} else if (IS_INSTANCE(_b)) {
|
||||
KrkValue method;
|
||||
if (!krk_tableGet(&AS_INSTANCE(_b)->_class->methods, vm.specialMethodNames[METHOD_STR], &method)) {
|
||||
sprintf(tmp, "<instance>");
|
||||
} else {
|
||||
/* Push the object for self reference */
|
||||
krk_push(_b);
|
||||
call(AS_CLOSURE(method), 0);
|
||||
int previousExitFrame = vm.exitOnFrame;
|
||||
vm.exitOnFrame = vm.frameCount - 1;
|
||||
KrkValue result = run();
|
||||
vm.exitOnFrame = previousExitFrame;
|
||||
if (!IS_STRING(result)) {
|
||||
runtimeError("__str__ produced something that wasn't a string: %s", typeName(result));
|
||||
return;
|
||||
}
|
||||
sprintf(tmp, "%s", AS_CSTRING(result));
|
||||
}
|
||||
} else {
|
||||
sprintf(tmp, "<Object>");
|
||||
}
|
||||
@ -339,10 +434,13 @@ static KrkValue run() {
|
||||
#undef DEBUG
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, " | ");
|
||||
int i = 0;
|
||||
for (KrkValue * slot = vm.stack; slot < vm.stackTop; slot++) {
|
||||
fprintf(stderr, "[ ");
|
||||
if (i == frame->slots) fprintf(stderr, "*");
|
||||
krk_printValue(stderr, *slot);
|
||||
fprintf(stderr, " ]");
|
||||
i++;
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
krk_disassembleInstruction(&frame->closure->function->chunk,
|
||||
@ -357,12 +455,12 @@ static KrkValue run() {
|
||||
}
|
||||
case OP_RETURN: {
|
||||
KrkValue result = krk_pop();
|
||||
closeUpvalues(frame->slots);
|
||||
closeUpvalues(&vm.stack[frame->slots]);
|
||||
vm.frameCount--;
|
||||
if (vm.frameCount == 0) {
|
||||
return result;
|
||||
}
|
||||
vm.stackTop = frame->slots;
|
||||
vm.stackTop = &vm.stack[frame->slots];
|
||||
if (vm.frameCount == vm.exitOnFrame) {
|
||||
return result;
|
||||
}
|
||||
@ -455,13 +553,13 @@ static KrkValue run() {
|
||||
case OP_GET_LOCAL_LONG:
|
||||
case OP_GET_LOCAL: {
|
||||
uint32_t slot = readBytes(frame, (opcode == OP_GET_LOCAL ? 1 : 3));
|
||||
krk_push(frame->slots[slot]);
|
||||
krk_push(vm.stack[frame->slots + slot]);
|
||||
break;
|
||||
}
|
||||
case OP_SET_LOCAL_LONG:
|
||||
case OP_SET_LOCAL: {
|
||||
uint32_t slot = readBytes(frame, (opcode == OP_SET_LOCAL ? 1 : 3));
|
||||
frame->slots[slot] = krk_peek(0);
|
||||
vm.stack[frame->slots + slot] = krk_peek(0);
|
||||
break;
|
||||
}
|
||||
case OP_JUMP_IF_FALSE: {
|
||||
@ -500,7 +598,7 @@ static KrkValue run() {
|
||||
int isLocal = READ_BYTE();
|
||||
int index = READ_BYTE();
|
||||
if (isLocal) {
|
||||
closure->upvalues[i] = captureUpvalue(frame->slots + index);
|
||||
closure->upvalues[i] = captureUpvalue(&vm.stack[frame->slots + index]);
|
||||
} else {
|
||||
closure->upvalues[i] = frame->closure->upvalues[index];
|
||||
}
|
||||
@ -568,6 +666,26 @@ static KrkValue run() {
|
||||
defineMethod(READ_STRING((opcode == OP_METHOD ? 1 : 3)));
|
||||
break;
|
||||
}
|
||||
case OP_INHERIT: {
|
||||
KrkValue superclass = krk_peek(1);
|
||||
if (!IS_CLASS(superclass)) {
|
||||
runtimeError("Superclass must be a class.");
|
||||
return NONE_VAL();
|
||||
}
|
||||
KrkClass * subclass = AS_CLASS(krk_peek(0));
|
||||
krk_tableAddAll(&AS_CLASS(superclass)->methods, &subclass->methods);
|
||||
krk_pop();
|
||||
break;
|
||||
}
|
||||
case OP_GET_SUPER_LONG:
|
||||
case OP_GET_SUPER: {
|
||||
KrkString * name = READ_STRING((opcode == OP_GET_SUPER ? 1 : 3));
|
||||
KrkClass * superclass = AS_CLASS(krk_pop());
|
||||
if (!bindMethod(superclass, name)) {
|
||||
return NONE_VAL();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
11
vm.h
11
vm.h
@ -10,9 +10,16 @@
|
||||
typedef struct {
|
||||
KrkClosure * closure;
|
||||
uint8_t * ip;
|
||||
KrkValue * slots;
|
||||
size_t slots;
|
||||
} CallFrame;
|
||||
|
||||
typedef enum {
|
||||
METHOD_INIT,
|
||||
METHOD_STR,
|
||||
|
||||
METHOD__MAX,
|
||||
} KrkSpecialMethods;
|
||||
|
||||
typedef struct {
|
||||
CallFrame frames[FRAMES_MAX];
|
||||
size_t frameCount;
|
||||
@ -22,7 +29,6 @@ typedef struct {
|
||||
KrkTable globals;
|
||||
KrkTable strings;
|
||||
KrkTable modules;
|
||||
KrkString * __init__;
|
||||
KrkUpvalue * openUpvalues;
|
||||
KrkObj * objects;
|
||||
size_t bytesAllocated;
|
||||
@ -31,6 +37,7 @@ typedef struct {
|
||||
size_t grayCapacity;
|
||||
size_t exitOnFrame;
|
||||
KrkObj** grayStack;
|
||||
KrkValue specialMethodNames[METHOD__MAX];
|
||||
} KrkVM;
|
||||
|
||||
extern KrkVM vm;
|
||||
|
Loading…
x
Reference in New Issue
Block a user