diff --git a/src/interpreter.c b/src/interpreter.c index 00e5a0a..289544c 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -3,6 +3,7 @@ #include "types.h" #include "include/uthash.h" #include +#include int currentInstruction = 0; @@ -21,6 +22,14 @@ void runtimeError(GroundRuntimeError error, char* what, GroundInstruction* where printf("TooManyArgs"); break; } + case UNKNOWN_LABEL: { + printf("UnknownLabel"); + break; + } + case UNKNOWN_VARIABLE: { + printf("UnknownVariable"); + break; + } default: case FIXME: { printf("FIXME (please report issue to https://chsp.au/ground/cground)"); @@ -98,6 +107,18 @@ void interpretGroundProgram(GroundProgram* in) { } void interpretGroundInstruction(GroundInstruction* in, GroundScope* scope) { + // Insert variables prefixed with $ + for (int i = 0; i < in->args.length; i++) { + if (in->args.args[i].type == VALREF) { + GroundVariable* variable = findVariable(*scope->variables, in->args.args[0].value.refName); + if (variable) { + in->args.args[i].value.value = variable->value; + in->args.args[i].type = VALUE; + } else { + runtimeError(UNKNOWN_VARIABLE, NULL, in, currentInstruction); + } + } + } switch (in->type) { // We can safely ignore any CREATELABEL instructions, these have been preprocessed case CREATELABEL: { @@ -156,7 +177,7 @@ void interpretGroundInstruction(GroundInstruction* in, GroundScope* scope) { } case PRINT: { if (in->args.length < 1) { - runtimeError(TOO_FEW_ARGS, NULL, in, currentInstruction); + runtimeError(TOO_FEW_ARGS, "Expecting 1 or more args", in, currentInstruction); } for (int i = 0; i < in->args.length; i++) { printGroundArg(&in->args.args[i]); @@ -166,7 +187,7 @@ void interpretGroundInstruction(GroundInstruction* in, GroundScope* scope) { } case PRINTLN: { if (in->args.length < 1) { - runtimeError(TOO_FEW_ARGS, NULL, in, currentInstruction); + runtimeError(TOO_FEW_ARGS, "Expecting 1 or more args", in, currentInstruction); } for (int i = 0; i < in->args.length; i++) { printGroundArg(&in->args.args[i]); @@ -175,6 +196,85 @@ void interpretGroundInstruction(GroundInstruction* in, GroundScope* scope) { printf("\n"); break; } + + case SET: { + if (in->args.length < 2) { + runtimeError(TOO_FEW_ARGS, "Expecting 2 args", in, currentInstruction); + } + if (in->args.length > 2) { + runtimeError(TOO_MANY_ARGS, "Expecting 2 args", in, currentInstruction); + } + if (in->args.args[0].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 1", in, currentInstruction); + } + if (in->args.args[1].type != VALUE) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction); + } + + addVariable(scope->variables, in->args.args[0].value.refName, in->args.args[1].value.value); + break; + } + + case ADD: { + if (in->args.length < 3) { + runtimeError(TOO_FEW_ARGS, "Expecting 3 args", in, currentInstruction); + } + if (in->args.length > 3) { + runtimeError(TOO_MANY_ARGS, "Expecting 3 args", in, currentInstruction); + } + if (in->args.args[0].type != VALUE) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 1", in, currentInstruction); + } + if (in->args.args[1].type != VALUE) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a Value for arg 2", in, currentInstruction); + } + if (in->args.args[2].type != DIRREF) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 3", in, currentInstruction); + } + + GroundValue* left = &in->args.args[0].value.value; + GroundValue* right = &in->args.args[1].value.value; + + if (left->type == STRING) { + if (right->type != STRING) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting a String for arg 2", in, currentInstruction); + } + char* newString = malloc(sizeof(char)); + strcpy(newString, left->data.stringVal); + strcat(newString, right->data.stringVal); + + addVariable(scope->variables, in->args.args[2].value.refName, createStringGroundValue(newString)); + } + else if (left->type == INT || left->type == DOUBLE) { + if (right->type != INT && right->type != DOUBLE) { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int or Double for arg 2", in, currentInstruction); + } + + if (left->type == DOUBLE || right->type == DOUBLE) { + double result = 0; + + if (left->type == INT) { + result += left->data.intVal; + } else if (left->type == DOUBLE) { + result += left->data.doubleVal; + } + + if (right->type == INT) { + result += right->data.intVal; + } else if (left->type == DOUBLE) { + result += right->data.doubleVal; + } + + addVariable(scope->variables, in->args.args[2].value.refName, createDoubleGroundValue(result)); + } else { + int64_t result = left->data.intVal + right->data.intVal; + addVariable(scope->variables, in->args.args[2].value.refName, createIntGroundValue(result)); + } + } else { + runtimeError(ARG_TYPE_MISMATCH, "Expecting an Int, Double, or String for arg 1", in, currentInstruction); + } + break; + } default: { runtimeError(FIXME, "Currently unimplemented instruction", in, currentInstruction); } diff --git a/src/interpreter.h b/src/interpreter.h index 36a8026..06c677f 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, FIXME + ARG_TYPE_MISMATCH, TOO_FEW_ARGS, TOO_MANY_ARGS, UNKNOWN_LABEL, UNKNOWN_VARIABLE, FIXME } GroundRuntimeError; typedef struct GroundLabel { diff --git a/tests/simple.grnd b/tests/simple.grnd index 002ef14..71d9ef2 100644 --- a/tests/simple.grnd +++ b/tests/simple.grnd @@ -1,3 +1,19 @@ -@myLabel -println "dingus" -jump %myLabel +# Check setting variables +set &myVar "dingus" +println $myVar + +# Check string concat +add "dingle" "fart" &myOtherVar +println $myOtherVar + +# Check double math +add 3.14 2.7 &myThirdVar +println $myThirdVar + +# Check int math +add 464398727 374298 &yetAnotherVar +println $yetAnotherVar + +# Check mixed math +add 432 4732.12 &finalVar +println $finalVar