mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-03-04 13:15:55 +08:00
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:
parent
710d0c47f3
commit
6a22653263
@ -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)
|
||||
|
@ -60,7 +60,6 @@ static PKVM* intializePocketVM() {
|
||||
}
|
||||
|
||||
PKVM* vm = pkNewVM(&config);
|
||||
pkRegisterLibs(vm);
|
||||
return vm;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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):
|
||||
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_SOURCE_DIR, file))
|
||||
if file.endswith('.c'): sources.append(join(POCKET_ROOT, dir, file))
|
||||
return sources
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -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
|
||||
|
||||
|
@ -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():
|
||||
|
@ -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,8 +762,34 @@ 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),
|
||||
semanticError(compiler, makeErrToken(parser),
|
||||
"Invalid escape character.");
|
||||
return;
|
||||
}
|
||||
@ -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,8 +2383,6 @@ 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);
|
||||
@ -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);
|
||||
|
||||
|
@ -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,8 +392,8 @@ 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;
|
||||
@ -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
|
||||
|
@ -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,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).
|
||||
#define ERR_INVALID_ARG_TYPE(ty_name) \
|
||||
#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 argument $.", \
|
||||
VM_SET_ERROR(vm, stringFormat(vm, "Expected a '$' at slot $.", \
|
||||
ty_name, buff)); \
|
||||
} while (false)
|
||||
|
||||
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
60
src/libs/std_time.c
Normal 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);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
from lang import clock
|
||||
from time import clock
|
||||
|
||||
start = clock()
|
||||
N = 50000000; factors = []
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
from lang import clock
|
||||
from time import clock
|
||||
|
||||
def fib(n)
|
||||
if n < 2 then return n end
|
||||
|
@ -1,4 +1,4 @@
|
||||
from lang import clock
|
||||
from time import clock
|
||||
from math import floor
|
||||
|
||||
def reverse_list(list)
|
||||
|
@ -1,4 +1,4 @@
|
||||
from lang import clock
|
||||
from time import clock
|
||||
start = clock()
|
||||
|
||||
l = []
|
||||
|
@ -1,4 +1,4 @@
|
||||
from lang import clock
|
||||
from time import clock
|
||||
|
||||
def is_prime(n)
|
||||
if n < 2 then return false end
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user