#include "interpreter.h" #include "parser.h" #include "types.h" #include "include/uthash.h" #include #include int currentInstruction = 0; void runtimeError(GroundRuntimeError error, char* what, GroundInstruction* where, int whereLine) { printf("Ground runtime error:\n ErrorType: "); switch (error) { case ARG_TYPE_MISMATCH: { printf("ArgTypeMismatch"); break; } case TOO_FEW_ARGS: { printf("TooFewArgs"); break; } case TOO_MANY_ARGS: { 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)"); } } printf("\n"); if (what != NULL) { printf(" ErrorContext: %s\n", what); } if (where != NULL) { printf(" ErrorInstruction: "); printGroundInstruction(where); printf("\n"); } if (whereLine > -1) { printf(" ErrorLine: %d\n", whereLine + 1); } exit(1); } void addLabel(GroundLabel **head, const char *id, int data) { GroundLabel* item = malloc(sizeof(GroundLabel)); snprintf(item->id, MAX_ID_LEN, "%s", id); item->lineNum = data; HASH_ADD_STR(*head, id, item); } GroundLabel* findLabel(GroundLabel* head, const char *id) { GroundLabel *item; HASH_FIND_STR(head, id, item); return item; } void deleteLabel(GroundLabel** head, GroundLabel *item) { HASH_DEL(*head, item); free(item); } void addVariable(GroundVariable **head, const char *id, GroundValue data) { GroundVariable* item = malloc(sizeof(GroundVariable)); snprintf(item->id, MAX_ID_LEN, "%s", id); item->value = data; HASH_ADD_STR(*head, id, item); } GroundVariable* findVariable(GroundVariable* head, const char *id) { GroundVariable *item; HASH_FIND_STR(head, id, item); return item; } void deleteVariable(GroundVariable** head, GroundVariable *item) { HASH_DEL(*head, item); free(item); } void interpretGroundProgram(GroundProgram* in) { GroundLabel* labels = NULL; GroundVariable* variables = NULL; GroundScope scope; scope.labels = &labels; scope.variables = &variables; // 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); } } while (currentInstruction < in->size) { interpretGroundInstruction(&in->instructions[currentInstruction], &scope); currentInstruction++; } } 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: { break; } case IF: { if (in->args.length < 2) { runtimeError(TOO_FEW_ARGS, "Expecting 2 arguments", in, currentInstruction); } if (in->args.length > 2) { runtimeError(TOO_MANY_ARGS, "Expecting 2 arguments", in, currentInstruction); } if (in->args.args[0].type != VALUE || in->args.args[0].value.value.type != BOOL) { runtimeError(ARG_TYPE_MISMATCH, "Expecting a bool for arg 0", in, currentInstruction); } if (in->args.args[1].type != LINEREF) { runtimeError(ARG_TYPE_MISMATCH, "Expecting a LineRef for arg 1", in, currentInstruction); } if (in->args.args[0].value.value.data.boolVal) { GroundLabel* label = findLabel(*scope->labels, in->args.args[1].value.refName); if (label) { currentInstruction = label->lineNum; } else { runtimeError(UNKNOWN_LABEL, NULL, in, currentInstruction); } } break; } case JUMP: { if (in->args.length < 1) { runtimeError(TOO_FEW_ARGS, "Expecting 1 argument", in, currentInstruction); } if (in->args.length > 1) { runtimeError(TOO_MANY_ARGS, "Expecting 1 argument", in, currentInstruction); } if (in->args.args[0].type != LINEREF) { runtimeError(ARG_TYPE_MISMATCH, "Expecting a LineRef for arg 1", in, currentInstruction); } GroundLabel* label = findLabel(*scope->labels, in->args.args[0].value.refName); if (label) { currentInstruction = label->lineNum; } else { runtimeError(UNKNOWN_LABEL, NULL, in, currentInstruction); } break; } case END: { if (in->args.length < 1 || in->args.args[0].type != VALUE || in->args.args[0].value.value.type != INT) { exit(0); } else { exit(in->args.args[0].value.value.data.intVal); } break; } case PRINT: { if (in->args.length < 1) { 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]); printf(" "); } break; } case PRINTLN: { if (in->args.length < 1) { 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]); printf(" "); } 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); } } }