classes and instances (from ch 27)
This commit is contained in:
parent
1d0fac5640
commit
f88e8de25d
6
chunk.h
6
chunk.h
@ -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;
|
||||
|
||||
/**
|
||||
|
32
compiler.c
32
compiler.c
@ -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},
|
||||
|
3
debug.c
3
debug.c
@ -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)
|
||||
|
20
memory.c
20
memory.c
@ -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;
|
||||
|
19
object.c
19
object.c
@ -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;
|
||||
}
|
||||
|
22
object.h
22
object.h
@ -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);
|
||||
|
1
table.h
1
table.h
@ -10,7 +10,6 @@
|
||||
#include <stdlib.h>
|
||||
#include "kuroko.h"
|
||||
#include "value.h"
|
||||
#include "object.h"
|
||||
|
||||
typedef struct {
|
||||
KrkValue key;
|
||||
|
25
test.krk
25
test.krk
@ -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
49
vm.c
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user