diff --git a/README.md b/README.md index d9376a7..40f8812 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,8 @@ Including the compiler, bytecode VM and runtime, it's a standalone executable with zero external dependencies just as it's self descriptive name. The pocketlang VM can be embedded in another hosting program very easily. -[Wren Language](https://wren.io/) and their wonderful book [Crafting Interpreters](http://www.craftinginterpreters.com/) were used as a reference to write the language. +[Wren Language](https://wren.io/) and their wonderful book [Crafting Interpreters](http://www.craftinginterpreters.com/) +were used as a reference to write this language. ## What pocketlang looks like @@ -98,7 +99,7 @@ for the MSVS installation path and setup the build environment. 4. Add `src/include` to include path. 5. Compile. -If you weren't able to compile it, please us report by [opening an issue](https://github.com/ThakeeNathees/pocketlang/issues/new). +If you weren't able to compile it, please report us by [opening an issue](https://github.com/ThakeeNathees/pocketlang/issues/new). ## References diff --git a/cli/main.c b/cli/main.c index a9b98b4..c8e9883 100644 --- a/cli/main.c +++ b/cli/main.c @@ -129,8 +129,9 @@ static PKVM* intializePocketVM() { config.write_fn = writeFunction; config.read_fn = readFunction; - config.free_inst_fn = freeObj; + config.inst_free_fn = freeObj; config.inst_name_fn = getObjName; + config.inst_get_attrib_fn = objGetAttrib; config.load_script_fn = loadScript; config.resolve_path_fn = resolvePath; @@ -138,7 +139,7 @@ static PKVM* intializePocketVM() { return pkNewVM(&config); } -int main(int argc, char** argv) { +int main(int argc, const char** argv) { // Parse command line arguments. @@ -171,7 +172,7 @@ int main(int argc, char** argv) { // Parse the options. struct argparse argparse; argparse_init(&argparse, cli_opts, usage, 0); - argc = argparse_parse(&argparse, argc, (const char **)argv); + argc = argparse_parse(&argparse, argc, argv); if (help) { // pocket --help. argparse_usage(&argparse); @@ -198,12 +199,12 @@ int main(int argc, char** argv) { if (cmd != NULL) { // pocket -c "print('foo')" - PkStringPtr source = { cmd, NULL, NULL }; - PkStringPtr path = { "$(Source)", NULL, NULL }; + PkStringPtr source = { cmd, NULL, NULL, 0, 0 }; + PkStringPtr path = { "$(Source)", NULL, NULL, 0, 0 }; PkResult result = pkInterpretSource(vm, source, path, NULL); exitcode = (int)result; - } if (argc == 0) { // Run on REPL mode. + } else if (argc == 0) { // Run on REPL mode. // Print the copyright and license notice, if --quiet not set. if (!quiet) { diff --git a/cli/modules.c b/cli/modules.c index 33c0b52..5afb3aa 100644 --- a/cli/modules.c +++ b/cli/modules.c @@ -5,6 +5,9 @@ #include "modules.h" +// Note: Everything here is for testing the native API, and will have to +// refactor everything. + // Allocate a new module object of type [Ty]. #define NEW_OBJ(Ty) (Ty*)malloc(sizeof(Ty)) @@ -12,6 +15,26 @@ // callback. #define FREE_OBJ(ptr) free(ptr) +void initObj(Obj* obj, ObjType type) { + obj->type = type; +} + +bool objGetAttrib(PKVM* vm, void* instance, PkStringPtr attrib) { + + Obj* obj = (Obj*)instance; + // TODO: assert obj type is valid. + + if (obj->type == OBJ_FILE) { + File* file = (File*)obj; + if (strcmp(attrib.string, "closed") == 0) { + pkReturnBool(vm, file->closed); + return true; + } + } + + return false; // Attribute not found. +} + void freeObj(PKVM* vm, void* instance) { Obj* obj = (Obj*)instance; @@ -278,16 +301,8 @@ void registerModulePath(PKVM* vm) { static void _fileOpen(PKVM* vm) { - // TODO: handle arg range using pocketlang native api. - // 1 <= argc <= 2 int argc = pkGetArgc(vm); - if (argc == 0) { - pkSetRuntimeError(vm, "Expected at least 1 argument"); - return; - } else if (argc > 2) { - pkSetRuntimeError(vm, "Expected at least 2 arguments"); - return; - } + if (!pkCheckArgcRange(vm, argc, 1, 2)) return; const char* path; if (!pkGetArgString(vm, 1, &path, NULL)) return; @@ -309,7 +324,7 @@ static void _fileOpen(PKVM* vm) { // TODO: (fmt, ...) va_arg for runtime error public api. // If we reached here, that means it's an invalid mode string. - pkSetRuntimeError(vm, "Invalid mode string"); + pkSetRuntimeError(vm, "Invalid mode string."); return; } while (false); } @@ -318,6 +333,7 @@ static void _fileOpen(PKVM* vm) { if (fp != NULL) { File* file = NEW_OBJ(File); + initObj(&file->_super, OBJ_FILE); file->fp = fp; file->mode = mode; file->closed = false; diff --git a/cli/modules.h b/cli/modules.h index 0795a15..1bc1672 100644 --- a/cli/modules.h +++ b/cli/modules.h @@ -54,6 +54,13 @@ typedef struct { /* MODULE PUBLIC FUNCTIONS */ /*****************************************************************************/ +// Initialize the native module object with it's default values. +void initObj(Obj* obj, ObjType type); + +// A function callback called by pocket VM to get attribute of a native +// instance. +bool objGetAttrib(PKVM* vm, void* instance, PkStringPtr attrib); + // The free callback of the object, that'll called by pocketlang when a // pocketlang native instance garbage collected. void freeObj(PKVM* vm, void* instance); diff --git a/docs/TODO.txt b/docs/TODO.txt index d3463bc..0ffb2bd 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -1,5 +1,6 @@ -// In good first issue +// To good first issue +Add '.title' attribute to string // To implement. diff --git a/docs/pages/Getting-Started/build-from-source.md b/docs/pages/Getting-Started/build-from-source.md index 7459c12..bdc621b 100644 --- a/docs/pages/Getting-Started/build-from-source.md +++ b/docs/pages/Getting-Started/build-from-source.md @@ -43,6 +43,6 @@ environment. 4. Add `src/include` to include path. 5. Compile. -If you weren't able to compile it, please us report by +If you weren't able to compile it, please report us by [opening an issue](https://github.com/ThakeeNathees/pocketlang/issues/new). diff --git a/docs/pages/home.md b/docs/pages/home.md index 6cb0006..9c71f57 100644 --- a/docs/pages/home.md +++ b/docs/pages/home.md @@ -19,4 +19,4 @@ for i in 0..5 end ``` -The complete language (including the internals and runtime) is a standalone executable with zero external dependency, that can be easily copied to a flash drive. And the language itself can be compiled from source in seconds without any dependencies (of cause you need a C compiler and **optionally** a build system). \ No newline at end of file +The complete language (including the internals and runtime) is a standalone executable with zero external dependency, that can be easily copied to a flash drive. And the language itself can be compiled from source in seconds without any dependencies (of course you need a C compiler and **optionally** a build system). \ No newline at end of file diff --git a/docs/static/prism/prism.js b/docs/static/prism/prism.js index 63711b3..88c13e2 100644 --- a/docs/static/prism/prism.js +++ b/docs/static/prism/prism.js @@ -13,6 +13,6 @@ Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookb // The below line was changed to the line below it. // keyword:/\b(?:alias|and|BEGIN|begin|break|case|class|def|define_method|defined|do|each|else|elsif|END|end|ensure|extend|for|if|in|include|module|new|next|nil|not|or|prepend|protected|private|public|raise|redo|require|rescue|retry|return|self|super|then|throw|undef|unless|until|when|while|yield)\b/}); -keyword:/\b(?:and|break|def|do|else|elsif|end|for|if|in|module|not|or|raise|return|self|then|throw|while|yield|null|from|import|as|func|native|continue)\b/}); +keyword:/\b(?:and|break|class|def|do|else|elsif|end|for|if|in|module|not|or|raise|return|self|then|throw|while|yield|null|from|import|as|func|native|continue)\b/}); var n={pattern:/#\{[^}]+\}/,inside:{delimiter:{pattern:/^#\{|\}$/,alias:"tag"},rest:e.languages.ruby}};delete e.languages.ruby.function,e.languages.insertBefore("ruby","keyword",{regex:[{pattern:RegExp("%r(?:"+["([^a-zA-Z0-9\\s{(\\[<])(?:(?!\\1)[^\\\\]|\\\\[^])*\\1","\\((?:[^()\\\\]|\\\\[^])*\\)","\\{(?:[^#{}\\\\]|#(?:\\{[^}]+\\})?|\\\\[^])*\\}","\\[(?:[^\\[\\]\\\\]|\\\\[^])*\\]","<(?:[^<>\\\\]|\\\\[^])*>"].join("|")+")[egimnosux]{0,6}"),greedy:!0,inside:{interpolation:n}},{pattern:/(^|[^/])\/(?!\/)(?:\[[^\r\n\]]+\]|\\.|[^[/\\\r\n])+\/[egimnosux]{0,6}(?=\s*(?:$|[\r\n,.;})#]))/,lookbehind:!0,greedy:!0,inside:{interpolation:n}}],variable:/[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/,symbol:{pattern:/(^|[^:]):[a-zA-Z_]\w*(?:[?!]|\b)/,lookbehind:!0},"method-definition":{pattern:/(\bdef\s+)[\w.]+/,lookbehind:!0,inside:{function:/\w+$/,rest:e.languages.ruby}}}),e.languages.insertBefore("ruby","number",{builtin:/\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Stat|Fixnum|Float|Hash|Integer|IO|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|String|Struct|TMS|Symbol|ThreadGroup|Thread|Time|TrueClass)\b/,constant:/\b[A-Z]\w*(?:[?!]|\b)/}),e.languages.ruby.string=[{pattern:RegExp("%[qQiIwWxs]?(?:"+["([^a-zA-Z0-9\\s{(\\[<])(?:(?!\\1)[^\\\\]|\\\\[^])*\\1","\\((?:[^()\\\\]|\\\\[^])*\\)","\\{(?:[^#{}\\\\]|#(?:\\{[^}]+\\})?|\\\\[^])*\\}","\\[(?:[^\\[\\]\\\\]|\\\\[^])*\\]","<(?:[^<>\\\\]|\\\\[^])*>"].join("|")+")"),greedy:!0,inside:{interpolation:n}},{pattern:/("|')(?:#\{[^}]+\}|#(?!\{)|\\(?:\r\n|[\s\S])|(?!\1)[^\\#\r\n])*\1/,greedy:!0,inside:{interpolation:n}}],e.languages.rb=e.languages.ruby}(Prism); diff --git a/src/include/pocketlang.h b/src/include/pocketlang.h index ee13ef9..d568bde 100644 --- a/src/include/pocketlang.h +++ b/src/include/pocketlang.h @@ -128,7 +128,8 @@ typedef enum { /* POCKETLANG FUNCTION POINTERS & CALLBACKS */ /*****************************************************************************/ -// C function pointer which is callable from PocketLang. +// C function pointer which is callable from pocketLang by native module +// functions. typedef void (*pkNativeFn)(PKVM* vm); // A function that'll be called for all the allocation calls by PKVM. @@ -160,7 +161,7 @@ typedef PkStringPtr (*pkReadFn) (PKVM* vm); // A function callback, that'll be called when a native instance (wrapper) is // freed by by the garbage collector, to indicate that pocketlang is done with // the native instance. -typedef void (*pkFreeInstFn) (PKVM* vm, void* instance); +typedef void (*pkInstFreeFn) (PKVM* vm, void* instance); // A function callback to get the name of the native instance from pocketlang, // using it's [id]. The returned string won't be copied by pocketlang so it's @@ -168,6 +169,22 @@ typedef void (*pkFreeInstFn) (PKVM* vm, void* instance); // a C literal string. typedef const char* (*pkInstNameFn) (uint32_t id); +// A get arribute callback, called by pocket VM when trying to get an attribute +// from a native type. to return the value of the attribute use 'pkReturn...()' +// functions, and return true. If the attribute not exists return false, But +// DON'T set an error to the VM, this is necessary. Example if the '.as_string' +// attribute doesn't exists, pocket VM will use a default to string value. +typedef bool (*pkInstGetAttribFn) (PKVM* vm, void* instance, + PkStringPtr attrib); + +// If the attribute dones't exists don't set an error, instead return false. +// Pocket VM will handle it, however if the type of the value is incompatible +// set an error with pkSetRuntimeError(); and return false, on success update +// the native instance and return true. And do not ever use 'pkReturn...()' +// function. This is a void return function. +typedef bool (*pkInstSetAttribFn) (PKVM* vm, void* instance, + PkStringPtr attrib, PkVar value); + // A function callback symbol for clean/free the pkStringResult. typedef void (*pkResultDoneFn) (PKVM* vm, PkStringPtr result); @@ -270,13 +287,18 @@ PK_PUBLIC PkResult pkResumeFiber(PKVM* vm, PkHandle* fiber, PkVar value); /* POCKETLANG PUBLIC TYPE DEFINES */ /*****************************************************************************/ -// A string pointer wrapper to pass cstring from host application to pocketlang -// vm, with a on_done() callback to clean it when the pocketlang vm is done with -// the string. +// A string pointer wrapper to pass c string between host application and +// pocket VM. With a on_done() callback to clean it when the pocket VM is done +// with the string. struct PkStringPtr { const char* string; //< The string result. pkResultDoneFn on_done; //< Called once vm done with the string. void* user_data; //< User related data. + + // These values are provided by the pocket VM to the host application, you're + // not expected to set this when provideing string to the pocket VM. + uint32_t length; //< Length of the string. + uint32_t hash; //< Its 32 bit FNV-1a hash. }; struct PkConfiguration { @@ -289,8 +311,10 @@ struct PkConfiguration { pkWriteFn write_fn; pkReadFn read_fn; - pkFreeInstFn free_inst_fn; + pkInstFreeFn inst_free_fn; pkInstNameFn inst_name_fn; + pkInstGetAttribFn inst_get_attrib_fn; + pkInstSetAttribFn inst_set_attrib_fn; pkResolvePathFn resolve_path_fn; pkLoadScriptFn load_script_fn; @@ -326,9 +350,12 @@ struct PkCompileOptions { /* NATIVE FUNCTION API */ /*****************************************************************************/ -// Set a runtime error to vm. +// Set a runtime error to VM. PK_PUBLIC void pkSetRuntimeError(PKVM* vm, const char* message); +// TODO: Set a runtime error to VM, with the formated string. +//PK_PUBLIC void pkSetRuntimeErrorFmt(PKVM* vm, const char* fmt, ...); + // Return the type of the [value] this will help to get the type of the // variable that was extracted from pkGetArg() earlier. PK_PUBLIC PkVarType pkGetValueType(const PkVar value); @@ -337,6 +364,11 @@ PK_PUBLIC PkVarType pkGetValueType(const PkVar value); // registered with -1 argument count (which means variadic arguments). PK_PUBLIC int pkGetArgc(const PKVM* vm); +// Check if the argc is in the range of (min <= argc <= max), if it's not, a +// runtime error will be set and return false, otherwise return true. Assuming +// that min <= max, and pocketlang won't validate this in release binary. +PK_PUBLIC bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max); + // Return the [arg] th argument as a PkVar. This pointer will only be // valid till the current function ends, because it points to the var at the // stack and it'll popped when the current call frame ended. Use handles to diff --git a/src/pk_compiler.c b/src/pk_compiler.c index 16fe2c6..18ffdd3 100644 --- a/src/pk_compiler.c +++ b/src/pk_compiler.c @@ -39,11 +39,6 @@ // Max number of break statement in a loop statement to patch. #define MAX_BREAK_PATCH 256 -// The size of the compiler time error message buffer excluding the file path, -// line number, and function name. Used for `vsprintf` and `vsnprintf` is not -// available in C++98. -#define ERROR_MESSAGE_SIZE 256 - // The name of a literal function. #define LITERAL_FN_NAME "$(LiteralFn)" @@ -427,9 +422,8 @@ static void reportError(Compiler* compiler, const char* file, int line, // crash. char message[ERROR_MESSAGE_SIZE]; - int length = vsprintf(message, fmt, args); - __ASSERT(length < ERROR_MESSAGE_SIZE, "Error message buffer should not " - "exceed the buffer"); + int length = vsnprintf(message, sizeof(message), fmt, args); + __ASSERT(length >= 0, "Error message buffer failed at vsnprintf()."); vm->config.error_fn(vm, PK_ERROR_COMPILE, file, line, message); } @@ -991,17 +985,18 @@ static void consumeStartBlock(Compiler* compiler, TokenType delimiter) { // Returns a optional compound assignment. static bool matchAssignment(Compiler* compiler) { - if (match(compiler, TK_EQ)) return true; - if (match(compiler, TK_PLUSEQ)) return true; - if (match(compiler, TK_MINUSEQ)) return true; - if (match(compiler, TK_STAREQ)) return true; - if (match(compiler, TK_DIVEQ)) return true; - if (match(compiler, TK_MODEQ)) return true; - if (match(compiler, TK_ANDEQ)) return true; - if (match(compiler, TK_OREQ)) return true; - if (match(compiler, TK_XOREQ)) return true; + if (match(compiler, TK_EQ)) return true; + if (match(compiler, TK_PLUSEQ)) return true; + if (match(compiler, TK_MINUSEQ)) return true; + if (match(compiler, TK_STAREQ)) return true; + if (match(compiler, TK_DIVEQ)) return true; + if (match(compiler, TK_MODEQ)) return true; + if (match(compiler, TK_ANDEQ)) return true; + if (match(compiler, TK_OREQ)) return true; + if (match(compiler, TK_XOREQ)) return true; if (match(compiler, TK_SRIGHTEQ)) return true; - if (match(compiler, TK_SLEFTEQ)) return true; + if (match(compiler, TK_SLEFTEQ)) return true; + return false; } @@ -1885,16 +1880,16 @@ static void emitLoopJump(Compiler* compiler) { static void emitAssignment(Compiler* compiler, TokenType assignment) { switch (assignment) { - case TK_PLUSEQ: emitOpcode(compiler, OP_ADD); break; - case TK_MINUSEQ: emitOpcode(compiler, OP_SUBTRACT); break; - case TK_STAREQ: emitOpcode(compiler, OP_MULTIPLY); break; - case TK_DIVEQ: emitOpcode(compiler, OP_DIVIDE); break; - case TK_MODEQ: emitOpcode(compiler, OP_MOD); break; - case TK_ANDEQ: emitOpcode(compiler, OP_BIT_AND); break; - case TK_OREQ: emitOpcode(compiler, OP_BIT_OR); break; - case TK_XOREQ: emitOpcode(compiler, OP_BIT_XOR); break; + case TK_PLUSEQ: emitOpcode(compiler, OP_ADD); break; + case TK_MINUSEQ: emitOpcode(compiler, OP_SUBTRACT); break; + case TK_STAREQ: emitOpcode(compiler, OP_MULTIPLY); break; + case TK_DIVEQ: emitOpcode(compiler, OP_DIVIDE); break; + case TK_MODEQ: emitOpcode(compiler, OP_MOD); break; + case TK_ANDEQ: emitOpcode(compiler, OP_BIT_AND); break; + case TK_OREQ: emitOpcode(compiler, OP_BIT_OR); break; + case TK_XOREQ: emitOpcode(compiler, OP_BIT_XOR); break; case TK_SRIGHTEQ: emitOpcode(compiler, OP_BIT_RSHIFT); break; - case TK_SLEFTEQ: emitOpcode(compiler, OP_BIT_LSHIFT); break; + case TK_SLEFTEQ: emitOpcode(compiler, OP_BIT_LSHIFT); break; default: UNREACHABLE(); break; diff --git a/src/pk_core.c b/src/pk_core.c index 02c078e..b29d336 100644 --- a/src/pk_core.c +++ b/src/pk_core.c @@ -32,9 +32,6 @@ static const char* DOCSTRING(fn) = docstring; \ static void fn(PKVM* vm) -// Checks if a number is a byte -#define IS_NUM_BYTE(num) ((CHAR_MIN <= (num)) && ((num) <= CHAR_MAX)) - /*****************************************************************************/ /* CORE PUBLIC API */ /*****************************************************************************/ @@ -121,7 +118,7 @@ PkHandle* pkGetFunction(PKVM* vm, PkHandle* module, do { \ __ASSERT(vm->fiber != NULL, \ "This function can only be called at runtime."); \ - __ASSERT(arg > 0 || arg <= ARGC, "Invalid argument index."); \ + __ASSERT(arg > 0 && arg <= ARGC, "Invalid argument index."); \ __ASSERT(value != NULL, "Argument [value] was NULL."); \ } while (false) @@ -140,6 +137,26 @@ int pkGetArgc(const PKVM* vm) { return ARGC; } +// pkCheckArgcRange implementation (see pocketlang.h for description). +bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max) { + ASSERT(min <= max, "invalid argc range (min > max)."); + + if (argc < min) { + char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", min); + VM_SET_ERROR(vm, stringFormat(vm, "Expected at least %s argument(s).", + buff)); + return false; + + } else if (argc > max) { + char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", max); + VM_SET_ERROR(vm, stringFormat(vm, "Expected at most %s argument(s).", + buff)); + return false; + } + + return true; +} + // pkGetArg implementation (see pocketlang.h for description). PkVar pkGetArg(const PKVM* vm, int arg) { __ASSERT(vm->fiber != NULL, "This function can only be called at runtime."); @@ -304,6 +321,9 @@ bool pkFiberIsDone(const PkHandle* fiber) { /* VALIDATORS */ /*****************************************************************************/ +// Evaluated to true of the [num] is in byte range. +#define IS_NUM_BYTE(num) ((CHAR_MIN <= (num)) && ((num) <= CHAR_MAX)) + // Check if [var] is a numeric value (bool/number) and set [value]. static inline bool isNumeric(Var var, double* value) { if (IS_NUM(var)) { @@ -607,12 +627,9 @@ DEF(coreInput, } DEF(coreExit, - "exit([value:int]) -> null\n" - "Sets the VM's fiber to NULL, causing the VM to return from execution " - "and thereby terminating the process entirely. The exit code of the " - "process is the optional argument [value] given to exit() which must be " - "between 0 and 255 inclusive. If no argument is given, the exit code is 0 " - "by default.") { + "exit([value:num]) -> null\n" + "Exit the process with an optional exit code provided by the argument " + "[value]. The default exit code is would be 0.") { int argc = ARGC; if (argc > 1) { // exit() or exit(val). @@ -625,7 +642,7 @@ DEF(coreExit, } // TODO: this actually needs to be the VM fiber being set to null though. - exit(value); + exit((int)value); } // String functions. @@ -967,11 +984,11 @@ DEF(stdMathArcSine, "expressed in radians.") { double num; - if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return; - if (num < -1 || 1 < num) + if (num < -1 || 1 < num) { RET_ERR(newString(vm, "Argument should be between -1 and +1")); + } RET(VAR_NUM(asin(num))); } @@ -982,11 +999,11 @@ DEF(stdMathArcCosine, "an angle expressed in radians.") { double num; - if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return; - if (num < -1 || 1 < num) + if (num < -1 || 1 < num) { RET_ERR(newString(vm, "Argument should be between -1 and +1")); + } RET(VAR_NUM(acos(num))); } @@ -1606,7 +1623,27 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) { { Instance* inst = (Instance*)obj; if (inst->is_native) { - TODO; + + if (vm->config.inst_get_attrib_fn) { + + // Temproarly change the fiber's "return address" to points to the + // below var 'ret' so that the users can use 'pkReturn...()' function + // to return the attribute as well. + Var* temp = vm->fiber->ret; + Var ret = VAR_NULL; + vm->fiber->ret = &ret; + + PkStringPtr attr = { attrib->data, NULL, NULL, + attrib->length, attrib->hash }; + if (!vm->config.inst_get_attrib_fn(vm, inst->native, attr)) { + // TODO: if the attribute is '.as_string' return repr. + ERR_NO_ATTRIB(vm, on, attrib); + return VAR_NULL; + } + + vm->fiber->ret = temp; + return ret; + } } else { @@ -1891,5 +1928,7 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) { UNREACHABLE(); } +#undef IS_NUM_BYTE + #undef DOCSTRING #undef DEF diff --git a/src/pk_internal.h b/src/pk_internal.h index d928f33..c65f26f 100644 --- a/src/pk_internal.h +++ b/src/pk_internal.h @@ -54,6 +54,9 @@ // The initial minimum capacity of a buffer to allocate. #define MIN_CAPACITY 8 +// The size of the error message buffer, used ar vsnprintf (since c99) buffer. +#define ERROR_MESSAGE_SIZE 512 + /*****************************************************************************/ /* ALLOCATION MACROS */ /*****************************************************************************/ diff --git a/src/pk_var.c b/src/pk_var.c index b8276ef..c792364 100644 --- a/src/pk_var.c +++ b/src/pk_var.c @@ -1050,9 +1050,9 @@ void freeObject(PKVM* vm, Object* self) { Instance* inst = (Instance*)self; if (inst->is_native) { - if (vm->config.free_inst_fn != NULL) { + if (vm->config.inst_free_fn != NULL) { // TODO: Allow user to set error when freeing the object. - vm->config.free_inst_fn(vm, inst->native); + vm->config.inst_free_fn(vm, inst->native); } } else { @@ -1341,11 +1341,9 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff, // Check if the list is recursive. OuterSequence* seq = outer; while (seq != NULL) { - if (seq->is_list) { - if (seq->list == list) { - pkByteBufferAddString(buff, vm, "[...]", 5); - return; - } + if (seq->is_list && seq->list == list) { + pkByteBufferAddString(buff, vm, "[...]", 5); + return; } seq = seq->outer; } @@ -1372,11 +1370,9 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff, // Check if the map is recursive. OuterSequence* seq = outer; while (seq != NULL) { - if (!seq->is_list) { - if (seq->map == map) { - pkByteBufferAddString(buff, vm, "{...}", 5); - return; - } + if (!seq->is_list && seq->map == map) { + pkByteBufferAddString(buff, vm, "{...}", 5); + return; } seq = seq->outer; } diff --git a/src/pk_vm.c b/src/pk_vm.c index 33ce8fc..9622f26 100644 --- a/src/pk_vm.c +++ b/src/pk_vm.c @@ -30,8 +30,10 @@ PkConfiguration pkNewConfiguration(void) { config.write_fn = NULL; config.read_fn = NULL; - config.free_inst_fn = NULL; + config.inst_free_fn = NULL; config.inst_name_fn = NULL; + config.inst_get_attrib_fn = NULL; + config.inst_set_attrib_fn = NULL; config.load_script_fn = NULL; config.resolve_path_fn = NULL; diff --git a/tests/benchmarks/benchmarks.py b/tests/benchmarks/benchmarks.py index d9f49ae..260dac0 100644 --- a/tests/benchmarks/benchmarks.py +++ b/tests/benchmarks/benchmarks.py @@ -27,14 +27,7 @@ BENCHMARKS = ( ) ## Map from file extension to it's interpreter, Will be updated. -INTERPRETERS = { - '.pk' : None, - '.wren' : None, - '.py' : None, - '.rb' : None, - '.lua' : None, - '.js' : None, -} +INTERPRETERS = {} def main(): update_interpreters() @@ -46,7 +39,7 @@ def main(): def run_all_benchmarsk(): for benchmark in BENCHMARKS: - print_title(benchmark) + print_title(benchmark.title()) dir = join(THIS_PATH, benchmark) for file in _source_files(os.listdir(dir)): file = abspath(join(dir, file)) @@ -55,7 +48,7 @@ def run_all_benchmarsk(): lang, interp, val = INTERPRETERS[ext] if not interp: continue - print("%-10s : "%lang, end=''); sys.stdout.flush() + print(" %-10s : "%lang, end=''); sys.stdout.flush() result = _run_command([interp, file]) time = re.findall(r'elapsed:\s*([0-9\.]+)\s*s', result.stdout.decode('utf8'), @@ -118,7 +111,7 @@ def _get_pocket_binary(): ## as (lang, interp, val) tuple, where the val is the additional. ## data related to the interpreter. def _find_interp(lang, interpreter, val): - print('%-25s' % ('Searching for %s ' % lang), end='') + print('%-27s' % (' Searching for %s ' % lang), end='') sys.stdout.flush() if which(interpreter): print_success('-- found') @@ -136,9 +129,9 @@ def get_ext(file_name): ## ---------------------------------------------------------------------------- def print_title(title): - print("----------------------------------") + print("-----------------------------------") print(" %s " % title) - print("----------------------------------") + print("-----------------------------------") ## ANSI color codes to print messages. COLORS = {