diff --git a/README.md b/README.md index 9b281d5..1a06596 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/cli/TODO.txt b/cli/TODO.txt index 76f140a..cf0d401 100644 --- a/cli/TODO.txt +++ b/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. diff --git a/cli/all.c b/cli/all.c index aca7f3b..2090f2c 100644 --- a/cli/all.c +++ b/cli/all.c @@ -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. diff --git a/docs/generate.py b/docs/generate.py index a087a03..e937ecf 100644 --- a/docs/generate.py +++ b/docs/generate.py @@ -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 += '
\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): '%s' % nk) ## codehilite mark the compilation command as error. - if 'build from source' in src: - content = content.replace('', '') + content = content.replace('', '') 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. diff --git a/docs/pages/Getting Started/build from source.md b/docs/pages/Getting-Started/build-from-source.md similarity index 100% rename from docs/pages/Getting Started/build from source.md rename to docs/pages/Getting-Started/build-from-source.md diff --git a/docs/pages/Getting Started/contributing.md b/docs/pages/Getting-Started/contributing.md similarity index 100% rename from docs/pages/Getting Started/contributing.md rename to docs/pages/Getting-Started/contributing.md diff --git a/docs/pages/Getting-Started/learn-in-5-minutes.md b/docs/pages/Getting-Started/learn-in-5-minutes.md new file mode 100644 index 0000000..fe0d0df --- /dev/null +++ b/docs/pages/Getting-Started/learn-in-5-minutes.md @@ -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') + +``` \ No newline at end of file diff --git a/docs/pages/Getting Started/try it now.html b/docs/pages/Getting-Started/try-it-now.html similarity index 100% rename from docs/pages/Getting Started/try it now.html rename to docs/pages/Getting-Started/try-it-now.html diff --git a/docs/pages/Language API/functions.md b/docs/pages/Language-API/functions.md similarity index 100% rename from docs/pages/Language API/functions.md rename to docs/pages/Language-API/functions.md diff --git a/docs/pages/Language API/modules.md b/docs/pages/Language-API/modules.md similarity index 100% rename from docs/pages/Language API/modules.md rename to docs/pages/Language-API/modules.md diff --git a/docs/pages/Language API/variables.md b/docs/pages/Language-API/variables.md similarity index 100% rename from docs/pages/Language API/variables.md rename to docs/pages/Language-API/variables.md diff --git a/src/compiler.c b/src/compiler.c index e22eed2..e5739b3 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -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; diff --git a/src/core.c b/src/core.c index 5430960..237209c 100644 --- a/src/core.c +++ b/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 '$'", \ diff --git a/src/core.h b/src/core.h index 892fba9..c3106ce 100644 --- a/src/core.h +++ b/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 diff --git a/src/include/pocketlang.h b/src/include/pocketlang.h index fc28caa..9449d25 100644 --- a/src/include/pocketlang.h +++ b/src/include/pocketlang.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 */ diff --git a/src/utils.c b/src/utils.c index 45d7ae2..ebc1a47 100644 --- a/src/utils.c +++ b/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; diff --git a/src/var.c b/src/var.c index c29f700..2e15ae5 100644 --- a/src/var.c +++ b/src/var.c @@ -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(); diff --git a/test/examples/brainfuck.pk b/test/examples/brainfuck.pk index 1980a7a..d020c5b 100644 --- a/test/examples/brainfuck.pk +++ b/test/examples/brainfuck.pk @@ -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 ## diff --git a/test/run.py b/test/run.py index 530d8ca..942ae73 100644 --- a/test/run.py +++ b/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()