1
0
forked from ground/ground-old

Closures in Ground

This commit is contained in:
2026-03-02 10:09:10 +11:00
parent d011d2beb4
commit 5577679ded
7 changed files with 152 additions and 62 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,6 +8,8 @@
#include <string.h>
#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

27
tests/closure.grnd Normal file
View File

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