From 5577679ded80fc21b3fad7d36e282eaa6cd3046d Mon Sep 17 00:00:00 2001 From: Maxwell Jeffress Date: Mon, 2 Mar 2026 10:09:10 +1100 Subject: [PATCH] Closures in Ground --- include/groundvm.h | 4 +++ src/interface.c | 34 ++++++++++++++++++- src/interpreter.c | 82 +++++++++++++++++++++++----------------------- src/interpreter.h | 20 ----------- src/types.c | 21 ++++++++++++ src/types.h | 26 +++++++++++++++ tests/closure.grnd | 27 +++++++++++++++ 7 files changed, 152 insertions(+), 62 deletions(-) create mode 100644 tests/closure.grnd diff --git a/include/groundvm.h b/include/groundvm.h index 770f947..cf7e8fb 100644 --- a/include/groundvm.h +++ b/include/groundvm.h @@ -208,6 +208,10 @@ GroundProgram groundParseFile(const char* code); GroundStruct groundCreateStruct(); void groundAddFieldToStruct(GroundStruct* gstruct, char* name, GroundValue field); +// Run function returned by Ground code +// Use argc to set count of args, accepts that amount of GroundValue's after +GroundValue groundRunFunction(GroundFunction* function, size_t argc, ...); + #ifdef __cplusplus } #endif diff --git a/src/interface.c b/src/interface.c index 511cb43..a60c0ca 100644 --- a/src/interface.c +++ b/src/interface.c @@ -83,9 +83,19 @@ GroundValue groundCreateValue(GroundValueType type, ...) { return createNoneGroundValue(); break; } - case ERROR: case CUSTOM: { + // CUSTOM values are created from structs + GroundValue gv; + GroundStruct* gstruct = va_arg(args, GroundStruct*); + gv.type = CUSTOM; + gv.data.customVal = malloc(sizeof(GroundObject)); + *gv.data.customVal = createObject(*gstruct); + gv.customType = gstruct; + break; + } + case ERROR: { // FIXME + return createNoneGroundValue(); break; } } @@ -128,3 +138,25 @@ void groundAddFieldToStruct(GroundStruct* gstruct, char* name, GroundValue field char* groundCompileProgram(GroundProgram* program) { return compileGroundProgram(program); } + +GroundValue groundRunFunction(GroundFunction* function, size_t argc, ...) { + va_list args; + va_start(args, argc); + + GroundScope callScope = copyGroundScope(&function->closure); + + if (argc != function->argSize) { + return createErrorGroundValue(createGroundError("Too few or too many arguments for function", "callError", NULL, NULL)); + } + + for (size_t i = 0; i < argc; i++) { + GroundValue gv = va_arg(args, GroundValue); + if (checkFnTypes(&gv, &function->args[i]) == false) { + return createErrorGroundValue(createGroundError("Mismatched argument type", "callError", NULL, NULL)); + } + addVariable(callScope.variables, function->args[i].name, gv); + } + va_end(args); + + return interpretGroundProgram(&function->program, &callScope); +} diff --git a/src/interpreter.c b/src/interpreter.c index d1d41da..cdc4b01 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -449,6 +449,7 @@ GroundStruct parseStruct(GroundProgram* in, GroundScope* scope, size_t errorOffs GroundFunction* function = parseFunction(&gp, errorOffset); function->startLine = i; + function->closure = copyGroundScope(scope); GroundValue gv = createFunctionGroundValue(function); addFieldToStruct(&gstruct, name, gv); break; @@ -484,42 +485,6 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) { if (in->instructions[i].type == CREATELABEL) { addLabel(scope.labels, in->instructions[i].args.args[0].value.refName, i); } - if (in->instructions[i].type == FUN) { - if (in->instructions[i].args.length < 1) { - runtimeError(TOO_FEW_ARGS, "Expecting 1 or more args", &in->instructions[i], i); - } - if (in->instructions[i].args.args[0].type != FNREF) { - runtimeError(ARG_TYPE_MISMATCH, "Expecting a FunctionRef for arg 1", &in->instructions[i], i); - } - char* name = malloc(strlen(in->instructions[i].args.args[0].value.refName) + 1); - strcpy(name, in->instructions[i].args.args[0].value.refName); - - size_t counter = 1; - GroundProgram gp = createGroundProgram(); - addInstructionToProgram(&gp, in->instructions[i]); - size_t errorOffset = i; - i++; - while (counter > 0) { - if (i >= in->size) { - runtimeError(PREMATURE_EOF, "Reached end of scope before function definition ended", &in->instructions[i - 1], i - 1); - } - if (in->instructions[i].type == FUN) { - counter++; - } - if (in->instructions[i].type == ENDFUN) { - counter--; - } - addInstructionToProgram(&gp, in->instructions[i]); - i++; - } - i--; - - GroundFunction* function = parseFunction(&gp, errorOffset); - function->startLine = i; - GroundValue gv = createFunctionGroundValue(function); - - addVariable(scope.variables, name, gv); - } if (in->instructions[i].type == STRUCT) { if (in->instructions[i].args.length < 1) { runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", &in->instructions[i], i); @@ -562,6 +527,44 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) { } } for (size_t i = 0; i < in->size; i++) { + if (in->instructions[i].type == FUN) { + if (in->instructions[i].args.length < 1) { + runtimeError(TOO_FEW_ARGS, "Expecting 1 or more args", &in->instructions[i], i); + } + if (in->instructions[i].args.args[0].type != FNREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a FunctionRef for arg 1", &in->instructions[i], i); + } + char* name = malloc(strlen(in->instructions[i].args.args[0].value.refName) + 1); + strcpy(name, in->instructions[i].args.args[0].value.refName); + + size_t counter = 1; + GroundProgram gp = createGroundProgram(); + addInstructionToProgram(&gp, in->instructions[i]); + size_t errorOffset = i; + i++; + while (counter > 0) { + if (i >= in->size) { + runtimeError(PREMATURE_EOF, "Reached end of scope before function definition ended", &in->instructions[i - 1], i - 1); + } + if (in->instructions[i].type == FUN) { + counter++; + } + if (in->instructions[i].type == ENDFUN) { + counter--; + } + addInstructionToProgram(&gp, in->instructions[i]); + i++; + } + i--; + + GroundFunction* function = parseFunction(&gp, errorOffset); + function->startLine = i; + function->closure = copyGroundScope(inScope); + GroundValue gv = createFunctionGroundValue(function); + + addVariable(scope.variables, name, gv); + } + /* if (in->instructions[i].type == FUN) { int count = 1; while (count > 0) { @@ -577,6 +580,7 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) { } } } + */ if (in->instructions[i].type == STRUCT) { int count = 1; while (count > 0) { @@ -1789,11 +1793,6 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop if (in->args.args[in->args.length - 1].type != DIRREF) { runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef as the last arg", in, currentInstruction); } - GroundVariable* variables = NULL; - GroundLabel* labels = NULL; - GroundScope newScope; - newScope.variables = &variables; - newScope.labels = &labels; GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName); if (var == NULL) { runtimeError(UNKNOWN_VARIABLE, "Function not found", in, currentInstruction); @@ -1841,6 +1840,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop } free(argsList.values); } else { + GroundScope newScope = copyGroundScope(&function->closure); 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); diff --git a/src/interpreter.h b/src/interpreter.h index ae78460..fa95826 100644 --- a/src/interpreter.h +++ b/src/interpreter.h @@ -1,6 +1,5 @@ #ifndef INTERPRETER_H #define INTERPRETER_H -#define MAX_ID_LEN 64 #include "types.h" #include "parser.h" @@ -14,24 +13,6 @@ typedef enum GroundDebugInstructionType { DUMP, INSPECT, EVAL, CONTINUE, EXIT, STEP, VIEW, HELP, UNKNOWN } GroundDebugInstructionType; -typedef struct GroundLabel { - char id[MAX_ID_LEN]; - int lineNum; - UT_hash_handle hh; -} GroundLabel; - -typedef struct GroundVariable { - char id[MAX_ID_LEN]; - GroundValue value; - UT_hash_handle hh; -} GroundVariable; - -typedef struct GroundScope { - GroundLabel** labels; - GroundVariable** variables; - bool isMainScope; -} GroundScope; - typedef struct GroundDebugInstruction { GroundDebugInstructionType type; char* arg; @@ -42,7 +23,6 @@ GroundFunction* parseFunction(GroundProgram* in, size_t errorOffset); GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope); GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scope); -void addVariable(GroundVariable **head, const char *id, GroundValue data); diff --git a/src/types.c b/src/types.c index 826ca86..2e3e5da 100644 --- a/src/types.c +++ b/src/types.c @@ -709,3 +709,24 @@ bool checkTypes(GroundValue* left, GroundValue* right) { } return true; } + +GroundScope copyGroundScope(GroundScope* scope) { + GroundScope newScope = { + .labels = malloc(sizeof(GroundLabel*)), + .variables = malloc(sizeof(GroundVariable*)), + .isMainScope = false + }; + *newScope.variables = NULL; + *newScope.labels = NULL; + + if (scope == NULL) { + printf("oh no scope is null\n"); + } + + GroundVariable *var, *tmp = NULL; + HASH_ITER(hh, *scope->variables, var, tmp) { + addVariable(newScope.variables, var->id, var->value); + } + + return newScope; +} diff --git a/src/types.h b/src/types.h index b4e3d40..458327a 100644 --- a/src/types.h +++ b/src/types.h @@ -8,6 +8,8 @@ #include #include "include/uthash.h" +#define MAX_ID_LEN 64 + typedef enum GroundInstType { IF, JUMP, END, INPUT, PRINT, PRINTLN, SET, GETTYPE, EXISTS, SETLIST, SETLISTAT, GETLISTAT, GETLISTSIZE, LISTAPPEND, GETSTRSIZE, GETSTRCHARAT, ADD, SUBTRACT, MULTIPLY, DIVIDE, EQUAL, INEQUAL, NOT, GREATER, LESSER, STOI, STOD, ITOC, CTOI, TOSTRING, FUN, RETURN, ENDFUN, PUSHARG, CALL, STRUCT, ENDSTRUCT, INIT, GETFIELD, SETFIELD, USE, EXTERN, CREATELABEL, PAUSE, DROP, ERRORCMD } GroundInstType; @@ -77,6 +79,24 @@ typedef struct GroundValue { } data; } GroundValue; +typedef struct GroundLabel { + char id[MAX_ID_LEN]; + int lineNum; + UT_hash_handle hh; +} GroundLabel; + +typedef struct GroundVariable { + char id[MAX_ID_LEN]; + GroundValue value; + UT_hash_handle hh; +} GroundVariable; + +typedef struct GroundScope { + GroundLabel** labels; + GroundVariable** variables; + bool isMainScope; +} GroundScope; + /* * Indicates status when accessing a list. * Associated functions: @@ -145,6 +165,7 @@ typedef struct GroundFunction { GroundProgram program; size_t startLine; bool isNative; + GroundScope closure; NativeGroundFunction nativeFn; } GroundFunction; @@ -284,5 +305,10 @@ bool checkFnTypes(GroundValue* left, GroundFunctionArgs* arg); // Compares types of two values. bool checkTypes(GroundValue* left, GroundValue* right); +// Adds variable to GroundScope +void addVariable(GroundVariable **head, const char *id, GroundValue data); + +// Deep copies a GroundScope +GroundScope copyGroundScope(GroundScope* scope); #endif diff --git a/tests/closure.grnd b/tests/closure.grnd new file mode 100644 index 0000000..f180a77 --- /dev/null +++ b/tests/closure.grnd @@ -0,0 +1,27 @@ +set &x 5 + +PAUSE + +fun !dingle -function -int &a + + PAUSE + + fun !capture -int -int &b + + PAUSE + + add $a $b &tmp + add $tmp $x &tmp + return $tmp + endfun + + return $capture + +endfun + +call !dingle 3 &result + +PAUSE + +call !result 5 &y +println $y