that's a lot of stuff and pretty much finishes the book; need to do optimizations

This commit is contained in:
K. Lange 2020-12-27 22:40:35 +09:00
parent 1f754ebc01
commit 4982881235
9 changed files with 298 additions and 32 deletions

View File

@ -60,6 +60,9 @@ typedef enum {
OP_METHOD_LONG,
OP_IMPORT,
OP_IMPORT_LONG,
OP_INHERIT,
OP_GET_SUPER,
OP_GET_SUPER_LONG,
} KrkOpCode;
/**

View File

@ -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];

View File

@ -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)

View File

@ -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();

View File

@ -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) {

View File

@ -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

View File

@ -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
View File

@ -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
View File

@ -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;