mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-05 20:26:53 +08:00
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:
parent
794c789fca
commit
273a31e588
@ -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
|
||||
|
13
cli/main.c
13
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) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
// In good first issue
|
||||
// To good first issue
|
||||
Add '.title' attribute to string
|
||||
|
||||
// To implement.
|
||||
|
||||
|
@ -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).
|
||||
|
||||
|
@ -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).
|
2
docs/static/prism/prism.js
vendored
2
docs/static/prism/prism.js
vendored
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
/*****************************************************************************/
|
||||
|
20
src/pk_var.c
20
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 = {
|
||||
|
Loading…
Reference in New Issue
Block a user