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,
|
||||||
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
32
compiler.c
32
compiler.c
@ -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},
|
||||||
|
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_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)
|
||||||
|
20
memory.c
20
memory.c
@ -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;
|
||||||
|
19
object.c
19
object.c
@ -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;
|
||||||
|
}
|
||||||
|
22
object.h
22
object.h
@ -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);
|
||||||
|
1
table.h
1
table.h
@ -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;
|
||||||
|
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
|
# 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
49
vm.c
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user