some (minor) code cleanups

This commit is contained in:
Thakee Nathees 2021-06-11 13:16:55 +05:30
parent 4bf045bcac
commit 749456e215
12 changed files with 245 additions and 127 deletions

View File

@ -38,7 +38,8 @@ void errorFunction(PKVM* vm, PkErrorType type, const char* file, int line,
if (type == PK_ERROR_COMPILE) { if (type == PK_ERROR_COMPILE) {
if (repl) fprintf(stderr, "Error: %s\n", message); if (repl) fprintf(stderr, "Error: %s\n", message);
else fprintf(stderr, "Error: %s\n at \"%s\":%i\n", message, file, line); else fprintf(stderr, "Error: %s\n at \"%s\":%i\n",
message, file, line);
} else if (type == PK_ERROR_RUNTIME) { } else if (type == PK_ERROR_RUNTIME) {
fprintf(stderr, "Error: %s\n", message); fprintf(stderr, "Error: %s\n", message);

View File

@ -1,12 +1,13 @@
// To implement. // To implement.
- Refactor fiber into a module and is_done as an attribute.
- move moduleAddGlobalInternal() to var.h (also other var functions).
- refactor build.bat batch script to powershell - refactor build.bat batch script to powershell
refactor makefile and setup a github action. refactor makefile and setup a github action.
- check for tabs and trailing white space in source.
they're not allowed.
- Hex, binary literals and floats like ".5". - Hex, binary literals and floats like ".5".
- change or add => to_string() to value.as_string - change or add => to_string() to value.as_string

View File

@ -222,7 +222,11 @@ PK_PUBLIC PkVar pkGetHandleValue(const PkHandle* handle);
// this for every handles before freeing the VM. // this for every handles before freeing the VM.
PK_PUBLIC void pkReleaseHandle(PKVM* vm, PkHandle* handle); PK_PUBLIC void pkReleaseHandle(PKVM* vm, PkHandle* handle);
// Add a native function to the given script. If [arity] is -1 that means // Add a global [value] to the given module.
PK_PUBLIC void pkModuleAddGlobal(PKVM* vm, PkHandle* module,
const char* name, PkHandle* value);
// Add a native function to the given module. If [arity] is -1 that means
// The function has variadic parameters and use pkGetArgc() to get the argc. // The function has variadic parameters and use pkGetArgc() to get the argc.
PK_PUBLIC void pkModuleAddFunction(PKVM* vm, PkHandle* module, PK_PUBLIC void pkModuleAddFunction(PKVM* vm, PkHandle* module,
const char* name, const char* name,

View File

@ -13,6 +13,13 @@
#include "pk_var.h" #include "pk_var.h"
#include "pk_vm.h" #include "pk_vm.h"
// M_PI is non standard. The macro _USE_MATH_DEFINES defining before importing
// <math.h> will define the constants for MSVC. But for a portable solution,
// we're defining it ourselves if it isn't already.
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
/*****************************************************************************/ /*****************************************************************************/
/* CORE PUBLIC API */ /* CORE PUBLIC API */
/*****************************************************************************/ /*****************************************************************************/
@ -21,6 +28,10 @@
// internal. Which will be wrapped by pkNewModule to return a pkHandle*. // internal. Which will be wrapped by pkNewModule to return a pkHandle*.
static Script* newModuleInternal(PKVM* vm, const char* name); static Script* newModuleInternal(PKVM* vm, const char* name);
// The internal function to add global value to a module.
static void moduleAddGlobalInternal(PKVM* vm, Script* script,
const char* name, Var value);
// The internal function to add functions to a module. // The internal function to add functions to a module.
static void moduleAddFunctionInternal(PKVM* vm, Script* script, static void moduleAddFunctionInternal(PKVM* vm, Script* script,
const char* name, pkNativeFn fptr, const char* name, pkNativeFn fptr,
@ -32,6 +43,18 @@ PkHandle* pkNewModule(PKVM* vm, const char* name) {
return vmNewHandle(vm, VAR_OBJ(module)); return vmNewHandle(vm, VAR_OBJ(module));
} }
// pkModuleAddGlobal implementation (see pocketlang.h for description).
PK_PUBLIC void pkModuleAddGlobal(PKVM* vm, PkHandle* module,
const char* name, PkHandle* value) {
__ASSERT(module != NULL, "Argument module was NULL.");
__ASSERT(value != NULL, "Argument value was NULL.");
Var scr = module->value;
__ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), "Given handle is not a module");
moduleAddGlobalInternal(vm, (Script*)AS_OBJ(scr), name, value->value);
}
// pkModuleAddFunction implementation (see pocketlang.h for description). // pkModuleAddFunction implementation (see pocketlang.h for description).
void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name, void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
pkNativeFn fptr, int arity) { pkNativeFn fptr, int arity) {
@ -88,7 +111,7 @@ PkHandle* pkGetFunction(PKVM* vm, PkHandle* module,
#define RET_ERR(err) \ #define RET_ERR(err) \
do { \ do { \
vm->fiber->error = err; \ VM_SET_ERROR(vm, err); \
RET(VAR_NULL); \ RET(VAR_NULL); \
} while(false) } while(false)
@ -106,8 +129,8 @@ PkHandle* pkGetFunction(PKVM* vm, PkHandle* module,
do { \ do { \
char buff[STR_INT_BUFF_SIZE]; \ char buff[STR_INT_BUFF_SIZE]; \
sprintf(buff, "%d", arg); \ sprintf(buff, "%d", arg); \
vm->fiber->error = stringFormat(vm, "Expected a " m_type \ VM_SET_ERROR(vm, stringFormat(vm, "Expected a " m_type \
" at argument $.", buff); \ " at argument $.", buff)); \
} while (false) } while (false)
// pkGetArgc implementation (see pocketlang.h for description). // pkGetArgc implementation (see pocketlang.h for description).
@ -175,8 +198,8 @@ bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) {
Var val = ARG(arg); Var val = ARG(arg);
if (pkGetValueType((PkVar)&val) != type) { if (pkGetValueType((PkVar)&val) != type) {
char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", arg); char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", arg);
vm->fiber->error = stringFormat(vm, VM_SET_ERROR(vm, stringFormat(vm, "Expected a $ at argument $.",
"Expected a $ at argument $.", getPkVarTypeName(type), buff); getPkVarTypeName(type), buff));
return false; return false;
} }
@ -240,7 +263,7 @@ bool pkFiberIsDone(const PkHandle* fiber) {
/* VALIDATORS */ /* VALIDATORS */
/*****************************************************************************/ /*****************************************************************************/
// Check if a numeric value bool/number and set [value]. // Check if [var] is a numeric value (bool/number) and set [value].
static inline bool isNumeric(Var var, double* value) { static inline bool isNumeric(Var var, double* value) {
if (IS_NUM(var)) { if (IS_NUM(var)) {
*value = AS_NUM(var); *value = AS_NUM(var);
@ -253,17 +276,8 @@ static inline bool isNumeric(Var var, double* value) {
return false; return false;
} }
// Check if [var] is bool/number. If not set error and return false. // Check if [var] is a numeric value (bool/number) and set [value].
static inline bool validateNumeric(PKVM* vm, Var var, double* value, static inline bool isInteger(Var var, int64_t* value) {
const char* name) {
if (isNumeric(var, value)) return true;
vm->fiber->error = stringFormat(vm, "$ must be a numeric value.", name);
return false;
}
// Check if [var] is 32 bit integer. If not set error and return false.
static inline bool validateInteger(PKVM* vm, Var var, int64_t* value,
const char* name) {
double number; double number;
if (isNumeric(var, &number)) { if (isNumeric(var, &number)) {
// TODO: check if the number is larger for a 64 bit integer. // TODO: check if the number is larger for a 64 bit integer.
@ -273,8 +287,22 @@ static inline bool validateInteger(PKVM* vm, Var var, int64_t* value,
return true; return true;
} }
} }
return false;
}
vm->fiber->error = stringFormat(vm, "$ must be a whole number.", name); // Check if [var] is bool/number. If not set error and return false.
static inline bool validateNumeric(PKVM* vm, Var var, double* value,
const char* name) {
if (isNumeric(var, value)) return true;
VM_SET_ERROR(vm, stringFormat(vm, "$ must be a numeric value.", name));
return false;
}
// Check if [var] is 32 bit integer. If not set error and return false.
static inline bool validateInteger(PKVM* vm, Var var, int64_t* value,
const char* name) {
if (isInteger(var, value)) return true;
VM_SET_ERROR(vm, stringFormat(vm, "$ must be a whole number.", name));
return false; return false;
} }
@ -283,7 +311,7 @@ static inline bool validateInteger(PKVM* vm, Var var, int64_t* value,
static inline bool validateIndex(PKVM* vm, int64_t index, uint32_t size, static inline bool validateIndex(PKVM* vm, int64_t index, uint32_t size,
const char* container) { const char* container) {
if (index < 0 || size <= index) { if (index < 0 || size <= index) {
vm->fiber->error = stringFormat(vm, "$ index out of bound.", container); VM_SET_ERROR(vm, stringFormat(vm, "$ index out of bound.", container));
return false; return false;
} }
return true; return true;
@ -297,8 +325,8 @@ static inline bool validateIndex(PKVM* vm, int64_t index, uint32_t size,
ASSERT(arg > 0 && arg <= ARGC, OOPS); \ ASSERT(arg > 0 && arg <= ARGC, OOPS); \
if (!IS_OBJ(var) || AS_OBJ(var)->type != m_type) { \ if (!IS_OBJ(var) || AS_OBJ(var)->type != m_type) { \
char buff[12]; sprintf(buff, "%d", arg); \ char buff[12]; sprintf(buff, "%d", arg); \
vm->fiber->error = stringFormat(vm, "Expected a " m_name \ VM_SET_ERROR(vm, stringFormat(vm, "Expected a " m_name \
" at argument $.", buff, false); \ " at argument $.", buff, false)); \
} \ } \
*value = (m_class*)AS_OBJ(var); \ *value = (m_class*)AS_OBJ(var); \
return true; \ return true; \
@ -413,7 +441,7 @@ static void coreHex(PKVM* vm)) {
*ptr++ = '0'; *ptr++ = 'x'; *ptr++ = '0'; *ptr++ = 'x';
if (value > UINT32_MAX || value < -(int64_t)(UINT32_MAX)) { if (value > UINT32_MAX || value < -(int64_t)(UINT32_MAX)) {
vm->fiber->error = newString(vm, "Integer is too large."); VM_SET_ERROR(vm, newString(vm, "Integer is too large."));
RET(VAR_NULL); RET(VAR_NULL);
} }
@ -444,10 +472,10 @@ static void coreAssert(PKVM* vm)) {
msg = (String*)AS_OBJ(ARG(2)); msg = (String*)AS_OBJ(ARG(2));
} }
vmPushTempRef(vm, &msg->_super); vmPushTempRef(vm, &msg->_super);
vm->fiber->error = stringFormat(vm, "Assertion failed: '@'.", msg); VM_SET_ERROR(vm, stringFormat(vm, "Assertion failed: '@'.", msg));
vmPopTempRef(vm); vmPopTempRef(vm);
} else { } else {
vm->fiber->error = newString(vm, "Assertion failed."); VM_SET_ERROR(vm, newString(vm, "Assertion failed."));
} }
} }
} }
@ -558,7 +586,7 @@ static void coreListAppend(PKVM* vm)) {
if (!validateArgList(vm, 1, &list)) return; if (!validateArgList(vm, 1, &list)) return;
Var elem = ARG(2); Var elem = ARG(2);
pkVarBufferWrite(&list->elements, vm, elem); listAppend(vm, list, elem);
RET(VAR_OBJ(list)); RET(VAR_OBJ(list));
} }
@ -691,11 +719,10 @@ static Script* newModuleInternal(PKVM* vm, const char* name) {
return scr; return scr;
} }
// An internal function to add a function to the given [script]. // This will fail an assertion if a function or a global with the [name]
static void moduleAddFunctionInternal(PKVM* vm, Script* script, // already exists in the module.
const char* name, pkNativeFn fptr, static inline void assertModuleNameDef(PKVM* vm, Script* script,
int arity) { const char* name) {
// Check if function with the same name already exists. // Check if function with the same name already exists.
if (scriptGetFunc(script, name, (uint32_t)strlen(name)) != -1) { if (scriptGetFunc(script, name, (uint32_t)strlen(name)) != -1) {
__ASSERT(false, stringFormat(vm, "A function named '$' already esists " __ASSERT(false, stringFormat(vm, "A function named '$' already esists "
@ -707,6 +734,30 @@ static void moduleAddFunctionInternal(PKVM* vm, Script* script,
__ASSERT(false, stringFormat(vm, "A global variable named '$' already " __ASSERT(false, stringFormat(vm, "A global variable named '$' already "
"esists on module '@'", name, script->moudle)->data); "esists on module '@'", name, script->moudle)->data);
} }
}
// The internal function to add global value to a module.
static void moduleAddGlobalInternal(PKVM* vm, Script* script,
const char* name, Var value) {
// Ensure the name isn't predefined.
assertModuleNameDef(vm, script, name);
// TODO: move this to pk_var.h and use it in the compilerAddVariable
// function.
uint32_t name_index = scriptAddName(script, vm, name,
(uint32_t)strlen(name));
pkUintBufferWrite(&script->global_names, vm, name_index);
pkVarBufferWrite(&script->globals, vm, value);
}
// An internal function to add a function to the given [script].
static void moduleAddFunctionInternal(PKVM* vm, Script* script,
const char* name, pkNativeFn fptr,
int arity) {
// Ensure the name isn't predefined.
assertModuleNameDef(vm, script, name);
Function* fn = newFunction(vm, name, (int)strlen(name), script, true); Function* fn = newFunction(vm, name, (int)strlen(name), script, true);
fn->native = fptr; fn->native = fptr;
@ -810,8 +861,7 @@ PK_DOC(
"hash(value:var) -> num\n" "hash(value:var) -> num\n"
"Return the hash value of the variable, if it's not hashable it'll " "Return the hash value of the variable, if it's not hashable it'll "
"return null.", "return null.",
static void stdMathHash(PKVM* vm)); static void stdMathHash(PKVM* vm)) {
void stdMathHash(PKVM* vm) {
if (IS_OBJ(ARG(1))) { if (IS_OBJ(ARG(1))) {
if (!isObjectHashable(AS_OBJ(ARG(1))->type)) { if (!isObjectHashable(AS_OBJ(ARG(1))->type)) {
RET(VAR_NULL); RET(VAR_NULL);
@ -820,6 +870,16 @@ void stdMathHash(PKVM* vm) {
RET(VAR_NUM((double)varHashValue(ARG(1)))); RET(VAR_NUM((double)varHashValue(ARG(1))));
} }
PK_DOC(
"sin(rad:num) -> num\n"
"Return the sine value of the argument [rad] which is an angle expressed "
"in radians.",
static void stdMathSine(PKVM* vm)) {
double rad;
if (!validateNumeric(vm, ARG(1), &rad, "Argument 1")) return;
RET(VAR_NUM(sin(rad)));
}
/*****************************************************************************/ /*****************************************************************************/
/* CORE INITIALIZATION */ /* CORE INITIALIZATION */
/*****************************************************************************/ /*****************************************************************************/
@ -903,6 +963,17 @@ void initializeCore(PKVM* vm) {
moduleAddFunctionInternal(vm, math, "abs", stdMathAbs, 1); moduleAddFunctionInternal(vm, math, "abs", stdMathAbs, 1);
moduleAddFunctionInternal(vm, math, "sign", stdMathSign, 1); moduleAddFunctionInternal(vm, math, "sign", stdMathSign, 1);
moduleAddFunctionInternal(vm, math, "hash", stdMathHash, 1); moduleAddFunctionInternal(vm, math, "hash", stdMathHash, 1);
moduleAddFunctionInternal(vm, math, "sin", stdMathSine, 1);
// TODO: add - cos, tan.
// low priority: sinh, cosh, tanh, asin, acos, atan.
// Note that currently it's mutable (since it's a global variable, not
// constant and pocketlang doesn't support constant) so the user shouldn't
// modify the PI, like in python.
// TODO: at varSetAttrib() we can detect if the user try to change an
// attribute of a core module and we can throw an error.
moduleAddGlobalInternal(vm, math, "PI", VAR_NUM(M_PI));
} }
/*****************************************************************************/ /*****************************************************************************/
@ -910,8 +981,8 @@ void initializeCore(PKVM* vm) {
/*****************************************************************************/ /*****************************************************************************/
#define UNSUPPORT_OPERAND_TYPES(op) \ #define UNSUPPORT_OPERAND_TYPES(op) \
vm->fiber->error = stringFormat(vm, "Unsupported operand types for " \ VM_SET_ERROR(vm, stringFormat(vm, "Unsupported operand types for " \
"operator '" op "' $ and $", varTypeName(v1), varTypeName(v2)) "operator '" op "' $ and $", varTypeName(v1), varTypeName(v2)))
Var varAdd(PKVM* vm, Var v1, Var v2) { Var varAdd(PKVM* vm, Var v1, Var v2) {
@ -937,10 +1008,10 @@ Var varAdd(PKVM* vm, Var v1, Var v2) {
case OBJ_LIST: case OBJ_LIST:
{ {
if (o2->type == OBJ_LIST) { if (o2->type == OBJ_LIST) {
List* l1 = (List*)o1, * l2 = (List*)o2;
TODO; TODO;
} }
} } break;
TODO;
case OBJ_MAP: case OBJ_MAP:
case OBJ_RANGE: case OBJ_RANGE:
@ -1015,6 +1086,20 @@ Var varModulo(PKVM* vm, Var v1, Var v2) {
return VAR_NULL; return VAR_NULL;
} }
Var varBitAnd(PKVM* vm, Var v1, Var v2) {
int64_t i1, i2;
if (isInteger(v1, &i1)) {
if (validateInteger(vm, v2, &i2, "Right operand")) {
return VAR_NUM(i1 & i2);
}
return VAR_NULL;
}
UNSUPPORT_OPERAND_TYPES("&");
return VAR_NULL;
}
bool varGreater(Var v1, Var v2) { bool varGreater(Var v1, Var v2) {
double d1, d2; double d1, d2;
if (isNumeric(v1, &d1) && isNumeric(v2, &d2)) { if (isNumeric(v1, &d1) && isNumeric(v2, &d2)) {
@ -1058,16 +1143,15 @@ bool varLesser(Var v1, Var v2) {
#define CASE_ATTRIB(name, hash) case hash #define CASE_ATTRIB(name, hash) case hash
// Set error for accessing non-existed attribute. // Set error for accessing non-existed attribute.
#define ERR_NO_ATTRIB(on) \ #define ERR_NO_ATTRIB(vm, on, attrib) \
vm->fiber->error = stringFormat(vm, "'$' object has no attribute " \ VM_SET_ERROR(vm, stringFormat(vm, "'$' object has no attribute named '$'", \
"named '$'", \ varTypeName(on), attrib->data))
varTypeName(on), attrib->data);
Var varGetAttrib(PKVM* vm, Var on, String* attrib) { Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
if (!IS_OBJ(on)) { if (!IS_OBJ(on)) {
vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.", VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.",
varTypeName(on)); varTypeName(on)));
return VAR_NULL; return VAR_NULL;
} }
@ -1091,7 +1175,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
return VAR_OBJ(stringStrip(vm, str)); return VAR_OBJ(stringStrip(vm, str));
default: default:
ERR_NO_ATTRIB(on); ERR_NO_ATTRIB(vm, on, attrib);
return VAR_NULL; return VAR_NULL;
} }
@ -1107,7 +1191,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
return VAR_NUM((double)(list->elements.count)); return VAR_NUM((double)(list->elements.count));
default: default:
ERR_NO_ATTRIB(on); ERR_NO_ATTRIB(vm, on, attrib);
return VAR_NULL; return VAR_NULL;
} }
@ -1132,7 +1216,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
return VAR_OBJ(rangeAsList(vm, range)); return VAR_OBJ(rangeAsList(vm, range));
default: default:
ERR_NO_ATTRIB(on); ERR_NO_ATTRIB(vm, on, attrib);
return VAR_NULL; return VAR_NULL;
} }
@ -1156,7 +1240,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
return scr->globals.data[index]; return scr->globals.data[index];
} }
ERR_NO_ATTRIB(on); ERR_NO_ATTRIB(vm, on, attrib);
return VAR_NULL; return VAR_NULL;
} }
@ -1172,7 +1256,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
return VAR_OBJ(newString(vm, fn->name)); return VAR_OBJ(newString(vm, fn->name));
default: default:
ERR_NO_ATTRIB(on); ERR_NO_ATTRIB(vm, on, attrib);
return VAR_NULL; return VAR_NULL;
} }
UNREACHABLE(); UNREACHABLE();
@ -1195,14 +1279,14 @@ void varSetAttrib(PKVM* vm, Var on, String* attrib, Var value) {
#define ATTRIB_IMMUTABLE(name) \ #define ATTRIB_IMMUTABLE(name) \
do { \ do { \
if ((attrib->length == strlen(name) && strcmp(name, attrib->data) == 0)) { \ if ((attrib->length == strlen(name) && strcmp(name, attrib->data) == 0)) { \
vm->fiber->error = stringFormat(vm, "'$' attribute is immutable.", name); \ VM_SET_ERROR(vm, stringFormat(vm, "'$' attribute is immutable.", name)); \
return; \ return; \
} \ } \
} while (false) } while (false)
if (!IS_OBJ(on)) { if (!IS_OBJ(on)) {
vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.", VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.",
varTypeName(on)); varTypeName(on)));
return; return;
} }
@ -1213,12 +1297,12 @@ do { \
ATTRIB_IMMUTABLE("lower"); ATTRIB_IMMUTABLE("lower");
ATTRIB_IMMUTABLE("upper"); ATTRIB_IMMUTABLE("upper");
ATTRIB_IMMUTABLE("strip"); ATTRIB_IMMUTABLE("strip");
ERR_NO_ATTRIB(on); ERR_NO_ATTRIB(vm, on, attrib);
return; return;
case OBJ_LIST: case OBJ_LIST:
ATTRIB_IMMUTABLE("length"); ATTRIB_IMMUTABLE("length");
ERR_NO_ATTRIB(on); ERR_NO_ATTRIB(vm, on, attrib);
return; return;
case OBJ_MAP: case OBJ_MAP:
@ -1228,12 +1312,12 @@ do { \
// map.foo = 'bar' // map.foo = 'bar'
TODO; TODO;
ERR_NO_ATTRIB(on); ERR_NO_ATTRIB(vm, on, attrib);
return; return;
case OBJ_RANGE: case OBJ_RANGE:
ATTRIB_IMMUTABLE("as_list"); ATTRIB_IMMUTABLE("as_list");
ERR_NO_ATTRIB(on); ERR_NO_ATTRIB(vm, on, attrib);
return; return;
case OBJ_SCRIPT: { case OBJ_SCRIPT: {
@ -1255,22 +1339,22 @@ do { \
return; return;
} }
ERR_NO_ATTRIB(on); ERR_NO_ATTRIB(vm, on, attrib);
return; return;
} }
case OBJ_FUNC: case OBJ_FUNC:
ATTRIB_IMMUTABLE("arity"); ATTRIB_IMMUTABLE("arity");
ATTRIB_IMMUTABLE("name"); ATTRIB_IMMUTABLE("name");
ERR_NO_ATTRIB(on); ERR_NO_ATTRIB(vm, on, attrib);
return; return;
case OBJ_FIBER: case OBJ_FIBER:
ERR_NO_ATTRIB(on); ERR_NO_ATTRIB(vm, on, attrib);
return; return;
case OBJ_USER: case OBJ_USER:
TODO; //ERR_NO_ATTRIB(on); TODO; //ERR_NO_ATTRIB(vm, on, attrib);
return; return;
default: default:
@ -1284,8 +1368,8 @@ do { \
Var varGetSubscript(PKVM* vm, Var on, Var key) { Var varGetSubscript(PKVM* vm, Var on, Var key) {
if (!IS_OBJ(on)) { if (!IS_OBJ(on)) {
vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.", VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.",
varTypeName(on)); varTypeName(on)));
return VAR_NULL; return VAR_NULL;
} }
@ -1326,9 +1410,9 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
String* key_str = toString(vm, key); String* key_str = toString(vm, key);
vmPushTempRef(vm, &key_str->_super); vmPushTempRef(vm, &key_str->_super);
if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) { if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) {
vm->fiber->error = stringFormat(vm, "Invalid key '@'.", key_str); VM_SET_ERROR(vm, stringFormat(vm, "Invalid key '@'.", key_str));
} else { } else {
vm->fiber->error = stringFormat(vm, "Key '@' not exists", key_str); VM_SET_ERROR(vm, stringFormat(vm, "Key '@' not exists", key_str));
} }
vmPopTempRef(vm); vmPopTempRef(vm);
return VAR_NULL; return VAR_NULL;
@ -1352,15 +1436,15 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
void varsetSubscript(PKVM* vm, Var on, Var key, Var value) { void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
if (!IS_OBJ(on)) { if (!IS_OBJ(on)) {
vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.", VM_SET_ERROR(vm, stringFormat(vm, "$ type is not subscriptable.",
varTypeName(on)); varTypeName(on)));
return; return;
} }
Object* obj = AS_OBJ(on); Object* obj = AS_OBJ(on);
switch (obj->type) { switch (obj->type) {
case OBJ_STRING: case OBJ_STRING:
vm->fiber->error = newString(vm, "String objects are immutable."); VM_SET_ERROR(vm, newString(vm, "String objects are immutable."));
return; return;
case OBJ_LIST: case OBJ_LIST:
@ -1376,8 +1460,8 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
case OBJ_MAP: case OBJ_MAP:
{ {
if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) { if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) {
vm->fiber->error = stringFormat(vm, "$ type is not hashable.", VM_SET_ERROR(vm, stringFormat(vm, "$ type is not hashable.",
varTypeName(key)); varTypeName(key)));
} else { } else {
mapSet(vm, (Map*)obj, key, value); mapSet(vm, (Map*)obj, key, value);
} }

View File

@ -36,6 +36,8 @@ Var varMultiply(PKVM* vm, Var v1, Var v2); // Returns v1 * v2.
Var varDivide(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. Var varModulo(PKVM* vm, Var v1, Var v2); // Returns v1 % v2.
Var varBitAnd(PKVM* vm, Var v1, Var v2); // Returns v1 & v2.
bool varGreater(Var v1, Var v2); // Returns v1 > v2. bool varGreater(Var v1, Var v2); // Returns v1 > v2.
bool varLesser(Var v1, Var v2); // Returns v1 < v2. bool varLesser(Var v1, Var v2); // Returns v1 < v2.

View File

@ -146,9 +146,9 @@ int utf8_encodeValue(int value, uint8_t* bytes) {
// to 4th byte, next 6 bits to 3rd byte, next 6 bits to 2nd byte, 3 bits // to 4th byte, next 6 bits to 3rd byte, next 6 bits to 2nd byte, 3 bits
// first byte. // first byte.
if (value <= 0x10ffff) { if (value <= 0x10ffff) {
*(bytes++) = (uint8_t)(0b11110000 | ((value & 0b111000000000000000000) >> 18)); *(bytes++) = (uint8_t)(0b11110000 | ((value & (0b111 << 18)) >> 18));
*(bytes++) = (uint8_t)(0b10000000 | ((value & 0b111111000000000000) >> 12)); *(bytes++) = (uint8_t)(0b10000000 | ((value & (0b111111 << 12)) >> 12));
*(bytes++) = (uint8_t)(0b10000000 | ((value & 0b111111000000) >> 6)); *(bytes++) = (uint8_t)(0b10000000 | ((value & (0b111111 << 6)) >> 6));
*(bytes) = (uint8_t)(0b10000000 | ((value & 0b111111))); *(bytes) = (uint8_t)(0b10000000 | ((value & 0b111111)));
return 4; return 4;
} }

View File

@ -466,6 +466,18 @@ String* stringUpper(PKVM* vm, String* self);
// If the string is already trimed it'll return the same string. // If the string is already trimed it'll return the same string.
String* stringStrip(PKVM* vm, String* self); String* stringStrip(PKVM* vm, String* self);
// An inline function/macro implementation of listAppend(). 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).
#if 0 // Function implementation.
static inline void listAppend(PKVM* vm, List* self, Var value) {
pkVarBufferWrite(&self->elements, vm, value);
}
#else // Macro implementation.
#define listAppend(vm, self, value) \
pkVarBufferWrite(&self->elements, vm, value)
#endif
// Insert [value] to the list at [index] and shift down the rest of the // Insert [value] to the list at [index] and shift down the rest of the
// elements. // elements.
void listInsert(PKVM* vm, List* self, uint32_t index, Var value); void listInsert(PKVM* vm, List* self, uint32_t index, Var value);

View File

@ -10,9 +10,6 @@
#include "pk_utils.h" #include "pk_utils.h"
#include "pk_debug.h" #include "pk_debug.h"
// Evaluated to "true" if a runtime error set on the current fiber.
#define HAS_ERROR() (vm->fiber->error != NULL)
/*****************************************************************************/ /*****************************************************************************/
/* VM PUBLIC API */ /* VM PUBLIC API */
/*****************************************************************************/ /*****************************************************************************/
@ -195,7 +192,7 @@ PkResult pkResumeFiber(PKVM* vm, PkHandle* fiber, PkVar value) {
void pkSetRuntimeError(PKVM* vm, const char* message) { void pkSetRuntimeError(PKVM* vm, const char* message) {
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime."); __ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
vm->fiber->error = newString(vm, message); VM_SET_ERROR(vm, newString(vm, message));
} }
/*****************************************************************************/ /*****************************************************************************/
@ -315,7 +312,7 @@ void vmCollectGarbage(PKVM* vm) {
#define _ERR_FAIL(msg) \ #define _ERR_FAIL(msg) \
do { \ do { \
if (vm->fiber != NULL) vm->fiber->error = msg; \ if (vm->fiber != NULL) VM_SET_ERROR(vm, msg); \
return false; \ return false; \
} while (false) } while (false)
@ -549,7 +546,7 @@ static inline void pushCallFrame(PKVM* vm, const Function* fn) {
} }
static void reportError(PKVM* vm) { static void reportError(PKVM* vm) {
ASSERT(HAS_ERROR(), "runtimeError() should be called after an error."); ASSERT(VM_HAS_ERROR(vm), "runtimeError() should be called after an error.");
// TODO: pass the error to the caller of the fiber. // TODO: pass the error to the caller of the fiber.
// Print the Error message and stack trace. // Print the Error message and stack trace.
@ -607,7 +604,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
// Check if any runtime error exists and if so returns RESULT_RUNTIME_ERROR. // Check if any runtime error exists and if so returns RESULT_RUNTIME_ERROR.
#define CHECK_ERROR() \ #define CHECK_ERROR() \
do { \ do { \
if (HAS_ERROR()) { \ if (VM_HAS_ERROR(vm)) { \
UPDATE_FRAME(); \ UPDATE_FRAME(); \
reportError(vm); \ reportError(vm); \
FIBER_SWITCH_BACK(); \ FIBER_SWITCH_BACK(); \
@ -618,7 +615,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
// [err_msg] must be of type String. // [err_msg] must be of type String.
#define RUNTIME_ERROR(err_msg) \ #define RUNTIME_ERROR(err_msg) \
do { \ do { \
vm->fiber->error = err_msg; \ VM_SET_ERROR(vm, err_msg); \
UPDATE_FRAME(); \ UPDATE_FRAME(); \
reportError(vm); \ reportError(vm); \
FIBER_SWITCH_BACK(); \ FIBER_SWITCH_BACK(); \
@ -1234,6 +1231,17 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
} }
OPCODE(BIT_AND) : OPCODE(BIT_AND) :
{
// Don't pop yet, we need the reference for gc.
Var r = PEEK(-1), l = PEEK(-2);
Var value = varBitAnd(vm, l, r);
DROP(); DROP(); // r, l
PUSH(value);
CHECK_ERROR();
DISPATCH();
}
OPCODE(BIT_OR): OPCODE(BIT_OR):
OPCODE(BIT_XOR): OPCODE(BIT_XOR):
OPCODE(BIT_LSHIFT): OPCODE(BIT_LSHIFT):

View File

@ -35,6 +35,12 @@
// allocated so far plus the fill factor of it. // allocated so far plus the fill factor of it.
#define HEAP_FILL_PERCENT 75 #define HEAP_FILL_PERCENT 75
// Evaluated to "true" if a runtime error set on the current fiber.
#define VM_HAS_ERROR(vm) (vm->fiber->error != NULL)
// Set the error message [err] to the [vm]'s current fiber.
#define VM_SET_ERROR(vm, err) (vm->fiber->error = err)
// Builtin functions are stored in an array in the VM (unlike script functions // Builtin functions are stored in an array in the VM (unlike script functions
// they're member of function buffer of the script) and this struct is a single // they're member of function buffer of the script) and this struct is a single
// entry of the array. // entry of the array.