Library support in interpreter (docs soon)
This commit is contained in:
@@ -5,6 +5,9 @@
|
|||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
int currentInstruction = 0;
|
int currentInstruction = 0;
|
||||||
|
|
||||||
@@ -113,6 +116,8 @@ GroundFunction* createGroundFunction() {
|
|||||||
gf->argSize = 0;
|
gf->argSize = 0;
|
||||||
gf->args = malloc(sizeof(GroundFunctionArgs));
|
gf->args = malloc(sizeof(GroundFunctionArgs));
|
||||||
gf->program = createGroundProgram();
|
gf->program = createGroundProgram();
|
||||||
|
gf->isNative = false;
|
||||||
|
gf->nativeFn = NULL;
|
||||||
return gf;
|
return gf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,6 +216,24 @@ GroundDebugInstruction parseDebugInstruction(char* in) {
|
|||||||
return gdi;
|
return gdi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void groundAddNativeFunction(GroundScope* scope, char* name, NativeGroundFunction fn, GroundValueType returnType, int argCount, ...) {
|
||||||
|
GroundFunction* gf = createGroundFunction();
|
||||||
|
gf->isNative = true;
|
||||||
|
gf->nativeFn = fn;
|
||||||
|
gf->returnType = returnType;
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, argCount);
|
||||||
|
for(int i = 0; i < argCount; i++) {
|
||||||
|
GroundValueType type = va_arg(args, GroundValueType);
|
||||||
|
char* argName = va_arg(args, char*);
|
||||||
|
addArgsToGroundFunction(gf, type, argName);
|
||||||
|
}
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
addVariable(scope->variables, name, createFunctionGroundValue(gf));
|
||||||
|
}
|
||||||
|
|
||||||
GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
|
GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
|
||||||
GroundLabel* labels = NULL;
|
GroundLabel* labels = NULL;
|
||||||
GroundVariable* variables = NULL;
|
GroundVariable* variables = NULL;
|
||||||
@@ -1449,23 +1472,156 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
|
|||||||
if (function->argSize > in->args.length - 2) {
|
if (function->argSize > in->args.length - 2) {
|
||||||
runtimeError(TOO_MANY_ARGS, "Incorrect amount of arguments provided for function", in, currentInstruction);
|
runtimeError(TOO_MANY_ARGS, "Incorrect amount of arguments provided for function", in, currentInstruction);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < function->argSize; i++) {
|
|
||||||
if (in->args.args[i + 1].type != VALUE) {
|
if (function->isNative) {
|
||||||
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction);
|
List argsList = createList();
|
||||||
|
for (size_t i = 0; i < function->argSize; i++) {
|
||||||
|
if (in->args.args[i + 1].type != VALUE) {
|
||||||
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction);
|
||||||
|
}
|
||||||
|
if (in->args.args[i + 1].value.value.type != function->args[i].type) {
|
||||||
|
runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction);
|
||||||
|
}
|
||||||
|
appendToList(&argsList, in->args.args[i + 1].value.value);
|
||||||
}
|
}
|
||||||
if (in->args.args[i + 1].value.value.type != function->args[i].type) {
|
|
||||||
runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction);
|
if (function->nativeFn) {
|
||||||
|
GroundValue returnValue = function->nativeFn(scope, argsList);
|
||||||
|
if (returnValue.type != function->returnType) {
|
||||||
|
runtimeError(RETURN_TYPE_MISMATCH, "Unexpected return value type from native function", in, currentInstruction);
|
||||||
|
}
|
||||||
|
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
|
||||||
}
|
}
|
||||||
addVariable(newScope.variables, function->args[i].name, in->args.args[i + 1].value.value);
|
free(argsList.values);
|
||||||
|
} else {
|
||||||
|
for (size_t i = 0; i < function->argSize; i++) {
|
||||||
|
if (in->args.args[i + 1].type != VALUE) {
|
||||||
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction);
|
||||||
|
}
|
||||||
|
if (in->args.args[i + 1].value.value.type != function->args[i].type) {
|
||||||
|
runtimeError(ARG_TYPE_MISMATCH, "Mismatched function argument types", in, currentInstruction);
|
||||||
|
}
|
||||||
|
addVariable(newScope.variables, function->args[i].name, in->args.args[i + 1].value.value);
|
||||||
|
}
|
||||||
|
size_t currentCurrentInstruction = currentInstruction;
|
||||||
|
currentInstruction = function->startLine;
|
||||||
|
GroundValue returnValue = interpretGroundProgram(&function->program, &newScope);
|
||||||
|
if (returnValue.type != function->returnType) {
|
||||||
|
runtimeError(RETURN_TYPE_MISMATCH, "Unexpected return value type from function", in, currentInstruction);
|
||||||
|
}
|
||||||
|
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
|
||||||
|
currentInstruction = currentCurrentInstruction;
|
||||||
}
|
}
|
||||||
size_t currentCurrentInstruction = currentInstruction;
|
break;
|
||||||
currentInstruction = function->startLine;
|
}
|
||||||
GroundValue returnValue = interpretGroundProgram(&function->program, &newScope);
|
|
||||||
if (returnValue.type != function->returnType) {
|
case USE: {
|
||||||
runtimeError(RETURN_TYPE_MISMATCH, "Unexpected return value type from function", in, currentInstruction);
|
if (in->args.length < 1) {
|
||||||
|
runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", in, currentInstruction);
|
||||||
}
|
}
|
||||||
addVariable(scope->variables, in->args.args[in->args.length - 1].value.refName, returnValue);
|
if (in->args.args[0].type != VALUE || in->args.args[0].value.value.type != STRING) {
|
||||||
currentInstruction = currentCurrentInstruction;
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a String for arg 1", in, currentInstruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* libName = in->args.args[0].value.value.data.stringVal;
|
||||||
|
char path[1024];
|
||||||
|
int found = 0;
|
||||||
|
|
||||||
|
char* envPath = getenv("GROUND_LIBS");
|
||||||
|
|
||||||
|
if (envPath) {
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", envPath, libName);
|
||||||
|
if (access(path, F_OK) == 0) found = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
snprintf(path, sizeof(path), "/usr/lib/ground/%s", libName);
|
||||||
|
if (access(path, F_OK) == 0) found = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
snprintf(path, sizeof(path), "%s", libName);
|
||||||
|
if (access(path, F_OK) == 0) found = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
char errorMsg[1100];
|
||||||
|
snprintf(errorMsg, sizeof(errorMsg), "Could not find library: %s", libName);
|
||||||
|
runtimeError(FIXME, errorMsg, in, currentInstruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* f = fopen(path, "r");
|
||||||
|
if (!f) {
|
||||||
|
runtimeError(FIXME, "Failed to open file", in, currentInstruction);
|
||||||
|
}
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
long fsize = ftell(f);
|
||||||
|
fseek(f, 0, SEEK_SET);
|
||||||
|
|
||||||
|
char* content = malloc(fsize + 1);
|
||||||
|
if (!content) {
|
||||||
|
fclose(f);
|
||||||
|
runtimeError(FIXME, "Failed to allocate memory for file content", in, currentInstruction);
|
||||||
|
}
|
||||||
|
fread(content, 1, fsize, f);
|
||||||
|
fclose(f);
|
||||||
|
content[fsize] = 0;
|
||||||
|
|
||||||
|
GroundProgram program = parseFile(content);
|
||||||
|
free(content);
|
||||||
|
|
||||||
|
interpretGroundProgram(&program, scope);
|
||||||
|
freeGroundProgram(&program);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EXTERN: {
|
||||||
|
if (in->args.length < 1) {
|
||||||
|
runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", in, currentInstruction);
|
||||||
|
}
|
||||||
|
if (in->args.args[0].type != VALUE || in->args.args[0].value.value.type != STRING) {
|
||||||
|
runtimeError(ARG_TYPE_MISMATCH, "Expecting a String for arg 1", in, currentInstruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* libName = in->args.args[0].value.value.data.stringVal;
|
||||||
|
char path[1024];
|
||||||
|
int found = 0;
|
||||||
|
|
||||||
|
char* envPath = getenv("GROUND_LIBS");
|
||||||
|
|
||||||
|
if (envPath) {
|
||||||
|
snprintf(path, sizeof(path), "%s/%s", envPath, libName);
|
||||||
|
if (access(path, F_OK) == 0) found = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
snprintf(path, sizeof(path), "/usr/lib/ground/%s", libName);
|
||||||
|
if (access(path, F_OK) == 0) found = 1;
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
snprintf(path, sizeof(path), "./%s", libName);
|
||||||
|
if (access(path, F_OK) == 0) found = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
char errorMsg[1100];
|
||||||
|
snprintf(errorMsg, sizeof(errorMsg), "Could not find external library: %s", libName);
|
||||||
|
runtimeError(FIXME, errorMsg, in, currentInstruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* handle = dlopen(path, RTLD_LAZY);
|
||||||
|
if (!handle) {
|
||||||
|
runtimeError(FIXME, dlerror(), in, currentInstruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef void (*GroundInitFn)(GroundScope*);
|
||||||
|
GroundInitFn initFn = (GroundInitFn)dlsym(handle, "ground_init");
|
||||||
|
|
||||||
|
if (!initFn) {
|
||||||
|
runtimeError(FIXME, "Could not find 'ground_init' symbol in library", in, currentInstruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
initFn(scope);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ typedef enum ListAccessStatus {
|
|||||||
|
|
||||||
struct GroundValue;
|
struct GroundValue;
|
||||||
struct GroundFunction;
|
struct GroundFunction;
|
||||||
|
struct GroundScope;
|
||||||
|
|
||||||
struct List;
|
struct List;
|
||||||
|
|
||||||
@@ -110,6 +111,11 @@ typedef struct GroundFunctionArgs {
|
|||||||
char* name;
|
char* name;
|
||||||
} GroundFunctionArgs;
|
} GroundFunctionArgs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Native function pointer type.
|
||||||
|
*/
|
||||||
|
typedef GroundValue (*NativeGroundFunction)(struct GroundScope* scope, List args);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Represents a Ground function.
|
* Represents a Ground function.
|
||||||
*/
|
*/
|
||||||
@@ -119,6 +125,8 @@ typedef struct GroundFunction {
|
|||||||
GroundValueType returnType;
|
GroundValueType returnType;
|
||||||
GroundProgram program;
|
GroundProgram program;
|
||||||
size_t startLine;
|
size_t startLine;
|
||||||
|
bool isNative;
|
||||||
|
NativeGroundFunction nativeFn;
|
||||||
} GroundFunction;
|
} GroundFunction;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user