Synthesized properties and methods for strings

This commit is contained in:
K. Lange 2020-12-28 09:08:35 +09:00
parent c55c806e40
commit 3bcc5d5530
6 changed files with 125 additions and 21 deletions

12
bindNativeMethod.krk Normal file
View File

@ -0,0 +1,12 @@
# Strings have a syntehesized __get__
# which returns an integer value of the byte...
# This will probably codepoint of a UTF-32 string later?
print "Test"[1]
class Test:
def __init__(self):
self.foo = "bar"
# Instances have a __class__ property synthesized by the VM
let test = Test()
print test.__class__

View File

@ -8,6 +8,7 @@
#include "scanner.h"
#include "object.h"
#include "debug.h"
#include "vm.h"
typedef struct {
KrkToken current;
@ -217,9 +218,8 @@ static void emitReturn() {
static KrkFunction * endCompiler() {
emitReturn();
KrkFunction * function = current->function;
#undef DEBUG
#ifdef DEBUG
if (!parser.hadError) {
#ifdef ENABLE_DEBUGGING
if (vm.enableDebugging && !parser.hadError) {
krk_disassembleChunk(currentChunk(), function->name != NULL ? function->name->chars : "<module>");
}
#endif

View File

@ -1,4 +1,5 @@
#include <stdio.h>
#include <unistd.h>
#include "kuroko.h"
#include "chunk.h"
@ -6,11 +7,31 @@
#include "vm.h"
int main(int argc, char * argv[]) {
if (argc < 2) return 1;
krk_initVM();
KrkValue result = krk_runfile(argv[1],0);
int opt;
while ((opt = getopt(argc, argv, "td")) != -1) {
switch (opt) {
case 't':
vm.enableTracing = 1;
break;
case 'd':
vm.enableDebugging = 1;
break;
}
}
if (optind == argc) {
fprintf(stderr, "no repl available; try running a script\n");
return 1;
}
KrkValue result;
for (int i = optind; i < argc; ++i) {
KrkValue out = krk_runfile(argv[i],0);
if (i + 1 == argc) result = out;
}
krk_freeVM();

View File

@ -3,3 +3,6 @@
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#define ENABLE_DEBUGGING
#define ENABLE_TRACING

95
vm.c
View File

@ -197,7 +197,7 @@ static int callValue(KrkValue callee, int argCount) {
break;
}
}
runtimeError("Attempted to call non-callable type.");
runtimeError("Attempted to call non-callable type: %s", typeName(callee));
return 0;
}
@ -247,6 +247,9 @@ static void defineMethod(KrkString * name) {
}
void krk_initVM() {
vm.enableDebugging = 0;
vm.enableTracing = 0;
resetStack();
vm.objects = NULL;
vm.bytesAllocated = 0;
@ -427,24 +430,72 @@ static size_t readBytes(CallFrame * frame, int num) {
return (size_t)-1;
}
static KrkClosure * boundNative(NativeFn method, int arity) {
/* Build an object */
KrkValue nativeFunction = OBJECT_VAL(newNative(method));
/* Build a function that calls it */
KrkFunction * methodWrapper = newFunction();
methodWrapper->arity = arity; /* This is WITHOUT the self reference */
krk_writeConstant(&methodWrapper->chunk, nativeFunction, 1);
/* Stack silliness */
krk_writeChunk(&methodWrapper->chunk, OP_GET_LOCAL, 1); /* Should be bind receiver */
krk_writeChunk(&methodWrapper->chunk, 0, 1);
for (int i = 0; i < arity; ++i) {
krk_writeChunk(&methodWrapper->chunk, OP_GET_LOCAL, 1); /* Should be arguments */
krk_writeChunk(&methodWrapper->chunk, i + 1, 1);
}
/* Call with these arguments */
krk_writeChunk(&methodWrapper->chunk, OP_CALL, 1);
krk_writeChunk(&methodWrapper->chunk, arity + 1, 1); /* arguments to call with */
/* Return from the wrapper with whatever result we got from the native method */
krk_writeChunk(&methodWrapper->chunk, OP_RETURN, 1);
return newClosure(methodWrapper);
}
static KrkValue _string_get(int argc, KrkValue argv[]) {
if (argc != 2) {
runtimeError("Wrong number of arguments to String.__get__");
return NONE_VAL();
}
if (!IS_STRING(argv[0])) {
runtimeError("First argument to __get__ must be String");
return NONE_VAL();
}
if (!IS_INTEGER(argv[1])) {
runtimeError("Strings can not index by %s", typeName(argv[1]));
return NONE_VAL();
}
int asInt = AS_INTEGER(argv[1]);
if (asInt < 0 || asInt >= AS_STRING(argv[0])->length) {
runtimeError("String index out of range: %d", asInt);
return NONE_VAL();
}
return INTEGER_VAL(AS_CSTRING(argv[0])[asInt]);
}
static KrkValue run() {
CallFrame* frame = &vm.frames[vm.frameCount - 1];
for (;;) {
#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++;
#ifdef ENABLE_DEBUGGING
if (vm.enableTracing) {
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,
(size_t)(frame->ip - frame->closure->function->chunk.code));
}
fprintf(stderr, "\n");
krk_disassembleInstruction(&frame->closure->function->chunk,
(size_t)(frame->ip - frame->closure->function->chunk.code));
#endif
uint8_t opcode;
switch ((opcode = READ_BYTE())) {
@ -640,15 +691,29 @@ static KrkValue run() {
krk_pop();
krk_push(value);
} else if (!bindMethod(instance->_class, name)) {
goto _undefined;
/* Try synthentic properties */
if (!strcmp(name->chars,"__class__")) {
krk_pop();
krk_push(OBJECT_VAL(instance->_class));
} else {
goto _undefined;
}
}
break;
}
case OBJ_STRING: {
KrkString * string = AS_STRING(krk_peek(0));
/* vm.specialMethodNames[NAME_LEN] ? */
if (!strcmp(name->chars,"length")) {
krk_pop(); /* The string */
krk_push(INTEGER_VAL(string->length));
} else if (!strcmp(name->chars,"__get__")) {
KrkBoundMethod * bound = newBoundMethod(krk_peek(0), boundNative(_string_get,1));
krk_pop(); /* The string */
krk_push(OBJECT_VAL(bound));
} else if (!strcmp(name->chars,"__set__")) {
runtimeError("Strings are not mutable.");
return NONE_VAL();
} else {
goto _undefined;
}

3
vm.h
View File

@ -38,6 +38,9 @@ typedef struct {
size_t exitOnFrame;
KrkObj** grayStack;
KrkValue specialMethodNames[METHOD__MAX];
char enableDebugging;
char enableTracing;
} KrkVM;
extern KrkVM vm;