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
|
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.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -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" }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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)))
|
||||||
|
@ -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.
|
||||||
|
@ -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();
|
||||||
|
@ -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.
|
||||||
|
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) {
|
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.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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')
|
||||||
|
Loading…
Reference in New Issue
Block a user