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 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
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
|
||||||
String* name = newString(vm, LITS__str); // TODO: static vm string?.
|
bool has_method = false;
|
||||||
vmPushTempRef(vm, &name->_super); // method.
|
if (!repr) {
|
||||||
bool has_method = hasMethod(vm, self, name, &closure);
|
String* name = newString(vm, LITS__str); // TODO: static vm string?.
|
||||||
vmPopTempRef(vm); // method.
|
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) {
|
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;
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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 */
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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, '"');
|
||||||
|
@ -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].
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@ -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
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("'\"'" == '\'"\'')
|
||||||
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(
|
||||||
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user