mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 15:16:41 +08:00
some (minor) code cleanups
This commit is contained in:
parent
4bf045bcac
commit
749456e215
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
218
src/pk_core.c
218
src/pk_core.c
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
12
src/pk_var.h
12
src/pk_var.h
@ -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);
|
||||||
|
24
src/pk_vm.c
24
src/pk_vm.c
@ -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):
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user