diff --git a/src/interpreter.c b/src/interpreter.c index 1bba766..8f686d0 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -34,6 +34,14 @@ void runtimeError(GroundRuntimeError error, char* what, GroundInstruction* where printf("ListError"); break; } + case STRING_ERROR: { + printf("StringError"); + break; + } + case MATH_ERROR: { + printf("MathError"); + break; + } default: case FIXME: { printf("FIXME (please report issue to https://chsp.au/ground/cground)"); @@ -99,27 +107,115 @@ void addVariable(GroundVariable **head, const char *id, GroundValue data) { HASH_ADD_STR(*head, id, item); } -void interpretGroundProgram(GroundProgram* in) { +GroundFunction createGroundFunction() { + GroundFunction gf; + gf.argSize = 0; + gf.args = malloc(sizeof(GroundFunctionArgs)); + gf.program = createGroundProgram(); + return gf; +} + +void addArgsToGroundFunction(GroundFunction* function, GroundValueType type, char* name) { + GroundFunctionArgs arg; + arg.type = type; + arg.name = name; + function->argSize ++; + GroundFunctionArgs* ptr = realloc(function->args, function->argSize * sizeof(GroundFunctionArgs)); + if (ptr == NULL) { + printf("Failed to allocate memory for arg of GroundFunction"); + function->argSize --; + return; + } + function->args = ptr; + function->args[function->argSize - 1] = arg; +}; + +GroundValueType stringToValueType(char* in) { + if (strcmp(in, "int") == 0) { + return INT; + } + if (strcmp(in, "double") == 0) { + return DOUBLE; + } + if (strcmp(in, "bool") == 0) { + return BOOL; + } + if (strcmp(in, "string") == 0) { + return STRING; + } + if (strcmp(in, "char") == 0) { + return CHAR; + } + if (strcmp(in, "list") == 0) { + return LIST; + } + return CUSTOM; +} + +GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope) { GroundLabel* labels = NULL; GroundVariable* variables = NULL; + GroundFunctionWrapper* functions = NULL; GroundScope scope; - scope.labels = &labels; - scope.variables = &variables; + if (inScope != NULL) { + scope = *inScope; + } else { + scope.labels = &labels; + scope.variables = &variables; + scope.functions = &functions; + } // Preprocess all labels for (int i = 0; i < in->size; i++) { if (in->instructions[i].type == CREATELABEL) { addLabel(scope.labels, in->instructions[i].args.args[0].value.refName, i); } + if (in->instructions[i].type == FUN) { + char* functionName; + GroundFunction function = createGroundFunction(); + 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); + } + if (in->instructions[i].args.length < 2) { + function.returnType = NONE; + } else { + if (in->instructions[i].args.args[1].type != TYPEREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a TypeRef for arg 2", &in->instructions[i], i); + } + functionName = in->instructions[i].args.args[0].value.refName; + GroundArg* args = in->instructions[i].args.args; + size_t length = in->instructions[i].args.length; + for (size_t j = 2; j < length; j += 2) { + if (args[j].type != TYPEREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a TypeRef", &in->instructions[i], i); + } + if (j + 1 >= length) { + runtimeError(TOO_FEW_ARGS, "Expecting a DirectRef after a TypeRef", &in->instructions[i], i); + } + if (args[j + 1].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef after a TypeRef", &in->instructions[i], i); + } + addArgsToGroundFunction(&function, stringToValueType(args[j].value.refName), args[j + 1].value.refName); + } + } + i++; + while (in->instructions[i].type != ENDFUN) { + addInstructionToProgram(&function.program, in->instructions[i]); + i++; + } + } } while (currentInstruction < in->size) { interpretGroundInstruction(in->instructions[currentInstruction], &scope); currentInstruction++; } - + return createIntGroundValue(0); } -void interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) { +GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) { GroundInstruction copied_inst = copyGroundInstruction(&inst); GroundInstruction* in = &copied_inst; @@ -137,7 +233,9 @@ void interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) { } } switch (in->type) { - // We can safely ignore any CREATELABEL instructions, these have been preprocessed + // We can safely ignore any CREATELABEL, FUN, and ENDFUN instructions, these have been preprocessed + case FUN: + case ENDFUN: case CREATELABEL: { break; } @@ -310,6 +408,9 @@ void interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) { addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("custom")); break; } + case NONE: { + addVariable(scope->variables, in->args.args[1].value.refName, createStringGroundValue("none")); + } } break; @@ -380,7 +481,7 @@ void interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) { ListAccessStatus status = setListAt(&value->data.listVal, in->args.args[1].value.value.data.intVal, in->args.args[2].value.value); switch (status) { case LIST_OKAY: - return; + break; case LIST_OUT_OF_BOUNDS: runtimeError(LIST_ERROR, "Out of bounds index", in, currentInstruction); case LIST_FIXME: @@ -571,9 +672,9 @@ void interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) { double result = 0; if (left->type == INT) { - result -= left->data.intVal; + result = left->data.intVal; } else if (left->type == DOUBLE) { - result -= left->data.doubleVal; + result = left->data.doubleVal; } if (right->type == INT) { @@ -618,7 +719,7 @@ void interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) { } if (left->type == DOUBLE || right->type == DOUBLE) { - double result = 0; + double result = 1.0; if (left->type == INT) { result *= left->data.intVal; @@ -668,16 +769,16 @@ void interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) { } if (right->type == INT && right->data.intVal == 0) { - + runtimeError(MATH_ERROR, "Division by zero", in, currentInstruction); } if (left->type == DOUBLE || right->type == DOUBLE) { double result = 0; if (left->type == INT) { - result /= left->data.intVal; + result = left->data.intVal; } else if (left->type == DOUBLE) { - result /= left->data.doubleVal; + result = left->data.doubleVal; } if (right->type == INT) { @@ -895,7 +996,7 @@ void interpretGroundInstruction(GroundInstruction inst, GroundScope* scope) { runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 2", in, currentInstruction); } - bool condition = !in->args.args[1].value.value.data.boolVal; + bool condition = !in->args.args[0].value.value.data.boolVal; addVariable(scope->variables, in->args.args[1].value.refName, createBoolGroundValue(condition)); } diff --git a/src/interpreter.h b/src/interpreter.h index 3c35069..7509550 100644 --- a/src/interpreter.h +++ b/src/interpreter.h @@ -7,7 +7,7 @@ #include "include/uthash.h" typedef enum GroundRuntimeError { - ARG_TYPE_MISMATCH, TOO_FEW_ARGS, TOO_MANY_ARGS, UNKNOWN_LABEL, UNKNOWN_VARIABLE, LIST_ERROR, STRING_ERROR, FIXME + ARG_TYPE_MISMATCH, TOO_FEW_ARGS, TOO_MANY_ARGS, UNKNOWN_LABEL, UNKNOWN_VARIABLE, LIST_ERROR, STRING_ERROR, MATH_ERROR, FIXME } GroundRuntimeError; typedef struct GroundLabel { @@ -22,13 +22,32 @@ typedef struct GroundVariable { UT_hash_handle hh; } GroundVariable; +typedef struct GroundFunctionArgs { + GroundValueType type; + char* name; +} GroundFunctionArgs; + +typedef struct GroundFunction { + GroundFunctionArgs* args; + size_t argSize; + GroundValueType returnType; + GroundProgram program; +} GroundFunction; + +typedef struct GroundFunctionWrapper { + char id[MAX_ID_LEN]; + GroundFunction function; + UT_hash_handle hh; +} GroundFunctionWrapper; + typedef struct GroundScope { GroundLabel** labels; GroundVariable** variables; + GroundFunctionWrapper** functions; } GroundScope; -void interpretGroundProgram(GroundProgram* in); -void interpretGroundInstruction(GroundInstruction inst, GroundScope* scope); +GroundValue interpretGroundProgram(GroundProgram* in, GroundScope* inScope); +GroundValue interpretGroundInstruction(GroundInstruction inst, GroundScope* scope); #endif diff --git a/src/main.c b/src/main.c index 27a9912..85ebc42 100644 --- a/src/main.c +++ b/src/main.c @@ -46,5 +46,5 @@ int main(int argc, char** argv) { char* file = getFileContents(argv[1]); GroundProgram program = parseFile(file); free(file); - interpretGroundProgram(&program); + interpretGroundProgram(&program, NULL); } diff --git a/src/types.h b/src/types.h index 73c5e2b..31ceff4 100644 --- a/src/types.h +++ b/src/types.h @@ -12,7 +12,7 @@ typedef enum GroundInstType { } GroundInstType; typedef enum GroundValueType { - INT, DOUBLE, STRING, CHAR, BOOL, LIST, CUSTOM + INT, DOUBLE, STRING, CHAR, BOOL, LIST, CUSTOM, NONE } GroundValueType; typedef enum GroundArgType { @@ -159,4 +159,4 @@ ListAccess getListAt(List* list, int idx); // Sets an item in list (list) at index (idx) to GroundValue (value). ListAccessStatus setListAt(List* list, int idx, GroundValue value); -#endif \ No newline at end of file +#endif diff --git a/tests/function.grnd b/tests/function.grnd new file mode 100644 index 0000000..71f3e89 --- /dev/null +++ b/tests/function.grnd @@ -0,0 +1,3 @@ +fun !dingus -string -int &x + +endfun