diff --git a/src/pk_compiler.c b/src/pk_compiler.c index 8fd9994..4b38e0f 100644 --- a/src/pk_compiler.c +++ b/src/pk_compiler.c @@ -125,6 +125,8 @@ typedef enum { TK_NOT, // not / ! TK_TRUE, // true TK_FALSE, // false + TK_SELF, // self + // TODO: TK_SUPER TK_DO, // do TK_THEN, // then @@ -186,6 +188,7 @@ static _Keyword _keywords[] = { { "not", 3, TK_NOT }, { "true", 4, TK_TRUE }, { "false", 5, TK_FALSE }, + { "self", 4, TK_SELF }, { "do", 2, TK_DO }, { "then", 4, TK_THEN }, { "while", 5, TK_WHILE }, @@ -241,6 +244,14 @@ typedef enum { DEPTH_LOCAL, //< Local scope. Increase with inner scope. } Depth; +typedef enum { + FUNC_MAIN, // The body function of the script. + FUNC_TOPLEVEL, + FUNC_LITERAL, + FUNC_METHOD, + FUNC_CONSTRUCTOR, +} FuncType; + typedef struct { const char* name; //< Directly points into the source string. uint32_t length; //< Length of the name. @@ -314,6 +325,9 @@ typedef struct sUpvalueInfo { typedef struct sFunc { + // Type of the current function. + FuncType type; + // Scope of the function. -2 for module body function, -1 for top level // function and literal functions will have the scope where it declared. int depth; @@ -396,8 +410,9 @@ typedef struct sParser { ForwardName forwards[MAX_FORWARD_NAMES]; int forwards_count; - bool repl_mode; //< True if compiling for REPL. - bool has_errors; //< True if any syntex error occurred at. + bool repl_mode; + bool parsing_class; + bool has_errors; bool need_more_lines; //< True if we need more lines in REPL mode. } Parser; @@ -507,6 +522,7 @@ static void parserInit(Parser* parser, PKVM* vm, Compiler* compiler, parser->forwards_count = 0; parser->repl_mode = !!(compiler->options && compiler->options->repl_mode); + parser->parsing_class = false; parser->has_errors = false; parser->need_more_lines = false; } @@ -1444,7 +1460,7 @@ static void compilerChangeStack(Compiler* compiler, int num); // Forward declaration of grammar functions. static void parsePrecedence(Compiler* compiler, Precedence precedence); -static void compileFunction(Compiler* compiler, bool is_literal); +static int compileFunction(Compiler* compiler, FuncType fn_type); static void compileExpression(Compiler* compiler); static void exprLiteral(Compiler* compiler); @@ -1469,6 +1485,8 @@ static void exprSubscript(Compiler* compiler); // true, false, null, self. static void exprValue(Compiler* compiler); +static void exprSelf(Compiler* compiler); + #define NO_RULE { NULL, NULL, PREC_NONE } #define NO_INFIX PREC_NONE @@ -1534,6 +1552,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence /* TK_NOT */ { exprUnaryOp, NULL, PREC_UNARY }, /* TK_TRUE */ { exprValue, NULL, NO_INFIX }, /* TK_FALSE */ { exprValue, NULL, NO_INFIX }, + /* TK_FALSE */ { exprSelf, NULL, NO_INFIX }, /* TK_DO */ NO_RULE, /* TK_THEN */ NO_RULE, /* TK_WHILE */ NO_RULE, @@ -1698,7 +1717,7 @@ static void exprInterpolation(Compiler* compiler) { } static void exprFunction(Compiler* compiler) { - compileFunction(compiler, true); + compileFunction(compiler, FUNC_LITERAL); } static void exprName(Compiler* compiler) { @@ -2035,6 +2054,25 @@ static void exprValue(Compiler* compiler) { } } +static void exprSelf(Compiler* compiler) { + + if (compiler->func->type == FUNC_CONSTRUCTOR || + compiler->func->type == FUNC_METHOD) { + emitOpcode(compiler, OP_PUSH_SELF); + return; + } + + // If we reach here 'self' is used in either non method or a closure + // inside a method. + + if (!compiler->parser.parsing_class) { + parseError(compiler, "Invalid use of 'self'."); + } else { + // FIXME: + parseError(compiler, "TODO: Closures cannot capture 'self' for now."); + } +} + static void parsePrecedence(Compiler* compiler, Precedence precedence) { lexToken(&(compiler->parser)); GrammarFn prefix = getRule(compiler->parser.previous.type)->prefix; @@ -2204,7 +2242,8 @@ static void compilerExitBlock(Compiler* compiler) { } static void compilerPushFunc(Compiler* compiler, Func* fn, - Function* func) { + Function* func, FuncType type) { + fn->type = type; fn->outer_func = compiler->func; fn->local_count = 0; fn->stack_size = 0; @@ -2318,14 +2357,12 @@ static void compileStatement(Compiler* compiler); static void compileBlockBody(Compiler* compiler, BlockType type); // Compile a class and return it's index in the module's types buffer. -static void compileClass(Compiler* compiler) { +static int compileClass(Compiler* compiler) { ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS); - TODO; //< compileClass Function is in-compilete. - // Consume the name of the type. - consume(compiler, TK_NAME, "Expected a type name."); + consume(compiler, TK_NAME, "Expected a class name."); const char* name = compiler->parser.previous.start; int name_len = compiler->parser.previous.length; int name_line = compiler->parser.previous.line; @@ -2336,23 +2373,58 @@ static void compileClass(Compiler* compiler) { Class* cls = newClass(_vm, name, name_len, _vm->builtin_classes[PK_OBJECT], compiler->module, NULL, &cls_index); + vmPushTempRef(_vm, &cls->_super); // cls. + compiler->parser.parsing_class = true; // Check count exceeded. checkMaxConstantsReached(compiler, cls_index); - // Compile all the methods and constructors. - TODO; + skipNewLines(compiler); + while (!match(compiler, TK_END)) { + // At the top level the stack size should be 0, before and after compiling + // a top level statement, since there aren't any locals at the top level. + ASSERT(compiler->parser.has_errors || + compiler->func->stack_size == 0, OOPS); - consume(compiler, TK_END, "Expected 'end' after a class declaration end."); + consume(compiler, TK_DEF, "Expected method definition."); + int fn_index = compileFunction(compiler, FUNC_METHOD); + Var fn_var = compiler->module->constants.data[fn_index]; + ASSERT(IS_OBJ_TYPE(fn_var, OBJ_FUNC), OOPS); + + // TODO: check if the constructor or method already exists and report + // error. Make sure the error report line match the name token's line. + + Closure* method = newClosure(_vm, (Function*)AS_OBJ(fn_var)); + if (strcmp(method->fn->name, "_init") == 0) { + cls->ctor = method; + + } else { + vmPushTempRef(_vm, &method->_super); // method. + pkClosureBufferWrite(&cls->methods, _vm, method); + vmPopTempRef(_vm); // method. + } + + // At the top level the stack size should be 0, before and after compiling + // a top level statement, since there aren't any locals at the top level. + ASSERT(compiler->parser.has_errors || + compiler->func->stack_size == 0, OOPS); + + skipNewLines(compiler); + } + + compiler->parser.parsing_class = false; + vmPopTempRef(_vm); // cls. + + return cls_index; } // Compile a function and return it's index in the module's function buffer. -static void compileFunction(Compiler* compiler, bool is_literal) { +static int compileFunction(Compiler* compiler, FuncType fn_type) { const char* name; int name_length; - if (!is_literal) { + if (fn_type != FUNC_LITERAL) { consume(compiler, TK_NAME, "Expected a function name."); name = compiler->parser.previous.start; name_length = compiler->parser.previous.length; @@ -2367,7 +2439,7 @@ static void compileFunction(Compiler* compiler, bool is_literal) { compiler->module, false, NULL, &fn_index); checkMaxConstantsReached(compiler, fn_index); - if (!is_literal) { + if (fn_type != FUNC_LITERAL) { ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS); int name_line = compiler->parser.previous.line; int g_index = compilerAddVariable(compiler, name, name_length, name_line); @@ -2378,8 +2450,12 @@ static void compileFunction(Compiler* compiler, bool is_literal) { vmPopTempRef(compiler->parser.vm); // func. } + if (fn_type == FUNC_METHOD && strncmp(name, "_init", name_length) == 0) { + fn_type = FUNC_CONSTRUCTOR; + } + Func curr_fn; - compilerPushFunc(compiler, &curr_fn, func); + compilerPushFunc(compiler, &curr_fn, func, fn_type); int argc = 0; compilerEnterBlock(compiler); // Parameter depth. @@ -2422,6 +2498,11 @@ static void compileFunction(Compiler* compiler, bool is_literal) { compileBlockBody(compiler, BLOCK_FUNC); + if (fn_type == FUNC_CONSTRUCTOR) { + emitOpcode(compiler, OP_PUSH_SELF); + emitOpcode(compiler, OP_RETURN); + } + consume(compiler, TK_END, "Expected 'end' after function definition end."); compilerExitBlock(compiler); // Parameter depth. emitFunctionEnd(compiler); @@ -2439,7 +2520,7 @@ static void compileFunction(Compiler* compiler, bool is_literal) { // function of this function, and the bellow emit calls will write to the // outer function. If it's a literal function, we need to push a closure // of it on the stack. - if (is_literal) { + if (fn_type == FUNC_LITERAL) { emitOpcode(compiler, OP_PUSH_CLOSURE); emitShort(compiler, fn_index); @@ -2449,6 +2530,8 @@ static void compileFunction(Compiler* compiler, bool is_literal) { emitByte(compiler, curr_fn.upvalues[i].index); } } + + return fn_index; } // Finish a block body. @@ -3013,10 +3096,22 @@ static void compileStatement(Compiler* compiler) { } if (matchEndStatement(compiler)) { - emitOpcode(compiler, OP_PUSH_NULL); + + // Constructors will return self. + if (compiler->func->type == FUNC_CONSTRUCTOR) { + emitOpcode(compiler, OP_PUSH_SELF); + } else { + emitOpcode(compiler, OP_PUSH_NULL); + } + emitOpcode(compiler, OP_RETURN); } else { + + if (compiler->func->type == FUNC_CONSTRUCTOR) { + parseError(compiler, "Cannor 'return' a value from constructor."); + } + compileExpression(compiler); //< Return value is at stack top. // If the last expression parsed with compileExpression() is a call @@ -3076,7 +3171,7 @@ static void compileTopLevelStatement(Compiler* compiler) { compileClass(compiler); } else if (match(compiler, TK_DEF)) { - compileFunction(compiler, false); + compileFunction(compiler, FUNC_TOPLEVEL); } else if (match(compiler, TK_FROM)) { compileFromImport(compiler); @@ -3130,7 +3225,7 @@ PkResult compile(PKVM* vm, Module* module, const char* source, uint32_t globals_count = module->globals.count; Func curr_fn; - compilerPushFunc(compiler, &curr_fn, module->body->fn); + compilerPushFunc(compiler, &curr_fn, module->body->fn, FUNC_MAIN); // Lex initial tokens. current <-- next. lexToken(&(compiler->parser)); diff --git a/src/pk_core.c b/src/pk_core.c index 7d200da..9fe0306 100644 --- a/src/pk_core.c +++ b/src/pk_core.c @@ -1289,8 +1289,10 @@ static void initializePrimitiveClasses(PKVM* vm) { Class* super = NULL; if (i != 0) super = vm->builtin_classes[PK_OBJECT]; const char* name = getPkVarTypeName((PkVarType)i); - vm->builtin_classes[i] = newClass(vm, name, (int)strlen(name), - super, NULL, NULL, NULL); + Class* cls = newClass(vm, name, (int)strlen(name), + super, NULL, NULL, NULL); + vm->builtin_classes[i] = cls; + cls->class_of = (PkVarType)i; } #define ADD_CTOR(type, name, ptr, arity_) \ @@ -1347,46 +1349,41 @@ Var preConstructSelf(PKVM* vm, Class* cls) { VM_SET_ERROR(vm, newString(vm, \ "Class '" type_name "' cannot be instanciated.")) - for (int i = 0; i < PK_INSTANCE; i++) { - if (vm->builtin_classes[i] == cls) { + switch (cls->class_of) { + case PK_OBJECT: + NO_INSTANCE("Object"); + return VAR_NULL; - switch ((PkVarType)i) { - case PK_OBJECT: - NO_INSTANCE("Object"); - return VAR_NULL; + case PK_NULL: + case PK_BOOL: + case PK_NUMBER: + case PK_STRING: + case PK_LIST: + case PK_MAP: + case PK_RANGE: + return VAR_NULL; // Constructor will override the null. - case PK_NULL: - case PK_BOOL: - case PK_NUMBER: - case PK_STRING: - case PK_LIST: - case PK_MAP: - case PK_RANGE: - return VAR_NULL; // Constructor will override the null. + case PK_MODULE: + NO_INSTANCE("Module"); + return VAR_NULL; - case PK_MODULE: - NO_INSTANCE("Module"); - return VAR_NULL; + case PK_CLOSURE: + NO_INSTANCE("Closure"); + return VAR_NULL; - case PK_CLOSURE: - NO_INSTANCE("Closure"); - return VAR_NULL; + case PK_FIBER: + return VAR_NULL; - case PK_FIBER: - return VAR_NULL; + case PK_CLASS: + NO_INSTANCE("Class"); + return VAR_NULL; - case PK_CLASS: - NO_INSTANCE("Class"); - return VAR_NULL; - - case PK_INSTANCE: - UNREACHABLE(); - return VAR_NULL; - } - } + case PK_INSTANCE: + return VAR_OBJ(newInstance(vm, cls)); } - return VAR_OBJ(newInstance(vm, cls)); + UNREACHABLE(); + return VAR_NULL; } Class* getClass(PKVM* vm, Var instance) { diff --git a/src/pk_value.c b/src/pk_value.c index dba3176..a07be49 100644 --- a/src/pk_value.c +++ b/src/pk_value.c @@ -288,7 +288,7 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) { case OBJ_INST: { Instance* inst = (Instance*)obj; - markObject(vm, &inst->attribs._super); + markObject(vm, &inst->attribs->_super); markObject(vm, &inst->cls->_super); vm->bytes_allocated += sizeof(Instance); } break; @@ -515,6 +515,7 @@ Class* newClass(PKVM* vm, const char* name, int length, pkClosureBufferInit(&cls->methods); + cls->class_of = PK_INSTANCE; cls->owner = NULL; cls->super_class = super; cls->docstring = NULL; @@ -538,28 +539,23 @@ Class* newClass(PKVM* vm, const char* name, int length, Instance* newInstance(PKVM* vm, Class* cls) { -#ifdef DEBUG - bool _builtin_class = false; - for (int i = 0; i < PK_INSTANCE; i++) { - if (vm->builtin_classes[i] == cls) { - _builtin_class = true; - break; - } - } - ASSERT(!_builtin_class, "Cannot create an instace of builtin class " - "with newInstance() function."); -#endif // DEBUG + ASSERT(cls->class_of == PK_INSTANCE, "Cannot create an instace of builtin " + "class with newInstance() function."); Instance* inst = ALLOCATE(vm, Instance); varInitObject(&inst->_super, vm, OBJ_INST); + vmPushTempRef(vm, &inst->_super); // inst. + inst->cls = cls; + inst->attribs = newMap(vm); + if (cls->new_fn != NULL) { - vmPushTempRef(vm, &inst->_super); // inst. inst->native = cls->new_fn(); - vmPopTempRef(vm); // inst. } else { inst->native = NULL; } + + vmPopTempRef(vm); // inst. return inst; } @@ -1170,10 +1166,10 @@ bool instGetAttrib(PKVM* vm, Instance* inst, String* attrib, Var* value) { ASSERT((inst != NULL) && (attrib != NULL) && (value != NULL), OOPS); if (inst->native != NULL) { + TODO; } - TODO; - Var value_ = mapGet(&inst->attribs, VAR_OBJ(attrib)); + Var value_ = mapGet(inst->attribs, VAR_OBJ(attrib)); if (IS_UNDEF(value_)) return false; *value = value_; @@ -1194,7 +1190,7 @@ bool instSetAttrib(PKVM* vm, Instance* inst, String* attrib, Var value) { return true; } - mapSet(vm, &inst->attribs, VAR_OBJ(attrib), value); + mapSet(vm, inst->attribs, VAR_OBJ(attrib), value); return true; } @@ -1591,9 +1587,10 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff, { const Instance* inst = (const Instance*)obj; pkByteBufferWrite(buff, vm, '['); + pkByteBufferWrite(buff, vm, '\''); pkByteBufferAddString(buff, vm, inst->cls->name->data, inst->cls->name->length); - pkByteBufferWrite(buff, vm, ':'); + pkByteBufferAddString(buff, vm, "' instance at ", 14); char buff_addr[STR_HEX_BUFF_SIZE]; char* ptr = (char*)buff_addr; diff --git a/src/pk_value.h b/src/pk_value.h index 20d128f..efd4f3f 100644 --- a/src/pk_value.h +++ b/src/pk_value.h @@ -501,6 +501,11 @@ struct Class { // entry in it's owner module's constant pool. const char* docstring; + // For builtin type it'll be it's enum (ex: PK_STRING, PK_NUMBER, ...) for + // every other classes it'll be PK_INSTANCE to indicate that it's not a + // builtin type's class. + PkVarType class_of; + Closure* ctor; //< The constructor function. // A buffer of methods of the class. @@ -531,7 +536,7 @@ struct Instance { void* native; // Dynamic attributes of an instance. - Map attribs; + Map* attribs; }; /*****************************************************************************/ diff --git a/src/pk_vm.c b/src/pk_vm.c index a6242f3..a9ab792 100644 --- a/src/pk_vm.c +++ b/src/pk_vm.c @@ -551,7 +551,7 @@ static inline void pushCallFrame(PKVM* vm, const Closure* closure, Var* rbp) { frame->closure = closure; frame->ip = closure->fn->fn->opcodes.data; - // Eat the self. + // Capture self. frame->self = vm->fiber->self; vm->fiber->self = VAR_UNDEFINED; } @@ -567,7 +567,10 @@ static inline void reuseCallFrame(PKVM* vm, const Closure* closure) { CallFrame* frame = fb->frames + fb->frame_count - 1; frame->closure = closure; frame->ip = closure->fn->fn->opcodes.data; - frame->self = VAR_UNDEFINED; + + // Capture self. + frame->self = vm->fiber->self; + vm->fiber->self = VAR_UNDEFINED; ASSERT(*frame->rbp == VAR_NULL, OOPS); diff --git a/tests/lang/class.pk b/tests/lang/class.pk index 13e5e7c..952b2ba 100644 --- a/tests/lang/class.pk +++ b/tests/lang/class.pk @@ -1,9 +1,33 @@ - - -## Note that classes are being implemented and temproarly -## the classes cannot be compiled. - - - + +class Vec2 + def _init(x, y) + self.x = x + self.y = y + end + + def add(other) + return Vec2(self.x + other.x, + self.y + other.y) + end + + ## Note that operator overloading / friend functions + ## haven't implemented at this point (to_string won't actually + ## override it). + def to_string + return "[${self.x}, ${self.y}]" + end + +end + +v1 = Vec2(1, 2); assert(v1.x == 1 and v1.y == 2) +print("v1 = ${v1.to_string()}") + +v2 = Vec2(3, 4); assert(v2.x == 3 and v2.y == 4) +print("v2 = ${v2.to_string()}") + +v3 = v1.add(v2); assert(v3.x == 4 and v3.y == 6) +print("v3 = ${v3.to_string()}") + +print('ALL TESTS PASSED')