forked from ground/ground-old
Closures in Ground
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
|
||||
|
||||
21
src/types.c
21
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;
|
||||
}
|
||||
|
||||
26
src/types.h
26
src/types.h
@@ -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
27
tests/closure.grnd
Normal 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
|
||||
Reference in New Issue
Block a user