Merge pull request #234 from ThakeeNathees/bytebuffer

More code enhancements (read bellow)
This commit is contained in:
Thakee Nathees 2022-05-23 04:23:06 +05:30 committed by GitHub
commit b2c68d6064
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 506 additions and 218 deletions

View File

@ -111,4 +111,14 @@
#define TODO __ASSERT(false, "TODO: It hasn't implemented yet.") #define TODO __ASSERT(false, "TODO: It hasn't implemented yet.")
#define OOPS "Oops a bug!! report please." #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 #endif //PK_COMMON_H

View File

@ -664,28 +664,6 @@ static void setNextValueToken(Parser* parser, _TokenType type, Var value);
static void setNextToken(Parser* parser, _TokenType type); static void setNextToken(Parser* parser, _TokenType type);
static bool matchChar(Parser* parser, char c); 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) { static void eatString(Compiler* compiler, bool single_quote) {
Parser* parser = &compiler->parser; 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 'n': pkByteBufferWrite(&buff, parser->vm, '\n'); break;
case 'r': pkByteBufferWrite(&buff, parser->vm, '\r'); break; case 'r': pkByteBufferWrite(&buff, parser->vm, '\r'); break;
case 't': pkByteBufferWrite(&buff, parser->vm, '\t'); break; case 't': pkByteBufferWrite(&buff, parser->vm, '\t'); break;
case '\n': break; // Just ignore the next line.
// '$' In pocketlang string is used for interpolation. // '$' In pocketlang string is used for interpolation.
case '$': pkByteBufferWrite(&buff, parser->vm, '$'); break; case '$': pkByteBufferWrite(&buff, parser->vm, '$'); break;
@ -767,22 +746,22 @@ static void eatString(Compiler* compiler, bool single_quote) {
uint8_t val = 0; uint8_t val = 0;
c = eatChar(parser); c = eatChar(parser);
if (!_isCharHex(c)) { if (!utilIsCharHex(c)) {
semanticError(compiler, makeErrToken(parser), semanticError(compiler, makeErrToken(parser),
"Invalid hex escape."); "Invalid hex escape.");
break; break;
} }
val = _charHexVal(c); val = utilCharHexVal(c);
c = eatChar(parser); c = eatChar(parser);
if (!_isCharHex(c)) { if (!utilIsCharHex(c)) {
semanticError(compiler, makeErrToken(parser), semanticError(compiler, makeErrToken(parser),
"Invalid hex escape."); "Invalid hex escape.");
break; break;
} }
val = (val << 4) | _charHexVal(c); val = (val << 4) | utilCharHexVal(c);
pkByteBufferWrite(&buff, parser->vm, val); pkByteBufferWrite(&buff, parser->vm, val);
@ -791,7 +770,7 @@ static void eatString(Compiler* compiler, bool single_quote) {
default: default:
semanticError(compiler, makeErrToken(parser), semanticError(compiler, makeErrToken(parser),
"Invalid escape character."); "Invalid escape character.");
return; break;
} }
} else { } else {
pkByteBufferWrite(&buff, parser->vm, c); pkByteBufferWrite(&buff, parser->vm, c);
@ -900,7 +879,7 @@ static void eatNumber(Compiler* compiler) {
c = peekChar(parser); c = peekChar(parser);
// The first digit should be hex digit. // The first digit should be hex digit.
if (!_isCharHex(c)) { if (!utilIsCharHex(c)) {
syntaxError(compiler, makeErrToken(parser), "Invalid hex literal."); syntaxError(compiler, makeErrToken(parser), "Invalid hex literal.");
return; return;
@ -908,7 +887,7 @@ static void eatNumber(Compiler* compiler) {
do { do {
// Consume the next digit. // Consume the next digit.
c = peekChar(parser); c = peekChar(parser);
if (!_isCharHex(c)) break; if (!utilIsCharHex(c)) break;
eatChar(parser); eatChar(parser);
// Check the length of the binary literal. // Check the length of the binary literal.
@ -920,7 +899,7 @@ static void eatNumber(Compiler* compiler) {
} }
// "Append" the next digit at the end. // "Append" the next digit at the end.
hex = (hex << 4) | _charHexVal(c); hex = (hex << 4) | utilCharHexVal(c);
} while (true); } while (true);
@ -2674,6 +2653,15 @@ static bool matchOperatorMethod(Compiler* compiler,
"Expected keyword self for unary operator definition."); "Expected keyword self for unary operator definition.");
return false; 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_PLUSEQ)) _RET("+=", 1);
if (match(compiler, TK_MINUSEQ)) _RET("-=", 1); if (match(compiler, TK_MINUSEQ)) _RET("-=", 1);

View File

@ -154,10 +154,7 @@ void initializeScript(PKVM* vm, Module* module) {
/* INTERNAL FUNCTIONS */ /* INTERNAL FUNCTIONS */
/*****************************************************************************/ /*****************************************************************************/
// Returns the string value of the variable, a wrapper of toString() function String* varToString(PKVM* vm, Var self, bool repr) {
// but for instances it'll try to calll "_to_string" function and on error
// it'll return NULL.
static inline String* _varToString(PKVM* vm, Var self) {
if (IS_OBJ_TYPE(self, OBJ_INST)) { if (IS_OBJ_TYPE(self, OBJ_INST)) {
// The closure is retrieved from [self] thus, it doesn't need to be push // 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). // from GC).
Closure* closure = NULL; Closure* closure = NULL;
bool has_method = false;
if (!repr) {
String* name = newString(vm, LITS__str); // TODO: static vm string?. String* name = newString(vm, LITS__str); // TODO: static vm string?.
vmPushTempRef(vm, &name->_super); // method. vmPushTempRef(vm, &name->_super); // name.
bool has_method = hasMethod(vm, self, name, &closure); has_method = hasMethod(vm, self, name, &closure);
vmPopTempRef(vm); // method. 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) { if (has_method) {
Var ret = VAR_NULL; Var ret = VAR_NULL;
@ -188,6 +195,7 @@ static inline String* _varToString(PKVM* vm, Var self) {
// "fall throught" and call 'toString()' bellow. // "fall throught" and call 'toString()' bellow.
} }
if (repr) return toRepr(vm, self);
return toString(vm, self); return toString(vm, self);
} }
@ -198,9 +206,9 @@ static inline bool _callUnaryOpMethod(PKVM* vm, Var self,
const char* method_name, Var* ret) { const char* method_name, Var* ret) {
Closure* closure = NULL; Closure* closure = NULL;
String* name = newString(vm, method_name); String* name = newString(vm, method_name);
vmPushTempRef(vm, &name->_super); // method. vmPushTempRef(vm, &name->_super); // name.
bool has_method = hasMethod(vm, self, name, &closure); bool has_method = hasMethod(vm, self, name, &closure);
vmPopTempRef(vm); // method. vmPopTempRef(vm); // name.
if (!has_method) return false; 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) { const char* method_name, Var* ret) {
Closure* closure = NULL; Closure* closure = NULL;
String* name = newString(vm, method_name); String* name = newString(vm, method_name);
vmPushTempRef(vm, &name->_super); // method. vmPushTempRef(vm, &name->_super); // name.
bool has_method = hasMethod(vm, self, name, &closure); bool has_method = hasMethod(vm, self, name, &closure);
vmPopTempRef(vm); // method. vmPopTempRef(vm); // name.
if (!has_method) return false; if (!has_method) return false;
@ -287,7 +295,7 @@ DEF(coreAssert,
if (argc == 2) { if (argc == 2) {
if (AS_OBJ(ARG(2))->type != OBJ_STRING) { 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. if (msg == NULL) return; //< Error at _to_string override.
} else { } else {
@ -380,7 +388,7 @@ DEF(coreToString,
"str(value:var) -> string\n" "str(value:var) -> string\n"
"Returns the string representation of the value.") { "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); if (str == NULL) RET(VAR_NULL);
RET(VAR_OBJ(str)); RET(VAR_OBJ(str));
} }
@ -425,7 +433,7 @@ DEF(corePrint,
for (int i = 1; i <= ARGC; i++) { for (int i = 1; i <= ARGC; i++) {
if (i != 1) vm->config.stdout_write(vm, " "); 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); if (str == NULL) RET(VAR_NULL);
vm->config.stdout_write(vm, str->data); vm->config.stdout_write(vm, str->data);
} }
@ -447,7 +455,7 @@ DEF(coreInput,
if (vm->config.stdin_read == NULL) return; if (vm->config.stdin_read == NULL) return;
if (argc == 1) { if (argc == 1) {
String* str = _varToString(vm, ARG(1)); String* str = varToString(vm, ARG(1), false);
if (str == NULL) RET(VAR_NULL); if (str == NULL) RET(VAR_NULL);
vm->config.stdout_write(vm, str->data); vm->config.stdout_write(vm, str->data);
} }
@ -537,7 +545,7 @@ DEF(coreListJoin,
pkByteBufferInit(&buff); pkByteBufferInit(&buff);
for (uint32_t i = 0; i < list->elements.count; i++) { 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); if (str == NULL) RET(VAR_NULL);
vmPushTempRef(vm, &str->_super); // elem vmPushTempRef(vm, &str->_super); // elem
pkByteBufferAddString(&buff, vm, str->data, str->length); pkByteBufferAddString(&buff, vm, str->data, str->length);
@ -703,7 +711,7 @@ DEF(stdLangWrite,
if (IS_OBJ_TYPE(arg, OBJ_STRING)) { if (IS_OBJ_TYPE(arg, OBJ_STRING)) {
str = (String*)AS_OBJ(arg); str = (String*)AS_OBJ(arg);
} else { } else {
str = _varToString(vm, ARG(1)); str = varToString(vm, ARG(1), false);
if (str == NULL) RET(VAR_NULL); if (str == NULL) RET(VAR_NULL);
} }
@ -769,7 +777,7 @@ static void _ctorString(PKVM* vm) {
RET(VAR_OBJ(newStringLength(vm, NULL, 0))); RET(VAR_OBJ(newStringLength(vm, NULL, 0)));
return; return;
} }
String* str = _varToString(vm, ARG(1)); String* str = varToString(vm, ARG(1), false);
if (str == NULL) RET(VAR_NULL); if (str == NULL) RET(VAR_NULL);
RET(VAR_OBJ(str)); RET(VAR_OBJ(str));
} }
@ -828,6 +836,20 @@ DEF(_numberTimes,
RET(VAR_NULL); 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, DEF(_listAppend,
"List.append(value:var) -> List\n" "List.append(value:var) -> List\n"
"Append the [value] to the list and return the list.") { "Append the [value] to the list and return the list.") {
@ -925,7 +947,10 @@ static void initializePrimitiveClasses(PKVM* vm) {
vmPopTempRef(vm); /* fn. */ \ vmPopTempRef(vm); /* fn. */ \
} while (false) } while (false)
// TODO: write docs.
ADD_METHOD(PK_NUMBER, "times", _numberTimes, 1); 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_LIST, "append", _listAppend, 1);
ADD_METHOD(PK_FIBER, "run", _fiberRun, -1); ADD_METHOD(PK_FIBER, "run", _fiberRun, -1);
ADD_METHOD(PK_FIBER, "resume", _fiberResume, -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++) { for (int i = 0; i < (int)cls_->methods.count; i++) {
Closure* method_ = cls_->methods.data[i]; Closure* method_ = cls_->methods.data[i];
ASSERT(method_->fn->is_method, OOPS); 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_; return method_;
} }
} }
@ -1289,7 +1315,7 @@ Var varOpRange(PKVM* vm, Var v1, Var v2) {
} }
if (IS_OBJ_TYPE(v1, OBJ_STRING)) { if (IS_OBJ_TYPE(v1, OBJ_STRING)) {
String* str = _varToString(vm, v2); String* str = varToString(vm, v2, false);
if (str == NULL) return VAR_NULL; if (str == NULL) return VAR_NULL;
String* concat = stringJoin(vm, (String*) AS_OBJ(v1), str); String* concat = stringJoin(vm, (String*) AS_OBJ(v1), str);
return VAR_OBJ(concat); return VAR_OBJ(concat);
@ -1742,7 +1768,7 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
VM_SET_ERROR(vm, stringFormat(vm, "Unhashable key '$'.", VM_SET_ERROR(vm, stringFormat(vm, "Unhashable key '$'.",
varTypeName(key))); varTypeName(key)));
} else { } else {
String* key_repr = toRepr(vm, key); String* key_repr = varToString(vm, key, true);
vmPushTempRef(vm, &key_repr->_super); // key_repr. vmPushTempRef(vm, &key_repr->_super); // key_repr.
VM_SET_ERROR(vm, stringFormat(vm, "Key '@' not exists", key_repr)); VM_SET_ERROR(vm, stringFormat(vm, "Key '@' not exists", key_repr));
vmPopTempRef(vm); // key_repr. vmPopTempRef(vm); // key_repr.
@ -1756,6 +1782,13 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
case OBJ_UPVALUE: case OBJ_UPVALUE:
UNREACHABLE(); // Not first class objects. UNREACHABLE(); // Not first class objects.
case OBJ_INST: {
Var ret;
if (_callBinaryOpMethod(vm, on, key, "[]", &ret)) {
return ret;
}
} break;
default: default:
break; break;
} }
@ -1804,6 +1837,22 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
case OBJ_UPVALUE: case OBJ_UPVALUE:
UNREACHABLE(); 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: default:
break; break;
} }

View File

@ -17,6 +17,7 @@
// restructre. The names of the macros are begin with LIST_ and the string. // restructre. The names of the macros are begin with LIST_ and the string.
#define LITS__init "_init" #define LITS__init "_init"
#define LITS__str "_str" #define LITS__str "_str"
#define LITS__repr "_repr"
// Functions, methods, classes and other names which are intrenal / special to // Functions, methods, classes and other names which are intrenal / special to
// pocketlang are starts with the following character (ex: @main, @literalFn). // 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. // otherwise and if the [method] argument is not NULL, method will be set.
bool hasMethod(PKVM* vm, Var self, String* name, Closure** method); 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 varPositive(PKVM* vm, Var v); // Returns +v.
Var varNegative(PKVM* vm, Var v); // Returns -v. Var varNegative(PKVM* vm, Var v); // Returns -v.
Var varNot(PKVM* vm, Var v); // Returns !v. Var varNot(PKVM* vm, Var v); // Returns !v.

View File

@ -113,16 +113,6 @@
/* REUSABLE INTERNAL MACROS */ /* 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 // 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 // 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 // to O(1) where n is the length of the string and k is the number of string

View File

@ -49,11 +49,6 @@
"Slot index is too large. Did you forget to call pkReserveSlots()?."); \ "Slot index is too large. Did you forget to call pkReserveSlots()?."); \
} while (false) } 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) \ #define CHECK_FIBER_EXISTS(vm) \
do { \ do { \
ASSERT(vm->fiber != NULL, \ 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). // 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 { \ do { \
char buff[STR_INT_BUFF_SIZE]; \ char buff[STR_INT_BUFF_SIZE]; \
sprintf(buff, "%d", arg); \ sprintf(buff, "%d", slot); \
VM_SET_ERROR(vm, stringFormat(vm, "Expected a '$' at slot $.", \ VM_SET_ERROR(vm, stringFormat(vm, "Expected a '$' at slot $.", \
ty_name, buff)); \ ty_name, buff)); \
} while (false) } while (false)
// FIXME: If the user needs just the boolean value of the object, they should // FIXME: If the user needs just the boolean value of the object, they should
// use pkGetSlotBool(). // 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); CHECK_FIBER_EXISTS(vm);
VALIDATE_ARGC(arg); VALIDATE_SLOT_INDEX(slot);
Var val = ARG(arg); Var val = ARG(slot);
if (!IS_BOOL(val)) { if (!IS_BOOL(val)) {
ERR_INVALID_SLOT_TYPE("Boolean"); ERR_INVALID_SLOT_TYPE(slot, "Boolean");
return false; return false;
} }
@ -572,13 +567,13 @@ PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int arg, bool* value) {
return true; 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); CHECK_FIBER_EXISTS(vm);
VALIDATE_ARGC(arg); VALIDATE_SLOT_INDEX(slot);
Var val = ARG(arg); Var val = ARG(slot);
if (!IS_NUM(val)) { if (!IS_NUM(val)) {
ERR_INVALID_SLOT_TYPE("Number"); ERR_INVALID_SLOT_TYPE(slot, "Number");
return false; return false;
} }
@ -586,14 +581,14 @@ PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int arg, double* value) {
return true; 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) { uint32_t* length) {
CHECK_FIBER_EXISTS(vm); 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)) { if (!IS_OBJ_TYPE(val, OBJ_STRING)) {
ERR_INVALID_SLOT_TYPE("String"); ERR_INVALID_SLOT_TYPE(slot, "String");
return false; return false;
} }
String* str = (String*)AS_OBJ(val); String* str = (String*)AS_OBJ(val);
@ -602,27 +597,27 @@ PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int arg, const char** value,
return true; return true;
} }
bool pkValidateSlotType(PKVM* vm, int arg, PkVarType type) { bool pkValidateSlotType(PKVM* vm, int slot, PkVarType type) {
CHECK_FIBER_EXISTS(vm); CHECK_FIBER_EXISTS(vm);
VALIDATE_ARGC(arg); VALIDATE_SLOT_INDEX(slot);
if (getVarType(ARG(arg)) != type) { if (getVarType(ARG(slot)) != type) {
ERR_INVALID_SLOT_TYPE(getPkVarTypeName(type)); ERR_INVALID_SLOT_TYPE(slot, getPkVarTypeName(type));
return false; return false;
} }
return true; 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); CHECK_FIBER_EXISTS(vm);
VALIDATE_ARGC(arg); VALIDATE_SLOT_INDEX(slot);
VALIDATE_SLOT_INDEX(cls); VALIDATE_SLOT_INDEX(cls);
Var instance = ARG(arg), class_ = SLOT(cls); Var instance = ARG(slot), class_ = SLOT(cls);
if (!varIsType(vm, instance, class_)) { if (!varIsType(vm, instance, class_)) {
// If [class_] is not a valid class, it's already an error. // If [class_] is not a valid class, it's already an error.
if (VM_HAS_ERROR(vm)) return false; 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; return false;
} }
@ -741,11 +736,12 @@ void pkSetSlotStringFmt(PKVM* vm, int index, const char* fmt, ...) {
void pkSetSlotHandle(PKVM* vm, int index, PkHandle* handle) { void pkSetSlotHandle(PKVM* vm, int index, PkHandle* handle) {
CHECK_FIBER_EXISTS(vm); CHECK_FIBER_EXISTS(vm);
CHECK_ARG_NULL(handle);
VALIDATE_SLOT_INDEX(index); VALIDATE_SLOT_INDEX(index);
SET_SLOT(index, handle->value); 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_FIBER_EXISTS(vm);
CHECK_ARG_NULL(name); CHECK_ARG_NULL(name);
VALIDATE_SLOT_INDEX(instance); 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)))); SET_SLOT(index, VAR_OBJ(getClass(vm, SLOT(instance))));
} }
#undef CHECK_RUNTIME
#undef VALIDATE_ARGC
#undef ERR_INVALID_ARG_TYPE #undef ERR_INVALID_ARG_TYPE
#undef ARG #undef ARG
#undef SLOT #undef SLOT
#undef SET_SLOT #undef SET_SLOT
#undef ARGC #undef ARGC
#undef CHECK_NULL
#undef CHECK_TYPE
/*****************************************************************************/ /*****************************************************************************/
/* INTERNAL */ /* INTERNAL */

View File

@ -40,6 +40,40 @@ bool utilIsDigit(char c) {
return ('0' <= c && c <= '9'); 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. // A union to reinterpret a double as raw bits and back.
typedef union { typedef union {
uint64_t bits64; uint64_t bits64;

View File

@ -23,6 +23,17 @@ bool utilIsName(char c);
// Returns true if `c` is [0-9]. // Returns true if `c` is [0-9].
bool utilIsDigit(char c); 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. // Return Reinterpreted bits of the double value.
uint64_t utilDoubleToBits(double value); uint64_t utilDoubleToBits(double value);

View File

@ -1212,6 +1212,11 @@ const char* varTypeName(Var v) {
ASSERT(IS_OBJ(v), OOPS); ASSERT(IS_OBJ(v), OOPS);
Object* obj = AS_OBJ(v); Object* obj = AS_OBJ(v);
if (obj->type == OBJ_INST) {
return ((Instance*)obj)->cls->name->data;
}
return getObjectTypeName(obj->type); return getObjectTypeName(obj->type);
} }
@ -1350,17 +1355,26 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff,
} else { } else {
// If recursive return with quotes (ex: [42, "hello", 0..10]). // If recursive return with quotes (ex: [42, "hello", 0..10]).
pkByteBufferWrite(buff, vm, '"'); pkByteBufferWrite(buff, vm, '"');
for (const char* c = str->data; *c != '\0'; c++) { for (uint32_t i = 0; i < str->length; i++) {
switch (*c) { char c = str->data[i];
switch (c) {
case '"': pkByteBufferAddString(buff, vm, "\\\"", 2); break; case '"': pkByteBufferAddString(buff, vm, "\\\"", 2); break;
case '\\': pkByteBufferAddString(buff, vm, "\\\\", 2); break; case '\\': pkByteBufferAddString(buff, vm, "\\\\", 2); break;
case '\n': pkByteBufferAddString(buff, vm, "\\n", 2); break; case '\n': pkByteBufferAddString(buff, vm, "\\n", 2); break;
case '\r': pkByteBufferAddString(buff, vm, "\\r", 2); break; case '\r': pkByteBufferAddString(buff, vm, "\\r", 2); break;
case '\t': pkByteBufferAddString(buff, vm, "\\t", 2); break; case '\t': pkByteBufferAddString(buff, vm, "\\t", 2); break;
default: default: {
pkByteBufferWrite(buff, vm, *c); if (isprint(c)) pkByteBufferWrite(buff, vm, c);
break; 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, '"'); pkByteBufferWrite(buff, vm, '"');

View File

@ -749,7 +749,9 @@ const char* getPkVarTypeName(PkVarType type);
// Returns the type name of the ObjectType enum value. // Returns the type name of the ObjectType enum value.
const char* getObjectTypeName(ObjectType type); 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); const char* varTypeName(Var v);
// Returns the PkVarType of the first class varaible [v]. // Returns the PkVarType of the first class varaible [v].

View File

@ -183,7 +183,7 @@ bool vmPrepareFiber(PKVM* vm, Fiber* fiber, int argc, Var* argv) {
ASSERT(fiber->closure->fn->arity >= -1, ASSERT(fiber->closure->fn->arity >= -1,
OOPS " (Forget to initialize arity.)"); 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]; char buff[STR_INT_BUFF_SIZE];
sprintf(buff, "%d", fiber->closure->fn->arity); sprintf(buff, "%d", fiber->closure->fn->arity);
_ERR_FAIL(stringFormat(vm, "Expected exactly $ argument(s).", buff)); _ERR_FAIL(stringFormat(vm, "Expected exactly $ argument(s).", buff));
@ -1128,9 +1128,9 @@ L_do_call:
} }
} else { } else {
RUNTIME_ERROR(stringFormat(vm, "$ $(@).", "Expected a callable to " RUNTIME_ERROR(stringFormat(vm, "$ '$'.", "Expected a callable to "
"call, instead got", "call, instead got",
varTypeName(callable), toString(vm, callable))); varTypeName(callable)));
} }
// If we reached here it's a valid callable. // If we reached here it's a valid callable.
@ -1785,7 +1785,7 @@ L_do_call:
if (vm->config.stdout_write != NULL) { if (vm->config.stdout_write != NULL) {
Var tmp = PEEK(-1); Var tmp = PEEK(-1);
if (!IS_NULL(tmp)) { 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"); vm->config.stdout_write(vm, "\n");
} }
} }

View File

@ -283,26 +283,26 @@ PK_PUBLIC int pkGetArgc(const PKVM* vm);
// that min <= max, and pocketlang won't validate this in release binary. // that min <= max, and pocketlang won't validate this in release binary.
PK_PUBLIC bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max); 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. // 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. // 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. // 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); 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. // [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. // 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 // 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] // 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 */ /* 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 // Get the attribute with [name] of the instance at the [instance] slot and
// place it at the [index] slot. Return true on success. // place it at the [index] slot. Return true on success.
PK_PUBLIC bool pkGetAttribute(PKVM* vm, int instance, const char* name, PK_PUBLIC bool pkGetAttribute(PKVM* vm, int instance, const char* name,
int index); 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. // Place the [self] instance at the [index] slot.
PK_PUBLIC void pkPlaceSelf(PKVM* vm, int index); PK_PUBLIC void pkPlaceSelf(PKVM* vm, int index);

View File

@ -9,6 +9,7 @@
#endif #endif
void registerModuleMath(PKVM* vm); void registerModuleMath(PKVM* vm);
void registerModuleTypes(PKVM* vm);
void registerModuleTime(PKVM* vm); void registerModuleTime(PKVM* vm);
void registerModuleIO(PKVM* vm); void registerModuleIO(PKVM* vm);
void registerModulePath(PKVM* vm); void registerModulePath(PKVM* vm);
@ -16,6 +17,7 @@ void registerModuleDummy(PKVM* vm);
void registerLibs(PKVM* vm) { void registerLibs(PKVM* vm) {
registerModuleMath(vm); registerModuleMath(vm);
registerModuleTypes(vm);
registerModuleTime(vm); registerModuleTime(vm);
registerModuleIO(vm); registerModuleIO(vm);
registerModulePath(vm); registerModulePath(vm);

View File

@ -19,98 +19,15 @@
/* MODULES INTERNAL */ /* MODULES INTERNAL */
/*****************************************************************************/ /*****************************************************************************/
// We're re defining some core internal macros here which should be considered // FIXME:
// as macro re-definition in amalgamated source, so we're skipping it for // Since this are part of the "standard" pocketlang libraries, we can include
// amalgumated build. // 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 #ifndef PK_AMALGAMATED
#include "../core/common.h"
#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)
#endif // PK_AMALGAMATED #endif // PK_AMALGAMATED
/*****************************************************************************/ /*****************************************************************************/

View File

@ -189,7 +189,7 @@ void registerModuleMath(PKVM* vm) {
pkReserveSlots(vm, 2); pkReserveSlots(vm, 2);
pkSetSlotHandle(vm, 0, math); // slot[0] = math pkSetSlotHandle(vm, 0, math); // slot[0] = math
pkSetSlotNumber(vm, 1, PK_PI); // slot[1] = 3.14 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, "floor", stdMathFloor, 1);
pkModuleAddFunction(vm, math, "ceil", stdMathCeil, 1); pkModuleAddFunction(vm, math, "ceil", stdMathCeil, 1);

266
src/libs/std_types.c Normal file
View 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);
}

View File

@ -7,6 +7,9 @@ assert(42 % 40.0 == 2)
assert("'\"'" == '\'"\'') assert("'\"'" == '\'"\'')
assert("testing" == "test" + "ing") assert("testing" == "test" + "ing")
assert("foo \
bar" == "foo bar")
assert(-0b10110010 == -178 and 0b11001010 == 202) assert(-0b10110010 == -178 and 0b11001010 == 202)
assert(0b1111111111111111 == 65535) assert(0b1111111111111111 == 65535)
assert( assert(

View File

@ -17,16 +17,16 @@ integrate pocket VM with your application
#### `example0.c` - Contains how to run simple pocket script #### `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 #### `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 #### `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
``` ```

View File

@ -43,16 +43,16 @@ typedef struct {
} Vector; } Vector;
// Native instance allocation callback. // Native instance allocation callback.
void* _newVec() { void* _newVec(PKVM* vm) {
Vector* vec = (Vector*)malloc(sizeof(Vector)); Vector* vec = pkRealloc(vm, NULL, sizeof(Vector));
vec->x = 0; vec->x = 0;
vec->y = 0; vec->y = 0;
return vec; return vec;
} }
// Native instance de-allocatoion callback. // Native instance de-allocatoion callback.
void _deleteVec(void* vector) { void _deleteVec(PKVM* vm, void* vec) {
free(vector); pkRealloc(vm, vec, 0);
} }
void _vecGetter(PKVM* vm) { void _vecGetter(PKVM* vm) {