forked from ground/ground
Experimental external library support
This commit is contained in:
213
src/main.cpp
213
src/main.cpp
@@ -43,6 +43,23 @@
|
||||
#include <cstdlib>
|
||||
#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;
|
||||
|
||||
/*
|
||||
@@ -256,6 +273,26 @@ struct Function {
|
||||
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
|
||||
Contains the code of functions and types of their values
|
||||
@@ -268,6 +305,58 @@ map<string, Function> functions;
|
||||
*/
|
||||
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
|
||||
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");
|
||||
}
|
||||
|
||||
// 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
|
||||
if (functions.find(ref.fnName) == functions.end()) {
|
||||
error("Function " + ref.fnName + " not found");
|
||||
@@ -1710,7 +1824,104 @@ Literal exec(vector<Instruction> in, bool executingFunction) {
|
||||
Literal ret = exec(parser(lexer(in)), false); }
|
||||
break;
|
||||
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;
|
||||
default:
|
||||
cout << "Still to be implemented" << endl;
|
||||
|
Reference in New Issue
Block a user