mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-06 04:37:47 +08:00
native instance interface implemented
This commit is contained in:
parent
2727f010ec
commit
48089b2a1a
@ -24,6 +24,14 @@ void _deleteDummy(void* ptr) {
|
||||
FREE_OBJ(dummy);
|
||||
}
|
||||
|
||||
DEF(_dummyInit, "") {
|
||||
double val;
|
||||
if (!pkValidateSlotNumber(vm, 1, &val)) return;
|
||||
|
||||
Dummy* self = (Dummy*) pkGetSelf(vm);
|
||||
self->val = val;
|
||||
}
|
||||
|
||||
DEF(_dummyGetter, "") {
|
||||
const char* name = pkGetSlotString(vm, 1, NULL);
|
||||
Dummy* self = (Dummy*)pkGetSelf(vm);
|
||||
@ -44,6 +52,25 @@ DEF(_dummySetter, "") {
|
||||
}
|
||||
}
|
||||
|
||||
DEF(_dummyAdd, "") {
|
||||
Dummy* self = (Dummy*) pkGetSelf(vm);
|
||||
|
||||
pkReserveSlots(vm, 4); // Now we have slots [0, 1, 2, 3].
|
||||
|
||||
pkPlaceSelf(vm, 2); // slot[2] = self
|
||||
pkGetClass(vm, 2, 2); // slot[2] = Dummy class.
|
||||
|
||||
// slots[1] = other.
|
||||
if (!pkValidateSlotInstanceOf(vm, 1, 2)) return;
|
||||
Dummy* other = (Dummy*) pkGetSlotNativeInstance(vm, 1);
|
||||
|
||||
// slot[3] = self.val + other.val
|
||||
pkSetSlotNumber(vm, 3, self->val + other->val);
|
||||
|
||||
// slot[0] = Dummy(slot[3]) => return value.
|
||||
if (!pkNewInstance(vm, 2, 0, 1, 3)) return;
|
||||
}
|
||||
|
||||
DEF(_dummyEq, "") {
|
||||
|
||||
// TODO: Currently there is no way of getting another native instance
|
||||
@ -85,8 +112,10 @@ void registerModuleDummy(PKVM* vm) {
|
||||
|
||||
PkHandle* cls_dummy = pkNewClass(vm, "Dummy", NULL, dummy,
|
||||
_newDummy, _deleteDummy);
|
||||
pkClassAddMethod(vm, cls_dummy, "_init", _dummyInit, 1);
|
||||
pkClassAddMethod(vm, cls_dummy, "@getter", _dummyGetter, 1);
|
||||
pkClassAddMethod(vm, cls_dummy, "@setter", _dummySetter, 2);
|
||||
pkClassAddMethod(vm, cls_dummy, "+", _dummyAdd, 1);
|
||||
pkClassAddMethod(vm, cls_dummy, "==", _dummyEq, 1);
|
||||
pkClassAddMethod(vm, cls_dummy, ">", _dummyGt, 1);
|
||||
pkClassAddMethod(vm, cls_dummy, "a_method", _dummyMethod, 2);
|
||||
|
@ -283,8 +283,6 @@ PK_PUBLIC int pkGetArgc(const PKVM* vm);
|
||||
// that min <= max, and pocketlang won't validate this in release binary.
|
||||
PK_PUBLIC bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max);
|
||||
|
||||
// SLOTS DOCS
|
||||
|
||||
// Helper function to check if the argument at the [arg] slot is Boolean and
|
||||
// if not set a runtime error.
|
||||
PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int arg, bool* value);
|
||||
@ -298,6 +296,16 @@ PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int arg, double* value);
|
||||
PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int arg,
|
||||
const char** value, uint32_t* length);
|
||||
|
||||
// Helper function to check if the argument at the [arg] slot is an instance
|
||||
// of the class which is at the [cls] index. If not set a runtime error.
|
||||
PK_PUBLIC bool pkValidateSlotInstanceOf(PKVM* vm, int arg, int cls);
|
||||
|
||||
// Helper function to check if the instance at the [inst] slot is an instance
|
||||
// of the class which is at the [cls] index. The value will be set to [val]
|
||||
// if the object at [cls] slot isn't a valid class a runtime error will be set
|
||||
// and return false.
|
||||
PK_PUBLIC bool pkIsSlotInstanceOf(PKVM* vm, int inst, int cls, bool* val);
|
||||
|
||||
// Make sure the fiber has [count] number of slots to work with (including the
|
||||
// arguments).
|
||||
PK_PUBLIC void pkReserveSlots(PKVM* vm, int count);
|
||||
@ -328,6 +336,10 @@ PK_PUBLIC const char* pkGetSlotString(PKVM* vm, int index, uint32_t* length);
|
||||
// garbage collected.
|
||||
PK_PUBLIC PkHandle* pkGetSlotHandle(PKVM* vm, int index);
|
||||
|
||||
// Returns the native instance at the [index] slot. If the value at the [index]
|
||||
// is not a valid native instance, an assertion will fail.
|
||||
PK_PUBLIC void* pkGetSlotNativeInstance(PKVM* vm, int index);
|
||||
|
||||
// Set the [index] slot value as pocketlang null.
|
||||
PK_PUBLIC void pkSetSlotNull(PKVM* vm, int index);
|
||||
|
||||
@ -345,6 +357,10 @@ PK_PUBLIC void pkSetSlotString(PKVM* vm, int index, const char* value);
|
||||
PK_PUBLIC void pkSetSlotStringLength(PKVM* vm, int index,
|
||||
const char* value, uint32_t length);
|
||||
|
||||
// Create a new string copying from the formated string and set it to [index]
|
||||
// slot.
|
||||
PK_PUBLIC void pkSetSlotStringFmt(PKVM* vm, int index, const char* fmt, ...);
|
||||
|
||||
// Set the [index] slot's value as the given [handle]. The function won't
|
||||
// reclaim the ownership of the handle and you can still use it till
|
||||
// it's released by yourself.
|
||||
@ -354,6 +370,20 @@ PK_PUBLIC void pkSetSlotHandle(PKVM* vm, int index, PkHandle* handle);
|
||||
// [global] slot with the given [name].
|
||||
PK_PUBLIC void pkSetGlobal(PKVM* vm, int module, int global, const char* name);
|
||||
|
||||
// Creates a new instance of class at the [cls] slot, calls the constructor,
|
||||
// and place it at the [index] slot. Returns true if the instance constructed
|
||||
// successfully.
|
||||
//
|
||||
// [argc] is the argument count for the constructor, and [argv]
|
||||
// is the first argument slot's index.
|
||||
PK_PUBLIC bool pkNewInstance(PKVM* vm, int cls, int index, int argc, int argv);
|
||||
|
||||
// Place the [self] instance at the [index] slot.
|
||||
PK_PUBLIC void pkPlaceSelf(PKVM* vm, int index);
|
||||
|
||||
// Set the [index] slot's value as the class of the [instance].
|
||||
PK_PUBLIC void pkGetClass(PKVM* vm, int instance, int index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
@ -576,6 +576,32 @@ PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int arg, const char** value,
|
||||
return true;
|
||||
}
|
||||
|
||||
PK_PUBLIC bool pkValidateSlotInstanceOf(PKVM* vm, int arg, int cls) {
|
||||
CHECK_FIBER_EXISTS(vm);
|
||||
VALIDATE_ARGC(arg);
|
||||
VALIDATE_SLOT_INDEX(cls);
|
||||
|
||||
Var instance = ARG(arg), class_ = SLOT(cls);
|
||||
if (!varIsType(vm, instance, class_)) {
|
||||
// If [class_] is not a valid class, it's already an error.
|
||||
if (VM_HAS_ERROR(vm)) return false;
|
||||
ERR_INVALID_ARG_TYPE(((Class*)AS_OBJ(class_))->name->data);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pkIsSlotInstanceOf(PKVM* vm, int inst, int cls, bool* val) {
|
||||
CHECK_ARG_NULL(val);
|
||||
VALIDATE_SLOT_INDEX(inst);
|
||||
VALIDATE_SLOT_INDEX(cls);
|
||||
|
||||
Var instance = SLOT(inst), class_ = SLOT(cls);
|
||||
*val = varIsType(vm, inst, cls);
|
||||
return !VM_HAS_ERROR(vm);
|
||||
}
|
||||
|
||||
void pkReserveSlots(PKVM* vm, int count) {
|
||||
if (vm->fiber == NULL) vm->fiber = newFiber(vm, NULL);
|
||||
int needed = (int)(vm->fiber->ret - vm->fiber->stack) + count;
|
||||
@ -584,7 +610,7 @@ void pkReserveSlots(PKVM* vm, int count) {
|
||||
|
||||
int pkGetSlotsCount(PKVM* vm) {
|
||||
CHECK_FIBER_EXISTS(vm);
|
||||
return (int) ((vm->fiber->stack + vm->fiber->stack_size) - vm->fiber->ret);
|
||||
return vm->fiber->stack_size - (int)(vm->fiber->ret - vm->fiber->stack);
|
||||
}
|
||||
|
||||
PkVarType pkGetSlotType(PKVM* vm, int index) {
|
||||
@ -623,6 +649,21 @@ PkHandle* pkGetSlotHandle(PKVM* vm, int index) {
|
||||
return vmNewHandle(vm, SLOT(index));
|
||||
}
|
||||
|
||||
void* pkGetSlotNativeInstance(PKVM* vm, int index) {
|
||||
CHECK_FIBER_EXISTS(vm);
|
||||
VALIDATE_SLOT_INDEX(index);
|
||||
|
||||
Var value = SLOT(index);
|
||||
ASSERT(IS_OBJ_TYPE(value, OBJ_INST), "Slot value wasn't an Instance");
|
||||
|
||||
// TODO: If the native initializer (pkNewInstanceFn()) returned NULL,
|
||||
// [inst->native] will be null - handle.
|
||||
Instance* inst = (Instance*) AS_OBJ(value);
|
||||
ASSERT(inst->native != NULL, "Slot value wasn't a Native Instance");
|
||||
|
||||
return inst->native;
|
||||
}
|
||||
|
||||
void pkSetSlotNull(PKVM* vm, int index) {
|
||||
CHECK_FIBER_EXISTS(vm);
|
||||
VALIDATE_SLOT_INDEX(index);
|
||||
@ -654,6 +695,14 @@ PK_PUBLIC void pkSetSlotStringLength(PKVM* vm, int index,
|
||||
SET_SLOT(index, VAR_OBJ(newStringLength(vm, value, length)));
|
||||
}
|
||||
|
||||
void pkSetSlotStringFmt(PKVM* vm, int index, const char* fmt, ...) {
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
SET_SLOT(index, VAR_OBJ(newStringCFmt(vm, fmt, args)));
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void pkSetSlotHandle(PKVM* vm, int index, PkHandle* handle) {
|
||||
CHECK_FIBER_EXISTS(vm);
|
||||
VALIDATE_SLOT_INDEX(index);
|
||||
@ -672,7 +721,50 @@ void pkSetGlobal(PKVM* vm, int module, int global, const char* name) {
|
||||
(void) moduleAddGlobal(vm, m, name, (uint32_t) strlen(name), SLOT(global));
|
||||
}
|
||||
|
||||
#undef CHECK_FIBER_EXISTS
|
||||
bool pkNewInstance(PKVM* vm, int cls, int index, int argc, int argv) {
|
||||
CHECK_FIBER_EXISTS(vm);
|
||||
VALIDATE_SLOT_INDEX(index);
|
||||
|
||||
if (argc != 0) {
|
||||
VALIDATE_SLOT_INDEX(argv);
|
||||
VALIDATE_SLOT_INDEX(argv + argc - 1);
|
||||
}
|
||||
|
||||
ASSERT(IS_OBJ_TYPE(SLOT(cls), OBJ_CLASS), "Slot value wasn't a class.");
|
||||
|
||||
Class* class_ = (Class*) AS_OBJ(SLOT(cls));
|
||||
Var instance = preConstructSelf(vm, class_);
|
||||
|
||||
Closure* ctor = class_->ctor;
|
||||
while (ctor == NULL) {
|
||||
class_ = class_->super_class;
|
||||
if (class_ == NULL) break;
|
||||
ctor = class_->ctor;
|
||||
}
|
||||
|
||||
if (ctor != NULL) {
|
||||
vmCallMethod(vm, instance, ctor, argc, vm->fiber->ret + argv, NULL);
|
||||
}
|
||||
|
||||
SET_SLOT(index, instance);
|
||||
return !VM_HAS_ERROR(vm);
|
||||
}
|
||||
|
||||
void pkPlaceSelf(PKVM* vm, int index) {
|
||||
CHECK_FIBER_EXISTS(vm);
|
||||
VALIDATE_SLOT_INDEX(index);
|
||||
SET_SLOT(index, vm->fiber->self);
|
||||
}
|
||||
|
||||
void pkGetClass(PKVM* vm, int instance, int index) {
|
||||
CHECK_FIBER_EXISTS(vm);
|
||||
VALIDATE_SLOT_INDEX(instance);
|
||||
VALIDATE_SLOT_INDEX(index);
|
||||
|
||||
SET_SLOT(index, VAR_OBJ(getClass(vm, SLOT(instance))));
|
||||
}
|
||||
|
||||
#undef CHECK_RUNTIME
|
||||
#undef VALIDATE_ARGC
|
||||
#undef ERR_INVALID_ARG_TYPE
|
||||
#undef ARG
|
||||
|
@ -274,6 +274,20 @@ String* newStringLength(PKVM* vm, const char* text, uint32_t length) {
|
||||
return string;
|
||||
}
|
||||
|
||||
String* newStringCFmt(PKVM* vm, const char* fmt, va_list args) {
|
||||
va_list copy;
|
||||
va_copy(copy, args);
|
||||
int size = vsnprintf(NULL, 0, fmt, copy);
|
||||
va_end(copy);
|
||||
|
||||
// [size] is not including the null terminator so +1.
|
||||
String* string = _allocateString(vm, (size_t) size + 1);
|
||||
vsnprintf(string->data, string->capacity, fmt, args);
|
||||
string->hash = utilHashString(string->data);
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
List* newList(PKVM* vm, uint32_t size) {
|
||||
List* list = ALLOCATE(vm, List);
|
||||
vmPushTempRef(vm, &list->_super);
|
||||
|
@ -547,6 +547,8 @@ void varInitObject(Object* self, PKVM* vm, ObjectType type);
|
||||
|
||||
String* newStringLength(PKVM* vm, const char* text, uint32_t length);
|
||||
|
||||
String* newStringCFmt(PKVM* vm, const char* fmt, va_list args);
|
||||
|
||||
// An inline function/macro implementation of newString(). Set below 0 to 1, to
|
||||
// make the implementation a static inline function, it's totally okey to
|
||||
// define a function inside a header as long as it's static (but not a fan).
|
||||
|
11
src/pk_vm.c
11
src/pk_vm.c
@ -1096,6 +1096,8 @@ L_do_call:
|
||||
// citizens.
|
||||
ASSERT(!IS_OBJ_TYPE(callable, OBJ_FUNC), OOPS);
|
||||
|
||||
*(fiber->ret) = VAR_NULL; //< Set the return value to null.
|
||||
|
||||
if (IS_OBJ_TYPE(callable, OBJ_CLOSURE)) {
|
||||
closure = (const Closure*)AS_OBJ(callable);
|
||||
|
||||
@ -1106,6 +1108,12 @@ L_do_call:
|
||||
fiber->self = preConstructSelf(vm, cls);
|
||||
CHECK_ERROR();
|
||||
|
||||
// Note:
|
||||
// For pocketlang instance the constructor will update self and return
|
||||
// the instance (which might not be necessary since we're setting it
|
||||
// here).
|
||||
*fiber->ret = fiber->self;
|
||||
|
||||
closure = (const Closure*)(cls)->ctor;
|
||||
while (closure == NULL) {
|
||||
cls = cls->super_class;
|
||||
@ -1120,7 +1128,6 @@ L_do_call:
|
||||
RUNTIME_ERROR(msg);
|
||||
}
|
||||
|
||||
*fiber->ret = fiber->self;
|
||||
fiber->self = VAR_UNDEFINED;
|
||||
DISPATCH();
|
||||
}
|
||||
@ -1142,8 +1149,6 @@ L_do_call:
|
||||
RUNTIME_ERROR(msg);
|
||||
}
|
||||
|
||||
*(fiber->ret) = VAR_NULL; //< Set the return value to null.
|
||||
|
||||
if (closure->fn->is_native) {
|
||||
|
||||
if (closure->fn->native == NULL) {
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
from dummy import Dummy
|
||||
|
||||
d = Dummy()
|
||||
d = Dummy(0)
|
||||
print(d)
|
||||
|
||||
assert(d.val == 0) ## @getter
|
||||
@ -19,5 +19,11 @@ assert(d >= 3.14) ## > and == overload
|
||||
|
||||
assert(d.a_method(12, 34) == 408) ## method
|
||||
|
||||
d1 = Dummy(12)
|
||||
d2 = Dummy(23)
|
||||
d3 = d1 + d2
|
||||
assert(d3 is Dummy)
|
||||
assert(d3.val == d1.val + d2.val)
|
||||
|
||||
# If we got here, that means all test were passed.
|
||||
print('All TESTS PASSED')
|
||||
|
@ -3,8 +3,6 @@
|
||||
* Distributed Under The MIT License
|
||||
*/
|
||||
|
||||
#error Native interface is being refactored and will be completed soon.
|
||||
|
||||
// This is an example on how to write your own custom type (Vector here) and
|
||||
// bind it with with the pocket VM.
|
||||
|
||||
@ -21,24 +19,18 @@ static const char* code =
|
||||
" \n"
|
||||
" v1 = Vec2(1, 2) \n"
|
||||
" print('v1 = $v1') \n"
|
||||
" print() \n"
|
||||
" \n"
|
||||
" print('v1.x = ${v1.x}') \n"
|
||||
" print('v1.y = ${v1.y}') \n"
|
||||
" print('v1.length = ${v1.length}') \n"
|
||||
" print() \n"
|
||||
" \n"
|
||||
" v1.x = 3; v1.y = 4; \n"
|
||||
" print('v1.x = ${v1.x}') \n"
|
||||
" print('v1.y = ${v1.y}') \n"
|
||||
" print('v1 = $v1') \n"
|
||||
" print('v1.length = ${v1.length}') \n"
|
||||
" print() \n"
|
||||
" \n"
|
||||
" v2 = Vec2(5, 6) \n"
|
||||
" print('v2 = $v2') \n"
|
||||
" v3 = v1 + v2 \n"
|
||||
" print('v3 = ${v3}') \n"
|
||||
" print('v3.x = ${v3.x}') \n"
|
||||
" print('v3.y = ${v3.y}') \n"
|
||||
" print('v3 = $v3') \n"
|
||||
" \n"
|
||||
;
|
||||
|
||||
@ -72,7 +64,12 @@ void _vecGetter(PKVM* vm) {
|
||||
} else if (strcmp("y", name) == 0) {
|
||||
pkSetSlotNumber(vm, 0, self->y);
|
||||
return;
|
||||
} else if (strcmp("length", name) == 0) {
|
||||
double length = sqrt(pow(self->x, 2) + pow(self->y, 2));
|
||||
pkSetSlotNumber(vm, 0, length);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void _vecSetter(PKVM* vm) {
|
||||
@ -91,12 +88,42 @@ void _vecSetter(PKVM* vm) {
|
||||
}
|
||||
}
|
||||
|
||||
void _vecInit(PKVM* vm) {
|
||||
double x, y;
|
||||
if (!pkValidateSlotNumber(vm, 1, &x)) return;
|
||||
if (!pkValidateSlotNumber(vm, 2, &y)) return;
|
||||
|
||||
Vector* self = (Vector*) pkGetSelf(vm);
|
||||
self->x = x;
|
||||
self->y = y;
|
||||
}
|
||||
|
||||
// Vec2 '+' operator method.
|
||||
void _vecAdd(PKVM* vm) {
|
||||
Vector* self = (Vector*) pkGetSelf(vm);
|
||||
// FIXME:
|
||||
// Temproarly it's not possible to get vector from the args since the native
|
||||
// interface is being refactored. Will be implemented soon.
|
||||
|
||||
pkReserveSlots(vm, 5); // Now we have slots [0, 1, 2, 3, 4].
|
||||
|
||||
pkPlaceSelf(vm, 2); // slot[2] = self
|
||||
pkGetClass(vm, 2, 2); // slot[2] = Vec2 class.
|
||||
|
||||
// slot[1] is slot[2] == other is Vec2 ?
|
||||
if (!pkValidateSlotInstanceOf(vm, 1, 2)) return;
|
||||
Vector* other = (Vector*) pkGetSlotNativeInstance(vm, 1);
|
||||
|
||||
// slot[3] = new.x
|
||||
pkSetSlotNumber(vm, 3, self->x + other->x);
|
||||
|
||||
// slot[4] = new.y
|
||||
pkSetSlotNumber(vm, 4, self->y + other->y);
|
||||
|
||||
// slot[0] = Vec2(slot[3], slot[4]) => return value.
|
||||
if (!pkNewInstance(vm, 2, 0, 2, 3)) return;
|
||||
}
|
||||
|
||||
void _vecStr(PKVM* vm) {
|
||||
Vector* self = (Vector*)pkGetSelf(vm);
|
||||
pkSetSlotStringFmt(vm, 0, "[%g, %g]", self->x, self->y);
|
||||
}
|
||||
|
||||
// Register the 'Vector' module and it's functions.
|
||||
@ -105,6 +132,11 @@ void registerVector(PKVM* vm) {
|
||||
|
||||
PkHandle* Vec2 = pkNewClass(vm, "Vec2", NULL /*Base Class*/,
|
||||
vector, _newVec, _deleteVec);
|
||||
|
||||
pkClassAddMethod(vm, Vec2, "@getter", _vecGetter, 1);
|
||||
pkClassAddMethod(vm, Vec2, "@setter", _vecSetter, 2);
|
||||
pkClassAddMethod(vm, Vec2, "_init", _vecInit, 2);
|
||||
pkClassAddMethod(vm, Vec2, "_str", _vecStr, 0);
|
||||
pkClassAddMethod(vm, Vec2, "+", _vecAdd, 1);
|
||||
pkReleaseHandle(vm, Vec2);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user