Merge pull request #38 from ThakeeNathees/more-docs

more comments written through out the source
This commit is contained in:
Thakee Nathees 2021-06-03 07:44:03 +05:30
commit 6ee8226a50
19 changed files with 293 additions and 134 deletions

View File

@ -5,9 +5,10 @@
**Pocketlang** is a small (~3000 semicollons) and fast functional programming **Pocketlang** is a small (~3000 semicollons) and fast functional programming
language written in C. It's syntactically similar to Ruby and it can be learned language written in C. It's syntactically similar to Ruby and it can be learned
in less than an hour. Including the compiler, bytecode VM and runtime, it's a in [less than an hour](https://thakeenathees.github.io/pocketlang/getting-started-learn-in-5-minutes.html).
standalone executable with zero external dependecies just as it's self descriptive Including the compiler, bytecode VM and runtime, it's a standalone executable
name. The pocketlang VM can be embedded in another hosting program very easily. with zero external dependecies just as it's self descriptive name. The pocketlang
VM can be embedded in another hosting program very easily.
The language is written using [Wren Language](https://wren.io/) and their The language is written using [Wren Language](https://wren.io/) and their
wonderful book [craftinginterpreters](http://www.craftinginterpreters.com/) as wonderful book [craftinginterpreters](http://www.craftinginterpreters.com/) as

View File

@ -1,35 +1,35 @@
// To implement. // To implement.
- Implement resolve path in cli.
- Implement file IO
- Implement argparse. - Implement argparse.
- -v --version - -v --version
- emit opcodes - emit opcodes
- maybe write a similer .pyc file for pocket - maybe write a similer .pyc file for pocket
- --docs to generate docs from cstring. - --docs to generate docs from cstring.
- dump compiled code to a file.
- Ignore line with '\' character. - In keyword.
- Structs (maybe enums).
- Structs. - Implement file IO (require structs).
- Implement math library. - Implement gdb like debugger (add color print for readability).
- Single header for embedding.
- Implement fiber from script body and vm run fibers (not scripts). - Implement fiber from script body and vm run fibers (not scripts).
Then remove vm's root script. Then remove vm's root script.
- C Integration api (including add core lib from host application). - Initialize imported scripts (require fiber based vm).
- REPL. - REPL. compile expression for a script.
- compile expression for a script. - Hex, binary literals and floats like ".5".
- Var handler implement. - Docs build to a single directory, and replace url space (%20) with a hyphen.
- Complete all the TODO; macros.
// Low priority.
- Ignore line with '\' character.
- Make it possible to override function names. - Make it possible to override function names.
- To do so the functions and global variables should be in the same - To do so the functions and global variables should be in the same
buffer as the property of the script. buffer as the property of the script.
- Hex, binary literals and floats like ".5".
- Function docstring property. - Function docstring property.
- Union tagging alter in var. - Union tagging alter in var.
- (future) add structs and maybe enums. - Github actions.
// Add more. // Add more.
- Single header for embedding (script in pk, require file IO).
[ ] Compilte core methods. [ ] Compilte core methods.
[ ] Complete var methods. [ ] Complete var methods.
[ ] Complete core functions. [ ] Complete core functions.

View File

@ -4,7 +4,7 @@
*/ */
// This file will include all source files from the cli sub-directories here // This file will include all source files from the cli sub-directories here
// (thirdparty/modules). into a single file. That'll make it easy to compile // (thirdparty/modules) into a single file. That'll make it easy to compile
// with the command `gcc cli/*.c ...` instead of adding each sub-directory to // with the command `gcc cli/*.c ...` instead of adding each sub-directory to
// the list of source directory. // the list of source directory.

View File

@ -18,7 +18,7 @@ ROOT_URL = 'https://thakeenathees.github.io/pocketlang/'
## Home page should be in the SOURCE_DIR. ## Home page should be in the SOURCE_DIR.
HOME_PAGE = 'home.md' HOME_PAGE = 'home.md'
TRY_PAGE = 'try it now.html' TRY_PAGE = 'try-it-now.html'
SOURCE_DIR = 'pages/' SOURCE_DIR = 'pages/'
TARGET_DIR = 'build/' TARGET_DIR = 'build/'
STATIC_DIR = 'static/' STATIC_DIR = 'static/'
@ -36,14 +36,16 @@ WASM_SOURCE_FILES = '''\
''' '''
## Navigation pages in order. Should match the path names. ## Navigation pages in order. Should match the path names.
## Any file/folder name shouldn't contain white space.
PAGES = [ PAGES = [
('Getting Started', [ ('Getting-Started', [
TRY_PAGE, TRY_PAGE,
'build from source.md', 'learn-in-5-minutes.md',
'build-from-source.md',
'contributing.md', 'contributing.md',
]), ]),
('Language API', [ ('Language-API', [
'variables.md', 'variables.md',
'functions.md', 'functions.md',
'modules.md', 'modules.md',
@ -85,15 +87,17 @@ def main():
ctx = generate_page_context(join(SOURCE_DIR, HOME_PAGE), index_html, navigation) ctx = generate_page_context(join(SOURCE_DIR, HOME_PAGE), index_html, navigation)
write_page(ctx, template, index_html) write_page(ctx, template, index_html)
for entry in PAGES: for entry in PAGES: ## entry = ('dirname', [files...])
_dir = entry[0] _dir = entry[0]
for file in entry[1]: for file in entry[1]:
ext = get_validated_ext(file) ext = get_validated_ext(file)
path = join(SOURCE_DIR, _dir, file) path = join(SOURCE_DIR, _dir, file)
dst = '' dst = ''; path_prefix = _dir.lower().replace(' ', '-') + '-'
if ext == '.md' : dst = join(TARGET_DIR, _dir, file.replace('.md', '.html')) if ext == '.md':
else: dst = join(TARGET_DIR, _dir, file) dst = join(TARGET_DIR, path_prefix + file.replace('.md', '.html'))
else:
dst = join(TARGET_DIR, path_prefix + file)
ctx = generate_page_context(path, dst, navigation) ctx = generate_page_context(path, dst, navigation)
_template = template _template = template
@ -108,22 +112,27 @@ def generate_navigation():
for entry in PAGES: for entry in PAGES:
_dir = entry[0] _dir = entry[0]
navigation += '<div class="navigation">\n' navigation += '<div class="navigation">\n'
navigation += '<h3><strong>%s</strong></h3>\n' % (_dir) navigation += '<h3><strong>%s</strong></h3>\n' % (_dir.replace('-', ' ').title())
navigation += '<ul class="menu">\n' navigation += '<ul class="menu">\n'
for file in entry[1]: for file in entry[1]:
ext = get_validated_ext(file) ext = get_validated_ext(file)
link = '' ## Assuming that file name don't contain '.md' at the middle. link = '' ## Assuming that file name don't contain '.md' at the middle.
if ext == '.md': link = join(ROOT_URL, _dir, file.replace('.md', '.html'))
else: link = join(ROOT_URL, _dir, file) path_prefix = _dir.lower().replace(' ', '-') + '-'
if ext == '.md':
link = join(ROOT_URL, path_prefix + file.replace('.md', '.html'))
else:
link = join(ROOT_URL, path_prefix + file)
link = link.replace('\\', '/') link = link.replace('\\', '/')
title = file.replace(ext, '') title = file.replace(ext, '').replace('-', ' ').title()
navigation += '<li><a href="%s">%s</a></li>\n' % (link, title) navigation += '<li><a href="%s">%s</a></li>\n' % (link, title)
navigation += '</ul>\n' navigation += '</ul>\n'
navigation += '</div>\n' navigation += '</div>\n'
return navigation return navigation
@ -155,6 +164,9 @@ def path_to_title(path):
## Return the static dir relative path. ## Return the static dir relative path.
def relative_static_dir(dst): def relative_static_dir(dst):
return STATIC_DIR ## No more relative paths.
_dir = os.path.dirname(dst) _dir = os.path.dirname(dst)
static_dir = os.path.relpath(join(TARGET_DIR, STATIC_DIR), _dir) static_dir = os.path.relpath(join(TARGET_DIR, STATIC_DIR), _dir)
static_dir = static_dir.replace('\\', '/') static_dir = static_dir.replace('\\', '/')
@ -203,8 +215,7 @@ def custom_html_override(src, content):
'<span class="n">%s</span>' % nk) '<span class="n">%s</span>' % nk)
## codehilite mark the compilation command as error. ## codehilite mark the compilation command as error.
if 'build from source' in src: content = content.replace('<span class="err">', '<span>')
content = content.replace('<span class="err">', '<span>')
return content return content
@ -227,8 +238,10 @@ if __name__ == '__main__':
_local = False _local = False
if len(sys.argv) >= 2: if len(sys.argv) >= 2:
if sys.argv[1] == 'local': if sys.argv[1] == 'local':
ROOT_URL = 'http://localhost:8000/'
_local = True _local = True
#ROOT_URL = 'http://localhost:8000/'
ROOT_URL = '' ## No more nested directory pages.
main() main()

View File

@ -0,0 +1,83 @@
# Learn pocketlang in 5 minutes
```ruby
# This is a comment.
x = 0 # Creating a variable.
# The value '0' is a type of number.
# In pocketlang statements should end with a new line
# or a semicollon. White space characters except for new
# lines are ignored in pocketlang.
a = 1; b = 2;
# Data types.
# -----------
# Primitive types
null # A null type.
true; false # Booleans.
42; 3.14 # Numbers.
0..10; 10..0 # Range (0..10 = 0 <= r < 10).
# Object types
"hello"; 'world' # Strings (support multiline).
[42, 'foo', null] # Lists.
{ 'Key':'value' } # Maps.
func(x) return x*x end # Lambda/literal functions.
import lang # Module (imported scripts).
# Control flow.
# -------------
# If condition.
if x == 'foo'
print('bar')
elif x == 'bar'
print('baz')
end
# In a single line (should add 'then' keyword).
if x == 'foo' then print('bar') end
# For loops, here 'do' keyword is optional if we have a
# newline after the sequence (like 'then' in if statements).
for i in 0..10 do
print(i)
end
# While statement.
while x > 0 do print(x -= 1) end
# In pocketlang variable's lifetime are scope based.
if true then
local = null
end
#print(local) # Error: Name 'local' is not defined.
# Functions.
#-----------
def add(a, b)
return a + b
end
# Functions can be assigned to a variable.
fn = func(x) return x*x end
# Functions can be passed as an argument and can be returned.
def call(fn, x)
fn(x)
return func print('foo') end
end
# Concanative call operator '->'
str_lower(str_strip('FOO ')) # This can be written as below
'FOO ' -> str_strip -> str_lower
'foo' -> print # similer as print('foo')
```

View File

@ -365,6 +365,7 @@ static OpInfo opcode_info[] = {
* ERROR HANDLERS * * ERROR HANDLERS *
*****************************************************************************/ *****************************************************************************/
// Internal error report function of the parseError() function.
static void reportError(Compiler* compiler, const char* file, int line, static void reportError(Compiler* compiler, const char* file, int line,
const char* fmt, va_list args) { const char* fmt, va_list args) {
PKVM* vm = compiler->vm; PKVM* vm = compiler->vm;

View File

@ -17,16 +17,22 @@
/* PUBLIC API */ /* PUBLIC API */
/*****************************************************************************/ /*****************************************************************************/
// Declare internal functions of public api. // Create a new module with the given [name] and returns as a Script* for
// internal. Which will be wrapped by pkNewModule to return a pkHandle*.
static Script* newModuleInternal(PKVM* vm, const char* name); static Script* newModuleInternal(PKVM* vm, const char* name);
static void moduleAddFunctionInternal(PKVM* vm, Script* script,
const char* name, pkNativeFn fptr, int arity);
// The internal function to add functions to a module.
static void moduleAddFunctionInternal(PKVM* vm, Script* script,
const char* name, pkNativeFn fptr,
int arity);
// pkNewModule implementation (see pocketlang.h for description).
PkHandle* pkNewModule(PKVM* vm, const char* name) { PkHandle* pkNewModule(PKVM* vm, const char* name) {
Script* module = newModuleInternal(vm, name); Script* module = newModuleInternal(vm, name);
return vmNewHandle(vm, VAR_OBJ(&module->_super)); return vmNewHandle(vm, VAR_OBJ(&module->_super));
} }
// pkModuleAddFunction implementation (see pocketlang.h for description).
void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name, void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
pkNativeFn fptr, int arity) { pkNativeFn fptr, int arity) {
__ASSERT(module != NULL, "Argument module was NULL."); __ASSERT(module != NULL, "Argument module was NULL.");
@ -38,18 +44,18 @@ void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
moduleAddFunctionInternal(vm, (Script*)AS_OBJ(scr), name, fptr, arity); moduleAddFunctionInternal(vm, (Script*)AS_OBJ(scr), name, fptr, arity);
} }
// Argument getter (1 based). // A convinent macro to get the nth (1 based) argument of the current function.
#define ARG(n) vm->fiber->ret[n] #define ARG(n) vm->fiber->ret[n]
// Convinent macros. // Convinent macros to get the 1st, 2nd, 3rd arguments.
#define ARG1 ARG(1) #define ARG1 ARG(1)
#define ARG2 ARG(2) #define ARG2 ARG(2)
#define ARG3 ARG(3) #define ARG3 ARG(3)
// Argument count used in variadic functions. // Evaluvates to the current function's argument count.
#define ARGC ((int)(vm->fiber->sp - vm->fiber->ret) - 1) #define ARGC ((int)(vm->fiber->sp - vm->fiber->ret) - 1)
// Set return value and return. // Set return value for the current native function and return.
#define RET(value) \ #define RET(value) \
do { \ do { \
*(vm->fiber->ret) = value; \ *(vm->fiber->ret) = value; \
@ -63,6 +69,7 @@ void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
__ASSERT(value != NULL, "Parameter [value] was NULL."); \ __ASSERT(value != NULL, "Parameter [value] was NULL."); \
((void*)0) ((void*)0)
// Set error for incompatible type provided as an argument.
#define ERR_INVALID_ARG_TYPE(m_type) \ #define ERR_INVALID_ARG_TYPE(m_type) \
do { \ do { \
char buff[STR_INT_BUFF_SIZE]; \ char buff[STR_INT_BUFF_SIZE]; \
@ -71,11 +78,13 @@ do { \
" at argument $.", buff); \ " at argument $.", buff); \
} while (false) } while (false)
// pkGetArgc implementation (see pocketlang.h for description).
int pkGetArgc(PKVM* vm) { int pkGetArgc(PKVM* vm) {
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime."); __ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
return ARGC; return ARGC;
} }
// pkGetArg implementation (see pocketlang.h for description).
PkVar pkGetArg(PKVM* vm, int arg) { PkVar pkGetArg(PKVM* vm, int arg) {
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime."); __ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
__ASSERT(arg > 0 || arg <= ARGC, "Invalid argument index."); __ASSERT(arg > 0 || arg <= ARGC, "Invalid argument index.");
@ -83,6 +92,7 @@ PkVar pkGetArg(PKVM* vm, int arg) {
return &(ARG(arg)); return &(ARG(arg));
} }
// pkGetArgBool implementation (see pocketlang.h for description).
bool pkGetArgBool(PKVM* vm, int arg, bool* value) { bool pkGetArgBool(PKVM* vm, int arg, bool* value) {
CHECK_GET_ARG_API_ERRORS(); CHECK_GET_ARG_API_ERRORS();
@ -91,6 +101,7 @@ bool pkGetArgBool(PKVM* vm, int arg, bool* value) {
return true; return true;
} }
// pkGetArgNumber implementation (see pocketlang.h for description).
bool pkGetArgNumber(PKVM* vm, int arg, double* value) { bool pkGetArgNumber(PKVM* vm, int arg, double* value) {
CHECK_GET_ARG_API_ERRORS(); CHECK_GET_ARG_API_ERRORS();
@ -109,6 +120,7 @@ bool pkGetArgNumber(PKVM* vm, int arg, double* value) {
return true; return true;
} }
// pkGetArgString implementation (see pocketlang.h for description).
bool pkGetArgString(PKVM* vm, int arg, const char** value) { bool pkGetArgString(PKVM* vm, int arg, const char** value) {
CHECK_GET_ARG_API_ERRORS(); CHECK_GET_ARG_API_ERRORS();
@ -124,6 +136,7 @@ bool pkGetArgString(PKVM* vm, int arg, const char** value) {
return true; return true;
} }
// pkGetArgValue implementation (see pocketlang.h for description).
bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) { bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) {
CHECK_GET_ARG_API_ERRORS(); CHECK_GET_ARG_API_ERRORS();
@ -139,54 +152,36 @@ bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) {
return true; return true;
} }
// pkReturnNull implementation (see pocketlang.h for description).
void pkReturnNull(PKVM* vm) { void pkReturnNull(PKVM* vm) {
RET(VAR_NULL); RET(VAR_NULL);
} }
// pkReturnBool implementation (see pocketlang.h for description).
void pkReturnBool(PKVM* vm, bool value) { void pkReturnBool(PKVM* vm, bool value) {
RET(VAR_BOOL(value)); RET(VAR_BOOL(value));
} }
// pkReturnNumber implementation (see pocketlang.h for description).
void pkReturnNumber(PKVM* vm, double value) { void pkReturnNumber(PKVM* vm, double value) {
RET(VAR_NUM(value)); RET(VAR_NUM(value));
} }
// pkReturnString implementation (see pocketlang.h for description).
void pkReturnString(PKVM* vm, const char* value) { void pkReturnString(PKVM* vm, const char* value) {
RET(VAR_OBJ(&newString(vm, value)->_super)); RET(VAR_OBJ(&newString(vm, value)->_super));
} }
// pkReturnStringLength implementation (see pocketlang.h for description).
void pkReturnStringLength(PKVM* vm, const char* value, size_t length) { void pkReturnStringLength(PKVM* vm, const char* value, size_t length) {
RET(VAR_OBJ(&newStringLength(vm, value, (uint32_t)length)->_super)); RET(VAR_OBJ(&newStringLength(vm, value, (uint32_t)length)->_super));
} }
// pkReturnValue implementation (see pocketlang.h for description).
void pkReturnValue(PKVM* vm, PkVar value) { void pkReturnValue(PKVM* vm, PkVar value) {
RET(*(Var*)value); RET(*(Var*)value);
} }
/*****************************************************************************/
/* CORE INTERNAL */
/*****************************************************************************/
static void initializeBuiltinFN(PKVM* vm, BuiltinFn* bfn, const char* name,
int length, int arity, pkNativeFn ptr) {
bfn->name = name;
bfn->length = length;
bfn->fn = newFunction(vm, name, length, NULL, true);
bfn->fn->arity = arity;
bfn->fn->native = ptr;
}
int findBuiltinFunction(PKVM* vm, const char* name, uint32_t length) {
for (int i = 0; i < vm->builtins_count; i++) {
if (length == vm->builtins[i].length &&
strncmp(name, vm->builtins[i].name, length) == 0) {
return i;
}
}
return -1;
}
/*****************************************************************************/ /*****************************************************************************/
/* VALIDATORS */ /* VALIDATORS */
/*****************************************************************************/ /*****************************************************************************/
@ -259,16 +254,30 @@ static inline bool validateIndex(PKVM* vm, int32_t index, int32_t size,
/* BUILTIN FUNCTIONS API */ /* BUILTIN FUNCTIONS API */
/*****************************************************************************/ /*****************************************************************************/
// findBuiltinFunction implementation (see core.h for description).
int findBuiltinFunction(PKVM* vm, const char* name, uint32_t length) {
for (int i = 0; i < vm->builtins_count; i++) {
if (length == vm->builtins[i].length &&
strncmp(name, vm->builtins[i].name, length) == 0) {
return i;
}
}
return -1;
}
// getBuiltinFunction implementation (see core.h for description).
Function* getBuiltinFunction(PKVM* vm, int index) { Function* getBuiltinFunction(PKVM* vm, int index) {
ASSERT_INDEX(index, vm->builtins_count); ASSERT_INDEX(index, vm->builtins_count);
return vm->builtins[index].fn; return vm->builtins[index].fn;
} }
// getBuiltinFunctionName implementation (see core.h for description).
const char* getBuiltinFunctionName(PKVM* vm, int index) { const char* getBuiltinFunctionName(PKVM* vm, int index) {
ASSERT_INDEX(index, vm->builtins_count); ASSERT_INDEX(index, vm->builtins_count);
return vm->builtins[index].name; return vm->builtins[index].name;
} }
// getCoreLib implementation (see core.h for description).
Script* getCoreLib(PKVM* vm, String* name) { Script* getCoreLib(PKVM* vm, String* name) {
Var lib = mapGet(vm->core_libs, VAR_OBJ(&name->_super)); Var lib = mapGet(vm->core_libs, VAR_OBJ(&name->_super));
if (IS_UNDEF(lib)) return NULL; if (IS_UNDEF(lib)) return NULL;
@ -309,16 +318,14 @@ FN_IS_OBJ_TYPE(UserObj, OBJ_USER)
PK_DOC(coreTypeName, PK_DOC(coreTypeName,
"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.") {
void coreTypeName(PKVM* vm) {
RET(VAR_OBJ(&newString(vm, varTypeName(ARG1))->_super)); RET(VAR_OBJ(&newString(vm, varTypeName(ARG1))->_super));
} }
PK_DOC(coreAssert, PK_DOC(coreAssert,
"assert(condition:bool [, msg:string]) -> void\n" "assert(condition:bool [, msg:string]) -> void\n"
"If the condition is false it'll terminate the current fiber with the " "If the condition is false it'll terminate the current fiber with the "
"optional error message"); "optional error message") {
void coreAssert(PKVM* vm) {
int argc = ARGC; int argc = ARGC;
if (argc != 1 && argc != 2) { if (argc != 1 && argc != 2) {
vm->fiber->error = newString(vm, "Invalid argument count."); vm->fiber->error = newString(vm, "Invalid argument count.");
@ -345,16 +352,14 @@ void coreAssert(PKVM* vm) {
PK_DOC(coreToString, PK_DOC(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.") {
void coreToString(PKVM* vm) {
RET(VAR_OBJ(&toString(vm, ARG1)->_super)); RET(VAR_OBJ(&toString(vm, ARG1)->_super));
} }
PK_DOC(corePrint, PK_DOC(corePrint,
"print(...) -> void\n" "print(...) -> void\n"
"Write each argument as comma seperated to the stdout and ends with a " "Write each argument as comma seperated to the stdout and ends with a "
"newline."); "newline.") {
void corePrint(PKVM* vm) {
// If the host appliaction donesn't provide any write function, discard the // If the host appliaction donesn't provide any write function, discard the
// output. // output.
if (vm->config.write_fn == NULL) return; if (vm->config.write_fn == NULL) return;
@ -379,8 +384,9 @@ void corePrint(PKVM* vm) {
// String functions. // String functions.
// ----------------- // -----------------
PK_DOC(coreStrLower,
void coreStrLower(PKVM* vm) { "str_lower(value:string) -> string\n"
"Returns a lower-case version of the given string.") {
String* str; String* str;
if (!validateArgString(vm, 1, &str)) return; if (!validateArgString(vm, 1, &str)) return;
@ -393,7 +399,9 @@ void coreStrLower(PKVM* vm) {
RET(VAR_OBJ(&result->_super)); RET(VAR_OBJ(&result->_super));
} }
void coreStrUpper(PKVM* vm) { PK_DOC(coreStrUpper,
"str_upper(value:string) -> string\n"
"Returns a upper-case version of the given string.") {
String* str; String* str;
if (!validateArgString(vm, 1, &str)) return; if (!validateArgString(vm, 1, &str)) return;
@ -406,7 +414,10 @@ void coreStrUpper(PKVM* vm) {
RET(VAR_OBJ(&result->_super)); RET(VAR_OBJ(&result->_super));
} }
void coreStrStrip(PKVM* vm) { PK_DOC(coreStrStrip,
"str_strip(value:string) -> string\n"
"Returns a copy of the string as the leading and trailing white spaces are"
"trimed.") {
String* str; String* str;
if (!validateArgString(vm, 1, &str)) return; if (!validateArgString(vm, 1, &str)) return;
@ -420,19 +431,21 @@ void coreStrStrip(PKVM* vm) {
RET(VAR_OBJ(&newStringLength(vm, start, (uint32_t)(end - start + 1))->_super)); RET(VAR_OBJ(&newStringLength(vm, start, (uint32_t)(end - start + 1))->_super));
} }
// Returns the ASCII string value of the integer argument. PK_DOC(coreStrChr,
void coreStrChr(PKVM* vm) { "str_chr(value:number) -> string\n"
"Returns the ASCII string value of the integer argument.") {
int32_t num; int32_t num;
if (!validateInteger(vm, ARG1, &num, "Argument 1")); if (!validateInteger(vm, ARG1, &num, "Argument 1")) return;
char c = (char)num; char c = (char)num;
RET(VAR_OBJ(&newStringLength(vm, &c, 1)->_super)); RET(VAR_OBJ(&newStringLength(vm, &c, 1)->_super));
} }
// Returns integer value of the given ASCII character. PK_DOC(coreStrOrd,
void coreStrOrd(PKVM* vm) { "str_ord(value:string) -> number\n"
"Returns integer value of the given ASCII character.") {
String* c; String* c;
if (!validateArgString(vm, 1, &c)); if (!validateArgString(vm, 1, &c)) return;
if (c->length != 1) { if (c->length != 1) {
vm->fiber->error = newString(vm, "Expected a string of length 1."); vm->fiber->error = newString(vm, "Expected a string of length 1.");
RET(VAR_NULL); RET(VAR_NULL);
@ -442,10 +455,12 @@ void coreStrOrd(PKVM* vm) {
} }
// List functions. // List functions.
// --------------- // ---------------
void coreListAppend(PKVM* vm) {
PK_DOC(coreListAppend,
"list_append(self:List, value:var) -> List\n"
"Append the [value] to the list [self] and return the list.") {
List* list; List* list;
if (!validateArgList(vm, 1, &list)) return; if (!validateArgList(vm, 1, &list)) return;
Var elem = ARG(2); Var elem = ARG(2);
@ -457,13 +472,15 @@ void coreListAppend(PKVM* vm) {
// Map functions. // Map functions.
// -------------- // --------------
void coreMapRemove(PKVM* vm) { PK_DOC(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.") {
Map* map; Map* map;
if (!validateArgMap(vm, 1, &map)) return; if (!validateArgMap(vm, 1, &map)) return;
Var key = ARG(2); Var key = ARG(2);
mapRemoveKey(vm, map, key); RET(mapRemoveKey(vm, map, key));
RET(VAR_OBJ(&map->_super));
} }
/*****************************************************************************/ /*****************************************************************************/
@ -497,8 +514,10 @@ static Script* newModuleInternal(PKVM* vm, const char* name) {
return scr; return scr;
} }
// 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, int arity) { const char* name, pkNativeFn fptr,
int arity) {
// Check if function with the same name already exists. // Check if function with the same name already exists.
if (scriptSearchFunc(script, name, (uint32_t)strlen(name)) != -1) { if (scriptSearchFunc(script, name, (uint32_t)strlen(name)) != -1) {
@ -624,6 +643,17 @@ void stdMathHash(PKVM* vm) {
/*****************************************************************************/ /*****************************************************************************/
/* CORE INITIALIZATION */ /* CORE INITIALIZATION */
/*****************************************************************************/ /*****************************************************************************/
static void initializeBuiltinFN(PKVM* vm, BuiltinFn* bfn, const char* name,
int length, int arity, pkNativeFn ptr) {
bfn->name = name;
bfn->length = length;
bfn->fn = newFunction(vm, name, length, NULL, true);
bfn->fn->arity = arity;
bfn->fn->native = ptr;
}
void initializeCore(PKVM* vm) { void initializeCore(PKVM* vm) {
#define INITALIZE_BUILTIN_FN(name, fn, argc) \ #define INITALIZE_BUILTIN_FN(name, fn, argc) \
@ -817,6 +847,7 @@ bool varLesser(Var v1, Var v2) {
#define IS_ATTRIB(name) \ #define IS_ATTRIB(name) \
(attrib->length == strlen(name) && strcmp(name, attrib->data) == 0) (attrib->length == strlen(name) && strcmp(name, attrib->data) == 0)
// Set error for accessing non-existed attribute.
#define ERR_NO_ATTRIB() \ #define ERR_NO_ATTRIB() \
vm->fiber->error = stringFormat(vm, "'$' objects has no attribute " \ vm->fiber->error = stringFormat(vm, "'$' objects has no attribute " \
"named '$'", \ "named '$'", \

View File

@ -30,19 +30,25 @@ Script* getCoreLib(PKVM* vm, String* name);
/* OPERATORS */ /* OPERATORS */
/*****************************************************************************/ /*****************************************************************************/
Var varAdd(PKVM* vm, Var v1, Var v2); Var varAdd(PKVM* vm, Var v1, Var v2); // Returns v1 + v2.
Var varSubtract(PKVM* vm, Var v1, Var v2); Var varSubtract(PKVM* vm, Var v1, Var v2); // Returns v1 - v2.
Var varMultiply(PKVM* vm, Var v1, Var v2); Var varMultiply(PKVM* vm, Var v1, Var v2); // Returns v1 * v2.
Var varDivide(PKVM* vm, Var v1, Var v2); Var varDivide(PKVM* vm, Var v1, Var v2); // Returns v1 / v2.
Var varModulo(PKVM* vm, Var v1, Var v2); Var varModulo(PKVM* vm, Var v1, Var v2); // Returns v1 % v2.
bool varGreater(Var v1, Var v2); bool varGreater(Var v1, Var v2); // Returns v1 > v2.
bool varLesser(Var v1, Var v2); bool varLesser(Var v1, Var v2); // Returns v1 < v2.
// Returns the attribute named [attrib] on the variable [on].
Var varGetAttrib(PKVM* vm, Var on, String* attrib); Var varGetAttrib(PKVM* vm, Var on, String* attrib);
// Set the attribute named [attrib] on the variable [on] with the given [value].
void varSetAttrib(PKVM* vm, Var on, String* name, Var value); void varSetAttrib(PKVM* vm, Var on, String* name, Var value);
// Returns the subscript value (ie. on[key]).
Var varGetSubscript(PKVM* vm, Var on, Var key); Var varGetSubscript(PKVM* vm, Var on, Var key);
// Set subscript [value] with the [key] (ie. on[key] = value).
void varsetSubscript(PKVM* vm, Var on, Var key, Var value); void varsetSubscript(PKVM* vm, Var on, Var key, Var value);
#endif // CORE_H #endif // CORE_H

View File

@ -57,10 +57,13 @@ extern "C" {
// A convinent macro to define documentation of funcions. Use it to document // A convinent macro to define documentation of funcions. Use it to document
// your native functions. // your native functions.
// //
// PK_DOC(foo, "The function will print 'foo' on the console."); // PK_DOC(foo,
// void foo(PKVM* vm) { printf("foo\n"); } // "The function will print 'foo' on the console.") {
// printf("foo\n");
// }
// //
#define PK_DOC(func, doc) static char __pkdoc__##func[] = doc #define PK_DOC(func, doc) \
static char __pkdoc__##func[] = doc; void func(PKVM* vm)
/*****************************************************************************/ /*****************************************************************************/
/* POCKETLANG TYPES */ /* POCKETLANG TYPES */

View File

@ -5,6 +5,7 @@
#include "utils.h" #include "utils.h"
// Function implementation, see utils.h for description.
int utilPowerOf2Ceil(int n) { int utilPowerOf2Ceil(int n) {
n--; n--;
n |= n >> 1; n |= n >> 1;
@ -17,10 +18,12 @@ int utilPowerOf2Ceil(int n) {
return n; return n;
} }
// Function implementation, see utils.h for description.
bool utilIsName(char c) { bool utilIsName(char c) {
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || (c == '_'); return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || (c == '_');
} }
// Function implementation, see utils.h for description.
bool utilIsDigit(char c) { bool utilIsDigit(char c) {
return ('0' <= c && c <= '9'); return ('0' <= c && c <= '9');
} }
@ -32,18 +35,21 @@ typedef union {
double num; double num;
} _DoubleBitsConv; } _DoubleBitsConv;
// Function implementation, see utils.h for description.
uint64_t utilDoubleToBits(double value) { uint64_t utilDoubleToBits(double value) {
_DoubleBitsConv bits; _DoubleBitsConv bits;
bits.num = value; bits.num = value;
return bits.bits64; return bits.bits64;
} }
// Function implementation, see utils.h for description.
double utilDoubleFromBits(uint64_t value) { double utilDoubleFromBits(uint64_t value) {
_DoubleBitsConv bits; _DoubleBitsConv bits;
bits.bits64 = value; bits.bits64 = value;
return bits.num; return bits.num;
} }
// Function implementation, see utils.h for description.
uint32_t utilHashBits(uint64_t hash) { uint32_t utilHashBits(uint64_t hash) {
// From v8's ComputeLongHash() which in turn cites: // From v8's ComputeLongHash() which in turn cites:
// Thomas Wang, Integer Hash Functions. // Thomas Wang, Integer Hash Functions.
@ -57,11 +63,13 @@ uint32_t utilHashBits(uint64_t hash) {
return (uint32_t)(hash & 0x3fffffff); return (uint32_t)(hash & 0x3fffffff);
} }
// Function implementation, see utils.h for description.
uint32_t utilHashNumber(double num) { uint32_t utilHashNumber(double num) {
// Hash the raw bits of the value. // Hash the raw bits of the value.
return utilHashBits(utilDoubleToBits(num)); return utilHashBits(utilDoubleToBits(num));
} }
// Function implementation, see utils.h for description.
uint32_t utilHashString(const char* string) { uint32_t utilHashString(const char* string) {
// FNV-1a hash. See: http://www.isthe.com/chongo/tech/comp/fnv/ // FNV-1a hash. See: http://www.isthe.com/chongo/tech/comp/fnv/
@ -85,6 +93,7 @@ uint32_t utilHashString(const char* string) {
* UTF8 * * UTF8 *
****************************************************************************/ ****************************************************************************/
// Function implementation, see utils.h for description.
int utf8_encodeBytesCount(int value) { int utf8_encodeBytesCount(int value) {
if (value <= 0x7f) return 1; if (value <= 0x7f) return 1;
if (value <= 0x7ff) return 2; if (value <= 0x7ff) return 2;
@ -95,6 +104,7 @@ int utf8_encodeBytesCount(int value) {
return 0; return 0;
} }
// Function implementation, see utils.h for description.
int utf8_decodeBytesCount(uint8_t byte) { int utf8_decodeBytesCount(uint8_t byte) {
if ((byte >> 7) == 0b0) return 1; if ((byte >> 7) == 0b0) return 1;
@ -107,6 +117,7 @@ int utf8_decodeBytesCount(uint8_t byte) {
return 1; return 1;
} }
// Function implementation, see utils.h for description.
int utf8_encodeValue(int value, uint8_t* bytes) { int utf8_encodeValue(int value, uint8_t* bytes) {
if (value <= 0x7f) { if (value <= 0x7f) {
@ -145,6 +156,7 @@ int utf8_encodeValue(int value, uint8_t* bytes) {
return 0; return 0;
} }
// Function implementation, see utils.h for description.
int utf8_decodeBytes(uint8_t* bytes, int* value) { int utf8_decodeBytes(uint8_t* bytes, int* value) {
int continue_bytes = 0; int continue_bytes = 0;

View File

@ -32,6 +32,7 @@ PkVarType pkGetValueType(PkVar value) {
case OBJ_SCRIPT: return PK_SCRIPT; case OBJ_SCRIPT: return PK_SCRIPT;
case OBJ_FUNC: return PK_FUNCTION; case OBJ_FUNC: return PK_FUNCTION;
case OBJ_FIBER: return PK_FIBER; case OBJ_FIBER: return PK_FIBER;
case OBJ_USER: TODO; break;
} }
UNREACHABLE(); UNREACHABLE();

View File

@ -23,16 +23,13 @@ triangle = ">++++[<++++++++>-]>++++++++[>++++<-]>>++>>>+>>>+<<<<<<<<<<[-[->+<]>
[-]<]+++++" [-]<]+++++"
## Source: https://github.com/fabianishere/brainfuck/blob/master/examples/math/fib.bf ## Source: https://github.com/fabianishere/brainfuck/blob/master/examples/math/fib.bf
fibonacci = ">++++++++++>+>+[ fibonacci = ">++++++++++>+>+[[+++++[>++++++++<-]>.<++++++[>--------<-]+<<<]>.
[+++++[>++++++++<-]>.<++++++[>--------<-]+<<<]>.>>[ >>[[-]<[>+<-]>>[<<+>+>-]<[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+
[-]<[>+<-]>>[<<+>+>-]<[>+<-[>+<-[>+<-[>+<-[>+<-[>+<- <-[>+<-[>[-]>+>+<<<-[>+<-]]]]]]]]]]]+>>>]<<<]"
[>+<-[>+<-[>+<-[>[-]>+>+<<<-[>+<-]]]]]]]]]]]+>>>
]<<<
]"
execute(hello_world) execute(hello_world)
execute(triangle) execute(triangle)
execute(fibonacci) ##execute(fibonacci) This will run endlessly (cannot run test).
############################################################################### ###############################################################################
## INTERNAL ## ## INTERNAL ##

View File

@ -2,17 +2,22 @@ import subprocess, os, sys
import json, re import json, re
from os.path import join from os.path import join
FMT_PATH = "%-25s" ## All the test files.
INDENTATION = ' | '
test_files = [ test_files = [
"lang/basics.pk", "lang/basics.pk",
"lang/functions.pk", "lang/functions.pk",
"lang/controlflow.pk", "lang/controlflow.pk",
"lang/import.pk",
"examples/helloworld.pk",
"examples/brainfuck.pk",
"examples/fib.pk", "examples/fib.pk",
"examples/prime.pk", "examples/prime.pk",
"examples/fizzbuzz.pk",
"examples/pi.pk",
] ]
## All benchmark files. ## TODO: pass args for iterations.
benchmarks = { benchmarks = {
"factors" : ['.pk', '.py', '.rb', '.wren'], "factors" : ['.pk', '.py', '.rb', '.wren'],
"fib" : ['.pk', '.py', '.rb', '.wren'], "fib" : ['.pk', '.py', '.rb', '.wren'],
@ -21,10 +26,14 @@ benchmarks = {
"primes" : ['.pk', '.py', '.rb', ".wren"], "primes" : ['.pk', '.py', '.rb', ".wren"],
} }
def main():
run_all_tests()
run_all_benchmarks()
def run_all_benchmarks(): def run_all_benchmarks():
print("--------------------------------") print_title("BENCHMARKS")
print(" BENCHMARKS ")
print("--------------------------------")
def get_interpreter(file): def get_interpreter(file):
if file.endswith('.pk' ) : return 'pocket' if file.endswith('.pk' ) : return 'pocket'
@ -47,23 +56,21 @@ def run_all_benchmarks():
print('%10ss'%time[0]) print('%10ss'%time[0])
def run_all_tests(): def run_all_tests():
print("--------------------------------") print_title("TESTS")
print(" TESTS ")
print("--------------------------------")
for path in test_files:
run_file(path)
def run_file(path): FMT_PATH = "%-25s"
print(FMT_PATH % path, end='') INDENTATION = ' | '
result = run_command(['pocket', path]) for path in test_files:
if result.returncode != 0: print(FMT_PATH % path, end='')
print('-- Failed') result = run_command(['pocket', path])
err = INDENTATION + result.stderr \ if result.returncode != 0:
.decode('utf8') \ print('-- Failed')
.replace('\n', '\n' + INDENTATION) err = INDENTATION + result.stderr \
print(err) .decode('utf8') \
else: .replace('\n', '\n' + INDENTATION)
print('-- OK') print(err)
else:
print('-- OK')
def run_command(command): def run_command(command):
@ -71,6 +78,10 @@ def run_command(command):
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE) stderr=subprocess.PIPE)
def print_title(title):
print("--------------------------------")
print(" %s " % title)
print("--------------------------------")
run_all_tests() if __name__ == '__main__':
run_all_benchmarks() main()