Merge pull request #215 from ThakeeNathees/operator-overload

operator overloading.
This commit is contained in:
Thakee Nathees 2022-05-01 05:39:44 +05:30 committed by GitHub
commit 962f868907
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1010 additions and 422 deletions

View File

@ -13,6 +13,7 @@
/* STD MODULE SOURCES */
/*****************************************************************************/
#include "modules/std_dummy.c"
#include "modules/std_io.c"
#include "modules/std_path.c"
#include "modules/std_math.c"

View File

@ -14,6 +14,7 @@
#include <stdio.h>
#include <string.h>
void registerModuleDummy(PKVM* vm);
void registerModuleIO(PKVM* vm);
void registerModulePath(PKVM* vm);
void registerModuleMath(PKVM* vm);
@ -21,6 +22,7 @@ void registerModuleMath(PKVM* vm);
// Registers all the cli modules.
#define REGISTER_ALL_MODULES(vm) \
do { \
registerModuleDummy(vm); \
registerModuleIO(vm); \
registerModulePath(vm); \
registerModuleMath(vm); \

97
cli/modules/std_dummy.c Normal file
View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2020-2022 Thakee Nathees
* Copyright (c) 2021-2022 Pocketlang Contributors
* Distributed Under The MIT License
*/
#include "modules.h"
// A DUMMY MODULE TO TEST NATIVE INTERFACE AND CLASSES.
typedef struct {
double val;
} Dummy;
void* _newDummy() {
Dummy* dummy = NEW_OBJ(Dummy);
ASSERT(dummy != NULL, "malloc failed.");
dummy->val = 0;
return dummy;
}
void _deleteDummy(void* ptr) {
Dummy* dummy = (Dummy*)ptr;
FREE_OBJ(dummy);
}
DEF(_dummyGetter, "") {
const char* name = pkGetSlotString(vm, 1, NULL);
Dummy* self = (Dummy*)pkGetSelf(vm);
if (strcmp("val", name) == 0) {
pkSetSlotNumber(vm, 0, self->val);
return;
}
}
DEF(_dummySetter, "") {
const char* name = pkGetSlotString(vm, 1, NULL);
Dummy* self = (Dummy*)pkGetSelf(vm);
if (strcmp("val", name) == 0) {
double val;
if (!pkValidateSlotNumber(vm, 2, &val)) return;
self->val = val;
return;
}
}
DEF(_dummyEq, "") {
// TODO: Currently there is no way of getting another native instance
// So, it's impossible to check self == other. So for now checking with
// number.
double value;
if (!pkValidateSlotNumber(vm, 1, &value)) return;
Dummy* self = (Dummy*)pkGetSelf(vm);
pkSetSlotBool(vm, 0, value == self->val);
}
DEF(_dummyGt, "") {
// TODO: Currently there is no way of getting another native instance
// So, it's impossible to check self == other. So for now checking with
// number.
double value;
if (!pkValidateSlotNumber(vm, 1, &value)) return;
Dummy* self = (Dummy*)pkGetSelf(vm);
pkSetSlotBool(vm, 0, self->val > value);
}
DEF(_dummyMethod,
"Dummy.a_method(n1:num, n2:num) -> num\n"
"A dummy method to check dummy method calls. Will take 2 number arguments "
"and return the multiplication.") {
double n1, n2;
if (!pkValidateSlotNumber(vm, 1, &n1)) return;
if (!pkValidateSlotNumber(vm, 2, &n2)) return;
pkSetSlotNumber(vm, 0, n1 * n2);
}
void registerModuleDummy(PKVM* vm) {
PkHandle* dummy = pkNewModule(vm, "dummy");
PkHandle* cls_dummy = pkNewClass(vm, "Dummy", NULL, dummy,
_newDummy, _deleteDummy);
pkClassAddMethod(vm, cls_dummy, "@getter", _dummyGetter, 1);
pkClassAddMethod(vm, cls_dummy, "@setter", _dummySetter, 2);
pkClassAddMethod(vm, cls_dummy, "==", _dummyEq, 1);
pkClassAddMethod(vm, cls_dummy, ">", _dummyGt, 1);
pkClassAddMethod(vm, cls_dummy, "a_method", _dummyMethod, 2);
pkReleaseHandle(vm, cls_dummy);
pkRegisterModule(vm, dummy);
pkReleaseHandle(vm, dummy);
}

View File

@ -1,12 +1,6 @@
// Language features.
- Closure (literal function are would become closure).
- Utf8 support.
- Method
Reference: CPython
See: https://github.com/python/cpython/blob/6db2db91b96aaa1270c200ec931a2250fe2799c7/Python/ceval.c#L4344-L4380
Note: consider dynamic attribute for instances like python and make the variables defined at the class level
are static.
- Make assert a keyword and disable it on release build.
// To implement.

View File

@ -1559,7 +1559,7 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence
/* TK_PIPE */ { NULL, exprBinaryOp, PREC_BITWISE_OR },
/* TK_CARET */ { NULL, exprBinaryOp, PREC_BITWISE_XOR },
/* TK_ARROW */ NO_RULE,
/* TK_PLUS */ { NULL, exprBinaryOp, PREC_TERM },
/* TK_PLUS */ { exprUnaryOp, exprBinaryOp, PREC_TERM },
/* TK_MINUS */ { exprUnaryOp, exprBinaryOp, PREC_TERM },
/* TK_STAR */ { NULL, exprBinaryOp, PREC_FACTOR },
/* TK_FSLASH */ { NULL, exprBinaryOp, PREC_FACTOR },
@ -1920,26 +1920,32 @@ static void exprBinaryOp(Compiler* compiler) {
skipNewLines(compiler);
parsePrecedence(compiler, (Precedence)(getRule(op)->precedence + 1));
// Emits the opcode and 0 (means false) as inplace operation.
#define EMIT_BINARY_OP_INPLACE(opcode)\
do { emitOpcode(compiler, opcode); emitByte(compiler, 0); } while (false)
switch (op) {
case TK_DOTDOT: emitOpcode(compiler, OP_RANGE); break;
case TK_PERCENT: emitOpcode(compiler, OP_MOD); break;
case TK_AMP: emitOpcode(compiler, OP_BIT_AND); break;
case TK_PIPE: emitOpcode(compiler, OP_BIT_OR); break;
case TK_CARET: emitOpcode(compiler, OP_BIT_XOR); break;
case TK_PLUS: emitOpcode(compiler, OP_ADD); break;
case TK_MINUS: emitOpcode(compiler, OP_SUBTRACT); break;
case TK_STAR: emitOpcode(compiler, OP_MULTIPLY); break;
case TK_FSLASH: emitOpcode(compiler, OP_DIVIDE); break;
case TK_GT: emitOpcode(compiler, OP_GT); break;
case TK_LT: emitOpcode(compiler, OP_LT); break;
case TK_EQEQ: emitOpcode(compiler, OP_EQEQ); break;
case TK_NOTEQ: emitOpcode(compiler, OP_NOTEQ); break;
case TK_GTEQ: emitOpcode(compiler, OP_GTEQ); break;
case TK_LTEQ: emitOpcode(compiler, OP_LTEQ); break;
case TK_SRIGHT: emitOpcode(compiler, OP_BIT_RSHIFT); break;
case TK_SLEFT: emitOpcode(compiler, OP_BIT_LSHIFT); break;
case TK_IN: emitOpcode(compiler, OP_IN); break;
case TK_IS: emitOpcode(compiler, OP_IS); break;
case TK_DOTDOT: emitOpcode(compiler, OP_RANGE); break;
case TK_PERCENT: EMIT_BINARY_OP_INPLACE(OP_MOD); break;
case TK_PLUS: EMIT_BINARY_OP_INPLACE(OP_ADD); break;
case TK_MINUS: EMIT_BINARY_OP_INPLACE(OP_SUBTRACT); break;
case TK_STAR: EMIT_BINARY_OP_INPLACE(OP_MULTIPLY); break;
case TK_FSLASH: EMIT_BINARY_OP_INPLACE(OP_DIVIDE); break;
case TK_AMP: EMIT_BINARY_OP_INPLACE(OP_BIT_AND); break;
case TK_PIPE: EMIT_BINARY_OP_INPLACE(OP_BIT_OR); break;
case TK_CARET: EMIT_BINARY_OP_INPLACE(OP_BIT_XOR); break;
case TK_SRIGHT: EMIT_BINARY_OP_INPLACE(OP_BIT_RSHIFT); break;
case TK_SLEFT: EMIT_BINARY_OP_INPLACE(OP_BIT_LSHIFT); break;
#undef EMIT_BINARY_OP_INPLACE
case TK_GT: emitOpcode(compiler, OP_GT); break;
case TK_LT: emitOpcode(compiler, OP_LT); break;
case TK_EQEQ: emitOpcode(compiler, OP_EQEQ); break;
case TK_NOTEQ: emitOpcode(compiler, OP_NOTEQ); break;
case TK_GTEQ: emitOpcode(compiler, OP_GTEQ); break;
case TK_LTEQ: emitOpcode(compiler, OP_LTEQ); break;
case TK_IN: emitOpcode(compiler, OP_IN); break;
case TK_IS: emitOpcode(compiler, OP_IS); break;
default:
UNREACHABLE();
}
@ -1952,6 +1958,7 @@ static void exprUnaryOp(Compiler* compiler) {
switch (op) {
case TK_TILD: emitOpcode(compiler, OP_BIT_NOT); break;
case TK_PLUS: emitOpcode(compiler, OP_POSITIVE); break;
case TK_MINUS: emitOpcode(compiler, OP_NEGATIVE); break;
case TK_NOT: emitOpcode(compiler, OP_NOT); break;
default:
@ -2361,20 +2368,25 @@ static void emitLoopJump(Compiler* compiler) {
}
static void emitAssignedOp(Compiler* compiler, TokenType assignment) {
// Emits the opcode and 1 (means true) as inplace operation.
#define EMIT_BINARY_OP_INPLACE(opcode)\
do { emitOpcode(compiler, opcode); emitByte(compiler, 1); } while (false)
switch (assignment) {
case TK_PLUSEQ: emitOpcode(compiler, OP_ADD); break;
case TK_MINUSEQ: emitOpcode(compiler, OP_SUBTRACT); break;
case TK_STAREQ: emitOpcode(compiler, OP_MULTIPLY); break;
case TK_DIVEQ: emitOpcode(compiler, OP_DIVIDE); break;
case TK_MODEQ: emitOpcode(compiler, OP_MOD); break;
case TK_ANDEQ: emitOpcode(compiler, OP_BIT_AND); break;
case TK_OREQ: emitOpcode(compiler, OP_BIT_OR); break;
case TK_XOREQ: emitOpcode(compiler, OP_BIT_XOR); break;
case TK_SRIGHTEQ: emitOpcode(compiler, OP_BIT_RSHIFT); break;
case TK_SLEFTEQ: emitOpcode(compiler, OP_BIT_LSHIFT); break;
case TK_PLUSEQ: EMIT_BINARY_OP_INPLACE(OP_ADD); break;
case TK_MINUSEQ: EMIT_BINARY_OP_INPLACE(OP_SUBTRACT); break;
case TK_STAREQ: EMIT_BINARY_OP_INPLACE(OP_MULTIPLY); break;
case TK_DIVEQ: EMIT_BINARY_OP_INPLACE(OP_DIVIDE); break;
case TK_MODEQ: EMIT_BINARY_OP_INPLACE(OP_MOD); break;
case TK_ANDEQ: EMIT_BINARY_OP_INPLACE(OP_BIT_AND); break;
case TK_OREQ: EMIT_BINARY_OP_INPLACE(OP_BIT_OR); break;
case TK_XOREQ: EMIT_BINARY_OP_INPLACE(OP_BIT_XOR); break;
case TK_SRIGHTEQ: EMIT_BINARY_OP_INPLACE(OP_BIT_RSHIFT); break;
case TK_SLEFTEQ: EMIT_BINARY_OP_INPLACE(OP_BIT_LSHIFT); break;
default:
UNREACHABLE();
break;
#undef EMIT_BINARY_OP_INPLACE
}
}
@ -2497,6 +2509,67 @@ static int compileClass(Compiler* compiler) {
return cls_index;
}
// Match operator mathod definition. This will match the operator overloading
// method syntax of ruby.
static bool matchOperatorMethod(Compiler* compiler,
const char** name, int* length, int* argc) {
ASSERT((name != NULL) && (length != NULL) && (argc != NULL), OOPS);
#define _RET(_name, _argc) \
do { \
*name = _name; *length = (int)strlen(_name); \
*argc = _argc; \
return true; \
} while (false)
if (match(compiler, TK_PLUS)) {
if (match(compiler, TK_SELF)) _RET("+self", 0);
else _RET("+", 1);
}
if (match(compiler, TK_MINUS)) {
if (match(compiler, TK_SELF)) _RET("-self", 0);
else _RET("-", 1);
}
if (match(compiler, TK_TILD)){
if (match(compiler, TK_SELF)) _RET("~self", 0);
syntaxError(compiler, compiler->parser.previous,
"Expected keyword self for unary operator definition.");
return false;
}
if (match(compiler, TK_NOT)) {
if (match(compiler, TK_SELF)) _RET("!self", 0);
syntaxError(compiler, compiler->parser.previous,
"Expected keyword self for unary operator definition.");
return false;
}
if (match(compiler, TK_PLUSEQ)) _RET("+=", 1);
if (match(compiler, TK_MINUSEQ)) _RET("-=", 1);
if (match(compiler, TK_STAR)) _RET("*", 1);
if (match(compiler, TK_STAREQ)) _RET("*=", 1);
if (match(compiler, TK_FSLASH)) _RET("/", 1);
if (match(compiler, TK_DIVEQ)) _RET("/=", 1);
if (match(compiler, TK_PERCENT)) _RET("%", 1);
if (match(compiler, TK_MODEQ)) _RET("%=", 1);
if (match(compiler, TK_AMP)) _RET("&", 1);
if (match(compiler, TK_ANDEQ)) _RET("&=", 1);
if (match(compiler, TK_PIPE)) _RET("|", 1);
if (match(compiler, TK_OREQ)) _RET("|=", 1);
if (match(compiler, TK_CARET)) _RET("^", 1);
if (match(compiler, TK_XOREQ)) _RET("^=", 1);
if (match(compiler, TK_SLEFT)) _RET("<<", 1);
if (match(compiler, TK_SLEFTEQ)) _RET("<<=", 1);
if (match(compiler, TK_SRIGHT)) _RET(">>", 1);
if (match(compiler, TK_SRIGHTEQ)) _RET(">>=", 1);
if (match(compiler, TK_EQEQ)) _RET("==", 1);
if (match(compiler, TK_GT)) _RET(">", 1);
if (match(compiler, TK_LT)) _RET("<", 1);
if (match(compiler, TK_DOTDOT)) _RET("..", 1);
if (match(compiler, TK_IN)) _RET("in", 1);
return false;
#undef _RET
}
// Compile a function, if it's a literal function after this call a closure of
// the function will be at the stack top, toplevel functions will be assigned
// to a global variable and popped, and methods will be bind to the class and
@ -2506,16 +2579,32 @@ static void compileFunction(Compiler* compiler, FuncType fn_type) {
const char* name;
int name_length;
// If it's an operator method the bellow value will set to a positive value
// (the argc of the method) it requires to throw a compile time error.
int operator_argc = -2;
if (fn_type != FUNC_LITERAL) {
consume(compiler, TK_NAME, "Expected a function name.");
name = compiler->parser.previous.start;
name_length = compiler->parser.previous.length;
if (match(compiler, TK_NAME)) {
name = compiler->parser.previous.start;
name_length = compiler->parser.previous.length;
} else if (fn_type == FUNC_METHOD &&
matchOperatorMethod(compiler, &name, &name_length, &operator_argc)) {
// Check if any error has been set by operator definition.
} else if (!compiler->parser.has_syntax_error) {
syntaxError(compiler, compiler->parser.previous,
"Expected a function name.");
}
} else {
name = LITERAL_FN_NAME;
name_length = (int)strlen(name);
}
if (compiler->parser.has_syntax_error) return;
int fn_index;
Function* func = newFunction(compiler->parser.vm, name, name_length,
compiler->module, false, NULL, &fn_index);
@ -2532,7 +2621,7 @@ static void compileFunction(Compiler* compiler, FuncType fn_type) {
global_index = compilerAddVariable(compiler, name, name_length, name_line);
}
if (fn_type == FUNC_METHOD && strncmp(name, "_init", name_length) == 0) {
if (fn_type == FUNC_METHOD && strncmp(name, CTOR_NAME, name_length) == 0) {
fn_type = FUNC_CONSTRUCTOR;
}
@ -2576,6 +2665,11 @@ static void compileFunction(Compiler* compiler, FuncType fn_type) {
consume(compiler, TK_RPARAN, "Expected ')' after parameter list.");
}
if (operator_argc >= 0 && argc != operator_argc) {
semanticError(compiler, compiler->parser.previous,
"Expected exactly %d parameters.", operator_argc);
}
func->arity = argc;
compilerChangeStack(compiler, argc);

View File

@ -153,6 +153,81 @@ void initializeCore(PKVM* vm) {
initializePrimitiveClasses(vm);
}
/*****************************************************************************/
/* INTERNAL FUNCTIONS */
/*****************************************************************************/
// Returns the string value of the variable, a wrapper of toString() function
// but for instances it'll try to calll "_to_string" function and on error
// it'll return NULL.
static inline String* _varToString(PKVM* vm, Var self) {
if (IS_OBJ_TYPE(self, OBJ_INST)) {
// The closure is retrieved from [self] thus, it doesn't need to be push
// on the VM's temp references (since [self] should already be protected
// from GC).
Closure* closure = NULL;
String* name = newString(vm, LITS__str); // TODO: static vm string?.
vmPushTempRef(vm, &name->_super); // method.
bool has_method = hasMethod(vm, self, name, &closure);
vmPopTempRef(vm); // method.
if (has_method) {
Var ret = VAR_NULL;
PkResult result = vmCallMethod(vm, self, closure, 0, NULL, &ret);
if (result != PK_RESULT_SUCCESS) return NULL;
if (!IS_OBJ_TYPE(ret, OBJ_STRING)) {
VM_SET_ERROR(vm, newString(vm, "method " LITS__str " returned "
"non-string type."));
return NULL;
}
return (String*)AS_OBJ(ret);
}
// If we reached here, it doesn't have a to string override. just
// "fall throught" and call 'toString()' bellow.
}
return toString(vm, self);
}
// Calls a unary operator overload method. If the method does not exists it'll
// return false, otherwise it'll call the method and return true. If any error
// occures it'll set an error.
static inline bool _callUnaryOpMethod(PKVM* vm, Var self,
const char* method_name, Var* ret) {
Closure* closure = NULL;
String* name = newString(vm, method_name);
vmPushTempRef(vm, &name->_super); // method.
bool has_method = hasMethod(vm, self, name, &closure);
vmPopTempRef(vm); // method.
if (!has_method) return false;
vmCallMethod(vm, self, closure, 0, NULL, ret);
return true;
}
// Calls a binary operator overload method. If the method does not exists it'll
// return false, otherwise it'll call the method and return true. If any error
// occures it'll set an error.
static inline bool _callBinaryOpMethod(PKVM* vm, Var self, Var other,
const char* method_name, Var* ret) {
Closure* closure = NULL;
String* name = newString(vm, method_name);
vmPushTempRef(vm, &name->_super); // method.
bool has_method = hasMethod(vm, self, name, &closure);
vmPopTempRef(vm); // method.
if (!has_method) return false;
vmCallMethod(vm, self, closure, 1, &other, ret);
return true;
}
/*****************************************************************************/
/* CORE BUILTIN FUNCTIONS */
/*****************************************************************************/
@ -215,13 +290,16 @@ DEF(coreAssert,
if (argc == 2) {
if (AS_OBJ(ARG(2))->type != OBJ_STRING) {
msg = toString(vm, ARG(2));
msg = _varToString(vm, ARG(2));
if (msg == NULL) return; //< Error at _to_string override.
} else {
msg = (String*)AS_OBJ(ARG(2));
}
vmPushTempRef(vm, &msg->_super);
vmPushTempRef(vm, &msg->_super); // msg.
VM_SET_ERROR(vm, stringFormat(vm, "Assertion failed: '@'.", msg));
vmPopTempRef(vm);
vmPopTempRef(vm); // msg.
} else {
VM_SET_ERROR(vm, newString(vm, "Assertion failed."));
}
@ -302,10 +380,12 @@ DEF(coreYield,
}
DEF(coreToString,
"to_string(value:var) -> string\n"
"str(value:var) -> string\n"
"Returns the string representation of the value.") {
RET(VAR_OBJ(toString(vm, ARG(1))));
String* str = _varToString(vm, ARG(1));
if (str == NULL) RET(VAR_NULL);
RET(VAR_OBJ(str));
}
DEF(coreChr,
@ -348,7 +428,9 @@ DEF(corePrint,
for (int i = 1; i <= ARGC; i++) {
if (i != 1) vm->config.stdout_write(vm, " ");
vm->config.stdout_write(vm, toString(vm, ARG(i))->data);
String* str = _varToString(vm, ARG(i));
if (str == NULL) RET(VAR_NULL);
vm->config.stdout_write(vm, str->data);
}
vm->config.stdout_write(vm, "\n");
@ -368,7 +450,9 @@ DEF(coreInput,
if (vm->config.stdin_read == NULL) return;
if (argc == 1) {
vm->config.stdout_write(vm, toString(vm, ARG(1))->data);
String* str = _varToString(vm, ARG(1));
if (str == NULL) RET(VAR_NULL);
vm->config.stdout_write(vm, str->data);
}
PkStringPtr result = vm->config.stdin_read(vm);
@ -452,9 +536,10 @@ DEF(coreListJoin,
pkByteBufferInit(&buff);
for (uint32_t i = 0; i < list->elements.count; i++) {
String* elem = toString(vm, list->elements.data[i]);
vmPushTempRef(vm, &elem->_super); // elem
pkByteBufferAddString(&buff, vm, elem->data, elem->length);
String* str = _varToString(vm, list->elements.data[i]);
if (str == NULL) RET(VAR_NULL);
vmPushTempRef(vm, &str->_super); // elem
pkByteBufferAddString(&buff, vm, str->data, str->length);
vmPopTempRef(vm); // elem
}
@ -500,7 +585,7 @@ static void initializeBuiltinFunctions(PKVM* vm) {
INITIALIZE_BUILTIN_FN("bin", coreBin, 1);
INITIALIZE_BUILTIN_FN("hex", coreHex, 1);
INITIALIZE_BUILTIN_FN("yield", coreYield, -1);
INITIALIZE_BUILTIN_FN("to_string", coreToString, 1);
INITIALIZE_BUILTIN_FN("str", coreToString, 1);
INITIALIZE_BUILTIN_FN("chr", coreChr, 1);
INITIALIZE_BUILTIN_FN("ord", coreOrd, 1);
INITIALIZE_BUILTIN_FN("print", corePrint, -1);
@ -625,7 +710,8 @@ DEF(stdLangWrite,
if (IS_OBJ_TYPE(arg, OBJ_STRING)) {
str = (String*)AS_OBJ(arg);
} else {
str = toString(vm, arg);
str = _varToString(vm, ARG(1));
if (str == NULL) RET(VAR_NULL);
}
vm->config.stdout_write(vm, str->data);
@ -679,7 +765,9 @@ static void _ctorString(PKVM* vm) {
RET(VAR_OBJ(newStringLength(vm, NULL, 0)));
return;
}
RET(VAR_OBJ(toString(vm, ARG(1))));
String* str = _varToString(vm, ARG(1));
if (str == NULL) RET(VAR_NULL);
RET(VAR_OBJ(str));
}
static void _ctorList(PKVM* vm) {
@ -880,229 +968,271 @@ Class* getClass(PKVM* vm, Var instance) {
return inst->cls;
}
Var getMethod(PKVM* vm, Var self, String* name, bool* is_method) {
bool hasMethod(PKVM* vm, Var self, String* name, Closure** _method) {
Class* cls = getClass(vm, self);
ASSERT(cls != NULL, OOPS);
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 (is_method) *is_method = true;
return VAR_OBJ(method);
Closure* method_ = cls_->methods.data[i];
if (IS_CSTR_EQ(name, method_->fn->name, name->length)) {
if (_method) *_method = method_;
return true;
}
}
cls_ = cls_->super_class;
} while (cls_ != NULL);
return false;
}
Var getMethod(PKVM* vm, Var self, String* name, bool* is_method) {
Closure* method;
if (hasMethod(vm, self, name, &method)) {
if (is_method) *is_method = true;
return VAR_OBJ(method);
}
// If the attribute not found it'll set an error.
if (is_method) *is_method = false;
return varGetAttrib(vm, self, name);
}
#define UNSUPPORTED_OPERAND_TYPES(op) \
VM_SET_ERROR(vm, stringFormat(vm, "Unsupported operand types for " \
#define UNSUPPORTED_UNARY_OP(op) \
VM_SET_ERROR(vm, stringFormat(vm, "Unsupported operand ($) for " \
"unary operator " op ".", varTypeName(v)))
#define UNSUPPORTED_BINARY_OP(op) \
VM_SET_ERROR(vm, stringFormat(vm, "Unsupported operand types for " \
"operator '" op "' $ and $", varTypeName(v1), varTypeName(v2)))
#define RIGHT_OPERAND "Right operand"
Var varAdd(PKVM* vm, Var v1, Var v2) {
double d1, d2;
#define CHECK_NUMERIC_OP(op) \
do { \
double n1, n2; \
if (isNumeric(v1, &n1)) { \
if (validateNumeric(vm, v2, &n2, RIGHT_OPERAND)) { \
return VAR_NUM(n1 op n2); \
} \
return VAR_NULL; \
} \
} while (false)
if (isNumeric(v1, &d1)) {
if (validateNumeric(vm, v2, &d2, RIGHT_OPERAND)) {
return VAR_NUM(d1 + d2);
}
return VAR_NULL;
}
#define CHECK_BITWISE_OP(op) \
do { \
int64_t i1, i2; \
if (isInteger(v1, &i1)) { \
if (validateInteger(vm, v2, &i2, RIGHT_OPERAND)) { \
return VAR_NUM((double)(i1 op i2)); \
} \
return VAR_NULL; \
} \
} while (false)
if (IS_OBJ(v1) && IS_OBJ(v2)) {
Object *o1 = AS_OBJ(v1), *o2 = AS_OBJ(v2);
#define CHECK_INST_UNARY_OP(name) \
do { \
if (IS_OBJ_TYPE(v, OBJ_INST)) { \
Var result; \
if (_callUnaryOpMethod(vm, v, name, &result)) { \
return result; \
} \
} \
} while (false)
#define CHECK_INST_BINARY_OP(name) \
do { \
if (IS_OBJ_TYPE(v1, OBJ_INST)) { \
Var result; \
if (inplace) { \
if (_callBinaryOpMethod(vm, v1, v2, name "=", &result)) { \
return result; \
} \
} \
if (_callBinaryOpMethod(vm, v1, v2, name, &result)) { \
return result; \
} \
} \
} while(false)
Var varPositive(PKVM* vm, Var v) {
double n; if (isNumeric(v, &n)) return v;
CHECK_INST_UNARY_OP("+self");
UNSUPPORTED_UNARY_OP("unary +");
return VAR_NULL;
}
Var varNegative(PKVM* vm, Var v) {
double n; if (isNumeric(v, &n)) return VAR_NUM(-AS_NUM(v));
CHECK_INST_UNARY_OP("-self");
UNSUPPORTED_UNARY_OP("unary -");
return VAR_NULL;
}
Var varNot(PKVM* vm, Var v) {
CHECK_INST_UNARY_OP("!self");
return VAR_BOOL(!toBool(v));
}
Var varBitNot(PKVM* vm, Var v) {
int64_t i;
if (isInteger(v, &i)) return VAR_NUM((double)(~i));
CHECK_INST_UNARY_OP("~self");
UNSUPPORTED_UNARY_OP("unary ~");
return VAR_NULL;
}
Var varAdd(PKVM* vm, Var v1, Var v2, bool inplace) {
CHECK_NUMERIC_OP(+);
if (IS_OBJ(v1)) {
Object *o1 = AS_OBJ(v1);
switch (o1->type) {
case OBJ_STRING:
{
case OBJ_STRING: {
if (!IS_OBJ(v2)) break;
Object* o2 = AS_OBJ(v2);
if (o2->type == OBJ_STRING) {
return VAR_OBJ(stringJoin(vm, (String*)o1, (String*)o2));
}
} break;
case OBJ_LIST:
{
case OBJ_LIST: {
if (!IS_OBJ(v2)) break;
Object* o2 = AS_OBJ(v2);
if (o2->type == OBJ_LIST) {
return VAR_OBJ(listJoin(vm, (List*)o1, (List*)o2));
if (inplace) {
pkVarBufferConcat(&((List*)o1)->elements, vm,
&((List*)o2)->elements);
return v1;
} else {
return VAR_OBJ(listAdd(vm, (List*)o1, (List*)o2));
}
}
} break;
}
}
UNSUPPORTED_OPERAND_TYPES("+");
CHECK_INST_BINARY_OP("+");
UNSUPPORTED_BINARY_OP("+");
return VAR_NULL;
}
Var varSubtract(PKVM* vm, Var v1, Var v2) {
double d1, d2;
if (isNumeric(v1, &d1)) {
if (validateNumeric(vm, v2, &d2, RIGHT_OPERAND)) {
return VAR_NUM(d1 - d2);
}
return VAR_NULL;
}
UNSUPPORTED_OPERAND_TYPES("-");
return VAR_NULL;
}
Var varMultiply(PKVM* vm, Var v1, Var v2) {
double d1, d2;
if (isNumeric(v1, &d1)) {
if (validateNumeric(vm, v2, &d2, RIGHT_OPERAND)) {
return VAR_NUM(d1 * d2);
}
return VAR_NULL;
}
UNSUPPORTED_OPERAND_TYPES("*");
return VAR_NULL;
}
Var varDivide(PKVM* vm, Var v1, Var v2) {
double d1, d2;
if (isNumeric(v1, &d1)) {
if (validateNumeric(vm, v2, &d2, RIGHT_OPERAND)) {
return VAR_NUM(d1 / d2);
}
return VAR_NULL;
}
UNSUPPORTED_OPERAND_TYPES("/");
return VAR_NULL;
}
Var varModulo(PKVM* vm, Var v1, Var v2) {
double d1, d2;
if (isNumeric(v1, &d1)) {
if (validateNumeric(vm, v2, &d2, RIGHT_OPERAND)) {
return VAR_NUM(fmod(d1, d2));
Var varModulo(PKVM* vm, Var v1, Var v2, bool inplace) {
double n1, n2;
if (isNumeric(v1, &n1)) {
if (validateNumeric(vm, v2, &n2, RIGHT_OPERAND)) {
return VAR_NUM(fmod(n1, n2));
}
return VAR_NULL;
}
if (IS_OBJ_TYPE(v1, OBJ_STRING)) {
//const String* str = (const String*)AS_OBJ(v1);
TODO; // "fmt" % v2.
}
UNSUPPORTED_OPERAND_TYPES("%");
CHECK_INST_BINARY_OP("%");
UNSUPPORTED_BINARY_OP("%");
return VAR_NULL;
}
Var varBitAnd(PKVM* vm, Var v1, Var v2) {
int64_t i1, i2;
// TODO: the bellow function definitions can be written as macros.
if (isInteger(v1, &i1)) {
if (validateInteger(vm, v2, &i2, RIGHT_OPERAND)) {
return VAR_NUM((double)(i1 & i2));
}
return VAR_NULL;
}
UNSUPPORTED_OPERAND_TYPES("&");
Var varSubtract(PKVM* vm, Var v1, Var v2, bool inplace) {
CHECK_NUMERIC_OP(-);
CHECK_INST_BINARY_OP("-");
UNSUPPORTED_BINARY_OP("-");
return VAR_NULL;
}
Var varBitOr(PKVM* vm, Var v1, Var v2) {
int64_t i1, i2;
if (isInteger(v1, &i1)) {
if (validateInteger(vm, v2, &i2, RIGHT_OPERAND)) {
return VAR_NUM((double)(i1 | i2));
}
return VAR_NULL;
}
UNSUPPORTED_OPERAND_TYPES("|");
Var varMultiply(PKVM* vm, Var v1, Var v2, bool inplace) {
CHECK_NUMERIC_OP(*);
CHECK_INST_BINARY_OP("*");
UNSUPPORTED_BINARY_OP("*");
return VAR_NULL;
}
Var varBitXor(PKVM* vm, Var v1, Var v2) {
int64_t i1, i2;
if (isInteger(v1, &i1)) {
if (validateInteger(vm, v2, &i2, RIGHT_OPERAND)) {
return VAR_NUM((double)(i1 ^ i2));
}
return VAR_NULL;
}
UNSUPPORTED_OPERAND_TYPES("^");
Var varDivide(PKVM* vm, Var v1, Var v2, bool inplace) {
CHECK_NUMERIC_OP(/);
CHECK_INST_BINARY_OP("/");
UNSUPPORTED_BINARY_OP("/");
return VAR_NULL;
}
Var varBitLshift(PKVM* vm, Var v1, Var v2) {
int64_t i1, i2;
if (isInteger(v1, &i1)) {
if (validateInteger(vm, v2, &i2, RIGHT_OPERAND)) {
return VAR_NUM((double)(i1 << i2));
}
return VAR_NULL;
}
UNSUPPORTED_OPERAND_TYPES("<<");
Var varBitAnd(PKVM* vm, Var v1, Var v2, bool inplace) {
CHECK_BITWISE_OP(&);
CHECK_INST_BINARY_OP("&");
UNSUPPORTED_BINARY_OP("&");
return VAR_NULL;
}
Var varBitRshift(PKVM* vm, Var v1, Var v2) {
int64_t i1, i2;
if (isInteger(v1, &i1)) {
if (validateInteger(vm, v2, &i2, RIGHT_OPERAND)) {
return VAR_NUM((double)(i1 >> i2));
}
return VAR_NULL;
}
UNSUPPORTED_OPERAND_TYPES(">>");
Var varBitOr(PKVM* vm, Var v1, Var v2, bool inplace) {
CHECK_BITWISE_OP(|);
CHECK_INST_BINARY_OP("|");
UNSUPPORTED_BINARY_OP("|");
return VAR_NULL;
}
Var varBitNot(PKVM* vm, Var v) {
int64_t i;
if (!validateInteger(vm, v, &i, "Unary operand")) return VAR_NULL;
return VAR_NUM((double)(~i));
Var varBitXor(PKVM* vm, Var v1, Var v2, bool inplace) {
CHECK_BITWISE_OP(^);
CHECK_INST_BINARY_OP("^");
UNSUPPORTED_BINARY_OP("^");
return VAR_NULL;
}
bool varGreater(Var v1, Var v2) {
double d1, d2;
if (isNumeric(v1, &d1) && isNumeric(v2, &d2)) {
return d1 > d2;
}
TODO;
return false;
Var varBitLshift(PKVM* vm, Var v1, Var v2, bool inplace) {
CHECK_BITWISE_OP(<<);
CHECK_INST_BINARY_OP("<<");
UNSUPPORTED_BINARY_OP("<<");
return VAR_NULL;
}
bool varLesser(Var v1, Var v2) {
double d1, d2;
Var varBitRshift(PKVM* vm, Var v1, Var v2, bool inplace) {
CHECK_BITWISE_OP(>>);
CHECK_INST_BINARY_OP(">>");
UNSUPPORTED_BINARY_OP(">>");
return VAR_NULL;
}
if (isNumeric(v1, &d1) && isNumeric(v2, &d2)) {
return d1 < d2;
Var varEqals(PKVM* vm, Var v1, Var v2) {
const bool inplace = false;
CHECK_INST_BINARY_OP("==");
return VAR_BOOL(isValuesEqual(v1, v2));
}
Var varGreater(PKVM* vm, Var v1, Var v2) {
CHECK_NUMERIC_OP(>);
const bool inplace = false;
CHECK_INST_BINARY_OP(">");
UNSUPPORTED_BINARY_OP(">");
return VAR_NULL;
}
Var varLesser(PKVM* vm, Var v1, Var v2) {
CHECK_NUMERIC_OP(<);
const bool inplace = false;
CHECK_INST_BINARY_OP("<");
UNSUPPORTED_BINARY_OP("<");
return VAR_NULL;
}
Var varOpRange(PKVM* vm, Var v1, Var v2) {
if (IS_NUM(v1) && IS_NUM(v2)) {
return VAR_OBJ(newRange(vm, AS_NUM(v1), AS_NUM(v2)));
}
TODO;
return false;
const bool inplace = false;
CHECK_INST_BINARY_OP("..");
UNSUPPORTED_BINARY_OP("..");
return VAR_NULL;
}
#undef RIGHT_OPERAND
#undef UNSUPPORTED_OPERAND_TYPES
#undef CHECK_NUMERIC_OP
#undef CHECK_BITWISE_OP
#undef UNSUPPORTED_UNARY_OP
#undef UNSUPPORTED_BINARY_OP
bool varContains(PKVM* vm, Var elem, Var container) {
if (!IS_OBJ(container)) {
@ -1140,6 +1270,13 @@ bool varContains(PKVM* vm, Var elem, Var container) {
} break;
}
#define v1 container
#define v2 elem
const bool inplace = false;
CHECK_INST_BINARY_OP("in");
#undef v1
#undef v2
VM_SET_ERROR(vm, stringFormat(vm, "Argument of type $ is not iterable.",
varTypeName(container)));
return VAR_NULL;
@ -1276,10 +1413,28 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
break;
case OBJ_INST: {
Var value;
if (instGetAttrib(vm, (Instance*)obj, attrib, &value)) {
return value;
Instance* inst = (Instance*)obj;
Var value = VAR_NULL;
if (inst->native != NULL) {
Closure* getter;
// TODO: static vm string?
String* getter_name = newString(vm, GETTER_NAME);
vmPushTempRef(vm, &getter_name->_super); // getter_name.
bool has_getter = hasMethod(vm, on, getter_name, &getter);
vmPopTempRef(vm); // getter_name.
if (has_getter) {
Var attrib_name = VAR_OBJ(attrib);
vmCallMethod(vm, on, getter, 1, &attrib_name, &value);
return value; // If any error occure, it was already set.
}
}
value = mapGet(inst->attribs, VAR_OBJ(attrib));
if (!IS_UNDEF(value)) return value;
} break;
}
@ -1371,16 +1526,32 @@ do { \
return;
case OBJ_INST: {
if (!instSetAttrib(vm, (Instance*)obj, attrib, value)) {
// If we has error by now, that means the set value type is
// incompatible. No need for us to set an other error, just return.
if (VM_HAS_ERROR(vm)) return;
ERR_NO_ATTRIB(vm, on, attrib);
Instance* inst = (Instance*)obj;
if (inst->native != NULL) {
Closure* setter;
// TODO: static vm string?
String* setter_name = newString(vm, SETTER_NAME);
vmPushTempRef(vm, &setter_name->_super); // setter_name.
bool has_setter = hasMethod(vm, VAR_OBJ(inst), setter_name, &setter);
vmPopTempRef(vm); // setter_name.
if (has_setter) {
// FIXME:
// Once we retreive values from directly the stack we can pass the
// args pointer, pointing in the VM stack, instead of creating a temp
// array as bellow.
Var args[2] = { VAR_OBJ(attrib), value };
vmCallMethod(vm, VAR_OBJ(inst), setter, 2, args, NULL);
return; // If any error occure, it was already set.
}
}
// If we reached here, that means the attribute exists and we have
// updated the value.
mapSet(vm, inst->attribs, VAR_OBJ(attrib), value);
return;
} break;
}
@ -1429,14 +1600,15 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
Var value = mapGet((Map*)obj, key);
if (IS_UNDEF(value)) {
String* key_str = toString(vm, key);
vmPushTempRef(vm, &key_str->_super);
if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) {
VM_SET_ERROR(vm, stringFormat(vm, "Invalid key '@'.", key_str));
VM_SET_ERROR(vm, stringFormat(vm, "Unhashable key '$'.",
varTypeName(key)));
} else {
VM_SET_ERROR(vm, stringFormat(vm, "Key '@' not exists", key_str));
String* key_repr = toRepr(vm, key);
vmPushTempRef(vm, &key_repr->_super); // key_repr.
VM_SET_ERROR(vm, stringFormat(vm, "Key '@' not exists", key_repr));
vmPopTempRef(vm); // key_repr.
}
vmPopTempRef(vm);
return VAR_NULL;
}
return value;

View File

@ -10,6 +10,35 @@
#include "pk_internal.h"
#include "pk_value.h"
// Literal strings used in various places in pocketlang. For now these are
// defined as macros so that it'll be easier in the future to refactor or
// restructre. The names of the macros are begin with LIST_ and the string.
#define LITS__init "_init"
#define LITS__str "_str"
// Functions, methods, classes and other names which are intrenal / special to
// pocketlang are starts with the following character (ex: @main, @literalFn).
// When importing all (*) from a module, if the name of an entry starts with
// this character it'll be skipped.
#define SPECIAL_NAME_CHAR '@'
// Name of the implicit function for a module. When a module is parsed all of
// it's statements are wrapped around an implicit function with this name.
#define IMPLICIT_MAIN_NAME "@main"
// Name of a literal function. All literal function will have the same name but
// they're uniquely identified by their index in the script's function buffer.
#define LITERAL_FN_NAME "@func"
// Name of a constructor function.
#define CTOR_NAME LITS__init
// Getter/Setter method names used by the native instance to get/ set value.
// Script instance's values doesn't support methods but they use vanila
// '.attrib', '.attrib=' operators.
#define GETTER_NAME "@getter"
#define SETTER_NAME "@setter"
// Initialize core language, builtin function and core libs.
void initializeCore(PKVM* vm);
@ -46,21 +75,33 @@ 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);
Var varAdd(PKVM* vm, Var v1, Var v2); // Returns v1 + v2.
Var varSubtract(PKVM* vm, Var v1, Var v2); // Returns v1 - v2.
Var varMultiply(PKVM* vm, Var v1, Var v2); // Returns v1 * v2.
Var varDivide(PKVM* vm, Var v1, Var v2); // Returns v1 / v2.
Var varModulo(PKVM* vm, Var v1, Var v2); // Returns v1 % v2.
// 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.
bool hasMethod(PKVM* vm, Var self, String* name, Closure** method);
Var varBitAnd(PKVM* vm, Var v1, Var v2); // Returns v1 & v2.
Var varBitOr(PKVM* vm, Var v1, Var v2); // Returns v1 | v2.
Var varBitXor(PKVM* vm, Var v1, Var v2); // Returns v1 ^ v2.
Var varBitLshift(PKVM* vm, Var v1, Var v2); // Returns v1 << v2.
Var varBitRshift(PKVM* vm, Var v1, Var v2); // Returns v1 >> v2.
Var varBitNot(PKVM* vm, Var v); // Returns ~v.
Var varPositive(PKVM* vm, Var v); // Returns +v.
Var varNegative(PKVM* vm, Var v); // Returns -v.
Var varNot(PKVM* vm, Var v); // Returns !v.
Var varBitNot(PKVM* vm, Var v); // Returns ~v.
bool varGreater(Var v1, Var v2); // Returns v1 > v2.
bool varLesser(Var v1, Var v2); // Returns v1 < v2.
Var varAdd(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 + v2.
Var varSubtract(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 - v2.
Var varMultiply(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 * v2.
Var varDivide(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 / v2.
Var varModulo(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 % v2.
Var varBitAnd(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 & v2.
Var varBitOr(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 | v2.
Var varBitXor(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 ^ v2.
Var varBitLshift(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 << v2.
Var varBitRshift(PKVM* vm, Var v1, Var v2, bool inplace); // Returns v1 >> v2.
Var varEqals(PKVM* vm, Var v1, Var v2); // Returns v1 == v2.
Var varGreater(PKVM* vm, Var v1, Var v2); // Returns v1 > v2.
Var varLesser(PKVM* vm, Var v1, Var v2); // Returns v1 < v2.
Var varOpRange(PKVM* vm, Var v1, Var v2); // Returns v1 .. v2.
// Returns [elem] in [container]. Sets an error if the [container] is not an
// iterable.

View File

@ -269,11 +269,17 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
uint32_t* lines = func->fn->oplines.data;
uint32_t line = 1, last_line = 0;
// Either path or name should be valid to a module.
ASSERT(func->owner->path != NULL || func->owner->name != NULL, OOPS);
const char* path = (func->owner->path)
? func->owner->path->data
: func->owner->name->data;
// This will print: Instruction Dump of function 'fn' "path.pk"\n
PRINT("Instruction Dump of function ");
PRINT(func->name);
PRINT(" ");
PRINT(func->owner->path->data);
PRINT(path);
NEWLINE();
while (i < func->fn->opcodes.count) {
@ -588,9 +594,11 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
NO_ARGS();
break;
case OP_POSITIVE:
case OP_NEGATIVE:
case OP_NOT:
case OP_BIT_NOT:
case OP_ADD:
case OP_SUBTRACT:
case OP_MULTIPLY:
@ -601,6 +609,17 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
case OP_BIT_XOR:
case OP_BIT_LSHIFT:
case OP_BIT_RSHIFT:
{
uint8_t inplace = READ_BYTE();
if (inplace == 1) {
PRINT("(inplace)\n");
} else {
PRINT("\n");
ASSERT(inplace == 0, "inplace should be either 0 or 1");
}
break;
}
case OP_EQEQ:
case OP_NOTEQ:
case OP_LT:

View File

@ -68,20 +68,6 @@
// The size of the error message buffer, used ar vsnprintf (since c99) buffer.
#define ERROR_MESSAGE_SIZE 512
// Functions, methods, classes and other names which are intrenal / special to
// pocketlang are starts with the following character (ex: @main, @literalFn).
// When importing all (*) from a module, if the name of an entry starts with
// this character it'll be skipped.
#define SPECIAL_NAME_CHAR '@'
// Name of the implicit function for a module. When a module is parsed all of
// it's statements are wrapped around an implicit function with this name.
#define IMPLICIT_MAIN_NAME "@main"
// Name of a literal function. All literal function will have the same name but
// they're uniquely identified by their index in the script's function buffer.
#define LITERAL_FN_NAME "@func"
/*****************************************************************************/
/* ALLOCATION MACROS */
/*****************************************************************************/

View File

@ -224,22 +224,24 @@ OPCODE(GET_SUBSCRIPT_KEEP, 0, 1)
OPCODE(SET_SUBSCRIPT, 0, -2)
// Pop unary operand and push value.
OPCODE(POSITIVE, 0, 0) //< Negative number value.
OPCODE(NEGATIVE, 0, 0) //< Negative number value.
OPCODE(NOT, 0, 0) //< boolean not.
OPCODE(BIT_NOT, 0, 0) //< bitwise not.
// Pop binary operands and push value.
OPCODE(ADD, 0, -1)
OPCODE(SUBTRACT, 0, -1)
OPCODE(MULTIPLY, 0, -1)
OPCODE(DIVIDE, 0, -1)
OPCODE(MOD, 0, -1)
// for parameter 1 byte is boolean inplace?.
OPCODE(ADD, 1, -1)
OPCODE(SUBTRACT, 1, -1)
OPCODE(MULTIPLY, 1, -1)
OPCODE(DIVIDE, 1, -1)
OPCODE(MOD, 1, -1)
OPCODE(BIT_AND, 0, -1)
OPCODE(BIT_OR, 0, -1)
OPCODE(BIT_XOR, 0, -1)
OPCODE(BIT_LSHIFT, 0, -1)
OPCODE(BIT_RSHIFT, 0, -1)
OPCODE(BIT_AND, 1, -1)
OPCODE(BIT_OR, 1, -1)
OPCODE(BIT_XOR, 1, -1)
OPCODE(BIT_LSHIFT, 1, -1)
OPCODE(BIT_RSHIFT, 1, -1)
OPCODE(EQEQ, 0, -1)
OPCODE(NOTEQ, 0, -1)

View File

@ -207,10 +207,16 @@ void pkClassAddMethod(PKVM* vm, PkHandle* cls,
CHECK_ARG_NULL(fptr);
CHECK_HANDLE_TYPE(cls, OBJ_CLASS);
// TODO:
// Check if the method name is valid, and validate argc for special
// methods (like "@getter", "@call", "+", "-", etc).
Class* class_ = (Class*)AS_OBJ(cls->value);
Function* fn = newFunction(vm, name, (int)strlen(name),
class_->owner, true, NULL, NULL);
fn->arity = arity;
fn->native = fptr;
// No need to push the function to temp references of the VM
// since it's written to the constant pool of the module and the module
@ -218,8 +224,7 @@ void pkClassAddMethod(PKVM* vm, PkHandle* cls,
Closure* method = newClosure(vm, fn);
// FIXME: name "_init" is literal everywhere.
if (strcmp(name, "_init") == 0) {
if (strcmp(name, CTOR_NAME) == 0) {
class_->ctor = method;
} else {
@ -344,6 +349,7 @@ int pkGetArgc(const PKVM* vm) {
}
bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max) {
CHECK_RUNTIME();
ASSERT(min <= max, "invalid argc range (min > max).");
if (argc < min) {

View File

@ -389,6 +389,8 @@ Upvalue* newUpvalue(PKVM* vm, Var* value) {
}
Fiber* newFiber(PKVM* vm, Closure* closure) {
ASSERT(closure->fn->arity >= -1, OOPS);
Fiber* fiber = ALLOCATE(vm, Fiber);
// Not sure why this memset is needed here. If it doesn't then remove it.
@ -404,6 +406,10 @@ Fiber* newFiber(PKVM* vm, Closure* closure) {
// there won't be any locals or temps (which are belongs to the
// native "C" stack).
int stack_size = utilPowerOf2Ceil(closure->fn->arity + 1);
// We need at least 1 stack slot for the return value.
if (stack_size == 0) stack_size++;
fiber->stack = ALLOCATE_ARRAY(vm, Var, stack_size);
fiber->stack_size = stack_size;
fiber->ret = fiber->stack;
@ -697,7 +703,7 @@ Var listRemoveAt(PKVM* vm, List* self, uint32_t index) {
return removed;
}
List* listJoin(PKVM* vm, List* l1, List* l2) {
List* listAdd(PKVM* vm, List* l1, List* l2) {
// Optimize end case.
if (l1->elements.count == 0) return l2;
@ -706,10 +712,10 @@ List* listJoin(PKVM* vm, List* l1, List* l2) {
uint32_t size = l1->elements.count + l2->elements.count;
List* list = newList(vm, size);
vmPushTempRef(vm, &list->_super);
vmPushTempRef(vm, &list->_super); // list.
pkVarBufferConcat(&list->elements, vm, &l1->elements);
pkVarBufferConcat(&list->elements, vm, &l2->elements);
vmPopTempRef(vm);
vmPopTempRef(vm); // list.
return list;
}
@ -1080,38 +1086,6 @@ void moduleAddMain(PKVM* vm, Module* module) {
VAR_OBJ(module->body));
}
bool instGetAttrib(PKVM* vm, Instance* inst, String* attrib, Var* value) {
ASSERT((inst != NULL) && (attrib != NULL) && (value != NULL), OOPS);
if (inst->native != NULL) {
TODO;
}
Var value_ = mapGet(inst->attribs, VAR_OBJ(attrib));
if (IS_UNDEF(value_)) return false;
*value = value_;
return true;
}
bool instSetAttrib(PKVM* vm, Instance* inst, String* attrib, Var value) {
ASSERT((inst != NULL) && (attrib != NULL), OOPS);
if (inst->native != NULL) {
// Try setting the attribute from the native interface, and if success, we
// should return. otherwise the code will "fall through" and set on it's
// dynamic attributes map.
TODO;
// FIXME:
// Only return true if attribute have been set.
return true;
}
mapSet(vm, inst->attribs, VAR_OBJ(attrib), value);
return true;
}
/*****************************************************************************/
/* UTILITY FUNCTIONS */
/*****************************************************************************/
@ -1222,6 +1196,11 @@ bool isValuesSame(Var v1, Var v2) {
bool isValuesEqual(Var v1, Var v2) {
if (isValuesSame(v1, v2)) return true;
// +0 and -0 have different bit value representations.
if (IS_NUM(v1) && IS_NUM(v2)) {
return AS_NUM(v1) == AS_NUM(v2);
}
// If we reach here only heap allocated objects could be compared.
if (!IS_OBJ(v1) || !IS_OBJ(v2)) return false;

View File

@ -665,7 +665,7 @@ void listInsert(PKVM* vm, List* self, uint32_t index, Var value);
Var listRemoveAt(PKVM* vm, List* self, uint32_t index);
// Create a new list by joining the 2 given list and return the result.
List* listJoin(PKVM* vm, List* l1, List* l2);
List* listAdd(PKVM* vm, List* l1, List* l2);
// Returns the value for the [key] in the map. If key not exists return
// VAR_UNDEFINED.
@ -718,16 +718,6 @@ void moduleSetGlobal(Module* module, int index, Var value);
// function.
void moduleAddMain(PKVM* vm, Module* module);
// Get the attribut from the instance and set it [value]. On success return
// true, if the attribute not exists it'll return false but won't set an error.
bool instGetAttrib(PKVM* vm, Instance* inst, String* attrib, Var* value);
// Set the attribute to the instance and return true on success, if the
// attribute doesn't exists it'll return false but if the [value] type is
// incompatible, this will set an error to the VM, which you can check with
// VM_HAS_ERROR() macro function.
bool instSetAttrib(PKVM* vm, Instance* inst, String* attrib, Var value);
// Release all the object owned by the [self] including itself.
void freeObject(PKVM* vm, Object* self);

View File

@ -173,15 +173,10 @@ bool vmPrepareFiber(PKVM* vm, Fiber* fiber, int argc, Var* argv) {
ASSERT(fiber->stack != NULL && fiber->sp == fiber->stack + 1, OOPS);
ASSERT(fiber->ret == fiber->stack, OOPS);
ASSERT((fiber->stack + fiber->stack_size) - fiber->sp >= argc, OOPS);
// Pass the function arguments.
// Assert we have the first frame (to push the arguments). And assert we have
// enough stack space for parameters.
ASSERT(fiber->frame_count == 1, OOPS);
ASSERT(fiber->frames[0].rbp == fiber->ret, OOPS);
ASSERT((fiber->stack + fiber->stack_size) - fiber->sp >= argc, OOPS);
// ARG1 is fiber, function arguments are ARG(2), ARG(3), ... ARG(argc).
// And ret[0] is the return value, parameters starts at ret[1], ...
for (int i = 0; i < argc; i++) {
@ -189,6 +184,18 @@ bool vmPrepareFiber(PKVM* vm, Fiber* fiber, int argc, Var* argv) {
}
fiber->sp += argc; // Parameters.
// Native functions doesn't own a stack frame so, we're done here.
if (fiber->closure->fn->is_native) return true;
// Assert we have the first frame (to push the arguments). And assert we have
// enough stack space for parameters.
ASSERT(fiber->frame_count == 1, OOPS);
ASSERT(fiber->frames[0].rbp == fiber->ret, OOPS);
// Capture self.
fiber->frames[0].self = fiber->self;
fiber->self = VAR_UNDEFINED;
// On success return true.
return true;
}
@ -245,18 +252,39 @@ void vmYieldFiber(PKVM* vm, Var* value) {
vm->fiber = caller;
}
PkResult vmRunFunction(PKVM* vm, Closure* fn, int argc, Var* argv, Var* ret) {
PkResult vmCallMethod(PKVM* vm, Var self, Closure* fn,
int argc, Var* argv, Var* ret) {
ASSERT(argc >= 0, "argc cannot be negative.");
ASSERT(argc == 0 || argv != NULL, "argv was NULL when argc > 0.");
Fiber* fiber = newFiber(vm, fn);
fiber->self = self;
vmPushTempRef(vm, &fiber->_super); // fiber.
vmPrepareFiber(vm, fiber, argc, argv);
bool success = vmPrepareFiber(vm, fiber, argc, argv);
vmPopTempRef(vm); // fiber.
if (!success) return PK_RESULT_RUNTIME_ERROR;
PkResult result;
Fiber* last = vm->fiber;
if (last != NULL) vmPushTempRef(vm, &last->_super); // last.
PkResult result = vmRunFiber(vm, fiber);
{
if (fiber->closure->fn->is_native) {
ASSERT(fiber->closure->fn->native != NULL, "Native function was NULL");
vm->fiber = fiber;
fiber->closure->fn->native(vm);
if (VM_HAS_ERROR(vm)) {
if (last != NULL) last->error = vm->fiber->error;
result = PK_RESULT_RUNTIME_ERROR;
} else {
result = PK_RESULT_SUCCESS;
}
} else {
result = vmRunFiber(vm, fiber);
}
}
if (last != NULL) vmPopTempRef(vm); // last.
vm->fiber = last;
@ -265,6 +293,13 @@ PkResult vmRunFunction(PKVM* vm, Closure* fn, int argc, Var* argv, Var* ret) {
return result;
}
PkResult vmRunFunction(PKVM* vm, Closure* fn, int argc, Var* argv, Var* ret) {
// Calling functions and methods are the same, except for the methods have
// self defined, and for functions it'll be VAR_UNDEFINED.
return vmCallMethod(vm, VAR_UNDEFINED, fn, argc, argv, ret);
}
/*****************************************************************************/
/* VM INTERNALS */
/*****************************************************************************/
@ -612,7 +647,7 @@ L_vm_main_loop:
DEBUG_BREAK(); \
} while (false)
#if DUMP_STACK
#if DUMP_STACK && defined(DEBUG)
_DUMP_STACK();
#endif
#undef _DUMP_STACK
@ -852,8 +887,7 @@ L_vm_main_loop:
Closure* method = (Closure*)AS_OBJ(PEEK(-1));
Class* cls = (Class*)AS_OBJ(PEEK(-2));
// FIXME: literal string "_inint".
if (strcmp(method->fn->name, "_init") == 0) {
if (strcmp(method->fn->name, CTOR_NAME) == 0) {
cls->ctor = method;
} else {
// TODO: The method buffer should be ordered with it's name and
@ -1318,30 +1352,48 @@ L_do_call:
DISPATCH();
}
OPCODE(POSITIVE):
{
// Don't pop yet, we need the reference for gc.
Var self = PEEK(-1);
Var result = varPositive(vm, self);
DROP(); // self
PUSH(result);
CHECK_ERROR();
DISPATCH();
}
OPCODE(NEGATIVE):
{
Var num = POP();
if (!IS_NUM(num)) {
RUNTIME_ERROR(newString(vm, "Can not negate a non numeric value."));
}
PUSH(VAR_NUM(-AS_NUM(num)));
// Don't pop yet, we need the reference for gc.
Var self = PEEK(-1);
Var result = varNegative(vm, self);
DROP(); // self
PUSH(result);
CHECK_ERROR();
DISPATCH();
}
OPCODE(NOT):
{
Var val = POP();
PUSH(VAR_BOOL(!toBool(val)));
// Don't pop yet, we need the reference for gc.
Var self = PEEK(-1);
Var result = varNot(vm, self);
DROP(); // self
PUSH(result);
CHECK_ERROR();
DISPATCH();
}
OPCODE(BIT_NOT):
{
// Don't pop yet, we need the reference for gc.
Var val = PEEK(-1);
Var result = varBitNot(vm, val);
DROP(); // val
Var self = PEEK(-1);
Var result = varBitNot(vm, self);
DROP(); // self
PUSH(result);
CHECK_ERROR();
@ -1355,7 +1407,8 @@ L_do_call:
{
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varAdd(vm, l, r);
uint8_t inplace = READ_BYTE(); ASSERT(inplace <= 1, OOPS);
Var result = varAdd(vm, l, r, inplace);
DROP(); DROP(); // r, l
PUSH(result);
@ -1367,7 +1420,8 @@ L_do_call:
{
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varSubtract(vm, l, r);
uint8_t inplace = READ_BYTE(); ASSERT(inplace <= 1, OOPS);
Var result = varSubtract(vm, l, r, inplace);
DROP(); DROP(); // r, l
PUSH(result);
@ -1379,7 +1433,8 @@ L_do_call:
{
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varMultiply(vm, l, r);
uint8_t inplace = READ_BYTE(); ASSERT(inplace <= 1, OOPS);
Var result = varMultiply(vm, l, r, inplace);
DROP(); DROP(); // r, l
PUSH(result);
@ -1391,7 +1446,8 @@ L_do_call:
{
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varDivide(vm, l, r);
uint8_t inplace = READ_BYTE(); ASSERT(inplace <= 1, OOPS);
Var result = varDivide(vm, l, r, inplace);
DROP(); DROP(); // r, l
PUSH(result);
@ -1403,7 +1459,8 @@ L_do_call:
{
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varModulo(vm, l, r);
uint8_t inplace = READ_BYTE(); ASSERT(inplace <= 1, OOPS);
Var result = varModulo(vm, l, r, inplace);
DROP(); DROP(); // r, l
PUSH(result);
@ -1415,7 +1472,8 @@ L_do_call:
{
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varBitAnd(vm, l, r);
uint8_t inplace = READ_BYTE(); ASSERT(inplace <= 1, OOPS);
Var result = varBitAnd(vm, l, r, inplace);
DROP(); DROP(); // r, l
PUSH(result);
@ -1427,8 +1485,8 @@ L_do_call:
{
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varBitOr(vm, l, r);
uint8_t inplace = READ_BYTE(); ASSERT(inplace <= 1, OOPS);
Var result = varBitOr(vm, l, r, inplace);
DROP(); DROP(); // r, l
PUSH(result);
@ -1440,8 +1498,8 @@ L_do_call:
{
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varBitXor(vm, l, r);
uint8_t inplace = READ_BYTE(); ASSERT(inplace <= 1, OOPS);
Var result = varBitXor(vm, l, r, inplace);
DROP(); DROP(); // r, l
PUSH(result);
@ -1453,8 +1511,8 @@ L_do_call:
{
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varBitLshift(vm, l, r);
uint8_t inplace = READ_BYTE(); ASSERT(inplace <= 1, OOPS);
Var result = varBitLshift(vm, l, r, inplace);
DROP(); DROP(); // r, l
PUSH(result);
@ -1466,8 +1524,8 @@ L_do_call:
{
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varBitRshift(vm, l, r);
uint8_t inplace = READ_BYTE(); ASSERT(inplace <= 1, OOPS);
Var result = varBitRshift(vm, l, r, inplace);
DROP(); DROP(); // r, l
PUSH(result);
@ -1477,74 +1535,89 @@ L_do_call:
OPCODE(EQEQ):
{
Var r = POP(), l = POP();
PUSH(VAR_BOOL(isValuesEqual(l, r)));
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varEqals(vm, l, r);
DROP(); DROP(); // r, l
PUSH(result);
CHECK_ERROR();
DISPATCH();
}
OPCODE(NOTEQ):
{
Var r = POP(), l = POP();
PUSH(VAR_BOOL(!isValuesEqual(l, r)));
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varEqals(vm, l, r);
DROP(); DROP(); // r, l
PUSH(VAR_BOOL(!toBool(result)));
CHECK_ERROR();
DISPATCH();
}
OPCODE(LT):
{
Var r = POP(), l = POP();
PUSH(VAR_BOOL(varLesser(l, r)));
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varLesser(vm, l, r);
DROP(); DROP(); // r, l
PUSH(result);
CHECK_ERROR();
DISPATCH();
}
OPCODE(LTEQ):
{
Var r = POP(), l = POP();
bool lteq = varLesser(l, r);
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varLesser(vm, l, r);
CHECK_ERROR();
bool lteq = toBool(result);
if (!lteq) result = varEqals(vm, l, r);
CHECK_ERROR();
if (!lteq) {
lteq = isValuesEqual(l, r);
CHECK_ERROR();
}
PUSH(VAR_BOOL(lteq));
DROP(); DROP(); // r, l
PUSH(result);
DISPATCH();
}
OPCODE(GT):
{
Var r = POP(), l = POP();
PUSH(VAR_BOOL(varGreater(l, r)));
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varGreater(vm, l, r);
DROP(); DROP(); // r, l
PUSH(result);
CHECK_ERROR();
DISPATCH();
}
OPCODE(GTEQ):
{
Var r = POP(), l = POP();
bool gteq = varGreater(l, r);
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varGreater(vm, l, r);
CHECK_ERROR();
bool gteq = toBool(result);
if (!gteq) result = varEqals(vm, l, r);
CHECK_ERROR();
if (!gteq) {
gteq = isValuesEqual(l, r);
CHECK_ERROR();
}
PUSH(VAR_BOOL(gteq));
DROP(); DROP(); // r, l
PUSH(result);
DISPATCH();
}
OPCODE(RANGE):
{
Var to = PEEK(-1); // Don't pop yet, we need the reference for gc.
Var from = PEEK(-2); // Don't pop yet, we need the reference for gc.
if (!IS_NUM(from) || !IS_NUM(to)) {
RUNTIME_ERROR(newString(vm, "Range arguments must be number."));
}
DROP(); // to
DROP(); // from
PUSH(VAR_OBJ(newRange(vm, AS_NUM(from), AS_NUM(to))));
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var result = varOpRange(vm, l, r);
DROP(); DROP(); // r, l
PUSH(result);
CHECK_ERROR();
DISPATCH();
}

View File

@ -221,9 +221,16 @@ void vmYieldFiber(PKVM* vm, Var* value);
// till the next yield or return statement, and return result.
PkResult vmRunFiber(PKVM* vm, Fiber* fiber);
// Runs the script function (if not an assertion will fail) and if the [ret] is
// not null the return value will be set. [argv] should be the first argument
// pointer following the rest of the arguments in an array.
// Runs the function and if the [ret] is not NULL the return value will be set.
// [argv] should be the first argument pointer following the rest of the
// arguments in an array.
PkResult vmRunFunction(PKVM* vm, Closure* fn, int argc, Var* argv, Var* ret);
// Call the method on the [self], (witch has retrieved by the getMethod()
// function) and if the [ret] is not NULL, the return value will be set.
// [argv] should be the first argument pointer following the rest of the
// arguments in an array.
PkResult vmCallMethod(PKVM* vm, Var self, Closure* fn,
int argc, Var* argv, Var* ret);
#endif // PK_VM_H

View File

@ -27,6 +27,11 @@ l1 = [1] + []; assert(l1.length == 1); assert(l1[0] == 1)
l2 = l1 + [1,2,3]; assert(l2.length == 4); assert(l2 == [1,1,2,3])
l3 = l2 + l1 + l2; assert(l3 == [1,1,2,3,1,1,1,2,3])
## list references are shared.
l1 = [1];l2 = l1;l1 += [2]
assert(l2[1] == 2)
assert(l1 == l2)
## in tests.
assert(!('abc' in 'a'))
assert(42 in [12, 42, 3.14])
@ -35,7 +40,7 @@ assert('key' in {'key':'value'})
assert(!('foo' in {'bar':'baz'}))
## Builtin functions tests.
assert(to_string(42) == '42')
assert(str(42) == '42')
## FIXME: add hash function.
##h1 = math.hash("testing"); h2 = math.hash("test" + "ing")
@ -57,13 +62,13 @@ assert((a and b) == null)
assert((b or a) == [1, 2, 3])
assert((a or b) == b or a)
## Recursive to_string list/map
## Recursive str list/map
l = [1]
list_append(l, l)
assert(to_string(l) == '[1, [...]]')
assert(str(l) == '[1, [...]]')
m = {}
m['m'] = m
assert(to_string(m) == '{"m":{...}}')
assert(str(m) == '{"m":{...}}')
# Bitwise operation tests
assert(0b1010 | 0b0101 == 0b1111)

View File

@ -1,50 +1,26 @@
class Vec2
def _init(x, y)
self.x = x
self.y = y
end
###############################################################################
## Basics
###############################################################################
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()}")
## Default constructor crash (issue #213)
class A
end
a = A()
print(a)
class B is A
end
b = B()
assert((b is B) and (b is A))
## TODO: write some tests here.
###############################################################################
## INHERITANCE
###############################################################################
class A
end
class B is A
end
a = A()
print(a)
b = B()
assert((b is B) and (b is A))
class Shape
def display()
return "${self.name} shape"
@ -95,6 +71,126 @@ assert(s is Rectangle)
assert(s is Shape)
assert(s.area() == 16)
###############################################################################
## OPERATOR OVERLOADING
###############################################################################
class Vec2
def _init(x, y)
self.x = x; self.y = y
end
def _str
return "<${self.x}, ${self.y}>"
end
def +(other)
return Vec2(self.x + other.x,
self.y + other.y)
end
def +=(other)
self.x += other.x
self.y += other.y
return self
end
def ==(other)
return self.x == other.x and self.y == other.y
end
end
v1 = Vec2(1, 2); assert(v1.x == 1 and v1.y == 2)
print("v1 = $v1")
assert(str(v1) == "<1, 2>")
v2 = Vec2(3, 4); assert(v2.x == 3 and v2.y == 4)
print("v2 = $v2")
v3 = v1 + v2
print("v3 = $v3")
assert(v1 == Vec2(1, 2))
v1 += v2
assert(v1 == v3)
class Path
def _init(path)
self.path = path
end
def /(other)
if other is String
return Path(self.path + "/" + other)
else if other is Path
return Path(self.path + "/" + other.path)
else
assert(false, "Invalid type")
end
end
def _str
return self.path
end
end
local = Path("/usr/local")
pocket = Path("bin/pocket")
print('local/pocket = "${local/pocket}"')
assert(str(local/pocket) == "/usr/local/bin/pocket")
assert(str(local/"lib") == "/usr/local/lib")
class N
def _init(val)
self.val = val
end
def _str()
return "N(${self.val})"
end
def +(other)
return N(self.val + other.val)
end
def +=(other)
self.val += other.val
return self
end
def %=(num)
self.val %= num
return self
end
def ==(other)
return self.val == other.val
end
def <<(num)
self.val *= 10
self.val += num
return self
end
def >>(other)
other.val = self.val % 10
self.val = (self.val - other.val) / 10
end
def !self()
self.val -= 1
return self.val + 1
end
end
n1 = N(12)
n2 = N(23)
assert(n1 + n2 == N(n1.val + n2.val))
n1 %= 5
assert(n1.val == 2)
n3 = N(4) << 8 << 3 << 6
assert(n3 == N(4836))
n4 = N(0)
n3 >> n4; assert(n4.val == 6)
n3 >> n4; assert(n4.val == 3)
n3 >> n4; assert(n4.val == 8)
n3 >> n4; assert(n4.val == 4)
n5 = N(3)
assert(!n5 == 3)
assert(!n5 == 2)
assert(!n5 == 1)
print('ALL TESTS PASSED')

23
tests/modules/dummy.pk Normal file
View File

@ -0,0 +1,23 @@
## TODO: this dummy module is just to test different aspects of the
## native module interface and will be removed once it become stable
## and we have enough tests on other native interfaces.
from dummy import Dummy
d = Dummy()
print(d)
assert(d.val == 0) ## @getter
d.val = 3.14 ## @setter
assert(d.val == 3.14)
assert(d == 3.14) ## == overload
assert(d > 3) ## > overload
assert(d >= 3) ## > overload
assert(d >= 3.14) ## > and == overload
assert(d.a_method(12, 34) == 408) ## method
# If we got here, that means all test were passed.
print('All TESTS PASSED')

View File

@ -28,6 +28,7 @@ TEST_SUITE = {
),
"Modules Test" : (
"modules/dummy.pk",
"modules/math.pk",
),