Minor code enhancements (read bellow)

- Warnings were fixed
- Libraries are registered internally when PKVM created
  and cleanedup when PKVM freed (if PK_NO_LIBS not defined)
- Lang.clock() moved to time module and sleep, epoch time
  were added.
- Support both upper case and lower case hex literals
- Support hex excaped characters inside strings (ex: "\x41")
- Native api for import modules added `pkImportModule(...)`
- pkAllocString, pkDeallocString are changed to pkRealloc.
- NewInstance, DeleteInstance functions now take PKVM however
  delete function should not allocate any memory since it's
  invoked at the GC execution.
This commit is contained in:
Thakee Nathees 2022-05-20 20:00:24 +05:30
parent 710d0c47f3
commit 6a22653263
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 ```ruby
# Python like import statement. # Python like import statement.
from lang import clock as now from time import clock as now
# A recursive fibonacci function. # A recursive fibonacci function.
def fib(n) def fib(n)

View File

@ -60,7 +60,6 @@ static PKVM* intializePocketVM() {
} }
PKVM* vm = pkNewVM(&config); PKVM* vm = pkNewVM(&config);
pkRegisterLibs(vm);
return 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 foo.bar # Import module bar from foo directory
import baz # If baz is a directory it'll import baz/_init.pk 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, ... import .foo # ./foo.pk or ./foo/_init.pk or ./foo.dll, ...
# ^ meaning parent directory relative to this script. # ^ 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 file is not intended to be included in other files at the moment.
THIS_PATH = abspath(dirname(__file__)) 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") JS_API_PATH = join(THIS_PATH, "io_api.js")
MAIN_C = join(THIS_PATH, "main.c") MAIN_C = join(THIS_PATH, "main.c")
TARGET_DIR = join(THIS_PATH, "../static/wasm/") TARGET_DIR = join(THIS_PATH, "../static/wasm/")
@ -27,7 +27,7 @@ def main():
os.mkdir(TARGET_DIR) os.mkdir(TARGET_DIR)
sources = ' '.join(collect_source_files()) sources = ' '.join(collect_source_files())
include = '-I' + join(POCKET_SOURCE_DIR, 'include/') include = '-I' + join(POCKET_ROOT, 'include/')
output = join(TARGET_DIR, TARGET_NAME) output = join(TARGET_DIR, TARGET_NAME)
exports = "\"EXPORTED_RUNTIME_METHODS=['ccall','cwrap']\"" exports = "\"EXPORTED_RUNTIME_METHODS=['ccall','cwrap']\""
js_api = JS_API_PATH js_api = JS_API_PATH
@ -39,9 +39,10 @@ def main():
def collect_source_files(): def collect_source_files():
sources = [] sources = []
for file in os.listdir(POCKET_SOURCE_DIR): for dir in ('core/', 'libs/'):
for file in os.listdir(join(POCKET_ROOT, dir)):
if isdir(file): continue if isdir(file): continue
if file.endswith('.c'): sources.append(join(POCKET_SOURCE_DIR, file)) if file.endswith('.c'): sources.append(join(POCKET_ROOT, dir, file))
return sources return sources
if __name__ == "__main__": if __name__ == "__main__":

View File

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

View File

@ -8,6 +8,9 @@
## To get the trace report redefine TRACE_MEMORY as 1 at the ## To get the trace report redefine TRACE_MEMORY as 1 at the
## pk_internal.h and compile pocketlang. ## pk_internal.h and compile pocketlang.
raise "This script Need refactor after removing pkAllocString " + \
"and adding pkRealloc"
import sys import sys
def detect_leak(): 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 void setNextToken(Parser* parser, _TokenType type);
static bool matchChar(Parser* parser, char c); 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) { static void eatString(Compiler* compiler, bool single_quote) {
Parser* parser = &compiler->parser; Parser* parser = &compiler->parser;
@ -693,15 +715,15 @@ static void eatString(Compiler* compiler, bool single_quote) {
if (parser->si_depth < MAX_STR_INTERP_DEPTH) { if (parser->si_depth < MAX_STR_INTERP_DEPTH) {
tk_type = TK_STRING_INTERP; tk_type = TK_STRING_INTERP;
char c = peekChar(parser); char c2 = peekChar(parser);
if (c == '{') { // Expression interpolation (ie. "${expr}"). if (c2 == '{') { // Expression interpolation (ie. "${expr}").
eatChar(parser); eatChar(parser);
parser->si_depth++; parser->si_depth++;
parser->si_quote[parser->si_depth - 1] = quote; parser->si_quote[parser->si_depth - 1] = quote;
parser->si_open_brace[parser->si_depth - 1] = 0; parser->si_open_brace[parser->si_depth - 1] = 0;
} else { // Name Interpolation. } else { // Name Interpolation.
if (!utilIsName(c)) { if (!utilIsName(c2)) {
syntaxError(compiler, makeErrToken(parser), syntaxError(compiler, makeErrToken(parser),
"Expected '{' or identifier after '$'."); "Expected '{' or identifier after '$'.");
return; return;
@ -740,8 +762,34 @@ static void eatString(Compiler* compiler, bool single_quote) {
// '$' In pocketlang string is used for interpolation. // '$' In pocketlang string is used for interpolation.
case '$': pkByteBufferWrite(&buff, parser->vm, '$'); break; 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: default:
syntaxError(compiler, makeErrToken(parser), semanticError(compiler, makeErrToken(parser),
"Invalid escape character."); "Invalid escape character.");
return; return;
} }
@ -807,10 +855,6 @@ static void eatName(Parser* parser) {
static void eatNumber(Compiler* compiler) { static void eatNumber(Compiler* compiler) {
Parser* parser = &compiler->parser; 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')) #define IS_BIN_CHAR(c) (((c) == '0') || ((c) == '1'))
Var value = VAR_NULL; // The number value. Var value = VAR_NULL; // The number value.
@ -855,8 +899,8 @@ static void eatNumber(Compiler* compiler) {
uint64_t hex = 0; uint64_t hex = 0;
c = peekChar(parser); c = peekChar(parser);
// The first digit should be either hex digit. // The first digit should be hex digit.
if (!IS_HEX_CHAR(c)) { if (!_isCharHex(c)) {
syntaxError(compiler, makeErrToken(parser), "Invalid hex literal."); syntaxError(compiler, makeErrToken(parser), "Invalid hex literal.");
return; return;
@ -864,7 +908,7 @@ static void eatNumber(Compiler* compiler) {
do { do {
// Consume the next digit. // Consume the next digit.
c = peekChar(parser); c = peekChar(parser);
if (!IS_HEX_CHAR(c)) break; if (!_isCharHex(c)) break;
eatChar(parser); eatChar(parser);
// Check the length of the binary literal. // Check the length of the binary literal.
@ -876,10 +920,7 @@ static void eatNumber(Compiler* compiler) {
} }
// "Append" the next digit at the end. // "Append" the next digit at the end.
uint8_t append_val = ('0' <= c && c <= '9') hex = (hex << 4) | _charHexVal(c);
? (uint8_t)(c - '0')
: (uint8_t)((c - 'a') + 10);
hex = (hex << 4) | append_val;
} while (true); } while (true);
@ -930,7 +971,6 @@ static void eatNumber(Compiler* compiler) {
setNextValueToken(parser, TK_NUMBER, value); setNextValueToken(parser, TK_NUMBER, value);
#undef IS_BIN_CHAR #undef IS_BIN_CHAR
#undef IS_HEX_CHAR
} }
// Read and ignore chars till it reach new line or EOF. // 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, static int findBuiltinClass(const PKVM* vm,
const char* name, uint32_t length) { const char* name, uint32_t length) {
for (int i = 0; i < PK_INSTANCE; i++) { 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)) { if (IS_CSTR_EQ(vm->builtin_classes[i]->name, name, length)) {
return i; return i;
} }
@ -2344,8 +2383,6 @@ static void compilerAddForward(Compiler* compiler, int instruction, Fn* fn,
// Add a literal constant to module literals and return it's index. // Add a literal constant to module literals and return it's index.
static int compilerAddConstant(Compiler* compiler, Var value) { static int compilerAddConstant(Compiler* compiler, Var value) {
pkVarBuffer* constants = &compiler->module->constants;
uint32_t index = moduleAddConstant(compiler->parser.vm, uint32_t index = moduleAddConstant(compiler->parser.vm,
compiler->module, value); compiler->module, value);
checkMaxConstantsReached(compiler, index); checkMaxConstantsReached(compiler, index);
@ -2899,7 +2936,7 @@ static Token compileImportPath(Compiler* compiler) {
// Create constant pool entry for the path string. // Create constant pool entry for the path string.
int index = 0; int index = 0;
moduleAddString(compiler->module, compiler->parser.vm, moduleAddString(compiler->module, compiler->parser.vm,
buff.data, buff.count - 1, &index); (const char*) buff.data, buff.count - 1, &index);
pkByteBufferClear(&buff, vm); pkByteBufferClear(&buff, vm);

View File

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

View File

@ -15,6 +15,23 @@
#include "vm.h" #include "vm.h"
#endif #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) \ #define CHECK_ARG_NULL(name) \
ASSERT((name) != NULL, "Argument " #name " was NULL."); 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* stdinRead(PKVM* vm);
static char* loadScript(PKVM* vm, const char* path); 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."); ASSERT(vm->config.realloc_fn != NULL, "PKVM's allocator was NULL.");
#if TRACE_MEMORY #if TRACE_MEMORY
void* ptr = vm->config.realloc_fn(NULL, size, vm->config.user_data); void* newptr = vm->config.realloc_fn(ptr, size, vm->config.user_data);
printf("[alloc string] alloc : %p = %+li bytes\n", ptr, (long) size); printf("[pkRealloc] %p -> %p %+li bytes\n", ptr, newptr, (long) size);
return (char*) ptr; return ptr;
#else #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 #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 pkNewConfiguration() {
PkConfiguration config; PkConfiguration config;
memset(&config, 0, sizeof(config)); memset(&config, 0, sizeof(config));
@ -110,7 +103,7 @@ PkConfiguration pkNewConfiguration() {
config.stdout_write = stdoutWrite; config.stdout_write = stdoutWrite;
config.stderr_write = stderrWrite; config.stderr_write = stderrWrite;
config.stdin_read = stdinRead; config.stdin_read = stdinRead;
#if !defined(PK_NO_LIBS) #ifndef PK_NO_LIBS
config.resolve_path_fn = pathResolveImport; config.resolve_path_fn = pathResolveImport;
#endif #endif
config.load_script_fn = loadScript; config.load_script_fn = loadScript;
@ -146,11 +139,20 @@ PKVM* pkNewVM(PkConfiguration* config) {
} }
initializeCore(vm); initializeCore(vm);
#ifndef PK_NO_LIBS
registerLibs(vm);
#endif
return vm; return vm;
} }
void pkFreeVM(PKVM* vm) { void pkFreeVM(PKVM* vm) {
#ifndef PK_NO_LIBS
cleanupLibs(vm);
#endif
Object* obj = vm->first; Object* obj = vm->first;
while (obj != NULL) { while (obj != NULL) {
Object* next = obj->next; Object* next = obj->next;
@ -334,7 +336,7 @@ PkResult pkRunFile(PKVM* vm, const char* path) {
// Set module path and and deallocate resolved. // Set module path and and deallocate resolved.
String* script_path = newString(vm, resolved_); String* script_path = newString(vm, resolved_);
vmPushTempRef(vm, &script_path->_super); // script_path. vmPushTempRef(vm, &script_path->_super); // script_path.
pkDeAllocString(vm, resolved_); pkRealloc(vm, resolved_, 0);
module->path = script_path; module->path = script_path;
vmPopTempRef(vm); // script_path. vmPopTempRef(vm); // script_path.
@ -352,7 +354,7 @@ PkResult pkRunFile(PKVM* vm, const char* path) {
} }
} else { } else {
result = compile(vm, module, source, NULL); result = compile(vm, module, source, NULL);
pkDeAllocString(vm, source); pkRealloc(vm, source, 0);
} }
if (result == PK_RESULT_SUCCESS) { if (result == PK_RESULT_SUCCESS) {
@ -449,21 +451,21 @@ PkResult pkRunREPL(PKVM* vm) {
if (line_length >= 1 && *(line + line_length - 1) == EOF) { if (line_length >= 1 && *(line + line_length - 1) == EOF) {
printfn(vm, "\n"); printfn(vm, "\n");
result = PK_RESULT_SUCCESS; result = PK_RESULT_SUCCESS;
pkDeAllocString(vm, line); pkRealloc(vm, line, 0);
break; break;
} }
// If the line is empty, we don't have to compile it. // If the line is empty, we don't have to compile it.
if (isStringEmpty(line)) { if (isStringEmpty(line)) {
if (need_more_lines) ASSERT(lines.count != 0, OOPS); if (need_more_lines) ASSERT(lines.count != 0, OOPS);
pkDeAllocString(vm, line); pkRealloc(vm, line, 0);
continue; continue;
} }
// Add the line to the lines buffer. // Add the line to the lines buffer.
if (lines.count != 0) pkByteBufferWrite(&lines, vm, '\n'); if (lines.count != 0) pkByteBufferWrite(&lines, vm, '\n');
pkByteBufferAddString(&lines, vm, line, (uint32_t) line_length); pkByteBufferAddString(&lines, vm, line, (uint32_t) line_length);
pkDeAllocString(vm, line); pkRealloc(vm, line, 0);
pkByteBufferWrite(&lines, vm, '\0'); pkByteBufferWrite(&lines, vm, '\0');
// Compile the buffer to the module. // Compile the buffer to the module.
@ -546,11 +548,11 @@ bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max) {
} }
// Set error for incompatible type provided as an argument. (TODO: got type). // Set error for incompatible type provided as an argument. (TODO: got type).
#define ERR_INVALID_ARG_TYPE(ty_name) \ #define ERR_INVALID_SLOT_TYPE(ty_name) \
do { \ do { \
char buff[STR_INT_BUFF_SIZE]; \ char buff[STR_INT_BUFF_SIZE]; \
sprintf(buff, "%d", arg); \ sprintf(buff, "%d", arg); \
VM_SET_ERROR(vm, stringFormat(vm, "Expected a '$' at argument $.", \ VM_SET_ERROR(vm, stringFormat(vm, "Expected a '$' at slot $.", \
ty_name, buff)); \ ty_name, buff)); \
} while (false) } while (false)
@ -562,7 +564,7 @@ PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int arg, bool* value) {
Var val = ARG(arg); Var val = ARG(arg);
if (!IS_BOOL(val)) { if (!IS_BOOL(val)) {
ERR_INVALID_ARG_TYPE("Boolean"); ERR_INVALID_SLOT_TYPE("Boolean");
return false; return false;
} }
@ -576,7 +578,7 @@ PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int arg, double* value) {
Var val = ARG(arg); Var val = ARG(arg);
if (!IS_NUM(val)) { if (!IS_NUM(val)) {
ERR_INVALID_ARG_TYPE("Number"); ERR_INVALID_SLOT_TYPE("Number");
return false; return false;
} }
@ -591,7 +593,7 @@ PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int arg, const char** value,
Var val = ARG(arg); Var val = ARG(arg);
if (!IS_OBJ_TYPE(val, OBJ_STRING)) { if (!IS_OBJ_TYPE(val, OBJ_STRING)) {
ERR_INVALID_ARG_TYPE("String"); ERR_INVALID_SLOT_TYPE("String");
return false; return false;
} }
String* str = (String*)AS_OBJ(val); String* str = (String*)AS_OBJ(val);
@ -604,7 +606,7 @@ bool pkValidateSlotType(PKVM* vm, int arg, PkVarType type) {
CHECK_FIBER_EXISTS(vm); CHECK_FIBER_EXISTS(vm);
VALIDATE_ARGC(arg); VALIDATE_ARGC(arg);
if (getVarType(ARG(arg)) != type) { if (getVarType(ARG(arg)) != type) {
ERR_INVALID_ARG_TYPE(getPkVarTypeName(type)); ERR_INVALID_SLOT_TYPE(getPkVarTypeName(type));
return false; return false;
} }
@ -620,7 +622,7 @@ PK_PUBLIC bool pkValidateSlotInstanceOf(PKVM* vm, int arg, int cls) {
if (!varIsType(vm, instance, class_)) { if (!varIsType(vm, instance, class_)) {
// If [class_] is not a valid class, it's already an error. // If [class_] is not a valid class, it's already an error.
if (VM_HAS_ERROR(vm)) return false; 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; return false;
} }
@ -632,7 +634,6 @@ bool pkIsSlotInstanceOf(PKVM* vm, int inst, int cls, bool* val) {
VALIDATE_SLOT_INDEX(inst); VALIDATE_SLOT_INDEX(inst);
VALIDATE_SLOT_INDEX(cls); VALIDATE_SLOT_INDEX(cls);
Var instance = SLOT(inst), class_ = SLOT(cls);
*val = varIsType(vm, inst, cls); *val = varIsType(vm, inst, cls);
return !VM_HAS_ERROR(vm); return !VM_HAS_ERROR(vm);
} }
@ -876,6 +877,19 @@ void pkPlaceSelf(PKVM* vm, int index) {
SET_SLOT(index, vm->fiber->self); 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) { void pkGetClass(PKVM* vm, int instance, int index) {
CHECK_FIBER_EXISTS(vm); CHECK_FIBER_EXISTS(vm);
VALIDATE_SLOT_INDEX(instance); VALIDATE_SLOT_INDEX(instance);
@ -928,7 +942,7 @@ static char* stdinRead(PKVM* vm) {
} while (c != EOF); } while (c != EOF);
pkByteBufferWrite(&buff, vm, '\0'); pkByteBufferWrite(&buff, vm, '\0');
char* str = pkAllocString(vm, buff.count); char* str = pkRealloc(vm, NULL, buff.count);
memcpy(str, buff.data, buff.count); memcpy(str, buff.data, buff.count);
return str; return str;
} }
@ -946,8 +960,8 @@ static char* loadScript(PKVM* vm, const char* path) {
fseek(file, 0, SEEK_SET); fseek(file, 0, SEEK_SET);
// Allocate string + 1 for the NULL terminator. // Allocate string + 1 for the NULL terminator.
char* buff = pkAllocString(vm, file_size + 1); char* buff = pkRealloc(vm, NULL, file_size + 1);
ASSERT(buff != NULL, "pkAllocString failed."); ASSERT(buff != NULL, "pkRealloc failed.");
clearerr(file); clearerr(file);
size_t read = fread(buff, sizeof(char), file_size, 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); inst->attribs = newMap(vm);
if (cls->new_fn != NULL) { if (cls->new_fn != NULL) {
inst->native = cls->new_fn(); inst->native = cls->new_fn(vm);
} else { } else {
inst->native = NULL; inst->native = NULL;
} }
@ -753,6 +753,8 @@ static uint32_t _hashObject(Object* obj) {
Range* range = (Range*)obj; Range* range = (Range*)obj;
return utilHashNumber(range->from) ^ utilHashNumber(range->to); return utilHashNumber(range->from) ^ utilHashNumber(range->to);
} }
default: break;
} }
UNREACHABLE(); UNREACHABLE();
@ -1020,7 +1022,7 @@ void freeObject(PKVM* vm, Object* self) {
case OBJ_INST: { case OBJ_INST: {
Instance* inst = (Instance*)self; Instance* inst = (Instance*)self;
if (inst->cls->delete_fn != NULL) { if (inst->cls->delete_fn != NULL) {
inst->cls->delete_fn(inst->native); inst->cls->delete_fn(vm, inst->native);
} }
DEALLOCATE(vm, inst, Instance); DEALLOCATE(vm, inst, Instance);
return; return;
@ -1506,7 +1508,6 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff,
} }
case OBJ_UPVALUE: { case OBJ_UPVALUE: {
const Upvalue* upvalue = (const Upvalue*)obj;
pkByteBufferAddString(buff, vm, "[Upvalue]", 9); pkByteBufferAddString(buff, vm, "[Upvalue]", 9);
return; 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 // Import and return the Module object with the [path] string. If the path
// starts with with './' or '../' we'll only try relative imports, otherwise // starts with with './' or '../' we'll only try relative imports, otherwise
// we'll search native modules first and then at relative path. // 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); ASSERT((path != NULL) && (path->length > 0), OOPS);
bool is_relative = path->data[0] == '.'; 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); (from) ? from->data : NULL, path->data);
if (_resolved == NULL) { // Can't resolve a relative module. if (_resolved == NULL) { // Can't resolve a relative module.
pkDeAllocString(vm, _resolved); pkRealloc(vm, _resolved, 0);
if (from) { VM_SET_ERROR(vm, stringFormat(vm, "Cannot import module '@'", path));
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));
}
return VAR_NULL; return VAR_NULL;
} }
String* resolved = newString(vm, _resolved); String* resolved = newString(vm, _resolved);
pkDeAllocString(vm, _resolved); pkRealloc(vm, _resolved, 0);
// If the script already imported and cached, return it. // If the script already imported and cached, return it.
Var entry = mapGet(vm->modules, VAR_OBJ(resolved)); Var entry = mapGet(vm->modules, VAR_OBJ(resolved));
@ -405,14 +399,14 @@ static inline Var importModule(PKVM* vm, String* from, String* path) {
break; break;
} }
// Make a new module and to compile it. // Make a new module, compile and cache it.
module = newModule(vm); module = newModule(vm);
module->path = resolved; module->path = resolved;
vmPushTempRef(vm, &module->_super); // module. vmPushTempRef(vm, &module->_super); // module.
{ {
initializeScript(vm, module); initializeScript(vm, module);
PkResult result = compile(vm, module, source, NULL); PkResult result = compile(vm, module, source, NULL);
pkDeAllocString(vm, source); pkRealloc(vm, source, 0);
if (result == PK_RESULT_SUCCESS) { if (result == PK_RESULT_SUCCESS) {
vmRegisterModule(vm, module, resolved); vmRegisterModule(vm, module, resolved);
} else { } else {
@ -948,14 +942,14 @@ L_vm_main_loop:
// Capture the vaupes. // Capture the vaupes.
for (int i = 0; i < fn->upvalue_count; i++) { for (int i = 0; i < fn->upvalue_count; i++) {
uint8_t is_immediate = READ_BYTE(); uint8_t is_immediate = READ_BYTE();
uint8_t index = READ_BYTE(); uint8_t idx = READ_BYTE();
if (is_immediate) { if (is_immediate) {
// rbp[0] is the return value, rbp + 1 is the first local and so on. // 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 { } else {
// The upvalue is already captured by the current function, reuse it. // 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); String* name = moduleGetStringAt(module, (int)index);
ASSERT(name != NULL, OOPS); ASSERT(name != NULL, OOPS);
Var _imported = importModule(vm, module->path, name); Var _imported = vmImportModule(vm, module->path, name);
CHECK_ERROR(); CHECK_ERROR();
ASSERT(IS_OBJ_TYPE(_imported, OBJ_MODULE), OOPS); ASSERT(IS_OBJ_TYPE(_imported, OBJ_MODULE), OOPS);
@ -1485,8 +1479,8 @@ L_do_call:
OPCODE(POSITIVE): OPCODE(POSITIVE):
{ {
// Don't pop yet, we need the reference for gc. // Don't pop yet, we need the reference for gc.
Var self = PEEK(-1); Var self_ = PEEK(-1);
Var result = varPositive(vm, self); Var result = varPositive(vm, self_);
DROP(); // self DROP(); // self
PUSH(result); PUSH(result);
@ -1497,8 +1491,8 @@ L_do_call:
OPCODE(NEGATIVE): OPCODE(NEGATIVE):
{ {
// Don't pop yet, we need the reference for gc. // Don't pop yet, we need the reference for gc.
Var self = PEEK(-1); Var v = PEEK(-1);
Var result = varNegative(vm, self); Var result = varNegative(vm, v);
DROP(); // self DROP(); // self
PUSH(result); PUSH(result);
@ -1509,8 +1503,8 @@ L_do_call:
OPCODE(NOT): OPCODE(NOT):
{ {
// Don't pop yet, we need the reference for gc. // Don't pop yet, we need the reference for gc.
Var self = PEEK(-1); Var v = PEEK(-1);
Var result = varNot(vm, self); Var result = varNot(vm, v);
DROP(); // self DROP(); // self
PUSH(result); PUSH(result);
@ -1521,8 +1515,8 @@ L_do_call:
OPCODE(BIT_NOT): OPCODE(BIT_NOT):
{ {
// Don't pop yet, we need the reference for gc. // Don't pop yet, we need the reference for gc.
Var self = PEEK(-1); Var v = PEEK(-1);
Var result = varBitNot(vm, self); Var result = varBitNot(vm, v);
DROP(); // self DROP(); // self
PUSH(result); 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, PkResult vmCallMethod(PKVM* vm, Var self, Closure* fn,
int argc, Var* argv, Var* ret); 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 #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 // 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 // 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. // string.
typedef char* (*pkReadFn) (PKVM* vm); typedef char* (*pkReadFn) (PKVM* vm);
// Load and return the script. Called by the compiler to fetch initial source // 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 // 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. // the VM will claim the ownership of the string.
typedef char* (*pkLoadScriptFn) (PKVM* vm, const char* path); 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 // 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 // value should be a normalized absolute path of the [path]. Return NULL to
// indicate failure to resolve. Othrewise the string **must** be allocated with // 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, typedef char* (*pkResolvePathFn) (PKVM* vm, const char* from,
const char* path); const char* path);
// A function callback to allocate and return a new instance of the registered // 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/ // class. Which will be called when the instance is constructed. The returned/
// data is expected to be alive till the delete callback occurs. // 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 // A function callback to de-allocate the allocated native instance of the
// registered class. // registered class. This function is invoked at the GC execution. No object
typedef void (*pkDeleteInstanceFn) (void*); // allocations are allowed during it, so **NEVER** allocate any objects
// inside them.
typedef void (*pkDeleteInstanceFn) (PKVM* vm, void*);
/*****************************************************************************/ /*****************************************************************************/
/* POCKETLANG TYPES */ /* POCKETLANG TYPES */
@ -197,26 +199,20 @@ PK_PUBLIC PKVM* pkNewVM(PkConfiguration* config);
// Clean the VM and dispose all the resources allocated by the VM. // Clean the VM and dispose all the resources allocated by the VM.
PK_PUBLIC void pkFreeVM(PKVM* 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. // Update the user data of the vm.
PK_PUBLIC void pkSetUserData(PKVM* vm, void* user_data); PK_PUBLIC void pkSetUserData(PKVM* vm, void* user_data);
// Returns the associated user data. // Returns the associated user data.
PK_PUBLIC void* pkGetUserData(const PKVM* vm); 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 // when the host application want to send strings to the PKVM that are claimed
// by the VM once the caller returned it. // by the VM once the caller returned it. For other uses you **should** call
PK_PUBLIC char* pkAllocString(PKVM* vm, size_t size); // pkRealloc with [size] 0 to cleanup, otherwise there will be a memory leak.
//
// Complementary function to pkAllocString. This should not be called if the // Internally it'll call `pkReallocFn` function that was provided in the
// string is returned to the VM. Since PKVM will claim the ownership and // configuration.
// deallocate the string itself. PK_PUBLIC void* pkRealloc(PKVM* vm, void* ptr, size_t size);
PK_PUBLIC void pkDeAllocString(PKVM* vm, char* ptr);
// Release the handle and allow its value to be garbage collected. Always call // Release the handle and allow its value to be garbage collected. Always call
// this for every handles before freeing the VM. // 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. // Place the [self] instance at the [index] slot.
PK_PUBLIC void pkPlaceSelf(PKVM* vm, int index); 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]. // Set the [index] slot's value as the class of the [instance].
PK_PUBLIC void pkGetClass(PKVM* vm, int instance, int index); PK_PUBLIC void pkGetClass(PKVM* vm, int instance, int index);

View File

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

View File

@ -113,13 +113,6 @@
#endif // PK_AMALGAMATED #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 */ /* SHARED FUNCTIONS */
/*****************************************************************************/ /*****************************************************************************/
@ -133,4 +126,11 @@
// inorder to use the import statements. // inorder to use the import statements.
char* pathResolveImport(PKVM * vm, const char* from, const char* path); 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 #endif // LIBS_H

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
from lang import clock from time import clock
def fib(n) def fib(n)
if n < 2 then return n end 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 from math import floor
def reverse_list(list) def reverse_list(list)

View File

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

View File

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

View File

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

View File

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