#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); } 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 addLabel(GroundLabel **head, const char *id, int data) { GroundLabel* label = findLabel(*head, id); if (label) { deleteLabel(head, label); } GroundLabel* item = malloc(sizeof(GroundLabel)); snprintf(item->id, MAX_ID_LEN, "%s", id); item->lineNum = 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 addVariable(GroundVariable **head, const char *id, GroundValue data) { GroundVariable* variable = findVariable(*head, id); if (variable) { deleteVariable(head, variable); } GroundVariable* item = malloc(sizeof(GroundVariable)); snprintf(item->id, MAX_ID_LEN, "%s", id); item->value = data; HASH_ADD_STR(*head, id, 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 inst, GroundScope* scope) { GroundInstruction copied_inst = copyGroundInstruction(&inst); GroundInstruction* in = &copied_inst; // 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[i].value.refName); if (variable) { free(in->args.args[i].value.refName); // Free the strdup'd refName 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++) { if (i != 0) printf(" "); printGroundArg(&in->args.args[i]); } 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++) { if (i != 0) printf(" "); printGroundArg(&in->args.args[i]); } printf("\n"); break; } case INPUT: { if (in->args.length < 1) { runtimeError(TOO_FEW_ARGS, "Expecting 1 arg", in, currentInstruction); } if (in->args.args[0].type != DIRREF) { runtimeError(ARG_TYPE_MISMATCH, "Expecting a DirectRef for arg 1", in, currentInstruction); } char buffer[256]; if (fgets(buffer, sizeof(buffer), stdin) != NULL) { buffer[strcspn(buffer, "\n")] = '\0'; addVariable(scope->variables, in->args.args[0].value.refName, createStringGroundValue(buffer)); } else { runtimeError(FIXME, "Failed to read input from console with fgets", in, currentInstruction); } 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(strlen(left->data.stringVal) + strlen(right->data.stringVal)); 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 (right->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; } case SUBTRACT: { 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 == 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 (right->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 or Double for arg 1", in, currentInstruction); } break; } case MULTIPLY: { 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 == 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 (right->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 or Double for arg 1", in, currentInstruction); } break; } case DIVIDE: { 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 == 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 (right->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 or Double for arg 1", in, currentInstruction); } break; } case EQUAL: { 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 != right->type) { addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(false)); } else { switch (left->type) { case INT: { bool cond = (left->data.intVal == right->data.intVal); addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond)); break; } case DOUBLE: { bool cond = (left->data.doubleVal == right->data.doubleVal); addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond)); break; } case STRING: { bool cond = (strcmp(left->data.stringVal, right->data.stringVal) == 0); addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond)); break; } case CHAR: { bool cond = (left->data.charVal == right->data.charVal); addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond)); break; } case BOOL: { bool cond = (left->data.boolVal == right->data.boolVal); addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(cond)); break; } default: { addVariable(scope->variables, in->args.args[2].value.refName, createBoolGroundValue(false)); break; } } } break; } default: { runtimeError(FIXME, "Currently unimplemented instruction", in, currentInstruction); } } freeGroundInstruction(in); }