Merge pull request #95 from ThakeeNathees/pk_doc

[WIP] Docstring extracted and `help()` function added
This commit is contained in:
Thakee Nathees 2021-06-18 15:45:19 +05:30 committed by GitHub
commit 733f006b03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 273 additions and 234 deletions

View File

@ -12,7 +12,7 @@ jobs:
run: | run: |
python3 tests/check.py python3 tests/check.py
## Compile and run test on linux system. ## Compile and run test on linux.
linux-build: linux-build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -24,7 +24,7 @@ jobs:
run: | run: |
python3 tests/tests.py python3 tests/tests.py
## Compile and run tests on windows system. ## Compile and run tests on windows.
windows-build: windows-build:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
@ -36,7 +36,7 @@ jobs:
run: | run: |
python3 tests/tests.py python3 tests/tests.py
## Compile and run tests on macos system. ## Compile and run tests on macos.
macos-build: macos-build:
runs-on: macos-latest runs-on: macos-latest
steps: steps:

View File

@ -63,13 +63,10 @@ directory. They were ran using a small python script in the test directory.
## Building From Source ## Building From Source
See [build documentation](https://thakeenathees.github.io/pocketlang/getting-started-build-from-source.html#using-a-build-script) It can be build from source easily without any dependencies, or additional requirements
for using an optional build script (Makefile, batch script for MSVC, SCons found in the `build/` directory). except for a c99 compatible compiler. It can be compiled with the following command.
It can be build from source easily without any dependencies, or additional
requirements except for a c99 compatible compiler. It can be compiled with the
following command.
#### GCC #### GCC / MinGw / Clang (alias with gcc)
``` ```
gcc -o pocket cli/*.c src/*.c -Isrc/include -lm -Wno-int-to-pointer-cast gcc -o pocket cli/*.c src/*.c -Isrc/include -lm -Wno-int-to-pointer-cast
``` ```
@ -79,6 +76,22 @@ gcc -o pocket cli/*.c src/*.c -Isrc/include -lm -Wno-int-to-pointer-cast
cl /Fepocket cli/*.c src/*.c /Isrc/include && rm *.obj cl /Fepocket cli/*.c src/*.c /Isrc/include && rm *.obj
``` ```
#### Makefile
```
make
```
To run make file on windows with mingw, you require `make` and `find` unix command in your path.
Which you can get from [msys2](https://www.msys2.org/) or [cygwin](https://www.cygwin.com/). Run
`set PATH=<path-to-env/usr/bin/>;%PATH% && make`, this will override the system `find` command with
unix `find` for the current session, and run the `make` script.
#### Windows batch script
```
build
```
You don't have to run the script from a Visual Studio .NET developer command prompt, It'll search
for the MSVS installation path and setup the build enviornment.
### For other compiler/IDE ### For other compiler/IDE
1. Create an empty project file / makefile. 1. Create an empty project file / makefile.

View File

@ -54,18 +54,6 @@ extern "C" {
#define PK_PUBLIC #define PK_PUBLIC
#endif #endif
// A convenient macro to define documentation of funcions. Use it to document
// your native functions.
//
// PK_DOC(
// "The function will print 'foo' on the console.",
// static void foo()) {
// printf("foo\n");
// }
//
#define PK_DOC(doc, func) \
/* TODO: static char __pkdoc__##func[] = doc;*/ func
// Name of the implicit function for a module. When a module is parsed all of // Name of the implicit function for a module. When a module is parsed all of
// it's statements are wrapped around an implicit function with this name. // it's statements are wrapped around an implicit function with this name.
#define PK_IMPLICIT_MAIN_NAME "$(SourceBody)" #define PK_IMPLICIT_MAIN_NAME "$(SourceBody)"

View File

@ -80,10 +80,13 @@
#include <stdio.h> //< Only needed here for ASSERT() macro and for release mode #include <stdio.h> //< Only needed here for ASSERT() macro and for release mode
//< TODO; macro use this to print a crash report. //< TODO; macro use this to print a crash report.
// This will terminate the compilation if the [condition] is false, because of #define TOSTRING(x) #x
// 1/0 evaluated. Use this to check missing enums in switch, or check if an #define STRINGIFY(x) TOSTRING(x)
// enum or macro has a specific value. (STATIC_ASSERT(ENUM_SUCCESS == 0)).
#define STATIC_ASSERT(condition) ( 1 / ((int)(condition)) ) // CONCAT_LINE(X) will result evaluvated X<__LINE__>.
#define __CONCAT_LINE_INTERNAL_R(a, b) a ## b
#define __CONCAT_LINE_INTERNAL_F(a, b) __CONCAT_LINE_INTERNAL_R(a, b)
#define CONCAT_LINE(X) __CONCAT_LINE_INTERNAL_F(X, __LINE__)
// The internal assertion macro, this will print error and break regardless of // The internal assertion macro, this will print error and break regardless of
// the build target (debug or release). Use ASSERT() for debug assertion and // the build target (debug or release). Use ASSERT() for debug assertion and
@ -109,6 +112,11 @@
#define DEBUG_BREAK() __builtin_trap() #define DEBUG_BREAK() __builtin_trap()
#endif #endif
// This will terminate the compilation if the [condition] is false, because of
// char _assertion_failure_<__LINE__>[-1] evaluated.
#define STATIC_ASSERT(condition) \
static char CONCAT_LINE(_assertion_failure_)[2*!!(condition) - 1]
#define ASSERT(condition, message) __ASSERT(condition, message) #define ASSERT(condition, message) __ASSERT(condition, message)
#define ASSERT_INDEX(index, size) \ #define ASSERT_INDEX(index, size) \
@ -124,6 +132,8 @@
#else #else
#define STATIC_ASSERT(condition) NO_OP
#define DEBUG_BREAK() NO_OP #define DEBUG_BREAK() NO_OP
#define ASSERT(condition, message) NO_OP #define ASSERT(condition, message) NO_OP
#define ASSERT_INDEX(index, size) NO_OP #define ASSERT_INDEX(index, size) NO_OP
@ -149,9 +159,6 @@
#define TODO __ASSERT(false, "TODO: It hasn't implemented yet.") #define TODO __ASSERT(false, "TODO: It hasn't implemented yet.")
#define OOPS "Oops a bug!! report please." #define OOPS "Oops a bug!! report please."
#define TOSTRING(x) #x
#define STRINGIFY(x) TOSTRING(x)
// The formated string to convert double to string. It'll be with the minimum // The formated string to convert double to string. It'll be with the minimum
// length string representation of either a regular float or a scientific // length string representation of either a regular float or a scientific
// notation (at most 15 decimal points). // notation (at most 15 decimal points).

View File

@ -92,6 +92,7 @@ typedef enum {
TK_MINUSEQ, // -= TK_MINUSEQ, // -=
TK_STAREQ, // *= TK_STAREQ, // *=
TK_DIVEQ, // /= TK_DIVEQ, // /=
//TK_MODEQ, // %=
TK_ANDEQ, // &= TK_ANDEQ, // &=
TK_OREQ, // |= TK_OREQ, // |=
@ -103,8 +104,6 @@ typedef enum {
//TODO: //TODO:
//TK_SRIGHTEQ // >>= //TK_SRIGHTEQ // >>=
//TK_SLEFTEQ // <<= //TK_SLEFTEQ // <<=
//TK_MODEQ, // %=
//TK_XOREQ, // ^=
// Keywords. // Keywords.
TK_MODULE, // module TK_MODULE, // module
@ -1884,7 +1883,7 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) {
} }
Function* func = newFunction(compiler->vm, name, name_length, Function* func = newFunction(compiler->vm, name, name_length,
compiler->script, fn_type == FN_NATIVE); compiler->script, fn_type == FN_NATIVE, NULL);
int fn_index = (int)compiler->script->functions.count - 1; int fn_index = (int)compiler->script->functions.count - 1;
if (fn_index == MAX_FUNCTIONS) { if (fn_index == MAX_FUNCTIONS) {
parseError(compiler, "A script should contain at most %d functions.", parseError(compiler, "A script should contain at most %d functions.",
@ -2561,9 +2560,7 @@ static void compileStatement(Compiler* compiler) {
compiler->new_local = false; compiler->new_local = false;
} }
// If running REPL mode, print the expression's evaluated value. Only if // If running REPL mode, print the expression's evaluated value.
// we're at the top level. Python does print local depth expression too.
// (it's just a design decision).
if (compiler->options && compiler->options->repl_mode && if (compiler->options && compiler->options->repl_mode &&
compiler->func->ptr == compiler->script->body && compiler->func->ptr == compiler->script->body &&
is_expression /*&& compiler->scope_depth == DEPTH_GLOBAL*/) { is_expression /*&& compiler->scope_depth == DEPTH_GLOBAL*/) {
@ -2591,7 +2588,7 @@ static void compileTopLevelStatement(Compiler* compiler) {
} else if (match(compiler, TK_MODULE)) { } else if (match(compiler, TK_MODULE)) {
parseError(compiler, "Module name must be the first statement " parseError(compiler, "Module name must be the first statement "
"of the script."); "of the script.");
} else { } else {
compileStatement(compiler); compileStatement(compiler);

View File

@ -21,6 +21,16 @@
#define M_PI 3.14159265358979323846 #define M_PI 3.14159265358979323846
#endif #endif
// Returns the docstring of the function, which is a static const char* defined
// just above the function by the DEF() macro below.
#define DOCSTRING(fn) _pk_doc_##fn
// A macro to declare a function, with docstring, which is defined as
// _pk_doc_<fn> = docstring; That'll used to generate function help text.
#define DEF(fn, docstring) \
const char* DOCSTRING(fn) = docstring; \
static void fn(PKVM* vm)
/*****************************************************************************/ /*****************************************************************************/
/* CORE PUBLIC API */ /* CORE PUBLIC API */
/*****************************************************************************/ /*****************************************************************************/
@ -36,7 +46,7 @@ static void moduleAddGlobalInternal(PKVM* vm, Script* script,
// The internal function to add functions to a module. // The internal function to add functions to a module.
static void moduleAddFunctionInternal(PKVM* vm, Script* script, static void moduleAddFunctionInternal(PKVM* vm, Script* script,
const char* name, pkNativeFn fptr, const char* name, pkNativeFn fptr,
int arity); int arity, const char* docstring);
// pkNewModule implementation (see pocketlang.h for description). // pkNewModule implementation (see pocketlang.h for description).
PkHandle* pkNewModule(PKVM* vm, const char* name) { PkHandle* pkNewModule(PKVM* vm, const char* name) {
@ -53,7 +63,6 @@ PK_PUBLIC void pkModuleAddGlobal(PKVM* vm, PkHandle* module,
__ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), "Given handle is not a module"); __ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), "Given handle is not a module");
moduleAddGlobalInternal(vm, (Script*)AS_OBJ(scr), name, value->value); moduleAddGlobalInternal(vm, (Script*)AS_OBJ(scr), name, value->value);
} }
// pkModuleAddFunction implementation (see pocketlang.h for description). // pkModuleAddFunction implementation (see pocketlang.h for description).
@ -62,7 +71,8 @@ void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
__ASSERT(module != NULL, "Argument module was NULL."); __ASSERT(module != NULL, "Argument module was NULL.");
Var scr = module->value; Var scr = module->value;
__ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), "Given handle is not a module"); __ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), "Given handle is not a module");
moduleAddFunctionInternal(vm, (Script*)AS_OBJ(scr), name, fptr, arity); moduleAddFunctionInternal(vm, (Script*)AS_OBJ(scr), name, fptr, arity,
NULL /*TODO: Public API for function docstring.*/);
} }
PkHandle* pkGetFunction(PKVM* vm, PkHandle* module, PkHandle* pkGetFunction(PKVM* vm, PkHandle* module,
@ -378,44 +388,78 @@ Script* getCoreLib(const PKVM* vm, String* name) {
/* CORE BUILTIN FUNCTIONS */ /* CORE BUILTIN FUNCTIONS */
/*****************************************************************************/ /*****************************************************************************/
#define FN_IS_PRIMITIVE_TYPE(name, check) \ DEF(coreTypeName,
static void coreIs##name(PKVM* vm) { \
RET(VAR_BOOL(check(ARG(1)))); \
}
#define FN_IS_OBJ_TYPE(name, _enum) \
static void coreIs##name(PKVM* vm) { \
Var arg1 = ARG(1); \
if (IS_OBJ_TYPE(arg1, _enum)) { \
RET(VAR_TRUE); \
} else { \
RET(VAR_FALSE); \
} \
}
FN_IS_PRIMITIVE_TYPE(Null, IS_NULL)
FN_IS_PRIMITIVE_TYPE(Bool, IS_BOOL)
FN_IS_PRIMITIVE_TYPE(Num, IS_NUM)
FN_IS_OBJ_TYPE(String, OBJ_STRING)
FN_IS_OBJ_TYPE(List, OBJ_LIST)
FN_IS_OBJ_TYPE(Map, OBJ_MAP)
FN_IS_OBJ_TYPE(Range, OBJ_RANGE)
FN_IS_OBJ_TYPE(Function, OBJ_FUNC)
FN_IS_OBJ_TYPE(Script, OBJ_SCRIPT)
FN_IS_OBJ_TYPE(UserObj, OBJ_USER)
PK_DOC(
"type_name(value:var) -> string\n" "type_name(value:var) -> string\n"
"Returns the type name of the of the value.", "Returns the type name of the of the value.") {
static void coreTypeName(PKVM* vm)) {
RET(VAR_OBJ(newString(vm, varTypeName(ARG(1))))); RET(VAR_OBJ(newString(vm, varTypeName(ARG(1)))));
} }
PK_DOC( DEF(coreHelp,
"help([fn]) -> null\n"
"This will write an error message to stdout and return null.") {
int argc = ARGC;
if (argc != 0 && argc != 1) {
RET_ERR(newString(vm, "Invalid argument count."));
}
if (argc == 0) {
// If there ins't an io function callback, we're done.
if (vm->config.write_fn == NULL) RET(VAR_NULL);
vm->config.write_fn(vm, "TODO: print help here\n");
} else if (argc == 1) {
Function* fn;
if (!validateArgFunction(vm, 1, &fn)) return;
// If there ins't an io function callback, we're done.
if (vm->config.write_fn == NULL) RET(VAR_NULL);
if (fn->docstring != NULL) {
vm->config.write_fn(vm, fn->docstring);
vm->config.write_fn(vm, "\n\n");
} else {
// TODO: A better message.
vm->config.write_fn(vm, "function '");
vm->config.write_fn(vm, fn->name);
vm->config.write_fn(vm, "()' doesn't have a docstring.\n");
}
}
}
DEF(coreAssert,
"assert(condition:bool [, msg:string]) -> void\n"
"If the condition is false it'll terminate the current fiber with the "
"optional error message") {
int argc = ARGC;
if (argc != 1 && argc != 2) {
RET_ERR(newString(vm, "Invalid argument count."));
}
if (!toBool(ARG(1))) {
String* msg = NULL;
if (argc == 2) {
if (AS_OBJ(ARG(2))->type != OBJ_STRING) {
msg = toString(vm, ARG(2));
} else {
msg = (String*)AS_OBJ(ARG(2));
}
vmPushTempRef(vm, &msg->_super);
VM_SET_ERROR(vm, stringFormat(vm, "Assertion failed: '@'.", msg));
vmPopTempRef(vm);
} else {
VM_SET_ERROR(vm, newString(vm, "Assertion failed."));
}
}
}
DEF(coreBin,
"bin(value:num) -> string\n" "bin(value:num) -> string\n"
"Returns as a binary value string with '0x' prefix.", "Returns as a binary value string with '0x' prefix.") {
static void coreBin(PKVM* vm)) {
int64_t value; int64_t value;
if (!validateInteger(vm, ARG(1), &value, "Argument 1")) return; if (!validateInteger(vm, ARG(1), &value, "Argument 1")) return;
@ -443,10 +487,10 @@ static void coreBin(PKVM* vm)) {
RET(VAR_OBJ(newStringLength(vm, ptr + 1, length))); RET(VAR_OBJ(newStringLength(vm, ptr + 1, length)));
} }
PK_DOC( DEF(coreHex,
"hex(value:num) -> string\n" "hex(value:num) -> string\n"
"Returns as a hexadecimal value string with '0x' prefix.", "Returns as a hexadecimal value string with '0x' prefix.") {
static void coreHex(PKVM* vm)) {
int64_t value; int64_t value;
if (!validateInteger(vm, ARG(1), &value, "Argument 1")) return; if (!validateInteger(vm, ARG(1), &value, "Argument 1")) return;
@ -467,44 +511,15 @@ static void coreHex(PKVM* vm)) {
int length = sprintf(ptr, "%x", _x); int length = sprintf(ptr, "%x", _x);
RET(VAR_OBJ(newStringLength(vm, buff, RET(VAR_OBJ(newStringLength(vm, buff,
(uint32_t) ((ptr + length) - (char*)(buff)) ))); (uint32_t)((ptr + length) - (char*)(buff)))));
} }
PK_DOC( DEF(coreYield,
"assert(condition:bool [, msg:string]) -> void\n"
"If the condition is false it'll terminate the current fiber with the "
"optional error message",
static void coreAssert(PKVM* vm)) {
int argc = ARGC;
if (argc != 1 && argc != 2) {
RET_ERR(newString(vm, "Invalid argument count."));
}
if (!toBool(ARG(1))) {
String* msg = NULL;
if (argc == 2) {
if (AS_OBJ(ARG(2))->type != OBJ_STRING) {
msg = toString(vm, ARG(2));
} else {
msg = (String*)AS_OBJ(ARG(2));
}
vmPushTempRef(vm, &msg->_super);
VM_SET_ERROR(vm, stringFormat(vm, "Assertion failed: '@'.", msg));
vmPopTempRef(vm);
} else {
VM_SET_ERROR(vm, newString(vm, "Assertion failed."));
}
}
}
PK_DOC(
"yield([value]) -> var\n" "yield([value]) -> var\n"
"Return the current function with the yield [value] to current running " "Return the current function with the yield [value] to current running "
"fiber. If the fiber is resumed, it'll run from the next statement of the " "fiber. If the fiber is resumed, it'll run from the next statement of the "
"yield() call. If the fiber resumed with with a value, the return value of " "yield() call. If the fiber resumed with with a value, the return value of "
"the yield() would be that value otherwise null.", "the yield() would be that value otherwise null.") {
static void coreYield(PKVM* vm)) {
int argc = ARGC; int argc = ARGC;
if (argc > 1) { // yield() or yield(val). if (argc > 1) { // yield() or yield(val).
@ -514,18 +529,18 @@ static void coreYield(PKVM* vm)) {
vmYieldFiber(vm, (argc == 1) ? &ARG(1) : NULL); vmYieldFiber(vm, (argc == 1) ? &ARG(1) : NULL);
} }
PK_DOC( DEF(coreToString,
"to_string(value:var) -> string\n" "to_string(value:var) -> string\n"
"Returns the string representation of the value.", "Returns the string representation of the value.") {
static void coreToString(PKVM* vm)) {
RET(VAR_OBJ(toString(vm, ARG(1)))); RET(VAR_OBJ(toString(vm, ARG(1))));
} }
PK_DOC( DEF(corePrint,
"print(...) -> void\n" "print(...) -> void\n"
"Write each argument as comma seperated to the stdout and ends with a " "Write each argument as space seperated, to the stdout and ends with a "
"newline.", "newline.") {
static void corePrint(PKVM* vm)) {
// If the host application doesn't provide any write function, discard the // If the host application doesn't provide any write function, discard the
// output. // output.
if (vm->config.write_fn == NULL) return; if (vm->config.write_fn == NULL) return;
@ -538,11 +553,11 @@ static void corePrint(PKVM* vm)) {
vm->config.write_fn(vm, "\n"); vm->config.write_fn(vm, "\n");
} }
PK_DOC( DEF(coreInput,
"input([msg:var]) -> string\n" "input([msg:var]) -> string\n"
"Read a line from stdin and returns it without the line ending. Accepting " "Read a line from stdin and returns it without the line ending. Accepting "
"an optional argument [msg] and prints it before reading.", "an optional argument [msg] and prints it before reading.") {
static void coreInput(PKVM* vm)) {
int argc = ARGC; int argc = ARGC;
if (argc != 1 && argc != 2) { if (argc != 1 && argc != 2) {
RET_ERR(newString(vm, "Invalid argument count.")); RET_ERR(newString(vm, "Invalid argument count."));
@ -566,10 +581,9 @@ static void coreInput(PKVM* vm)) {
// TODO: substring. // TODO: substring.
PK_DOC( DEF(coreStrChr,
"str_chr(value:number) -> string\n" "str_chr(value:number) -> string\n"
"Returns the ASCII string value of the integer argument.", "Returns the ASCII string value of the integer argument.") {
static void coreStrChr(PKVM* vm)) {
int64_t num; int64_t num;
if (!validateInteger(vm, ARG(1), &num, "Argument 1")) return; if (!validateInteger(vm, ARG(1), &num, "Argument 1")) return;
@ -579,10 +593,10 @@ static void coreStrChr(PKVM* vm)) {
RET(VAR_OBJ(newStringLength(vm, &c, 1))); RET(VAR_OBJ(newStringLength(vm, &c, 1)));
} }
PK_DOC( DEF(coreStrOrd,
"str_ord(value:string) -> number\n" "str_ord(value:string) -> number\n"
"Returns integer value of the given ASCII character.", "Returns integer value of the given ASCII character.") {
static void coreStrOrd(PKVM* vm)) {
String* c; String* c;
if (!validateArgString(vm, 1, &c)) return; if (!validateArgString(vm, 1, &c)) return;
if (c->length != 1) { if (c->length != 1) {
@ -596,10 +610,10 @@ static void coreStrOrd(PKVM* vm)) {
// List functions. // List functions.
// --------------- // ---------------
PK_DOC( DEF(coreListAppend,
"list_append(self:List, value:var) -> List\n" "list_append(self:List, value:var) -> List\n"
"Append the [value] to the list [self] and return the list.", "Append the [value] to the list [self] and return the list.") {
static void coreListAppend(PKVM* vm)) {
List* list; List* list;
if (!validateArgList(vm, 1, &list)) return; if (!validateArgList(vm, 1, &list)) return;
Var elem = ARG(2); Var elem = ARG(2);
@ -611,11 +625,11 @@ static void coreListAppend(PKVM* vm)) {
// Map functions. // Map functions.
// -------------- // --------------
PK_DOC( DEF(coreMapRemove,
"map_remove(self:map, key:var) -> var\n" "map_remove(self:map, key:var) -> var\n"
"Remove the [key] from the map [self] and return it's value if the key " "Remove the [key] from the map [self] and return it's value if the key "
"exists, otherwise it'll return null.", "exists, otherwise it'll return null.") {
static void coreMapRemove(PKVM* vm)) {
Map* map; Map* map;
if (!validateArgMap(vm, 1, &map)) return; if (!validateArgMap(vm, 1, &map)) return;
Var key = ARG(2); Var key = ARG(2);
@ -626,39 +640,38 @@ static void coreMapRemove(PKVM* vm)) {
// Fiber functions. // Fiber functions.
// ---------------- // ----------------
PK_DOC( DEF(coreFiberNew,
"fiber_new(fn:Function) -> fiber\n" "fiber_new(fn:Function) -> fiber\n"
"Create and return a new fiber from the given function [fn].", "Create and return a new fiber from the given function [fn].") {
static void coreFiberNew(PKVM* vm)) {
Function* fn; Function* fn;
if (!validateArgFunction(vm, 1, &fn)) return; if (!validateArgFunction(vm, 1, &fn)) return;
RET(VAR_OBJ(newFiber(vm, fn))); RET(VAR_OBJ(newFiber(vm, fn)));
} }
PK_DOC( DEF(coreFiberGetFunc,
"fiber_get_func(fb:Fiber) -> function\n" "fiber_get_func(fb:Fiber) -> function\n"
"Retruns the fiber's functions. Which is usefull if you want to re-run the " "Retruns the fiber's functions. Which is usefull if you want to re-run the "
"fiber, you can get the function and crate a new fiber.", "fiber, you can get the function and crate a new fiber.") {
static void coreFiberGetFunc(PKVM* vm)) {
Fiber* fb; Fiber* fb;
if (!validateArgFiber(vm, 1, &fb)) return; if (!validateArgFiber(vm, 1, &fb)) return;
RET(VAR_OBJ(fb->func)); RET(VAR_OBJ(fb->func));
} }
PK_DOC( DEF(coreFiberIsDone,
"fiber_is_done(fb:Fiber) -> bool\n" "fiber_is_done(fb:Fiber) -> bool\n"
"Returns true if the fiber [fb] is done running and can no more resumed.", "Returns true if the fiber [fb] is done running and can no more resumed.") {
static void coreFiberIsDone(PKVM* vm)) {
Fiber* fb; Fiber* fb;
if (!validateArgFiber(vm, 1, &fb)) return; if (!validateArgFiber(vm, 1, &fb)) return;
RET(VAR_BOOL(fb->state == FIBER_DONE)); RET(VAR_BOOL(fb->state == FIBER_DONE));
} }
PK_DOC( DEF(coreFiberRun,
"fiber_run(fb:Fiber, ...) -> var\n" "fiber_run(fb:Fiber, ...) -> var\n"
"Runs the fiber's function with the provided arguments and returns it's " "Runs the fiber's function with the provided arguments and returns it's "
"return value or the yielded value if it's yielded.", "return value or the yielded value if it's yielded.") {
static void coreFiberRun(PKVM* vm)) {
int argc = ARGC; int argc = ARGC;
if (argc == 0) // Missing the fiber argument. if (argc == 0) // Missing the fiber argument.
@ -682,11 +695,10 @@ static void coreFiberRun(PKVM* vm)) {
} }
} }
PK_DOC( DEF(coreFiberResume,
"fiber_resume(fb:Fiber) -> var\n" "fiber_resume(fb:Fiber) -> var\n"
"Resumes a yielded function from a previous call of fiber_run() function. " "Resumes a yielded function from a previous call of fiber_run() function. "
"Return it's return value or the yielded value if it's yielded.", "Return it's return value or the yielded value if it's yielded.") {
static void coreFiberResume(PKVM* vm)) {
int argc = ARGC; int argc = ARGC;
if (argc == 0) // Missing the fiber argument. if (argc == 0) // Missing the fiber argument.
@ -772,12 +784,13 @@ static void moduleAddGlobalInternal(PKVM* vm, Script* script,
// An internal function to add a function to the given [script]. // An internal function to add a function to the given [script].
static void moduleAddFunctionInternal(PKVM* vm, Script* script, static void moduleAddFunctionInternal(PKVM* vm, Script* script,
const char* name, pkNativeFn fptr, const char* name, pkNativeFn fptr,
int arity) { int arity, const char* docstring) {
// Ensure the name isn't predefined. // Ensure the name isn't predefined.
assertModuleNameDef(vm, script, name); assertModuleNameDef(vm, script, name);
Function* fn = newFunction(vm, name, (int)strlen(name), script, true); Function* fn = newFunction(vm, name, (int)strlen(name),
script, true, docstring);
fn->native = fptr; fn->native = fptr;
fn->arity = arity; fn->arity = arity;
} }
@ -787,23 +800,27 @@ static void moduleAddFunctionInternal(PKVM* vm, Script* script,
// 'lang' library methods. // 'lang' library methods.
// ----------------------- // -----------------------
// Returns the number of seconds since the application started. DEF(stdLangClock,
void stdLangClock(PKVM* vm) { "clock() -> num\n"
"Returns the number of seconds since the application started") {
RET(VAR_NUM((double)clock() / CLOCKS_PER_SEC)); RET(VAR_NUM((double)clock() / CLOCKS_PER_SEC));
} }
// Trigger garbage collection and return the amount of bytes cleaned. DEF(stdLangGC,
void stdLangGC(PKVM* vm) { "gc() -> num\n"
"Trigger garbage collection and return the amount of bytes cleaned.") {
size_t bytes_before = vm->bytes_allocated; size_t bytes_before = vm->bytes_allocated;
vmCollectGarbage(vm); vmCollectGarbage(vm);
size_t garbage = bytes_before - vm->bytes_allocated; size_t garbage = bytes_before - vm->bytes_allocated;
RET(VAR_NUM((double)garbage)); RET(VAR_NUM((double)garbage));
} }
PK_DOC( DEF(stdLangDisas,
"disas(fn:Function) -> String\n" "disas(fn:Function) -> String\n"
"Returns the disassembled opcode of the function [fn]. ", "Returns the disassembled opcode of the function [fn].") {
static void stdLangDisas(PKVM* vm)) {
Function* fn; Function* fn;
if (!validateArgFunction(vm, 1, &fn)) return; if (!validateArgFunction(vm, 1, &fn)) return;
@ -816,14 +833,18 @@ static void stdLangDisas(PKVM* vm)) {
RET(VAR_OBJ(dump)); RET(VAR_OBJ(dump));
} }
// A debug function for development (will be removed). DEF(stdLangDebugBreak,
void stdLangDebugBreak(PKVM* vm) { "debug_break() -> null\n"
"A debug function for development (will be removed).") {
DEBUG_BREAK(); DEBUG_BREAK();
} }
// Write function, just like print function but it wont put space between args DEF(stdLangWrite,
// and write a new line at the end. "write(...) -> null\n"
void stdLangWrite(PKVM* vm) { "Write function, just like print function but it wont put space between"
"args and write a new line at the end.") {
// If the host application doesn't provide any write function, discard the // If the host application doesn't provide any write function, discard the
// output. // output.
if (vm->config.write_fn == NULL) return; if (vm->config.write_fn == NULL) return;
@ -846,43 +867,51 @@ void stdLangWrite(PKVM* vm) {
// 'math' library methods. // 'math' library methods.
// ----------------------- // -----------------------
void stdMathFloor(PKVM* vm) { DEF(stdMathFloor,
"floor(value:num) -> num\n") {
double num; double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return; if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
RET(VAR_NUM(floor(num))); RET(VAR_NUM(floor(num)));
} }
void stdMathCeil(PKVM* vm) { DEF(stdMathCeil,
"ceil(value:num) -> num\n") {
double num; double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return; if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
RET(VAR_NUM(ceil(num))); RET(VAR_NUM(ceil(num)));
} }
void stdMathPow(PKVM* vm) { DEF(stdMathPow,
"pow(value:num) -> num\n") {
double num, ex; double num, ex;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return; if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
if (!validateNumeric(vm, ARG(2), &ex, "Argument 2")) return; if (!validateNumeric(vm, ARG(2), &ex, "Argument 2")) return;
RET(VAR_NUM(pow(num, ex))); RET(VAR_NUM(pow(num, ex)));
} }
void stdMathSqrt(PKVM* vm) { DEF(stdMathSqrt,
"sqrt(value:num) -> num\n") {
double num; double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return; if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
RET(VAR_NUM(sqrt(num))); RET(VAR_NUM(sqrt(num)));
} }
void stdMathAbs(PKVM* vm) { DEF(stdMathAbs,
"abs(value:num) -> num\n") {
double num; double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return; if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
if (num < 0) num = -num; if (num < 0) num = -num;
RET(VAR_NUM(num)); RET(VAR_NUM(num));
} }
void stdMathSign(PKVM* vm) { DEF(stdMathSign,
"sign(value:num) -> num\n") {
double num; double num;
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return; if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
if (num < 0) num = -1; if (num < 0) num = -1;
@ -891,11 +920,11 @@ void stdMathSign(PKVM* vm) {
RET(VAR_NUM(num)); RET(VAR_NUM(num));
} }
PK_DOC( DEF(stdMathHash,
"hash(value:var) -> num\n" "hash(value:var) -> num\n"
"Return the hash value of the variable, if it's not hashable it'll " "Return the hash value of the variable, if it's not hashable it'll "
"return null.", "return null.") {
static void stdMathHash(PKVM* vm)) {
if (IS_OBJ(ARG(1))) { if (IS_OBJ(ARG(1))) {
if (!isObjectHashable(AS_OBJ(ARG(1))->type)) { if (!isObjectHashable(AS_OBJ(ARG(1))->type)) {
RET(VAR_NULL); RET(VAR_NULL);
@ -904,31 +933,31 @@ static void stdMathHash(PKVM* vm)) {
RET(VAR_NUM((double)varHashValue(ARG(1)))); RET(VAR_NUM((double)varHashValue(ARG(1))));
} }
PK_DOC( DEF(stdMathSine,
"sin(rad:num) -> num\n" "sin(rad:num) -> num\n"
"Return the sine value of the argument [rad] which is an angle expressed " "Return the sine value of the argument [rad] which is an angle expressed "
"in radians.", "in radians.") {
static void stdMathSine(PKVM* vm)) {
double rad; double rad;
if (!validateNumeric(vm, ARG(1), &rad, "Argument 1")) return; if (!validateNumeric(vm, ARG(1), &rad, "Argument 1")) return;
RET(VAR_NUM(sin(rad))); RET(VAR_NUM(sin(rad)));
} }
PK_DOC( DEF(stdMathCosine,
"cos(rad:num) -> num\n" "cos(rad:num) -> num\n"
"Return the cosine value of the argument [rad] which is an angle expressed " "Return the cosine value of the argument [rad] which is an angle expressed "
"in radians.", "in radians.") {
static void stdMathCosine(PKVM* vm)) {
double rad; double rad;
if (!validateNumeric(vm, ARG(1), &rad, "Argument 1")) return; if (!validateNumeric(vm, ARG(1), &rad, "Argument 1")) return;
RET(VAR_NUM(cos(rad))); RET(VAR_NUM(cos(rad)));
} }
PK_DOC( DEF(stdMathTangent,
"tan(rad:num) -> num\n" "tan(rad:num) -> num\n"
"Return the tangent value of the argument [rad] which is an angle expressed " "Return the tangent value of the argument [rad] which is an angle expressed "
"in radians.", "in radians.") {
static void stdMathTangent(PKVM* vm)) {
double rad; double rad;
if (!validateNumeric(vm, ARG(1), &rad, "Argument 1")) return; if (!validateNumeric(vm, ARG(1), &rad, "Argument 1")) return;
RET(VAR_NUM(tan(rad))); RET(VAR_NUM(tan(rad)));
@ -939,11 +968,12 @@ static void stdMathTangent(PKVM* vm)) {
/*****************************************************************************/ /*****************************************************************************/
static void initializeBuiltinFN(PKVM* vm, BuiltinFn* bfn, const char* name, static void initializeBuiltinFN(PKVM* vm, BuiltinFn* bfn, const char* name,
int length, int arity, pkNativeFn ptr) { int length, int arity, pkNativeFn ptr,
const char* docstring) {
bfn->name = name; bfn->name = name;
bfn->length = length; bfn->length = length;
bfn->fn = newFunction(vm, name, length, NULL, true); bfn->fn = newFunction(vm, name, length, NULL, true, docstring);
bfn->fn->arity = arity; bfn->fn->arity = arity;
bfn->fn->native = ptr; bfn->fn->native = ptr;
} }
@ -952,32 +982,23 @@ void initializeCore(PKVM* vm) {
#define INITIALIZE_BUILTIN_FN(name, fn, argc) \ #define INITIALIZE_BUILTIN_FN(name, fn, argc) \
initializeBuiltinFN(vm, &vm->builtins[vm->builtins_count++], name, \ initializeBuiltinFN(vm, &vm->builtins[vm->builtins_count++], name, \
(int)strlen(name), argc, fn); (int)strlen(name), argc, fn, DOCSTRING(fn));
#define MODULE_ADD_FN(module, name, fn, argc) \
moduleAddFunctionInternal(vm, module, name, fn, argc, DOCSTRING(fn))
// Initialize builtin functions. // Initialize builtin functions.
INITIALIZE_BUILTIN_FN("type_name", coreTypeName, 1); INITIALIZE_BUILTIN_FN("type_name", coreTypeName, 1);
// TODO: (maybe remove is_*() functions) suspend by type_name. // TODO: Add is keyword with modules for builtin types.
// and add is keyword with modules for builtin types
// ex: val is Num; val is null; val is List; val is Range // ex: val is Num; val is null; val is List; val is Range
// List.append(l, e) # List is implicitly imported core module. // List.append(l, e) # List is implicitly imported core module.
// String.lower(s) // String.lower(s)
INITIALIZE_BUILTIN_FN("is_null", coreIsNull, 1); INITIALIZE_BUILTIN_FN("help", coreHelp, -1);
INITIALIZE_BUILTIN_FN("is_bool", coreIsBool, 1); INITIALIZE_BUILTIN_FN("assert", coreAssert, -1);
INITIALIZE_BUILTIN_FN("is_num", coreIsNum, 1);
INITIALIZE_BUILTIN_FN("is_string", coreIsString, 1);
INITIALIZE_BUILTIN_FN("is_list", coreIsList, 1);
INITIALIZE_BUILTIN_FN("is_map", coreIsMap, 1);
INITIALIZE_BUILTIN_FN("is_range", coreIsRange, 1);
INITIALIZE_BUILTIN_FN("is_function", coreIsFunction, 1);
INITIALIZE_BUILTIN_FN("is_script", coreIsScript, 1);
INITIALIZE_BUILTIN_FN("is_userobj", coreIsUserObj, 1);
INITIALIZE_BUILTIN_FN("bin", coreBin, 1); INITIALIZE_BUILTIN_FN("bin", coreBin, 1);
INITIALIZE_BUILTIN_FN("hex", coreHex, 1); INITIALIZE_BUILTIN_FN("hex", coreHex, 1);
INITIALIZE_BUILTIN_FN("assert", coreAssert, -1);
INITIALIZE_BUILTIN_FN("yield", coreYield, -1); INITIALIZE_BUILTIN_FN("yield", coreYield, -1);
INITIALIZE_BUILTIN_FN("to_string", coreToString, 1); INITIALIZE_BUILTIN_FN("to_string", coreToString, 1);
INITIALIZE_BUILTIN_FN("print", corePrint, -1); INITIALIZE_BUILTIN_FN("print", corePrint, -1);
@ -1003,25 +1024,25 @@ void initializeCore(PKVM* vm) {
// Core Modules ///////////////////////////////////////////////////////////// // Core Modules /////////////////////////////////////////////////////////////
Script* lang = newModuleInternal(vm, "lang"); Script* lang = newModuleInternal(vm, "lang");
moduleAddFunctionInternal(vm, lang, "clock", stdLangClock, 0); MODULE_ADD_FN(lang, "clock", stdLangClock, 0);
moduleAddFunctionInternal(vm, lang, "gc", stdLangGC, 0); MODULE_ADD_FN(lang, "gc", stdLangGC, 0);
moduleAddFunctionInternal(vm, lang, "disas", stdLangDisas, 1); MODULE_ADD_FN(lang, "disas", stdLangDisas, 1);
moduleAddFunctionInternal(vm, lang, "write", stdLangWrite, -1); MODULE_ADD_FN(lang, "write", stdLangWrite, -1);
#ifdef DEBUG #ifdef DEBUG
moduleAddFunctionInternal(vm, lang, "debug_break", stdLangDebugBreak, 0); MODULE_ADD_FN(lang, "debug_break", stdLangDebugBreak, 0);
#endif #endif
Script* math = newModuleInternal(vm, "math"); Script* math = newModuleInternal(vm, "math");
moduleAddFunctionInternal(vm, math, "floor", stdMathFloor, 1); MODULE_ADD_FN(math, "floor", stdMathFloor, 1);
moduleAddFunctionInternal(vm, math, "ceil", stdMathCeil, 1); MODULE_ADD_FN(math, "ceil", stdMathCeil, 1);
moduleAddFunctionInternal(vm, math, "pow", stdMathPow, 2); MODULE_ADD_FN(math, "pow", stdMathPow, 2);
moduleAddFunctionInternal(vm, math, "sqrt", stdMathSqrt, 1); MODULE_ADD_FN(math, "sqrt", stdMathSqrt, 1);
moduleAddFunctionInternal(vm, math, "abs", stdMathAbs, 1); MODULE_ADD_FN(math, "abs", stdMathAbs, 1);
moduleAddFunctionInternal(vm, math, "sign", stdMathSign, 1); MODULE_ADD_FN(math, "sign", stdMathSign, 1);
moduleAddFunctionInternal(vm, math, "hash", stdMathHash, 1); MODULE_ADD_FN(math, "hash", stdMathHash, 1);
moduleAddFunctionInternal(vm, math, "sin", stdMathSine, 1); MODULE_ADD_FN(math, "sin", stdMathSine, 1);
moduleAddFunctionInternal(vm, math, "cos", stdMathCosine, 1); MODULE_ADD_FN(math, "cos", stdMathCosine, 1);
moduleAddFunctionInternal(vm, math, "tan", stdMathTangent, 1); MODULE_ADD_FN(math, "tan", stdMathTangent, 1);
// TODO: low priority - sinh, cosh, tanh, asin, acos, atan. // TODO: low priority - sinh, cosh, tanh, asin, acos, atan.

View File

@ -337,7 +337,8 @@ Script* newScript(PKVM* vm, String* path) {
vmPushTempRef(vm, &script->_super); vmPushTempRef(vm, &script->_super);
const char* fn_name = PK_IMPLICIT_MAIN_NAME; const char* fn_name = PK_IMPLICIT_MAIN_NAME;
script->body = newFunction(vm, fn_name, (int)strlen(fn_name), script, false); script->body = newFunction(vm, fn_name, (int)strlen(fn_name),
script, false, NULL/*TODO*/);
script->body->arity = 0; // TODO: should it be 1 (ARGV)?. script->body->arity = 0; // TODO: should it be 1 (ARGV)?.
vmPopTempRef(vm); vmPopTempRef(vm);
@ -345,7 +346,7 @@ Script* newScript(PKVM* vm, String* path) {
} }
Function* newFunction(PKVM* vm, const char* name, int length, Script* owner, Function* newFunction(PKVM* vm, const char* name, int length, Script* owner,
bool is_native) { bool is_native, const char* docstring) {
Function* func = ALLOCATE(vm, Function); Function* func = ALLOCATE(vm, Function);
varInitObject(&func->_super, vm, OBJ_FUNC); varInitObject(&func->_super, vm, OBJ_FUNC);
@ -380,6 +381,10 @@ Function* newFunction(PKVM* vm, const char* name, int length, Script* owner,
fn->stack_size = 0; fn->stack_size = 0;
func->fn = fn; func->fn = fn;
} }
// Both native and script (TODO:) functions support docstring.
func->docstring = docstring;
return func; return func;
} }

View File

@ -305,14 +305,19 @@ typedef struct {
struct Function { struct Function {
Object _super; Object _super;
const char* name; //< Name in the script [owner] or C literal. const char* name; //< Name in the script [owner] or C literal.
Script* owner; //< Owner script of the function. Script* owner; //< Owner script of the function.
int arity; //< Number of argument the function expects. int arity; //< Number of argument the function expects.
bool is_native; //< True if Native function. // Docstring of the function, currently it's just the C string literal
// pointer, refactor this into String* so that we can support public
// native functions to provide a docstring.
const char* docstring;
bool is_native; //< True if Native function.
union { union {
pkNativeFn native; //< Native function pointer. pkNativeFn native; //< Native function pointer.
Fn* fn; //< Script function pointer. Fn* fn; //< Script function pointer.
}; };
}; };
@ -406,8 +411,10 @@ Script* newScript(PKVM* vm, String* path);
// be the name in the Script's nametable. If the [owner] is NULL the function // be the name in the Script's nametable. If the [owner] is NULL the function
// would be builtin function. For builtin function arity and the native // would be builtin function. For builtin function arity and the native
// function pointer would be initialized after calling this function. // function pointer would be initialized after calling this function.
// The argument [docstring] is an optional documentation text (could be NULL)
// That'll printed when running help(fn).
Function* newFunction(PKVM* vm, const char* name, int length, Script* owner, Function* newFunction(PKVM* vm, const char* name, int length, Script* owner,
bool is_native); bool is_native, const char* docstring);
// Allocate new Fiber object around the function [fn] and return Fiber*. // Allocate new Fiber object around the function [fn] and return Fiber*.
Fiber* newFiber(PKVM* vm, Function* fn); Fiber* newFiber(PKVM* vm, Function* fn);

View File

@ -25,7 +25,7 @@ HASH_CHECK_LIST = [
] ]
## A list of directory, contains C source files to perform static checks. ## A list of directory, contains C source files to perform static checks.
## This will include both '.c' and '.h' files. ## This will include '.c', '.h', '.py' and '.pk' files.
C_SOURCE_DIRS = [ C_SOURCE_DIRS = [
"../src/", "../src/",
"../cli/", "../cli/",

View File

@ -18,9 +18,9 @@ assert(0xa1b2c3 == 10597059 and 0xff == 255)
assert(0xffffffffffffffff == 18446744073709551615) assert(0xffffffffffffffff == 18446744073709551615)
## Lists test. ## Lists test.
l1 = [1, false, null, func print('hello') end] l1 = [1, false, null, func print('hello') end, true]
assert(is_null(l1[2])) assert(l1[4])
l1[1] = true; assert(l1[1]) l1[3] = null; assert(!l1[3])
l1 = [] + [] ; assert(l1.length == 0) l1 = [] + [] ; assert(l1.length == 0)
l1 = [] + [1]; assert(l1.length == 1); assert(l1[0] == 1) l1 = [] + [1]; assert(l1.length == 1); assert(l1[0] == 1)
l1 = [1] + []; assert(l1.length == 1); assert(l1[0] == 1) l1 = [1] + []; assert(l1.length == 1); assert(l1[0] == 1)

View File

@ -9,13 +9,13 @@ assert(variable == 42, 'If statement failed.')
if false then unreachable() if false then unreachable()
elsif true then variable = null elsif true then variable = null
else unreachable() end else unreachable() end
assert(is_null(variable), 'elsif statement failed.') assert(variable == null)
if false then unreachable() if false then unreachable()
elsif false then unreachable() elsif false then unreachable()
elsif false then unreachable() elsif false then unreachable()
else variable = "changed" end else variable = "changed" end
assert(variable == "changed", 'Else statement failed.') assert(variable == "changed")
if false then unreachable() if false then unreachable()
elsif true elsif true

View File

@ -4,13 +4,13 @@
import os, sys, platform import os, sys, platform
import subprocess, json, re import subprocess, json, re
from os.path import join from os.path import join, abspath, relpath
## TODO: Re write this in doctest (https://github.com/onqtam/doctest) ## TODO: Re write this in doctest (https://github.com/onqtam/doctest)
## The absolute path of this file, when run as a script. ## The absolute path of this file, when run as a script.
## This file is not intended to be included in other files at the moment. ## This file is not intended to be included in other files at the moment.
THIS_PATH = os.path.abspath(os.path.dirname(__file__)) THIS_PATH = abspath(os.path.dirname(__file__))
## All the test files. ## All the test files.
TEST_SUITE = { TEST_SUITE = {
@ -42,9 +42,10 @@ SYSTEM_TO_BINARY_PATH = {
## This global variable will be set to true if any test failed. ## This global variable will be set to true if any test failed.
tests_failed = False tests_failed = False
def main(): def main():
## this will enable ansi codes in windows terminal. ## This will enable ANSI codes in windows terminal.
os.system('') os.system('')
run_all_tests() run_all_tests()
@ -58,7 +59,7 @@ def run_all_tests():
for suite in TEST_SUITE: for suite in TEST_SUITE:
print_title(suite) print_title(suite)
for test in TEST_SUITE[suite]: for test in TEST_SUITE[suite]:
path = os.path.join(THIS_PATH, test) path = join(THIS_PATH, test)
run_test_file(pocket, test, path) run_test_file(pocket, test, path)
def run_test_file(pocket, test, path): def run_test_file(pocket, test, path):
@ -84,7 +85,7 @@ def get_pocket_binary():
if system not in SYSTEM_TO_BINARY_PATH: if system not in SYSTEM_TO_BINARY_PATH:
error_exit("Unsupported platform %s" % system) error_exit("Unsupported platform %s" % system)
pocket = os.path.join(THIS_PATH, SYSTEM_TO_BINARY_PATH[system]) pocket = abspath(join(THIS_PATH, SYSTEM_TO_BINARY_PATH[system]))
if not os.path.exists(pocket): if not os.path.exists(pocket):
error_exit("Pocket interpreter not found at: '%s'" % pocket) error_exit("Pocket interpreter not found at: '%s'" % pocket)
@ -92,8 +93,8 @@ def get_pocket_binary():
def run_command(command): def run_command(command):
return subprocess.run(command, return subprocess.run(command,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) stderr=subprocess.PIPE)
## ---------------------------------------------------------------------------- ## ----------------------------------------------------------------------------