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 with zero external dependencies just as it's self descriptive name. The pocketlang
VM can be embedded in another hosting program very easily. 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 ## 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. 4. Add `src/include` to include path.
5. Compile. 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 ## References

View File

@ -129,8 +129,9 @@ static PKVM* intializePocketVM() {
config.write_fn = writeFunction; config.write_fn = writeFunction;
config.read_fn = readFunction; config.read_fn = readFunction;
config.free_inst_fn = freeObj; config.inst_free_fn = freeObj;
config.inst_name_fn = getObjName; config.inst_name_fn = getObjName;
config.inst_get_attrib_fn = objGetAttrib;
config.load_script_fn = loadScript; config.load_script_fn = loadScript;
config.resolve_path_fn = resolvePath; config.resolve_path_fn = resolvePath;
@ -138,7 +139,7 @@ static PKVM* intializePocketVM() {
return pkNewVM(&config); return pkNewVM(&config);
} }
int main(int argc, char** argv) { int main(int argc, const char** argv) {
// Parse command line arguments. // Parse command line arguments.
@ -171,7 +172,7 @@ int main(int argc, char** argv) {
// Parse the options. // Parse the options.
struct argparse argparse; struct argparse argparse;
argparse_init(&argparse, cli_opts, usage, 0); 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. if (help) { // pocket --help.
argparse_usage(&argparse); argparse_usage(&argparse);
@ -198,12 +199,12 @@ int main(int argc, char** argv) {
if (cmd != NULL) { // pocket -c "print('foo')" if (cmd != NULL) { // pocket -c "print('foo')"
PkStringPtr source = { cmd, NULL, NULL }; PkStringPtr source = { cmd, NULL, NULL, 0, 0 };
PkStringPtr path = { "$(Source)", NULL, NULL }; PkStringPtr path = { "$(Source)", NULL, NULL, 0, 0 };
PkResult result = pkInterpretSource(vm, source, path, NULL); PkResult result = pkInterpretSource(vm, source, path, NULL);
exitcode = (int)result; 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. // Print the copyright and license notice, if --quiet not set.
if (!quiet) { if (!quiet) {

View File

@ -5,6 +5,9 @@
#include "modules.h" #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]. // Allocate a new module object of type [Ty].
#define NEW_OBJ(Ty) (Ty*)malloc(sizeof(Ty)) #define NEW_OBJ(Ty) (Ty*)malloc(sizeof(Ty))
@ -12,6 +15,26 @@
// callback. // callback.
#define FREE_OBJ(ptr) free(ptr) #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) { void freeObj(PKVM* vm, void* instance) {
Obj* obj = (Obj*)instance; Obj* obj = (Obj*)instance;
@ -278,16 +301,8 @@ void registerModulePath(PKVM* vm) {
static void _fileOpen(PKVM* vm) { static void _fileOpen(PKVM* vm) {
// TODO: handle arg range using pocketlang native api.
// 1 <= argc <= 2
int argc = pkGetArgc(vm); int argc = pkGetArgc(vm);
if (argc == 0) { if (!pkCheckArgcRange(vm, argc, 1, 2)) return;
pkSetRuntimeError(vm, "Expected at least 1 argument");
return;
} else if (argc > 2) {
pkSetRuntimeError(vm, "Expected at least 2 arguments");
return;
}
const char* path; const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return; 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. // TODO: (fmt, ...) va_arg for runtime error public api.
// If we reached here, that means it's an invalid mode string. // If we reached here, that means it's an invalid mode string.
pkSetRuntimeError(vm, "Invalid mode string"); pkSetRuntimeError(vm, "Invalid mode string.");
return; return;
} while (false); } while (false);
} }
@ -318,6 +333,7 @@ static void _fileOpen(PKVM* vm) {
if (fp != NULL) { if (fp != NULL) {
File* file = NEW_OBJ(File); File* file = NEW_OBJ(File);
initObj(&file->_super, OBJ_FILE);
file->fp = fp; file->fp = fp;
file->mode = mode; file->mode = mode;
file->closed = false; file->closed = false;

View File

@ -54,6 +54,13 @@ typedef struct {
/* MODULE PUBLIC FUNCTIONS */ /* 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 // The free callback of the object, that'll called by pocketlang when a
// pocketlang native instance garbage collected. // pocketlang native instance garbage collected.
void freeObj(PKVM* vm, void* instance); 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. // To implement.

View File

@ -43,6 +43,6 @@ environment.
4. Add `src/include` to include path. 4. Add `src/include` to include path.
5. Compile. 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). [opening an issue](https://github.com/ThakeeNathees/pocketlang/issues/new).

View File

@ -19,4 +19,4 @@ for i in 0..5
end 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. // 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(?: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); 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 */ /* 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); typedef void (*pkNativeFn)(PKVM* vm);
// A function that'll be called for all the allocation calls by PKVM. // 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 // 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 // freed by by the garbage collector, to indicate that pocketlang is done with
// the native instance. // 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, // 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 // 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. // a C literal string.
typedef const char* (*pkInstNameFn) (uint32_t id); 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. // A function callback symbol for clean/free the pkStringResult.
typedef void (*pkResultDoneFn) (PKVM* vm, PkStringPtr result); 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 */ /* POCKETLANG PUBLIC TYPE DEFINES */
/*****************************************************************************/ /*****************************************************************************/
// A string pointer wrapper to pass cstring from host application to pocketlang // A string pointer wrapper to pass c string between host application and
// vm, with a on_done() callback to clean it when the pocketlang vm is done with // pocket VM. With a on_done() callback to clean it when the pocket VM is done
// the string. // with the string.
struct PkStringPtr { struct PkStringPtr {
const char* string; //< The string result. const char* string; //< The string result.
pkResultDoneFn on_done; //< Called once vm done with the string. pkResultDoneFn on_done; //< Called once vm done with the string.
void* user_data; //< User related data. 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 { struct PkConfiguration {
@ -289,8 +311,10 @@ struct PkConfiguration {
pkWriteFn write_fn; pkWriteFn write_fn;
pkReadFn read_fn; pkReadFn read_fn;
pkFreeInstFn free_inst_fn; pkInstFreeFn inst_free_fn;
pkInstNameFn inst_name_fn; pkInstNameFn inst_name_fn;
pkInstGetAttribFn inst_get_attrib_fn;
pkInstSetAttribFn inst_set_attrib_fn;
pkResolvePathFn resolve_path_fn; pkResolvePathFn resolve_path_fn;
pkLoadScriptFn load_script_fn; pkLoadScriptFn load_script_fn;
@ -326,9 +350,12 @@ struct PkCompileOptions {
/* NATIVE FUNCTION API */ /* NATIVE FUNCTION API */
/*****************************************************************************/ /*****************************************************************************/
// Set a runtime error to vm. // Set a runtime error to VM.
PK_PUBLIC void pkSetRuntimeError(PKVM* vm, const char* message); 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 // Return the type of the [value] this will help to get the type of the
// variable that was extracted from pkGetArg() earlier. // variable that was extracted from pkGetArg() earlier.
PK_PUBLIC PkVarType pkGetValueType(const PkVar value); 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). // registered with -1 argument count (which means variadic arguments).
PK_PUBLIC int pkGetArgc(const PKVM* vm); 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 // 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 // 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 // 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. // Max number of break statement in a loop statement to patch.
#define MAX_BREAK_PATCH 256 #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. // The name of a literal function.
#define LITERAL_FN_NAME "$(LiteralFn)" #define LITERAL_FN_NAME "$(LiteralFn)"
@ -427,9 +422,8 @@ static void reportError(Compiler* compiler, const char* file, int line,
// crash. // crash.
char message[ERROR_MESSAGE_SIZE]; char message[ERROR_MESSAGE_SIZE];
int length = vsprintf(message, fmt, args); int length = vsnprintf(message, sizeof(message), fmt, args);
__ASSERT(length < ERROR_MESSAGE_SIZE, "Error message buffer should not " __ASSERT(length >= 0, "Error message buffer failed at vsnprintf().");
"exceed the buffer");
vm->config.error_fn(vm, PK_ERROR_COMPILE, file, line, message); 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. // Returns a optional compound assignment.
static bool matchAssignment(Compiler* compiler) { static bool matchAssignment(Compiler* compiler) {
if (match(compiler, TK_EQ)) return true; if (match(compiler, TK_EQ)) return true;
if (match(compiler, TK_PLUSEQ)) return true; if (match(compiler, TK_PLUSEQ)) return true;
if (match(compiler, TK_MINUSEQ)) return true; if (match(compiler, TK_MINUSEQ)) return true;
if (match(compiler, TK_STAREQ)) return true; if (match(compiler, TK_STAREQ)) return true;
if (match(compiler, TK_DIVEQ)) return true; if (match(compiler, TK_DIVEQ)) return true;
if (match(compiler, TK_MODEQ)) return true; if (match(compiler, TK_MODEQ)) return true;
if (match(compiler, TK_ANDEQ)) return true; if (match(compiler, TK_ANDEQ)) return true;
if (match(compiler, TK_OREQ)) return true; if (match(compiler, TK_OREQ)) return true;
if (match(compiler, TK_XOREQ)) return true; if (match(compiler, TK_XOREQ)) return true;
if (match(compiler, TK_SRIGHTEQ)) 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; return false;
} }
@ -1885,16 +1880,16 @@ static void emitLoopJump(Compiler* compiler) {
static void emitAssignment(Compiler* compiler, TokenType assignment) { static void emitAssignment(Compiler* compiler, TokenType assignment) {
switch (assignment) { switch (assignment) {
case TK_PLUSEQ: emitOpcode(compiler, OP_ADD); break; case TK_PLUSEQ: emitOpcode(compiler, OP_ADD); break;
case TK_MINUSEQ: emitOpcode(compiler, OP_SUBTRACT); break; case TK_MINUSEQ: emitOpcode(compiler, OP_SUBTRACT); break;
case TK_STAREQ: emitOpcode(compiler, OP_MULTIPLY); break; case TK_STAREQ: emitOpcode(compiler, OP_MULTIPLY); break;
case TK_DIVEQ: emitOpcode(compiler, OP_DIVIDE); break; case TK_DIVEQ: emitOpcode(compiler, OP_DIVIDE); break;
case TK_MODEQ: emitOpcode(compiler, OP_MOD); break; case TK_MODEQ: emitOpcode(compiler, OP_MOD); break;
case TK_ANDEQ: emitOpcode(compiler, OP_BIT_AND); break; case TK_ANDEQ: emitOpcode(compiler, OP_BIT_AND); break;
case TK_OREQ: emitOpcode(compiler, OP_BIT_OR); break; case TK_OREQ: emitOpcode(compiler, OP_BIT_OR); break;
case TK_XOREQ: emitOpcode(compiler, OP_BIT_XOR); break; case TK_XOREQ: emitOpcode(compiler, OP_BIT_XOR); break;
case TK_SRIGHTEQ: emitOpcode(compiler, OP_BIT_RSHIFT); 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: default:
UNREACHABLE(); UNREACHABLE();
break; break;

View File

@ -32,9 +32,6 @@
static const char* DOCSTRING(fn) = docstring; \ static const char* DOCSTRING(fn) = docstring; \
static void fn(PKVM* vm) 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 */ /* CORE PUBLIC API */
/*****************************************************************************/ /*****************************************************************************/
@ -121,7 +118,7 @@ PkHandle* pkGetFunction(PKVM* vm, PkHandle* module,
do { \ do { \
__ASSERT(vm->fiber != NULL, \ __ASSERT(vm->fiber != NULL, \
"This function can only be called at runtime."); \ "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."); \ __ASSERT(value != NULL, "Argument [value] was NULL."); \
} while (false) } while (false)
@ -140,6 +137,26 @@ int pkGetArgc(const PKVM* vm) {
return ARGC; 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). // pkGetArg implementation (see pocketlang.h for description).
PkVar pkGetArg(const PKVM* vm, int arg) { PkVar pkGetArg(const PKVM* vm, int arg) {
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime."); __ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
@ -304,6 +321,9 @@ bool pkFiberIsDone(const PkHandle* fiber) {
/* VALIDATORS */ /* 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]. // Check if [var] is a numeric value (bool/number) and set [value].
static inline bool isNumeric(Var var, double* value) { static inline bool isNumeric(Var var, double* value) {
if (IS_NUM(var)) { if (IS_NUM(var)) {
@ -607,12 +627,9 @@ DEF(coreInput,
} }
DEF(coreExit, DEF(coreExit,
"exit([value:int]) -> null\n" "exit([value:num]) -> null\n"
"Sets the VM's fiber to NULL, causing the VM to return from execution " "Exit the process with an optional exit code provided by the argument "
"and thereby terminating the process entirely. The exit code of the " "[value]. The default exit code is would be 0.") {
"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.") {
int argc = ARGC; int argc = ARGC;
if (argc > 1) { // exit() or exit(val). 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. // TODO: this actually needs to be the VM fiber being set to null though.
exit(value); exit((int)value);
} }
// String functions. // String functions.
@ -967,11 +984,11 @@ DEF(stdMathArcSine,
"expressed in radians.") { "expressed in radians.") {
double num; double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return; 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_ERR(newString(vm, "Argument should be between -1 and +1"));
}
RET(VAR_NUM(asin(num))); RET(VAR_NUM(asin(num)));
} }
@ -982,11 +999,11 @@ DEF(stdMathArcCosine,
"an angle expressed in radians.") { "an angle expressed in radians.") {
double num; double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return; 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_ERR(newString(vm, "Argument should be between -1 and +1"));
}
RET(VAR_NUM(acos(num))); RET(VAR_NUM(acos(num)));
} }
@ -1606,7 +1623,27 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
{ {
Instance* inst = (Instance*)obj; Instance* inst = (Instance*)obj;
if (inst->is_native) { 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 { } else {
@ -1891,5 +1928,7 @@ void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
UNREACHABLE(); UNREACHABLE();
} }
#undef IS_NUM_BYTE
#undef DOCSTRING #undef DOCSTRING
#undef DEF #undef DEF

View File

@ -54,6 +54,9 @@
// The initial minimum capacity of a buffer to allocate. // The initial minimum capacity of a buffer to allocate.
#define MIN_CAPACITY 8 #define MIN_CAPACITY 8
// The size of the error message buffer, used ar vsnprintf (since c99) buffer.
#define ERROR_MESSAGE_SIZE 512
/*****************************************************************************/ /*****************************************************************************/
/* ALLOCATION MACROS */ /* ALLOCATION MACROS */
/*****************************************************************************/ /*****************************************************************************/

View File

@ -1050,9 +1050,9 @@ void freeObject(PKVM* vm, Object* self) {
Instance* inst = (Instance*)self; Instance* inst = (Instance*)self;
if (inst->is_native) { 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. // 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 { } else {
@ -1341,11 +1341,9 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff,
// Check if the list is recursive. // Check if the list is recursive.
OuterSequence* seq = outer; OuterSequence* seq = outer;
while (seq != NULL) { while (seq != NULL) {
if (seq->is_list) { if (seq->is_list && seq->list == list) {
if (seq->list == list) { pkByteBufferAddString(buff, vm, "[...]", 5);
pkByteBufferAddString(buff, vm, "[...]", 5); return;
return;
}
} }
seq = seq->outer; seq = seq->outer;
} }
@ -1372,11 +1370,9 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff,
// Check if the map is recursive. // Check if the map is recursive.
OuterSequence* seq = outer; OuterSequence* seq = outer;
while (seq != NULL) { while (seq != NULL) {
if (!seq->is_list) { if (!seq->is_list && seq->map == map) {
if (seq->map == map) { pkByteBufferAddString(buff, vm, "{...}", 5);
pkByteBufferAddString(buff, vm, "{...}", 5); return;
return;
}
} }
seq = seq->outer; seq = seq->outer;
} }

View File

@ -30,8 +30,10 @@ PkConfiguration pkNewConfiguration(void) {
config.write_fn = NULL; config.write_fn = NULL;
config.read_fn = NULL; config.read_fn = NULL;
config.free_inst_fn = NULL; config.inst_free_fn = NULL;
config.inst_name_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.load_script_fn = NULL;
config.resolve_path_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. ## Map from file extension to it's interpreter, Will be updated.
INTERPRETERS = { INTERPRETERS = {}
'.pk' : None,
'.wren' : None,
'.py' : None,
'.rb' : None,
'.lua' : None,
'.js' : None,
}
def main(): def main():
update_interpreters() update_interpreters()
@ -46,7 +39,7 @@ def main():
def run_all_benchmarsk(): def run_all_benchmarsk():
for benchmark in BENCHMARKS: for benchmark in BENCHMARKS:
print_title(benchmark) print_title(benchmark.title())
dir = join(THIS_PATH, benchmark) dir = join(THIS_PATH, benchmark)
for file in _source_files(os.listdir(dir)): for file in _source_files(os.listdir(dir)):
file = abspath(join(dir, file)) file = abspath(join(dir, file))
@ -55,7 +48,7 @@ def run_all_benchmarsk():
lang, interp, val = INTERPRETERS[ext] lang, interp, val = INTERPRETERS[ext]
if not interp: continue if not interp: continue
print("%-10s : "%lang, end=''); sys.stdout.flush() print(" %-10s : "%lang, end=''); sys.stdout.flush()
result = _run_command([interp, file]) result = _run_command([interp, file])
time = re.findall(r'elapsed:\s*([0-9\.]+)\s*s', time = re.findall(r'elapsed:\s*([0-9\.]+)\s*s',
result.stdout.decode('utf8'), result.stdout.decode('utf8'),
@ -118,7 +111,7 @@ def _get_pocket_binary():
## as (lang, interp, val) tuple, where the val is the additional. ## as (lang, interp, val) tuple, where the val is the additional.
## data related to the interpreter. ## data related to the interpreter.
def _find_interp(lang, interpreter, val): def _find_interp(lang, interpreter, val):
print('%-25s' % ('Searching for %s ' % lang), end='') print('%-27s' % (' Searching for %s ' % lang), end='')
sys.stdout.flush() sys.stdout.flush()
if which(interpreter): if which(interpreter):
print_success('-- found') print_success('-- found')
@ -136,9 +129,9 @@ def get_ext(file_name):
## ---------------------------------------------------------------------------- ## ----------------------------------------------------------------------------
def print_title(title): def print_title(title):
print("----------------------------------") print("-----------------------------------")
print(" %s " % title) print(" %s " % title)
print("----------------------------------") print("-----------------------------------")
## ANSI color codes to print messages. ## ANSI color codes to print messages.
COLORS = { COLORS = {