aboutsummaryrefslogtreecommitdiff
path: root/src/core/sok-level-parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/sok-level-parser.c')
-rw-r--r--src/core/sok-level-parser.c193
1 files changed, 193 insertions, 0 deletions
diff --git a/src/core/sok-level-parser.c b/src/core/sok-level-parser.c
new file mode 100644
index 0000000..276ebcb
--- /dev/null
+++ b/src/core/sok-level-parser.c
@@ -0,0 +1,193 @@
+#include <stdbool.h>
+#include <stddef.h> // size_t
+#include "sok.h"
+
+#define UNUSED(a) ((void) (a))
+
+#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9')
+#define CASE_DIGIT \
+ case '0': \
+ case '1': \
+ case '2': \
+ case '3': \
+ case '4': \
+ case '5': \
+ case '6': \
+ case '7': \
+ case '8': \
+ case '9':
+
+void
+sok_level_parser_init(
+ sok_level_parser_t * const parser,
+ const sok_level_parser_cbs_t * const cbs,
+ void * const user_data
+) {
+ parser->cbs = cbs;
+ parser->user_data = user_data;
+}
+
+bool
+sok_level_parser_parse(
+ sok_level_parser_t * const parser,
+ const char * const buf,
+ const size_t buf_len
+) {
+ sok_pos_t size = { .x = 0, .y = 0 };
+
+ for (size_t i = 0, ofs = 0, w = 0; i < buf_len; i++) {
+ if (buf[i] == '|') {
+ if (w > size.x) {
+ size.x = w;
+ }
+
+ // reset column position, increment row count
+ w = 0;
+ size.y++;
+ } else if (IS_DIGIT(buf[i])) {
+ ofs = 10 * ofs + (buf[i] - '0');
+ } else if (ofs > 0) {
+ w += ofs;
+ ofs = 0;
+ } else {
+ w++;
+ }
+ }
+
+ // increment row count
+ size.y++;
+
+ // emit level size
+ if (
+ parser->cbs->on_size &&
+ !parser->cbs->on_size(parser, size)
+ ) {
+ // return failure
+ return false;
+ }
+
+ sok_pos_t pos = { 0, 0 };
+ for (size_t i = 0, ofs = 0; i < buf_len; i++) {
+ switch (buf[i]) {
+ case '|':
+ // new line
+ pos.x = 0;
+ pos.y++;
+
+ break;
+ CASE_DIGIT
+ ofs = 10 * ofs + (buf[i] - '0');
+ break;
+ case '-':
+ case '_':
+ case ' ':
+ // advance
+ pos.x += (ofs > 0) ? ofs : 1;
+ ofs = 0;
+
+ break;
+ case '+':
+ // emit goal
+ if (parser->cbs->on_goal && !parser->cbs->on_goal(parser, pos)) {
+ // return failure
+ return false;
+ }
+
+ // emit home
+ if (parser->cbs->on_home && !parser->cbs->on_home(parser, pos)) {
+ // return failure
+ return false;
+ }
+
+ // advance
+ pos.x++;
+
+ break;
+ case '@':
+ // emit home
+ if (parser->cbs->on_home && !parser->cbs->on_home(parser, pos)) {
+ // return failure
+ return false;
+ }
+
+ // advance
+ pos.x++;
+
+ break;
+ case '#':
+ for (size_t j = (ofs > 0) ? ofs : 1; j; j--) {
+ // emit wall
+ if (parser->cbs->on_wall && !parser->cbs->on_wall(parser, pos)) {
+ // return failure
+ return false;
+ }
+
+ // advance
+ pos.x++;
+ }
+ ofs = 0;
+
+ break;
+ case '*':
+ for (size_t j = (ofs > 0) ? ofs : 1; j--;) {
+ // emit goal
+ if (parser->cbs->on_goal && !parser->cbs->on_goal(parser, pos)) {
+ // return failure
+ return false;
+ }
+
+ // emit box
+ if (parser->cbs->on_box && !parser->cbs->on_box(parser, pos)) {
+ // return failure
+ return false;
+ }
+
+ // advance
+ pos.x++;
+ }
+ ofs = 0;
+
+ break;
+ case '.':
+ for (size_t j = (ofs > 0) ? ofs : 1; j--;) {
+ // emit goal
+ if (parser->cbs->on_goal && !parser->cbs->on_goal(parser, pos)) {
+ // return failure
+ return false;
+ }
+
+ // advance
+ pos.x++;
+ }
+ ofs = 0;
+
+ break;
+ case '$':
+ for (size_t j = (ofs > 0) ? ofs : 1; j--;) {
+ // emit box
+ if (parser->cbs->on_box && !parser->cbs->on_box(parser, pos)) {
+ // return failure
+ return false;
+ }
+
+ // advance
+ pos.x++;
+ }
+ ofs = 0;
+
+ break;
+ default:
+ // emit junk
+ if (parser->cbs->on_junk && !parser->cbs->on_junk(parser, i, buf[i])) {
+ // return failure
+ return false;
+ }
+
+ // return failure
+ return false;
+ }
+ }
+
+ // return success
+ return true;
+}