diff --git a/Makefile b/Makefile index 05ee0f4..724a5e0 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ -# ## Copyright (c) 2020-2021 Thakee Nathees -# ## Distributed Under The MIT License +## Copyright (c) 2020-2021 Thakee Nathees +## Distributed Under The MIT License CC = gcc -CFLAGS = -fPIC -Wno-int-to-pointer-cast +CFLAGS = -fPIC DEBUG_CFLAGS = -D DEBUG -g3 -Og RELEASE_CFLAGS = -g -O3 LDFLAGS = -lm diff --git a/README.md b/README.md index a72cc74..fa16c6d 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ except for a c99 compatible compiler. It can be compiled with the following comm #### GCC / MinGw / Clang (alias with gcc) ``` -gcc -o pocket cli/*.c src/*.c -Isrc/include -lm -Wno-int-to-pointer-cast +gcc -o pocket cli/*.c src/*.c -Isrc/include -lm ``` #### MSVC diff --git a/cli/main.c b/cli/main.c index 0116187..01f497d 100644 --- a/cli/main.c +++ b/cli/main.c @@ -130,7 +130,7 @@ PkStringPtr loadScript(PKVM* vm, const char* path) { int main(int argc, char** argv) { - const char* usage = "usage: pocket [-c cmd | file]\n"; + //const char* usage = "usage: pocket [-c cmd | file]\n"; // TODO: implement arg parse, REPL. diff --git a/cli/modules/path.c b/cli/modules/path.c index 002787f..aa69e30 100644 --- a/cli/modules/path.c +++ b/cli/modules/path.c @@ -126,10 +126,10 @@ static void _pathRelpath(PKVM* vm) { if (!pkGetArgString(vm, 2, &path)) return; char abs_from[FILENAME_MAX]; - size_t len_from = pathAbs(from, abs_from, sizeof(abs_from)); + pathAbs(from, abs_from, sizeof(abs_from)); char abs_path[FILENAME_MAX]; - size_t len_path = pathAbs(path, abs_path, sizeof(abs_path)); + pathAbs(path, abs_path, sizeof(abs_path)); char result[FILENAME_MAX]; size_t len = cwk_path_get_relative(abs_from, abs_path, diff --git a/cli/repl.c b/cli/repl.c index 751725e..61a06a4 100644 --- a/cli/repl.c +++ b/cli/repl.c @@ -33,7 +33,7 @@ const char* read_line(uint32_t* length) { // Read a line from stdin and returns it without the line ending. static void readLine(ByteBuffer* buff) { do { - char c = fgetc(stdin); + char c = (char)fgetc(stdin); if (c == EOF || c == '\n') break; byteBufferWrite(buff, (uint8_t)c); diff --git a/docs/TODO.txt b/docs/TODO.txt index d4a4cd2..f6ece8f 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -6,6 +6,8 @@ // To implement. +- no __file__ for core modules. + - def f(x) f(x) ## not tco, unless return f(x) end diff --git a/src/include/pocketlang.h b/src/include/pocketlang.h index b558428..24fdfd2 100644 --- a/src/include/pocketlang.h +++ b/src/include/pocketlang.h @@ -89,6 +89,8 @@ typedef enum { PK_SCRIPT, PK_FUNCTION, PK_FIBER, + PK_CLASS, + PK_INST, } PkVarType; typedef struct PkStringPtr PkStringPtr; @@ -387,7 +389,6 @@ PK_PUBLIC PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn); //PK_PUBLIC PkVar pkPushBool(PKVM* vm, bool value); //PK_PUBLIC PkVar pkPushNumber(PKVM* vm, double value); - #ifdef __cplusplus } // extern "C" #endif diff --git a/src/pk_common.h b/src/pk_common.h index a144cfa..3bc3a17 100644 --- a/src/pk_common.h +++ b/src/pk_common.h @@ -77,6 +77,14 @@ /* COMMON MACROS */ /*****************************************************************************/ +#if defined(__GNUC__) + #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" + #pragma GCC diagnostic ignored "-Wunused-parameter" +#elif defined(__clang__) + #pragma clang diagnostic ignored "-Wint-to-pointer-cast" + #pragma clang diagnostic ignored "-Wunused-parameter" +#endif + #include //< Only needed here for ASSERT() macro and for release mode //< TODO; macro use this to print a crash report. diff --git a/src/pk_compiler.c b/src/pk_compiler.c index 3614eda..f197da3 100644 --- a/src/pk_compiler.c +++ b/src/pk_compiler.c @@ -17,9 +17,13 @@ #define MAX_VARIABLES 256 // The maximum number of functions a script could contain. Also it's limited by -// it's opcode which is using a single byte value to identify the local. +// it's opcode which is using a single byte value to identify. #define MAX_FUNCTIONS 256 +// The maximum number of classes a script could contain. Also it's limited by +// it's opcode which is using a single byte value to identify. +#define MAX_CLASSES 255 + // The maximum number of names that were used before defined. Its just the size // of the Forward buffer of the compiler. Feel free to increase it if it // require more. @@ -107,6 +111,7 @@ typedef enum { // Keywords. TK_MODULE, // module + TK_CLASS, // class TK_FROM, // from TK_IMPORT, // import TK_AS, // as @@ -170,6 +175,7 @@ typedef struct { // List of keywords mapped into their identifiers. static _Keyword _keywords[] = { { "module", 6, TK_MODULE }, + { "class", 5, TK_CLASS }, { "from", 4, TK_FROM }, { "import", 6, TK_IMPORT }, { "as", 2, TK_AS }, @@ -973,6 +979,7 @@ typedef enum { NAME_LOCAL_VAR, //< Including parameter. NAME_GLOBAL_VAR, NAME_FUNCTION, + NAME_CLASS, NAME_BUILTIN, //< Native builtin function. } NameDefnType; @@ -1025,6 +1032,14 @@ static NameSearchResult compilerSearchName(Compiler* compiler, return result; } + // Search through classes. + index = scriptGetClass(compiler->script, name, length); + if (index != -1) { + result.type = NAME_CLASS; + result.index = index; + return result; + } + // Search through functions. index = scriptGetFunc(compiler->script, name, length); if (index != -1) { @@ -1140,6 +1155,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence /* TK_SRIGHT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT }, /* TK_SLEFT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT }, /* TK_MODULE */ NO_RULE, + /* TK_CLASS */ NO_RULE, /* TK_FROM */ NO_RULE, /* TK_IMPORT */ NO_RULE, /* TK_AS */ NO_RULE, @@ -1293,6 +1309,11 @@ static void exprName(Compiler* compiler) { emitByte(compiler, result.index); break; + case NAME_CLASS: + emitOpcode(compiler, OP_PUSH_TYPE); + emitByte(compiler, result.index); + break; + case NAME_BUILTIN: emitOpcode(compiler, OP_PUSH_BUILTIN_FN); emitByte(compiler, result.index); @@ -1776,6 +1797,19 @@ static void compilerExitBlock(Compiler* compiler) { compiler->scope_depth--; } +static void compilerPushFunc(Compiler* compiler, Func* fn, + Function* func, int index) { + fn->outer_func = compiler->func; + fn->ptr = func; + fn->depth = compiler->scope_depth; + fn->index = index; + compiler->func = fn; +} + +static void compilerPopFunc(Compiler* compiler) { + compiler->func = compiler->func->outer_func; +} + /*****************************************************************************/ /* COMPILING (EMIT BYTECODE) */ /*****************************************************************************/ @@ -1864,6 +1898,94 @@ typedef enum { static void compileStatement(Compiler* compiler); static void compileBlockBody(Compiler* compiler, BlockType type); +// Compile a type and return it's index in the script's types buffer. +static int compileType(Compiler* compiler) { + + // Consume the name of the type. + consume(compiler, TK_NAME, "Expected a type name."); + const char* name = compiler->previous.start; + int name_len = compiler->previous.length; + NameSearchResult result = compilerSearchName(compiler, name, name_len); + if (result.type != NAME_NOT_DEFINED) { + parseError(compiler, "Name '%.*s' already exists.", name_len, name); + } + + // Create a new type. + Class* type = newClass(compiler->vm, compiler->script, + name, (uint32_t)name_len); + type->ctor->arity = 0; + + // Check count exceeded. + int fn_index = (int)compiler->script->functions.count - 1; + if (fn_index == MAX_FUNCTIONS) { + parseError(compiler, "A script should contain at most %d functions.", + MAX_FUNCTIONS); + } + + int ty_index = (int)(compiler->script->classes.count - 1); + if (ty_index == MAX_CLASSES) { + parseError(compiler, "A script should contain at most %d types.", + MAX_CLASSES); + } + + // Compile the constructor function. + ASSERT(compiler->func->ptr == compiler->script->body, OOPS); + Func curr_fn; + compilerPushFunc(compiler, &curr_fn, type->ctor, fn_index); + compilerEnterBlock(compiler); + + // Push an instance on the stack. + emitOpcode(compiler, OP_PUSH_INSTANCE); + emitByte(compiler, ty_index); + + skipNewLines(compiler); + TokenType next = peek(compiler); + while (next != TK_END && next != TK_EOF) { + + // Compile field name. + consume(compiler, TK_NAME, "Expected a type name."); + const char* f_name = compiler->previous.start; + int f_len = compiler->previous.length; + + uint32_t f_index = scriptAddName(compiler->script, compiler->vm, + f_name, f_len); + + // TODO: Add a string compare macro. + String* new_name = compiler->script->names.data[f_index]; + for (uint32_t i = 0; i < type->field_names.count; i++) { + String* prev = compiler->script->names.data[type->field_names.data[i]]; + if (new_name->hash == prev->hash && new_name->length == prev->length && + memcmp(new_name->data, prev->data, prev->length) == 0) { + parseError(compiler, "Class field with name '%s' already exists.", + new_name->data); + } + } + + pkUintBufferWrite(&type->field_names, compiler->vm, f_index); + + // Consume the assignment expression. + consume(compiler, TK_EQ, "Expected an assignment after field name."); + compileExpression(compiler); // Assigned value. + consumeEndStatement(compiler); + + // At this point the stack top would be the expression. + emitOpcode(compiler, OP_INST_APPEND); + + skipNewLines(compiler); + next = peek(compiler); + } + consume(compiler, TK_END, "Expected 'end' after type declaration end."); + + compilerExitBlock(compiler); + + // At this point, the stack top would be the constructed instance. Return it. + emitFunctionEnd(compiler); + + compilerPopFunc(compiler); + + return -1; // TODO; +} + // Compile a function and return it's index in the script's function buffer. static int compileFunction(Compiler* compiler, FuncType fn_type) { @@ -1892,13 +2014,8 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) { MAX_FUNCTIONS); } - Func curr_func; - curr_func.outer_func = compiler->func; - curr_func.ptr = func; - curr_func.depth = compiler->scope_depth; - curr_func.index = fn_index; - - compiler->func = &curr_func; + Func curr_fn; + compilerPushFunc(compiler, &curr_fn, func, fn_index); int argc = 0; compilerEnterBlock(compiler); // Parameter depth. @@ -1968,7 +2085,8 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) { printf("%s", buff.data); pkByteBufferClear(&buff, compiler->vm); #endif - compiler->func = compiler->func->outer_func; + + compilerPopFunc(compiler); return fn_index; } @@ -2156,6 +2274,7 @@ static int compilerImportName(Compiler* compiler, int line, return result.index; case NAME_FUNCTION: + case NAME_CLASS: case NAME_BUILTIN: parseError(compiler, "Name '%.*s' already exists.", length, name); return -1; @@ -2196,8 +2315,12 @@ static void compilerImportAll(Compiler* compiler, Script* script) { ASSERT(script != NULL, OOPS); ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS); - bool done = false; //< A flag to jump out of the loop. - pkUintBuffer* name_buff = NULL; //< The string buffer to iterate through. + // Import all types. + for (uint32_t i = 0; i < script->classes.count; i++) { + uint32_t name_ind = script->classes.data[i]->name; + String* name = script->names.data[name_ind]; + compilerImportSingleEntry(compiler, name->data, name->length); + } // Import all functions. for (uint32_t i = 0; i < script->functions.count; i++) { @@ -2581,7 +2704,10 @@ static void compileTopLevelStatement(Compiler* compiler) { // If the statement is call, this will be set to true. compiler->is_last_call = false; - if (match(compiler, TK_NATIVE)) { + if (match(compiler, TK_CLASS)) { + compileType(compiler); + + } else if (match(compiler, TK_NATIVE)) { compileFunction(compiler, FN_NATIVE); } else if (match(compiler, TK_DEF)) { @@ -2624,10 +2750,11 @@ PkResult compile(PKVM* vm, Script* script, const char* source, ASSERT(script->body != NULL, OOPS); pkByteBufferClear(&script->body->fn->opcodes, vm); - // Remember the count of the globals and functions, If the compilation failed - // discard all the globals and functions added by the compilation. + // Remember the count of the globals, functions and types, If the compilation + // failed discard all the globals and functions added by the compilation. uint32_t globals_count = script->globals.count; uint32_t functions_count = script->functions.count; + uint32_t types_count = script->classes.count; Func curr_fn; curr_fn.depth = DEPTH_SCRIPT; @@ -2688,6 +2815,7 @@ PkResult compile(PKVM* vm, Script* script, const char* source, if (compiler->has_errors) { script->globals.count = script->global_names.count = globals_count; script->functions.count = functions_count; + script->classes.count = types_count; } #if DEBUG_DUMP_COMPILED_CODE diff --git a/src/pk_core.c b/src/pk_core.c index 6919fb2..0673091 100644 --- a/src/pk_core.c +++ b/src/pk_core.c @@ -815,12 +815,14 @@ DEF(stdLangDisas, RET(VAR_OBJ(dump)); } +#ifdef DEBUG DEF(stdLangDebugBreak, "debug_break() -> null\n" "A debug function for development (will be removed).") { DEBUG_BREAK(); } +#endif DEF(stdLangWrite, "write(...) -> null\n" @@ -1080,7 +1082,8 @@ Var varAdd(PKVM* vm, Var v1, Var v2) { case OBJ_SCRIPT: case OBJ_FUNC: case OBJ_FIBER: - case OBJ_USER: + case OBJ_CLASS: + case OBJ_INST: break; } } @@ -1339,6 +1342,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { // map = { "foo" : 42, "can't access" : 32 } // val = map.foo ## 42 TODO; + UNREACHABLE(); } case OBJ_RANGE: @@ -1371,8 +1375,15 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { { Script* scr = (Script*)obj; + // Search in types. + int index = scriptGetClass(scr, attrib->data, attrib->length); + if (index != -1) { + ASSERT_INDEX((uint32_t)index, scr->classes.count); + return VAR_OBJ(scr->classes.data[index]); + } + // Search in functions. - int index = scriptGetFunc(scr, attrib->data, attrib->length); + index = scriptGetFunc(scr, attrib->data, attrib->length); if (index != -1) { ASSERT_INDEX((uint32_t)index, scr->functions.count); return VAR_OBJ(scr->functions.data[index]); @@ -1408,8 +1419,38 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { } case OBJ_FIBER: - case OBJ_USER: TODO; + UNREACHABLE(); + + case OBJ_CLASS: + TODO; + UNREACHABLE(); + + case OBJ_INST: + { + Instance* inst = (Instance*)obj; + if (inst->is_native) { + TODO; + + } else { + + // TODO: Optimize this with binary search. + Class* ty = inst->ins->type; + for (uint32_t i = 0; i < ty->field_names.count; i++) { + ASSERT_INDEX(i, ty->field_names.count); + ASSERT_INDEX(ty->field_names.data[i], ty->owner->names.count); + String* f_name = ty->owner->names.data[ty->field_names.data[i]]; + if (f_name->hash == attrib->hash && + f_name->length == attrib->length && + memcmp(f_name->data, attrib->data, attrib->length) == 0) { + return inst->ins->fields.data[i]; + } + } + } + + ERR_NO_ATTRIB(vm, on, attrib); + return VAR_NULL; + } default: UNREACHABLE(); @@ -1485,6 +1526,15 @@ do { \ return; } + index = scriptGetClass(scr, attrib->data, attrib->length); + if (index != -1) { + ASSERT_INDEX((uint32_t)index, scr->classes.count); + ASSERT_INDEX(scr->classes.data[index]->name, scr->names.count); + String* name = scr->names.data[scr->classes.data[index]->name]; + ATTRIB_IMMUTABLE(name->data); + return; + } + ERR_NO_ATTRIB(vm, on, attrib); return; } @@ -1499,10 +1549,39 @@ do { \ ERR_NO_ATTRIB(vm, on, attrib); return; - case OBJ_USER: - TODO; //ERR_NO_ATTRIB(vm, on, attrib); + case OBJ_CLASS: + ERR_NO_ATTRIB(vm, on, attrib); return; + case OBJ_INST: + { + Instance* inst = (Instance*)obj; + if (inst->is_native) { + TODO; + return; + + } else { + + // TODO: Optimize this with binary search. + Class* ty = inst->ins->type; + for (uint32_t i = 0; i < ty->field_names.count; i++) { + ASSERT_INDEX(i, ty->field_names.count); + ASSERT_INDEX(ty->field_names.data[i], ty->owner->names.count); + String* f_name = ty->owner->names.data[ty->field_names.data[i]]; + if (f_name->hash == attrib->hash && + f_name->length == attrib->length && + memcmp(f_name->data, attrib->data, attrib->length) == 0) { + inst->ins->fields.data[i] = value; + return; + } + } + ERR_NO_ATTRIB(vm, on, attrib); + return; + } + + UNREACHABLE(); + } + default: UNREACHABLE(); } @@ -1574,8 +1653,11 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) { case OBJ_SCRIPT: case OBJ_FUNC: case OBJ_FIBER: - case OBJ_USER: + case OBJ_CLASS: + case OBJ_INST: TODO; + UNREACHABLE(); + default: UNREACHABLE(); } @@ -1621,8 +1703,11 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) { case OBJ_SCRIPT: case OBJ_FUNC: case OBJ_FIBER: - case OBJ_USER: + case OBJ_CLASS: + case OBJ_INST: TODO; + UNREACHABLE(); + default: UNREACHABLE(); } diff --git a/src/pk_debug.c b/src/pk_debug.c index 40ffd1b..816a8c7 100644 --- a/src/pk_debug.c +++ b/src/pk_debug.c @@ -96,7 +96,7 @@ void dumpFunctionCode(PKVM* vm, Function* func, pkByteBuffer* buff) { const char* op_name = op_names[opcodes[i]]; uint32_t op_length = (uint32_t)strlen(op_name); pkByteBufferAddString(buff, vm, op_name, op_length); - for (uint32_t i = 0; i < 16 - op_length; i++) { // Padding. + for (uint32_t j = 0; j < 16 - op_length; j++) { // Padding. ADD_CHAR(vm, buff, ' '); } @@ -124,10 +124,26 @@ void dumpFunctionCode(PKVM* vm, Function* func, pkByteBuffer* buff) { NO_ARGS(); break; - case OP_PUSH_LIST: SHORT_ARG(); break; - case OP_PUSH_MAP: NO_ARGS(); break; - case OP_LIST_APPEND: NO_ARGS(); break; - case OP_MAP_INSERT: NO_ARGS(); break; + case OP_PUSH_LIST: SHORT_ARG(); break; + case OP_PUSH_INSTANCE: + { + int ty_index = READ_BYTE(); + ASSERT_INDEX((uint32_t)ty_index, func->owner->classes.count); + uint32_t name_ind = func->owner->classes.data[ty_index]->name; + ASSERT_INDEX(name_ind, func->owner->names.count); + String* ty_name = func->owner->names.data[name_ind]; + + // Prints: %5d [Ty:%s]\n + ADD_INTEGER(vm, buff, ty_index, INT_WIDTH); + pkByteBufferAddString(buff, vm, STR_AND_LEN(" [Ty:")); + pkByteBufferAddString(buff, vm, ty_name->data, ty_name->length); + pkByteBufferAddString(buff, vm, STR_AND_LEN("]\n")); + break; + } + case OP_PUSH_MAP: NO_ARGS(); break; + case OP_LIST_APPEND: NO_ARGS(); break; + case OP_MAP_INSERT: NO_ARGS(); break; + case OP_INST_APPEND: NO_ARGS(); break; case OP_PUSH_LOCAL_0: case OP_PUSH_LOCAL_1: @@ -148,7 +164,7 @@ void dumpFunctionCode(PKVM* vm, Function* func, pkByteBuffer* buff) { } else { arg = (int)(op - OP_PUSH_LOCAL_0); - for (int i = 0; i < INT_WIDTH; i++) ADD_CHAR(vm, buff, ' '); + for (int j = 0; j < INT_WIDTH; j++) ADD_CHAR(vm, buff, ' '); } if (arg < func->arity) { @@ -181,7 +197,7 @@ void dumpFunctionCode(PKVM* vm, Function* func, pkByteBuffer* buff) { } else { arg = (int)(op - OP_STORE_LOCAL_0); - for (int i = 0; i < INT_WIDTH; i++) ADD_CHAR(vm, buff, ' '); + for (int j = 0; j < INT_WIDTH; j++) ADD_CHAR(vm, buff, ' '); } if (arg < func->arity) { @@ -224,6 +240,23 @@ void dumpFunctionCode(PKVM* vm, Function* func, pkByteBuffer* buff) { break; } + case OP_PUSH_TYPE: + { + int ty_index = READ_BYTE(); + ASSERT_INDEX((uint32_t)ty_index, func->owner->classes.count); + uint32_t name_ind = func->owner->classes.data[ty_index]->name; + ASSERT_INDEX(name_ind, func->owner->names.count); + String* ty_name = func->owner->names.data[name_ind]; + + // Prints: %5d [Ty:%s]\n + ADD_INTEGER(vm, buff, ty_index, INT_WIDTH); + pkByteBufferAddString(buff, vm, STR_AND_LEN(" [Ty:")); + pkByteBufferAddString(buff, vm, ty_name->data, ty_name->length); + pkByteBufferAddString(buff, vm, STR_AND_LEN("]\n")); + + break; + } + case OP_PUSH_BUILTIN_FN: { int index = READ_BYTE(); diff --git a/src/pk_opcodes.h b/src/pk_opcodes.h index f4c7a17..9f60995 100644 --- a/src/pk_opcodes.h +++ b/src/pk_opcodes.h @@ -39,6 +39,10 @@ OPCODE(PUSH_LIST, 2, 1) // Push a new map to construct from literal. OPCODE(PUSH_MAP, 0, 1) +// Push a new instance to the stack. +// param: 1 byte index. +OPCODE(PUSH_INSTANCE, 1, 1) + // Pop the value on the stack the next stack top would be a list. Append the // value to the list. Used in literal array construction. OPCODE(LIST_APPEND, 0, -1) @@ -47,6 +51,10 @@ OPCODE(LIST_APPEND, 0, -1) // Insert the key value pairs to the map. Used in literal map construction. OPCODE(MAP_INSERT, 0, -2) +// Pop the value on the stack, the next stack top would be an instance. Append +// the value to the instance. Used in instance construction. +OPCODE(INST_APPEND, 0, -1) + // Push stack local on top of the stack. Locals at 0 to 8 marked explicitly // since it's performance critical. // params: PUSH_LOCAL_N -> 1 byte count value. @@ -89,6 +97,10 @@ OPCODE(STORE_GLOBAL, 1, 0) // params: 1 byte index. OPCODE(PUSH_FN, 1, 1) +// Push the script's type on the stack. +// params: 1 byte index +OPCODE(PUSH_TYPE, 1, 1) + // Push a built in function. // params: 1 bytes index. OPCODE(PUSH_BUILTIN_FN, 1, 1) diff --git a/src/pk_var.c b/src/pk_var.c index 78de015..e0781ae 100644 --- a/src/pk_var.c +++ b/src/pk_var.c @@ -34,7 +34,8 @@ PkVarType pkGetValueType(const PkVar value) { case OBJ_SCRIPT: return PK_SCRIPT; case OBJ_FUNC: return PK_FUNCTION; case OBJ_FIBER: return PK_FIBER; - case OBJ_USER: TODO; break; + case OBJ_CLASS: return PK_CLASS; + case OBJ_INST: return PK_INST; } UNREACHABLE(); @@ -83,6 +84,7 @@ DEFINE_BUFFER(Byte, uint8_t) DEFINE_BUFFER(Var, Var) DEFINE_BUFFER(String, String*) DEFINE_BUFFER(Function, Function*) +DEFINE_BUFFER(Class, Class*) void pkByteBufferAddString(pkByteBuffer* self, PKVM* vm, const char* str, uint32_t length) { @@ -138,6 +140,7 @@ void markVarBuffer(PKVM* vm, pkVarBuffer* self) { MARK_OBJ_BUFFER(String) MARK_OBJ_BUFFER(Function) +MARK_OBJ_BUFFER(Class) #undef MARK_OBJ_BUFFER @@ -192,6 +195,9 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) { markFunctionBuffer(vm, &scr->functions); vm->bytes_allocated += sizeof(Function*) * scr->functions.capacity; + markClassBuffer(vm, &scr->classes); + vm->bytes_allocated += sizeof(Class*) * scr->classes.count; + markStringBuffer(vm, &scr->names); vm->bytes_allocated += sizeof(String*) * scr->names.capacity; @@ -207,8 +213,10 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) { if (!func->is_native) { Fn* fn = func->fn; + vm->bytes_allocated += sizeof(Fn); + vm->bytes_allocated += sizeof(uint8_t)* fn->opcodes.capacity; - vm->bytes_allocated += sizeof(int) * fn->oplines.capacity; + vm->bytes_allocated += sizeof(uint32_t) * fn->oplines.capacity; } } break; @@ -237,9 +245,24 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) { } break; - case OBJ_USER: - TODO; - break; + case OBJ_CLASS: + { + Class* type = (Class*)obj; + vm->bytes_allocated += sizeof(Class); + markObject(vm, &type->owner->_super); + markObject(vm, &type->ctor->_super); + vm->bytes_allocated += sizeof(uint32_t) * type->field_names.capacity; + } break; + + case OBJ_INST: + { + Instance* inst = (Instance*)obj; + if (!inst->is_native) { + Inst* ins = inst->ins; + vm->bytes_allocated += sizeof(Inst); + vm->bytes_allocated += sizeof(Var*) * ins->fields.capacity; + } + } break; } } @@ -329,6 +352,7 @@ Script* newScript(PKVM* vm, String* path) { pkUintBufferInit(&script->global_names); pkVarBufferInit(&script->literals); pkFunctionBufferInit(&script->functions); + pkClassBufferInit(&script->classes); pkStringBufferInit(&script->names); vmPushTempRef(vm, &script->_super); @@ -434,6 +458,59 @@ Fiber* newFiber(PKVM* vm, Function* fn) { return fiber; } +Class* newClass(PKVM* vm, Script* scr, const char* name, uint32_t length) { + Class* type = ALLOCATE(vm, Class); + varInitObject(&type->_super, vm, OBJ_CLASS); + + vmPushTempRef(vm, &type->_super); // type. + + pkClassBufferWrite(&scr->classes, vm, type); + type->owner = scr; + type->name = scriptAddName(scr, vm, name, length); + pkUintBufferInit(&type->field_names); + + // Can't use '$' in string format. (TODO) + String* ty_name = scr->names.data[type->name]; + String* dollar = newStringLength(vm, "$", 1); + vmPushTempRef(vm, &dollar->_super); // dollar + String* ctor_name = stringFormat(vm, "@(Ctor:@)", dollar, ty_name); + vmPopTempRef(vm); // dollar + + // Constructor. + vmPushTempRef(vm, &ctor_name->_super); // ctor_name + type->ctor = newFunction(vm, ctor_name->data, ctor_name->length, + scr, false, NULL); + vmPopTempRef(vm); // ctor_name + + vmPopTempRef(vm); // type. + return type; +} + +Instance* newInstance(PKVM* vm, Class* ty, bool initialize) { + + Instance* inst = ALLOCATE(vm, Instance); + varInitObject(&inst->_super, vm, OBJ_INST); + + vmPushTempRef(vm, &inst->_super); // inst. + + ASSERT(ty->name < ty->owner->names.count, OOPS); + inst->name = ty->owner->names.data[ty->name]->data; + inst->is_native = false; + + Inst* ins = ALLOCATE(vm, Inst); + inst->ins = ins; + ins->type = ty; + pkVarBufferInit(&ins->fields); + + if (initialize && ty->field_names.count != 0) { + pkVarBufferFill(&ins->fields, vm, VAR_NULL, ty->field_names.count); + } + + vmPopTempRef(vm); // inst. + + return inst; +} + List* rangeAsList(PKVM* vm, Range* self) { List* list; if (self->from < self->to) { @@ -678,8 +755,10 @@ static uint32_t _hashObject(Object* obj) { case OBJ_SCRIPT: case OBJ_FUNC: case OBJ_FIBER: - case OBJ_USER: + case OBJ_CLASS: + case OBJ_INST: TODO; + UNREACHABLE(); default: L_unhashable: @@ -898,6 +977,7 @@ void freeObject(PKVM* vm, Object* self) { pkUintBufferClear(&scr->global_names, vm); pkVarBufferClear(&scr->literals, vm); pkFunctionBufferClear(&scr->functions, vm); + pkClassBufferClear(&scr->classes, vm); pkStringBufferClear(&scr->names, vm); } break; @@ -906,6 +986,7 @@ void freeObject(PKVM* vm, Object* self) { if (!func->is_native) { pkByteBufferClear(&func->fn->opcodes, vm); pkUintBufferClear(&func->fn->oplines, vm); + DEALLOCATE(vm, func->fn); } } break; @@ -915,9 +996,26 @@ void freeObject(PKVM* vm, Object* self) { DEALLOCATE(vm, fiber->frames); } break; - case OBJ_USER: - TODO; // Remove OBJ_USER. + case OBJ_CLASS: { + Class* type = (Class*)self; + pkUintBufferClear(&type->field_names, vm); + } break; + + case OBJ_INST: + { + Instance* inst = (Instance*)self; + + if (inst->is_native) { + TODO; + + } else { + Inst* ins = inst->ins; + pkVarBufferClear(&ins->fields, vm); + DEALLOCATE(vm, ins); + } + break; + } } DEALLOCATE(vm, self); @@ -943,23 +1041,36 @@ uint32_t scriptAddName(Script* self, PKVM* vm, const char* name, return self->names.count - 1; } -uint32_t scriptGetFunc(Script* script, const char* name, uint32_t length) { - for (uint32_t i = 0; i < script->functions.count; i++) { - const char* fn_name = script->functions.data[i]->name; - uint32_t fn_length = (uint32_t)strlen(fn_name); - if (fn_length == length && strncmp(fn_name, name, length) == 0) { - return i; +int scriptGetClass(Script* script, const char* name, uint32_t length) { + for (uint32_t i = 0; i < script->classes.count; i++) { + uint32_t name_ind = script->classes.data[i]->name; + ASSERT(name_ind < script->names.count, OOPS); + String* ty_name = script->names.data[name_ind]; + if (ty_name->length == length && + strncmp(ty_name->data, name, length) == 0) { + return (int)i; } } return -1; } -uint32_t scriptGetGlobals(Script* script, const char* name, uint32_t length) { +int scriptGetFunc(Script* script, const char* name, uint32_t length) { + for (uint32_t i = 0; i < script->functions.count; i++) { + const char* fn_name = script->functions.data[i]->name; + uint32_t fn_length = (uint32_t)strlen(fn_name); + if (fn_length == length && strncmp(fn_name, name, length) == 0) { + return (int)i; + } + } + return -1; +} + +int scriptGetGlobals(Script* script, const char* name, uint32_t length) { for (uint32_t i = 0; i < script->global_names.count; i++) { uint32_t name_index = script->global_names.data[i]; String* g_name = script->names.data[name_index]; if (g_name->length == length && strncmp(g_name->data, name, length) == 0) { - return i; + return (int)i; } } return -1; @@ -970,9 +1081,9 @@ uint32_t scriptAddGlobal(PKVM* vm, Script* script, Var value) { // If already exists update the value. - uint32_t var_ind = scriptGetGlobals(script, name, length); + int var_ind = scriptGetGlobals(script, name, length); if (var_ind != -1) { - ASSERT(var_ind < script->globals.count, OOPS); + ASSERT(var_ind < (int)script->globals.count, OOPS); script->globals.data[var_ind] = value; return var_ind; } @@ -985,13 +1096,15 @@ uint32_t scriptAddGlobal(PKVM* vm, Script* script, return script->globals.count - 1; } -// Utility functions ////////////////////////////////////////////////////////// +/*****************************************************************************/ +/* UTILITY FUNCTIONS */ +/*****************************************************************************/ const char* getPkVarTypeName(PkVarType type) { switch (type) { - case PK_NULL: return "null"; - case PK_BOOL: return "bool"; - case PK_NUMBER: return "number"; + case PK_NULL: return "Null"; + case PK_BOOL: return "Bool"; + case PK_NUMBER: return "Number"; case PK_STRING: return "String"; case PK_LIST: return "List"; case PK_MAP: return "Map"; @@ -999,6 +1112,8 @@ const char* getPkVarTypeName(PkVarType type) { case PK_SCRIPT: return "Script"; case PK_FUNCTION: return "Function"; case PK_FIBER: return "Fiber"; + case PK_CLASS: return "Class"; + case PK_INST: return "Inst"; } UNREACHABLE(); @@ -1013,15 +1128,16 @@ const char* getObjectTypeName(ObjectType type) { case OBJ_SCRIPT: return "Script"; case OBJ_FUNC: return "Func"; case OBJ_FIBER: return "Fiber"; - case OBJ_USER: return "UserObj"; + case OBJ_CLASS: return "Class"; + case OBJ_INST: return "Inst"; } UNREACHABLE(); } const char* varTypeName(Var v) { - if (IS_NULL(v)) return "null"; - if (IS_BOOL(v)) return "bool"; - if (IS_NUM(v)) return "number"; + if (IS_NULL(v)) return "Null"; + if (IS_BOOL(v)) return "Bool"; + if (IS_NUM(v)) return "Number"; ASSERT(IS_OBJ(v), OOPS); Object* obj = AS_OBJ(v); @@ -1299,9 +1415,40 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff, return; } - case OBJ_USER: { - // TODO: - pkByteBufferAddString(buff, vm, "[UserObj]", 9); + case OBJ_CLASS: { + const Class* ty = (const Class*)obj; + pkByteBufferAddString(buff, vm, "[Class:", 7); + String* ty_name = ty->owner->names.data[ty->name]; + pkByteBufferAddString(buff, vm, ty_name->data, ty_name->length); + pkByteBufferWrite(buff, vm, ']'); + return; + } + + case OBJ_INST: + { + const Instance* inst = (const Instance*)obj; + pkByteBufferWrite(buff, vm, '['); + pkByteBufferAddString(buff, vm, inst->name, + (uint32_t)strlen(inst->name)); + + if (!inst->is_native) { + const Class* ty = inst->ins->type; + const Inst* ins = inst->ins; + ASSERT(ins->fields.count == ty->field_names.count, OOPS); + + pkByteBufferWrite(buff, vm, ':'); + for (uint32_t i = 0; i < ty->field_names.count; i++) { + if (i != 0) pkByteBufferWrite(buff, vm, ','); + + pkByteBufferWrite(buff, vm, ' '); + String* f_name = ty->owner->names.data[ty->field_names.data[i]]; + pkByteBufferAddString(buff, vm, f_name->data, f_name->length); + pkByteBufferWrite(buff, vm, '='); + _toStringInternal(vm, ins->fields.data[i], buff, outer, repr); + } + } + + pkByteBufferWrite(buff, vm, ']'); return; } } @@ -1351,7 +1498,8 @@ bool toBool(Var v) { case OBJ_SCRIPT: case OBJ_FUNC: case OBJ_FIBER: - case OBJ_USER: + case OBJ_CLASS: + case OBJ_INST: return true; } diff --git a/src/pk_var.h b/src/pk_var.h index d970e18..f6fe735 100644 --- a/src/pk_var.h +++ b/src/pk_var.h @@ -187,6 +187,8 @@ typedef struct Range Range; typedef struct Script Script; typedef struct Function Function; typedef struct Fiber Fiber; +typedef struct Class Class; +typedef struct Instance Instance; // Declaration of buffer objects of different types. DECLARE_BUFFER(Uint, uint32_t) @@ -194,6 +196,7 @@ DECLARE_BUFFER(Byte, uint8_t) DECLARE_BUFFER(Var, Var) DECLARE_BUFFER(String, String*) DECLARE_BUFFER(Function, Function*) +DECLARE_BUFFER(Class, Class*) // Add all the characters to the buffer, byte buffer can also be used as a // buffer to write string (like a string stream). Note that this will not @@ -209,16 +212,14 @@ typedef enum { OBJ_SCRIPT, OBJ_FUNC, OBJ_FIBER, - // TODO: - OBJ_USER, + OBJ_CLASS, + OBJ_INST, } ObjectType; // Base struct for all heap allocated objects. struct Object { ObjectType type; //< Type of the object in \ref var_Object_Type. bool is_marked; //< Marked when garbage collection's marking phase. - //Class* is; //< The class the object IS. // No OOP in PK. - Object* next; //< Next object in the heap allocated link list. }; @@ -269,24 +270,11 @@ struct Script { String* module; //< Module name of the script. String* path; //< Path of the script. - /* - names: ["v1", "fn1", "v2", "fn2", ...] - 0 1 2 3 - - g_names: [ 1, 3 ] <-- function name - 0 1 <-- it's index - - globals: [ fn1, fn2 ] - 0 1 - */ - - // TODO: (maybe) join the function buffer and variable buffers. - // and make if possible to override functions. (also have to allow builtin). - pkVarBuffer globals; //< Script level global variables. pkUintBuffer global_names; //< Name map to index in globals. - pkFunctionBuffer functions; //< Script level functions. + pkFunctionBuffer functions; //< Functions of the script. + pkClassBuffer classes; //< Classes of the script. pkStringBuffer names; //< Name literals, attribute names, etc. pkVarBuffer literals; //< Script literal constant values. @@ -305,9 +293,9 @@ typedef struct { struct Function { Object _super; - const char* name; //< Name in the script [owner] or C literal. - Script* owner; //< Owner script of the function. - int arity; //< Number of argument the function expects. + const char* name; //< Name in the script [owner] or C literal. + Script* owner; //< Owner script of the function. + int arity; //< Number of argument the function expects. // Docstring of the function, currently it's just the C string literal // pointer, refactor this into String* so that we can support public @@ -328,10 +316,10 @@ typedef struct { } CallFrame; typedef enum { - FIBER_NEW, //< Fiber haven't started yet. - FIBER_RUNNING, //< Fiber is currently running. - FIBER_YIELDED, //< Yielded fiber, can be resumed. - FIBER_DONE, //< Fiber finished and cannot be resumed. + FIBER_NEW, //< Fiber haven't started yet. + FIBER_RUNNING, //< Fiber is currently running. + FIBER_YIELDED, //< Yielded fiber, can be resumed. + FIBER_DONE, //< Fiber finished and cannot be resumed. } FiberState; struct Fiber { @@ -369,6 +357,33 @@ struct Fiber { String* error; }; +struct Class { + Object _super; + + Script* owner; //< The script it belongs to. + uint32_t name; //< Index of the type's name in the script's name buffer. + + Function* ctor; //< The constructor function. + pkUintBuffer field_names; //< Buffer of field names. + // TODO: ordered names buffer for binary search. +}; + +typedef struct { + Class* type; //< Class this instance belongs to. + pkVarBuffer fields; //< Var buffer of the instance. +} Inst; + +struct Instance { + Object _super; + + const char* name; //< Name of the type it belongs to. + bool is_native; //< True if it's a native type instance. + union { + void* native; //< C struct pointer. // TODO: + Inst* ins; //< Module instance pointer. + }; +}; + /*****************************************************************************/ /* "CONSTRUCTORS" */ /*****************************************************************************/ @@ -392,7 +407,7 @@ String* newStringLength(PKVM* vm, const char* text, uint32_t length); #else // Macro implementation. // Allocate new string using the cstring [text]. #define newString(vm, text) \ - newStringLength(vm, text, (text == NULL) ? 0 : (uint32_t)strlen(text)) + newStringLength(vm, text, (!text) ? 0 : (uint32_t)strlen(text)) #endif // Allocate new List and return List*. @@ -419,6 +434,14 @@ Function* newFunction(PKVM* vm, const char* name, int length, Script* owner, // Allocate new Fiber object around the function [fn] and return Fiber*. Fiber* newFiber(PKVM* vm, Function* fn); +// Allocate new Class object and return Class* with name [name]. +Class* newClass(PKVM* vm, Script* scr, const char* name, uint32_t length); + +// Allocate new instance with of the base [type]. Note that if [initialize] is +// false, the field value buffer of the instance would be un initialized (ie. +// the buffer count = 0). Otherwise they'll be set to VAR_NULL. +Instance* newInstance(PKVM* vm, Class* ty, bool initialize); + /*****************************************************************************/ /* METHODS */ /*****************************************************************************/ @@ -520,13 +543,17 @@ bool fiberHasError(Fiber* fiber); uint32_t scriptAddName(Script* self, PKVM* vm, const char* name, uint32_t length); +// Search for the type name in the script and return it's index in it's +// [classes] buffer. If not found returns -1. +int scriptGetClass(Script* script, const char* name, uint32_t length); + // Search for the function name in the script and return it's index in it's // [functions] buffer. If not found returns -1. -uint32_t scriptGetFunc(Script* script, const char* name, uint32_t length); +int scriptGetFunc(Script* script, const char* name, uint32_t length); // Search for the global variable name in the script and return it's index in // it's [globals] buffer. If not found returns -1. -uint32_t scriptGetGlobals(Script* script, const char* name, uint32_t length); +int scriptGetGlobals(Script* script, const char* name, uint32_t length); // Add a global [value] to the [scrpt] and return its index. uint32_t scriptAddGlobal(PKVM* vm, Script* script, diff --git a/src/pk_vm.c b/src/pk_vm.c index 94e8eaf..51f93c9 100644 --- a/src/pk_vm.c +++ b/src/pk_vm.c @@ -755,6 +755,15 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { DISPATCH(); } + OPCODE(PUSH_INSTANCE): + { + uint8_t index = READ_BYTE(); + ASSERT_INDEX(index, script->classes.count); + Instance* inst = newInstance(vm, script->classes.data[index], false); + PUSH(VAR_OBJ(inst)); + DISPATCH(); + } + OPCODE(LIST_APPEND): { Var elem = PEEK(-1); // Don't pop yet, we need the reference for gc. @@ -785,6 +794,21 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { DISPATCH(); } + OPCODE(INST_APPEND): + { + Var value = PEEK(-1); // Don't pop yet, we need the reference for gc. + Var inst = PEEK(-2); + ASSERT(IS_OBJ_TYPE(inst, OBJ_INST), OOPS); + + Instance* inst_p = (Instance*)AS_OBJ(inst); + ASSERT(!inst_p->is_native, OOPS); + Inst* ins = inst_p->ins; + pkVarBufferWrite(&ins->fields, vm, value); + DROP(); // value + + DISPATCH(); + } + OPCODE(PUSH_LOCAL_0): OPCODE(PUSH_LOCAL_1): OPCODE(PUSH_LOCAL_2): @@ -830,7 +854,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { OPCODE(PUSH_GLOBAL): { uint8_t index = READ_BYTE(); - ASSERT(index < script->globals.count, OOPS); + ASSERT_INDEX(index, script->globals.count); PUSH(script->globals.data[index]); DISPATCH(); } @@ -838,7 +862,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { OPCODE(STORE_GLOBAL): { uint8_t index = READ_BYTE(); - ASSERT(index < script->globals.count, OOPS); + ASSERT_INDEX(index, script->globals.count); script->globals.data[index] = PEEK(-1); DISPATCH(); } @@ -846,12 +870,21 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { OPCODE(PUSH_FN): { uint8_t index = READ_BYTE(); - ASSERT(index < script->functions.count, OOPS); + ASSERT_INDEX(index, script->functions.count); Function* fn = script->functions.data[index]; PUSH(VAR_OBJ(fn)); DISPATCH(); } + OPCODE(PUSH_TYPE): + { + uint8_t index = READ_BYTE(); + ASSERT_INDEX(index, script->classes.count); + Class* ty = script->classes.data[index]; + PUSH(VAR_OBJ(ty)); + DISPATCH(); + } + OPCODE(PUSH_BUILTIN_FN): { uint8_t index = READ_BYTE(); @@ -890,64 +923,73 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { Fiber* call_fiber = vm->fiber; Var* callable = call_fiber->sp - argc - 1; + const Function* fn = NULL; + if (IS_OBJ_TYPE(*callable, OBJ_FUNC)) { - const Function* fn = (const Function*)AS_OBJ(*callable); + fn = (const Function*)AS_OBJ(*callable); - // -1 argument means multiple number of args. - if (fn->arity != -1 && fn->arity != argc) { - char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", fn->arity); - String* msg = stringFormat(vm, "Expected exactly $ argument(s).", - buff); - RUNTIME_ERROR(msg); - } - - if (fn->is_native) { - - if (fn->native == NULL) { - RUNTIME_ERROR(stringFormat(vm, - "Native function pointer of $ was NULL.", fn->name)); - } - - // Update the current frame's ip. - UPDATE_FRAME(); - - // Next call frame starts here. (including return value). - call_fiber->ret = callable; - *(call_fiber->ret) = VAR_NULL; //< Set the return value to null. - - fn->native(vm); //< Call the native function. - - // Calling yield() will change vm->fiber to it's caller fiber, which - // would be null if we're not running the function with a fiber. - if (vm->fiber == NULL) return PK_RESULT_SUCCESS; - - // Load the top frame to vm's execution variables. - if (vm->fiber != call_fiber) LOAD_FRAME(); - - // Pop function arguments except for the return value. - // Don't use 'vm->fiber' because calling fiber_new() and yield() - // would change the fiber. - call_fiber->sp = call_fiber->ret + 1; - CHECK_ERROR(); - - } else { - - if (instruction == OP_CALL) { - UPDATE_FRAME(); //< Update the current frame's ip. - pushCallFrame(vm, fn, callable); - LOAD_FRAME(); //< Load the top frame to vm's execution variables. - - } else { - ASSERT(instruction == OP_TAIL_CALL, OOPS); - - reuseCallFrame(vm, fn); - LOAD_FRAME(); //< Re-load the frame to vm's execution variables. - } - } + } else if (IS_OBJ_TYPE(*callable, OBJ_CLASS)) { + fn = (const Function*)((Class*)AS_OBJ(*callable))->ctor; } else { RUNTIME_ERROR(stringFormat(vm, "$ $(@).", "Expected a function in " - "call, instead got", varTypeName(*callable), toString(vm, *callable))); + "call, instead got", + varTypeName(*callable), toString(vm, *callable))); + DISPATCH(); + } + + // If we reached here it's a valid callable. + + // -1 argument means multiple number of args. + if (fn->arity != -1 && fn->arity != argc) { + char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", fn->arity); + String* msg = stringFormat(vm, "Expected exactly $ argument(s).", + buff); + RUNTIME_ERROR(msg); + } + + if (fn->is_native) { + + if (fn->native == NULL) { + RUNTIME_ERROR(stringFormat(vm, + "Native function pointer of $ was NULL.", fn->name)); + } + + // Update the current frame's ip. + UPDATE_FRAME(); + + // Next call frame starts here. (including return value). + call_fiber->ret = callable; + *(call_fiber->ret) = VAR_NULL; //< Set the return value to null. + + fn->native(vm); //< Call the native function. + + // Calling yield() will change vm->fiber to it's caller fiber, which + // would be null if we're not running the function with a fiber. + if (vm->fiber == NULL) return PK_RESULT_SUCCESS; + + // Load the top frame to vm's execution variables. + if (vm->fiber != call_fiber) LOAD_FRAME(); + + // Pop function arguments except for the return value. + // Don't use 'vm->fiber' because calling fiber_new() and yield() + // would change the fiber. + call_fiber->sp = call_fiber->ret + 1; + CHECK_ERROR(); + + } else { + + if (instruction == OP_CALL) { + UPDATE_FRAME(); //< Update the current frame's ip. + pushCallFrame(vm, fn, callable); + LOAD_FRAME(); //< Load the top frame to vm's execution variables. + + } else { + ASSERT(instruction == OP_TAIL_CALL, OOPS); + + reuseCallFrame(vm, fn); + LOAD_FRAME(); //< Re-load the frame to vm's execution variables. + } } DISPATCH(); @@ -1051,7 +1093,8 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { case OBJ_SCRIPT: case OBJ_FUNC: case OBJ_FIBER: - case OBJ_USER: + case OBJ_CLASS: + case OBJ_INST: TODO; break; default: UNREACHABLE(); @@ -1433,6 +1476,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { OPCODE(IN): // TODO: Implement bool varContaines(vm, on, value); TODO; + UNREACHABLE(); OPCODE(REPL_PRINT): { @@ -1447,7 +1491,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { } OPCODE(END): - TODO; + UNREACHABLE(); break; default: @@ -1455,5 +1499,5 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) { } - return PK_RESULT_SUCCESS; + UNREACHABLE(); //return PK_RESULT_SUCCESS; } diff --git a/tests/lang/class.pk b/tests/lang/class.pk new file mode 100644 index 0000000..93792b9 --- /dev/null +++ b/tests/lang/class.pk @@ -0,0 +1,36 @@ + +## TODO: Implement ctor with va arg to +## initialize, fields. + +class _Vec + x = 0 + y = 0 +end + +def Vec(x, y) + ret = _Vec() + ret.x = x; ret.y = y + return ret +end + +def vecAdd(v1, v2) + return Vec(v1.x + v2.x, + v1.y + v2.y) +end + +v1 = Vec(1, 2); assert(v1.x == 1 and v1.y == 2) +v2 = Vec(3, 4); assert(v2.x == 3 and v2.y == 4) + +v3 = vecAdd(v1, v2) +assert(v3.x == 4 and v3.y == 6) + +class Test + fn = null + val = Vec(12, 32) +end + +test = Test() +test.fn = to_string +res = test.fn(test.val) +assert(res == "[_Vec: x=12, y=32]") + diff --git a/tests/lang/import.pk b/tests/lang/import.pk index ce84fa7..be57a4f 100644 --- a/tests/lang/import.pk +++ b/tests/lang/import.pk @@ -13,6 +13,10 @@ import "basics.pk" ## will import all import "controlflow.pk" as if_test from "functions.pk" import fn1, fn2 as f2, fn3 +import "class.pk" as class_ +assert(class_.Vec(42, 3.14).y == 3.14) +(vec = class_._Vec()).x = 'a'; assert(vec.x == 'a') + ## If it has a module name it'll bind to that name. import 'import/module.pk' assert(module_name.get_module_name() == 'module_name') diff --git a/tests/tests.py b/tests/tests.py index 718eb3c..9cf13e0 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -16,6 +16,7 @@ THIS_PATH = abspath(dirname(__file__)) TEST_SUITE = { "Unit Tests": ( "lang/basics.pk", + "lang/class.pk", "lang/core.pk", "lang/controlflow.pk", "lang/fibers.pk",