#include #include // 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; }