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,
OP_SET_UPVALUE_LONG, OP_SET_UPVALUE_LONG,
OP_CLOSE_UPVALUE, OP_CLOSE_UPVALUE,
OP_CLASS,
OP_CLASS_LONG,
OP_SET_PROPERTY,
OP_SET_PROPERTY_LONG,
OP_GET_PROPERTY,
OP_GET_PROPERTY_LONG,
} KrkOpCode; } KrkOpCode;
/** /**

View File

@ -108,6 +108,8 @@ static void statement();
static void declaration(); static void declaration();
static void or_(int canAssign); static void or_(int canAssign);
static void and_(int canAssign); static void and_(int canAssign);
static void classDeclaration();
static void declareVariable();
static void errorAt(KrkToken * token, const char * message) { static void errorAt(KrkToken * token, const char * message) {
if (parser.panicMode) return; if (parser.panicMode) return;
@ -260,6 +262,17 @@ static void call(int canAssign) {
emitBytes(OP_CALL, argCount); 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) { static void literal(int canAssign) {
switch (parser.previous.type) { switch (parser.previous.type) {
case TOKEN_FALSE: emitByte(OP_FALSE); break; case TOKEN_FALSE: emitByte(OP_FALSE); break;
@ -317,6 +330,8 @@ static void declaration() {
defDeclaration(); defDeclaration();
} else if (match(TOKEN_LET)) { } else if (match(TOKEN_LET)) {
varDeclaration(); varDeclaration();
} else if (check(TOKEN_CLASS)) {
classDeclaration();
} else if (check(TOKEN_EOL)) { } else if (check(TOKEN_EOL)) {
return; return;
} else { } 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() { static void markInitialized() {
if (current->scopeDepth == 0) return; if (current->scopeDepth == 0) return;
current->locals[current->localCount - 1].depth = current->scopeDepth; current->locals[current->localCount - 1].depth = current->scopeDepth;
@ -682,7 +712,7 @@ ParseRule rules[] = {
[TOKEN_RIGHT_SQUARE] = {NULL, NULL, PREC_NONE}, [TOKEN_RIGHT_SQUARE] = {NULL, NULL, PREC_NONE},
[TOKEN_COLON] = {NULL, NULL, PREC_NONE}, [TOKEN_COLON] = {NULL, NULL, PREC_NONE},
[TOKEN_COMMA] = {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_MINUS] = {unary, binary, PREC_TERM},
[TOKEN_PLUS] = {NULL, binary, PREC_TERM}, [TOKEN_PLUS] = {NULL, binary, PREC_TERM},
[TOKEN_SEMICOLON] = {NULL, NULL, PREC_NONE}, [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_CONSTANT,(void)0)
CONSTANT(OP_GET_GLOBAL,(void)0) CONSTANT(OP_GET_GLOBAL,(void)0)
CONSTANT(OP_SET_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) CONSTANT(OP_CLOSURE, CLOSURE_MORE)
OPERANDL(OP_SET_LOCAL) OPERANDL(OP_SET_LOCAL)
OPERANDL(OP_GET_LOCAL) OPERANDL(OP_GET_LOCAL)

View File

@ -2,6 +2,7 @@
#include "memory.h" #include "memory.h"
#include "object.h" #include "object.h"
#include "compiler.h" #include "compiler.h"
#include "table.h"
void * krk_reallocate(void * ptr, size_t old, size_t new) { void * krk_reallocate(void * ptr, size_t old, size_t new) {
vm.bytesAllocated += new - old; vm.bytesAllocated += new - old;
@ -49,6 +50,15 @@ static void freeObject(KrkObj * object) {
FREE(KrkUpvalue, object); FREE(KrkUpvalue, object);
break; 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: case OBJ_UPVALUE:
krk_markValue(((KrkUpvalue*)object)->closed); krk_markValue(((KrkUpvalue*)object)->closed);
break; 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_NATIVE:
case OBJ_STRING: case OBJ_STRING:
break; break;

View File

@ -79,6 +79,12 @@ void krk_printObject(FILE * f, KrkValue value) {
case OBJ_UPVALUE: case OBJ_UPVALUE:
fprintf(f, "<upvalue>"); fprintf(f, "<upvalue>");
break; 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(); upvalue->closed = NONE_VAL();
return upvalue; 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 "kuroko.h"
#include "value.h" #include "value.h"
#include "chunk.h" #include "chunk.h"
#include "table.h"
#define OBJECT_TYPE(value) (AS_OBJECT(value)->type) #define OBJECT_TYPE(value) (AS_OBJECT(value)->type)
#define IS_STRING(value) isObjType(value, OBJ_STRING) #define IS_STRING(value) isObjType(value, OBJ_STRING)
@ -16,6 +17,10 @@
#define AS_NATIVE(value) (((KrkNative *)AS_OBJECT(value))->function) #define AS_NATIVE(value) (((KrkNative *)AS_OBJECT(value))->function)
#define IS_CLOSURE(value) isObjType(value, OBJ_CLOSURE) #define IS_CLOSURE(value) isObjType(value, OBJ_CLOSURE)
#define AS_CLOSURE(value) ((KrkClosure *)AS_OBJECT(value)) #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 { typedef enum {
OBJ_FUNCTION, OBJ_FUNCTION,
@ -23,6 +28,8 @@ typedef enum {
OBJ_CLOSURE, OBJ_CLOSURE,
OBJ_STRING, OBJ_STRING,
OBJ_UPVALUE, OBJ_UPVALUE,
OBJ_CLASS,
OBJ_INSTANCE,
} ObjType; } ObjType;
struct Obj { struct Obj {
@ -60,6 +67,16 @@ typedef struct {
size_t upvalueCount; size_t upvalueCount;
} KrkClosure; } 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 KrkValue (*NativeFn)(int argCount, KrkValue* args);
typedef struct { typedef struct {
@ -74,10 +91,9 @@ static inline int isObjType(KrkValue value, ObjType type) {
extern KrkString * takeString(char * chars, size_t length); extern KrkString * takeString(char * chars, size_t length);
extern KrkString * copyString(const char * chars, size_t length); extern KrkString * copyString(const char * chars, size_t length);
extern void krk_printObject(FILE * f, KrkValue value); extern void krk_printObject(FILE * f, KrkValue value);
extern KrkFunction * newFunction(); extern KrkFunction * newFunction();
extern KrkNative * newNative(NativeFn function); extern KrkNative * newNative(NativeFn function);
extern KrkClosure * newClosure(KrkFunction * function); extern KrkClosure * newClosure(KrkFunction * function);
extern KrkUpvalue * newUpvalue(KrkValue * slot); 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 <stdlib.h>
#include "kuroko.h" #include "kuroko.h"
#include "value.h" #include "value.h"
#include "object.h"
typedef struct { typedef struct {
KrkValue key; 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 # figured something with arguments would be more useful? The purpose of this
# language is to be used for writing syntax highlighters, configs, and also # language is to be used for writing syntax highlighters, configs, and also
# plugins for bim, so native bindings are going to be very important. # 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 print "Call to sleep returned: " + result
function("something else") function("something else")
# This is some stuff to test closures
class Funcs:
let funcs = Funcs()
if True: if True:
let a = 1 let a = 1
def f(): def f():
@ -75,6 +79,13 @@ if True:
let c = 3 let c = 3
def h(): def h():
print c print c
funcs.f = f
funcs.g = g
funcs.h = h
funcs.f()
funcs.g()
funcs.h()
def outer(): def outer():
let x = "outside" let x = "outside"
@ -85,4 +96,16 @@ def outer():
print "Function is defined, creating it..." print "Function is defined, creating it..."
let closure = outer() let closure = outer()
print "And executing the result..." print "And executing the result..."
# This should correctly print "outside"
closure() 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() { KrkValue krk_pop() {
vm.stackTop--; vm.stackTop--;
if (vm.stackTop < vm.stack) { if (vm.stackTop < vm.stack) {
fprintf(stderr, "XXX: Stack overflow?"); fprintf(stderr, "stack overflow - too many pops\n");
return NONE_VAL();
} }
return *vm.stackTop; return *vm.stackTop;
} }
@ -115,6 +116,11 @@ static int callValue(KrkValue callee, int argCount) {
krk_push(result); krk_push(result);
return 1; return 1;
} }
case OBJ_CLASS: {
KrkClass * _class = AS_CLASS(callee);
vm.stackTop[-argCount - 1] = OBJECT_VAL(newInstance(_class));
return 1;
}
default: default:
break; break;
} }
@ -323,10 +329,8 @@ static KrkValue run() {
closeUpvalues(frame->slots); closeUpvalues(frame->slots);
vm.frameCount--; vm.frameCount--;
if (vm.frameCount == 0) { if (vm.frameCount == 0) {
krk_pop(); return result;
return INTEGER_VAL(0);
} }
vm.stackTop = frame->slots; vm.stackTop = frame->slots;
krk_push(result); krk_push(result);
frame = &vm.frames[vm.frameCount - 1]; frame = &vm.frames[vm.frameCount - 1];
@ -466,6 +470,43 @@ static KrkValue run() {
closeUpvalues(vm.stackTop - 1); closeUpvalues(vm.stackTop - 1);
krk_pop(); krk_pop();
break; 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;
}
} }
} }