From 04b64e10d198f56ca679866ad4ded8e1a5b71b44 Mon Sep 17 00:00:00 2001 From: Maxwell Jeffress Date: Fri, 2 Jan 2026 20:29:27 +1100 Subject: [PATCH] Library support in interpreter (docs soon) --- src/interpreter.c | 182 ++++++++++++++++++++++++++++++++++++++++++---- src/types.h | 8 ++ 2 files changed, 177 insertions(+), 13 deletions(-) diff --git a/src/interpreter.c b/src/interpreter.c index 1680c2f..d08f44b 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -5,6 +5,9 @@ #include #include #include +#include +#include +#include int currentInstruction = 0; @@ -113,6 +116,8 @@ GroundFunction* createGroundFunction() { gf->argSize = 0; gf->args = malloc(sizeof(GroundFunctionArgs)); gf->program = createGroundProgram(); + gf->isNative = false; + gf->nativeFn = NULL; return gf; } @@ -211,6 +216,24 @@ GroundDebugInstruction parseDebugInstruction(char* in) { 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) { GroundLabel* labels = NULL; GroundVariable* variables = NULL; @@ -1449,23 +1472,156 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop if (function->argSize > in->args.length - 2) { 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) { - runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction); + + if (function->isNative) { + 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; - 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); + break; + } + + case USE: { + 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); - currentInstruction = currentCurrentInstruction; + 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 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; } diff --git a/src/types.h b/src/types.h index 28c4979..574137a 100644 --- a/src/types.h +++ b/src/types.h @@ -25,6 +25,7 @@ typedef enum ListAccessStatus { struct GroundValue; struct GroundFunction; +struct GroundScope; struct List; @@ -110,6 +111,11 @@ typedef struct GroundFunctionArgs { char* name; } GroundFunctionArgs; +/* + * Native function pointer type. + */ +typedef GroundValue (*NativeGroundFunction)(struct GroundScope* scope, List args); + /* * Represents a Ground function. */ @@ -119,6 +125,8 @@ typedef struct GroundFunction { GroundValueType returnType; GroundProgram program; size_t startLine; + bool isNative; + NativeGroundFunction nativeFn; } GroundFunction;