mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-05 20:26:53 +08:00
Merge pull request #95 from ThakeeNathees/pk_doc
[WIP] Docstring extracted and `help()` function added
This commit is contained in:
commit
733f006b03
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
||||
run: |
|
||||
python3 tests/check.py
|
||||
|
||||
## Compile and run test on linux system.
|
||||
## Compile and run test on linux.
|
||||
linux-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@ -24,7 +24,7 @@ jobs:
|
||||
run: |
|
||||
python3 tests/tests.py
|
||||
|
||||
## Compile and run tests on windows system.
|
||||
## Compile and run tests on windows.
|
||||
windows-build:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
@ -36,7 +36,7 @@ jobs:
|
||||
run: |
|
||||
python3 tests/tests.py
|
||||
|
||||
## Compile and run tests on macos system.
|
||||
## Compile and run tests on macos.
|
||||
macos-build:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
|
25
README.md
25
README.md
@ -63,13 +63,10 @@ directory. They were ran using a small python script in the test directory.
|
||||
|
||||
## Building From Source
|
||||
|
||||
See [build documentation](https://thakeenathees.github.io/pocketlang/getting-started-build-from-source.html#using-a-build-script)
|
||||
for using an optional build script (Makefile, batch script for MSVC, SCons found in the `build/` directory).
|
||||
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.
|
||||
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
|
||||
```
|
||||
@ -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
|
||||
```
|
||||
|
||||
#### 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
|
||||
|
||||
1. Create an empty project file / makefile.
|
||||
|
@ -54,18 +54,6 @@ extern "C" {
|
||||
#define PK_PUBLIC
|
||||
#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
|
||||
// it's statements are wrapped around an implicit function with this name.
|
||||
#define PK_IMPLICIT_MAIN_NAME "$(SourceBody)"
|
||||
|
@ -80,10 +80,13 @@
|
||||
#include <stdio.h> //< Only needed here for ASSERT() macro and for release mode
|
||||
//< TODO; macro use this to print a crash report.
|
||||
|
||||
// This will terminate the compilation if the [condition] is false, because of
|
||||
// 1/0 evaluated. Use this to check missing enums in switch, or check if an
|
||||
// enum or macro has a specific value. (STATIC_ASSERT(ENUM_SUCCESS == 0)).
|
||||
#define STATIC_ASSERT(condition) ( 1 / ((int)(condition)) )
|
||||
#define TOSTRING(x) #x
|
||||
#define STRINGIFY(x) TOSTRING(x)
|
||||
|
||||
// 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 build target (debug or release). Use ASSERT() for debug assertion and
|
||||
@ -109,6 +112,11 @@
|
||||
#define DEBUG_BREAK() __builtin_trap()
|
||||
#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_INDEX(index, size) \
|
||||
@ -124,6 +132,8 @@
|
||||
|
||||
#else
|
||||
|
||||
#define STATIC_ASSERT(condition) NO_OP
|
||||
|
||||
#define DEBUG_BREAK() NO_OP
|
||||
#define ASSERT(condition, message) NO_OP
|
||||
#define ASSERT_INDEX(index, size) NO_OP
|
||||
@ -149,9 +159,6 @@
|
||||
#define TODO __ASSERT(false, "TODO: It hasn't implemented yet.")
|
||||
#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
|
||||
// length string representation of either a regular float or a scientific
|
||||
// notation (at most 15 decimal points).
|
||||
|
@ -92,6 +92,7 @@ typedef enum {
|
||||
TK_MINUSEQ, // -=
|
||||
TK_STAREQ, // *=
|
||||
TK_DIVEQ, // /=
|
||||
//TK_MODEQ, // %=
|
||||
|
||||
TK_ANDEQ, // &=
|
||||
TK_OREQ, // |=
|
||||
@ -103,8 +104,6 @@ typedef enum {
|
||||
//TODO:
|
||||
//TK_SRIGHTEQ // >>=
|
||||
//TK_SLEFTEQ // <<=
|
||||
//TK_MODEQ, // %=
|
||||
//TK_XOREQ, // ^=
|
||||
|
||||
// Keywords.
|
||||
TK_MODULE, // module
|
||||
@ -1884,7 +1883,7 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) {
|
||||
}
|
||||
|
||||
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;
|
||||
if (fn_index == MAX_FUNCTIONS) {
|
||||
parseError(compiler, "A script should contain at most %d functions.",
|
||||
@ -2561,9 +2560,7 @@ static void compileStatement(Compiler* compiler) {
|
||||
compiler->new_local = false;
|
||||
}
|
||||
|
||||
// If running REPL mode, print the expression's evaluated value. Only if
|
||||
// we're at the top level. Python does print local depth expression too.
|
||||
// (it's just a design decision).
|
||||
// If running REPL mode, print the expression's evaluated value.
|
||||
if (compiler->options && compiler->options->repl_mode &&
|
||||
compiler->func->ptr == compiler->script->body &&
|
||||
is_expression /*&& compiler->scope_depth == DEPTH_GLOBAL*/) {
|
||||
@ -2591,7 +2588,7 @@ static void compileTopLevelStatement(Compiler* compiler) {
|
||||
|
||||
} else if (match(compiler, TK_MODULE)) {
|
||||
parseError(compiler, "Module name must be the first statement "
|
||||
"of the script.");
|
||||
"of the script.");
|
||||
|
||||
} else {
|
||||
compileStatement(compiler);
|
||||
|
375
src/pk_core.c
375
src/pk_core.c
@ -21,6 +21,16 @@
|
||||
#define M_PI 3.14159265358979323846
|
||||
#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 */
|
||||
/*****************************************************************************/
|
||||
@ -36,7 +46,7 @@ static void moduleAddGlobalInternal(PKVM* vm, Script* script,
|
||||
// The internal function to add functions to a module.
|
||||
static void moduleAddFunctionInternal(PKVM* vm, Script* script,
|
||||
const char* name, pkNativeFn fptr,
|
||||
int arity);
|
||||
int arity, const char* docstring);
|
||||
|
||||
// pkNewModule implementation (see pocketlang.h for description).
|
||||
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");
|
||||
|
||||
moduleAddGlobalInternal(vm, (Script*)AS_OBJ(scr), name, value->value);
|
||||
|
||||
}
|
||||
|
||||
// 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.");
|
||||
Var scr = module->value;
|
||||
__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,
|
||||
@ -378,44 +388,78 @@ Script* getCoreLib(const PKVM* vm, String* name) {
|
||||
/* CORE BUILTIN FUNCTIONS */
|
||||
/*****************************************************************************/
|
||||
|
||||
#define FN_IS_PRIMITIVE_TYPE(name, check) \
|
||||
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(
|
||||
DEF(coreTypeName,
|
||||
"type_name(value:var) -> string\n"
|
||||
"Returns the type name of the of the value.",
|
||||
static void coreTypeName(PKVM* vm)) {
|
||||
"Returns the type name of the of the value.") {
|
||||
|
||||
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"
|
||||
"Returns as a binary value string with '0x' prefix.",
|
||||
static void coreBin(PKVM* vm)) {
|
||||
"Returns as a binary value string with '0x' prefix.") {
|
||||
|
||||
int64_t value;
|
||||
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)));
|
||||
}
|
||||
|
||||
PK_DOC(
|
||||
DEF(coreHex,
|
||||
"hex(value:num) -> string\n"
|
||||
"Returns as a hexadecimal value string with '0x' prefix.",
|
||||
static void coreHex(PKVM* vm)) {
|
||||
"Returns as a hexadecimal value string with '0x' prefix.") {
|
||||
|
||||
int64_t value;
|
||||
if (!validateInteger(vm, ARG(1), &value, "Argument 1")) return;
|
||||
|
||||
@ -467,44 +511,15 @@ static void coreHex(PKVM* vm)) {
|
||||
int length = sprintf(ptr, "%x", _x);
|
||||
|
||||
RET(VAR_OBJ(newStringLength(vm, buff,
|
||||
(uint32_t) ((ptr + length) - (char*)(buff)) )));
|
||||
(uint32_t)((ptr + length) - (char*)(buff)))));
|
||||
}
|
||||
|
||||
PK_DOC(
|
||||
"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(
|
||||
DEF(coreYield,
|
||||
"yield([value]) -> var\n"
|
||||
"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 "
|
||||
"yield() call. If the fiber resumed with with a value, the return value of "
|
||||
"the yield() would be that value otherwise null.",
|
||||
static void coreYield(PKVM* vm)) {
|
||||
"the yield() would be that value otherwise null.") {
|
||||
|
||||
int argc = ARGC;
|
||||
if (argc > 1) { // yield() or yield(val).
|
||||
@ -514,18 +529,18 @@ static void coreYield(PKVM* vm)) {
|
||||
vmYieldFiber(vm, (argc == 1) ? &ARG(1) : NULL);
|
||||
}
|
||||
|
||||
PK_DOC(
|
||||
DEF(coreToString,
|
||||
"to_string(value:var) -> string\n"
|
||||
"Returns the string representation of the value.",
|
||||
static void coreToString(PKVM* vm)) {
|
||||
"Returns the string representation of the value.") {
|
||||
|
||||
RET(VAR_OBJ(toString(vm, ARG(1))));
|
||||
}
|
||||
|
||||
PK_DOC(
|
||||
DEF(corePrint,
|
||||
"print(...) -> void\n"
|
||||
"Write each argument as comma seperated to the stdout and ends with a "
|
||||
"newline.",
|
||||
static void corePrint(PKVM* vm)) {
|
||||
"Write each argument as space seperated, to the stdout and ends with a "
|
||||
"newline.") {
|
||||
|
||||
// If the host application doesn't provide any write function, discard the
|
||||
// output.
|
||||
if (vm->config.write_fn == NULL) return;
|
||||
@ -538,11 +553,11 @@ static void corePrint(PKVM* vm)) {
|
||||
vm->config.write_fn(vm, "\n");
|
||||
}
|
||||
|
||||
PK_DOC(
|
||||
DEF(coreInput,
|
||||
"input([msg:var]) -> string\n"
|
||||
"Read a line from stdin and returns it without the line ending. Accepting "
|
||||
"an optional argument [msg] and prints it before reading.",
|
||||
static void coreInput(PKVM* vm)) {
|
||||
"an optional argument [msg] and prints it before reading.") {
|
||||
|
||||
int argc = ARGC;
|
||||
if (argc != 1 && argc != 2) {
|
||||
RET_ERR(newString(vm, "Invalid argument count."));
|
||||
@ -566,10 +581,9 @@ static void coreInput(PKVM* vm)) {
|
||||
|
||||
// TODO: substring.
|
||||
|
||||
PK_DOC(
|
||||
DEF(coreStrChr,
|
||||
"str_chr(value:number) -> string\n"
|
||||
"Returns the ASCII string value of the integer argument.",
|
||||
static void coreStrChr(PKVM* vm)) {
|
||||
"Returns the ASCII string value of the integer argument.") {
|
||||
int64_t num;
|
||||
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)));
|
||||
}
|
||||
|
||||
PK_DOC(
|
||||
DEF(coreStrOrd,
|
||||
"str_ord(value:string) -> number\n"
|
||||
"Returns integer value of the given ASCII character.",
|
||||
static void coreStrOrd(PKVM* vm)) {
|
||||
"Returns integer value of the given ASCII character.") {
|
||||
|
||||
String* c;
|
||||
if (!validateArgString(vm, 1, &c)) return;
|
||||
if (c->length != 1) {
|
||||
@ -596,10 +610,10 @@ static void coreStrOrd(PKVM* vm)) {
|
||||
// List functions.
|
||||
// ---------------
|
||||
|
||||
PK_DOC(
|
||||
DEF(coreListAppend,
|
||||
"list_append(self:List, value:var) -> List\n"
|
||||
"Append the [value] to the list [self] and return the list.",
|
||||
static void coreListAppend(PKVM* vm)) {
|
||||
"Append the [value] to the list [self] and return the list.") {
|
||||
|
||||
List* list;
|
||||
if (!validateArgList(vm, 1, &list)) return;
|
||||
Var elem = ARG(2);
|
||||
@ -611,11 +625,11 @@ static void coreListAppend(PKVM* vm)) {
|
||||
// Map functions.
|
||||
// --------------
|
||||
|
||||
PK_DOC(
|
||||
DEF(coreMapRemove,
|
||||
"map_remove(self:map, key:var) -> var\n"
|
||||
"Remove the [key] from the map [self] and return it's value if the key "
|
||||
"exists, otherwise it'll return null.",
|
||||
static void coreMapRemove(PKVM* vm)) {
|
||||
"exists, otherwise it'll return null.") {
|
||||
|
||||
Map* map;
|
||||
if (!validateArgMap(vm, 1, &map)) return;
|
||||
Var key = ARG(2);
|
||||
@ -626,39 +640,38 @@ static void coreMapRemove(PKVM* vm)) {
|
||||
// Fiber functions.
|
||||
// ----------------
|
||||
|
||||
PK_DOC(
|
||||
DEF(coreFiberNew,
|
||||
"fiber_new(fn:Function) -> fiber\n"
|
||||
"Create and return a new fiber from the given function [fn].",
|
||||
static void coreFiberNew(PKVM* vm)) {
|
||||
"Create and return a new fiber from the given function [fn].") {
|
||||
|
||||
Function* fn;
|
||||
if (!validateArgFunction(vm, 1, &fn)) return;
|
||||
RET(VAR_OBJ(newFiber(vm, fn)));
|
||||
}
|
||||
|
||||
PK_DOC(
|
||||
DEF(coreFiberGetFunc,
|
||||
"fiber_get_func(fb:Fiber) -> function\n"
|
||||
"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.",
|
||||
static void coreFiberGetFunc(PKVM* vm)) {
|
||||
"fiber, you can get the function and crate a new fiber.") {
|
||||
|
||||
Fiber* fb;
|
||||
if (!validateArgFiber(vm, 1, &fb)) return;
|
||||
RET(VAR_OBJ(fb->func));
|
||||
}
|
||||
|
||||
PK_DOC(
|
||||
DEF(coreFiberIsDone,
|
||||
"fiber_is_done(fb:Fiber) -> bool\n"
|
||||
"Returns true if the fiber [fb] is done running and can no more resumed.",
|
||||
static void coreFiberIsDone(PKVM* vm)) {
|
||||
"Returns true if the fiber [fb] is done running and can no more resumed.") {
|
||||
|
||||
Fiber* fb;
|
||||
if (!validateArgFiber(vm, 1, &fb)) return;
|
||||
RET(VAR_BOOL(fb->state == FIBER_DONE));
|
||||
}
|
||||
|
||||
PK_DOC(
|
||||
DEF(coreFiberRun,
|
||||
"fiber_run(fb:Fiber, ...) -> var\n"
|
||||
"Runs the fiber's function with the provided arguments and returns it's "
|
||||
"return value or the yielded value if it's yielded.",
|
||||
static void coreFiberRun(PKVM* vm)) {
|
||||
"return value or the yielded value if it's yielded.") {
|
||||
|
||||
int argc = ARGC;
|
||||
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"
|
||||
"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.",
|
||||
static void coreFiberResume(PKVM* vm)) {
|
||||
"Return it's return value or the yielded value if it's yielded.") {
|
||||
|
||||
int argc = ARGC;
|
||||
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].
|
||||
static void moduleAddFunctionInternal(PKVM* vm, Script* script,
|
||||
const char* name, pkNativeFn fptr,
|
||||
int arity) {
|
||||
int arity, const char* docstring) {
|
||||
|
||||
// Ensure the name isn't predefined.
|
||||
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->arity = arity;
|
||||
}
|
||||
@ -787,23 +800,27 @@ static void moduleAddFunctionInternal(PKVM* vm, Script* script,
|
||||
// 'lang' library methods.
|
||||
// -----------------------
|
||||
|
||||
// Returns the number of seconds since the application started.
|
||||
void stdLangClock(PKVM* vm) {
|
||||
DEF(stdLangClock,
|
||||
"clock() -> num\n"
|
||||
"Returns the number of seconds since the application started") {
|
||||
|
||||
RET(VAR_NUM((double)clock() / CLOCKS_PER_SEC));
|
||||
}
|
||||
|
||||
// Trigger garbage collection and return the amount of bytes cleaned.
|
||||
void stdLangGC(PKVM* vm) {
|
||||
DEF(stdLangGC,
|
||||
"gc() -> num\n"
|
||||
"Trigger garbage collection and return the amount of bytes cleaned.") {
|
||||
|
||||
size_t bytes_before = vm->bytes_allocated;
|
||||
vmCollectGarbage(vm);
|
||||
size_t garbage = bytes_before - vm->bytes_allocated;
|
||||
RET(VAR_NUM((double)garbage));
|
||||
}
|
||||
|
||||
PK_DOC(
|
||||
DEF(stdLangDisas,
|
||||
"disas(fn:Function) -> String\n"
|
||||
"Returns the disassembled opcode of the function [fn]. ",
|
||||
static void stdLangDisas(PKVM* vm)) {
|
||||
"Returns the disassembled opcode of the function [fn].") {
|
||||
|
||||
Function* fn;
|
||||
if (!validateArgFunction(vm, 1, &fn)) return;
|
||||
|
||||
@ -816,14 +833,18 @@ static void stdLangDisas(PKVM* vm)) {
|
||||
RET(VAR_OBJ(dump));
|
||||
}
|
||||
|
||||
// A debug function for development (will be removed).
|
||||
void stdLangDebugBreak(PKVM* vm) {
|
||||
DEF(stdLangDebugBreak,
|
||||
"debug_break() -> null\n"
|
||||
"A debug function for development (will be removed).") {
|
||||
|
||||
DEBUG_BREAK();
|
||||
}
|
||||
|
||||
// Write function, just like print function but it wont put space between args
|
||||
// and write a new line at the end.
|
||||
void stdLangWrite(PKVM* vm) {
|
||||
DEF(stdLangWrite,
|
||||
"write(...) -> null\n"
|
||||
"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
|
||||
// output.
|
||||
if (vm->config.write_fn == NULL) return;
|
||||
@ -846,43 +867,51 @@ void stdLangWrite(PKVM* vm) {
|
||||
// 'math' library methods.
|
||||
// -----------------------
|
||||
|
||||
void stdMathFloor(PKVM* vm) {
|
||||
DEF(stdMathFloor,
|
||||
"floor(value:num) -> num\n") {
|
||||
|
||||
double num;
|
||||
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
|
||||
|
||||
RET(VAR_NUM(floor(num)));
|
||||
}
|
||||
|
||||
void stdMathCeil(PKVM* vm) {
|
||||
DEF(stdMathCeil,
|
||||
"ceil(value:num) -> num\n") {
|
||||
|
||||
double num;
|
||||
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
|
||||
|
||||
RET(VAR_NUM(ceil(num)));
|
||||
}
|
||||
|
||||
void stdMathPow(PKVM* vm) {
|
||||
DEF(stdMathPow,
|
||||
"pow(value:num) -> num\n") {
|
||||
|
||||
double num, ex;
|
||||
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
|
||||
if (!validateNumeric(vm, ARG(2), &ex, "Argument 2")) return;
|
||||
|
||||
RET(VAR_NUM(pow(num, ex)));
|
||||
}
|
||||
|
||||
void stdMathSqrt(PKVM* vm) {
|
||||
DEF(stdMathSqrt,
|
||||
"sqrt(value:num) -> num\n") {
|
||||
|
||||
double num;
|
||||
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
|
||||
|
||||
RET(VAR_NUM(sqrt(num)));
|
||||
}
|
||||
|
||||
void stdMathAbs(PKVM* vm) {
|
||||
DEF(stdMathAbs,
|
||||
"abs(value:num) -> num\n") {
|
||||
|
||||
double num;
|
||||
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
|
||||
if (num < 0) num = -num;
|
||||
RET(VAR_NUM(num));
|
||||
}
|
||||
|
||||
void stdMathSign(PKVM* vm) {
|
||||
DEF(stdMathSign,
|
||||
"sign(value:num) -> num\n") {
|
||||
|
||||
double num;
|
||||
if (!validateNumeric(vm, ARG(1), &num, "Argument 1")) return;
|
||||
if (num < 0) num = -1;
|
||||
@ -891,11 +920,11 @@ void stdMathSign(PKVM* vm) {
|
||||
RET(VAR_NUM(num));
|
||||
}
|
||||
|
||||
PK_DOC(
|
||||
DEF(stdMathHash,
|
||||
"hash(value:var) -> num\n"
|
||||
"Return the hash value of the variable, if it's not hashable it'll "
|
||||
"return null.",
|
||||
static void stdMathHash(PKVM* vm)) {
|
||||
"return null.") {
|
||||
|
||||
if (IS_OBJ(ARG(1))) {
|
||||
if (!isObjectHashable(AS_OBJ(ARG(1))->type)) {
|
||||
RET(VAR_NULL);
|
||||
@ -904,31 +933,31 @@ static void stdMathHash(PKVM* vm)) {
|
||||
RET(VAR_NUM((double)varHashValue(ARG(1))));
|
||||
}
|
||||
|
||||
PK_DOC(
|
||||
DEF(stdMathSine,
|
||||
"sin(rad:num) -> num\n"
|
||||
"Return the sine value of the argument [rad] which is an angle expressed "
|
||||
"in radians.",
|
||||
static void stdMathSine(PKVM* vm)) {
|
||||
"in radians.") {
|
||||
|
||||
double rad;
|
||||
if (!validateNumeric(vm, ARG(1), &rad, "Argument 1")) return;
|
||||
RET(VAR_NUM(sin(rad)));
|
||||
}
|
||||
|
||||
PK_DOC(
|
||||
DEF(stdMathCosine,
|
||||
"cos(rad:num) -> num\n"
|
||||
"Return the cosine value of the argument [rad] which is an angle expressed "
|
||||
"in radians.",
|
||||
static void stdMathCosine(PKVM* vm)) {
|
||||
"in radians.") {
|
||||
|
||||
double rad;
|
||||
if (!validateNumeric(vm, ARG(1), &rad, "Argument 1")) return;
|
||||
RET(VAR_NUM(cos(rad)));
|
||||
}
|
||||
|
||||
PK_DOC(
|
||||
DEF(stdMathTangent,
|
||||
"tan(rad:num) -> num\n"
|
||||
"Return the tangent value of the argument [rad] which is an angle expressed "
|
||||
"in radians.",
|
||||
static void stdMathTangent(PKVM* vm)) {
|
||||
"in radians.") {
|
||||
|
||||
double rad;
|
||||
if (!validateNumeric(vm, ARG(1), &rad, "Argument 1")) return;
|
||||
RET(VAR_NUM(tan(rad)));
|
||||
@ -939,11 +968,12 @@ static void stdMathTangent(PKVM* vm)) {
|
||||
/*****************************************************************************/
|
||||
|
||||
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->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->native = ptr;
|
||||
}
|
||||
@ -952,32 +982,23 @@ void initializeCore(PKVM* vm) {
|
||||
|
||||
#define INITIALIZE_BUILTIN_FN(name, fn, argc) \
|
||||
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_FN("type_name", coreTypeName, 1);
|
||||
|
||||
// TODO: (maybe remove is_*() functions) suspend by type_name.
|
||||
// and add is keyword with modules for builtin types
|
||||
// TODO: Add is keyword with modules for builtin types.
|
||||
// ex: val is Num; val is null; val is List; val is Range
|
||||
// List.append(l, e) # List is implicitly imported core module.
|
||||
// String.lower(s)
|
||||
|
||||
INITIALIZE_BUILTIN_FN("is_null", coreIsNull, 1);
|
||||
INITIALIZE_BUILTIN_FN("is_bool", coreIsBool, 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("help", coreHelp, -1);
|
||||
INITIALIZE_BUILTIN_FN("assert", coreAssert, -1);
|
||||
INITIALIZE_BUILTIN_FN("bin", coreBin, 1);
|
||||
INITIALIZE_BUILTIN_FN("hex", coreHex, 1);
|
||||
INITIALIZE_BUILTIN_FN("assert", coreAssert, -1);
|
||||
INITIALIZE_BUILTIN_FN("yield", coreYield, -1);
|
||||
INITIALIZE_BUILTIN_FN("to_string", coreToString, 1);
|
||||
INITIALIZE_BUILTIN_FN("print", corePrint, -1);
|
||||
@ -1003,25 +1024,25 @@ void initializeCore(PKVM* vm) {
|
||||
// Core Modules /////////////////////////////////////////////////////////////
|
||||
|
||||
Script* lang = newModuleInternal(vm, "lang");
|
||||
moduleAddFunctionInternal(vm, lang, "clock", stdLangClock, 0);
|
||||
moduleAddFunctionInternal(vm, lang, "gc", stdLangGC, 0);
|
||||
moduleAddFunctionInternal(vm, lang, "disas", stdLangDisas, 1);
|
||||
moduleAddFunctionInternal(vm, lang, "write", stdLangWrite, -1);
|
||||
MODULE_ADD_FN(lang, "clock", stdLangClock, 0);
|
||||
MODULE_ADD_FN(lang, "gc", stdLangGC, 0);
|
||||
MODULE_ADD_FN(lang, "disas", stdLangDisas, 1);
|
||||
MODULE_ADD_FN(lang, "write", stdLangWrite, -1);
|
||||
#ifdef DEBUG
|
||||
moduleAddFunctionInternal(vm, lang, "debug_break", stdLangDebugBreak, 0);
|
||||
MODULE_ADD_FN(lang, "debug_break", stdLangDebugBreak, 0);
|
||||
#endif
|
||||
|
||||
Script* math = newModuleInternal(vm, "math");
|
||||
moduleAddFunctionInternal(vm, math, "floor", stdMathFloor, 1);
|
||||
moduleAddFunctionInternal(vm, math, "ceil", stdMathCeil, 1);
|
||||
moduleAddFunctionInternal(vm, math, "pow", stdMathPow, 2);
|
||||
moduleAddFunctionInternal(vm, math, "sqrt", stdMathSqrt, 1);
|
||||
moduleAddFunctionInternal(vm, math, "abs", stdMathAbs, 1);
|
||||
moduleAddFunctionInternal(vm, math, "sign", stdMathSign, 1);
|
||||
moduleAddFunctionInternal(vm, math, "hash", stdMathHash, 1);
|
||||
moduleAddFunctionInternal(vm, math, "sin", stdMathSine, 1);
|
||||
moduleAddFunctionInternal(vm, math, "cos", stdMathCosine, 1);
|
||||
moduleAddFunctionInternal(vm, math, "tan", stdMathTangent, 1);
|
||||
MODULE_ADD_FN(math, "floor", stdMathFloor, 1);
|
||||
MODULE_ADD_FN(math, "ceil", stdMathCeil, 1);
|
||||
MODULE_ADD_FN(math, "pow", stdMathPow, 2);
|
||||
MODULE_ADD_FN(math, "sqrt", stdMathSqrt, 1);
|
||||
MODULE_ADD_FN(math, "abs", stdMathAbs, 1);
|
||||
MODULE_ADD_FN(math, "sign", stdMathSign, 1);
|
||||
MODULE_ADD_FN(math, "hash", stdMathHash, 1);
|
||||
MODULE_ADD_FN(math, "sin", stdMathSine, 1);
|
||||
MODULE_ADD_FN(math, "cos", stdMathCosine, 1);
|
||||
MODULE_ADD_FN(math, "tan", stdMathTangent, 1);
|
||||
|
||||
// TODO: low priority - sinh, cosh, tanh, asin, acos, atan.
|
||||
|
||||
|
@ -337,7 +337,8 @@ Script* newScript(PKVM* vm, String* path) {
|
||||
|
||||
vmPushTempRef(vm, &script->_super);
|
||||
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)?.
|
||||
vmPopTempRef(vm);
|
||||
|
||||
@ -345,7 +346,7 @@ Script* newScript(PKVM* vm, String* path) {
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
func->fn = fn;
|
||||
}
|
||||
|
||||
// Both native and script (TODO:) functions support docstring.
|
||||
func->docstring = docstring;
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
|
21
src/pk_var.h
21
src/pk_var.h
@ -305,14 +305,19 @@ typedef struct {
|
||||
struct Function {
|
||||
Object _super;
|
||||
|
||||
const char* name; //< Name in the script [owner] or C literal.
|
||||
Script* owner; //< Owner script of the function.
|
||||
int arity; //< Number of argument the function expects.
|
||||
const char* name; //< Name in the script [owner] or C literal.
|
||||
Script* owner; //< Owner script of the function.
|
||||
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 {
|
||||
pkNativeFn native; //< Native function pointer.
|
||||
Fn* fn; //< Script function pointer.
|
||||
pkNativeFn native; //< Native 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
|
||||
// would be builtin function. For builtin function arity and the native
|
||||
// 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,
|
||||
bool is_native);
|
||||
bool is_native, const char* docstring);
|
||||
|
||||
// Allocate new Fiber object around the function [fn] and return Fiber*.
|
||||
Fiber* newFiber(PKVM* vm, Function* fn);
|
||||
|
@ -25,7 +25,7 @@ HASH_CHECK_LIST = [
|
||||
]
|
||||
|
||||
## 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 = [
|
||||
"../src/",
|
||||
"../cli/",
|
||||
|
@ -18,9 +18,9 @@ assert(0xa1b2c3 == 10597059 and 0xff == 255)
|
||||
assert(0xffffffffffffffff == 18446744073709551615)
|
||||
|
||||
## Lists test.
|
||||
l1 = [1, false, null, func print('hello') end]
|
||||
assert(is_null(l1[2]))
|
||||
l1[1] = true; assert(l1[1])
|
||||
l1 = [1, false, null, func print('hello') end, true]
|
||||
assert(l1[4])
|
||||
l1[3] = null; assert(!l1[3])
|
||||
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)
|
||||
|
@ -9,13 +9,13 @@ assert(variable == 42, 'If statement failed.')
|
||||
if false then unreachable()
|
||||
elsif true then variable = null
|
||||
else unreachable() end
|
||||
assert(is_null(variable), 'elsif statement failed.')
|
||||
assert(variable == null)
|
||||
|
||||
if false then unreachable()
|
||||
elsif false then unreachable()
|
||||
elsif false then unreachable()
|
||||
else variable = "changed" end
|
||||
assert(variable == "changed", 'Else statement failed.')
|
||||
assert(variable == "changed")
|
||||
|
||||
if false then unreachable()
|
||||
elsif true
|
||||
|
@ -4,13 +4,13 @@
|
||||
|
||||
import os, sys, platform
|
||||
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)
|
||||
|
||||
## 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_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||
THIS_PATH = abspath(os.path.dirname(__file__))
|
||||
|
||||
## All the test files.
|
||||
TEST_SUITE = {
|
||||
@ -42,9 +42,10 @@ SYSTEM_TO_BINARY_PATH = {
|
||||
|
||||
## This global variable will be set to true if any test failed.
|
||||
tests_failed = False
|
||||
|
||||
def main():
|
||||
|
||||
## this will enable ansi codes in windows terminal.
|
||||
## This will enable ANSI codes in windows terminal.
|
||||
os.system('')
|
||||
|
||||
run_all_tests()
|
||||
@ -58,7 +59,7 @@ def run_all_tests():
|
||||
for suite in TEST_SUITE:
|
||||
print_title(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)
|
||||
|
||||
def run_test_file(pocket, test, path):
|
||||
@ -84,7 +85,7 @@ def get_pocket_binary():
|
||||
if system not in SYSTEM_TO_BINARY_PATH:
|
||||
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):
|
||||
error_exit("Pocket interpreter not found at: '%s'" % pocket)
|
||||
|
||||
@ -92,8 +93,8 @@ def get_pocket_binary():
|
||||
|
||||
def run_command(command):
|
||||
return subprocess.run(command,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
## ----------------------------------------------------------------------------
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user