Merge pull request #233 from ThakeeNathees/warnings-fix

Minor code enhancements
This commit is contained in:
Thakee Nathees 2022-05-21 13:18:32 +05:30 committed by GitHub
commit 4072a06bd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 299 additions and 184 deletions

View File

@ -17,7 +17,7 @@ were used as a reference to write this language.
```ruby
# Python like import statement.
from lang import clock as now
from time import clock as now
# A recursive fibonacci function.
def fib(n)

View File

@ -60,7 +60,6 @@ static PKVM* intializePocketVM() {
}
PKVM* vm = pkNewVM(&config);
pkRegisterLibs(vm);
return vm;
}

View File

@ -384,7 +384,7 @@ from lang import clock as now
import foo.bar # Import module bar from foo directory
import baz # If baz is a directory it'll import baz/_init.pk
# I'll only search for foo relatievly.
# It'll only search for foo relatievly.
import .foo # ./foo.pk or ./foo/_init.pk or ./foo.dll, ...
# ^ meaning parent directory relative to this script.

View File

@ -6,7 +6,7 @@ from shutil import which
## This file is not intended to be included in other files at the moment.
THIS_PATH = abspath(dirname(__file__))
POCKET_SOURCE_DIR = join(THIS_PATH, "../../../pocketlang/src/")
POCKET_ROOT = join(THIS_PATH, "../../../pocketlang/src/")
JS_API_PATH = join(THIS_PATH, "io_api.js")
MAIN_C = join(THIS_PATH, "main.c")
TARGET_DIR = join(THIS_PATH, "../static/wasm/")
@ -27,7 +27,7 @@ def main():
os.mkdir(TARGET_DIR)
sources = ' '.join(collect_source_files())
include = '-I' + join(POCKET_SOURCE_DIR, 'include/')
include = '-I' + join(POCKET_ROOT, 'include/')
output = join(TARGET_DIR, TARGET_NAME)
exports = "\"EXPORTED_RUNTIME_METHODS=['ccall','cwrap']\""
js_api = JS_API_PATH
@ -39,9 +39,10 @@ def main():
def collect_source_files():
sources = []
for file in os.listdir(POCKET_SOURCE_DIR):
if isdir(file): continue
if file.endswith('.c'): sources.append(join(POCKET_SOURCE_DIR, file))
for dir in ('core/', 'libs/'):
for file in os.listdir(join(POCKET_ROOT, dir)):
if isdir(file): continue
if file.endswith('.c'): sources.append(join(POCKET_ROOT, dir, file))
return sources
if __name__ == "__main__":

View File

@ -96,10 +96,10 @@ def generate():
'\n'
gen += parse(header) + '\n'
gen += '#ifdef PK_IMPL\n\n'
gen += '#ifdef PK_IMPLEMENT\n\n'
for source in SOURCES:
gen += parse(source)
gen += '#endif // PK_IMPL\n'
gen += '#endif // PK_IMPLEMENT\n'
return gen

View File

@ -8,6 +8,9 @@
## To get the trace report redefine TRACE_MEMORY as 1 at the
## pk_internal.h and compile pocketlang.
raise "This script Need refactor after removing pkAllocString " + \
"and adding pkRealloc"
import sys
def detect_leak():

View File

@ -664,6 +664,28 @@ static void setNextValueToken(Parser* parser, _TokenType type, Var value);
static void setNextToken(Parser* parser, _TokenType type);
static bool matchChar(Parser* parser, char c);
#define _BETWEEN(a, c, b) (((a) <= (c)) && ((c) <= (b)))
static inline bool _isCharHex(char c) {
return (_BETWEEN('0', c, '9')
|| _BETWEEN('a', c, 'z')
|| _BETWEEN('A', c, 'Z'));
}
static inline uint8_t _charHexVal(char c) {
ASSERT(_isCharHex(c), OOPS);
if (_BETWEEN('0', c, '9')) {
return c - '0';
} else if (_BETWEEN('a', c, 'z')) {
return c - 'a' + 10;
} else if (_BETWEEN('A', c, 'Z')) {
return c - 'A' + 10;
}
UNREACHABLE();
return 0;
}
#undef _BETWEEN
static void eatString(Compiler* compiler, bool single_quote) {
Parser* parser = &compiler->parser;
@ -693,15 +715,15 @@ static void eatString(Compiler* compiler, bool single_quote) {
if (parser->si_depth < MAX_STR_INTERP_DEPTH) {
tk_type = TK_STRING_INTERP;
char c = peekChar(parser);
if (c == '{') { // Expression interpolation (ie. "${expr}").
char c2 = peekChar(parser);
if (c2 == '{') { // Expression interpolation (ie. "${expr}").
eatChar(parser);
parser->si_depth++;
parser->si_quote[parser->si_depth - 1] = quote;
parser->si_open_brace[parser->si_depth - 1] = 0;
} else { // Name Interpolation.
if (!utilIsName(c)) {
if (!utilIsName(c2)) {
syntaxError(compiler, makeErrToken(parser),
"Expected '{' or identifier after '$'.");
return;
@ -740,9 +762,35 @@ static void eatString(Compiler* compiler, bool single_quote) {
// '$' In pocketlang string is used for interpolation.
case '$': pkByteBufferWrite(&buff, parser->vm, '$'); break;
// Hex literal in string should match `\x[0-9a-zA-Z][0-9a-zA-Z]`
case 'x': {
uint8_t val = 0;
c = eatChar(parser);
if (!_isCharHex(c)) {
semanticError(compiler, makeErrToken(parser),
"Invalid hex escape.");
break;
}
val = _charHexVal(c);
c = eatChar(parser);
if (!_isCharHex(c)) {
semanticError(compiler, makeErrToken(parser),
"Invalid hex escape.");
break;
}
val = (val << 4) | _charHexVal(c);
pkByteBufferWrite(&buff, parser->vm, val);
} break;
default:
syntaxError(compiler, makeErrToken(parser),
"Invalid escape character.");
semanticError(compiler, makeErrToken(parser),
"Invalid escape character.");
return;
}
} else {
@ -807,10 +855,6 @@ static void eatName(Parser* parser) {
static void eatNumber(Compiler* compiler) {
Parser* parser = &compiler->parser;
#define IS_HEX_CHAR(c) \
(('0' <= (c) && (c) <= '9') || \
('a' <= (c) && (c) <= 'f'))
#define IS_BIN_CHAR(c) (((c) == '0') || ((c) == '1'))
Var value = VAR_NULL; // The number value.
@ -855,8 +899,8 @@ static void eatNumber(Compiler* compiler) {
uint64_t hex = 0;
c = peekChar(parser);
// The first digit should be either hex digit.
if (!IS_HEX_CHAR(c)) {
// The first digit should be hex digit.
if (!_isCharHex(c)) {
syntaxError(compiler, makeErrToken(parser), "Invalid hex literal.");
return;
@ -864,7 +908,7 @@ static void eatNumber(Compiler* compiler) {
do {
// Consume the next digit.
c = peekChar(parser);
if (!IS_HEX_CHAR(c)) break;
if (!_isCharHex(c)) break;
eatChar(parser);
// Check the length of the binary literal.
@ -876,10 +920,7 @@ static void eatNumber(Compiler* compiler) {
}
// "Append" the next digit at the end.
uint8_t append_val = ('0' <= c && c <= '9')
? (uint8_t)(c - '0')
: (uint8_t)((c - 'a') + 10);
hex = (hex << 4) | append_val;
hex = (hex << 4) | _charHexVal(c);
} while (true);
@ -930,7 +971,6 @@ static void eatNumber(Compiler* compiler) {
setNextValueToken(parser, TK_NUMBER, value);
#undef IS_BIN_CHAR
#undef IS_HEX_CHAR
}
// Read and ignore chars till it reach new line or EOF.
@ -1337,7 +1377,6 @@ static int findBuiltinFunction(const PKVM* vm,
static int findBuiltinClass(const PKVM* vm,
const char* name, uint32_t length) {
for (int i = 0; i < PK_INSTANCE; i++) {
uint32_t bfn_length = vm->builtin_classes[i]->name->length;
if (IS_CSTR_EQ(vm->builtin_classes[i]->name, name, length)) {
return i;
}
@ -2344,12 +2383,10 @@ static void compilerAddForward(Compiler* compiler, int instruction, Fn* fn,
// Add a literal constant to module literals and return it's index.
static int compilerAddConstant(Compiler* compiler, Var value) {
pkVarBuffer* constants = &compiler->module->constants;
uint32_t index = moduleAddConstant(compiler->parser.vm,
compiler->module, value);
checkMaxConstantsReached(compiler, index);
return (int)index;
return (int) index;
}
// Enters inside a block.
@ -2899,7 +2936,7 @@ static Token compileImportPath(Compiler* compiler) {
// Create constant pool entry for the path string.
int index = 0;
moduleAddString(compiler->module, compiler->parser.vm,
buff.data, buff.count - 1, &index);
(const char*) buff.data, buff.count - 1, &index);
pkByteBufferClear(&buff, vm);

View File

@ -7,7 +7,6 @@
#include <ctype.h>
#include <limits.h>
#include <math.h>
#include <time.h>
#ifndef PK_AMALGAMATED
#include "core.h"
@ -40,9 +39,6 @@
/* VALIDATORS */
/*****************************************************************************/
// Evaluated to true of the [num] is in byte range.
#define IS_NUM_BYTE(num) ((CHAR_MIN <= (num)) && ((num) <= CHAR_MAX))
// Check if [var] is a numeric value (bool/number) and set [value].
static inline bool isNumeric(Var var, double* value) {
if (IS_NUM(var)) {
@ -396,11 +392,11 @@ DEF(coreChr,
int64_t num;
if (!validateInteger(vm, ARG(1), &num, "Argument 1")) return;
if (!IS_NUM_BYTE(num)) {
RET_ERR(newString(vm, "The number is not in a byte range."));
if (!(0 <= num && num <= 0xff)) {
RET_ERR(newString(vm, "The number should be in range 0x00 to 0xff."));
}
char c = (char)num;
char c = (char) num;
RET(VAR_OBJ(newStringLength(vm, &c, 1)));
}
@ -462,7 +458,7 @@ DEF(coreInput,
}
String* line = newString(vm, str);
pkDeAllocString(vm, str);
pkRealloc(vm, str, 0);
RET(VAR_OBJ(line));
}
@ -655,14 +651,6 @@ void moduleAddFunctionInternal(PKVM* vm, Module* module,
}
// 'lang' library methods.
// -----------------------
DEF(stdLangClock,
"clock() -> num\n"
"Returns the number of seconds since the application started") {
RET(VAR_NUM((double)clock() / CLOCKS_PER_SEC));
}
DEF(stdLangGC,
"gc() -> num\n"
@ -734,7 +722,6 @@ static void initializeCoreModules(PKVM* vm) {
vmPopTempRef(vm) /* module */
NEW_MODULE(lang, "lang");
MODULE_ADD_FN(lang, "clock", stdLangClock, 0);
MODULE_ADD_FN(lang, "gc", stdLangGC, 0);
MODULE_ADD_FN(lang, "disas", stdLangDisas, 1);
MODULE_ADD_FN(lang, "write", stdLangWrite, -1);
@ -760,13 +747,13 @@ static void _ctorBool(PKVM* vm) {
static void _ctorNumber(PKVM* vm) {
double value;
if (isNumeric(ARG(1), &value)) {
RET(VAR_NUM(value));
}
if (IS_OBJ_TYPE(ARG(1), OBJ_STRING)) {
String* str = (String*) AS_OBJ(ARG(1));
double value;
const char* err = utilToNumber(str->data, &value);
if (err == NULL) RET(VAR_NUM(value));
VM_SET_ERROR(vm, newString(vm, err));
@ -919,6 +906,7 @@ static void initializePrimitiveClasses(PKVM* vm) {
ADD_CTOR(PK_BOOL, "@ctorBool", _ctorBool, 1);
ADD_CTOR(PK_NUMBER, "@ctorNumber", _ctorNumber, 1);
ADD_CTOR(PK_STRING, "@ctorString", _ctorString, -1);
ADD_CTOR(PK_RANGE, "@ctorRange", _ctorRange, 2);
ADD_CTOR(PK_LIST, "@ctorList", _ctorList, -1);
ADD_CTOR(PK_MAP, "@ctorMap", _ctorMap, 0);
ADD_CTOR(PK_FIBER, "@ctorFiber", _ctorFiber, 1);
@ -945,8 +933,6 @@ static void initializePrimitiveClasses(PKVM* vm) {
#undef ADD_METHOD
}
#undef IS_NUM_BYTE
/*****************************************************************************/
/* OPERATORS */
/*****************************************************************************/
@ -1176,6 +1162,8 @@ Var varAdd(PKVM* vm, Var v1, Var v2, bool inplace) {
}
}
} break;
default: break;
}
}
CHECK_INST_BINARY_OP("+");
@ -1353,6 +1341,8 @@ bool varContains(PKVM* vm, Var elem, Var container) {
Map* map = (Map*)AS_OBJ(container);
return !IS_UNDEF(mapGet(map, elem));
} break;
default: break;
}
#define v1 container

View File

@ -15,6 +15,23 @@
#include "vm.h"
#endif
// TODO: Document this or Find a better way.
//
// Pocketlang core doesn't implement path resolving funcionality. Rather it
// should be provided the host application. By default we're using an
// implementation from the path library. However pocket core cannot be depend
// on its libs, otherwise it'll breaks the encapsulation.
//
// As a workaround we declare the default path resolver here and use it.
// But if someone wants to compile just the core pocketlang without libs
// they have to define PK_NO_LIBS to prevent the compiler from not be able
// to find functions when linking.
#ifndef PK_NO_LIBS
void registerLibs(PKVM* vm);
void cleanupLibs(PKVM* vm);
char* pathResolveImport(PKVM* vm, const char* from, const char* path);
#endif
#define CHECK_ARG_NULL(name) \
ASSERT((name) != NULL, "Argument " #name " was NULL.");
@ -66,41 +83,17 @@ static void stdoutWrite(PKVM* vm, const char* text);
static char* stdinRead(PKVM* vm);
static char* loadScript(PKVM* vm, const char* path);
char* pkAllocString(PKVM* vm, size_t size) {
PK_PUBLIC void* pkRealloc(PKVM* vm, void* ptr, size_t size) {
ASSERT(vm->config.realloc_fn != NULL, "PKVM's allocator was NULL.");
#if TRACE_MEMORY
void* ptr = vm->config.realloc_fn(NULL, size, vm->config.user_data);
printf("[alloc string] alloc : %p = %+li bytes\n", ptr, (long) size);
return (char*) ptr;
void* newptr = vm->config.realloc_fn(ptr, size, vm->config.user_data);
printf("[pkRealloc] %p -> %p %+li bytes\n", ptr, newptr, (long) size);
return ptr;
#else
return (char*) vm->config.realloc_fn(NULL, size, vm->config.user_data);
return vm->config.realloc_fn(ptr, size, vm->config.user_data);
#endif
}
void pkDeAllocString(PKVM* vm, char* str) {
ASSERT(vm->config.realloc_fn != NULL, "PKVM's allocator was NULL.");
#if TRACE_MEMORY
printf("[alloc string] dealloc : %p\n", str);
#endif
vm->config.realloc_fn(str, 0, vm->config.user_data);
}
// TODO: Document this or Find a better way.
//
// Pocketlang core doesn't implement path resolving funcionality. Rather it
// should be provided the host application. By default we're using an
// implementation from the path library. However pocket core cannot be depend
// on its libs, otherwise it'll breaks the encapsulation.
//
// As a workaround we declare the default path resolver here and use it.
// But if someone wants to compile just the core pocketlang without libs
// they have to define PK_NO_LIBS to prevent the compiler from not be able
// to find functions when linking.
#if !defined(PK_NO_LIBS)
char* pathResolveImport(PKVM* vm, const char* from, const char* path);
#endif
PkConfiguration pkNewConfiguration() {
PkConfiguration config;
memset(&config, 0, sizeof(config));
@ -110,7 +103,7 @@ PkConfiguration pkNewConfiguration() {
config.stdout_write = stdoutWrite;
config.stderr_write = stderrWrite;
config.stdin_read = stdinRead;
#if !defined(PK_NO_LIBS)
#ifndef PK_NO_LIBS
config.resolve_path_fn = pathResolveImport;
#endif
config.load_script_fn = loadScript;
@ -146,11 +139,20 @@ PKVM* pkNewVM(PkConfiguration* config) {
}
initializeCore(vm);
#ifndef PK_NO_LIBS
registerLibs(vm);
#endif
return vm;
}
void pkFreeVM(PKVM* vm) {
#ifndef PK_NO_LIBS
cleanupLibs(vm);
#endif
Object* obj = vm->first;
while (obj != NULL) {
Object* next = obj->next;
@ -334,7 +336,7 @@ PkResult pkRunFile(PKVM* vm, const char* path) {
// Set module path and and deallocate resolved.
String* script_path = newString(vm, resolved_);
vmPushTempRef(vm, &script_path->_super); // script_path.
pkDeAllocString(vm, resolved_);
pkRealloc(vm, resolved_, 0);
module->path = script_path;
vmPopTempRef(vm); // script_path.
@ -352,7 +354,7 @@ PkResult pkRunFile(PKVM* vm, const char* path) {
}
} else {
result = compile(vm, module, source, NULL);
pkDeAllocString(vm, source);
pkRealloc(vm, source, 0);
}
if (result == PK_RESULT_SUCCESS) {
@ -449,21 +451,21 @@ PkResult pkRunREPL(PKVM* vm) {
if (line_length >= 1 && *(line + line_length - 1) == EOF) {
printfn(vm, "\n");
result = PK_RESULT_SUCCESS;
pkDeAllocString(vm, line);
pkRealloc(vm, line, 0);
break;
}
// If the line is empty, we don't have to compile it.
if (isStringEmpty(line)) {
if (need_more_lines) ASSERT(lines.count != 0, OOPS);
pkDeAllocString(vm, line);
pkRealloc(vm, line, 0);
continue;
}
// Add the line to the lines buffer.
if (lines.count != 0) pkByteBufferWrite(&lines, vm, '\n');
pkByteBufferAddString(&lines, vm, line, (uint32_t) line_length);
pkDeAllocString(vm, line);
pkRealloc(vm, line, 0);
pkByteBufferWrite(&lines, vm, '\0');
// Compile the buffer to the module.
@ -546,12 +548,12 @@ bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max) {
}
// Set error for incompatible type provided as an argument. (TODO: got type).
#define ERR_INVALID_ARG_TYPE(ty_name) \
do { \
char buff[STR_INT_BUFF_SIZE]; \
sprintf(buff, "%d", arg); \
VM_SET_ERROR(vm, stringFormat(vm, "Expected a '$' at argument $.", \
ty_name, buff)); \
#define ERR_INVALID_SLOT_TYPE(ty_name) \
do { \
char buff[STR_INT_BUFF_SIZE]; \
sprintf(buff, "%d", arg); \
VM_SET_ERROR(vm, stringFormat(vm, "Expected a '$' at slot $.", \
ty_name, buff)); \
} while (false)
// FIXME: If the user needs just the boolean value of the object, they should
@ -562,7 +564,7 @@ PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int arg, bool* value) {
Var val = ARG(arg);
if (!IS_BOOL(val)) {
ERR_INVALID_ARG_TYPE("Boolean");
ERR_INVALID_SLOT_TYPE("Boolean");
return false;
}
@ -576,7 +578,7 @@ PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int arg, double* value) {
Var val = ARG(arg);
if (!IS_NUM(val)) {
ERR_INVALID_ARG_TYPE("Number");
ERR_INVALID_SLOT_TYPE("Number");
return false;
}
@ -591,7 +593,7 @@ PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int arg, const char** value,
Var val = ARG(arg);
if (!IS_OBJ_TYPE(val, OBJ_STRING)) {
ERR_INVALID_ARG_TYPE("String");
ERR_INVALID_SLOT_TYPE("String");
return false;
}
String* str = (String*)AS_OBJ(val);
@ -604,7 +606,7 @@ bool pkValidateSlotType(PKVM* vm, int arg, PkVarType type) {
CHECK_FIBER_EXISTS(vm);
VALIDATE_ARGC(arg);
if (getVarType(ARG(arg)) != type) {
ERR_INVALID_ARG_TYPE(getPkVarTypeName(type));
ERR_INVALID_SLOT_TYPE(getPkVarTypeName(type));
return false;
}
@ -620,7 +622,7 @@ PK_PUBLIC bool pkValidateSlotInstanceOf(PKVM* vm, int arg, int cls) {
if (!varIsType(vm, instance, class_)) {
// If [class_] is not a valid class, it's already an error.
if (VM_HAS_ERROR(vm)) return false;
ERR_INVALID_ARG_TYPE(((Class*)AS_OBJ(class_))->name->data);
ERR_INVALID_SLOT_TYPE(((Class*)AS_OBJ(class_))->name->data);
return false;
}
@ -632,7 +634,6 @@ bool pkIsSlotInstanceOf(PKVM* vm, int inst, int cls, bool* val) {
VALIDATE_SLOT_INDEX(inst);
VALIDATE_SLOT_INDEX(cls);
Var instance = SLOT(inst), class_ = SLOT(cls);
*val = varIsType(vm, inst, cls);
return !VM_HAS_ERROR(vm);
}
@ -876,6 +877,19 @@ void pkPlaceSelf(PKVM* vm, int index) {
SET_SLOT(index, vm->fiber->self);
}
bool pkImportModule(PKVM* vm, const char* path, int index) {
CHECK_FIBER_EXISTS(vm);
VALIDATE_SLOT_INDEX(index);
String* path_ = newString(vm, path);
vmPushTempRef(vm, &path_->_super); // path_
Var module = vmImportModule(vm, NULL, path_);
vmPopTempRef(vm); // path_
SET_SLOT(index, module);
return !VM_HAS_ERROR(vm);
}
void pkGetClass(PKVM* vm, int instance, int index) {
CHECK_FIBER_EXISTS(vm);
VALIDATE_SLOT_INDEX(instance);
@ -928,7 +942,7 @@ static char* stdinRead(PKVM* vm) {
} while (c != EOF);
pkByteBufferWrite(&buff, vm, '\0');
char* str = pkAllocString(vm, buff.count);
char* str = pkRealloc(vm, NULL, buff.count);
memcpy(str, buff.data, buff.count);
return str;
}
@ -946,8 +960,8 @@ static char* loadScript(PKVM* vm, const char* path) {
fseek(file, 0, SEEK_SET);
// Allocate string + 1 for the NULL terminator.
char* buff = pkAllocString(vm, file_size + 1);
ASSERT(buff != NULL, "pkAllocString failed.");
char* buff = pkRealloc(vm, NULL, file_size + 1);
ASSERT(buff != NULL, "pkRealloc failed.");
clearerr(file);
size_t read = fread(buff, sizeof(char), file_size, file);

View File

@ -508,7 +508,7 @@ Instance* newInstance(PKVM* vm, Class* cls) {
inst->attribs = newMap(vm);
if (cls->new_fn != NULL) {
inst->native = cls->new_fn();
inst->native = cls->new_fn(vm);
} else {
inst->native = NULL;
}
@ -753,6 +753,8 @@ static uint32_t _hashObject(Object* obj) {
Range* range = (Range*)obj;
return utilHashNumber(range->from) ^ utilHashNumber(range->to);
}
default: break;
}
UNREACHABLE();
@ -1020,7 +1022,7 @@ void freeObject(PKVM* vm, Object* self) {
case OBJ_INST: {
Instance* inst = (Instance*)self;
if (inst->cls->delete_fn != NULL) {
inst->cls->delete_fn(inst->native);
inst->cls->delete_fn(vm, inst->native);
}
DEALLOCATE(vm, inst, Instance);
return;
@ -1506,7 +1508,6 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff,
}
case OBJ_UPVALUE: {
const Upvalue* upvalue = (const Upvalue*)obj;
pkByteBufferAddString(buff, vm, "[Upvalue]", 9);
return;
}

View File

@ -339,7 +339,7 @@ PkResult vmCallFunction(PKVM* vm, Closure* fn, int argc, Var* argv, Var* ret) {
// Import and return the Module object with the [path] string. If the path
// starts with with './' or '../' we'll only try relative imports, otherwise
// we'll search native modules first and then at relative path.
static inline Var importModule(PKVM* vm, String* from, String* path) {
Var vmImportModule(PKVM* vm, String* from, String* path) {
ASSERT((path != NULL) && (path->length > 0), OOPS);
bool is_relative = path->data[0] == '.';
@ -362,19 +362,13 @@ static inline Var importModule(PKVM* vm, String* from, String* path) {
(from) ? from->data : NULL, path->data);
if (_resolved == NULL) { // Can't resolve a relative module.
pkDeAllocString(vm, _resolved);
if (from) {
VM_SET_ERROR(vm, stringFormat(vm, "Cannot resolve a relative import "
"path \"@\" from \"@\".", path, from));
} else {
VM_SET_ERROR(vm, stringFormat(vm, "Cannot resolve a relative import "
"path \"@\"", path));
}
pkRealloc(vm, _resolved, 0);
VM_SET_ERROR(vm, stringFormat(vm, "Cannot import module '@'", path));
return VAR_NULL;
}
String* resolved = newString(vm, _resolved);
pkDeAllocString(vm, _resolved);
pkRealloc(vm, _resolved, 0);
// If the script already imported and cached, return it.
Var entry = mapGet(vm->modules, VAR_OBJ(resolved));
@ -405,14 +399,14 @@ static inline Var importModule(PKVM* vm, String* from, String* path) {
break;
}
// Make a new module and to compile it.
// Make a new module, compile and cache it.
module = newModule(vm);
module->path = resolved;
vmPushTempRef(vm, &module->_super); // module.
{
initializeScript(vm, module);
PkResult result = compile(vm, module, source, NULL);
pkDeAllocString(vm, source);
pkRealloc(vm, source, 0);
if (result == PK_RESULT_SUCCESS) {
vmRegisterModule(vm, module, resolved);
} else {
@ -948,14 +942,14 @@ L_vm_main_loop:
// Capture the vaupes.
for (int i = 0; i < fn->upvalue_count; i++) {
uint8_t is_immediate = READ_BYTE();
uint8_t index = READ_BYTE();
uint8_t idx = READ_BYTE();
if (is_immediate) {
// rbp[0] is the return value, rbp + 1 is the first local and so on.
closure->upvalues[i] = captureUpvalue(vm, fiber, (rbp + 1 + index));
closure->upvalues[i] = captureUpvalue(vm, fiber, (rbp + 1 + idx));
} else {
// The upvalue is already captured by the current function, reuse it.
closure->upvalues[i] = frame->closure->upvalues[index];
closure->upvalues[i] = frame->closure->upvalues[idx];
}
}
@ -1025,7 +1019,7 @@ L_vm_main_loop:
String* name = moduleGetStringAt(module, (int)index);
ASSERT(name != NULL, OOPS);
Var _imported = importModule(vm, module->path, name);
Var _imported = vmImportModule(vm, module->path, name);
CHECK_ERROR();
ASSERT(IS_OBJ_TYPE(_imported, OBJ_MODULE), OOPS);
@ -1485,8 +1479,8 @@ L_do_call:
OPCODE(POSITIVE):
{
// Don't pop yet, we need the reference for gc.
Var self = PEEK(-1);
Var result = varPositive(vm, self);
Var self_ = PEEK(-1);
Var result = varPositive(vm, self_);
DROP(); // self
PUSH(result);
@ -1497,8 +1491,8 @@ L_do_call:
OPCODE(NEGATIVE):
{
// Don't pop yet, we need the reference for gc.
Var self = PEEK(-1);
Var result = varNegative(vm, self);
Var v = PEEK(-1);
Var result = varNegative(vm, v);
DROP(); // self
PUSH(result);
@ -1509,8 +1503,8 @@ L_do_call:
OPCODE(NOT):
{
// Don't pop yet, we need the reference for gc.
Var self = PEEK(-1);
Var result = varNot(vm, self);
Var v = PEEK(-1);
Var result = varNot(vm, v);
DROP(); // self
PUSH(result);
@ -1521,8 +1515,8 @@ L_do_call:
OPCODE(BIT_NOT):
{
// Don't pop yet, we need the reference for gc.
Var self = PEEK(-1);
Var result = varBitNot(vm, self);
Var v = PEEK(-1);
Var result = varBitNot(vm, v);
DROP(); // self
PUSH(result);

View File

@ -235,4 +235,10 @@ PkResult vmCallFunction(PKVM* vm, Closure* fn, int argc, Var* argv, Var* ret);
PkResult vmCallMethod(PKVM* vm, Var self, Closure* fn,
int argc, Var* argv, Var* ret);
// Import a module with the [path] and return it. The path sepearation should
// be '/' example: to import module "a.b" the [path] should be "a/b".
// If the [from] is not NULL, it'll be used for relative path search.
// On failure, it'll set an error and return VAR_NULL.
Var vmImportModule(PKVM* vm, String* from, String* path);
#endif // PK_VM_H

View File

@ -94,13 +94,13 @@ typedef void (*pkWriteFn) (PKVM* vm, const char* text);
// A function callback to read a line from stdin. The returned string shouldn't
// contain a line ending (\n or \r\n). The returned string **must** be
// allocated with pkAllocString() and the VM will claim the ownership of the
// allocated with pkRealloc() and the VM will claim the ownership of the
// string.
typedef char* (*pkReadFn) (PKVM* vm);
// Load and return the script. Called by the compiler to fetch initial source
// code and source for import statements. Return NULL to indicate failure to
// load. Otherwise the string **must** be allocated with pkAllocString() and
// load. Otherwise the string **must** be allocated with pkRealloc() and
// the VM will claim the ownership of the string.
typedef char* (*pkLoadScriptFn) (PKVM* vm, const char* path);
@ -108,18 +108,20 @@ typedef char* (*pkLoadScriptFn) (PKVM* vm, const char* path);
// be either path to a script or NULL if [path] is relative to cwd. The return
// value should be a normalized absolute path of the [path]. Return NULL to
// indicate failure to resolve. Othrewise the string **must** be allocated with
// pkAllocString() and the VM will claim the ownership of the string.
// pkRealloc() and the VM will claim the ownership of the string.
typedef char* (*pkResolvePathFn) (PKVM* vm, const char* from,
const char* path);
// A function callback to allocate and return a new instance of the registered
// class. Which will be called when the instance is constructed. The returned/
// data is expected to be alive till the delete callback occurs.
typedef void* (*pkNewInstanceFn) ();
typedef void* (*pkNewInstanceFn) (PKVM* vm);
// A function callback to de-allocate the aloocated native instance of the
// registered class.
typedef void (*pkDeleteInstanceFn) (void*);
// A function callback to de-allocate the allocated native instance of the
// registered class. This function is invoked at the GC execution. No object
// allocations are allowed during it, so **NEVER** allocate any objects
// inside them.
typedef void (*pkDeleteInstanceFn) (PKVM* vm, void*);
/*****************************************************************************/
/* POCKETLANG TYPES */
@ -197,26 +199,20 @@ PK_PUBLIC PKVM* pkNewVM(PkConfiguration* config);
// Clean the VM and dispose all the resources allocated by the VM.
PK_PUBLIC void pkFreeVM(PKVM* vm);
// This will register all the standard libraries of pocketlang to the VM. The
// libraries are not part of the core implementation, and one can use just the
// bare bone of the language without any libraries if they don't call this.
PK_PUBLIC void pkRegisterLibs(PKVM* vm);
// Update the user data of the vm.
PK_PUBLIC void pkSetUserData(PKVM* vm, void* user_data);
// Returns the associated user data.
PK_PUBLIC void* pkGetUserData(const PKVM* vm);
// Allocate memory with [size] and return it. This function should be called
// Invoke pocketlang's allocator directly. This function should be called
// when the host application want to send strings to the PKVM that are claimed
// by the VM once the caller returned it.
PK_PUBLIC char* pkAllocString(PKVM* vm, size_t size);
// Complementary function to pkAllocString. This should not be called if the
// string is returned to the VM. Since PKVM will claim the ownership and
// deallocate the string itself.
PK_PUBLIC void pkDeAllocString(PKVM* vm, char* ptr);
// by the VM once the caller returned it. For other uses you **should** call
// pkRealloc with [size] 0 to cleanup, otherwise there will be a memory leak.
//
// Internally it'll call `pkReallocFn` function that was provided in the
// configuration.
PK_PUBLIC void* pkRealloc(PKVM* vm, void* ptr, size_t size);
// Release the handle and allow its value to be garbage collected. Always call
// this for every handles before freeing the VM.
@ -391,6 +387,11 @@ PK_PUBLIC bool pkGetAttribute(PKVM* vm, int instance, const char* name,
// Place the [self] instance at the [index] slot.
PK_PUBLIC void pkPlaceSelf(PKVM* vm, int index);
// Import a module with the [path] and place it at [index] slot. The path
// sepearation should be '/'. Example: to import module "foo.bar" the [path]
// should be "foo/bar". On failure, it'll set an error and return false.
PK_PUBLIC bool pkImportModule(PKVM* vm, const char* path, int index);
// Set the [index] slot's value as the class of the [instance].
PK_PUBLIC void pkGetClass(PKVM* vm, int instance, int index);

View File

@ -8,14 +8,20 @@
#include "libs.h"
#endif
void registerModuleDummy(PKVM* vm);
void registerModuleMath(PKVM* vm);
void registerModuleTime(PKVM* vm);
void registerModuleIO(PKVM* vm);
void registerModulePath(PKVM* vm);
void registerModuleMath(PKVM* vm);
void registerModuleDummy(PKVM* vm);
void pkRegisterLibs(PKVM* vm) {
registerModuleDummy(vm);
void registerLibs(PKVM* vm) {
registerModuleMath(vm);
registerModuleTime(vm);
registerModuleIO(vm);
registerModulePath(vm);
registerModuleMath(vm);
registerModuleDummy(vm);
}
void cleanupLibs(PKVM* vm) {
}

View File

@ -113,13 +113,6 @@
#endif // PK_AMALGAMATED
// Allocate a new module object of type [Ty].
#define NEW_OBJ(Ty) (Ty*) malloc(sizeof(Ty))
// Dellocate module object, allocated by NEW_OBJ(). Called by the freeObj
// callback.
#define FREE_OBJ(ptr) free(ptr)
/*****************************************************************************/
/* SHARED FUNCTIONS */
/*****************************************************************************/
@ -133,4 +126,11 @@
// inorder to use the import statements.
char* pathResolveImport(PKVM * vm, const char* from, const char* path);
// Register all the the libraries to the PKVM.
void registerLibs(PKVM* vm);
// Cleanup registered libraries call this only if the libraries were registered
// with registerLibs() function.
void cleanupLibs(PKVM* vm);
#endif // LIBS_H

View File

@ -14,16 +14,15 @@ typedef struct {
double val;
} Dummy;
void* _newDummy() {
Dummy* dummy = NEW_OBJ(Dummy);
ASSERT(dummy != NULL, "malloc failed.");
void* _newDummy(PKVM* vm) {
Dummy* dummy = pkRealloc(vm, NULL, sizeof(Dummy));
ASSERT(dummy != NULL, "pkRealloc failed.");
dummy->val = 0;
return dummy;
}
void _deleteDummy(void* ptr) {
Dummy* dummy = (Dummy*)ptr;
FREE_OBJ(dummy);
void _deleteDummy(PKVM* vm, void* ptr) {
pkRealloc(vm, ptr, 0);
}
DEF(_dummyInit, "") {
@ -146,6 +145,7 @@ DEF(_dummyCallMethod,
}
void registerModuleDummy(PKVM* vm) {
PkHandle* dummy = pkNewModule(vm, "dummy");
pkModuleAddFunction(vm, dummy, "afunc", _dummyFunction, 2);

View File

@ -37,21 +37,22 @@ typedef struct {
bool closed; // True if the file isn't closed yet.
} File;
void* _newFile() {
File* file = NEW_OBJ(File);
void* _newFile(PKVM* vm) {
File* file = pkRealloc(vm, NULL, sizeof(File));
ASSERT(file != NULL, "pkRealloc failed.");
file->closed = true;
file->mode = FMODE_NONE;
file->fp = NULL;
return file;
}
void _deleteFile(void* ptr) {
void _deleteFile(PKVM* vm, void* ptr) {
File* file = (File*)ptr;
if (!file->closed) {
if (fclose(file->fp) != 0) { /* TODO: error! */ }
file->closed = true;
}
FREE_OBJ(file);
pkRealloc(vm, file, 0);
}
/*****************************************************************************/
@ -123,8 +124,10 @@ DEF(_fileRead, "") {
}
// TODO: this is temporary.
char buff[2048];
fread((void*)buff, sizeof(char), sizeof(buff), file->fp);
size_t read = fread((void*)buff, sizeof(char), sizeof(buff), file->fp);
(void) read;
pkSetSlotString(vm, 0, (const char*)buff);
}

View File

@ -20,7 +20,7 @@
#include <errno.h>
#include <sys/stat.h>
#if defined(_WIN32)
#ifdef _WIN32
#include <windows.h>
#endif
@ -109,7 +109,7 @@ static char* tryImportPaths(PKVM* vm, char* path, char* buff) {
char* ret = NULL;
if (path_size != 0) {
ret = pkAllocString(vm, path_size + 1);
ret = pkRealloc(vm, NULL, path_size + 1);
memcpy(ret, buff, path_size + 1);
}
return ret;
@ -138,7 +138,7 @@ char* pathResolveImport(PKVM* vm, const char* from, const char* path) {
if (cwk_path_is_absolute(path)) {
// buff1 = normalized path. +1 for null terminator.
size_t size = cwk_path_normalize(path, buff1, sizeof(buff1)) + 1;
cwk_path_normalize(path, buff1, sizeof(buff1));
pathFixWindowsSeperator(buff1);
return tryImportPaths(vm, buff1, buff2);
@ -150,7 +150,7 @@ char* pathResolveImport(PKVM* vm, const char* from, const char* path) {
pathAbs(path, buff1, sizeof(buff1));
// buff2 = normalized path. +1 for null terminator.
size_t size = cwk_path_normalize(buff1, buff2, sizeof(buff2)) + 1;
cwk_path_normalize(buff1, buff2, sizeof(buff2));
pathFixWindowsSeperator(buff2);
return tryImportPaths(vm, buff2, buff1);
@ -173,7 +173,7 @@ char* pathResolveImport(PKVM* vm, const char* from, const char* path) {
cwk_path_join(buff1, path, buff2, sizeof(buff2));
// buff1 = normalized absolute path. +1 for null terminator
size_t size = cwk_path_normalize(buff2, buff1, sizeof(buff1)) + 1;
cwk_path_normalize(buff2, buff1, sizeof(buff1));
pathFixWindowsSeperator(buff1);
return tryImportPaths(vm, buff1, buff2);

60
src/libs/std_time.c Normal file
View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2020-2022 Thakee Nathees
* Copyright (c) 2021-2022 Pocketlang Contributors
* Distributed Under The MIT License
*/
#include <time.h>
#ifndef PK_AMALGAMATED
#include "libs.h"
#endif
#ifdef _WIN32
#include <windows.h>
#endif
#if !defined(_MSC_VER) && !(defined(_WIN32) && defined(__TINYC__))
#include <unistd.h> // usleep
#endif
DEF(_timeEpoch,
"time() -> num\n"
"Returns the number of seconds since the Epoch, 1970-01-01 "
"00:00:00 +0000 (UTC).") {
pkSetSlotNumber(vm, 0, (double) time(NULL));
}
DEF(_timeClock,
"clock() -> num\n"
"Returns the number of clocks passed divied by CLOCKS_PER_SEC.") {
pkSetSlotNumber(vm, 0, (double) clock() / CLOCKS_PER_SEC);
}
DEF(_timeSleep,
"sleep(t:num) -> num\n"
"Sleep for [t] milliseconds.") {
double t;
pkValidateSlotNumber(vm, 1, &t);
#if defined(_MSC_VER) || (defined(_WIN32) && defined(__TINYC__))
// Sleep(milli seconds)
Sleep((DWORD) t);
#else // usleep(micro seconds)
usleep(((useconds_t) (t)) * 1000);
#endif
}
void registerModuleTime(PKVM* vm) {
PkHandle* time = pkNewModule(vm, "time");
pkModuleAddFunction(vm, time, "epoch", _timeEpoch, 0);
pkModuleAddFunction(vm, time, "sleep", _timeSleep, 1);
pkModuleAddFunction(vm, time, "clock", _timeClock, 0);
pkRegisterModule(vm, time);
pkReleaseHandle(vm, time);
}

View File

@ -1,4 +1,4 @@
from lang import clock
from time import clock
start = clock()
N = 50000000; factors = []

View File

@ -1,5 +1,5 @@
from lang import clock
from time import clock
def fib(n)
if n < 2 then return n end

View File

@ -1,4 +1,4 @@
from lang import clock
from time import clock
from math import floor
def reverse_list(list)

View File

@ -1,4 +1,4 @@
from lang import clock
from time import clock
start = clock()
l = []

View File

@ -1,4 +1,4 @@
from lang import clock
from time import clock
def is_prime(n)
if n < 2 then return false end

View File

@ -2,7 +2,7 @@
## Run the script in pocketlang with
## toc enabled VS toc disabled
from lang import clock
from time import clock
N = 20000

View File

@ -4,7 +4,7 @@ import lang
import lang, path
import lang as o, path as p
from lang import write
from lang import clock as c
from time import sleep as s
import basics
import controlflow as if_test