Merge pull request #30 from ThakeeNathees/custom-libs

Native function api implemented
This commit is contained in:
Thakee Nathees 2021-05-23 02:35:50 +05:30 committed by GitHub
commit 79a3700284
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 590 additions and 197 deletions

43
cli/cli_modules.c Normal file
View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2021 Thakee Nathees
* Licensed under: MIT License
*/
#include "pocketlang.h"
// FIXME: everything below here is temproary and for testing.
#include <stdio.h>
void stdPathAbspath(PKVM* vm) {
PkVar path;
if (!pkGetArgValue(vm, 1, PK_STRING, &path)) return;
const char* data = pkStringGetData(path);
pkReturnNull(vm);
}
void stdPathCurdir(PKVM* vm) {
pkReturnNull(vm);
}
void testAdd(PKVM* vm) {
double v1, v2;
if (!pkGetArgNumber(vm, 1, &v1)) return;
if (!pkGetArgNumber(vm, 2, &v2)) return;
double total = v1 + v2;
pkReturnNumber(vm, total);
}
void register_cli_modules(PKVM* vm) {
PkHandle* path = pkNewModule(vm, "path");
pkModuleAddFunction(vm, path, "abspath", stdPathAbspath, 1);
pkModuleAddFunction(vm, path, "curdir", stdPathCurdir, 0);
PkHandle* test = pkNewModule(vm, "test");
pkModuleAddFunction(vm, test, "add", testAdd, 2);
}

View File

@ -9,7 +9,15 @@
#include "pocketlang.h"
void errorPrint(PKVM* vm, PKErrorType type, const char* file, int line,
// FIXME: everything below here is temproary and for testing.
// TODO: include this.
void register_cli_modules(PKVM* vm);
// ---------------------------------------
void errorPrint(PKVM* vm, PkErrorType type, const char* file, int line,
const char* message) {
if (type == PK_ERROR_COMPILE) {
fprintf(stderr, "Error: %s\n at %s:%i\n", message, file, line);
@ -101,16 +109,15 @@ int main(int argc, char** argv) {
// FIXME: this is temp till arg parse implemented.
if (argc >= 3 && strcmp(argv[1], "-c") == 0) {
PKVM* vm = pkNewVM(&config);
PKInterpretResult result = pkInterpretSource(vm, argv[2], "$(Source)");
pkFreeVM(vm);
return result;
} else {
PKVM* vm = pkNewVM(&config);
PKInterpretResult result = pkInterpret(vm, argv[1]);
PkInterpretResult result = pkInterpretSource(vm, argv[2], "$(Source)");
pkFreeVM(vm);
return result;
}
return 0;
PKVM* vm = pkNewVM(&config);
register_cli_modules(vm);
PkInterpretResult result = pkInterpret(vm, argv[1]);
pkFreeVM(vm);
return result;
}

View File

@ -23,7 +23,11 @@
#include <stdio.h> //< Only needed for ASSERT() macro and for release mode
//< TODO; macro use this to print a crash report.
// The internal assertion macro, do not use this. Use ASSERT() instead.
// 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 and assetion's in public methods (to indicate that
// the host application did something wrong).
#define __ASSERT(condition, message) \
do { \
if (!(condition)) { \
@ -103,19 +107,22 @@
#define DEALLOCATE(vm, pointer) \
vmRealloc(vm, pointer, 0, 0)
// Unique number to identify for various cases.
typedef uint32_t ID;
#if VAR_NAN_TAGGING
typedef uint64_t Var;
#else
typedef struct Var Var;
#endif
typedef struct Object Object;
typedef struct String String;
typedef struct List List;
typedef struct Map Map;
typedef struct Range Range;
typedef struct Script Script;
typedef struct Function Function;
// Unique number to identify for various cases.
typedef uint32_t ID;
// VM's fiber type.
typedef struct Fiber Fiber;
#endif //PK_COMMON_H

View File

@ -13,6 +13,137 @@
#include "var.h"
#include "vm.h"
/*****************************************************************************/
/* PUBLIC API */
/*****************************************************************************/
// Declare internal functions of public api.
static Script* newModuleInternal(PKVM* vm, const char* name);
static void moduleAddFunctionInternal(PKVM* vm, Script* script,
const char* name, pkNativeFn fptr, int arity);
PkHandle* pkNewModule(PKVM* vm, const char* name) {
Script* module = newModuleInternal(vm, name);
return vmNewHandle(vm, VAR_OBJ(&module->_super));
}
void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name,
pkNativeFn fptr, int arity) {
__ASSERT(module != NULL, "Argument module was NULL.");
Var scr = module->value;
__ASSERT(IS_OBJ(scr) && AS_OBJ(scr)->type == OBJ_SCRIPT,
"Given handle is not a module");
moduleAddFunctionInternal(vm, (Script*)AS_OBJ(scr), name, fptr, arity);
}
// Argument getter (1 based).
#define ARG(n) vm->fiber->ret[n]
// Convinent macros.
#define ARG1 ARG(1)
#define ARG2 ARG(2)
#define ARG3 ARG(3)
// Argument count used in variadic functions.
#define ARGC ((int)(vm->fiber->sp - vm->fiber->ret) - 1)
// Set return value.
#define RET(value) \
do { \
*(vm->fiber->ret) = value; \
return; \
} while (false)
// Check for errors in before calling the get arg public api function.
#define CHECK_GET_ARG_API_ERRORS() \
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime."); \
__ASSERT(arg > 0 || arg < ARGC, "Invalid argument index."); \
__ASSERT(value != NULL, "Parameter [value] was NULL."); \
((void*)0)
#define ERR_INVALID_ARG_TYPE(m_type) \
do { \
/* 12 chars is enought for a 4 byte integer string.*/ \
char buff[12]; \
sprintf(buff, "%d", arg); \
vm->fiber->error = stringFormat(vm, "Expected a " m_type \
" at argument $.", buff); \
} while (false)
int pkGetArgc(PKVM* vm) {
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
return ARGC;
}
PkVar pkGetArg(PKVM* vm, int arg) {
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
__ASSERT(arg > 0 || arg < ARGC, "Invalid argument index.");
return &(ARG(arg));
}
bool pkGetArgNumber(PKVM* vm, int arg, double* value) {
CHECK_GET_ARG_API_ERRORS();
Var val = ARG(arg);
if (IS_NUM(val)) {
*value = AS_NUM(val);
} else if (IS_BOOL(val)) {
*value = AS_BOOL(val) ? 1 : 0;
} else {
ERR_INVALID_ARG_TYPE("number");
return false;
}
return true;
}
bool pkGetArgBool(PKVM* vm, int arg, bool* value) {
CHECK_GET_ARG_API_ERRORS();
Var val = ARG(arg);
*value = toBool(val);
return true;
}
bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) {
CHECK_GET_ARG_API_ERRORS();
Var val = ARG(arg);
if (pkGetValueType((PkVar)&val) != type) {
char buff[12]; sprintf(buff, "%d", arg);
vm->fiber->error = stringFormat(vm,
"Expected a $ at argument $.", getPkVarTypeName(type), buff);
return false;
}
*value = (PkVar)&val;
return true;
}
void pkReturnNull(PKVM* vm) {
RET(VAR_NULL);
}
void pkReturnBool(PKVM* vm, bool value) {
RET(VAR_BOOL(value));
}
void pkReturnNumber(PKVM* vm, double value) {
RET(VAR_NUM(value));
}
void pkReturnValue(PKVM* vm, PkVar value) {
RET(*(Var*)value);
}
// ----------------------------------------------------------------------------
// Convert number var as int32_t. Check if it's number before using it.
#define _AS_INTEGER(var) (int32_t)trunc(AS_NUM(var))
@ -104,23 +235,6 @@ static bool validateArgString(PKVM* vm, Var var, String** value, int arg_ind) {
/* BUILTIN FUNCTIONS API */
/*****************************************************************************/
// Argument getter (1 based).
#define ARG(n) vm->fiber->ret[n]
// Convinent macros.
#define ARG1 ARG(1)
#define ARG2 ARG(2)
#define ARG3 ARG(3)
// Argument count used in variadic functions.
#define ARGC ((int)(vm->fiber->sp - vm->fiber->ret) - 1)
// Set return value.
#define RET(value) \
do { \
*(vm->fiber->ret) = value; \
return; \
} while (false)
Function* getBuiltinFunction(PKVM* vm, int index) {
ASSERT_INDEX(index, vm->builtins_count);
@ -276,25 +390,54 @@ void coreStrStrip(PKVM* vm) {
}
/*****************************************************************************/
/* CORE LIBRARY METHODS */
/* CORE MODULE METHODS */
/*****************************************************************************/
// 'path' library methods.
// -----------------------
// Create a module and add it to the vm's core modules, returns the script.
static Script* newModuleInternal(PKVM* vm, const char* name) {
// TODO: path library should be added by the cli (or the hosting application).
void stdPathAbspath(PKVM* vm) {
Var relpath = ARG1;
if (!IS_OBJ(relpath) || AS_OBJ(relpath)->type != OBJ_STRING) {
vm->fiber->error = newString(vm, "Expected a string at argument 1.");
// Create a new Script for the module.
String* _name = newString(vm, name);
vmPushTempRef(vm, &_name->_super);
// Check if any module with the same name already exists and assert to the
// hosting application.
if (!IS_UNDEF(mapGet(vm->core_libs, VAR_OBJ(&_name->_super)))) {
vmPopTempRef(vm); // _name
__ASSERT(false, stringFormat(vm, "A module named '$' already exists",
name)->data);
}
// TODO: abspath.
RET(VAR_OBJ(newString(vm, "TODO: abspath")));
Script* scr = newScript(vm, _name);
scr->moudle = _name;
vmPopTempRef(vm); // _name
// Add the script to core_libs.
vmPushTempRef(vm, &scr->_super);
mapSet(vm, vm->core_libs, VAR_OBJ(&_name->_super), VAR_OBJ(&scr->_super));
vmPopTempRef(vm);
return scr;
}
void stdPathCurdir(PKVM* vm) {
RET(VAR_OBJ(newString(vm, "TODO: curdir")));
static void moduleAddFunctionInternal(PKVM* vm, Script* script,
const char* name, pkNativeFn fptr, int arity) {
// Check if function with the same name already exists.
if (scriptSearchFunc(script, name, (uint32_t)strlen(name)) != -1) {
__ASSERT(false, stringFormat(vm, "A function named '$' already esists "
"on module '@'", name, script->moudle)->data);
}
// Check if a global variable with the same name already exists.
if (scriptSearchGlobals(script, name, (uint32_t)strlen(name)) != -1) {
__ASSERT(false, stringFormat(vm, "A global variable named '$' already "
"esists on module '@'", name, script->moudle)->data);
}
Function* fn = newFunction(vm, name, (int)strlen(name), script, true);
fn->native = fptr;
fn->arity = arity;
}
// 'lang' library methods.
@ -367,40 +510,12 @@ void initializeCore(PKVM* vm) {
INITALIZE_BUILTIN_FN("str_upper", coreStrUpper, 1);
INITALIZE_BUILTIN_FN("str_strip", coreStrStrip, 1);
// Make STD scripts.
Script* std; // A temporary pointer to the current std script.
Function* fn; // A temporary pointer to the allocated function function.
#define STD_NEW_SCRIPT(_name) \
do { \
/* Create a new Script. */ \
String* name = newString(vm, _name); \
vmPushTempRef(vm, &name->_super); \
std = newScript(vm, name); \
std->moudle = name; /* Core libs's path and the module are the same. */ \
vmPopTempRef(vm); \
/* Add the script to core_libs. */ \
vmPushTempRef(vm, &std->_super); \
mapSet(vm, vm->core_libs, VAR_OBJ(&name->_super), VAR_OBJ(&std->_super)); \
vmPopTempRef(vm); \
} while (false)
// Core Modules /////////////////////////////////////////////////////////////
#define STD_ADD_FUNCTION(_name, fptr, _arity) \
do { \
fn = newFunction(vm, _name, (int)strlen(_name), std, true); \
fn->native = fptr; \
fn->arity = _arity; \
} while (false)
// path
STD_NEW_SCRIPT("path");
STD_ADD_FUNCTION("abspath", stdPathAbspath, 1);
STD_ADD_FUNCTION("curdir", stdPathCurdir, 0);
// lang
STD_NEW_SCRIPT("lang");
STD_ADD_FUNCTION("clock", stdLangClock, 0);
STD_ADD_FUNCTION("gc", stdLangGC, 0);
STD_ADD_FUNCTION("write", stdLangWrite, -1);
Script* lang = newModuleInternal(vm, "lang");
moduleAddFunctionInternal(vm, lang, "clock", stdLangClock, 0);
moduleAddFunctionInternal(vm, lang, "gc", stdLangGC, 0);
moduleAddFunctionInternal(vm, lang, "write", stdLangWrite, -1);
}
/*****************************************************************************/

View File

@ -3,8 +3,8 @@
* Licensed under: MIT License
*/
#ifndef MINISCRIPT_H
#define MINISCRIPT_H
#ifndef POCKETLANG_H
#define POCKETLANG_H
#ifdef __cplusplus
extern "C" {
@ -14,7 +14,14 @@ extern "C" {
#include <stdbool.h>
#include <stdlib.h>
/*****************************************************************************/
/* POCKETLANG DEFINES */
/*****************************************************************************/
// The version number macros.
// Major Version - Increment when changes break compatibility.
// Minor Version - Increment when new functionality added to public api.
// Patch Version - Increment when bug fixed or minor changes between releases.
#define PK_VERSION_MAJOR 0
#define PK_VERSION_MINOR 1
#define PK_VERSION_PATCH 0
@ -22,8 +29,9 @@ extern "C" {
// String representation of the value.
#define PK_VERSION_STRING "0.1.0"
// miniscript visibility macros. define PK_DLL for using miniscript as a
// shared library and define PK_COMPILE to export symbols.
// Pocketlang visibility macros. define PK_DLL for using pocketlang as a
// shared library and define PK_COMPILE to export symbols when compiling the
// pocketlang it self as a shared library.
#ifdef _MSC_VER
#define _PK_EXPORT __declspec(dllexport)
@ -46,24 +54,72 @@ extern "C" {
#define PK_PUBLIC
#endif
// MiniScript Virtual Machine.
// it'll contain the state of the execution, stack, heap, and manage memory
// allocations.
typedef struct PKVM PKVM;
/*****************************************************************************/
/* POCKETLANG TYPES */
/*****************************************************************************/
// Nan-Tagging could be disable for debugging/portability purposes only when
// compiling the compiler. Do not change this if using the miniscript library
// compiling the compiler. Do not change this if using the pocketlang library
// for embedding. To disable when compiling the compiler, define
// `VAR_NAN_TAGGING 0`, otherwise it defaults to Nan-Tagging.
#ifndef VAR_NAN_TAGGING
#define VAR_NAN_TAGGING 1
#endif
#if VAR_NAN_TAGGING
typedef uint64_t Var;
#else
typedef struct Var Var;
#endif
// PocketLang Virtual Machine. It'll contain the state of the execution, stack,
// heap, and manage memory allocations.
typedef struct PKVM PKVM;
// A handle to the pocketlang variables. It'll hold the reference to the
// variable and ensure that the variable it holds won't be garbage collected
// till it released with pkReleaseHandle().
typedef struct PkHandle PkHandle;
// A temproary pointer to the pocketlang variable. This pointer is aquired
// from the pocketlang's current stack frame and the pointer will become
// dangling once after the stack frame is popped.
typedef void* PkVar;
// Type enum of the pocketlang varaibles, this can be used to get the type
// from a Var* in the method pkGetVarType().
typedef enum {
PK_NULL,
PK_BOOL,
PK_NUMBER,
PK_STRING,
PK_LIST,
PK_MAP,
PK_RANGE,
PK_SCRIPT,
PK_FUNCTION,
PK_FIBER,
} PkVarType;
typedef struct pkStringPtr pkStringPtr;
typedef struct pkConfiguration pkConfiguration;
// Type of the error message that pocketlang will provide with the pkErrorFn
// callback.
typedef enum {
PK_ERROR_COMPILE = 0, // Compile time errors.
PK_ERROR_RUNTIME, // Runtime error message.
PK_ERROR_STACKTRACE, // One entry of a runtime error stack.
} PkErrorType;
// Result that pocketlang will return after running a script or a function
// or evaluvating an expression.
typedef enum {
PK_RESULT_SUCCESS = 0, // Successfully finished the execution.
PK_RESULT_COMPILE_ERROR, // Compilation failed.
PK_RESULT_RUNTIME_ERROR, // An error occured at runtime.
} PkInterpretResult;
/*****************************************************************************/
/* POCKETLANG FUNCTION POINTERS & CALLBACKS */
/*****************************************************************************/
// C function pointer which is callable from PocketLang.
typedef void (*pkNativeFn)(PKVM* vm);
// A function that'll be called for all the allocation calls by PKVM.
//
@ -77,43 +133,18 @@ typedef struct PKVM PKVM;
// function will return NULL.
typedef void* (*pkReallocFn)(void* memory, size_t new_size, void* user_data);
// C function pointer which is callable from MiniScript.
typedef void (*pkNativeFn)(PKVM* vm);
typedef enum {
// Compile time errors (syntax errors, unresolved fn, etc).
PK_ERROR_COMPILE = 0,
// Runtime error message.
PK_ERROR_RUNTIME,
// One entry of a runtime error stack.
PK_ERROR_STACKTRACE,
} PKErrorType;
// Error callback function pointer. for runtime error it'll call first with
// PK_ERROR_RUNTIME followed by multiple callbacks with PK_ERROR_STACKTRACE.
typedef void (*pkErrorFn) (PKVM* vm, PKErrorType type,
typedef void (*pkErrorFn) (PKVM* vm, PkErrorType type,
const char* file, int line,
const char* message);
// A function callback used by `print()` statement.
typedef void (*pkWriteFn) (PKVM* vm, const char* text);
typedef struct pkStringPtr pkStringPtr;
// A function callback symbol for clean/free the pkStringResult.
typedef void (*pkResultDoneFn) (PKVM* vm, pkStringPtr result);
// A string pointer wrapper to pass cstring around with a on_done() callback
// to clean it when the user of the string done with the string.
struct pkStringPtr {
const char* string; //< The string result.
pkResultDoneFn on_done; //< Called once vm done with the string.
void* user_data; //< User related data.
};
// A function callback to resolve the import script name from the [from] path
// to an absolute (or relative to the cwd). This is required to solve same
// script imported with different relative path. Set the string attribute to
@ -126,11 +157,73 @@ typedef pkStringPtr(*pkResolvePathFn) (PKVM* vm, const char* from,
// to indicate if it's failed to load the script.
typedef pkStringPtr(*pkLoadScriptFn) (PKVM* vm, const char* path);
// This function will be called once it done with the loaded script only if
// it's corresponding MSLoadScriptResult is succeeded (ie. is_failed = false).
//typedef void (*pkLoadDoneFn) (PKVM* vm, pkStringResult result);
/*****************************************************************************/
/* POCKETLANG PUBLIC API */
/*****************************************************************************/
typedef struct {
// Create a new pkConfiguraition with the default values and return it.
// Override those default configuration to adopt to another hosting
// application.
PK_PUBLIC pkConfiguration pkNewConfiguration();
// Allocate initialize and returns a new VM
PK_PUBLIC PKVM* pkNewVM(pkConfiguration* config);
// Clean the VM and dispose all the resources allocated by the VM.
PK_PUBLIC void pkFreeVM(PKVM* vm);
// Update the user data of the vm.
PK_PUBLIC void pkSetUserData(PKVM* vm, void* user_data);
// Returns the associated user data.
PK_PUBLIC void* pkGetUserData(PKVM* vm);
// Create a new handle for the [value]. This is usefull to keep the [value]
// alive once it aquired from the stack. Do not use the [value] once creating
// a new handle for it instead get the value from the handle by using
// pkGetHandleValue() function.
PK_PUBLIC PkHandle* pkNewHandle(PKVM* vm, PkVar value);
// Return the PkVar pointer in the handle, the returned pointer will be valid
// till the handle is released.
PK_PUBLIC PkVar pkGetHandleValue(PkHandle* handle);
// Release the handle and allow it's value to be garbage collected. Always call
// this for every handles before freeing the VM.
PK_PUBLIC void pkReleaseHandle(PKVM* vm, PkHandle* handle);
// Add a new module named [name] to the [vm]. Note that the module shouldn't
// already existed, otherwise an assertion will fail to indicate that.
PK_PUBLIC PkHandle* pkNewModule(PKVM* vm, const char* name);
// Add a native function to the given script. If [arity] is -1 that means
// The function has variadic parameters and use pkGetArgc() to get the argc.
PK_PUBLIC void pkModuleAddFunction(PKVM* vm, PkHandle* module,
const char* name,
pkNativeFn fptr, int arity);
// Interpret the source and return the result.
PK_PUBLIC PkInterpretResult pkInterpretSource(PKVM* vm,
const char* source,
const char* path);
// Compile and execut file at given path.
PK_PUBLIC PkInterpretResult pkInterpret(PKVM* vm, const char* path);
/*****************************************************************************/
/* POCKETLANG PUBLIC TYPE DEFINES */
/*****************************************************************************/
// A string pointer wrapper to pass cstring from host application to pocketlang
// vm, with a on_done() callback to clean it when the pocketlang vm done with
// the string.
struct pkStringPtr {
const char* string; //< The string result.
pkResultDoneFn on_done; //< Called once vm done with the string.
void* user_data; //< User related data.
};
struct pkConfiguration {
// The callback used to allocate, reallocate, and free. If the function
// pointer is NULL it defaults to the VM's realloc(), free() wrappers.
@ -144,57 +237,72 @@ typedef struct {
// User defined data associated with VM.
void* user_data;
};
} pkConfiguration;
// Create a new pkConfiguraition with the default values and return it.
// Override those default configuration to adopt to another hosting
// application.
PK_PUBLIC pkConfiguration pkNewConfiguration();
typedef enum {
PK_RESULT_SUCCESS = 0,
PK_RESULT_COMPILE_ERROR,
PK_RESULT_RUNTIME_ERROR,
} PKInterpretResult;
// Allocate initialize and returns a new VM
PK_PUBLIC PKVM* pkNewVM(pkConfiguration* config);
// Clean the VM and dispose all the resources allocated by the VM.
PK_PUBLIC void pkFreeVM(PKVM* vm);
// Interpret the source and return the result.
PK_PUBLIC PKInterpretResult pkInterpretSource(PKVM* vm,
const char* source,
const char* path);
// Compile and execut file at given path.
PK_PUBLIC PKInterpretResult pkInterpret(PKVM* vm, const char* path);
/*****************************************************************************/
/* NATIVE FUNCTION API */
/*****************************************************************************/
// Set a runtime error to vm.
PK_PUBLIC void pkSetRuntimeError(PKVM* vm, const char* message);
// Returns the associated user data.
PK_PUBLIC void* pkGetUserData(PKVM* vm);
// Return the type of the [value] this will help to get the type of the
// variable that was extracted from pkGetArg() earlier.
PK_PUBLIC PkVarType pkGetValueType(PkVar value);
// Update the user data of the vm.
PK_PUBLIC void pkSetUserData(PKVM* vm, void* user_data);
// Return the current functions argument count. This is needed for functions
// registered with -1 argument count (which means variadic arguments).
PK_PUBLIC int pkGetArgc(PKVM* vm);
// Encode types to var.
// TODO: user need to use vmPushTempRoot() for strings.
PK_PUBLIC Var pkVarBool(PKVM* vm, bool value);
PK_PUBLIC Var pkVarNumber(PKVM* vm, double value);
PK_PUBLIC Var pkVarString(PKVM* vm, const char* value);
// Return the [arg] th argument as a PkVar. This pointer will only be
// valid till the current function ends, because it points to the var at the
// stack and it'll popped when the current call frame ended. Use handlers to
// keep the var alive even after that.
PK_PUBLIC PkVar pkGetArg(PKVM* vm, int arg);
// The below functions are used to extract the function arguments from the
// stack as a type. They will first validate the argument's type and set a
// runtime error if it's not and return false. Otherwise it'll set the [value]
// with the extracted value. Note that the arguments are 1 based (to get the
// first argument use 1 not 0).
PK_PUBLIC bool pkGetArgBool(PKVM* vm, int arg, bool* vlaue);
PK_PUBLIC bool pkGetArgNumber(PKVM* vm, int arg, double* value);
PK_PUBLIC bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value);
// The below functions are used to set the return value of the current native
// function's. Don't use it outside a registered native function.
PK_PUBLIC void pkReturnNull(PKVM* vm);
PK_PUBLIC void pkReturnBool(PKVM* vm, bool value);
PK_PUBLIC void pkReturnNumber(PKVM* vm, double value);
PK_PUBLIC void pkReturnValue(PKVM* vm, PkVar value);
/*****************************************************************************/
/* POCKETLANG TYPE FUNCTIONS */
/*****************************************************************************/
// The below functions will allocate a new object and return's it's value
// wrapped around a handler.
PK_PUBLIC PkHandle* pkNewString(PKVM* vm, const char* value);
PK_PUBLIC PkHandle* pkNewList(PKVM* vm);
PK_PUBLIC PkHandle* pkNewMap(PKVM* vm);
PK_PUBLIC const char* pkStringGetData(PkVar value);
// TODO:
// The below functions will push the primitive values on the stack and return
// it's pointer as a PkVar it's usefull to convert your primitive values as
// pocketlang variables.
//PK_PUBLIC PkVar pkPushNull(PKVM* vm);
//PK_PUBLIC PkVar pkPushBool(PKVM* vm, bool value);
//PK_PUBLIC PkVar pkPushNumber(PKVM* vm, double value);
// Decode var types.
// TODO: const char* should be copied otherwise it'll become dangling pointer.
PK_PUBLIC bool pkAsBool(PKVM* vm, Var value);
PK_PUBLIC double pkAsNumber(PKVM* vm, Var value);
PK_PUBLIC const char* pkAsString(PKVM* vm, Var value);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // MINISCRIPT_H
#endif // POCKETLANG_H

View File

@ -9,32 +9,57 @@
#include "var.h"
#include "vm.h"
// Public Api /////////////////////////////////////////////////////////////////
Var pkVarBool(PKVM* vm, bool value) {
return VAR_BOOL(value);
/*****************************************************************************/
/* PUBLIC API */
/*****************************************************************************/
PkVarType pkGetValueType(PkVar value) {
__ASSERT(value != NULL, "Given value was NULL.");
if (IS_NULL(*(Var*)(value))) return PK_NULL;
if (IS_BOOL(*(Var*)(value))) return PK_BOOL;
if (IS_NUM(*(Var*)(value))) return PK_NUMBER;
__ASSERT(IS_OBJ(*(Var*)(value)),
"Invalid var pointer. Might be a dangling pointer");
Object* obj = AS_OBJ(*(Var*)(value));
switch (obj->type) {
case OBJ_STRING: return PK_STRING;
case OBJ_LIST: return PK_LIST;
case OBJ_MAP: return PK_MAP;
case OBJ_RANGE: return PK_RANGE;
case OBJ_SCRIPT: return PK_SCRIPT;
case OBJ_FUNC: return PK_FUNCTION;
case OBJ_FIBER: return PK_FIBER;
}
UNREACHABLE();
return (PkVarType)0;
}
Var pkVarNumber(PKVM* vm, double value) {
return VAR_NUM(value);
PkHandle* pkNewString(PKVM* vm, const char* value) {
return vmNewHandle(vm, VAR_OBJ(&newString(vm, value)->_super));
}
Var pkVarString(PKVM* vm, const char* value) {
return VAR_OBJ(newStringLength(vm, value, (uint32_t)strlen(value)));
PkHandle* pkNewList(PKVM* vm) {
return vmNewHandle(vm, VAR_OBJ(&newList(vm, MIN_CAPACITY)->_super));
}
bool pkAsBool(PKVM* vm, Var value) {
return AS_BOOL(value);
PkHandle* pkNewMap(PKVM* vm) {
return vmNewHandle(vm, VAR_OBJ(&newMap(vm)->_super));
}
double pkAsNumber(PKVM* vm, Var value) {
return AS_NUM(value);
const char* pkStringGetData(PkVar value) {
Var str = (*(Var*)value);
__ASSERT(IS_OBJ(str) && AS_OBJ(str)->type == OBJ_STRING,
"Value should be of type string.");
return ((String*)AS_OBJ(str))->data;
}
const char* pkAsString(PKVM* vm, Var value) {
return AS_STRING(value)->data;
}
///////////////////////////////////////////////////////////////////////////////
/*****************************************************************************/
/* VAR INTERNALS */
/*****************************************************************************/
// Number of maximum digits for to_string buffer.
#define TO_STRING_BUFF_SIZE 128
@ -644,14 +669,26 @@ void freeObject(PKVM* vm, Object* self) {
// Utility functions //////////////////////////////////////////////////////////
const char* varTypeName(Var v) {
if (IS_NULL(v)) return "null";
if (IS_BOOL(v)) return "bool";
if (IS_NUM(v)) return "number";
const char* getPkVarTypeName(PkVarType type) {
switch (type) {
case PK_NULL: return "null";
case PK_BOOL: return "bool";
case PK_NUMBER: return "number";
case PK_STRING: return "String";
case PK_LIST: return "List";
case PK_MAP: return "Map";
case PK_RANGE: return "Range";
case PK_SCRIPT: return "Script";
case PK_FUNCTION: return "Function";
case PK_FIBER: return "Fiber";
}
ASSERT(IS_OBJ(v), OOPS);
Object* obj = AS_OBJ(v);
switch (obj->type) {
UNREACHABLE();
return NULL;
}
const char* getObjectTypeName(ObjectType type) {
switch (type) {
case OBJ_STRING: return "String";
case OBJ_LIST: return "List";
case OBJ_MAP: return "Map";
@ -665,6 +702,16 @@ const char* varTypeName(Var v) {
}
}
const char* varTypeName(Var v) {
if (IS_NULL(v)) return "null";
if (IS_BOOL(v)) return "bool";
if (IS_NUM(v)) return "number";
ASSERT(IS_OBJ(v), OOPS);
Object* obj = AS_OBJ(v);
return getObjectTypeName(obj->type);
}
bool isValuesSame(Var v1, Var v2) {
#if VAR_NAN_TAGGING
// Bit representation of each values are unique so just compare the bits.

View File

@ -180,18 +180,15 @@ typedef struct {
#endif // VAR_NAN_TAGGING
typedef enum /* ObjectType */ {
typedef enum {
OBJ_STRING,
OBJ_LIST,
OBJ_MAP,
OBJ_RANGE,
OBJ_SCRIPT,
OBJ_FUNC,
OBJ_FIBER,
// TODO: remove OBJ_USER and implement handlers for that.
// TODO:
OBJ_USER,
} ObjectType;
@ -433,6 +430,12 @@ void freeObject(PKVM* vm, Object* self);
// Utility functions //////////////////////////////////////////////////////////
// Returns the type name of the PkVarType enum value.
const char* getPkVarTypeName(PkVarType type);
// Returns the type name of the ObjectType enum value.
const char* getObjectTypeName(ObjectType type);
// Returns the type name of the var [v].
const char* varTypeName(Var v);

View File

@ -107,9 +107,41 @@ void pkFreeVM(PKVM* self) {
self->gray_list = (Object**)self->config.realloc_fn(
self->gray_list, 0, self->config.user_data);
// Tell the host application that it forget to release all of it's handles
// before freeing the VM.
__ASSERT(self->handles != NULL, "Not all handles were released.");
DEALLOCATE(self, self);
}
PkHandle* pkNewHandle(PKVM* vm, PkVar value) {
return vmNewHandle(vm, *((Var*)value));
}
PkVar pkGetHandleValue(PkHandle* handle) {
return (PkVar)&handle->value;
}
void pkReleaseHandle(PKVM* vm, PkHandle* handle) {
__ASSERT(handle != NULL, "Given handle was NULL.");
// If the handle is the head of the vm's handle chain set it to the next one.
if (handle == vm->handles) {
vm->handles = handle->next;
}
// Remove the handle from the chain by connecting the both ends together.
if (handle->next) handle->next->prev = handle->prev;
if (handle->prev) handle->prev->next = handle->next;
// Free the handle.
DEALLOCATE(vm, handle);
}
/*****************************************************************************/
/* VM INTERNALS */
/*****************************************************************************/
void vmPushTempRef(PKVM* self, Object* obj) {
ASSERT(obj != NULL, "Cannot reference to NULL.");
ASSERT(self->temp_reference_count < MAX_TEMP_REFERENCE,
@ -122,6 +154,16 @@ void vmPopTempRef(PKVM* self) {
self->temp_reference_count--;
}
PkHandle* vmNewHandle(PKVM* self, Var value) {
PkHandle* handle = (PkHandle*)ALLOCATE(self, PkHandle);
handle->value = value;
handle->prev = NULL;
handle->next = self->handles;
if (handle->next != NULL) handle->next->prev = handle;
self->handles = handle;
return handle;
}
void vmCollectGarbage(PKVM* self) {
// Reset VM's bytes_allocated value and count it again so that we don't
@ -142,6 +184,11 @@ void vmCollectGarbage(PKVM* self) {
grayObject(self, self->temp_reference[i]);
}
// Mark the handles.
for (PkHandle* handle = self->handles; handle != NULL; handle = handle->next) {
grayValue(self, handle->value);
}
// Garbage collection triggered at the middle of a compilation.
if (self->compiler != NULL) {
compilerMarkObjects(self, self->compiler);
@ -326,6 +373,7 @@ static inline void pushCallFrame(PKVM* vm, Function* fn) {
}
void pkSetRuntimeError(PKVM* vm, const char* message) {
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
vm->fiber->error = newString(vm, message);
}
@ -349,7 +397,7 @@ void vmReportError(PKVM* vm) {
// This function is responsible to call on_done function if it's done with the
// provided string pointers.
static PKInterpretResult interpretSource(PKVM* vm, pkStringPtr source,
static PkInterpretResult interpretSource(PKVM* vm, pkStringPtr source,
pkStringPtr path) {
String* path_name = newString(vm, path.string);
if (path.on_done) path.on_done(vm, path);
@ -376,7 +424,7 @@ static PKInterpretResult interpretSource(PKVM* vm, pkStringPtr source,
return vmRunScript(vm, scr);
}
PKInterpretResult pkInterpretSource(PKVM* vm, const char* source,
PkInterpretResult pkInterpretSource(PKVM* vm, const char* source,
const char* path) {
// Call the internal interpretSource implementation.
pkStringPtr source_ptr = { source, NULL, NULL };
@ -384,7 +432,7 @@ PKInterpretResult pkInterpretSource(PKVM* vm, const char* source,
return interpretSource(vm, source_ptr, path_ptr);
}
PKInterpretResult pkInterpret(PKVM* vm, const char* path) {
PkInterpretResult pkInterpret(PKVM* vm, const char* path) {
pkStringPtr resolved;
resolved.string = path;
@ -413,7 +461,7 @@ PKInterpretResult pkInterpret(PKVM* vm, const char* path) {
return interpretSource(vm, source, resolved);
}
PKInterpretResult vmRunScript(PKVM* vm, Script* _script) {
PkInterpretResult vmRunScript(PKVM* vm, Script* _script) {
// Reference to the instruction pointer in the call frame.
register uint8_t** ip;

View File

@ -32,6 +32,14 @@ typedef struct {
Function* fn; //< Native function pointer.
} BuiltinFn;
// A doubly link list of vars that have reference in the host application.
struct PkHandle {
Var value;
PkHandle* prev;
PkHandle* next;
};
struct PKVM {
// The first object in the link list of all heap allocated objects.
@ -61,6 +69,9 @@ struct PKVM {
Object* temp_reference[MAX_TEMP_REFERENCE];
int temp_reference_count;
// Pointer to the first handle in the doubly linked list of handles.
PkHandle* handles;
// VM's configurations.
pkConfiguration config;
@ -109,10 +120,14 @@ void vmPushTempRef(PKVM* self, Object* obj);
// Pop the top most object from temporary reference stack.
void vmPopTempRef(PKVM* self);
// Create and return a new handle for the [value].
PkHandle* vmNewHandle(PKVM* self, Var value);
// Trigger garbage collection manually.
void vmCollectGarbage(PKVM* self);
// Runs the script and return result.
PKInterpretResult vmRunScript(PKVM* vm, Script* script);
PkInterpretResult vmRunScript(PKVM* vm, Script* script);
#endif // VM_H

View File

@ -13,7 +13,7 @@ from path import *
import "basics.pk" ## will import all
import "if.pk" as if_test
from "chain_call.pk" import fn1, fn2 as f2, fn3
from "functions.pk" import fn1, fn2 as f2, fn3
## If it has a module name it'll bind to that name.
import 'import/module.pk'