forked from ground/ground
Experimental external library support
This commit is contained in:
@@ -282,10 +282,12 @@ Note: Ground will check the directory where the program is being run from when t
|
|||||||
|
|
||||||
Usage: `use $stringvalue`
|
Usage: `use $stringvalue`
|
||||||
|
|
||||||
#### extern (WORK IN PROGRESS)
|
#### extern (Experimental, please report bugs!)
|
||||||
|
|
||||||
Attempts to import a shared object library written for Ground. All functions in the external library will be usable with `call`.
|
Attempts to import a shared object library written for Ground. All functions in the external library will be usable with `call`.
|
||||||
|
|
||||||
Note: Ground will check the directory where the program is being run from when trying to find external programs. If that fails, it will check the directory set in the $GROUND_LIBS environment variable set by your system. The '.so', '.dll', etc extension is appended automatically.
|
Note: Ground will check the directory set in the $GROUND_LIBS environment variable set by your system. The '.so' (Linux), '.dylib' (macOS), or '.dll' (Windows) extension is appended automatically.
|
||||||
|
|
||||||
|
Documentation on how to do external libraries coming soon.
|
||||||
|
|
||||||
Usage: `extern $stringvalue`
|
Usage: `extern $stringvalue`
|
||||||
|
213
src/main.cpp
213
src/main.cpp
@@ -43,6 +43,23 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
|
// Headers for external libraries
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Note: Windows support is experiemental. Maybe try using a superior
|
||||||
|
// operating system? (cough cough, Linux?)
|
||||||
|
#include <windows.h>
|
||||||
|
#define DLOPEN(path) LoadLibrary(path)
|
||||||
|
#define DLSYM(handle, name) GetProcAddress(handle, name)
|
||||||
|
#define DLCLOSE(handle) FreeLibrary(handle)
|
||||||
|
#define DLERROR() "Windows DLL Error"
|
||||||
|
#else
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#define DLOPEN(path) dlopen(path, RTLD_LAZY)
|
||||||
|
#define DLSYM(handle, name) dlsym(handle, name)
|
||||||
|
#define DLCLOSE(handle) dlclose(handle)
|
||||||
|
#define DLERROR() dlerror()
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -256,6 +273,26 @@ struct Function {
|
|||||||
map<string, int> localLabels;
|
map<string, int> localLabels;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// C-compatible enum and types for developing libraries for Ground in C
|
||||||
|
typedef enum {
|
||||||
|
GROUND_INT,
|
||||||
|
GROUND_DOUBLE,
|
||||||
|
GROUND_BOOL,
|
||||||
|
GROUND_STRING,
|
||||||
|
GROUND_CHAR
|
||||||
|
} GroundType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GroundType type;
|
||||||
|
union {
|
||||||
|
int int_val;
|
||||||
|
double double_val;
|
||||||
|
int bool_val;
|
||||||
|
char* string_val;
|
||||||
|
char char_val;
|
||||||
|
} data;
|
||||||
|
} GroundValue;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
functions map
|
functions map
|
||||||
Contains the code of functions and types of their values
|
Contains the code of functions and types of their values
|
||||||
@@ -268,6 +305,58 @@ map<string, Function> functions;
|
|||||||
*/
|
*/
|
||||||
vector<Literal> fnArgs;
|
vector<Literal> fnArgs;
|
||||||
|
|
||||||
|
// External library functions and other things
|
||||||
|
|
||||||
|
// Handle to loaded libraries
|
||||||
|
map<string, void*> loadedLibraries;
|
||||||
|
|
||||||
|
// Map of function name to function pointer
|
||||||
|
map<string, void*> externalFunctions;
|
||||||
|
|
||||||
|
// Conversion functions
|
||||||
|
GroundValue literalToGroundValue(const Literal& lit) {
|
||||||
|
GroundValue gv;
|
||||||
|
if (holds_alternative<int>(lit.val)) {
|
||||||
|
gv.type = GROUND_INT;
|
||||||
|
gv.data.int_val = get<int>(lit.val);
|
||||||
|
} else if (holds_alternative<double>(lit.val)) {
|
||||||
|
gv.type = GROUND_DOUBLE;
|
||||||
|
gv.data.double_val = get<double>(lit.val);
|
||||||
|
} else if (holds_alternative<bool>(lit.val)) {
|
||||||
|
gv.type = GROUND_BOOL;
|
||||||
|
gv.data.bool_val = get<bool>(lit.val) ? 1 : 0;
|
||||||
|
} else if (holds_alternative<string>(lit.val)) {
|
||||||
|
gv.type = GROUND_STRING;
|
||||||
|
gv.data.string_val = const_cast<char*>(get<string>(lit.val).c_str());
|
||||||
|
} else if (holds_alternative<char>(lit.val)) {
|
||||||
|
gv.type = GROUND_CHAR;
|
||||||
|
gv.data.char_val = get<char>(lit.val);
|
||||||
|
}
|
||||||
|
return gv;
|
||||||
|
}
|
||||||
|
|
||||||
|
Literal groundValueToLiteral(const GroundValue& gv) {
|
||||||
|
Literal lit;
|
||||||
|
switch (gv.type) {
|
||||||
|
case GROUND_INT:
|
||||||
|
lit.val = gv.data.int_val;
|
||||||
|
break;
|
||||||
|
case GROUND_DOUBLE:
|
||||||
|
lit.val = gv.data.double_val;
|
||||||
|
break;
|
||||||
|
case GROUND_BOOL:
|
||||||
|
lit.val = (gv.data.bool_val != 0);
|
||||||
|
break;
|
||||||
|
case GROUND_STRING:
|
||||||
|
lit.val = string(gv.data.string_val);
|
||||||
|
break;
|
||||||
|
case GROUND_CHAR:
|
||||||
|
lit.val = gv.data.char_val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return lit;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
error function
|
error function
|
||||||
Takes a string (which is a debug message) and prints it to the console, letting the
|
Takes a string (which is a debug message) and prints it to the console, letting the
|
||||||
@@ -1631,6 +1720,31 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
|||||||
error("Second argument of call must be a direct reference");
|
error("Second argument of call must be a direct reference");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for external function
|
||||||
|
if (externalFunctions.find(ref.fnName) != externalFunctions.end()) {
|
||||||
|
// Call external function
|
||||||
|
typedef GroundValue (*ExtFunc)(GroundValue*, int);
|
||||||
|
ExtFunc extFunc = (ExtFunc)externalFunctions[ref.fnName];
|
||||||
|
|
||||||
|
// Convert arguments
|
||||||
|
vector<GroundValue> gvArgs;
|
||||||
|
for (const Literal& arg : fnArgs) {
|
||||||
|
gvArgs.push_back(literalToGroundValue(arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call function
|
||||||
|
GroundValue result = extFunc(gvArgs.data(), gvArgs.size());
|
||||||
|
|
||||||
|
// Convert result back
|
||||||
|
Literal resultLit = groundValueToLiteral(result);
|
||||||
|
|
||||||
|
// Clear arguments and store result
|
||||||
|
fnArgs.clear();
|
||||||
|
variables[returnRef.varName] = resultLit;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if function exists
|
// Check if function exists
|
||||||
if (functions.find(ref.fnName) == functions.end()) {
|
if (functions.find(ref.fnName) == functions.end()) {
|
||||||
error("Function " + ref.fnName + " not found");
|
error("Function " + ref.fnName + " not found");
|
||||||
@@ -1710,7 +1824,104 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
|||||||
Literal ret = exec(parser(lexer(in)), false); }
|
Literal ret = exec(parser(lexer(in)), false); }
|
||||||
break;
|
break;
|
||||||
case Instructions::Extern:
|
case Instructions::Extern:
|
||||||
cout << "Still to be implemented" << endl;
|
if (l.args.size() < 1) {
|
||||||
|
error("Could not find all arguments for Extern inbuilt");
|
||||||
|
}
|
||||||
|
{
|
||||||
|
string libName;
|
||||||
|
|
||||||
|
if (holds_alternative<Literal>(l.args[0])) {
|
||||||
|
if (holds_alternative<string>(get<Literal>(l.args[0]).val)) {
|
||||||
|
libName = get<string>(get<Literal>(l.args[0]).val);
|
||||||
|
} else {
|
||||||
|
error("First argument for extern requires a string literal");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error("First argument for extern requires a string literal");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add appropriate extension
|
||||||
|
string fullLibName = libName;
|
||||||
|
#ifdef _WIN32
|
||||||
|
fullLibName += ".dll";
|
||||||
|
#elif __APPLE__
|
||||||
|
fullLibName += ".dylib";
|
||||||
|
#else
|
||||||
|
fullLibName += ".so";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check multiple locations for the library
|
||||||
|
string libPath;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
// Check GROUND_LIBS directory
|
||||||
|
const char* groundLibsDir = getenv("GROUND_LIBS");
|
||||||
|
if (groundLibsDir) {
|
||||||
|
string envPath = string(groundLibsDir);
|
||||||
|
// Add trailing slash if not present
|
||||||
|
if (!envPath.empty() && envPath.back() != '/' && envPath.back() != '\\') {
|
||||||
|
envPath += "/";
|
||||||
|
}
|
||||||
|
string fullEnvPath = envPath + fullLibName;
|
||||||
|
if (filesystem::exists(fullEnvPath)) {
|
||||||
|
libPath = fullEnvPath;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
error("Could not find external library: " + fullLibName +
|
||||||
|
" (searched current directory and GROUND_LIBS)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to open the library
|
||||||
|
void* handle = DLOPEN(libPath.c_str());
|
||||||
|
if (!handle) {
|
||||||
|
error("Failed to load library " + libPath + ": " + string(DLERROR()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store handle for cleanup later
|
||||||
|
loadedLibraries[libName] = handle;
|
||||||
|
|
||||||
|
// Get required functions
|
||||||
|
typedef const char** (*GetFunctionsFunc)();
|
||||||
|
typedef void* (*GetFunctionFunc)(const char*);
|
||||||
|
|
||||||
|
GetFunctionsFunc getFunctions = (GetFunctionsFunc)DLSYM(handle, "ground_get_functions");
|
||||||
|
GetFunctionFunc getFunction = (GetFunctionFunc)DLSYM(handle, "ground_get_function");
|
||||||
|
|
||||||
|
if (!getFunctions || !getFunction) {
|
||||||
|
error("Library " + libName + " is not Ground-compatible (missing required functions: ground_get_functions or ground_get_function)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional initialization
|
||||||
|
typedef void (*InitFunc)();
|
||||||
|
InitFunc initFunc = (InitFunc)DLSYM(handle, "ground_lib_init");
|
||||||
|
if (initFunc) {
|
||||||
|
initFunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register all functions
|
||||||
|
const char** functionNames = getFunctions();
|
||||||
|
if (!functionNames) {
|
||||||
|
error("Library " + libName + " returned null function list");
|
||||||
|
}
|
||||||
|
|
||||||
|
int functionCount = 0;
|
||||||
|
for (int i = 0; functionNames[i] != nullptr; i++) {
|
||||||
|
void* funcPtr = getFunction(functionNames[i]);
|
||||||
|
if (funcPtr) {
|
||||||
|
externalFunctions[string(functionNames[i])] = funcPtr;
|
||||||
|
functionCount++;
|
||||||
|
} else {
|
||||||
|
error("Failed to get function pointer for: " + string(functionNames[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (functionCount == 0) {
|
||||||
|
error("No functions were loaded from library: " + libName);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
cout << "Still to be implemented" << endl;
|
cout << "Still to be implemented" << endl;
|
||||||
|
Reference in New Issue
Block a user