native types initialization & attribute getters. (#147)

getters/setters native api functions added to the
vm's configurations and native types were initialized.
This commit is contained in:
Thakee Nathees 2021-06-28 19:17:19 +05:30
parent 794c789fca
commit 273a31e588
15 changed files with 184 additions and 98 deletions

View File

@ -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

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -1,5 +1,6 @@
// In good first issue
// To good first issue
Add '.title' attribute to string
// To implement.

View File

@ -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).

View File

@ -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).
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).

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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 */
/*****************************************************************************/

View File

@ -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;
}

View File

@ -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;

View File

@ -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 = {