diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/puzzles/puzzle.c | 209 | ||||
| -rw-r--r-- | src/puzzles/puzzle.h | 17 | ||||
| -rw-r--r-- | src/wren_inter/wren_inter.c | 5 | ||||
| -rw-r--r-- | src/wren_inter/wren_inter.h | 13 |
4 files changed, 195 insertions, 49 deletions
diff --git a/src/puzzles/puzzle.c b/src/puzzles/puzzle.c index 11328a8..2da9ffc 100644 --- a/src/puzzles/puzzle.c +++ b/src/puzzles/puzzle.c @@ -57,6 +57,64 @@ void FreePuzzle(Puzzle *self) { free(self); } +void PrintArgumentToSB(PuzzleIO *io, StringBuilder *sb) { + if (sb == NULL || io == NULL) return; + char buf[128]; + switch (io->type) { + case (WREN_TYPE_BOOL): + bool value = *(bool *)io->value; + AppendStringBuilder(sb, value ? "true" : "false"); + return; + case (WREN_TYPE_NUM): + sprintf(buf, "%lf", *(double *)io->value) ; + AppendStringBuilder(sb, buf); + break; + case (WREN_TYPE_LIST): + AppendStringBuilder(sb, "[TODO]"); + break; + case (WREN_TYPE_MAP): + // TODO + return; + case (WREN_TYPE_NULL): + AppendStringBuilder(sb, "null"); + break; + case (WREN_TYPE_STRING): + sprintf(buf, "\"%s\"", (char *)io->value) ; + AppendStringBuilder(sb, buf); + break; + default: + return; + } +} + +bool PrintTestCallToSB(Test *test, StringBuilder *sb) { + if (test == NULL || test->MainMethodSignature == NULL || test->RecieverName == NULL) + return false; + AppendStringBuilder(sb, test->RecieverName); + AppendStringBuilder(sb, "."); + size_t sig_len = strlen(test->MainMethodSignature); + size_t arg_ptr = 0; + for (size_t i = 0; i < sig_len; i++) { + char ch = test->MainMethodSignature[i]; + + if (ch != '_') { + char buf[2]; + buf[0] = ch; buf[1] = 0; + AppendStringBuilder(sb, buf); + continue; + } + if (arg_ptr >= test->argc) { + AppendStringBuilder(sb, "null"); + continue; + } + + PuzzleIO arg = test->argv[arg_ptr]; + PrintArgumentToSB(&arg, sb); + arg_ptr += 1; + } + return true; +} + // We are currenttly void PutIOIntoSlot(int slot, PuzzleIO *io, WrenVM *vm) { switch (io->type) { @@ -83,33 +141,69 @@ void PutIOIntoSlot(int slot, PuzzleIO *io, WrenVM *vm) { } } -// The cleanest way to do it is move this from the CheckResult enum -bool checkResultString(PuzzleIO *io, WrenVM *vm) { - const char *res = wrenGetSlotString(vm, 0); - if (res == NULL || io->value == NULL) return false; - return strcmp(res, io->value) == 0; +const char *GetTypeName(WrenType type) { + switch (type) { + case WREN_TYPE_BOOL: return "bool"; + case WREN_TYPE_NUM: return "num"; + case WREN_TYPE_FOREIGN: return "foreign"; + case WREN_TYPE_LIST: return "list"; + case WREN_TYPE_MAP: return "map"; + case WREN_TYPE_NULL: return "null"; + case WREN_TYPE_STRING: return "string"; + case WREN_TYPE_UNKNOWN: return "unknown"; + } } -bool CheckResult(PuzzleIO *io, WrenVM *vm) { +bool CheckResult(PuzzleIO *io, WrenVM *vm, char *outp) { WrenType actual_type = wrenGetSlotType(vm, 0); + if (io->type != actual_type) { + sprintf(outp, "Expected type %s, got %s", + GetTypeName(io->type), + GetTypeName(actual_type)); + return false; + } + bool output = false; switch (io->type) { - case (WREN_TYPE_BOOL): - if (actual_type != WREN_TYPE_BOOL) return false; - if (io->value == NULL) return false; - return wrenGetSlotBool(vm, 0) == *((bool *)io->value); + case (WREN_TYPE_BOOL): + { + if (io->value == NULL) return false; // Invalid puzzle + bool result = wrenGetSlotBool(vm, 0); + output = result == *((bool *)io->value); + if (!output) + sprintf(outp, "Expected '%s', got '%s'", + result ? "true" : "false", + output ? "true" : "false"); + return output; + } - case (WREN_TYPE_NUM): - if (actual_type != WREN_TYPE_NUM) return false; - if (io->value == NULL) return false; - return wrenGetSlotDouble(vm, 0) == *((double *)io->value); + case (WREN_TYPE_NUM): + { + if (io->value == NULL) return false; // Invalid puzzle + double result = wrenGetSlotDouble(vm, 0); + output = result == *((double *)io->value); + if (!output) + sprintf(outp, "Expected '%lf', got '%lf'", + *((double *)io->value), + result); + return output; + } case (WREN_TYPE_NULL): return actual_type == WREN_TYPE_NULL; case (WREN_TYPE_STRING): - if (actual_type != WREN_TYPE_STRING) return false; - return checkResultString(io, vm); + { + if (io->value == NULL) return false; // Invalid puzzle + + const char *result = wrenGetSlotString(vm, 0); + output = strcmp(result, io->value) == 0; + if (!output) + sprintf(outp, "Expected '%s', got '%s'", + (char *)io->value, + result); + return output; + } case (WREN_TYPE_LIST): case (WREN_TYPE_MAP): @@ -118,19 +212,33 @@ bool CheckResult(PuzzleIO *io, WrenVM *vm) { } } -enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm, size_t max_op) { + + +TestResult RunTest(Test *test, WrenVM *vm, size_t max_op) { wrenEnsureSlots(vm, test->argc + 1); // finction handle + arguments wrenGetVariable(vm, USER_MODULE_NAME, test->RecieverName, 0); + char outp_buf[512]; WrenHandle *obj_handle = wrenGetSlotHandle(vm, 0); if (obj_handle == NULL) { - return RESULT_COMPTIME_NO_ENTRY; + sprintf(outp_buf, "No such class as %s", test->RecieverName); + return (TestResult) { + .res = RESULT_COMPTIME_NO_ENTRY, + .message = strdup(outp_buf), + }; } WrenHandle *fn_handle = wrenMakeCallHandle(vm, test->MainMethodSignature); if (fn_handle == NULL) { wrenReleaseHandle(vm, obj_handle); - return RESULT_COMPTIME_NO_ENTRY; + sprintf(outp_buf, "No such method as %s in a class %s", + test->MainMethodSignature, + test->RecieverName); + + return (TestResult) { + .res = RESULT_COMPTIME_NO_ENTRY, + .message = strdup(outp_buf), + }; } wrenSetSlotHandle(vm, 0, obj_handle); @@ -141,25 +249,31 @@ enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm, size_t max_op) { WrenInterpretResult call_result = wrenCallLim(vm, fn_handle, max_op); switch (call_result) { - case WREN_RESULT_COMPILE_ERROR: - return RESULT_COMPTIME_ERR; + case WREN_RESULT_COMPILE_ERROR: + return (TestResult) { .res = RESULT_COMPTIME_ERR, .message = NULL }; case WREN_RESULT_RUNTIME_ERROR: - return RESULT_RUNTIME_ERR; + return (TestResult) { .res = RESULT_RUNTIME_ERR, .message = NULL }; case WREN_RESULT_RUNTIME_LIMIT: - return RESULT_TIMEOUT; + return (TestResult) { + .res = RESULT_RUNTIME_ERR, + .message = strdup("The execution halted") + }; default: break; } - bool result = CheckResult(&test->ExpectedResult, vm); + bool result = CheckResult(&test->ExpectedResult, vm, outp_buf); wrenReleaseHandle(vm, fn_handle); wrenReleaseHandle(vm, obj_handle); if (result) { - return RESULT_OK; - } else { - return RESULT_WRONG_ANSWER; + return (TestResult) { .res = RESULT_OK, .message = NULL }; } + + return (TestResult) { + .res = RESULT_WRONG_ANSWER, + .message = strdup(outp_buf) + }; } // Returns the res ptr. Does not modify the address @@ -203,21 +317,42 @@ SubmitionResult *SubmitSolution(Puzzle *self, char *script) { res->result = RESULT_INIT_ERROR; return res; } - + + StringBuilder *sb = GetOutputSb(vm); + AppendStringBuilder(sb, "Evaluating user code...\n"); res = TryEvaluateUserCode(vm, script, res, cfg->MaxOperations); - if (res->result != RESULT_OK) { - return PrepareResult(res, vm, res->result); + switch (res->result) { + case RESULT_OK: break; + case RESULT_COMPTIME_ERR: + return PrepareResult(res, vm, res->result); + break; + case RESULT_RUNTIME_ERR: + return PrepareResult(res, vm, res->result); + break; + case RESULT_TIMEOUT: + AppendStringBuilder(sb, "The execution halted\n"); + return PrepareResult(res, vm, res->result); + break; + default: break; } + AppendStringBuilder(sb, "Success!\n"); for (size_t i = 0; i < self->TestC; i++) { - enum PUZZLE_TEST_RESULT test_res = RunTest(&self->Tests[i], vm, cfg->MaxOperations); - switch (test_res) { - case WREN_RESULT_COMPILE_ERROR: - return PrepareResult(res, vm, RESULT_COMPTIME_ERR); - case WREN_RESULT_RUNTIME_ERROR: - return PrepareResult(res, vm, RESULT_RUNTIME_ERR); - default: break; + Test *test = &self->Tests[i]; + AppendStringBuilder(sb, "\nRunning test "); + PrintTestCallToSB(test, sb); + AppendStringBuilder(sb, "... \n"); + TestResult test_res = RunTest(test, vm, cfg->MaxOperations); + if (test_res.message != NULL) { + AppendStringBuilder(sb, test_res.message); + AppendStringBuilder(sb, "\n"); + free(test_res.message); + } + if (test_res.res != RESULT_OK) { + AppendStringBuilder(sb, "Test failed.\n"); + return PrepareResult(res, vm, test_res.res); } + AppendStringBuilder(sb, "Success!\n"); } return PrepareResult(res, vm, RESULT_OK); diff --git a/src/puzzles/puzzle.h b/src/puzzles/puzzle.h index 4dfd0f8..e7aebb1 100644 --- a/src/puzzles/puzzle.h +++ b/src/puzzles/puzzle.h @@ -25,11 +25,26 @@ typedef struct { PuzzleIO ExpectedResult; } Test; +enum PUZZLE_TEST_RESULT { + RESULT_OK = 0, + RESULT_WRONG_ANSWER, + RESULT_COMPTIME_NO_ENTRY, + RESULT_COMPTIME_ERR, + RESULT_RUNTIME_ERR, + // TODO: add async evaluation to prevent `while (true)` situation + RESULT_TIMEOUT, + RESULT_INIT_ERROR, +}; + +typedef struct { + enum PUZZLE_TEST_RESULT res; + char *message; +} TestResult; /// The user submitted code should be evaluated by this point. /// The entry point should be registered and the absence of the /// Main method signatire will be concidered an error -enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm, size_t max_op); +TestResult RunTest(Test *test, WrenVM *vm, size_t max_op); typedef struct { char *Name; diff --git a/src/wren_inter/wren_inter.c b/src/wren_inter/wren_inter.c index 4b72633..103451e 100644 --- a/src/wren_inter/wren_inter.c +++ b/src/wren_inter/wren_inter.c @@ -46,6 +46,7 @@ char *EvaluateScript(char *script) { WrenInterpretResult result = wrenInterpret(vm, "main", script); switch (result) { case WREN_RESULT_SUCCESS: break; + case WREN_RESULT_RUNTIME_LIMIT: break; case WREN_RESULT_COMPILE_ERROR: break; case WREN_RESULT_RUNTIME_ERROR: break; } @@ -78,3 +79,7 @@ void InterpreterFreeMachine(WrenVM *vm) { if (sb != NULL && sb->val != NULL) free(sb); wrenFreeVM(vm); } + +StringBuilder *GetOutputSb(WrenVM *vm) { + return (StringBuilder*)wrenGetUserData(vm); +} diff --git a/src/wren_inter/wren_inter.h b/src/wren_inter/wren_inter.h index 358dc1d..bd3ec48 100644 --- a/src/wren_inter/wren_inter.h +++ b/src/wren_inter/wren_inter.h @@ -2,20 +2,11 @@ #define WREN_INTER_H #include <wren.h> - -enum PUZZLE_TEST_RESULT { - RESULT_OK = 0, - RESULT_WRONG_ANSWER, - RESULT_COMPTIME_NO_ENTRY, - RESULT_COMPTIME_ERR, - RESULT_RUNTIME_ERR, - // TODO: add async evaluation to prevent `while (true)` situation - RESULT_TIMEOUT, - RESULT_INIT_ERROR, -}; +#include "../utils/stringBuilder.h" WrenVM *InterpreterPrepareMachine(); char *GetMachineOutput(WrenVM *vm); void InterpreterFreeMachine(WrenVM *vm); +StringBuilder *GetOutputSb(WrenVM *vm); #endif // WREN_INTER_H |
