summaryrefslogtreecommitdiff
path: root/src/puzzles
diff options
context:
space:
mode:
Diffstat (limited to 'src/puzzles')
-rw-r--r--src/puzzles/puzzle.c209
-rw-r--r--src/puzzles/puzzle.h17
2 files changed, 188 insertions, 38 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;