mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-06 04:37:47 +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: |
|
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:
|
||||||
|
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
|
## 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.
|
||||||
|
@ -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)"
|
||||||
|
@ -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).
|
||||||
|
@ -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*/) {
|
||||||
|
375
src/pk_core.c
375
src/pk_core.c
@ -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.
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,6 +309,11 @@ struct Function {
|
|||||||
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.
|
||||||
|
|
||||||
|
// 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.
|
bool is_native; //< True if Native function.
|
||||||
union {
|
union {
|
||||||
pkNativeFn native; //< Native function pointer.
|
pkNativeFn native; //< Native 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);
|
||||||
|
@ -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/",
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user