mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-03-04 13:15:55 +08:00
Merge pull request #234 from ThakeeNathees/bytebuffer
More code enhancements (read bellow)
This commit is contained in:
commit
b2c68d6064
@ -111,4 +111,14 @@
|
||||
#define TODO __ASSERT(false, "TODO: It hasn't implemented yet.")
|
||||
#define OOPS "Oops a bug!! report please."
|
||||
|
||||
// Returns the docstring of the function, which is a static const char* defined
|
||||
// just above the function by the DEF() macro below.
|
||||
#define DOCSTRING(fn) _pk_doc_##fn
|
||||
|
||||
// A macro to declare a function, with docstring, which is defined as
|
||||
// _pk_doc_<fn> = docstring; That'll used to generate function help text.
|
||||
#define DEF(fn, docstring) \
|
||||
static const char* DOCSTRING(fn) = docstring; \
|
||||
static void fn(PKVM* vm)
|
||||
|
||||
#endif //PK_COMMON_H
|
||||
|
@ -664,28 +664,6 @@ static void setNextValueToken(Parser* parser, _TokenType type, Var value);
|
||||
static void setNextToken(Parser* parser, _TokenType type);
|
||||
static bool matchChar(Parser* parser, char c);
|
||||
|
||||
#define _BETWEEN(a, c, b) (((a) <= (c)) && ((c) <= (b)))
|
||||
static inline bool _isCharHex(char c) {
|
||||
return (_BETWEEN('0', c, '9')
|
||||
|| _BETWEEN('a', c, 'z')
|
||||
|| _BETWEEN('A', c, 'Z'));
|
||||
}
|
||||
|
||||
static inline uint8_t _charHexVal(char c) {
|
||||
ASSERT(_isCharHex(c), OOPS);
|
||||
|
||||
if (_BETWEEN('0', c, '9')) {
|
||||
return c - '0';
|
||||
} else if (_BETWEEN('a', c, 'z')) {
|
||||
return c - 'a' + 10;
|
||||
} else if (_BETWEEN('A', c, 'Z')) {
|
||||
return c - 'A' + 10;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
#undef _BETWEEN
|
||||
|
||||
static void eatString(Compiler* compiler, bool single_quote) {
|
||||
Parser* parser = &compiler->parser;
|
||||
|
||||
@ -758,6 +736,7 @@ static void eatString(Compiler* compiler, bool single_quote) {
|
||||
case 'n': pkByteBufferWrite(&buff, parser->vm, '\n'); break;
|
||||
case 'r': pkByteBufferWrite(&buff, parser->vm, '\r'); break;
|
||||
case 't': pkByteBufferWrite(&buff, parser->vm, '\t'); break;
|
||||
case '\n': break; // Just ignore the next line.
|
||||
|
||||
// '$' In pocketlang string is used for interpolation.
|
||||
case '$': pkByteBufferWrite(&buff, parser->vm, '$'); break;
|
||||
@ -767,22 +746,22 @@ static void eatString(Compiler* compiler, bool single_quote) {
|
||||
uint8_t val = 0;
|
||||
|
||||
c = eatChar(parser);
|
||||
if (!_isCharHex(c)) {
|
||||
if (!utilIsCharHex(c)) {
|
||||
semanticError(compiler, makeErrToken(parser),
|
||||
"Invalid hex escape.");
|
||||
break;
|
||||
}
|
||||
|
||||
val = _charHexVal(c);
|
||||
val = utilCharHexVal(c);
|
||||
|
||||
c = eatChar(parser);
|
||||
if (!_isCharHex(c)) {
|
||||
if (!utilIsCharHex(c)) {
|
||||
semanticError(compiler, makeErrToken(parser),
|
||||
"Invalid hex escape.");
|
||||
break;
|
||||
}
|
||||
|
||||
val = (val << 4) | _charHexVal(c);
|
||||
val = (val << 4) | utilCharHexVal(c);
|
||||
|
||||
pkByteBufferWrite(&buff, parser->vm, val);
|
||||
|
||||
@ -791,7 +770,7 @@ static void eatString(Compiler* compiler, bool single_quote) {
|
||||
default:
|
||||
semanticError(compiler, makeErrToken(parser),
|
||||
"Invalid escape character.");
|
||||
return;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
pkByteBufferWrite(&buff, parser->vm, c);
|
||||
@ -900,7 +879,7 @@ static void eatNumber(Compiler* compiler) {
|
||||
c = peekChar(parser);
|
||||
|
||||
// The first digit should be hex digit.
|
||||
if (!_isCharHex(c)) {
|
||||
if (!utilIsCharHex(c)) {
|
||||
syntaxError(compiler, makeErrToken(parser), "Invalid hex literal.");
|
||||
return;
|
||||
|
||||
@ -908,7 +887,7 @@ static void eatNumber(Compiler* compiler) {
|
||||
do {
|
||||
// Consume the next digit.
|
||||
c = peekChar(parser);
|
||||
if (!_isCharHex(c)) break;
|
||||
if (!utilIsCharHex(c)) break;
|
||||
eatChar(parser);
|
||||
|
||||
// Check the length of the binary literal.
|
||||
@ -920,7 +899,7 @@ static void eatNumber(Compiler* compiler) {
|
||||
}
|
||||
|
||||
// "Append" the next digit at the end.
|
||||
hex = (hex << 4) | _charHexVal(c);
|
||||
hex = (hex << 4) | utilCharHexVal(c);
|
||||
|
||||
} while (true);
|
||||
|
||||
@ -2674,6 +2653,15 @@ static bool matchOperatorMethod(Compiler* compiler,
|
||||
"Expected keyword self for unary operator definition.");
|
||||
return false;
|
||||
}
|
||||
if (match(compiler, TK_LBRACKET)) {
|
||||
if (match(compiler, TK_RBRACKET)) {
|
||||
if (match(compiler, TK_EQ)) _RET("[]=", 2);
|
||||
_RET("[]", 1);
|
||||
}
|
||||
syntaxError(compiler, compiler->parser.previous,
|
||||
"Invalid operator method symbol.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (match(compiler, TK_PLUSEQ)) _RET("+=", 1);
|
||||
if (match(compiler, TK_MINUSEQ)) _RET("-=", 1);
|
||||
|
@ -154,10 +154,7 @@ void initializeScript(PKVM* vm, Module* module) {
|
||||
/* 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) {
|
||||
String* varToString(PKVM* vm, Var self, bool repr) {
|
||||
if (IS_OBJ_TYPE(self, OBJ_INST)) {
|
||||
|
||||
// The closure is retrieved from [self] thus, it doesn't need to be push
|
||||
@ -165,10 +162,20 @@ static inline String* _varToString(PKVM* vm, Var self) {
|
||||
// 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.
|
||||
bool has_method = false;
|
||||
if (!repr) {
|
||||
String* name = newString(vm, LITS__str); // TODO: static vm string?.
|
||||
vmPushTempRef(vm, &name->_super); // name.
|
||||
has_method = hasMethod(vm, self, name, &closure);
|
||||
vmPopTempRef(vm); // name.
|
||||
}
|
||||
|
||||
if (!has_method) {
|
||||
String* name = newString(vm, LITS__repr); // TODO: static vm string?.
|
||||
vmPushTempRef(vm, &name->_super); // name.
|
||||
has_method = hasMethod(vm, self, name, &closure);
|
||||
vmPopTempRef(vm); // name.
|
||||
}
|
||||
|
||||
if (has_method) {
|
||||
Var ret = VAR_NULL;
|
||||
@ -188,6 +195,7 @@ static inline String* _varToString(PKVM* vm, Var self) {
|
||||
// "fall throught" and call 'toString()' bellow.
|
||||
}
|
||||
|
||||
if (repr) return toRepr(vm, self);
|
||||
return toString(vm, self);
|
||||
}
|
||||
|
||||
@ -198,9 +206,9 @@ 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.
|
||||
vmPushTempRef(vm, &name->_super); // name.
|
||||
bool has_method = hasMethod(vm, self, name, &closure);
|
||||
vmPopTempRef(vm); // method.
|
||||
vmPopTempRef(vm); // name.
|
||||
|
||||
if (!has_method) return false;
|
||||
|
||||
@ -215,9 +223,9 @@ 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.
|
||||
vmPushTempRef(vm, &name->_super); // name.
|
||||
bool has_method = hasMethod(vm, self, name, &closure);
|
||||
vmPopTempRef(vm); // method.
|
||||
vmPopTempRef(vm); // name.
|
||||
|
||||
if (!has_method) return false;
|
||||
|
||||
@ -287,7 +295,7 @@ DEF(coreAssert,
|
||||
|
||||
if (argc == 2) {
|
||||
if (AS_OBJ(ARG(2))->type != OBJ_STRING) {
|
||||
msg = _varToString(vm, ARG(2));
|
||||
msg = varToString(vm, ARG(2), false);
|
||||
if (msg == NULL) return; //< Error at _to_string override.
|
||||
|
||||
} else {
|
||||
@ -380,7 +388,7 @@ DEF(coreToString,
|
||||
"str(value:var) -> string\n"
|
||||
"Returns the string representation of the value.") {
|
||||
|
||||
String* str = _varToString(vm, ARG(1));
|
||||
String* str = varToString(vm, ARG(1), false);
|
||||
if (str == NULL) RET(VAR_NULL);
|
||||
RET(VAR_OBJ(str));
|
||||
}
|
||||
@ -425,7 +433,7 @@ DEF(corePrint,
|
||||
|
||||
for (int i = 1; i <= ARGC; i++) {
|
||||
if (i != 1) vm->config.stdout_write(vm, " ");
|
||||
String* str = _varToString(vm, ARG(i));
|
||||
String* str = varToString(vm, ARG(i), false);
|
||||
if (str == NULL) RET(VAR_NULL);
|
||||
vm->config.stdout_write(vm, str->data);
|
||||
}
|
||||
@ -447,7 +455,7 @@ DEF(coreInput,
|
||||
if (vm->config.stdin_read == NULL) return;
|
||||
|
||||
if (argc == 1) {
|
||||
String* str = _varToString(vm, ARG(1));
|
||||
String* str = varToString(vm, ARG(1), false);
|
||||
if (str == NULL) RET(VAR_NULL);
|
||||
vm->config.stdout_write(vm, str->data);
|
||||
}
|
||||
@ -537,7 +545,7 @@ DEF(coreListJoin,
|
||||
pkByteBufferInit(&buff);
|
||||
|
||||
for (uint32_t i = 0; i < list->elements.count; i++) {
|
||||
String* str = _varToString(vm, list->elements.data[i]);
|
||||
String* str = varToString(vm, list->elements.data[i], false);
|
||||
if (str == NULL) RET(VAR_NULL);
|
||||
vmPushTempRef(vm, &str->_super); // elem
|
||||
pkByteBufferAddString(&buff, vm, str->data, str->length);
|
||||
@ -703,7 +711,7 @@ DEF(stdLangWrite,
|
||||
if (IS_OBJ_TYPE(arg, OBJ_STRING)) {
|
||||
str = (String*)AS_OBJ(arg);
|
||||
} else {
|
||||
str = _varToString(vm, ARG(1));
|
||||
str = varToString(vm, ARG(1), false);
|
||||
if (str == NULL) RET(VAR_NULL);
|
||||
}
|
||||
|
||||
@ -769,7 +777,7 @@ static void _ctorString(PKVM* vm) {
|
||||
RET(VAR_OBJ(newStringLength(vm, NULL, 0)));
|
||||
return;
|
||||
}
|
||||
String* str = _varToString(vm, ARG(1));
|
||||
String* str = varToString(vm, ARG(1), false);
|
||||
if (str == NULL) RET(VAR_NULL);
|
||||
RET(VAR_OBJ(str));
|
||||
}
|
||||
@ -828,6 +836,20 @@ DEF(_numberTimes,
|
||||
RET(VAR_NULL);
|
||||
}
|
||||
|
||||
DEF(_numberIsint,
|
||||
"Number.isint() -> bool\n"
|
||||
"Returns true if the number is a whold number, otherwise false.") {
|
||||
double n = AS_NUM(SELF);
|
||||
RET(VAR_BOOL(floor(n) == n));
|
||||
}
|
||||
|
||||
DEF(_numberIsbyte,
|
||||
"Number.isbyte() -> bool\n"
|
||||
"Returns true if the number is an integer and is between 0x00 and 0xff.") {
|
||||
double n = AS_NUM(SELF);
|
||||
RET(VAR_BOOL((floor(n) == n) && (0x00 <= n && n <= 0xff)));
|
||||
}
|
||||
|
||||
DEF(_listAppend,
|
||||
"List.append(value:var) -> List\n"
|
||||
"Append the [value] to the list and return the list.") {
|
||||
@ -925,7 +947,10 @@ static void initializePrimitiveClasses(PKVM* vm) {
|
||||
vmPopTempRef(vm); /* fn. */ \
|
||||
} while (false)
|
||||
|
||||
// TODO: write docs.
|
||||
ADD_METHOD(PK_NUMBER, "times", _numberTimes, 1);
|
||||
ADD_METHOD(PK_NUMBER, "isint", _numberIsint, 0);
|
||||
ADD_METHOD(PK_NUMBER, "isbyte", _numberIsbyte, 0);
|
||||
ADD_METHOD(PK_LIST, "append", _listAppend, 1);
|
||||
ADD_METHOD(PK_FIBER, "run", _fiberRun, -1);
|
||||
ADD_METHOD(PK_FIBER, "resume", _fiberResume, -1);
|
||||
@ -998,7 +1023,8 @@ static inline Closure* clsGetMethod(Class* cls, String* name) {
|
||||
for (int i = 0; i < (int)cls_->methods.count; i++) {
|
||||
Closure* method_ = cls_->methods.data[i];
|
||||
ASSERT(method_->fn->is_method, OOPS);
|
||||
if (IS_CSTR_EQ(name, method_->fn->name, name->length)) {
|
||||
const char* method_name = method_->fn->name;
|
||||
if (IS_CSTR_EQ(name, method_name, strlen(method_name))) {
|
||||
return method_;
|
||||
}
|
||||
}
|
||||
@ -1289,7 +1315,7 @@ Var varOpRange(PKVM* vm, Var v1, Var v2) {
|
||||
}
|
||||
|
||||
if (IS_OBJ_TYPE(v1, OBJ_STRING)) {
|
||||
String* str = _varToString(vm, v2);
|
||||
String* str = varToString(vm, v2, false);
|
||||
if (str == NULL) return VAR_NULL;
|
||||
String* concat = stringJoin(vm, (String*) AS_OBJ(v1), str);
|
||||
return VAR_OBJ(concat);
|
||||
@ -1742,7 +1768,7 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "Unhashable key '$'.",
|
||||
varTypeName(key)));
|
||||
} else {
|
||||
String* key_repr = toRepr(vm, key);
|
||||
String* key_repr = varToString(vm, key, true);
|
||||
vmPushTempRef(vm, &key_repr->_super); // key_repr.
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "Key '@' not exists", key_repr));
|
||||
vmPopTempRef(vm); // key_repr.
|
||||
@ -1756,6 +1782,13 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
||||
case OBJ_UPVALUE:
|
||||
UNREACHABLE(); // Not first class objects.
|
||||
|
||||
case OBJ_INST: {
|
||||
Var ret;
|
||||
if (_callBinaryOpMethod(vm, on, key, "[]", &ret)) {
|
||||
return ret;
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1804,6 +1837,22 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
|
||||
case OBJ_UPVALUE:
|
||||
UNREACHABLE();
|
||||
|
||||
case OBJ_INST: {
|
||||
|
||||
Closure* closure = NULL;
|
||||
String* name = newString(vm, "[]=");
|
||||
vmPushTempRef(vm, &name->_super); // name.
|
||||
bool has_method = hasMethod(vm, on, name, &closure);
|
||||
vmPopTempRef(vm); // name.
|
||||
|
||||
if (has_method) {
|
||||
Var args[2] = { key, value };
|
||||
vmCallMethod(vm, on, closure, 2, args, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
// restructre. The names of the macros are begin with LIST_ and the string.
|
||||
#define LITS__init "_init"
|
||||
#define LITS__str "_str"
|
||||
#define LITS__repr "_repr"
|
||||
|
||||
// Functions, methods, classes and other names which are intrenal / special to
|
||||
// pocketlang are starts with the following character (ex: @main, @literalFn).
|
||||
@ -91,6 +92,15 @@ Closure* getSuperMethod(PKVM* vm, Var self, String* name);
|
||||
// otherwise and if the [method] argument is not NULL, method will be set.
|
||||
bool hasMethod(PKVM* vm, Var self, String* name, Closure** method);
|
||||
|
||||
// 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.
|
||||
// if parameter [repr] is true it'll return repr string of the value and for
|
||||
// instances it'll call "_repr()" method.
|
||||
// Note that if _str method does not exists it'll use _repr method for to
|
||||
// string.
|
||||
String* varToString(PKVM* vm, Var self, bool repr);
|
||||
|
||||
Var varPositive(PKVM* vm, Var v); // Returns +v.
|
||||
Var varNegative(PKVM* vm, Var v); // Returns -v.
|
||||
Var varNot(PKVM* vm, Var v); // Returns !v.
|
||||
|
@ -113,16 +113,6 @@
|
||||
/* REUSABLE INTERNAL MACROS */
|
||||
/*****************************************************************************/
|
||||
|
||||
// Returns the docstring of the function, which is a static const char* defined
|
||||
// just above the function by the DEF() macro below.
|
||||
#define DOCSTRING(fn) _pk_doc_##fn
|
||||
|
||||
// A macro to declare a function, with docstring, which is defined as
|
||||
// _pk_doc_<fn> = docstring; That'll used to generate function help text.
|
||||
#define DEF(fn, docstring) \
|
||||
static const char* DOCSTRING(fn) = docstring; \
|
||||
static void fn(PKVM* vm)
|
||||
|
||||
// Here we're switching the FNV-1a hash value of the name (cstring). Which is
|
||||
// an efficient way than having multiple if (attrib == "name"). From O(n) * k
|
||||
// to O(1) where n is the length of the string and k is the number of string
|
||||
|
@ -49,11 +49,6 @@
|
||||
"Slot index is too large. Did you forget to call pkReserveSlots()?."); \
|
||||
} while (false)
|
||||
|
||||
// ARGC won't be the real arity if any slots allocated before calling argument
|
||||
// validation calling this first is the callers responsibility.
|
||||
#define VALIDATE_ARGC(arg) \
|
||||
ASSERT(arg > 0 && arg <= ARGC, "Invalid argument index.")
|
||||
|
||||
#define CHECK_FIBER_EXISTS(vm) \
|
||||
do { \
|
||||
ASSERT(vm->fiber != NULL, \
|
||||
@ -548,23 +543,23 @@ bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max) {
|
||||
}
|
||||
|
||||
// Set error for incompatible type provided as an argument. (TODO: got type).
|
||||
#define ERR_INVALID_SLOT_TYPE(ty_name) \
|
||||
#define ERR_INVALID_SLOT_TYPE(slot, ty_name) \
|
||||
do { \
|
||||
char buff[STR_INT_BUFF_SIZE]; \
|
||||
sprintf(buff, "%d", arg); \
|
||||
sprintf(buff, "%d", slot); \
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "Expected a '$' at slot $.", \
|
||||
ty_name, buff)); \
|
||||
} while (false)
|
||||
|
||||
// FIXME: If the user needs just the boolean value of the object, they should
|
||||
// use pkGetSlotBool().
|
||||
PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int arg, bool* value) {
|
||||
PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int slot, bool* value) {
|
||||
CHECK_FIBER_EXISTS(vm);
|
||||
VALIDATE_ARGC(arg);
|
||||
VALIDATE_SLOT_INDEX(slot);
|
||||
|
||||
Var val = ARG(arg);
|
||||
Var val = ARG(slot);
|
||||
if (!IS_BOOL(val)) {
|
||||
ERR_INVALID_SLOT_TYPE("Boolean");
|
||||
ERR_INVALID_SLOT_TYPE(slot, "Boolean");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -572,13 +567,13 @@ PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int arg, bool* value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int arg, double* value) {
|
||||
PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int slot, double* value) {
|
||||
CHECK_FIBER_EXISTS(vm);
|
||||
VALIDATE_ARGC(arg);
|
||||
VALIDATE_SLOT_INDEX(slot);
|
||||
|
||||
Var val = ARG(arg);
|
||||
Var val = ARG(slot);
|
||||
if (!IS_NUM(val)) {
|
||||
ERR_INVALID_SLOT_TYPE("Number");
|
||||
ERR_INVALID_SLOT_TYPE(slot, "Number");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -586,14 +581,14 @@ PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int arg, double* value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int arg, const char** value,
|
||||
PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int slot, const char** value,
|
||||
uint32_t* length) {
|
||||
CHECK_FIBER_EXISTS(vm);
|
||||
VALIDATE_ARGC(arg);
|
||||
VALIDATE_SLOT_INDEX(slot);
|
||||
|
||||
Var val = ARG(arg);
|
||||
Var val = ARG(slot);
|
||||
if (!IS_OBJ_TYPE(val, OBJ_STRING)) {
|
||||
ERR_INVALID_SLOT_TYPE("String");
|
||||
ERR_INVALID_SLOT_TYPE(slot, "String");
|
||||
return false;
|
||||
}
|
||||
String* str = (String*)AS_OBJ(val);
|
||||
@ -602,27 +597,27 @@ PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int arg, const char** value,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pkValidateSlotType(PKVM* vm, int arg, PkVarType type) {
|
||||
bool pkValidateSlotType(PKVM* vm, int slot, PkVarType type) {
|
||||
CHECK_FIBER_EXISTS(vm);
|
||||
VALIDATE_ARGC(arg);
|
||||
if (getVarType(ARG(arg)) != type) {
|
||||
ERR_INVALID_SLOT_TYPE(getPkVarTypeName(type));
|
||||
VALIDATE_SLOT_INDEX(slot);
|
||||
if (getVarType(ARG(slot)) != type) {
|
||||
ERR_INVALID_SLOT_TYPE(slot, getPkVarTypeName(type));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PK_PUBLIC bool pkValidateSlotInstanceOf(PKVM* vm, int arg, int cls) {
|
||||
PK_PUBLIC bool pkValidateSlotInstanceOf(PKVM* vm, int slot, int cls) {
|
||||
CHECK_FIBER_EXISTS(vm);
|
||||
VALIDATE_ARGC(arg);
|
||||
VALIDATE_SLOT_INDEX(slot);
|
||||
VALIDATE_SLOT_INDEX(cls);
|
||||
|
||||
Var instance = ARG(arg), class_ = SLOT(cls);
|
||||
Var instance = ARG(slot), class_ = SLOT(cls);
|
||||
if (!varIsType(vm, instance, class_)) {
|
||||
// If [class_] is not a valid class, it's already an error.
|
||||
if (VM_HAS_ERROR(vm)) return false;
|
||||
ERR_INVALID_SLOT_TYPE(((Class*)AS_OBJ(class_))->name->data);
|
||||
ERR_INVALID_SLOT_TYPE(slot, ((Class*)AS_OBJ(class_))->name->data);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -741,11 +736,12 @@ void pkSetSlotStringFmt(PKVM* vm, int index, const char* fmt, ...) {
|
||||
|
||||
void pkSetSlotHandle(PKVM* vm, int index, PkHandle* handle) {
|
||||
CHECK_FIBER_EXISTS(vm);
|
||||
CHECK_ARG_NULL(handle);
|
||||
VALIDATE_SLOT_INDEX(index);
|
||||
SET_SLOT(index, handle->value);
|
||||
}
|
||||
|
||||
bool pkSetAttribute(PKVM* vm, int instance, int value, const char* name) {
|
||||
bool pkSetAttribute(PKVM* vm, int instance, const char* name, int value) {
|
||||
CHECK_FIBER_EXISTS(vm);
|
||||
CHECK_ARG_NULL(name);
|
||||
VALIDATE_SLOT_INDEX(instance);
|
||||
@ -898,15 +894,11 @@ void pkGetClass(PKVM* vm, int instance, int index) {
|
||||
SET_SLOT(index, VAR_OBJ(getClass(vm, SLOT(instance))));
|
||||
}
|
||||
|
||||
#undef CHECK_RUNTIME
|
||||
#undef VALIDATE_ARGC
|
||||
#undef ERR_INVALID_ARG_TYPE
|
||||
#undef ARG
|
||||
#undef SLOT
|
||||
#undef SET_SLOT
|
||||
#undef ARGC
|
||||
#undef CHECK_NULL
|
||||
#undef CHECK_TYPE
|
||||
|
||||
/*****************************************************************************/
|
||||
/* INTERNAL */
|
||||
|
@ -40,6 +40,40 @@ bool utilIsDigit(char c) {
|
||||
return ('0' <= c && c <= '9');
|
||||
}
|
||||
|
||||
#define _BETWEEN(a, c, b) (((a) <= (c)) && ((c) <= (b)))
|
||||
bool utilIsCharHex(char c) {
|
||||
return (_BETWEEN('0', c, '9')
|
||||
|| _BETWEEN('a', c, 'z')
|
||||
|| _BETWEEN('A', c, 'Z'));
|
||||
}
|
||||
|
||||
uint8_t utilCharHexVal(char c) {
|
||||
assert(utilIsCharHex(c));
|
||||
|
||||
if (_BETWEEN('0', c, '9')) {
|
||||
return c - '0';
|
||||
} else if (_BETWEEN('a', c, 'z')) {
|
||||
return c - 'a' + 10;
|
||||
} else if (_BETWEEN('A', c, 'Z')) {
|
||||
return c - 'A' + 10;
|
||||
}
|
||||
|
||||
assert(false); // Unreachable.
|
||||
return 0;
|
||||
}
|
||||
|
||||
char utilHexDigit(uint8_t value, bool uppercase) {
|
||||
assert(_BETWEEN(0x0, value, 0xf));
|
||||
|
||||
if (_BETWEEN(0, value, 9)) return '0' + value;
|
||||
|
||||
return (uppercase)
|
||||
? 'A' + (value - 10)
|
||||
: 'a' + (value - 10);
|
||||
|
||||
}
|
||||
#undef _BETWEEN
|
||||
|
||||
// A union to reinterpret a double as raw bits and back.
|
||||
typedef union {
|
||||
uint64_t bits64;
|
||||
|
@ -23,6 +23,17 @@ bool utilIsName(char c);
|
||||
// Returns true if `c` is [0-9].
|
||||
bool utilIsDigit(char c);
|
||||
|
||||
// Returns true if character is a hex digit.
|
||||
// ie [a-zA-Z0-9].
|
||||
bool utilIsCharHex(char c);
|
||||
|
||||
// Returns the decimal value of the hex digit.
|
||||
// char should match [a-zA-Z0-9].
|
||||
uint8_t utilCharHexVal(char c);
|
||||
|
||||
// Returns the values hex digit. The value must be 0x0 <= val < 0xf
|
||||
char utilHexDigit(uint8_t value, bool uppercase);
|
||||
|
||||
// Return Reinterpreted bits of the double value.
|
||||
uint64_t utilDoubleToBits(double value);
|
||||
|
||||
|
@ -1212,6 +1212,11 @@ const char* varTypeName(Var v) {
|
||||
|
||||
ASSERT(IS_OBJ(v), OOPS);
|
||||
Object* obj = AS_OBJ(v);
|
||||
|
||||
if (obj->type == OBJ_INST) {
|
||||
return ((Instance*)obj)->cls->name->data;
|
||||
}
|
||||
|
||||
return getObjectTypeName(obj->type);
|
||||
}
|
||||
|
||||
@ -1350,17 +1355,26 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff,
|
||||
} else {
|
||||
// If recursive return with quotes (ex: [42, "hello", 0..10]).
|
||||
pkByteBufferWrite(buff, vm, '"');
|
||||
for (const char* c = str->data; *c != '\0'; c++) {
|
||||
switch (*c) {
|
||||
for (uint32_t i = 0; i < str->length; i++) {
|
||||
char c = str->data[i];
|
||||
switch (c) {
|
||||
case '"': pkByteBufferAddString(buff, vm, "\\\"", 2); break;
|
||||
case '\\': pkByteBufferAddString(buff, vm, "\\\\", 2); break;
|
||||
case '\n': pkByteBufferAddString(buff, vm, "\\n", 2); break;
|
||||
case '\r': pkByteBufferAddString(buff, vm, "\\r", 2); break;
|
||||
case '\t': pkByteBufferAddString(buff, vm, "\\t", 2); break;
|
||||
|
||||
default:
|
||||
pkByteBufferWrite(buff, vm, *c);
|
||||
break;
|
||||
default: {
|
||||
if (isprint(c)) pkByteBufferWrite(buff, vm, c);
|
||||
else {
|
||||
pkByteBufferAddString(buff, vm, "\\x", 2);
|
||||
uint8_t byte = (uint8_t) c;
|
||||
pkByteBufferWrite(buff, vm, utilHexDigit(((byte >> 4) & 0xf),
|
||||
false));
|
||||
pkByteBufferWrite(buff, vm, utilHexDigit(((byte >> 0) & 0xf),
|
||||
false));
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
pkByteBufferWrite(buff, vm, '"');
|
||||
|
@ -749,7 +749,9 @@ const char* getPkVarTypeName(PkVarType type);
|
||||
// Returns the type name of the ObjectType enum value.
|
||||
const char* getObjectTypeName(ObjectType type);
|
||||
|
||||
// Returns the type name of the var [v].
|
||||
// Returns the type name of the var [v]. If [v] is an instance of a class
|
||||
// the return pointer will be the class name string's data, which would be
|
||||
// dangling if [v] is garbage collected.
|
||||
const char* varTypeName(Var v);
|
||||
|
||||
// Returns the PkVarType of the first class varaible [v].
|
||||
|
@ -183,7 +183,7 @@ bool vmPrepareFiber(PKVM* vm, Fiber* fiber, int argc, Var* argv) {
|
||||
ASSERT(fiber->closure->fn->arity >= -1,
|
||||
OOPS " (Forget to initialize arity.)");
|
||||
|
||||
if (argc != fiber->closure->fn->arity) {
|
||||
if ((fiber->closure->fn->arity != -1) && argc != fiber->closure->fn->arity) {
|
||||
char buff[STR_INT_BUFF_SIZE];
|
||||
sprintf(buff, "%d", fiber->closure->fn->arity);
|
||||
_ERR_FAIL(stringFormat(vm, "Expected exactly $ argument(s).", buff));
|
||||
@ -1128,9 +1128,9 @@ L_do_call:
|
||||
}
|
||||
|
||||
} else {
|
||||
RUNTIME_ERROR(stringFormat(vm, "$ $(@).", "Expected a callable to "
|
||||
RUNTIME_ERROR(stringFormat(vm, "$ '$'.", "Expected a callable to "
|
||||
"call, instead got",
|
||||
varTypeName(callable), toString(vm, callable)));
|
||||
varTypeName(callable)));
|
||||
}
|
||||
|
||||
// If we reached here it's a valid callable.
|
||||
@ -1785,7 +1785,7 @@ L_do_call:
|
||||
if (vm->config.stdout_write != NULL) {
|
||||
Var tmp = PEEK(-1);
|
||||
if (!IS_NULL(tmp)) {
|
||||
vm->config.stdout_write(vm, toRepr(vm, tmp)->data);
|
||||
vm->config.stdout_write(vm, varToString(vm, tmp, true)->data);
|
||||
vm->config.stdout_write(vm, "\n");
|
||||
}
|
||||
}
|
||||
|
@ -283,26 +283,26 @@ PK_PUBLIC int pkGetArgc(const PKVM* vm);
|
||||
// that min <= max, and pocketlang won't validate this in release binary.
|
||||
PK_PUBLIC bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max);
|
||||
|
||||
// Helper function to check if the argument at the [arg] slot is Boolean and
|
||||
// Helper function to check if the argument at the [slot] slot is Boolean and
|
||||
// if not set a runtime error.
|
||||
PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int arg, bool* value);
|
||||
PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int slot, bool* value);
|
||||
|
||||
// Helper function to check if the argument at the [arg] slot is Number and
|
||||
// Helper function to check if the argument at the [slot] slot is Number and
|
||||
// if not set a runtime error.
|
||||
PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int arg, double* value);
|
||||
PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int slot, double* value);
|
||||
|
||||
// Helper function to check if the argument at the [arg] slot is String and
|
||||
// Helper function to check if the argument at the [slot] slot is String and
|
||||
// if not set a runtime error.
|
||||
PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int arg,
|
||||
PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int slot,
|
||||
const char** value, uint32_t* length);
|
||||
|
||||
// Helper function to check if the argument at the [arg] slot is of type
|
||||
// Helper function to check if the argument at the [slot] slot is of type
|
||||
// [type] and if not sets a runtime error.
|
||||
PK_PUBLIC bool pkValidateSlotType(PKVM* vm, int arg, PkVarType type);
|
||||
PK_PUBLIC bool pkValidateSlotType(PKVM* vm, int slot, PkVarType type);
|
||||
|
||||
// Helper function to check if the argument at the [arg] slot is an instance
|
||||
// Helper function to check if the argument at the [slot] slot is an instance
|
||||
// of the class which is at the [cls] index. If not set a runtime error.
|
||||
PK_PUBLIC bool pkValidateSlotInstanceOf(PKVM* vm, int arg, int cls);
|
||||
PK_PUBLIC bool pkValidateSlotInstanceOf(PKVM* vm, int slot, int cls);
|
||||
|
||||
// Helper function to check if the instance at the [inst] slot is an instance
|
||||
// of the class which is at the [cls] index. The value will be set to [val]
|
||||
@ -374,16 +374,16 @@ PK_PUBLIC void pkSetSlotHandle(PKVM* vm, int index, PkHandle* handle);
|
||||
/* POCKET FFI */
|
||||
/*****************************************************************************/
|
||||
|
||||
// Set the attribute with [name] of the instance at the [instance] slot to
|
||||
// the value at the [value] index slot. Return true on success.
|
||||
PK_PUBLIC bool pkSetAttribute(PKVM* vm, int instance, int value,
|
||||
const char* name);
|
||||
|
||||
// Get the attribute with [name] of the instance at the [instance] slot and
|
||||
// place it at the [index] slot. Return true on success.
|
||||
PK_PUBLIC bool pkGetAttribute(PKVM* vm, int instance, const char* name,
|
||||
int index);
|
||||
|
||||
// Set the attribute with [name] of the instance at the [instance] slot to
|
||||
// the value at the [value] index slot. Return true on success.
|
||||
PK_PUBLIC bool pkSetAttribute(PKVM* vm, int instance,
|
||||
const char* name, int value);
|
||||
|
||||
// Place the [self] instance at the [index] slot.
|
||||
PK_PUBLIC void pkPlaceSelf(PKVM* vm, int index);
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#endif
|
||||
|
||||
void registerModuleMath(PKVM* vm);
|
||||
void registerModuleTypes(PKVM* vm);
|
||||
void registerModuleTime(PKVM* vm);
|
||||
void registerModuleIO(PKVM* vm);
|
||||
void registerModulePath(PKVM* vm);
|
||||
@ -16,6 +17,7 @@ void registerModuleDummy(PKVM* vm);
|
||||
|
||||
void registerLibs(PKVM* vm) {
|
||||
registerModuleMath(vm);
|
||||
registerModuleTypes(vm);
|
||||
registerModuleTime(vm);
|
||||
registerModuleIO(vm);
|
||||
registerModulePath(vm);
|
||||
|
@ -19,98 +19,15 @@
|
||||
/* MODULES INTERNAL */
|
||||
/*****************************************************************************/
|
||||
|
||||
// We're re defining some core internal macros here which should be considered
|
||||
// as macro re-definition in amalgamated source, so we're skipping it for
|
||||
// amalgumated build.
|
||||
// FIXME:
|
||||
// Since this are part of the "standard" pocketlang libraries, we can include
|
||||
// pocketlang internals here using the relative path, however it'll make these
|
||||
// libraries less "portable" in a sence that these files cannot be just drag
|
||||
// and dropped into another embedded application where is cannot find the
|
||||
// relative include.
|
||||
//
|
||||
#ifndef PK_AMALGAMATED
|
||||
|
||||
#define TOSTRING(x) #x
|
||||
#define STRINGIFY(x) TOSTRING(x)
|
||||
|
||||
// CONCAT_LINE(X) will result evaluvated X<__LINE__>.
|
||||
#define __CONCAT_LINE_INTERNAL_R(a, b) a ## b
|
||||
#define __CONCAT_LINE_INTERNAL_F(a, b) __CONCAT_LINE_INTERNAL_R(a, b)
|
||||
#define CONCAT_LINE(X) __CONCAT_LINE_INTERNAL_F(X, __LINE__)
|
||||
|
||||
// The internal assertion macro, this will print error and break regardless of
|
||||
// the build target (debug or release). Use ASSERT() for debug assertion and
|
||||
// use __ASSERT() for TODOs.
|
||||
#define __ASSERT(condition, message) \
|
||||
do { \
|
||||
if (!(condition)) { \
|
||||
fprintf(stderr, "Assertion failed: %s\n\tat %s() (%s:%i)\n", \
|
||||
message, __func__, __FILE__, __LINE__); \
|
||||
DEBUG_BREAK(); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define NO_OP do {} while (false)
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define DEBUG_BREAK() __debugbreak()
|
||||
#else
|
||||
#define DEBUG_BREAK() __builtin_trap()
|
||||
#endif
|
||||
|
||||
// This will terminate the compilation if the [condition] is false, because of
|
||||
// char _assertion_failure_<__LINE__>[-1] evaluated.
|
||||
#define STATIC_ASSERT(condition) \
|
||||
static char CONCAT_LINE(_assertion_failure_)[2*!!(condition) - 1]
|
||||
|
||||
#define ASSERT(condition, message) __ASSERT(condition, message)
|
||||
|
||||
#define ASSERT_INDEX(index, size) \
|
||||
ASSERT(index >= 0 && index < size, "Index out of bounds.")
|
||||
|
||||
#define UNREACHABLE() \
|
||||
do { \
|
||||
fprintf(stderr, "Execution reached an unreachable path\n" \
|
||||
"\tat %s() (%s:%i)\n", __func__, __FILE__, __LINE__); \
|
||||
DEBUG_BREAK(); \
|
||||
abort(); \
|
||||
} while (false)
|
||||
|
||||
#else
|
||||
|
||||
#define STATIC_ASSERT(condition) NO_OP
|
||||
|
||||
#define DEBUG_BREAK() NO_OP
|
||||
#define ASSERT(condition, message) NO_OP
|
||||
#define ASSERT_INDEX(index, size) NO_OP
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define UNREACHABLE() __assume(0)
|
||||
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
|
||||
#define UNREACHABLE() __builtin_unreachable()
|
||||
#elif defined(__EMSCRIPTEN__) || defined(__clang__)
|
||||
#if __has_builtin(__builtin_unreachable)
|
||||
#define UNREACHABLE() __builtin_unreachable()
|
||||
#else
|
||||
#define UNREACHABLE() NO_OP
|
||||
#endif
|
||||
#else
|
||||
#define UNREACHABLE() NO_OP
|
||||
#endif
|
||||
|
||||
#endif // DEBUG
|
||||
|
||||
// Using __ASSERT() for make it crash in release binary too.
|
||||
#define TODO __ASSERT(false, "TODO: It hasn't implemented yet.")
|
||||
#define OOPS "Oops a bug!! report please."
|
||||
|
||||
// Returns the docstring of the function, which is a static const char* defined
|
||||
// just above the function by the DEF() macro below.
|
||||
#define DOCSTRING(fn) __doc_##fn
|
||||
|
||||
// A macro to declare a function, with docstring, which is defined as
|
||||
// ___doc_<fn> = docstring; That'll used to generate function help text.
|
||||
#define DEF(fn, docstring) \
|
||||
static const char* DOCSTRING(fn) = docstring; \
|
||||
static void fn(PKVM* vm)
|
||||
|
||||
#include "../core/common.h"
|
||||
#endif // PK_AMALGAMATED
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -189,7 +189,7 @@ void registerModuleMath(PKVM* vm) {
|
||||
pkReserveSlots(vm, 2);
|
||||
pkSetSlotHandle(vm, 0, math); // slot[0] = math
|
||||
pkSetSlotNumber(vm, 1, PK_PI); // slot[1] = 3.14
|
||||
pkSetAttribute(vm, 0, 1, "PI"); // slot[0].PI = slot[1]
|
||||
pkSetAttribute(vm, 0, "PI", 1); // slot[0].PI = slot[1]
|
||||
|
||||
pkModuleAddFunction(vm, math, "floor", stdMathFloor, 1);
|
||||
pkModuleAddFunction(vm, math, "ceil", stdMathCeil, 1);
|
||||
|
266
src/libs/std_types.c
Normal file
266
src/libs/std_types.c
Normal file
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* Copyright (c) 2020-2022 Thakee Nathees
|
||||
* Copyright (c) 2021-2022 Pocketlang Contributors
|
||||
* Distributed Under The MIT License
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#ifndef PK_AMALGAMATED
|
||||
#include "libs.h"
|
||||
#endif
|
||||
|
||||
/*****************************************************************************/
|
||||
/* BYTE BUFFER */
|
||||
/*****************************************************************************/
|
||||
|
||||
#ifndef PK_AMALGAMATED
|
||||
#include "../core/value.h"
|
||||
#endif
|
||||
|
||||
void* _bytebuffNew(PKVM* vm) {
|
||||
pkByteBuffer* self = pkRealloc(vm, NULL, sizeof(pkByteBuffer));
|
||||
pkByteBufferInit(self);
|
||||
return self;
|
||||
}
|
||||
|
||||
void _bytebuffDelete(PKVM* vm, void* buff) {
|
||||
pkRealloc(vm, buff, 0);
|
||||
}
|
||||
|
||||
void _bytebuffReserve(PKVM* vm) {
|
||||
double size;
|
||||
if (!pkValidateSlotNumber(vm, 1, &size)) return;
|
||||
|
||||
pkByteBuffer* self = pkGetSelf(vm);
|
||||
pkByteBufferReserve(self, vm, (size_t) size);
|
||||
}
|
||||
|
||||
void _bytebuffClear(PKVM* vm) {
|
||||
// TODO: Should I also zero or reduce the capacity?
|
||||
pkByteBuffer* self = pkGetSelf(vm);
|
||||
self->count = 0;
|
||||
}
|
||||
|
||||
// Returns the length of bytes were written.
|
||||
void _bytebuffWrite(PKVM* vm) {
|
||||
pkByteBuffer* self = pkGetSelf(vm);
|
||||
|
||||
PkVarType type = pkGetSlotType(vm, 1);
|
||||
|
||||
switch (type) {
|
||||
case PK_BOOL:
|
||||
pkByteBufferWrite(self, vm, pkGetSlotBool(vm, 1) ? 1 : 0);
|
||||
pkSetSlotNumber(vm, 0, 1);
|
||||
return;
|
||||
|
||||
case PK_NUMBER: {
|
||||
double n = pkGetSlotNumber(vm, 1);
|
||||
if (floor(n) != n) {
|
||||
pkSetRuntimeErrorFmt(vm, "Expected an integer, got float %g.", n);
|
||||
return;
|
||||
}
|
||||
int64_t i = (int64_t) n;
|
||||
if (i < 0x00 || i > 0xff) {
|
||||
pkSetRuntimeErrorFmt(vm, "Expected integer in range "
|
||||
"0x00 to 0xff, got %i.", i);
|
||||
return;
|
||||
}
|
||||
|
||||
pkByteBufferWrite(self, vm, (uint8_t) i);
|
||||
pkSetSlotNumber(vm, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
case PK_STRING: {
|
||||
uint32_t length;
|
||||
const char* str = pkGetSlotString(vm, 1, &length);
|
||||
pkByteBufferAddString(self, vm, str, length);
|
||||
pkSetSlotNumber(vm, 0, (double) length);
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//< Pocketlang internal function.
|
||||
const char* name = getPkVarTypeName(type);
|
||||
pkSetRuntimeErrorFmt(vm, "Object %s cannot be written to ByteBuffer.", name);
|
||||
|
||||
}
|
||||
|
||||
void _bytebuffSubscriptGet(PKVM* vm) {
|
||||
double index;
|
||||
if (!pkValidateSlotNumber(vm, 1, &index)) return;
|
||||
if (floor(index) != index) {
|
||||
pkSetRuntimeError(vm, "Expected an integer but got number.");
|
||||
return;
|
||||
}
|
||||
|
||||
pkByteBuffer* self = pkGetSelf(vm);
|
||||
|
||||
if (index < 0 || index >= self->count) {
|
||||
pkSetRuntimeError(vm, "Index out of bound");
|
||||
return;
|
||||
}
|
||||
|
||||
pkSetSlotNumber(vm, 0, self->data[(uint32_t)index]);
|
||||
|
||||
}
|
||||
|
||||
void _bytebuffSubscriptSet(PKVM* vm) {
|
||||
double index, value;
|
||||
if (!pkValidateSlotNumber(vm, 1, &index)) return;
|
||||
if (!pkValidateSlotNumber(vm, 2, &value)) return;
|
||||
|
||||
if (floor(index) != index) {
|
||||
pkSetRuntimeError(vm, "Expected an integer but got float.");
|
||||
return;
|
||||
}
|
||||
if (floor(value) != value) {
|
||||
pkSetRuntimeError(vm, "Expected an integer but got float.");
|
||||
return;
|
||||
}
|
||||
|
||||
pkByteBuffer* self = pkGetSelf(vm);
|
||||
|
||||
if (index < 0 || index >= self->count) {
|
||||
pkSetRuntimeError(vm, "Index out of bound");
|
||||
return;
|
||||
}
|
||||
|
||||
if (value < 0x00 || value > 0xff) {
|
||||
pkSetRuntimeError(vm, "Value should be in range 0x00 to 0xff.");
|
||||
return;
|
||||
}
|
||||
|
||||
self->data[(uint32_t) index] = (uint8_t) value;
|
||||
|
||||
}
|
||||
|
||||
void _bytebuffString(PKVM* vm) {
|
||||
pkByteBuffer* self = pkGetSelf(vm);
|
||||
pkSetSlotStringLength(vm, 0, self->data, self->count);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* VECTOR */
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
double x, y, z;
|
||||
} Vector;
|
||||
|
||||
void* _vectorNew(PKVM* vm) {
|
||||
Vector* vec = pkRealloc(vm, NULL, sizeof(Vector));
|
||||
memset(vec, 0, sizeof(Vector));
|
||||
return vec;
|
||||
}
|
||||
|
||||
void _vectorDelete(PKVM* vm, void* vec) {
|
||||
pkRealloc(vm, vec, 0);
|
||||
}
|
||||
|
||||
void _vectorInit(PKVM* vm) {
|
||||
int argc = pkGetArgc(vm);
|
||||
if (!pkCheckArgcRange(vm, argc, 0, 3)) return;
|
||||
|
||||
double x, y, z;
|
||||
Vector* vec = pkGetSelf(vm);
|
||||
|
||||
if (argc == 0) return;
|
||||
if (argc >= 1) {
|
||||
if (!pkValidateSlotNumber(vm, 1, &x)) return;
|
||||
vec->x = x;
|
||||
}
|
||||
|
||||
if (argc >= 2) {
|
||||
if (!pkValidateSlotNumber(vm, 2, &y)) return;
|
||||
vec->y = y;
|
||||
}
|
||||
|
||||
if (argc == 3) {
|
||||
if (!pkValidateSlotNumber(vm, 3, &z)) return;
|
||||
vec->z = z;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void _vectorGetter(PKVM* vm) {
|
||||
const char* name; uint32_t length;
|
||||
if (!pkValidateSlotString(vm, 1, &name, &length)) return;
|
||||
|
||||
Vector* vec = pkGetSelf(vm);
|
||||
if (length == 1) {
|
||||
if (*name == 'x') {
|
||||
pkSetSlotNumber(vm, 0, vec->x);
|
||||
return;
|
||||
} else if (*name == 'y') {
|
||||
pkSetSlotNumber(vm, 0, vec->y);
|
||||
return;
|
||||
} else if (*name == 'z') {
|
||||
pkSetSlotNumber(vm, 0, vec->z);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _vectorSetter(PKVM* vm) {
|
||||
const char* name; uint32_t length;
|
||||
if (!pkValidateSlotString(vm, 1, &name, &length)) return;
|
||||
|
||||
Vector* vec = pkGetSelf(vm);
|
||||
|
||||
if (length == 1) {
|
||||
if (*name == 'x') {
|
||||
double val;
|
||||
if (!pkValidateSlotNumber(vm, 2, &val)) return;
|
||||
vec->x = val; return;
|
||||
} else if (*name == 'y') {
|
||||
double val;
|
||||
if (!pkValidateSlotNumber(vm, 2, &val)) return;
|
||||
vec->y = val; return;
|
||||
} else if (*name == 'z') {
|
||||
double val;
|
||||
if (!pkValidateSlotNumber(vm, 2, &val)) return;
|
||||
vec->z = val; return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _vectorRepr(PKVM* vm) {
|
||||
Vector* vec = pkGetSelf(vm);
|
||||
pkSetSlotStringFmt(vm, 0, "[%g, %g, %g]", vec->x, vec->y, vec->z);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* REGISTER TYPES */
|
||||
/*****************************************************************************/
|
||||
|
||||
void registerModuleTypes(PKVM* vm) {
|
||||
PkHandle* types = pkNewModule(vm, "types");
|
||||
|
||||
PkHandle* cls_byte_buffer = pkNewClass(vm, "ByteBuffer", NULL, types,
|
||||
_bytebuffNew, _bytebuffDelete);
|
||||
|
||||
pkClassAddMethod(vm, cls_byte_buffer, "[]", _bytebuffSubscriptGet, 1);
|
||||
pkClassAddMethod(vm, cls_byte_buffer, "[]=", _bytebuffSubscriptSet, 2);
|
||||
pkClassAddMethod(vm, cls_byte_buffer, "reserve", _bytebuffReserve, 1);
|
||||
pkClassAddMethod(vm, cls_byte_buffer, "clear", _bytebuffClear, 0);
|
||||
pkClassAddMethod(vm, cls_byte_buffer, "write", _bytebuffWrite, 1);
|
||||
pkClassAddMethod(vm, cls_byte_buffer, "string", _bytebuffString, 0);
|
||||
pkReleaseHandle(vm, cls_byte_buffer);
|
||||
|
||||
// TODO: add move mthods.
|
||||
PkHandle* cls_vector = pkNewClass(vm, "Vector", NULL, types,
|
||||
_vectorNew, _vectorDelete);
|
||||
pkClassAddMethod(vm, cls_vector, "_init", _vectorInit, -1);
|
||||
pkClassAddMethod(vm, cls_vector, "@getter", _vectorGetter, 1);
|
||||
pkClassAddMethod(vm, cls_vector, "@setter", _vectorSetter, 2);
|
||||
pkClassAddMethod(vm, cls_vector, "_repr", _vectorRepr, 0);
|
||||
pkReleaseHandle(vm, cls_vector);
|
||||
|
||||
pkRegisterModule(vm, types);
|
||||
pkReleaseHandle(vm, types);
|
||||
}
|
@ -7,6 +7,9 @@ assert(42 % 40.0 == 2)
|
||||
assert("'\"'" == '\'"\'')
|
||||
assert("testing" == "test" + "ing")
|
||||
|
||||
assert("foo \
|
||||
bar" == "foo bar")
|
||||
|
||||
assert(-0b10110010 == -178 and 0b11001010 == 202)
|
||||
assert(0b1111111111111111 == 65535)
|
||||
assert(
|
||||
|
@ -17,16 +17,16 @@ integrate pocket VM with your application
|
||||
|
||||
#### `example0.c` - Contains how to run simple pocket script
|
||||
```
|
||||
gcc example0.c -o example0 ../../src/*.c -I../../src/include -lm
|
||||
gcc example0.c -o example0 ../../src/core/*.c ../../src/libs/*.c -I../../src/include -lm
|
||||
```
|
||||
|
||||
#### `example1.c` - Contains how to pass values between pocket VM and C
|
||||
```
|
||||
gcc example1.c -o example1 ../../src/*.c -I../../src/include -lm
|
||||
gcc example1.c -o example1 ../../src/core/*.c ../../src/libs/*.c -I../../src/include -lm
|
||||
```
|
||||
|
||||
#### `example2.c` - Contains how to create your own custom native type in C
|
||||
```
|
||||
gcc example2.c -o example2 ../../src/*.c -I../../src/include -lm
|
||||
gcc example2.c -o example2 ../../src/core/*.c ../../src/libs/*.c -I../../src/include -lm
|
||||
```
|
||||
|
||||
|
@ -43,16 +43,16 @@ typedef struct {
|
||||
} Vector;
|
||||
|
||||
// Native instance allocation callback.
|
||||
void* _newVec() {
|
||||
Vector* vec = (Vector*)malloc(sizeof(Vector));
|
||||
void* _newVec(PKVM* vm) {
|
||||
Vector* vec = pkRealloc(vm, NULL, sizeof(Vector));
|
||||
vec->x = 0;
|
||||
vec->y = 0;
|
||||
return vec;
|
||||
}
|
||||
|
||||
// Native instance de-allocatoion callback.
|
||||
void _deleteVec(void* vector) {
|
||||
free(vector);
|
||||
void _deleteVec(PKVM* vm, void* vec) {
|
||||
pkRealloc(vm, vec, 0);
|
||||
}
|
||||
|
||||
void _vecGetter(PKVM* vm) {
|
||||
|
Loading…
Reference in New Issue
Block a user