Merge pull request #100 from ThakeeNathees/minor-refactors

yet another minor refactor
This commit is contained in:
Thakee Nathees 2021-06-20 16:01:02 +05:30 committed by GitHub
commit 3f1d0e9380
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 140 additions and 173 deletions

View File

@ -17,7 +17,7 @@
// Doc : https://likle.github.io/cwalk/
// About : Path library for C/C++. Cross-Platform for Windows, MacOS and
// Linux. Supports UNIX and Windows path styles on those platforms.
#include "thirdparty/cwalk/cwalk.c"
#include "modules/thirdparty/cwalk/cwalk.c"
/*****************************************************************************/
/* CLI MODULES */

View File

@ -7,12 +7,9 @@
#include <pocketlang.h>
#include <stdio.h> /* defines FILENAME_MAX */
// TODO: more cli/thirdparty to cli/modules/thirdparty to remove ".." in the
// relative import and split thirdparty sources into dependent directory.
#include "../thirdparty/cwalk/cwalk.h"
#include "thirdparty/cwalk/cwalk.h"
#if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
#include "../thirdparty/dirent/dirent.h"
#include "thirdparty/dirent/dirent.h"
#else
#include <dirent.h>
#endif
@ -26,9 +23,6 @@
#define get_cwd getcwd
#endif
// TODO: No error is handled below. I should check for path with size more than
// FILENAME_MAX.
// TODO: this macros should be moved to a general place of in cli.
#define TOSTRING(x) #x
#define STRINGIFY(x) TOSTRING(x)
@ -54,7 +48,7 @@ size_t pathNormalize(const char* path, char* buff, size_t buff_size) {
}
size_t pathJoin(const char* path_a, const char* path_b, char* buffer,
size_t buff_size) {
size_t buff_size) {
return cwk_path_join(path_a, path_b, buffer, buff_size);
}
@ -77,7 +71,7 @@ static inline bool pathIsDirectoryExists(const char* path) {
if (dir) { /* Directory exists. */
closedir(dir);
return true;
} else if (ENOENT == errno) { /* Directory does not exist. */
} else if (errno == ENOENT) { /* Directory does not exist. */
} else { /* opendir() failed for some other reason. */
}
@ -88,6 +82,17 @@ static inline bool pathIsExists(const char* path) {
return pathIsFileExists(path) || pathIsDirectoryExists(path);
}
static inline size_t pathAbs(const char* path, char* buff, size_t buff_size) {
char cwd[FILENAME_MAX];
if (get_cwd(cwd, sizeof(cwd)) == NULL) {
// TODO: handle error.
}
return cwk_path_get_absolute(cwd, path, buff, buff_size);
}
/*****************************************************************************/
/* MODULE FUNCTIONS */
/*****************************************************************************/
@ -100,7 +105,9 @@ static void _pathSetStyleUnix(PKVM* vm) {
static void _pathGetCWD(PKVM* vm) {
char cwd[FILENAME_MAX];
get_cwd(cwd, sizeof(cwd)); // Check if res is NULL.
if (get_cwd(cwd, sizeof(cwd)) == NULL) {
// TODO: Handle error.
}
pkReturnString(vm, cwd);
}
@ -108,11 +115,8 @@ static void _pathAbspath(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path)) return;
char cwd[FILENAME_MAX];
get_cwd(cwd, sizeof(cwd)); // Check if res is NULL.
char abspath[FILENAME_MAX];
size_t len = cwk_path_get_absolute(cwd, path, abspath, sizeof(abspath));
size_t len = pathAbs(path, abspath, sizeof(abspath));
pkReturnStringLength(vm, abspath, len);
}
@ -121,15 +125,15 @@ static void _pathRelpath(PKVM* vm) {
if (!pkGetArgString(vm, 1, &from)) return;
if (!pkGetArgString(vm, 2, &path)) return;
// TODO: this won't work if both [from] and [path] doen't have a similler
// root path, so we need to get the absolute path of the both paths (if
// thre're not already) and call the cwalk_relative().
// ie.
// a/b/c --> a/b/d.txt - works
// a/b/c --> ../d.txt - won't work
char abs_from[FILENAME_MAX];
size_t len_from = pathAbs(from, abs_from, sizeof(abs_from));
char abs_path[FILENAME_MAX];
size_t len_path = pathAbs(path, abs_path, sizeof(abs_path));
char result[FILENAME_MAX];
size_t len = cwk_path_get_relative(from, path, result, sizeof(result));
size_t len = cwk_path_get_relative(abs_from, abs_path,
result, sizeof(result));
pkReturnStringLength(vm, result, len);
}

View File

@ -1,8 +1,11 @@
// In good first issue
- Refactor fiber into a module and is_done as an attribute.
- floats like ".5".
- Implement argparse.
// To implement.
- Refactor fiber into a module and is_done as an attribute.
- def f(x)
f(x) ## not tco, unless return f(x)
end
@ -11,13 +14,6 @@
- implement 'lang.getMaxCallDepth()' (default=1000 like python) and
setMaxCallDepth(val) to change stack size at runtime.
- move moduleAddGlobalInternal() to var.h (also other var functions).
- refactor build.bat batch script to powershell
refactor makefile and setup a github action.
- floats like ".5".
- change or add => to_string() to value.as_string
and add as_repr, as_bool.
@ -30,13 +26,6 @@
fn(a, b) // May be closure support?
end
- Implement argparse.
- -v --version
- emit opcodes
- maybe write a similar .pyc file for pocket
- --docs to generate docs from cstring.
- dump compiled code to a file.
- In keyword.
- Structs (maybe enums).
- Implement file IO (require structs).
@ -52,10 +41,7 @@
- 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.
- Function docstring property.
- Union tagging alter in var.
- Github actions.
// Add more.
- Single header for embedding (script in pk, require file IO).

View File

@ -1942,6 +1942,17 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) {
if (fn_type != FN_NATIVE) {
compileBlockBody(compiler, BLOCK_FUNC);
// Tail call optimization disabled at debug mode.
if (compiler->options && !compiler->options->debug) {
if (compiler->is_last_call) {
ASSERT(_FN->opcodes.count >= 3, OOPS); // OP_CALL, argc, OP_POP
ASSERT(_FN->opcodes.data[_FN->opcodes.count - 1] == OP_POP, OOPS);
ASSERT(_FN->opcodes.data[_FN->opcodes.count - 3] == OP_CALL, OOPS);
_FN->opcodes.data[_FN->opcodes.count - 3] = OP_TAIL_CALL;
}
}
consume(compiler, TK_END, "Expected 'end' after function definition end.");
compilerExitBlock(compiler); // Parameter depth.
emitFunctionEnd(compiler);
@ -2153,6 +2164,31 @@ static int compilerImportName(Compiler* compiler, int line,
UNREACHABLE();
}
// This will called by the compilerImportAll() function to import a single
// entry from the imported script. (could be a function or global variable).
static void compilerImportSingleEntry(Compiler* compiler,
const char* name, uint32_t length) {
// Special names are begins with '$' like function body (only for now).
// Skip them.
if (name[0] == '$') return;
// Line number of the variables which will be bind to the imported symbol.
int line = compiler->previous.line;
// Add the name to the **current** script's name buffer.
int name_index = (int)scriptAddName(compiler->script, compiler->vm,
name, length);
// Get the function from the script.
emitOpcode(compiler, OP_GET_ATTRIB_KEEP);
emitShort(compiler, name_index);
int index = compilerImportName(compiler, line, name, length);
if (index != -1) emitStoreVariable(compiler, index, true);
emitOpcode(compiler, OP_POP);
}
// Import all from the script, which is also would be at the top of the stack
// before executing the below instructions.
static void compilerImportAll(Compiler* compiler, Script* script) {
@ -2160,65 +2196,24 @@ static void compilerImportAll(Compiler* compiler, Script* script) {
ASSERT(script != NULL, OOPS);
ASSERT(compiler->scope_depth == DEPTH_GLOBAL, OOPS);
// Line number of the variables which will be bind to the imported symbol.
int line = compiler->previous.line;
// TODO: Refactor this to a loop rather than jumping with goto.
// !!! WARNING !!!
//
// The below code uses 'goto' statement to run same loop twice with different
// string buffer, instead of making the loop a function or writeing it twice.
// So modify the below code with caution.
bool done = false; //< A flag to jump out of the loop.
pkUintBuffer* name_buff = NULL; //< The string buffer to iterate through.
goto L_first_buffer; //< Skip pass the below iteration.
// --------------------------------------------------------------------------
L_import_all_from_buffer:
// Iterate over the names and import them.
for (uint32_t i = 0; i < name_buff->count; i++) {
String* name = script->names.data[name_buff->data[i]];
// Special names are begins with '$' like function body (only for now).
// Skip them.
if (name->data[0] == '$') continue;
// Add the name to the **current** script's name buffer.
int name_index = (int)scriptAddName(compiler->script, compiler->vm,
name->data, name->length);
// Get the function from the script.
emitOpcode(compiler, OP_GET_ATTRIB_KEEP);
emitShort(compiler, name_index);
int index = compilerImportName(compiler, line, name->data, name->length);
if (index != -1) emitStoreVariable(compiler, index, true);
emitOpcode(compiler, OP_POP);
// Import all functions.
for (uint32_t i = 0; i < script->functions.count; i++) {
const char* name = script->functions.data[i]->name;
uint32_t length = (uint32_t)strlen(name);
compilerImportSingleEntry(compiler, name, length);
}
// If we have multiple buffer, we need to use an integer to keep track by
// incrementing it, But it's just 2 buffers so using a boolean 'done' here.
if (!done) {
done = true;
goto L_next_buffer;
} else {
goto L_import_done;
// Import all globals.
for (uint32_t i = 0; i < script->globals.count; i++) {
ASSERT(i < script->global_names.count, OOPS);
ASSERT(script->global_names.data[i] < script->names.count, OOPS);
const String* name = script->names.data[script->global_names.data[i]];
compilerImportSingleEntry(compiler, name->data, name->length);
}
// --------------------------------------------------------------------------
// Set the buffer to function names and run the iteration.
L_first_buffer:
name_buff = &script->function_names;
goto L_import_all_from_buffer;
// Set the buffer to global names and run the iteration.
L_next_buffer:
name_buff = &script->global_names;
goto L_import_all_from_buffer;
L_import_done:
return;
}
// from module import symbol [as alias [, symbol2 [as alias]]]
@ -2488,6 +2483,9 @@ static void compileStatement(Compiler* compiler) {
// print it's value when running in REPL mode.
bool is_expression = false;
// If the statement is call, this will be set to true.
compiler->is_last_call = false;
if (match(compiler, TK_BREAK)) {
if (compiler->loop == NULL) {
parseError(compiler, "Cannot use 'break' outside a loop.");
@ -2536,6 +2534,9 @@ static void compileStatement(Compiler* compiler) {
ASSERT(_FN->opcodes.count >= 2, OOPS); // OP_CALL, argc
ASSERT(_FN->opcodes.data[_FN->opcodes.count - 2] == OP_CALL, OOPS);
_FN->opcodes.data[_FN->opcodes.count - 2] = OP_TAIL_CALL;
// Now it's a return statement, not call.
compiler->is_last_call = false;
}
}
@ -2576,6 +2577,10 @@ static void compileStatement(Compiler* compiler) {
// as import statement, function define, and if we're running REPL mode top
// level expression's evaluated value will be printed.
static void compileTopLevelStatement(Compiler* compiler) {
// If the statement is call, this will be set to true.
compiler->is_last_call = false;
if (match(compiler, TK_NATIVE)) {
compileFunction(compiler, FN_NATIVE);
@ -2682,7 +2687,7 @@ PkResult compile(PKVM* vm, Script* script, const char* source,
// If compilation failed, discard all the invalid functions and globals.
if (compiler->has_errors) {
script->globals.count = script->global_names.count = globals_count;
script->functions.count = script->function_names.count = functions_count;
script->functions.count = functions_count;
}
#if DEBUG_DUMP_COMPILED_CODE

View File

@ -27,8 +27,8 @@
// A macro to declare a function, with docstring, which is defined as
// _pk_doc_<fn> = docstring; That'll used to generate function help text.
#define DEF(fn, docstring) \
const char* DOCSTRING(fn) = docstring; \
#define DEF(fn, docstring) \
static const char* DOCSTRING(fn) = docstring; \
static void fn(PKVM* vm)
/*****************************************************************************/
@ -82,20 +82,6 @@ PkHandle* pkGetFunction(PKVM* vm, PkHandle* module,
__ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), "Given handle is not a module");
Script* script = (Script*)AS_OBJ(scr);
// TODO: Currently it's O(n) and could be optimized to O(log(n)) but does it
// worth it?
//
// 'function_names' buffer is un-necessary since the function itself has the
// reference to the function name and it can be refactored into a index
// buffer in an "increasing-name" order which can be used to binary search.
// Similer for 'global_names' refactor them from VarBuffer to GlobalVarBuffer
// where GlobalVar is struct { const char* name, Var value };
//
// "increasing-name" order index buffer:
// A buffer of int where each is an index in the function buffer and each
// points to different functions in an "increasing-name" (could be hash
// value) order. If we have more than some threshold number of function
// use binary search. (remember to skip literal functions).
for (uint32_t i = 0; i < script->functions.count; i++) {
const char* fn_name = script->functions.data[i]->name;
if (strcmp(name, fn_name) == 0) {
@ -770,15 +756,11 @@ static inline void assertModuleNameDef(PKVM* vm, Script* script,
static void moduleAddGlobalInternal(PKVM* vm, Script* script,
const char* name, Var value) {
// Ensure the name isn't predefined.
// Ensure the name isn't defined already.
assertModuleNameDef(vm, script, name);
// TODO: move this to pk_var.h and use it in the compilerAddVariable
// function.
uint32_t name_index = scriptAddName(script, vm, name,
(uint32_t)strlen(name));
pkUintBufferWrite(&script->global_names, vm, name_index);
pkVarBufferWrite(&script->globals, vm, value);
// Add the value to the globals buffer.
scriptAddGlobal(vm, script, name, (uint32_t)strlen(name), value);
}
// An internal function to add a function to the given [script].

View File

@ -214,13 +214,12 @@ void dumpFunctionCode(PKVM* vm, Function* func, pkByteBuffer* buff) {
case OP_PUSH_FN:
{
int fn_index = READ_BYTE();
int name_index = func->owner->function_names.data[fn_index];
String* name = func->owner->names.data[name_index];
const char* name = func->owner->functions.data[fn_index]->name;
// Prints: %5d [Fn:%s]\n
ADD_INTEGER(vm, buff, fn_index, INT_WIDTH);
pkByteBufferAddString(buff, vm, STR_AND_LEN(" [Fn:"));
pkByteBufferAddString(buff, vm, name->data, name->length);
pkByteBufferAddString(buff, vm, STR_AND_LEN(name));
pkByteBufferAddString(buff, vm, STR_AND_LEN("]\n"));
break;
}

View File

@ -192,9 +192,6 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) {
markFunctionBuffer(vm, &scr->functions);
vm->bytes_allocated += sizeof(Function*) * scr->functions.capacity;
// Integer buffer have no gray call.
vm->bytes_allocated += sizeof(uint32_t) * scr->function_names.capacity;
markStringBuffer(vm, &scr->names);
vm->bytes_allocated += sizeof(String*) * scr->names.capacity;
@ -332,7 +329,6 @@ Script* newScript(PKVM* vm, String* path) {
pkUintBufferInit(&script->global_names);
pkVarBufferInit(&script->literals);
pkFunctionBufferInit(&script->functions);
pkUintBufferInit(&script->function_names);
pkStringBufferInit(&script->names);
vmPushTempRef(vm, &script->_super);
@ -358,6 +354,8 @@ Function* newFunction(PKVM* vm, const char* name, int length, Script* owner,
Function* func = ALLOCATE(vm, Function);
varInitObject(&func->_super, vm, OBJ_FUNC);
vmPushTempRef(vm, &func->_super); // func
if (owner == NULL) {
ASSERT(is_native, OOPS);
func->name = name;
@ -365,12 +363,8 @@ Function* newFunction(PKVM* vm, const char* name, int length, Script* owner,
func->is_native = is_native;
} else {
// Add the name in the script's function buffer.
vmPushTempRef(vm, &func->_super);
pkFunctionBufferWrite(&owner->functions, vm, func);
uint32_t name_index = scriptAddName(owner, vm, name, length);
pkUintBufferWrite(&owner->function_names, vm, name_index);
vmPopTempRef(vm);
func->name = owner->names.data[name_index]->data;
func->owner = owner;
@ -380,9 +374,9 @@ Function* newFunction(PKVM* vm, const char* name, int length, Script* owner,
if (is_native) {
func->native = NULL;
} else {
Fn* fn = ALLOCATE(vm, Fn);
pkByteBufferInit(&fn->opcodes);
pkUintBufferInit(&fn->oplines);
fn->stack_size = 0;
@ -392,6 +386,7 @@ Function* newFunction(PKVM* vm, const char* name, int length, Script* owner,
// Both native and script (TODO:) functions support docstring.
func->docstring = docstring;
vmPopTempRef(vm); // func
return func;
}
@ -903,7 +898,6 @@ void freeObject(PKVM* vm, Object* self) {
pkUintBufferClear(&scr->global_names, vm);
pkVarBufferClear(&scr->literals, vm);
pkFunctionBufferClear(&scr->functions, vm);
pkUintBufferClear(&scr->function_names, vm);
pkStringBufferClear(&scr->names, vm);
} break;
@ -950,11 +944,10 @@ uint32_t scriptAddName(Script* self, PKVM* vm, const char* name,
}
uint32_t scriptGetFunc(Script* script, const char* name, uint32_t length) {
for (uint32_t i = 0; i < script->function_names.count; i++) {
uint32_t name_index = script->function_names.data[i];
String* fn_name = script->names.data[name_index];
if (fn_name->length == length &&
strncmp(fn_name->data, name, length) == 0) {
for (uint32_t i = 0; i < script->functions.count; i++) {
const char* fn_name = script->functions.data[i]->name;
uint32_t fn_length = (uint32_t)strlen(fn_name);
if (fn_length == length && strncmp(fn_name, name, length) == 0) {
return i;
}
}

View File

@ -273,10 +273,10 @@ struct Script {
names: ["v1", "fn1", "v2", "fn2", ...]
0 1 2 3
fn_names: [ 1, 3 ] <-- function name
g_names: [ 1, 3 ] <-- function name
0 1 <-- it's index
functions: [ fn1, fn2 ]
globals: [ fn1, fn2 ]
0 1
*/
@ -285,8 +285,8 @@ struct Script {
pkVarBuffer globals; //< Script level global variables.
pkUintBuffer global_names; //< Name map to index in globals.
pkFunctionBuffer functions; //< Script level functions.
pkUintBuffer function_names; //< Name map to index in functions.
pkStringBuffer names; //< Name literals, attribute names, etc.
pkVarBuffer literals; //< Script literal constant values.
@ -299,7 +299,7 @@ struct Script {
typedef struct {
pkByteBuffer opcodes; //< Buffer of opcodes.
pkUintBuffer oplines; //< Line number of opcodes for debug (1 based).
int stack_size; //< Maximum size of stack required.
int stack_size; //< Maximum size of stack required.
} Fn;
struct Function {

View File

@ -526,26 +526,27 @@ static inline void growStack(PKVM* vm, int size) {
}
}
static inline void pushCallFrame(PKVM* vm, const Function* fn) {
ASSERT(!fn->is_native, "Native function shouldn't use call frames.");
static inline void pushCallFrame(PKVM* vm, const Function* fn, Var* rbp) {
ASSERT(!fn->is_native, "Native function shouldn't use call frames.");
// Grow the stack frame if needed.
if (vm->fiber->frame_count + 1 > vm->fiber->frame_capacity) {
int new_capacity = vm->fiber->frame_capacity << 1;
vm->fiber->frames = (CallFrame*)vmRealloc(vm, vm->fiber->frames,
sizeof(CallFrame) * vm->fiber->frame_capacity,
sizeof(CallFrame) * new_capacity);
vm->fiber->frame_capacity = new_capacity;
}
// Grow the stack frame if needed.
if (vm->fiber->frame_count + 1 > vm->fiber->frame_capacity) {
int new_capacity = vm->fiber->frame_capacity << 1;
vm->fiber->frames = (CallFrame*)vmRealloc(vm, vm->fiber->frames,
sizeof(CallFrame) * vm->fiber->frame_capacity,
sizeof(CallFrame) * new_capacity);
vm->fiber->frame_capacity = new_capacity;
}
// Grow the stack if needed.
int needed = fn->fn->stack_size + (int)(vm->fiber->sp - vm->fiber->stack);
if (vm->fiber->stack_size <= needed) growStack(vm, needed);
// Grow the stack if needed.
int needed = fn->fn->stack_size + (int)(vm->fiber->sp - vm->fiber->stack);
if (vm->fiber->stack_size <= needed) growStack(vm, needed);
CallFrame* frame = vm->fiber->frames + vm->fiber->frame_count++;
frame->rbp = vm->fiber->ret;
frame->fn = fn;
frame->ip = fn->fn->opcodes.data;
CallFrame* frame = vm->fiber->frames + vm->fiber->frame_count++;
*rbp = VAR_NULL;
frame->rbp = rbp;
frame->fn = fn;
frame->ip = fn->fn->opcodes.data;
}
static inline void reuseCallFrame(PKVM* vm, const Function* fn) {
@ -556,9 +557,15 @@ static inline void reuseCallFrame(PKVM* vm, const Function* fn) {
Fiber* fb = vm->fiber;
CallFrame* frame = fb->frames + fb->frame_count - 1;
frame->fn = fn;
frame->ip = fn->fn->opcodes.data;
ASSERT(*frame->rbp == VAR_NULL, OOPS);
// Move all the argument(s) to the base of the current frame.
Var* arg = fb->sp - fn->arity;
Var* target = fb->ret + 1;
Var* target = frame->rbp + 1;
for (; arg < fb->sp; arg++, target++) {
*target = *arg;
}
@ -569,11 +576,6 @@ static inline void reuseCallFrame(PKVM* vm, const Function* fn) {
// Grow the stack if needed (least probably).
int needed = fn->fn->stack_size + (int)(vm->fiber->sp - vm->fiber->stack);
if (vm->fiber->stack_size <= needed) growStack(vm, needed);
CallFrame* frame = vm->fiber->frames + vm->fiber->frame_count - 1;
ASSERT(frame->rbp == fb->ret, OOPS);
frame->fn = fn;
frame->ip = fn->fn->opcodes.data;
}
static void reportError(PKVM* vm) {
@ -901,10 +903,6 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
if (fn->is_native) {
// Next call frame starts here. (including return value).
call_fiber->ret = callable;
*(call_fiber->ret) = VAR_NULL; //< Set the return value to null.
if (fn->native == NULL) {
RUNTIME_ERROR(stringFormat(vm,
"Native function pointer of $ was NULL.", fn->name));
@ -913,6 +911,10 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
// Update the current frame's ip.
UPDATE_FRAME();
// Next call frame starts here. (including return value).
call_fiber->ret = callable;
*(call_fiber->ret) = VAR_NULL; //< Set the return value to null.
fn->native(vm); //< Call the native function.
// Calling yield() will change vm->fiber to it's caller fiber, which
@ -931,12 +933,8 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
} else {
if (instruction == OP_CALL) {
// Next call frame starts here. (including return value).
call_fiber->ret = callable;
*(call_fiber->ret) = VAR_NULL; //< Set the return value to null.
UPDATE_FRAME(); //< Update the current frame's ip.
pushCallFrame(vm, fn);
pushCallFrame(vm, fn, callable);
LOAD_FRAME(); //< Load the top frame to vm's execution variables.
} else {