Merge pull request #217 from ThakeeNathees/super-calls

super class method call implemented
This commit is contained in:
Thakee Nathees 2022-05-02 05:35:39 +05:30 committed by GitHub
commit c05c839c0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 158 additions and 36 deletions

View File

@ -13,7 +13,7 @@ can download it your self at https://premake.github.io/download.
python3 download_premake.py python3 download_premake.py
``` ```
It will download and place the `premake.exe` (in windows) binary next to It will download and place the `premake5.exe` (in windows) binary next to
it, next run the following command to generate Visual studio solution files. it, next run the following command to generate Visual studio solution files.
``` ```

View File

@ -68,13 +68,3 @@ project "pocket_cli"
cli_dir .. "**.h" } cli_dir .. "**.h" }
includedirs ({ src_dir .. "include/" }) includedirs ({ src_dir .. "include/" })
links { "pocket_static" } links { "pocket_static" }

View File

@ -127,7 +127,7 @@ typedef enum {
TK_TRUE, // true TK_TRUE, // true
TK_FALSE, // false TK_FALSE, // false
TK_SELF, // self TK_SELF, // self
// TODO: TK_SUPER TK_SUPER, // super
TK_DO, // do TK_DO, // do
TK_THEN, // then TK_THEN, // then
@ -191,6 +191,7 @@ static _Keyword _keywords[] = {
{ "true", 4, TK_TRUE }, { "true", 4, TK_TRUE },
{ "false", 5, TK_FALSE }, { "false", 5, TK_FALSE },
{ "self", 4, TK_SELF }, { "self", 4, TK_SELF },
{ "super", 5, TK_SUPER },
{ "do", 2, TK_DO }, { "do", 2, TK_DO },
{ "then", 4, TK_THEN }, { "then", 4, TK_THEN },
{ "while", 5, TK_WHILE }, { "while", 5, TK_WHILE },
@ -1533,6 +1534,7 @@ static void exprSubscript(Compiler* compiler);
static void exprValue(Compiler* compiler); static void exprValue(Compiler* compiler);
static void exprSelf(Compiler* compiler); static void exprSelf(Compiler* compiler);
static void exprSuper(Compiler* compiler);
#define NO_RULE { NULL, NULL, PREC_NONE } #define NO_RULE { NULL, NULL, PREC_NONE }
#define NO_INFIX PREC_NONE #define NO_INFIX PREC_NONE
@ -1600,7 +1602,8 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
/* TK_NOT */ { exprUnaryOp, NULL, PREC_UNARY }, /* TK_NOT */ { exprUnaryOp, NULL, PREC_UNARY },
/* TK_TRUE */ { exprValue, NULL, NO_INFIX }, /* TK_TRUE */ { exprValue, NULL, NO_INFIX },
/* TK_FALSE */ { exprValue, NULL, NO_INFIX }, /* TK_FALSE */ { exprValue, NULL, NO_INFIX },
/* TK_FALSE */ { exprSelf, NULL, NO_INFIX }, /* TK_SELF */ { exprSelf, NULL, NO_INFIX },
/* TK_SUPER */ { exprSuper, NULL, NO_INFIX },
/* TK_DO */ NO_RULE, /* TK_DO */ NO_RULE,
/* TK_THEN */ NO_RULE, /* TK_THEN */ NO_RULE,
/* TK_WHILE */ NO_RULE, /* TK_WHILE */ NO_RULE,
@ -2020,7 +2023,9 @@ static void exprMap(Compiler* compiler) {
// is OP_METHOD_CALL the [method] should refer a string in the module's // is OP_METHOD_CALL the [method] should refer a string in the module's
// constant pool, otherwise it's ignored. // constant pool, otherwise it's ignored.
static void _compileCall(Compiler* compiler, Opcode call_type, int method) { static void _compileCall(Compiler* compiler, Opcode call_type, int method) {
ASSERT((call_type == OP_CALL) || (call_type == OP_METHOD_CALL), OOPS); ASSERT((call_type == OP_CALL) ||
(call_type == OP_METHOD_CALL) ||
(call_type == OP_SUPER_CALL), OOPS);
// Compile parameters. // Compile parameters.
int argc = 0; int argc = 0;
@ -2038,7 +2043,7 @@ static void _compileCall(Compiler* compiler, Opcode call_type, int method) {
emitByte(compiler, argc); emitByte(compiler, argc);
if (call_type == OP_METHOD_CALL) { if ((call_type == OP_METHOD_CALL) || (call_type == OP_SUPER_CALL)) {
ASSERT_INDEX(method, (int)compiler->module->constants.count); ASSERT_INDEX(method, (int)compiler->module->constants.count);
emitShort(compiler, method); emitShort(compiler, method);
} }
@ -2146,6 +2151,42 @@ static void exprSelf(Compiler* compiler) {
} }
} }
static void exprSuper(Compiler* compiler) {
if (compiler->func->type != FUNC_CONSTRUCTOR &&
compiler->func->type != FUNC_METHOD) {
semanticError(compiler, compiler->parser.previous,
"Invalid use of 'super'.");
return;
}
ASSERT(compiler->func->ptr != NULL, OOPS);
int index = 0;
const char* name = compiler->func->ptr->name;
int name_length = -1;
if (!match(compiler, TK_LPARAN)) { // super.method().
consume(compiler, TK_DOT, "Invalid use of 'super'.");
consume(compiler, TK_NAME, "Expected a method name after 'super'.");
name = compiler->parser.previous.start;
name_length = compiler->parser.previous.length;
consume(compiler, TK_LPARAN, "Expected symbol '('.");
} else { // super().
name_length = (int)strlen(name);
}
if (compiler->parser.has_syntax_error) return;
emitOpcode(compiler, OP_PUSH_SELF);
moduleAddString(compiler->module, compiler->parser.vm,
name, name_length, &index);
_compileCall(compiler, OP_SUPER_CALL, index);
}
static void parsePrecedence(Compiler* compiler, Precedence precedence) { static void parsePrecedence(Compiler* compiler, Precedence precedence) {
lexToken(compiler); lexToken(compiler);
if (compiler->parser.has_syntax_error) return; if (compiler->parser.has_syntax_error) return;

View File

@ -968,21 +968,31 @@ Class* getClass(PKVM* vm, Var instance) {
return inst->cls; return inst->cls;
} }
bool hasMethod(PKVM* vm, Var self, String* name, Closure** _method) { // Returns a method on a class (it'll walk up the inheritance tree to search
Class* cls = getClass(vm, self); // and if the method not found, it'll return NULL.
ASSERT(cls != NULL, OOPS); static inline Closure* clsGetMethod(Class* cls, String* name) {
Class* cls_ = cls; Class* cls_ = cls;
do { do {
for (int i = 0; i < (int)cls_->methods.count; i++) { for (int i = 0; i < (int)cls_->methods.count; i++) {
Closure* method_ = cls_->methods.data[i]; Closure* method_ = cls_->methods.data[i];
if (IS_CSTR_EQ(name, method_->fn->name, name->length)) { if (IS_CSTR_EQ(name, method_->fn->name, name->length)) {
if (_method) *_method = method_; return method_;
return true;
} }
} }
cls_ = cls_->super_class; cls_ = cls_->super_class;
} while (cls_ != NULL); } while (cls_ != NULL);
return NULL;
}
bool hasMethod(PKVM* vm, Var self, String* name, Closure** _method) {
Class* cls = getClass(vm, self);
ASSERT(cls != NULL, OOPS);
Closure* method_ = clsGetMethod(cls, name);
if (method_ != NULL) {
*_method = method_;
return true;
}
return false; return false;
} }
@ -1000,6 +1010,22 @@ Var getMethod(PKVM* vm, Var self, String* name, bool* is_method) {
return varGetAttrib(vm, self, name); return varGetAttrib(vm, self, name);
} }
Closure* getSuperMethod(PKVM* vm, Var self, String* name) {
Class* super = getClass(vm, self)->super_class;
if (super == NULL) {
VM_SET_ERROR(vm, stringFormat(vm, "'$' object has no parent class.", \
varTypeName(self)));
return NULL;
};
Closure* method = clsGetMethod(super, name);
if (method == NULL) {
VM_SET_ERROR(vm, stringFormat(vm, "'@' class has no method named '@'.", \
super->name, name));
}
return method;
}
#define UNSUPPORTED_UNARY_OP(op) \ #define UNSUPPORTED_UNARY_OP(op) \
VM_SET_ERROR(vm, stringFormat(vm, "Unsupported operand ($) for " \ VM_SET_ERROR(vm, stringFormat(vm, "Unsupported operand ($) for " \
"unary operator " op ".", varTypeName(v))) "unary operator " op ".", varTypeName(v)))

View File

@ -75,6 +75,10 @@ Class* getClass(PKVM* vm, Var instance);
// If the method / attribute not found, it'll set a runtime error on the VM. // If the method / attribute not found, it'll set a runtime error on the VM.
Var getMethod(PKVM* vm, Var self, String* name, bool* is_method); Var getMethod(PKVM* vm, Var self, String* name, bool* is_method);
// Returns the method (closure) from the instance's super class. If the method
// doesn't exists, it'll set an error on the VM.
Closure* getSuperMethod(PKVM* vm, Var self, String* name);
// Unlike getMethod this will not set error and will not try to get attribute // Unlike getMethod this will not set error and will not try to get attribute
// with the same name. It'll return true if the method exists on [self], false // with the same name. It'll return true if the method exists on [self], false
// otherwise and if the [method] argument is not NULL, method will be set. // otherwise and if the [method] argument is not NULL, method will be set.

View File

@ -512,6 +512,7 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
break; break;
} }
case OP_SUPER_CALL:
case OP_METHOD_CALL: case OP_METHOD_CALL:
{ {
int argc = READ_BYTE(); int argc = READ_BYTE();

View File

@ -133,7 +133,14 @@ OPCODE(POP, 0, -1)
// params: 2 byte name index. // params: 2 byte name index.
OPCODE(IMPORT, 2, 1) OPCODE(IMPORT, 2, 1)
// Call a method on the variable at the stack top. See opcode CALL for detail. // Call a super class's method on the variable at (stack_top - argc).
// See opcode CALL for detail.
// params: 2 bytes method name index in the constant pool.
// 1 byte argc.
OPCODE(SUPER_CALL, 3, -0) //< Stack size will be calculated at compile time.
// Call a method on the variable at (stack_top - argc). See opcode CALL for
// detail.
// params: 2 bytes method name index in the constant pool. // params: 2 bytes method name index in the constant pool.
// 1 byte argc. // 1 byte argc.
OPCODE(METHOD_CALL, 3, -0) //< Stack size will be calculated at compile time. OPCODE(METHOD_CALL, 3, -0) //< Stack size will be calculated at compile time.

View File

@ -889,13 +889,10 @@ L_vm_main_loop:
if (strcmp(method->fn->name, CTOR_NAME) == 0) { if (strcmp(method->fn->name, CTOR_NAME) == 0) {
cls->ctor = method; cls->ctor = method;
} else {
// TODO: The method buffer should be ordered with it's name and
// inserted in a way to preserve the order to implement binary search
// to find a method.
pkClosureBufferWrite(&cls->methods, vm, method);
} }
pkClosureBufferWrite(&cls->methods, vm, method);
DROP(); DROP();
DISPATCH(); DISPATCH();
} }
@ -952,14 +949,28 @@ L_vm_main_loop:
Var callable; Var callable;
const Closure* closure; const Closure* closure;
uint16_t index; //< To get the method name.
String* name; //< The method name.
OPCODE(SUPER_CALL):
argc = READ_BYTE();
fiber->ret = (fiber->sp - argc - 1);
fiber->self = *fiber->ret; //< Self for the next call.
index = READ_SHORT();
name = moduleGetStringAt(module, (int)index);
Closure* super_method = getSuperMethod(vm, fiber->self, name);
CHECK_ERROR(); // Will return if super_method is NULL.
callable = VAR_OBJ(super_method);
goto L_do_call;
OPCODE(METHOD_CALL): OPCODE(METHOD_CALL):
argc = READ_BYTE(); argc = READ_BYTE();
fiber->ret = (fiber->sp - argc - 1); fiber->ret = (fiber->sp - argc - 1);
fiber->self = *fiber->ret; //< Self for the next call. fiber->self = *fiber->ret; //< Self for the next call.
uint16_t index = READ_SHORT(); index = READ_SHORT();
name = moduleGetStringAt(module, (int)index);
bool is_method; bool is_method;
String* name = moduleGetStringAt(module, (int)index);
callable = getMethod(vm, fiber->self, name, &is_method); callable = getMethod(vm, fiber->self, name, &is_method);
CHECK_ERROR(); CHECK_ERROR();
goto L_do_call; goto L_do_call;
@ -1055,16 +1066,18 @@ L_do_call:
} else { } else {
if (instruction == OP_CALL || instruction == OP_METHOD_CALL) { if (instruction == OP_TAIL_CALL) {
reuseCallFrame(vm, closure);
LOAD_FRAME(); //< Re-load the frame to vm's execution variables.
} else {
ASSERT((instruction == OP_CALL) ||
(instruction == OP_METHOD_CALL) ||
(instruction == OP_SUPER_CALL), OOPS);
UPDATE_FRAME(); //< Update the current frame's ip. UPDATE_FRAME(); //< Update the current frame's ip.
pushCallFrame(vm, closure, fiber->ret); pushCallFrame(vm, closure, fiber->ret);
LOAD_FRAME(); //< Load the top frame to vm's execution variables. LOAD_FRAME(); //< Load the top frame to vm's execution variables.
} else {
ASSERT(instruction == OP_TAIL_CALL, OOPS);
reuseCallFrame(vm, closure);
LOAD_FRAME(); //< Re-load the frame to vm's execution variables.
} }
} }

View File

@ -193,4 +193,44 @@ assert(!n5 == 3)
assert(!n5 == 2) assert(!n5 == 2)
assert(!n5 == 1) assert(!n5 == 1)
###############################################################################
## SUPER CLASS METHOD
###############################################################################
class A
def _init()
print("A init")
end
def foo()
print("A foo")
ret = self.bar()
assert(ret == "B.bar")
return "A.foo"
end
def bar()
print("A bar")
return "A.bar"
end
end
class B is A
def _init()
super()
print("B init")
end
def foo()
print("B foo")
ret = super()
assert(ret == "A.foo")
return super.bar()
end
def bar()
print("B bar")
return "B.bar"
end
end
b = B()
assert(b.foo() == "A.bar")
print('ALL TESTS PASSED') print('ALL TESTS PASSED')