Native types & File object implementations.

This commit is contained in:
Thakee Nathees 2021-06-21 18:32:20 +05:30
parent a21bd98945
commit 600f972927
29 changed files with 932 additions and 460 deletions

View File

@ -1,34 +0,0 @@
/*
* Copyright (c) 2020-2021 Thakee Nathees
* Distributed Under The MIT License
*/
// This file will include all source files from the cli sub-directories here
// (thirdparty/modules) into a single file. That'll make it easy to compile
// with the command `gcc cli/*.c ...` instead of having to add every single
// sub-directory to the list of source directory.
/*****************************************************************************/
/* THIRD PARTY */
/*****************************************************************************/
// Library : cwalk
// Source : https://github.com/likle/cwalk
// Doc : https://likle.github.io/cwalk/
// About : Path library for C/C++. Cross-Platform for Windows, MacOS and
// Linux. Supports UNIX and Windows path styles on those platforms.
#include "modules/thirdparty/cwalk/cwalk.c"
/*****************************************************************************/
/* CLI MODULES */
/*****************************************************************************/
#include "modules/path.c"
/*****************************************************************************/
/* MODULES REGISTER */
/*****************************************************************************/
void registerModules(PKVM* vm) {
registerModulePath(vm);
}

View File

@ -3,38 +3,40 @@
* Distributed Under The MIT License
*/
#ifndef COMMON_H
#define COMMON_H
// A collection of reusable macros that pocketlang use. This file doesn't have
// any dependencies, you can just drag and drop this file in your project if
// you want to use these macros.
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <assert.h>
#ifndef PK_COMMON_H
#define PK_COMMON_H
#include <pocketlang.h>
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
#define CLI_NOTICE \
"PocketLang " PK_VERSION_STRING " (https://github.com/ThakeeNathees/pocketlang/)\n" \
"Copyright(c) 2020 - 2021 ThakeeNathees.\n" \
"Free and open source software under the terms of the MIT license.\n"
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#elif defined(__clang__)
#pragma clang diagnostic ignored "-Wint-to-pointer-cast"
#pragma clang diagnostic ignored "-Wunused-parameter"
#endif
// Note that the cli itself is not a part of the pocketlang compiler, instead
// it's a host application to run pocketlang from the command line. We're
// embedding the pocketlang VM and we can only use its public APIs, not any
// internals of it, including assertion macros. So we're re-defining those
// macros here (like if it's a new project).
#include <stdio.h> //< Only needed here for ASSERT() macro and for release mode
//< TODO; macro use this to print a crash report.
/*****************************************************************************/
/* COMMON MACROS */
/*****************************************************************************/
#define TOSTRING(x) #x
#define STRINGIFY(x) TOSTRING(x)
// These macros below are copied from pocketlang at "src/common.h". See above
// for not re-using these macros.
// CONCAT_LINE(X) will result evaluvated X<__LINE__>.
#define __CONCAT_LINE_INTERNAL_R(a, b) a ## b
#define __CONCAT_LINE_INTERNAL_F(a, b) __CONCAT_LINE_INTERNAL_R(a, b)
#define CONCAT_LINE(X) __CONCAT_LINE_INTERNAL_F(X, __LINE__)
// The internal assertion macro, this will print error and break regardless of
// the build target (debug or release). Use ASSERT() for debug assertion and
// use __ASSERT() for TODOs.
// use __ASSERT() for TODOs and assertions in public methods (to indicate that
// the host application did something wrong).
#define __ASSERT(condition, message) \
do { \
if (!(condition)) { \
@ -55,6 +57,11 @@
#define DEBUG_BREAK() __builtin_trap()
#endif
// This will terminate the compilation if the [condition] is false, because of
// char _assertion_failure_<__LINE__>[-1] evaluated.
#define STATIC_ASSERT(condition) \
static char CONCAT_LINE(_assertion_failure_)[2*!!(condition) - 1]
#define ASSERT(condition, message) __ASSERT(condition, message)
#define ASSERT_INDEX(index, size) \
@ -70,41 +77,77 @@
#else
#define STATIC_ASSERT(condition) NO_OP
#define DEBUG_BREAK() NO_OP
#define ASSERT(condition, message) NO_OP
#define ASSERT_INDEX(index, size) NO_OP
// Reference : https://github.com/wren-lang/
#if defined( _MSC_VER )
#if defined(_MSC_VER)
#define UNREACHABLE() __assume(0)
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define UNREACHABLE() __builtin_unreachable()
#elif defined(__EMSCRIPTEN__) || defined(__clang__)
#if __has_builtin(__builtin_unreachable)
#define UNREACHABLE() __builtin_unreachable()
#else
#define UNREACHABLE() NO_OP
#endif
#else
#define UNREACHABLE() NO_OP
#endif
#endif // DEBUG
#if defined( _MSC_VER )
#if defined(_MSC_VER)
#define forceinline __forceinline
#else
#define forceinline __attribute__((always_inline))
#endif
// Using __ASSERT() for make it to crash in release binary too.
#define TODO __ASSERT(false, "TODO: It hasn't been implemented yet.")
// Using __ASSERT() for make it crash in release binary too.
#define TODO __ASSERT(false, "TODO: It hasn't implemented yet.")
#define OOPS "Oops a bug!! report please."
#define TOSTRING(x) #x
#define STRINGIFY(x) TOSTRING(x)
// The formated string to convert double to string. It'll be with the minimum
// length string representation of either a regular float or a scientific
// notation (at most 15 decimal points).
// Reference: https://www.cplusplus.com/reference/cstdio/printf/
#define DOUBLE_FMT "%.16g"
/*****************************************************************************/
/* CLI DEFINES */
/*****************************************************************************/
// Double number to string buffer size, used in sprintf with DOUBLE_FMT.
// A largest number : "-1.234567890123456e+308"
// + 1 fot sign '+' or '-'
// + 16 fot significant digits
// + 1 for decimal point '.'
// + 1 for exponent char 'e'
// + 1 for sign of exponent
// + 3 for the exponent digits
// + 1 for null byte '\0'
#define STR_DBL_BUFF_SIZE 24
// FIXME: the vm user data of cli.
typedef struct {
bool repl_mode;
} VmUserData;
// Integer number to string buffer size, used in sprintf with format "%d".
// The minimum 32 bit integer = -2147483648
// + 1 for sign '-'
// + 10 for digits
// + 1 for null byte '\0'
#define STR_INT_BUFF_SIZE 12
#endif // COMMON_H
// Integer number (double) to hex string buffer size.
// The maximum value an unsigned 64 bit integer can get is
// 0xffffffffffffffff which is 16 characters.
// + 16 for hex digits
// + 1 for sign '-'
// + 2 for '0x' prefix
// + 1 for null byte '\0'
#define STR_HEX_BUFF_SIZE 20
// Integer number (double) to bin string buffer size.
// The maximum value an unsigned 64 bit integer can get is 0b11111... 64 1s.
// + 64 for bin digits
// + 1 for sign '-'
// + 2 for '0b' prefix
// + 1 for null byte '\0'
#define STR_BIN_BUFF_SIZE 68
#endif //PK_COMMON_H

37
cli/internal.h Normal file
View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2020-2021 Thakee Nathees
* Distributed Under The MIT License
*/
#ifndef COMMON_H
#define COMMON_H
#include <pocketlang.h>
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
// Note that the cli itself is not a part of the pocketlang compiler, instead
// it's a host application to run pocketlang from the command line. We're
// embedding the pocketlang VM and we can only use its public APIs, not any
// internals of it, including assertion macros. So that we've copyied the
// "common.h" header. This can be moved to "src/include/common.h" and include
// as optional header, which is something I don't like because it makes
// pocketlang contain 2 headers (we'll always try to be minimal).
#include "common.h"
#define CLI_NOTICE \
"PocketLang " PK_VERSION_STRING " (https://github.com/ThakeeNathees/pocketlang/)\n" \
"Copyright(c) 2020 - 2021 ThakeeNathees.\n" \
"Free and open source software under the terms of the MIT license.\n"
// FIXME: the vm user data of cli.
typedef struct {
bool repl_mode;
} VmUserData;
#endif // COMMON_H

View File

@ -3,22 +3,15 @@
* Distributed Under The MIT License
*/
#include "common.h"
#include "internal.h"
#include "modules.h"
// FIXME: Everything below here is temporary and for testing.
void repl(PKVM* vm, const PkCompileOptions* options);
const char* read_line(uint32_t* length);
void registerModules(PKVM* vm);
// Path public functions (TODO: should I add a header for that?)
bool pathIsAbsolute(const char* path);
void pathGetDirName(const char* path, size_t* length);
size_t pathNormalize(const char* path, char* buff, size_t buff_size);
size_t pathJoin(const char* from, const char* path, char* buffer,
size_t buff_size);
// ---------------------------------------
void onResultDone(PKVM* vm, PkStringPtr result) {
@ -143,6 +136,10 @@ int main(int argc, char** argv) {
config.error_fn = errorFunction;
config.write_fn = writeFunction;
config.read_fn = readFunction;
config.free_inst_fn = freeObj;
config.inst_name_fn = getObjName;
config.load_script_fn = loadScript;
config.resolve_path_fn = resolvePath;

405
cli/modules.c Normal file
View File

@ -0,0 +1,405 @@
/*
* Copyright (c) 2021 Thakee Nathees
* Distributed Under The MIT License
*/
#include "modules.h"
// 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)
void freeObj(PKVM* vm, void* instance) {
Obj* obj = (Obj*)instance;
// TODO: assert obj type is valid.
// If the file isn't closed, close it to flush it's buffer.
if (obj->type == OBJ_FILE) {
File* file = (File*)obj;
if (!file->closed) {
if (fclose(file->fp) != 0) { /* TODO: error! */ }
file->closed = true;
}
}
FREE_OBJ(obj);
}
const char* getObjName(uint32_t id) {
switch ((ObjType)id) {
case OBJ_FILE: return "File";
}
return NULL;
}
/*****************************************************************************/
/* PATH MODULE */
/*****************************************************************************/
#include "thirdparty/cwalk/cwalk.h"
#if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
#include "thirdparty/dirent/dirent.h"
#else
#include <dirent.h>
#endif
#if defined(_WIN32)
#include <windows.h>
#include <direct.h>
#define get_cwd _getcwd
#else
#include <unistd.h>
#define get_cwd getcwd
#endif
// The cstring pointer buffer size used in path.join(p1, p2, ...). Tune this
// value as needed.
#define MAX_JOIN_PATHS 8
/*****************************************************************************/
/* PUBLIC FUNCTIONS */
/*****************************************************************************/
bool pathIsAbsolute(const char* path) {
return cwk_path_is_absolute(path);
}
void pathGetDirName(const char* path, size_t* length) {
cwk_path_get_dirname(path, length);
}
size_t pathNormalize(const char* path, char* buff, size_t buff_size) {
return cwk_path_normalize(path, buff, buff_size);
}
size_t pathJoin(const char* path_a, const char* path_b, char* buffer,
size_t buff_size) {
return cwk_path_join(path_a, path_b, buffer, buff_size);
}
/*****************************************************************************/
/* PATH INTERNAL FUNCTIONS */
/*****************************************************************************/
static inline bool pathIsFileExists(const char* path) {
FILE* file = fopen(path, "r");
if (file != NULL) {
fclose(file);
return true;
}
return false;
}
// Reference: https://stackoverflow.com/a/12510903/10846399
static inline bool pathIsDirectoryExists(const char* path) {
DIR* dir = opendir(path);
if (dir) { /* Directory exists. */
closedir(dir);
return true;
} else if (errno == ENOENT) { /* Directory does not exist. */
} else { /* opendir() failed for some other reason. */
}
return false;
}
static inline bool pathIsExists(const char* path) {
return pathIsFileExists(path) || pathIsDirectoryExists(path);
}
static inline size_t pathAbs(const char* path, char* buff, size_t buff_size) {
char cwd[FILENAME_MAX];
if (get_cwd(cwd, sizeof(cwd)) == NULL) {
// TODO: handle error.
}
return cwk_path_get_absolute(cwd, path, buff, buff_size);
}
/*****************************************************************************/
/* PATH MODULES FUNCTIONS */
/*****************************************************************************/
static void _pathSetStyleUnix(PKVM* vm) {
bool value;
if (!pkGetArgBool(vm, 1, &value)) return;
cwk_path_set_style((value) ? CWK_STYLE_UNIX : CWK_STYLE_WINDOWS);
}
static void _pathGetCWD(PKVM* vm) {
char cwd[FILENAME_MAX];
if (get_cwd(cwd, sizeof(cwd)) == NULL) {
// TODO: Handle error.
}
pkReturnString(vm, cwd);
}
static void _pathAbspath(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
char abspath[FILENAME_MAX];
size_t len = pathAbs(path, abspath, sizeof(abspath));
pkReturnStringLength(vm, abspath, len);
}
static void _pathRelpath(PKVM* vm) {
const char* from, * path;
if (!pkGetArgString(vm, 1, &from, NULL)) return;
if (!pkGetArgString(vm, 2, &path, NULL)) return;
char abs_from[FILENAME_MAX];
pathAbs(from, abs_from, sizeof(abs_from));
char abs_path[FILENAME_MAX];
pathAbs(path, abs_path, sizeof(abs_path));
char result[FILENAME_MAX];
size_t len = cwk_path_get_relative(abs_from, abs_path,
result, sizeof(result));
pkReturnStringLength(vm, result, len);
}
static void _pathJoin(PKVM* vm) {
const char* paths[MAX_JOIN_PATHS + 1]; // +1 for NULL.
int argc = pkGetArgc(vm);
if (argc > MAX_JOIN_PATHS) {
pkSetRuntimeError(vm, "Cannot join more than " STRINGIFY(MAX_JOIN_PATHS)
"paths.");
return;
}
for (int i = 0; i < argc; i++) {
pkGetArgString(vm, i + 1, &paths[i], NULL);
}
paths[argc] = NULL;
char result[FILENAME_MAX];
size_t len = cwk_path_join_multiple(paths, result, sizeof(result));
pkReturnStringLength(vm, result, len);
}
static void _pathNormalize(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
char result[FILENAME_MAX];
size_t len = cwk_path_normalize(path, result, sizeof(result));
pkReturnStringLength(vm, result, len);
}
static void _pathBaseName(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
const char* base_name;
size_t length;
cwk_path_get_basename(path, &base_name, &length);
pkReturnString(vm, base_name);
}
static void _pathDirName(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
size_t length;
cwk_path_get_dirname(path, &length);
pkReturnStringLength(vm, path, length);
}
static void _pathIsPathAbs(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
pkReturnBool(vm, cwk_path_is_absolute(path));
}
static void _pathGetExtension(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
const char* ext;
size_t length;
if (cwk_path_get_extension(path, &ext, &length)) {
pkReturnStringLength(vm, ext, length);
} else {
pkReturnStringLength(vm, NULL, 0);
}
}
static void _pathExists(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
pkReturnBool(vm, pathIsExists(path));
}
static void _pathIsFile(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
pkReturnBool(vm, pathIsFileExists(path));
}
static void _pathIsDir(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
pkReturnBool(vm, pathIsDirectoryExists(path));
}
void registerModulePath(PKVM* vm) {
PkHandle* path = pkNewModule(vm, "path");
pkModuleAddFunction(vm, path, "setunix", _pathSetStyleUnix, 1);
pkModuleAddFunction(vm, path, "getcwd", _pathGetCWD, 0);
pkModuleAddFunction(vm, path, "abspath", _pathAbspath, 1);
pkModuleAddFunction(vm, path, "relpath", _pathRelpath, 2);
pkModuleAddFunction(vm, path, "join", _pathJoin, -1);
pkModuleAddFunction(vm, path, "normalize", _pathNormalize, 1);
pkModuleAddFunction(vm, path, "basename", _pathBaseName, 1);
pkModuleAddFunction(vm, path, "dirname", _pathDirName, 1);
pkModuleAddFunction(vm, path, "isabspath", _pathIsPathAbs, 1);
pkModuleAddFunction(vm, path, "getext", _pathGetExtension, 1);
pkModuleAddFunction(vm, path, "exists", _pathExists, 1);
pkModuleAddFunction(vm, path, "isfile", _pathIsFile, 1);
pkModuleAddFunction(vm, path, "isdir", _pathIsDir, 1);
pkReleaseHandle(vm, path);
}
/*****************************************************************************/
/* FILE MODULE */
/*****************************************************************************/
static void _fileOpen(PKVM* vm) {
// TODO: handle arg range using pocketlang native api.
// 1 <= argc <= 2
int argc = pkGetArgc(vm);
if (argc == 0) {
pkSetRuntimeError(vm, "Expected at least 1 argument");
return;
} else if (argc > 2) {
pkSetRuntimeError(vm, "Expected at least 2 arguments");
return;
}
const char* path;
if (!pkGetArgString(vm, 1, &path, NULL)) return;
const char* mode_str = "r";
FileAccessMode mode = FMODE_READ;
if (argc == 2) {
if (!pkGetArgString(vm, 2, &mode_str, NULL)) return;
// Check if the mode string is valid, and update the mode value.
do {
if (strcmp(mode_str, "r") == 0) { mode = FMODE_READ; break; }
if (strcmp(mode_str, "w") == 0) { mode = FMODE_WRITE; break; }
if (strcmp(mode_str, "a") == 0) { mode = FMODE_APPEND; break; }
if (strcmp(mode_str, "r+") == 0) { mode = FMODE_READ_EXT; break; }
if (strcmp(mode_str, "w+") == 0) { mode = FMODE_WRITE_EXT; break; }
if (strcmp(mode_str, "a+") == 0) { mode = FMODE_APPEND_EXT; break; }
// TODO: (fmt, ...) va_arg for runtime error public api.
// If we reached here, that means it's an invalid mode string.
pkSetRuntimeError(vm, "Invalid mode string");
return;
} while (false);
}
FILE* fp = fopen(path, mode_str);
if (fp != NULL) {
File* file = NEW_OBJ(File);
file->fp = fp;
file->mode = mode;
file->closed = false;
pkReturnInstNative(vm, (void*)file, OBJ_FILE);
} else {
pkReturnNull(vm);
}
}
static void _fileRead(PKVM* vm) {
File* file;
if (!pkGetArgInst(vm, 1, OBJ_FILE, (void**)&file)) return;
if (file->closed) {
pkSetRuntimeError(vm, "Cannot read from a closed file.");
return;
}
if ((file->mode != FMODE_READ) && ((_FMODE_EXT & file->mode) == 0)) {
pkSetRuntimeError(vm, "File is not readable.");
return;
}
// TODO: this is temproary.
char buff[2048];
fread((void*)buff, sizeof(char), sizeof(buff), file->fp);
pkReturnString(vm, (const char*)buff);
}
static void _fileWrite(PKVM* vm) {
File* file;
const char* text; uint32_t length;
if (!pkGetArgInst(vm, 1, OBJ_FILE, (void**)&file)) return;
if (!pkGetArgString(vm, 2, &text, &length)) return;
if (file->closed) {
pkSetRuntimeError(vm, "Cannot write to a closed file.");
return;
}
if ((file->mode != FMODE_WRITE) && ((_FMODE_EXT & file->mode) == 0)) {
pkSetRuntimeError(vm, "File is not writable.");
return;
}
fwrite(text, sizeof(char), (size_t)length, file->fp);
}
static void _fileClose(PKVM* vm) {
File* file;
if (!pkGetArgInst(vm, 1, OBJ_FILE, (void**)&file)) return;
if (file->closed) {
pkSetRuntimeError(vm, "File already closed.");
return;
}
if (fclose(file->fp) != 0) {
pkSetRuntimeError(vm, "fclose() failed!\n" \
" at " __FILE__ ":" STRINGIFY(__LINE__) ".");
}
file->closed = true;
}
void registerModuleFile(PKVM* vm) {
PkHandle* file = pkNewModule(vm, "File");
pkModuleAddFunction(vm, file, "open", _fileOpen, -1);
pkModuleAddFunction(vm, file, "read", _fileRead, 1);
pkModuleAddFunction(vm, file, "write", _fileWrite, 2);
pkModuleAddFunction(vm, file, "close", _fileClose, 1);
pkReleaseHandle(vm, file);
}
/*****************************************************************************/
/* REGISTER MODULES */
/*****************************************************************************/
void registerModules(PKVM* vm) {
registerModuleFile(vm);
registerModulePath(vm);
}

73
cli/modules.h Normal file
View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2021 Thakee Nathees
* Distributed Under The MIT License
*/
#include "internal.h"
/*****************************************************************************/
/* MODULE OBJECTS */
/*****************************************************************************/
// Type enums of cli module objects.
typedef enum {
OBJ_FILE = 1,
} ObjType;
// The abstract type of the objects.
typedef struct {
ObjType type;
} Obj;
// File access mode.
typedef enum {
FMODE_READ = (1 << 0),
FMODE_WRITE = (1 << 1),
FMODE_APPEND = (1 << 2),
_FMODE_EXT = (1 << 3),
FMODE_READ_EXT = (_FMODE_EXT | FMODE_READ),
FMODE_WRITE_EXT = (_FMODE_EXT | FMODE_WRITE),
FMODE_APPEND_EXT = (_FMODE_EXT | FMODE_APPEND),
} FileAccessMode;
// Str | If already exists | If does not exist |
// -----+-------------------+-------------------|
// 'r' | read from start | failure to open |
// 'w' | destroy contents | create new |
// 'a' | write to end | create new |
// 'r+' | read from start | error |
// 'w+' | destroy contents | create new |
// 'a+' | write to end | create new |
// A wrapper around the FILE* for the File module.
typedef struct {
Obj _super;
FILE* fp; // C file poinnter.
FileAccessMode mode; // Access mode of the file.
bool closed; // True if the file isn't closed yet.
} File;
/*****************************************************************************/
/* MODULE PUBLIC FUNCTIONS */
/*****************************************************************************/
// The free callback of the object, that'll called by pocketlang when a
// pocketlang native instance garbage collected.
void freeObj(PKVM* vm, void* instance);
// The native instance get_name callback used to get the name of a native
// instance from pocketlang. Here the id we're using is the ObjType enum.
const char* getObjName(uint32_t id);
// Registers all the cli modules.
void registerModules(PKVM* vm);
// 'path' moudle public functions used at various cli functions.
bool pathIsAbsolute(const char* path);
void pathGetDirName(const char* path, size_t* length);
size_t pathNormalize(const char* path, char* buff, size_t buff_size);
size_t pathJoin(const char* from, const char* path, char* buffer,
size_t buff_size);

View File

@ -1,248 +0,0 @@
/*
* Copyright (c) 2021 Thakee Nathees
* Distributed Under The MIT License
*/
#include <errno.h>
#include <pocketlang.h>
#include <stdio.h> /* defines FILENAME_MAX */
#include "thirdparty/cwalk/cwalk.h"
#if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__))
#include "thirdparty/dirent/dirent.h"
#else
#include <dirent.h>
#endif
#if defined(_WIN32)
#include <windows.h>
#include <direct.h>
#define get_cwd _getcwd
#else
#include <unistd.h>
#define get_cwd getcwd
#endif
// TODO: this macros should be moved to a general place of in cli.
#define TOSTRING(x) #x
#define STRINGIFY(x) TOSTRING(x)
// The cstring pointer buffer size used in path.join(p1, p2, ...). Tune this
// value as needed.
#define MAX_JOIN_PATHS 8
/*****************************************************************************/
/* PUBLIC FUNCTIONS */
/*****************************************************************************/
bool pathIsAbsolute(const char* path) {
return cwk_path_is_absolute(path);
}
void pathGetDirName(const char* path, size_t* length) {
cwk_path_get_dirname(path, length);
}
size_t pathNormalize(const char* path, char* buff, size_t buff_size) {
return cwk_path_normalize(path, buff, buff_size);
}
size_t pathJoin(const char* path_a, const char* path_b, char* buffer,
size_t buff_size) {
return cwk_path_join(path_a, path_b, buffer, buff_size);
}
/*****************************************************************************/
/* INTERNAL FUNCTIONS */
/*****************************************************************************/
static inline bool pathIsFileExists(const char* path) {
FILE* file = fopen(path, "r");
if (file != NULL) {
fclose(file);
return true;
}
return false;
}
// Reference: https://stackoverflow.com/a/12510903/10846399
static inline bool pathIsDirectoryExists(const char* path) {
DIR* dir = opendir(path);
if (dir) { /* Directory exists. */
closedir(dir);
return true;
} else if (errno == ENOENT) { /* Directory does not exist. */
} else { /* opendir() failed for some other reason. */
}
return false;
}
static inline bool pathIsExists(const char* path) {
return pathIsFileExists(path) || pathIsDirectoryExists(path);
}
static inline size_t pathAbs(const char* path, char* buff, size_t buff_size) {
char cwd[FILENAME_MAX];
if (get_cwd(cwd, sizeof(cwd)) == NULL) {
// TODO: handle error.
}
return cwk_path_get_absolute(cwd, path, buff, buff_size);
}
/*****************************************************************************/
/* MODULE FUNCTIONS */
/*****************************************************************************/
static void _pathSetStyleUnix(PKVM* vm) {
bool value;
if (!pkGetArgBool(vm, 1, &value)) return;
cwk_path_set_style((value)? CWK_STYLE_UNIX : CWK_STYLE_WINDOWS);
}
static void _pathGetCWD(PKVM* vm) {
char cwd[FILENAME_MAX];
if (get_cwd(cwd, sizeof(cwd)) == NULL) {
// TODO: Handle error.
}
pkReturnString(vm, cwd);
}
static void _pathAbspath(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path)) return;
char abspath[FILENAME_MAX];
size_t len = pathAbs(path, abspath, sizeof(abspath));
pkReturnStringLength(vm, abspath, len);
}
static void _pathRelpath(PKVM* vm) {
const char *from, *path;
if (!pkGetArgString(vm, 1, &from)) return;
if (!pkGetArgString(vm, 2, &path)) return;
char abs_from[FILENAME_MAX];
pathAbs(from, abs_from, sizeof(abs_from));
char abs_path[FILENAME_MAX];
pathAbs(path, abs_path, sizeof(abs_path));
char result[FILENAME_MAX];
size_t len = cwk_path_get_relative(abs_from, abs_path,
result, sizeof(result));
pkReturnStringLength(vm, result, len);
}
static void _pathJoin(PKVM* vm) {
const char* paths[MAX_JOIN_PATHS + 1]; // +1 for NULL.
int argc = pkGetArgc(vm);
if (argc > MAX_JOIN_PATHS) {
pkSetRuntimeError(vm, "Cannot join more than " STRINGIFY(MAX_JOIN_PATHS)
"paths.");
return;
}
for (int i = 0; i < argc; i++) {
pkGetArgString(vm, i+1, &paths[i]);
}
paths[argc] = NULL;
char result[FILENAME_MAX];
size_t len = cwk_path_join_multiple(paths, result, sizeof(result));
pkReturnStringLength(vm, result, len);
}
static void _pathNormalize(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path)) return;
char result[FILENAME_MAX];
size_t len = cwk_path_normalize(path, result, sizeof(result));
pkReturnStringLength(vm, result, len);
}
static void _pathBaseName(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path)) return;
const char* base_name;
size_t length;
cwk_path_get_basename(path, &base_name, &length);
pkReturnString(vm, base_name);
}
static void _pathDirName(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path)) return;
size_t length;
cwk_path_get_dirname(path, &length);
pkReturnStringLength(vm, path, length);
}
static void _pathIsPathAbs(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path)) return;
pkReturnBool(vm, cwk_path_is_absolute(path));
}
static void _pathGetExtension(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path)) return;
const char* ext;
size_t length;
if (cwk_path_get_extension(path, &ext, &length)) {
pkReturnStringLength(vm, ext, length);
} else {
pkReturnStringLength(vm, NULL, 0);
}
}
static void _pathExists(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path)) return;
pkReturnBool(vm, pathIsExists(path));
}
static void _pathIsFile(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path)) return;
pkReturnBool(vm, pathIsFileExists(path));
}
static void _pathIsDir(PKVM* vm) {
const char* path;
if (!pkGetArgString(vm, 1, &path)) return;
pkReturnBool(vm, pathIsDirectoryExists(path));
}
/*****************************************************************************/
/* REGISTER MODULE */
/*****************************************************************************/
void registerModulePath(PKVM* vm) {
PkHandle* path = pkNewModule(vm, "path");
pkModuleAddFunction(vm, path, "setunix", _pathSetStyleUnix, 1);
pkModuleAddFunction(vm, path, "getcwd", _pathGetCWD, 0);
pkModuleAddFunction(vm, path, "abspath", _pathAbspath, 1);
pkModuleAddFunction(vm, path, "relpath", _pathRelpath, 2);
pkModuleAddFunction(vm, path, "join", _pathJoin, -1);
pkModuleAddFunction(vm, path, "normalize", _pathNormalize, 1);
pkModuleAddFunction(vm, path, "basename", _pathBaseName, 1);
pkModuleAddFunction(vm, path, "dirname", _pathDirName, 1);
pkModuleAddFunction(vm, path, "isabspath", _pathIsPathAbs, 1);
pkModuleAddFunction(vm, path, "getext", _pathGetExtension, 1);
pkModuleAddFunction(vm, path, "exists", _pathExists, 1);
pkModuleAddFunction(vm, path, "isfile", _pathIsFile, 1);
pkModuleAddFunction(vm, path, "isdir", _pathIsDir, 1);
pkReleaseHandle(vm, path);
}

View File

@ -6,7 +6,7 @@
// The REPL (Read Evaluate Print Loop) implementation.
// https://en.wikipedia.org/wiki/Read-eval-print_loop.
#include "common.h"
#include "internal.h"
#include <ctype.h> // isspace
#include "utils.h"
@ -20,7 +20,9 @@
const char* read_line(uint32_t* length) {
const int size = 1024;
char* mem = (char*)malloc(size);
fgets(mem, size, stdin);
if (fgets(mem, size, stdin) == NULL) {
// TODO: handle error.
}
size_t len = strlen(mem);
// FIXME: handle \r\n, this is temp.
@ -61,7 +63,7 @@ int repl(PKVM* vm, const PkCompileOptions* options) {
user_data->repl_mode = true;
// Print the copyright and license notice.
printf("%s\n", CLI_NOTICE);
printf("%s", CLI_NOTICE);
// The main module that'll be used to compile and execute the input source.
PkHandle* module = pkNewModule(vm, "$(REPL)");

17
cli/thirdparty.c Normal file
View File

@ -0,0 +1,17 @@
/*
* Copyright (c) 2020-2021 Thakee Nathees
* Distributed Under The MIT License
*/
// This file will include all thirdparty source files from the thirdparty sub
// directories here into a single file. That'll make it easy to compile with
// the command `gcc cli/*.c ...` instead of having to add every single sub
// directory to the list of source directory.
// Library : cwalk
// Source : https://github.com/likle/cwalk
// Doc : https://likle.github.io/cwalk/
// About : Path library for C/C++. Cross-Platform for Windows, MacOS and
// Linux. Supports UNIX and Windows path styles on those platforms.
#include "thirdparty/cwalk/cwalk.c"

View File

@ -3,7 +3,7 @@
* Distributed Under The MIT License
*/
#include "common.h"
#include "internal.h"
typedef struct {
uint8_t* data;

View File

@ -157,6 +157,17 @@ typedef void (*pkWriteFn) (PKVM* vm, const char* text);
// contain a line ending (\n or \r\n).
typedef PkStringPtr (*pkReadFn) (PKVM* vm);
// A function callback, that'll be called when a native instance (wrapper) is
// freed by by the garbage collector, to indicate that pocketlang is done with
// the native instance.
typedef void (*pkFreeInstFn) (PKVM* vm, void* instance);
// A function callback to get the name of the native instance from pocketlang,
// using it's [id]. The returned string won't be copied by pocketlang so it's
// expected to be alived since the instance is alive and recomended to return
// a C literal string.
typedef const char* (*pkInstNameFn) (uint32_t id);
// A function callback symbol for clean/free the pkStringResult.
typedef void (*pkResultDoneFn) (PKVM* vm, PkStringPtr result);
@ -278,6 +289,9 @@ struct PkConfiguration {
pkWriteFn write_fn;
pkReadFn read_fn;
pkFreeInstFn free_inst_fn;
pkInstNameFn inst_name_fn;
pkResolvePathFn resolve_path_fn;
pkLoadScriptFn load_script_fn;
@ -337,7 +351,9 @@ PK_PUBLIC PkVar pkGetArg(const PKVM* vm, int arg);
PK_PUBLIC bool pkGetArgBool(PKVM* vm, int arg, bool* value);
PK_PUBLIC bool pkGetArgNumber(PKVM* vm, int arg, double* value);
PK_PUBLIC bool pkGetArgString(PKVM* vm, int arg, const char** value);
PK_PUBLIC bool pkGetArgString(PKVM* vm, int arg,
const char** value, uint32_t* length);
PK_PUBLIC bool pkGetArgInst(PKVM* vm, int arg, uint32_t id, void** value);
PK_PUBLIC bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value);
// The functions follow are used to set the return value of the current native
@ -349,6 +365,9 @@ PK_PUBLIC void pkReturnNumber(PKVM* vm, double value);
PK_PUBLIC void pkReturnString(PKVM* vm, const char* value);
PK_PUBLIC void pkReturnStringLength(PKVM* vm, const char* value, size_t len);
PK_PUBLIC void pkReturnValue(PKVM* vm, PkVar value);
PK_PUBLIC void pkReturnHandle(PKVM* vm, PkHandle* handle);
PK_PUBLIC void pkReturnInstNative(PKVM* vm, void* data, uint32_t id);
// Returns the cstring pointer of the given string. Make sure if the [value] is
// a string before calling this function, otherwise it'll fail an assertion.
@ -382,6 +401,12 @@ PK_PUBLIC PkHandle* pkNewModule(PKVM* vm, const char* name);
// Create and return a new fiber around the function [fn].
PK_PUBLIC PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn);
// Create and return a native instance around the [data]. The [id] is the
// unique id of the instance, this would be used to check if two instances are
// equal and used to get the name of the instance using NativeTypeNameFn
// callback.
PK_PUBLIC PkHandle* pkNewInstNative(PKVM* vm, void* data, uint32_t id);
// TODO: The functions below will push the primitive values on the stack
// and return it's pointer as a PkVar. It's useful to convert your primitive
// values as pocketlang variables.

View File

@ -6,7 +6,7 @@
#ifndef BUFFERS_TEMPLATE_H
#define BUFFERS_TEMPLATE_H
#include "pk_common.h"
#include "pk_internal.h"
// The macro 'DECLARE_BUFFER()' emulate the C++ template to declare and define
// different types of buffer objects.

View File

@ -3,79 +3,16 @@
* Distributed Under The MIT License
*/
// A collection of reusable macros that pocketlang use. This file doesn't have
// any dependencies, you can just drag and drop this file in your project if
// you want to use these macros.
#ifndef PK_COMMON_H
#define PK_COMMON_H
#include "include/pocketlang.h"
// Commonly used c standard headers across the sources. Don't include any
// headers that are specific to a single source here, instead include them in
// their source files explicitly (can not be implicitly included by another
// header). And don't include any C standard headers in any of the pocketlang
// headers.
#include <assert.h>
#include <errno.h>
#include <float.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
// __STDC_LIMIT_MACROS and __STDC_CONSTANT_MACROS are a workaround to
// allow C++ programs to use stdint.h macros specified in the C99
// standard that aren't in the C++ standard.
#define __STDC_LIMIT_MACROS
#include <stdint.h>
/*****************************************************************************/
/* INTERNAL CONFIGURATIONS */
/*****************************************************************************/
// Set this to dump compiled opcodes of each functions.
#define DEBUG_DUMP_COMPILED_CODE 0
// Set this to dump stack frame before executing the next instruction.
#define DEBUG_DUMP_CALL_STACK 0
// Nan-Tagging could be disable for debugging/portability purposes. See "var.h"
// header for more information on Nan-tagging.
#define VAR_NAN_TAGGING 1
// The maximum number of argument a pocketlang function supported to call. This
// value is arbitrary and feel free to change it. (Just used this limit for an
// internal buffer to store values before calling a new fiber).
#define MAX_ARGC 32
// The factor by which a buffer will grow when it's capacity reached.
#define GROW_FACTOR 2
// The initial minimum capacity of a buffer to allocate.
#define MIN_CAPACITY 8
/*****************************************************************************/
/* ALLOCATION MACROS */
/*****************************************************************************/
// Allocate object of [type] using the vmRealloc function.
#define ALLOCATE(vm, type) \
((type*)vmRealloc(vm, NULL, 0, sizeof(type)))
// Allocate object of [type] which has a dynamic tail array of type [tail_type]
// with [count] entries.
#define ALLOCATE_DYNAMIC(vm, type, count, tail_type) \
((type*)vmRealloc(vm, NULL, 0, sizeof(type) + sizeof(tail_type) * (count)))
// Allocate [count] amount of object of [type] array.
#define ALLOCATE_ARRAY(vm, type, count) \
((type*)vmRealloc(vm, NULL, 0, sizeof(type) * (count)))
// Deallocate a pointer allocated by vmRealloc before.
#define DEALLOCATE(vm, pointer) \
vmRealloc(vm, pointer, 0, 0)
/*****************************************************************************/
/* COMMON MACROS */
/*****************************************************************************/
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
@ -146,20 +83,23 @@
#define ASSERT(condition, message) NO_OP
#define ASSERT_INDEX(index, size) NO_OP
// Reference : https://github.com/wren-lang/
#if defined(_MSC_VER)
#define UNREACHABLE() __assume(0)
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define UNREACHABLE() __builtin_unreachable()
#elif defined(__EMSCRIPTEN__)
#elif defined(__EMSCRIPTEN__) || defined(__clang__)
#if __has_builtin(__builtin_unreachable)
#define UNREACHABLE() __builtin_unreachable()
#else
#define UNREACHABLE() NO_OP
#endif
#else
#define UNREACHABLE() NO_OP
#endif
#endif // DEBUG
#if defined( _MSC_VER )
#if defined(_MSC_VER)
#define forceinline __forceinline
#else
#define forceinline __attribute__((always_inline))
@ -203,9 +143,7 @@
#define STR_HEX_BUFF_SIZE 20
// Integer number (double) to bin string buffer size.
// The maximum value an unsigned 64 bit integer can get is
// 0b1111111111111111111111111111111111111111111111111111111111111111
// which is 64 characters.
// The maximum value an unsigned 64 bit integer can get is 0b11111... 64 1s.
// + 64 for bin digits
// + 1 for sign '-'
// + 2 for '0b' prefix

View File

@ -6,7 +6,7 @@
#ifndef COMPILER_H
#define COMPILER_H
#include "pk_common.h"
#include "pk_internal.h"
#include "pk_var.h"
typedef enum {

View File

@ -121,13 +121,13 @@ PkHandle* pkGetFunction(PKVM* vm, PkHandle* module,
__ASSERT(value != NULL, "Argument [value] was NULL."); \
} while (false)
// Set error for incompatible type provided as an argument.
// Set error for incompatible type provided as an argument. (TODO: got type).
#define ERR_INVALID_ARG_TYPE(m_type) \
do { \
char buff[STR_INT_BUFF_SIZE]; \
sprintf(buff, "%d", arg); \
VM_SET_ERROR(vm, stringFormat(vm, "Expected a " m_type \
" at argument $.", buff)); \
VM_SET_ERROR(vm, stringFormat(vm, "Expected a '$' at argument $.", \
m_type, buff)); \
} while (false)
// pkGetArgc implementation (see pocketlang.h for description).
@ -173,12 +173,14 @@ bool pkGetArgNumber(PKVM* vm, int arg, double* value) {
}
// pkGetArgString implementation (see pocketlang.h for description).
bool pkGetArgString(PKVM* vm, int arg, const char** value) {
bool pkGetArgString(PKVM* vm, int arg, const char** value, uint32_t* length) {
CHECK_GET_ARG_API_ERRORS();
Var val = ARG(arg);
if (IS_OBJ_TYPE(val, OBJ_STRING)) {
*value = ((String*)AS_OBJ(val))->data;
String* str = (String*)AS_OBJ(val);
*value = str->data;
if (length) *length = str->length;
} else {
ERR_INVALID_ARG_TYPE("string");
@ -188,6 +190,34 @@ bool pkGetArgString(PKVM* vm, int arg, const char** value) {
return true;
}
// pkGetArgInstance implementation (see pocketlang.h for description).
bool pkGetArgInst(PKVM* vm, int arg, uint32_t id, void** value) {
CHECK_GET_ARG_API_ERRORS();
Var val = ARG(arg);
bool is_native_instance = false;
if (IS_OBJ_TYPE(val, OBJ_INST)) {
Instance* inst = ((Instance*)AS_OBJ(val));
if (inst->is_native && inst->native_id == id) {
*value = inst->native;
is_native_instance = true;
}
}
if (!is_native_instance) {
const char* ty_name = "$(?)";
if (vm->config.inst_name_fn != NULL) {
ty_name = vm->config.inst_name_fn(id);
}
ERR_INVALID_ARG_TYPE(ty_name);
return false;
}
return true;
}
// pkGetArgValue implementation (see pocketlang.h for description).
bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) {
CHECK_GET_ARG_API_ERRORS();
@ -234,6 +264,16 @@ void pkReturnValue(PKVM* vm, PkVar value) {
RET(*(Var*)value);
}
// pkReturnHandle implementation (see pocketlang.h for description).
void pkReturnHandle(PKVM* vm, PkHandle* handle) {
RET(handle->value);
}
// pkReturnInstNative implementation (see pocketlang.h for description).
void pkReturnInstNative(PKVM* vm, void* data, uint32_t id) {
RET(VAR_OBJ(newInstanceNative(vm, data, id)));
}
const char* pkStringGetData(const PkVar value) {
const Var str = (*(const Var*)value);
__ASSERT(IS_OBJ_TYPE(str, OBJ_STRING), "Value should be of type string.");

View File

@ -6,7 +6,7 @@
#ifndef CORE_H
#define CORE_H
#include "pk_common.h"
#include "pk_internal.h"
#include "pk_var.h"
// Initialize core language, builtin function and core libs.

View File

@ -6,7 +6,7 @@
#ifndef DEBUG_H
#define DEBUG_H
#include "pk_common.h"
#include "pk_internal.h"
#include "pk_var.h"
// Dump the value of the [value] without a new line at the end to the buffer

78
src/pk_internal.h Normal file
View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2020-2021 Thakee Nathees
* Distributed Under The MIT License
*/
#ifndef PK_INTERNAL
#define PK_INTERNAL
#include "include/pocketlang.h"
#include "pk_common.h"
// Commonly used c standard headers across the sources. Don't include any
// headers that are specific to a single source here, instead include them in
// their source files explicitly (can not be implicitly included by another
// header). And don't include any C standard headers in any of the pocketlang
// headers.
#include <assert.h>
#include <errno.h>
#include <float.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
// __STDC_LIMIT_MACROS and __STDC_CONSTANT_MACROS are a workaround to
// allow C++ programs to use stdint.h macros specified in the C99
// standard that aren't in the C++ standard.
#define __STDC_LIMIT_MACROS
#include <stdint.h>
/*****************************************************************************/
/* INTERNAL CONFIGURATIONS */
/*****************************************************************************/
// Set this to dump compiled opcodes of each functions.
#define DEBUG_DUMP_COMPILED_CODE 0
// Set this to dump stack frame before executing the next instruction.
#define DEBUG_DUMP_CALL_STACK 0
// Nan-Tagging could be disable for debugging/portability purposes. See "var.h"
// header for more information on Nan-tagging.
#define VAR_NAN_TAGGING 1
// The maximum number of argument a pocketlang function supported to call. This
// value is arbitrary and feel free to change it. (Just used this limit for an
// internal buffer to store values before calling a new fiber).
#define MAX_ARGC 32
// The factor by which a buffer will grow when it's capacity reached.
#define GROW_FACTOR 2
// The initial minimum capacity of a buffer to allocate.
#define MIN_CAPACITY 8
/*****************************************************************************/
/* ALLOCATION MACROS */
/*****************************************************************************/
// Allocate object of [type] using the vmRealloc function.
#define ALLOCATE(vm, type) \
((type*)vmRealloc(vm, NULL, 0, sizeof(type)))
// Allocate object of [type] which has a dynamic tail array of type [tail_type]
// with [count] entries.
#define ALLOCATE_DYNAMIC(vm, type, count, tail_type) \
((type*)vmRealloc(vm, NULL, 0, sizeof(type) + sizeof(tail_type) * (count)))
// Allocate [count] amount of object of [type] array.
#define ALLOCATE_ARRAY(vm, type, count) \
((type*)vmRealloc(vm, NULL, 0, sizeof(type) * (count)))
// Deallocate a pointer allocated by vmRealloc before.
#define DEALLOCATE(vm, pointer) \
vmRealloc(vm, pointer, 0, 0)
#endif // PK_INTERNAL

View File

@ -6,7 +6,7 @@
#ifndef UTILS_H
#define UTILS_H
#include "pk_common.h"
#include "pk_internal.h"
// Returns the smallest power of two that is equal to or greater than [n].
// Source : https://github.com/wren-lang/wren/blob/main/src/vm/wren_utils.h#L119

View File

@ -42,26 +42,53 @@ PkVarType pkGetValueType(const PkVar value) {
}
PkHandle* pkNewString(PKVM* vm, const char* value) {
return vmNewHandle(vm, VAR_OBJ(newString(vm, value)));
String* str = newString(vm, value);
vmPushTempRef(vm, &str->_super); // str
PkHandle* handle = vmNewHandle(vm, VAR_OBJ(str));
vmPopTempRef(vm); // str
return handle;
}
PkHandle* pkNewStringLength(PKVM* vm, const char* value, size_t len) {
return vmNewHandle(vm, VAR_OBJ(newStringLength(vm, value, (uint32_t)len)));
String* str = newStringLength(vm, value, (uint32_t)len);
vmPushTempRef(vm, &str->_super); // str
PkHandle* handle = vmNewHandle(vm, VAR_OBJ(str));
vmPopTempRef(vm); // str
return handle;
}
PkHandle* pkNewList(PKVM* vm) {
return vmNewHandle(vm, VAR_OBJ(newList(vm, MIN_CAPACITY)));
List* list = newList(vm, MIN_CAPACITY);
vmPushTempRef(vm, &list->_super); // list
PkHandle* handle = vmNewHandle(vm, VAR_OBJ(list));
vmPopTempRef(vm); // list
return handle;
}
PkHandle* pkNewMap(PKVM* vm) {
return vmNewHandle(vm, VAR_OBJ(newMap(vm)));
Map* map = newMap(vm);
vmPushTempRef(vm, &map->_super); // map
PkHandle* handle = vmNewHandle(vm, VAR_OBJ(map));
vmPopTempRef(vm); // map
return handle;
}
PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn) {
__ASSERT(IS_OBJ_TYPE(fn->value, OBJ_FUNC), "Fn should be of type function.");
Fiber* fiber = newFiber(vm, (Function*)AS_OBJ(fn->value));
return vmNewHandle(vm, VAR_OBJ(fiber));
vmPushTempRef(vm, &fiber->_super); // fiber
PkHandle* handle = vmNewHandle(vm, VAR_OBJ(fiber));
vmPopTempRef(vm); // fiber
return handle;
}
PkHandle* pkNewInstNative(PKVM* vm, void* data, uint32_t id) {
Instance* inst = newInstanceNative(vm, data, id);
vmPushTempRef(vm, &inst->_super); // inst
PkHandle* handle = vmNewHandle(vm, VAR_OBJ(inst));
vmPopTempRef(vm); // inst
return handle;
}
/*****************************************************************************/
@ -511,6 +538,22 @@ Instance* newInstance(PKVM* vm, Class* ty, bool initialize) {
return inst;
}
Instance* newInstanceNative(PKVM* vm, void* data, uint32_t id) {
Instance* inst = ALLOCATE(vm, Instance);
varInitObject(&inst->_super, vm, OBJ_INST);
inst->is_native = true;
inst->native_id = id;
if (vm->config.inst_name_fn != NULL) {
inst->name = vm->config.inst_name_fn(id);
} else {
inst->name = "$(?)";
}
inst->native = data;
return inst;
}
List* rangeAsList(PKVM* vm, Range* self) {
List* list;
if (self->from < self->to) {
@ -1006,7 +1049,10 @@ void freeObject(PKVM* vm, Object* self) {
Instance* inst = (Instance*)self;
if (inst->is_native) {
TODO;
if (vm->config.free_inst_fn != NULL) {
// TODO: Allow user to set error when freeing the object.
vm->config.free_inst_fn(vm, inst->native);
}
} else {
Inst* ins = inst->ins;
@ -1446,6 +1492,15 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff,
pkByteBufferWrite(buff, vm, '=');
_toStringInternal(vm, ins->fields.data[i], buff, outer, repr);
}
} else {
pkByteBufferWrite(buff, vm, ':');
char buff_addr[STR_HEX_BUFF_SIZE];
char* ptr = (char*)buff_addr;
(*ptr++) = '0'; (*ptr++) = 'x';
const int len = snprintf(ptr, sizeof(buff_addr) - 2,
"%08x", (unsigned int)(uintptr_t)inst->native);
pkByteBufferAddString(buff, vm, buff_addr, (uint32_t)len);
}
pkByteBufferWrite(buff, vm, ']');

View File

@ -7,7 +7,7 @@
#define VAR_H
#include "pk_buffers.h"
#include "pk_common.h"
#include "pk_internal.h"
/** @file
* A simple dynamic type system library for small dynamic typed languages using
@ -204,6 +204,7 @@ DECLARE_BUFFER(Class, Class*)
void pkByteBufferAddString(pkByteBuffer* self, PKVM* vm, const char* str,
uint32_t length);
// Type enums of the pocketlang heap allocated types.
typedef enum {
OBJ_STRING,
OBJ_LIST,
@ -377,7 +378,10 @@ struct Instance {
Object _super;
const char* name; //< Name of the type it belongs to.
bool is_native; //< True if it's a native type instance.
uint32_t native_id; //< Unique ID of this native instance.
union {
void* native; //< C struct pointer. // TODO:
Inst* ins; //< Module instance pointer.
@ -442,6 +446,12 @@ Class* newClass(PKVM* vm, Script* scr, const char* name, uint32_t length);
// the buffer count = 0). Otherwise they'll be set to VAR_NULL.
Instance* newInstance(PKVM* vm, Class* ty, bool initialize);
// Allocate new native instance and with [data] as the native type handle and
// return Instance*. The [id] is the unique id of the instance, this would be
// used to check if two instances are equal and used to get the name of the
// instance using NativeTypeNameFn callback.
Instance* newInstanceNative(PKVM* vm, void* data, uint32_t id);
/*****************************************************************************/
/* METHODS */
/*****************************************************************************/

View File

@ -30,6 +30,9 @@ PkConfiguration pkNewConfiguration(void) {
config.write_fn = NULL;
config.read_fn = NULL;
config.free_inst_fn = NULL;
config.inst_name_fn = NULL;
config.load_script_fn = NULL;
config.resolve_path_fn = NULL;
config.user_data = NULL;

View File

@ -6,8 +6,8 @@
#ifndef VM_H
#define VM_H
#include "pk_common.h"
#include "pk_compiler.h"
#include "pk_internal.h"
#include "pk_var.h"
// The maximum number of temporary object reference to protect them from being

View File

@ -6,8 +6,9 @@
## uses of tabs and trailing white spaces, etc.
import os, sys, re
from os.path import join, abspath, dirname
from os import listdir
from os.path import (
join, abspath, dirname, relpath)
## The absolute path of this file, when run as a script.
## This file is not intended to be included in other files at the moment.
@ -18,6 +19,11 @@ THIS_PATH = abspath(dirname(__file__))
def to_abs_paths(sources):
return map(lambda s: os.path.join(THIS_PATH, s), sources)
## Converts the path from absolute path to relative path from the
## toplelve of the project.
def to_tolevel_path(path):
return relpath(path, join(THIS_PATH, '..'))
## A list of source files, to check if the fnv1a hash values match it's
## corresponding cstring in the CASE_ATTRIB(name, hash) macro calls.
HASH_CHECK_LIST = [
@ -29,7 +35,14 @@ HASH_CHECK_LIST = [
C_SOURCE_DIRS = [
"../src/",
"../cli/",
"../cli/modules/",
]
## A list of common header that just copied in different projects.
## These common header cannot be re-used because we're trying to achieve
## minimalistic with the count of the sources in pocketlang.
COMMON_HEADERS = [
"../src/pk_common.h",
"../cli/common.h",
]
## This global variable will be set to true if any check failed.
@ -38,6 +51,7 @@ checks_failed = False
def main():
check_fnv1_hash(to_abs_paths(HASH_CHECK_LIST))
check_static(to_abs_paths(C_SOURCE_DIRS))
check_common_header_match(to_abs_paths(COMMON_HEADERS))
if checks_failed:
sys.exit(1)
print("Static checks were passed.")
@ -58,7 +72,7 @@ def check_fnv1_hash(sources):
if val == hash: continue
## Path of the file relative to top-level.
file_path = os.path.relpath(file, join(THIS_PATH, '..'))
file_path = to_tolevel_path(file)
report_error(f"{location(file_path, line_no)} - hash mismatch. "
f"hash('{name}') = {hash} not {val}")
@ -77,7 +91,7 @@ def check_static(dirs):
fp = open(join(dir, file), 'r')
## Path of the file relative to top-level.
file_path = os.path.relpath(join(dir, file), join(THIS_PATH, '..'))
file_path = to_tolevel_path(join(dir, file))
## This will be set to true if the last line is empty.
is_last_empty = False; line_no = 0
@ -109,6 +123,23 @@ def check_static(dirs):
fp.close()
## Assert all the content of the headers list below are the same.
def check_common_header_match(headers):
headers = list(headers)
assert len(headers) >= 1
content = ''
with open(headers[0], 'r') as f:
content = f.read()
for i in range(1, len(headers)):
with open(headers[i], 'r') as f:
if f.read() != content:
main_header = to_tolevel_path(headers[0])
curr_header = to_tolevel_path(headers[i])
report_error("File content mismatch: \"%s\" and \"%s\"\n"
" These files contants should be the same."
%(main_header, curr_header))
## Returns a formated string of the error location.
def location(file, line):