mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-05 20:26:53 +08:00
Native types & File object implementations.
This commit is contained in:
parent
a21bd98945
commit
600f972927
34
cli/all.c
34
cli/all.c
@ -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);
|
||||
}
|
147
cli/common.h
147
cli/common.h
@ -3,46 +3,48 @@
|
||||
* 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.
|
||||
#define __ASSERT(condition, message) \
|
||||
do { \
|
||||
if (!(condition)) { \
|
||||
fprintf(stderr, "Assertion failed: %s\n\tat %s() (%s:%i)\n", \
|
||||
message, __func__, __FILE__, __LINE__); \
|
||||
DEBUG_BREAK(); \
|
||||
abort(); \
|
||||
} \
|
||||
// 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)) { \
|
||||
fprintf(stderr, "Assertion failed: %s\n\tat %s() (%s:%i)\n", \
|
||||
message, __func__, __FILE__, __LINE__); \
|
||||
DEBUG_BREAK(); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
#define NO_OP do {} while (false)
|
||||
@ -55,56 +57,97 @@
|
||||
#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) \
|
||||
ASSERT(index >= 0 && index < size, "Index out of bounds.")
|
||||
|
||||
#define UNREACHABLE() \
|
||||
do { \
|
||||
fprintf(stderr, "Execution reached an unreachable path\n" \
|
||||
"\tat %s() (%s:%i)\n", __func__, __FILE__, __LINE__); \
|
||||
DEBUG_BREAK(); \
|
||||
abort(); \
|
||||
#define UNREACHABLE() \
|
||||
do { \
|
||||
fprintf(stderr, "Execution reached an unreachable path\n" \
|
||||
"\tat %s() (%s:%i)\n", __func__, __FILE__, __LINE__); \
|
||||
DEBUG_BREAK(); \
|
||||
abort(); \
|
||||
} while (false)
|
||||
|
||||
#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
37
cli/internal.h
Normal 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
|
17
cli/main.c
17
cli/main.c
@ -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
405
cli/modules.c
Normal 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
73
cli/modules.h
Normal 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);
|
@ -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);
|
||||
}
|
@ -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
17
cli/thirdparty.c
Normal 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"
|
||||
|
@ -3,7 +3,7 @@
|
||||
* Distributed Under The MIT License
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "internal.h"
|
||||
|
||||
typedef struct {
|
||||
uint8_t* data;
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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__)
|
||||
#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))
|
||||
@ -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
|
||||
|
@ -6,7 +6,7 @@
|
||||
#ifndef COMPILER_H
|
||||
#define COMPILER_H
|
||||
|
||||
#include "pk_common.h"
|
||||
#include "pk_internal.h"
|
||||
#include "pk_var.h"
|
||||
|
||||
typedef enum {
|
||||
|
@ -113,21 +113,21 @@ PkHandle* pkGetFunction(PKVM* vm, PkHandle* module,
|
||||
} while(false)
|
||||
|
||||
// Check for errors in before calling the get arg public api function.
|
||||
#define CHECK_GET_ARG_API_ERRORS() \
|
||||
do { \
|
||||
__ASSERT(vm->fiber != NULL, \
|
||||
"This function can only be called at runtime."); \
|
||||
__ASSERT(arg > 0 || arg <= ARGC, "Invalid argument index."); \
|
||||
__ASSERT(value != NULL, "Argument [value] was NULL."); \
|
||||
#define CHECK_GET_ARG_API_ERRORS() \
|
||||
do { \
|
||||
__ASSERT(vm->fiber != NULL, \
|
||||
"This function can only be called at runtime."); \
|
||||
__ASSERT(arg > 0 || arg <= ARGC, "Invalid argument index."); \
|
||||
__ASSERT(value != NULL, "Argument [value] was NULL."); \
|
||||
} while (false)
|
||||
|
||||
// Set error for incompatible type provided as an argument.
|
||||
#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)); \
|
||||
// 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 '$' 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.");
|
||||
|
@ -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.
|
||||
|
@ -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
78
src/pk_internal.h
Normal 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
|
@ -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
|
||||
|
67
src/pk_var.c
67
src/pk_var.c
@ -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, ']');
|
||||
|
14
src/pk_var.h
14
src/pk_var.h
@ -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,10 +378,13 @@ 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.
|
||||
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 */
|
||||
/*****************************************************************************/
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
@ -108,7 +122,24 @@ def check_static(dirs):
|
||||
is_last_empty = False
|
||||
|
||||
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):
|
||||
|
Loading…
Reference in New Issue
Block a user