Experimental external library support

This commit is contained in:
2025-08-25 17:35:16 +10:00
parent 38b17e7db5
commit e56e6de911
2 changed files with 216 additions and 3 deletions

View File

@@ -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`

View File

@@ -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;