mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-05 20:26:53 +08:00
Merge pull request #38 from ThakeeNathees/more-docs
more comments written through out the source
This commit is contained in:
commit
6ee8226a50
@ -5,9 +5,10 @@
|
||||
|
||||
**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
|
||||
in less than an hour. Including the compiler, bytecode VM and runtime, it's a
|
||||
standalone executable with zero external dependecies just as it's self descriptive
|
||||
name. The pocketlang VM can be embedded in another hosting program very easily.
|
||||
in [less than an hour](https://thakeenathees.github.io/pocketlang/getting-started-learn-in-5-minutes.html).
|
||||
Including the compiler, bytecode VM and runtime, it's a standalone executable
|
||||
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
|
||||
wonderful book [craftinginterpreters](http://www.craftinginterpreters.com/) as
|
||||
|
34
cli/TODO.txt
34
cli/TODO.txt
@ -1,35 +1,35 @@
|
||||
|
||||
// To implement.
|
||||
|
||||
- Implement resolve path in cli.
|
||||
- Implement file IO
|
||||
|
||||
- Implement argparse.
|
||||
- -v --version
|
||||
- emit opcodes
|
||||
- maybe write a similer .pyc file for pocket
|
||||
- --docs to generate docs from cstring.
|
||||
|
||||
- Ignore line with '\' character.
|
||||
|
||||
- Structs.
|
||||
- Implement math library.
|
||||
- Single header for embedding.
|
||||
- --docs to generate docs from cstring.
|
||||
- dump compiled code to a file.
|
||||
- In keyword.
|
||||
- Structs (maybe enums).
|
||||
- Implement file IO (require structs).
|
||||
- Implement gdb like debugger (add color print for readability).
|
||||
- Implement fiber from script body and vm run fibers (not scripts).
|
||||
Then remove vm's root script.
|
||||
- C Integration api (including add core lib from host application).
|
||||
- REPL.
|
||||
- compile expression for a script.
|
||||
- Var handler implement.
|
||||
- Initialize imported scripts (require fiber based vm).
|
||||
- REPL. compile expression for a script.
|
||||
- Hex, binary literals and floats like ".5".
|
||||
- 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.
|
||||
- To do so the functions and global variables should be in the same
|
||||
buffer as the property of the script.
|
||||
- Hex, binary literals and floats like ".5".
|
||||
- Function docstring property.
|
||||
- Union tagging alter in var.
|
||||
- (future) add structs and maybe enums.
|
||||
- Github actions.
|
||||
|
||||
|
||||
// Add more.
|
||||
- Single header for embedding (script in pk, require file IO).
|
||||
[ ] Compilte core methods.
|
||||
[ ] Complete var methods.
|
||||
[ ] Complete core functions.
|
||||
|
@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
// 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
|
||||
// the list of source directory.
|
||||
|
||||
|
@ -18,7 +18,7 @@ ROOT_URL = 'https://thakeenathees.github.io/pocketlang/'
|
||||
|
||||
## Home page should be in the SOURCE_DIR.
|
||||
HOME_PAGE = 'home.md'
|
||||
TRY_PAGE = 'try it now.html'
|
||||
TRY_PAGE = 'try-it-now.html'
|
||||
SOURCE_DIR = 'pages/'
|
||||
TARGET_DIR = 'build/'
|
||||
STATIC_DIR = 'static/'
|
||||
@ -36,14 +36,16 @@ WASM_SOURCE_FILES = '''\
|
||||
'''
|
||||
|
||||
## Navigation pages in order. Should match the path names.
|
||||
## Any file/folder name shouldn't contain white space.
|
||||
PAGES = [
|
||||
('Getting Started', [
|
||||
('Getting-Started', [
|
||||
TRY_PAGE,
|
||||
'build from source.md',
|
||||
'learn-in-5-minutes.md',
|
||||
'build-from-source.md',
|
||||
'contributing.md',
|
||||
]),
|
||||
|
||||
('Language API', [
|
||||
('Language-API', [
|
||||
'variables.md',
|
||||
'functions.md',
|
||||
'modules.md',
|
||||
@ -85,15 +87,17 @@ def main():
|
||||
ctx = generate_page_context(join(SOURCE_DIR, HOME_PAGE), index_html, navigation)
|
||||
write_page(ctx, template, index_html)
|
||||
|
||||
for entry in PAGES:
|
||||
for entry in PAGES: ## entry = ('dirname', [files...])
|
||||
_dir = entry[0]
|
||||
for file in entry[1]:
|
||||
ext = get_validated_ext(file)
|
||||
path = join(SOURCE_DIR, _dir, file)
|
||||
|
||||
dst = ''
|
||||
if ext == '.md' : dst = join(TARGET_DIR, _dir, file.replace('.md', '.html'))
|
||||
else: dst = join(TARGET_DIR, _dir, file)
|
||||
dst = ''; path_prefix = _dir.lower().replace(' ', '-') + '-'
|
||||
if ext == '.md':
|
||||
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)
|
||||
|
||||
_template = template
|
||||
@ -108,22 +112,27 @@ def generate_navigation():
|
||||
for entry in PAGES:
|
||||
_dir = entry[0]
|
||||
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'
|
||||
|
||||
for file in entry[1]:
|
||||
ext = get_validated_ext(file)
|
||||
|
||||
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('\\', '/')
|
||||
|
||||
title = file.replace(ext, '')
|
||||
title = file.replace(ext, '').replace('-', ' ').title()
|
||||
navigation += '<li><a href="%s">%s</a></li>\n' % (link, title)
|
||||
|
||||
navigation += '</ul>\n'
|
||||
navigation += '</div>\n'
|
||||
|
||||
return navigation
|
||||
|
||||
|
||||
@ -155,6 +164,9 @@ def path_to_title(path):
|
||||
|
||||
## Return the static dir relative path.
|
||||
def relative_static_dir(dst):
|
||||
|
||||
return STATIC_DIR ## No more relative paths.
|
||||
|
||||
_dir = os.path.dirname(dst)
|
||||
static_dir = os.path.relpath(join(TARGET_DIR, STATIC_DIR), _dir)
|
||||
static_dir = static_dir.replace('\\', '/')
|
||||
@ -203,8 +215,7 @@ def custom_html_override(src, content):
|
||||
'<span class="n">%s</span>' % nk)
|
||||
|
||||
## 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
|
||||
|
||||
@ -227,9 +238,11 @@ if __name__ == '__main__':
|
||||
_local = False
|
||||
if len(sys.argv) >= 2:
|
||||
if sys.argv[1] == 'local':
|
||||
ROOT_URL = 'http://localhost:8000/'
|
||||
_local = True
|
||||
|
||||
#ROOT_URL = 'http://localhost:8000/'
|
||||
|
||||
ROOT_URL = '' ## No more nested directory pages.
|
||||
|
||||
main()
|
||||
|
||||
## Write a batch file to start the server in windows.
|
||||
|
83
docs/pages/Getting-Started/learn-in-5-minutes.md
Normal file
83
docs/pages/Getting-Started/learn-in-5-minutes.md
Normal 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')
|
||||
|
||||
```
|
@ -365,6 +365,7 @@ static OpInfo opcode_info[] = {
|
||||
* ERROR HANDLERS *
|
||||
*****************************************************************************/
|
||||
|
||||
// Internal error report function of the parseError() function.
|
||||
static void reportError(Compiler* compiler, const char* file, int line,
|
||||
const char* fmt, va_list args) {
|
||||
PKVM* vm = compiler->vm;
|
||||
|
141
src/core.c
141
src/core.c
@ -17,16 +17,22 @@
|
||||
/* 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 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) {
|
||||
Script* module = newModuleInternal(vm, name);
|
||||
return vmNewHandle(vm, VAR_OBJ(&module->_super));
|
||||
}
|
||||
|
||||
// pkModuleAddFunction implementation (see pocketlang.h for description).
|
||||
void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
|
||||
pkNativeFn fptr, int arity) {
|
||||
__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);
|
||||
}
|
||||
|
||||
// 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]
|
||||
|
||||
// Convinent macros.
|
||||
// Convinent macros to get the 1st, 2nd, 3rd arguments.
|
||||
#define ARG1 ARG(1)
|
||||
#define ARG2 ARG(2)
|
||||
#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)
|
||||
|
||||
// Set return value and return.
|
||||
// Set return value for the current native function and return.
|
||||
#define RET(value) \
|
||||
do { \
|
||||
*(vm->fiber->ret) = value; \
|
||||
@ -63,6 +69,7 @@ void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
|
||||
__ASSERT(value != NULL, "Parameter [value] was NULL."); \
|
||||
((void*)0)
|
||||
|
||||
// Set error for incompatible type provided as an argument.
|
||||
#define ERR_INVALID_ARG_TYPE(m_type) \
|
||||
do { \
|
||||
char buff[STR_INT_BUFF_SIZE]; \
|
||||
@ -71,11 +78,13 @@ do { \
|
||||
" at argument $.", buff); \
|
||||
} while (false)
|
||||
|
||||
// pkGetArgc implementation (see pocketlang.h for description).
|
||||
int pkGetArgc(PKVM* vm) {
|
||||
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
|
||||
return ARGC;
|
||||
}
|
||||
|
||||
// pkGetArg implementation (see pocketlang.h for description).
|
||||
PkVar pkGetArg(PKVM* vm, int arg) {
|
||||
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
|
||||
__ASSERT(arg > 0 || arg <= ARGC, "Invalid argument index.");
|
||||
@ -83,6 +92,7 @@ PkVar pkGetArg(PKVM* vm, int arg) {
|
||||
return &(ARG(arg));
|
||||
}
|
||||
|
||||
// pkGetArgBool implementation (see pocketlang.h for description).
|
||||
bool pkGetArgBool(PKVM* vm, int arg, bool* value) {
|
||||
CHECK_GET_ARG_API_ERRORS();
|
||||
|
||||
@ -91,6 +101,7 @@ bool pkGetArgBool(PKVM* vm, int arg, bool* value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// pkGetArgNumber implementation (see pocketlang.h for description).
|
||||
bool pkGetArgNumber(PKVM* vm, int arg, double* value) {
|
||||
CHECK_GET_ARG_API_ERRORS();
|
||||
|
||||
@ -109,6 +120,7 @@ bool pkGetArgNumber(PKVM* vm, int arg, double* value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// pkGetArgString implementation (see pocketlang.h for description).
|
||||
bool pkGetArgString(PKVM* vm, int arg, const char** value) {
|
||||
CHECK_GET_ARG_API_ERRORS();
|
||||
|
||||
@ -124,6 +136,7 @@ bool pkGetArgString(PKVM* vm, int arg, const char** value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// pkGetArgValue implementation (see pocketlang.h for description).
|
||||
bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) {
|
||||
CHECK_GET_ARG_API_ERRORS();
|
||||
|
||||
@ -139,54 +152,36 @@ bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// pkReturnNull implementation (see pocketlang.h for description).
|
||||
void pkReturnNull(PKVM* vm) {
|
||||
RET(VAR_NULL);
|
||||
}
|
||||
|
||||
// pkReturnBool implementation (see pocketlang.h for description).
|
||||
void pkReturnBool(PKVM* vm, bool value) {
|
||||
RET(VAR_BOOL(value));
|
||||
}
|
||||
|
||||
// pkReturnNumber implementation (see pocketlang.h for description).
|
||||
void pkReturnNumber(PKVM* vm, double value) {
|
||||
RET(VAR_NUM(value));
|
||||
}
|
||||
|
||||
// pkReturnString implementation (see pocketlang.h for description).
|
||||
void pkReturnString(PKVM* vm, const char* value) {
|
||||
RET(VAR_OBJ(&newString(vm, value)->_super));
|
||||
}
|
||||
|
||||
// pkReturnStringLength implementation (see pocketlang.h for description).
|
||||
void pkReturnStringLength(PKVM* vm, const char* value, size_t length) {
|
||||
RET(VAR_OBJ(&newStringLength(vm, value, (uint32_t)length)->_super));
|
||||
}
|
||||
|
||||
// pkReturnValue implementation (see pocketlang.h for description).
|
||||
void pkReturnValue(PKVM* vm, PkVar 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 */
|
||||
/*****************************************************************************/
|
||||
@ -259,16 +254,30 @@ static inline bool validateIndex(PKVM* vm, int32_t index, int32_t size,
|
||||
/* 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) {
|
||||
ASSERT_INDEX(index, vm->builtins_count);
|
||||
return vm->builtins[index].fn;
|
||||
}
|
||||
|
||||
// getBuiltinFunctionName implementation (see core.h for description).
|
||||
const char* getBuiltinFunctionName(PKVM* vm, int index) {
|
||||
ASSERT_INDEX(index, vm->builtins_count);
|
||||
return vm->builtins[index].name;
|
||||
}
|
||||
|
||||
// getCoreLib implementation (see core.h for description).
|
||||
Script* getCoreLib(PKVM* vm, String* name) {
|
||||
Var lib = mapGet(vm->core_libs, VAR_OBJ(&name->_super));
|
||||
if (IS_UNDEF(lib)) return NULL;
|
||||
@ -309,16 +318,14 @@ FN_IS_OBJ_TYPE(UserObj, OBJ_USER)
|
||||
|
||||
PK_DOC(coreTypeName,
|
||||
"type_name(value:var) -> string\n"
|
||||
"Returns the type name of the of the value.");
|
||||
void coreTypeName(PKVM* vm) {
|
||||
"Returns the type name of the of the value.") {
|
||||
RET(VAR_OBJ(&newString(vm, varTypeName(ARG1))->_super));
|
||||
}
|
||||
|
||||
PK_DOC(coreAssert,
|
||||
"assert(condition:bool [, msg:string]) -> void\n"
|
||||
"If the condition is false it'll terminate the current fiber with the "
|
||||
"optional error message");
|
||||
void coreAssert(PKVM* vm) {
|
||||
"optional error message") {
|
||||
int argc = ARGC;
|
||||
if (argc != 1 && argc != 2) {
|
||||
vm->fiber->error = newString(vm, "Invalid argument count.");
|
||||
@ -345,16 +352,14 @@ void coreAssert(PKVM* vm) {
|
||||
|
||||
PK_DOC(coreToString,
|
||||
"to_string(value:var) -> string\n"
|
||||
"Returns the string representation of the value.");
|
||||
void coreToString(PKVM* vm) {
|
||||
"Returns the string representation of the value.") {
|
||||
RET(VAR_OBJ(&toString(vm, ARG1)->_super));
|
||||
}
|
||||
|
||||
PK_DOC(corePrint,
|
||||
"print(...) -> void\n"
|
||||
"Write each argument as comma seperated to the stdout and ends with a "
|
||||
"newline.");
|
||||
void corePrint(PKVM* vm) {
|
||||
"newline.") {
|
||||
// If the host appliaction donesn't provide any write function, discard the
|
||||
// output.
|
||||
if (vm->config.write_fn == NULL) return;
|
||||
@ -379,8 +384,9 @@ void corePrint(PKVM* vm) {
|
||||
|
||||
// String functions.
|
||||
// -----------------
|
||||
|
||||
void coreStrLower(PKVM* vm) {
|
||||
PK_DOC(coreStrLower,
|
||||
"str_lower(value:string) -> string\n"
|
||||
"Returns a lower-case version of the given string.") {
|
||||
String* str;
|
||||
if (!validateArgString(vm, 1, &str)) return;
|
||||
|
||||
@ -393,7 +399,9 @@ void coreStrLower(PKVM* vm) {
|
||||
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;
|
||||
if (!validateArgString(vm, 1, &str)) return;
|
||||
|
||||
@ -406,7 +414,10 @@ void coreStrUpper(PKVM* vm) {
|
||||
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;
|
||||
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));
|
||||
}
|
||||
|
||||
// Returns the ASCII string value of the integer argument.
|
||||
void coreStrChr(PKVM* vm) {
|
||||
PK_DOC(coreStrChr,
|
||||
"str_chr(value:number) -> string\n"
|
||||
"Returns the ASCII string value of the integer argument.") {
|
||||
int32_t num;
|
||||
if (!validateInteger(vm, ARG1, &num, "Argument 1"));
|
||||
if (!validateInteger(vm, ARG1, &num, "Argument 1")) return;
|
||||
|
||||
char c = (char)num;
|
||||
RET(VAR_OBJ(&newStringLength(vm, &c, 1)->_super));
|
||||
}
|
||||
|
||||
// Returns integer value of the given ASCII character.
|
||||
void coreStrOrd(PKVM* vm) {
|
||||
PK_DOC(coreStrOrd,
|
||||
"str_ord(value:string) -> number\n"
|
||||
"Returns integer value of the given ASCII character.") {
|
||||
String* c;
|
||||
if (!validateArgString(vm, 1, &c));
|
||||
if (!validateArgString(vm, 1, &c)) return;
|
||||
if (c->length != 1) {
|
||||
vm->fiber->error = newString(vm, "Expected a string of length 1.");
|
||||
RET(VAR_NULL);
|
||||
@ -442,10 +455,12 @@ void coreStrOrd(PKVM* vm) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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;
|
||||
if (!validateArgList(vm, 1, &list)) return;
|
||||
Var elem = ARG(2);
|
||||
@ -457,13 +472,15 @@ void coreListAppend(PKVM* vm) {
|
||||
// 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;
|
||||
if (!validateArgMap(vm, 1, &map)) return;
|
||||
Var key = ARG(2);
|
||||
|
||||
mapRemoveKey(vm, map, key);
|
||||
RET(VAR_OBJ(&map->_super));
|
||||
RET(mapRemoveKey(vm, map, key));
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
@ -497,8 +514,10 @@ static Script* newModuleInternal(PKVM* vm, const char* name) {
|
||||
return scr;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
const char* name, pkNativeFn fptr,
|
||||
int arity) {
|
||||
|
||||
// Check if function with the same name already exists.
|
||||
if (scriptSearchFunc(script, name, (uint32_t)strlen(name)) != -1) {
|
||||
@ -624,6 +643,17 @@ void stdMathHash(PKVM* vm) {
|
||||
/*****************************************************************************/
|
||||
/* 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) {
|
||||
|
||||
#define INITALIZE_BUILTIN_FN(name, fn, argc) \
|
||||
@ -817,6 +847,7 @@ bool varLesser(Var v1, Var v2) {
|
||||
#define IS_ATTRIB(name) \
|
||||
(attrib->length == strlen(name) && strcmp(name, attrib->data) == 0)
|
||||
|
||||
// Set error for accessing non-existed attribute.
|
||||
#define ERR_NO_ATTRIB() \
|
||||
vm->fiber->error = stringFormat(vm, "'$' objects has no attribute " \
|
||||
"named '$'", \
|
||||
|
20
src/core.h
20
src/core.h
@ -30,19 +30,25 @@ Script* getCoreLib(PKVM* vm, String* name);
|
||||
/* OPERATORS */
|
||||
/*****************************************************************************/
|
||||
|
||||
Var varAdd(PKVM* vm, Var v1, Var v2);
|
||||
Var varSubtract(PKVM* vm, Var v1, Var v2);
|
||||
Var varMultiply(PKVM* vm, Var v1, Var v2);
|
||||
Var varDivide(PKVM* vm, Var v1, Var v2);
|
||||
Var varModulo(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); // Returns v1 - v2.
|
||||
Var varMultiply(PKVM* vm, Var v1, Var v2); // Returns v1 * v2.
|
||||
Var varDivide(PKVM* vm, Var v1, Var v2); // Returns v1 / v2.
|
||||
Var varModulo(PKVM* vm, Var v1, Var v2); // Returns v1 % v2.
|
||||
|
||||
bool varGreater(Var v1, Var v2);
|
||||
bool varLesser(Var v1, Var v2);
|
||||
bool varGreater(Var v1, Var v2); // Returns v1 > 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);
|
||||
|
||||
// Set the attribute named [attrib] on the variable [on] with the given [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);
|
||||
|
||||
// Set subscript [value] with the [key] (ie. on[key] = value).
|
||||
void varsetSubscript(PKVM* vm, Var on, Var key, Var value);
|
||||
|
||||
#endif // CORE_H
|
||||
|
@ -57,10 +57,13 @@ extern "C" {
|
||||
// A convinent macro to define documentation of funcions. Use it to document
|
||||
// your native functions.
|
||||
//
|
||||
// PK_DOC(foo, "The function will print 'foo' on the console.");
|
||||
// void foo(PKVM* vm) { printf("foo\n"); }
|
||||
// PK_DOC(foo,
|
||||
// "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 */
|
||||
|
12
src/utils.c
12
src/utils.c
@ -5,6 +5,7 @@
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
// Function implementation, see utils.h for description.
|
||||
int utilPowerOf2Ceil(int n) {
|
||||
n--;
|
||||
n |= n >> 1;
|
||||
@ -17,10 +18,12 @@ int utilPowerOf2Ceil(int n) {
|
||||
return n;
|
||||
}
|
||||
|
||||
// Function implementation, see utils.h for description.
|
||||
bool utilIsName(char c) {
|
||||
return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || (c == '_');
|
||||
}
|
||||
|
||||
// Function implementation, see utils.h for description.
|
||||
bool utilIsDigit(char c) {
|
||||
return ('0' <= c && c <= '9');
|
||||
}
|
||||
@ -32,18 +35,21 @@ typedef union {
|
||||
double num;
|
||||
} _DoubleBitsConv;
|
||||
|
||||
// Function implementation, see utils.h for description.
|
||||
uint64_t utilDoubleToBits(double value) {
|
||||
_DoubleBitsConv bits;
|
||||
bits.num = value;
|
||||
return bits.bits64;
|
||||
}
|
||||
|
||||
// Function implementation, see utils.h for description.
|
||||
double utilDoubleFromBits(uint64_t value) {
|
||||
_DoubleBitsConv bits;
|
||||
bits.bits64 = value;
|
||||
return bits.num;
|
||||
}
|
||||
|
||||
// Function implementation, see utils.h for description.
|
||||
uint32_t utilHashBits(uint64_t hash) {
|
||||
// From v8's ComputeLongHash() which in turn cites:
|
||||
// Thomas Wang, Integer Hash Functions.
|
||||
@ -57,11 +63,13 @@ uint32_t utilHashBits(uint64_t hash) {
|
||||
return (uint32_t)(hash & 0x3fffffff);
|
||||
}
|
||||
|
||||
// Function implementation, see utils.h for description.
|
||||
uint32_t utilHashNumber(double num) {
|
||||
// Hash the raw bits of the value.
|
||||
return utilHashBits(utilDoubleToBits(num));
|
||||
}
|
||||
|
||||
// Function implementation, see utils.h for description.
|
||||
uint32_t utilHashString(const char* string) {
|
||||
// FNV-1a hash. See: http://www.isthe.com/chongo/tech/comp/fnv/
|
||||
|
||||
@ -85,6 +93,7 @@ uint32_t utilHashString(const char* string) {
|
||||
* UTF8 *
|
||||
****************************************************************************/
|
||||
|
||||
// Function implementation, see utils.h for description.
|
||||
int utf8_encodeBytesCount(int value) {
|
||||
if (value <= 0x7f) return 1;
|
||||
if (value <= 0x7ff) return 2;
|
||||
@ -95,6 +104,7 @@ int utf8_encodeBytesCount(int value) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Function implementation, see utils.h for description.
|
||||
int utf8_decodeBytesCount(uint8_t byte) {
|
||||
|
||||
if ((byte >> 7) == 0b0) return 1;
|
||||
@ -107,6 +117,7 @@ int utf8_decodeBytesCount(uint8_t byte) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Function implementation, see utils.h for description.
|
||||
int utf8_encodeValue(int value, uint8_t* bytes) {
|
||||
|
||||
if (value <= 0x7f) {
|
||||
@ -145,6 +156,7 @@ int utf8_encodeValue(int value, uint8_t* bytes) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Function implementation, see utils.h for description.
|
||||
int utf8_decodeBytes(uint8_t* bytes, int* value) {
|
||||
|
||||
int continue_bytes = 0;
|
||||
|
@ -32,6 +32,7 @@ PkVarType pkGetValueType(PkVar value) {
|
||||
case OBJ_SCRIPT: return PK_SCRIPT;
|
||||
case OBJ_FUNC: return PK_FUNCTION;
|
||||
case OBJ_FIBER: return PK_FIBER;
|
||||
case OBJ_USER: TODO; break;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
|
@ -23,16 +23,13 @@ triangle = ">++++[<++++++++>-]>++++++++[>++++<-]>>++>>>+>>>+<<<<<<<<<<[-[->+<]>
|
||||
[-]<]+++++"
|
||||
|
||||
## Source: https://github.com/fabianishere/brainfuck/blob/master/examples/math/fib.bf
|
||||
fibonacci = ">++++++++++>+>+[
|
||||
[+++++[>++++++++<-]>.<++++++[>--------<-]+<<<]>.>>[
|
||||
[-]<[>+<-]>>[<<+>+>-]<[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-
|
||||
[>+<-[>+<-[>+<-[>[-]>+>+<<<-[>+<-]]]]]]]]]]]+>>>
|
||||
]<<<
|
||||
]"
|
||||
fibonacci = ">++++++++++>+>+[[+++++[>++++++++<-]>.<++++++[>--------<-]+<<<]>.
|
||||
>>[[-]<[>+<-]>>[<<+>+>-]<[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+<-[>+
|
||||
<-[>+<-[>[-]>+>+<<<-[>+<-]]]]]]]]]]]+>>>]<<<]"
|
||||
|
||||
execute(hello_world)
|
||||
execute(triangle)
|
||||
execute(fibonacci)
|
||||
##execute(fibonacci) This will run endlessly (cannot run test).
|
||||
|
||||
###############################################################################
|
||||
## INTERNAL ##
|
||||
|
61
test/run.py
61
test/run.py
@ -2,17 +2,22 @@ import subprocess, os, sys
|
||||
import json, re
|
||||
from os.path import join
|
||||
|
||||
FMT_PATH = "%-25s"
|
||||
INDENTATION = ' | '
|
||||
|
||||
## All the test files.
|
||||
test_files = [
|
||||
"lang/basics.pk",
|
||||
"lang/functions.pk",
|
||||
"lang/controlflow.pk",
|
||||
"lang/import.pk",
|
||||
|
||||
"examples/helloworld.pk",
|
||||
"examples/brainfuck.pk",
|
||||
"examples/fib.pk",
|
||||
"examples/prime.pk",
|
||||
"examples/fizzbuzz.pk",
|
||||
"examples/pi.pk",
|
||||
]
|
||||
|
||||
## All benchmark files. ## TODO: pass args for iterations.
|
||||
benchmarks = {
|
||||
"factors" : ['.pk', '.py', '.rb', '.wren'],
|
||||
"fib" : ['.pk', '.py', '.rb', '.wren'],
|
||||
@ -21,10 +26,14 @@ benchmarks = {
|
||||
"primes" : ['.pk', '.py', '.rb', ".wren"],
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
run_all_tests()
|
||||
run_all_benchmarks()
|
||||
|
||||
|
||||
def run_all_benchmarks():
|
||||
print("--------------------------------")
|
||||
print(" BENCHMARKS ")
|
||||
print("--------------------------------")
|
||||
print_title("BENCHMARKS")
|
||||
|
||||
def get_interpreter(file):
|
||||
if file.endswith('.pk' ) : return 'pocket'
|
||||
@ -47,23 +56,21 @@ def run_all_benchmarks():
|
||||
print('%10ss'%time[0])
|
||||
|
||||
def run_all_tests():
|
||||
print("--------------------------------")
|
||||
print(" TESTS ")
|
||||
print("--------------------------------")
|
||||
for path in test_files:
|
||||
run_file(path)
|
||||
print_title("TESTS")
|
||||
|
||||
def run_file(path):
|
||||
print(FMT_PATH % path, end='')
|
||||
result = run_command(['pocket', path])
|
||||
if result.returncode != 0:
|
||||
print('-- Failed')
|
||||
err = INDENTATION + result.stderr \
|
||||
.decode('utf8') \
|
||||
.replace('\n', '\n' + INDENTATION)
|
||||
print(err)
|
||||
else:
|
||||
print('-- OK')
|
||||
FMT_PATH = "%-25s"
|
||||
INDENTATION = ' | '
|
||||
for path in test_files:
|
||||
print(FMT_PATH % path, end='')
|
||||
result = run_command(['pocket', path])
|
||||
if result.returncode != 0:
|
||||
print('-- Failed')
|
||||
err = INDENTATION + result.stderr \
|
||||
.decode('utf8') \
|
||||
.replace('\n', '\n' + INDENTATION)
|
||||
print(err)
|
||||
else:
|
||||
print('-- OK')
|
||||
|
||||
|
||||
def run_command(command):
|
||||
@ -71,6 +78,10 @@ def run_command(command):
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
|
||||
run_all_tests()
|
||||
run_all_benchmarks()
|
||||
def print_title(title):
|
||||
print("--------------------------------")
|
||||
print(" %s " % title)
|
||||
print("--------------------------------")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
Loading…
Reference in New Issue
Block a user