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(); GroundStruct groundCreateStruct();
void groundAddFieldToStruct(GroundStruct* gstruct, char* name, GroundValue field); 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 #ifdef __cplusplus
} }
#endif #endif

View File

@@ -83,9 +83,19 @@ GroundValue groundCreateValue(GroundValueType type, ...) {
return createNoneGroundValue(); return createNoneGroundValue();
break; break;
} }
case ERROR:
case CUSTOM: { 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 // FIXME
return createNoneGroundValue();
break; break;
} }
} }
@@ -128,3 +138,25 @@ void groundAddFieldToStruct(GroundStruct* gstruct, char* name, GroundValue field
char* groundCompileProgram(GroundProgram* program) { char* groundCompileProgram(GroundProgram* program) {
return compileGroundProgram(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); GroundFunction* function = parseFunction(&gp, errorOffset);
function->startLine = i; function->startLine = i;
function->closure = copyGroundScope(scope);
GroundValue gv = createFunctionGroundValue(function); GroundValue gv = createFunctionGroundValue(function);
addFieldToStruct(&gstruct, name, gv); addFieldToStruct(&gstruct, name, gv);
break; break;
@@ -484,42 +485,6 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
if (in->instructions[i].type == CREATELABEL) { if (in->instructions[i].type == CREATELABEL) {
addLabel(scope.labels, in->instructions[i].args.args[0].value.refName, i); 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].type == STRUCT) {
if (in->instructions[i].args.length < 1) { if (in->instructions[i].args.length < 1) {
runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", &in->instructions[i], i); 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++) { 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) { if (in->instructions[i].type == FUN) {
int count = 1; int count = 1;
while (count > 0) { while (count > 0) {
@@ -577,6 +580,7 @@ GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) {
} }
} }
} }
*/
if (in->instructions[i].type == STRUCT) { if (in->instructions[i].type == STRUCT) {
int count = 1; int count = 1;
while (count > 0) { while (count > 0) {
@@ -1789,11 +1793,6 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
if (in->args.args[in->args.length - 1].type != DIRREF) { if (in->args.args[in->args.length - 1].type != DIRREF) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef as the last arg", in, currentInstruction); 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); GroundVariable* var = findVariable(*scope->variables, in->args.args[0].value.refName);
if (var == NULL) { if (var == NULL) {
runtimeError(UNKNOWN_VARIABLE, "Function not found", in, currentInstruction); runtimeError(UNKNOWN_VARIABLE, "Function not found", in, currentInstruction);
@@ -1841,6 +1840,7 @@ GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scop
} }
free(argsList.values); free(argsList.values);
} else { } else {
GroundScope newScope = copyGroundScope(&function->closure);
for (size_t i = 0; i < function->argSize; i++) { for (size_t i = 0; i < function->argSize; i++) {
if (in->args.args[i + 1].type != VALUE) { if (in->args.args[i + 1].type != VALUE) {
runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction); runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value", in, currentInstruction);

View File

@@ -1,6 +1,5 @@
#ifndef INTERPRETER_H #ifndef INTERPRETER_H
#define INTERPRETER_H #define INTERPRETER_H
#define MAX_ID_LEN 64
#include "types.h" #include "types.h"
#include "parser.h" #include "parser.h"
@@ -14,24 +13,6 @@ typedef enum GroundDebugInstructionType {
DUMP, INSPECT, EVAL, CONTINUE, EXIT, STEP, VIEW, HELP, UNKNOWN DUMP, INSPECT, EVAL, CONTINUE, EXIT, STEP, VIEW, HELP, UNKNOWN
} GroundDebugInstructionType; } 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 { typedef struct GroundDebugInstruction {
GroundDebugInstructionType type; GroundDebugInstructionType type;
char* arg; char* arg;
@@ -42,7 +23,6 @@ GroundFunction* parseFunction(GroundProgram* in, size_t errorOffset);
GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope); GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope);
GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scope); 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; 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 <string.h>
#include "include/uthash.h" #include "include/uthash.h"
#define MAX_ID_LEN 64
typedef enum GroundInstType { 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 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; } GroundInstType;
@@ -77,6 +79,24 @@ typedef struct GroundValue {
} data; } data;
} GroundValue; } 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. * Indicates status when accessing a list.
* Associated functions: * Associated functions:
@@ -145,6 +165,7 @@ typedef struct GroundFunction {
GroundProgram program; GroundProgram program;
size_t startLine; size_t startLine;
bool isNative; bool isNative;
GroundScope closure;
NativeGroundFunction nativeFn; NativeGroundFunction nativeFn;
} GroundFunction; } GroundFunction;
@@ -284,5 +305,10 @@ bool checkFnTypes(GroundValue* left, GroundFunctionArgs* arg);
// Compares types of two values. // Compares types of two values.
bool checkTypes(GroundValue* left, GroundValue* right); 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 #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