mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-06 12:46:53 +08:00
Merge pull request #217 from ThakeeNathees/super-calls
super class method call implemented
This commit is contained in:
commit
c05c839c0a
@ -13,7 +13,7 @@ can download it your self at https://premake.github.io/download.
|
||||
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.
|
||||
|
||||
```
|
||||
|
@ -68,13 +68,3 @@ project "pocket_cli"
|
||||
cli_dir .. "**.h" }
|
||||
includedirs ({ src_dir .. "include/" })
|
||||
links { "pocket_static" }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -127,7 +127,7 @@ typedef enum {
|
||||
TK_TRUE, // true
|
||||
TK_FALSE, // false
|
||||
TK_SELF, // self
|
||||
// TODO: TK_SUPER
|
||||
TK_SUPER, // super
|
||||
|
||||
TK_DO, // do
|
||||
TK_THEN, // then
|
||||
@ -191,6 +191,7 @@ static _Keyword _keywords[] = {
|
||||
{ "true", 4, TK_TRUE },
|
||||
{ "false", 5, TK_FALSE },
|
||||
{ "self", 4, TK_SELF },
|
||||
{ "super", 5, TK_SUPER },
|
||||
{ "do", 2, TK_DO },
|
||||
{ "then", 4, TK_THEN },
|
||||
{ "while", 5, TK_WHILE },
|
||||
@ -1533,6 +1534,7 @@ static void exprSubscript(Compiler* compiler);
|
||||
static void exprValue(Compiler* compiler);
|
||||
|
||||
static void exprSelf(Compiler* compiler);
|
||||
static void exprSuper(Compiler* compiler);
|
||||
|
||||
#define NO_RULE { NULL, NULL, PREC_NONE }
|
||||
#define NO_INFIX PREC_NONE
|
||||
@ -1600,7 +1602,8 @@ 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_SELF */ { exprSelf, NULL, NO_INFIX },
|
||||
/* TK_SUPER */ { exprSuper, NULL, NO_INFIX },
|
||||
/* TK_DO */ NO_RULE,
|
||||
/* TK_THEN */ 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
|
||||
// constant pool, otherwise it's ignored.
|
||||
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.
|
||||
int argc = 0;
|
||||
@ -2038,7 +2043,7 @@ static void _compileCall(Compiler* compiler, Opcode call_type, int method) {
|
||||
|
||||
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);
|
||||
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) {
|
||||
lexToken(compiler);
|
||||
if (compiler->parser.has_syntax_error) return;
|
||||
|
@ -968,21 +968,31 @@ Class* getClass(PKVM* vm, Var instance) {
|
||||
return inst->cls;
|
||||
}
|
||||
|
||||
bool hasMethod(PKVM* vm, Var self, String* name, Closure** _method) {
|
||||
Class* cls = getClass(vm, self);
|
||||
ASSERT(cls != NULL, OOPS);
|
||||
|
||||
// Returns a method on a class (it'll walk up the inheritance tree to search
|
||||
// and if the method not found, it'll return NULL.
|
||||
static inline Closure* clsGetMethod(Class* cls, String* name) {
|
||||
Class* cls_ = cls;
|
||||
do {
|
||||
for (int i = 0; i < (int)cls_->methods.count; i++) {
|
||||
Closure* method_ = cls_->methods.data[i];
|
||||
if (IS_CSTR_EQ(name, method_->fn->name, name->length)) {
|
||||
if (_method) *_method = method_;
|
||||
return true;
|
||||
return method_;
|
||||
}
|
||||
}
|
||||
cls_ = cls_->super_class;
|
||||
} 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;
|
||||
}
|
||||
@ -1000,6 +1010,22 @@ Var getMethod(PKVM* vm, Var self, String* name, bool* is_method) {
|
||||
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) \
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "Unsupported operand ($) for " \
|
||||
"unary operator " op ".", varTypeName(v)))
|
||||
|
@ -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.
|
||||
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
|
||||
// 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.
|
||||
|
@ -512,6 +512,7 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_SUPER_CALL:
|
||||
case OP_METHOD_CALL:
|
||||
{
|
||||
int argc = READ_BYTE();
|
||||
|
@ -133,7 +133,14 @@ OPCODE(POP, 0, -1)
|
||||
// params: 2 byte name index.
|
||||
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.
|
||||
// 1 byte argc.
|
||||
OPCODE(METHOD_CALL, 3, -0) //< Stack size will be calculated at compile time.
|
||||
|
41
src/pk_vm.c
41
src/pk_vm.c
@ -889,13 +889,10 @@ L_vm_main_loop:
|
||||
|
||||
if (strcmp(method->fn->name, CTOR_NAME) == 0) {
|
||||
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();
|
||||
DISPATCH();
|
||||
}
|
||||
@ -952,14 +949,28 @@ L_vm_main_loop:
|
||||
Var callable;
|
||||
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):
|
||||
argc = READ_BYTE();
|
||||
fiber->ret = (fiber->sp - argc - 1);
|
||||
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;
|
||||
String* name = moduleGetStringAt(module, (int)index);
|
||||
callable = getMethod(vm, fiber->self, name, &is_method);
|
||||
CHECK_ERROR();
|
||||
goto L_do_call;
|
||||
@ -1055,16 +1066,18 @@ L_do_call:
|
||||
|
||||
} 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.
|
||||
pushCallFrame(vm, closure, fiber->ret);
|
||||
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.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,4 +193,44 @@ assert(!n5 == 3)
|
||||
assert(!n5 == 2)
|
||||
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')
|
||||
|
Loading…
Reference in New Issue
Block a user