classes and instances (from ch 27)

This commit is contained in:
K. Lange 2020-12-27 16:45:34 +09:00
parent 1d0fac5640
commit f88e8de25d
9 changed files with 167 additions and 10 deletions

View File

@ -50,6 +50,12 @@ typedef enum {
OP_SET_UPVALUE,
OP_SET_UPVALUE_LONG,
OP_CLOSE_UPVALUE,
OP_CLASS,
OP_CLASS_LONG,
OP_SET_PROPERTY,
OP_SET_PROPERTY_LONG,
OP_GET_PROPERTY,
OP_GET_PROPERTY_LONG,
} KrkOpCode;
/**

View File

@ -108,6 +108,8 @@ static void statement();
static void declaration();
static void or_(int canAssign);
static void and_(int canAssign);
static void classDeclaration();
static void declareVariable();
static void errorAt(KrkToken * token, const char * message) {
if (parser.panicMode) return;
@ -260,6 +262,17 @@ static void call(int canAssign) {
emitBytes(OP_CALL, argCount);
}
static void dot(int canAssign) {
consume(TOKEN_IDENTIFIER, "Expected propert name");
size_t ind = identifierConstant(&parser.previous);
if (canAssign && match(TOKEN_EQUAL)) {
expression();
EMIT_CONSTANT_OP(OP_SET_PROPERTY, ind);
} else {
EMIT_CONSTANT_OP(OP_GET_PROPERTY, ind);
}
}
static void literal(int canAssign) {
switch (parser.previous.type) {
case TOKEN_FALSE: emitByte(OP_FALSE); break;
@ -317,6 +330,8 @@ static void declaration() {
defDeclaration();
} else if (match(TOKEN_LET)) {
varDeclaration();
} else if (check(TOKEN_CLASS)) {
classDeclaration();
} else if (check(TOKEN_EOL)) {
return;
} else {
@ -403,6 +418,21 @@ static void function(FunctionType type, int blockWidth) {
}
}
static void classDeclaration() {
int blockWidth = (parser.previous.type == TOKEN_INDENTATION) ? parser.previous.length : 0;
advance(); /* Collect the `class` */
consume(TOKEN_IDENTIFIER, "Expected class name.");
size_t constInd = identifierConstant(&parser.previous);
declareVariable();
EMIT_CONSTANT_OP(OP_CLASS, constInd);
defineVariable(constInd);
consume(TOKEN_COLON, "Expected colon after class");
/* TODO block semantics */
}
static void markInitialized() {
if (current->scopeDepth == 0) return;
current->locals[current->localCount - 1].depth = current->scopeDepth;
@ -682,7 +712,7 @@ ParseRule rules[] = {
[TOKEN_RIGHT_SQUARE] = {NULL, NULL, PREC_NONE},
[TOKEN_COLON] = {NULL, NULL, PREC_NONE},
[TOKEN_COMMA] = {NULL, NULL, PREC_NONE},
[TOKEN_DOT] = {NULL, NULL, PREC_NONE},
[TOKEN_DOT] = {NULL, dot, PREC_CALL},
[TOKEN_MINUS] = {unary, binary, PREC_TERM},
[TOKEN_PLUS] = {NULL, binary, PREC_TERM},
[TOKEN_SEMICOLON] = {NULL, NULL, PREC_NONE},

View File

@ -75,6 +75,9 @@ size_t krk_disassembleInstruction(KrkChunk * chunk, size_t offset) {
CONSTANT(OP_CONSTANT,(void)0)
CONSTANT(OP_GET_GLOBAL,(void)0)
CONSTANT(OP_SET_GLOBAL,(void)0)
CONSTANT(OP_CLASS,(void)0)
CONSTANT(OP_GET_PROPERTY, (void)0)
CONSTANT(OP_SET_PROPERTY, (void)0)
CONSTANT(OP_CLOSURE, CLOSURE_MORE)
OPERANDL(OP_SET_LOCAL)
OPERANDL(OP_GET_LOCAL)

View File

@ -2,6 +2,7 @@
#include "memory.h"
#include "object.h"
#include "compiler.h"
#include "table.h"
void * krk_reallocate(void * ptr, size_t old, size_t new) {
vm.bytesAllocated += new - old;
@ -49,6 +50,15 @@ static void freeObject(KrkObj * object) {
FREE(KrkUpvalue, object);
break;
}
case OBJ_CLASS: {
FREE(KrkClass, object);
break;
}
case OBJ_INSTANCE: {
krk_freeTable(&((KrkInstance*)object)->fields);
FREE(KrkInstance, object);
break;
}
}
}
@ -105,6 +115,16 @@ static void blackenObject(KrkObj * object) {
case OBJ_UPVALUE:
krk_markValue(((KrkUpvalue*)object)->closed);
break;
case OBJ_CLASS: {
KrkClass * _class = (KrkClass *)object;
krk_markObject((KrkObj*)_class->name);
break;
}
case OBJ_INSTANCE: {
krk_markObject((KrkObj*)((KrkInstance*)object)->_class);
krk_markTable(&((KrkInstance*)object)->fields);
break;
}
case OBJ_NATIVE:
case OBJ_STRING:
break;

View File

@ -79,6 +79,12 @@ void krk_printObject(FILE * f, KrkValue value) {
case OBJ_UPVALUE:
fprintf(f, "<upvalue>");
break;
case OBJ_CLASS:
fprintf(f, "<class %s>", AS_CLASS(value)->name->chars);
break;
case OBJ_INSTANCE:
fprintf(f, "<instance of %s>", AS_INSTANCE(value)->_class->name->chars);
break;
}
}
@ -116,3 +122,16 @@ KrkUpvalue * newUpvalue(KrkValue * slot) {
upvalue->closed = NONE_VAL();
return upvalue;
}
KrkClass * newClass(KrkString * name) {
KrkClass * _class = ALLOCATE_OBJECT(KrkClass, OBJ_CLASS);
_class->name = name;
return _class;
}
KrkInstance * newInstance(KrkClass * _class) {
KrkInstance * instance = ALLOCATE_OBJECT(KrkInstance, OBJ_INSTANCE);
instance->_class = _class;
krk_initTable(&instance->fields);
return instance;
}

View File

@ -5,6 +5,7 @@
#include "kuroko.h"
#include "value.h"
#include "chunk.h"
#include "table.h"
#define OBJECT_TYPE(value) (AS_OBJECT(value)->type)
#define IS_STRING(value) isObjType(value, OBJ_STRING)
@ -16,6 +17,10 @@
#define AS_NATIVE(value) (((KrkNative *)AS_OBJECT(value))->function)
#define IS_CLOSURE(value) isObjType(value, OBJ_CLOSURE)
#define AS_CLOSURE(value) ((KrkClosure *)AS_OBJECT(value))
#define IS_CLASS(value) isObjType(value, OBJ_CLASS)
#define AS_CLASS(value) ((KrkClass *)AS_OBJECT(value))
#define IS_INSTANCE(value) isObjType(value, OBJ_INSTANCE)
#define AS_INSTANCE(value) ((KrkInstance *)AS_OBJECT(value))
typedef enum {
OBJ_FUNCTION,
@ -23,6 +28,8 @@ typedef enum {
OBJ_CLOSURE,
OBJ_STRING,
OBJ_UPVALUE,
OBJ_CLASS,
OBJ_INSTANCE,
} ObjType;
struct Obj {
@ -60,6 +67,16 @@ typedef struct {
size_t upvalueCount;
} KrkClosure;
typedef struct {
KrkObj obj;
KrkString * name;
} KrkClass;
typedef struct {
KrkObj obj;
KrkClass * _class;
KrkTable fields;
} KrkInstance;
typedef KrkValue (*NativeFn)(int argCount, KrkValue* args);
typedef struct {
@ -74,10 +91,9 @@ static inline int isObjType(KrkValue value, ObjType type) {
extern KrkString * takeString(char * chars, size_t length);
extern KrkString * copyString(const char * chars, size_t length);
extern void krk_printObject(FILE * f, KrkValue value);
extern KrkFunction * newFunction();
extern KrkNative * newNative(NativeFn function);
extern KrkClosure * newClosure(KrkFunction * function);
extern KrkUpvalue * newUpvalue(KrkValue * slot);
extern KrkClass * newClass(KrkString * name);
extern KrkInstance * newInstance(KrkClass * _class);

View File

@ -10,7 +10,6 @@
#include <stdlib.h>
#include "kuroko.h"
#include "value.h"
#include "object.h"
typedef struct {
KrkValue key;

View File

@ -59,12 +59,16 @@ print "The function call returned: " + result
# figured something with arguments would be more useful? The purpose of this
# language is to be used for writing syntax highlighters, configs, and also
# plugins for bim, so native bindings are going to be very important.
result = sleep(0.5)
result = sleep(0.1)
print "Call to sleep returned: " + result
function("something else")
# This is some stuff to test closures
class Funcs:
let funcs = Funcs()
if True:
let a = 1
def f():
@ -75,6 +79,13 @@ if True:
let c = 3
def h():
print c
funcs.f = f
funcs.g = g
funcs.h = h
funcs.f()
funcs.g()
funcs.h()
def outer():
let x = "outside"
@ -85,4 +96,16 @@ def outer():
print "Function is defined, creating it..."
let closure = outer()
print "And executing the result..."
# This should correctly print "outside"
closure()
# This is surprisingly similar to Python already...
print "Let's do some classes."
class Test: # This is a test class
print Test
let test = Test()
print test
test.foo = "bar"
print test.foo

49
vm.c
View File

@ -54,7 +54,8 @@ void krk_push(KrkValue value) {
KrkValue krk_pop() {
vm.stackTop--;
if (vm.stackTop < vm.stack) {
fprintf(stderr, "XXX: Stack overflow?");
fprintf(stderr, "stack overflow - too many pops\n");
return NONE_VAL();
}
return *vm.stackTop;
}
@ -115,6 +116,11 @@ static int callValue(KrkValue callee, int argCount) {
krk_push(result);
return 1;
}
case OBJ_CLASS: {
KrkClass * _class = AS_CLASS(callee);
vm.stackTop[-argCount - 1] = OBJECT_VAL(newInstance(_class));
return 1;
}
default:
break;
}
@ -323,10 +329,8 @@ static KrkValue run() {
closeUpvalues(frame->slots);
vm.frameCount--;
if (vm.frameCount == 0) {
krk_pop();
return INTEGER_VAL(0);
return result;
}
vm.stackTop = frame->slots;
krk_push(result);
frame = &vm.frames[vm.frameCount - 1];
@ -466,6 +470,43 @@ static KrkValue run() {
closeUpvalues(vm.stackTop - 1);
krk_pop();
break;
case OP_CLASS_LONG:
case OP_CLASS: {
KrkString * name = READ_STRING((opcode == OP_CLASS ? 1 : 3));
krk_push(OBJECT_VAL(newClass(name)));
break;
}
case OP_GET_PROPERTY_LONG:
case OP_GET_PROPERTY: {
if (!IS_INSTANCE(krk_peek(0))) {
runtimeError("Don't know how to retreive properties for %s yet", typeName(krk_peek(0)));
return NONE_VAL();
}
KrkInstance * instance = AS_INSTANCE(krk_peek(0));
KrkString * name = READ_STRING((opcode == OP_GET_PROPERTY ? 1 : 3));
KrkValue value;
if (krk_tableGet(&instance->fields, OBJECT_VAL(name), &value)) {
krk_pop();
krk_push(value);
break;
}
runtimeError("Undefined property '%s'.", name->chars);
return NONE_VAL();
}
case OP_SET_PROPERTY_LONG:
case OP_SET_PROPERTY: {
if (!IS_INSTANCE(krk_peek(1))) {
runtimeError("Don't know how to set properties for %s yet", typeName(krk_peek(1)));
return NONE_VAL();
}
KrkInstance * instance = AS_INSTANCE(krk_peek(1));
KrkString * name = READ_STRING((opcode == OP_SET_PROPERTY ? 1 : 3));
krk_tableSet(&instance->fields, OBJECT_VAL(name), krk_peek(0));
KrkValue value = krk_pop();
krk_pop(); /* instance */
krk_push(value); /* Moves value in */
break;
}
}
}