diff options
| author | Ruben Beltran del Rio <jj@r.bdr.sh> | 2025-12-12 12:54:49 +0100 |
|---|---|---|
| committer | Ruben Beltran del Rio <jj@r.bdr.sh> | 2025-12-12 15:05:41 +0100 |
| commit | 1fec7e9fba5aace20e0b5c5463eca9c0d0692f51 (patch) | |
| tree | ff681a11559ffa0ce2f5e4fb7bc2653269f75000 | |
| parent | 735e4e1c8f5b51480b801024fd4d142758644810 (diff) | |
Improve performance
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | src/test.c | 36 | ||||
| -rw-r--r-- | src/wmap_parser.c | 896 | ||||
| -rw-r--r-- | src/wmap_parser.h | 35 |
4 files changed, 505 insertions, 464 deletions
@@ -2,7 +2,7 @@ # ANSI C compatible library with clean build structure CC ?= gcc -CFLAGS_RELEASE = -ansi -pedantic -Wall -Wextra -O2 -fomit-frame-pointer +CFLAGS_RELEASE = -ansi -pedantic -Wall -Wextra -O3 -march=native -fomit-frame-pointer CFLAGS_DEBUG = -ansi -pedantic -Wall -Wextra -g -DDEBUG # Build configuration @@ -97,13 +97,46 @@ int benchmark_parser(const char* filename, double time_limit_seconds) { double elapsed_time; int iterations = 0; double avg_time_ns; + FILE* file; + long size; + char* buffer; + /* Load file once before benchmarking */ + file = fopen(filename, "r"); + if (!file) { + printf("Error: Failed to open file %s\n", filename); + return 1; + } + + fseek(file, 0, SEEK_END); + size = ftell(file); + fseek(file, 0, SEEK_SET); + + if (size < 0 || size > 10 * 1024 * 1024) { + fclose(file); + printf("Error: Invalid file size\n"); + return 1; + } + + buffer = (char*)malloc(size + 1); + if (!buffer) { + fclose(file); + printf("Error: Failed to allocate buffer\n"); + return 1; + } + + fread(buffer, 1, size, file); + buffer[size] = '\0'; + fclose(file); + + /* Now benchmark only the parse_string function */ start = clock(); while (1) { - wmap_map_t* map = wmap_parse_file(filename); + wmap_map_t* map = wmap_parse_string(buffer); if (!map) { printf("Error: Failed to parse file on iteration %d\n", iterations); + free(buffer); return 1; } wmap_map_free(map); @@ -124,6 +157,7 @@ int benchmark_parser(const char* filename, double time_limit_seconds) { printf(" Avg time: %.2f ns/iter\n", avg_time_ns); printf(" Ops/sec: %.2f\n", iterations / elapsed_time); + free(buffer); return 0; } diff --git a/src/wmap_parser.c b/src/wmap_parser.c index 0fa8427..09ccd69 100644 --- a/src/wmap_parser.c +++ b/src/wmap_parser.c @@ -1,307 +1,178 @@ #include "wmap_parser.h" -/* Forward declarations for older compilers */ -static int wmap_lexer_is_identifier_char(char c); -static int wmap_lexer_is_keyword(const char* text, const char* keyword); +/* Helper macros */ +#define IS_WHITESPACE(c) ((c) == ' ' || (c) == '\t') +#define IS_DIGIT(c) ((c) >= '0' && (c) <= '9') +#define IS_NEWLINE(c) ((c) == '\n' || (c) == '\r') -static void wmap_lexer_init(wmap_lexer_t* lexer, const char* input) { - lexer->input = input; - lexer->pos = 0; - lexer->line = 1; - lexer->col = 1; - lexer->current_token = WMAP_TOKEN_EOF; - lexer->token_text[0] = '\0'; - lexer->token_number = 0.0f; -} +/* Forward declarations */ +static void wmap_skip_whitespace(const char** p, const char* end); +static int wmap_parse_float(const char** p, const char* end, float* result); +static int wmap_case_insensitive_compare(const char* a, const char* b, int len); +static int wmap_is_keyword(const char* str, int len, const char* keyword); +static wmap_shape_t wmap_parse_shape_name(const char* str, int len); +static wmap_stage_t wmap_parse_stage_name(const char* str, int len); +static void wmap_parse_line(wmap_parser_t* parser, const char* line, int len); -static void wmap_lexer_skip_whitespace(wmap_lexer_t* lexer) { - while (lexer->input[lexer->pos] != '\0' && - (lexer->input[lexer->pos] == ' ' || lexer->input[lexer->pos] == '\t')) { - lexer->pos++; - lexer->col++; +/* Skip whitespace in a line */ +static void wmap_skip_whitespace(const char** p, const char* end) { + while (*p < end && IS_WHITESPACE(**p)) { + (*p)++; } } -static int wmap_lexer_is_identifier_char(char c) { - return c != '-' && c != '+' && c != ',' && c != '[' && c != ']' && - c != '(' && c != ')' && c != '\n' && c != '\r' && c != '\0' && c != ' ' && c != '\t'; -} - -static wmap_token_type_t wmap_lexer_next_token(wmap_lexer_t* lexer) { - char c; - int start, len, has_dot; - - wmap_lexer_skip_whitespace(lexer); - - if (lexer->input[lexer->pos] == '\0') { - return lexer->current_token = WMAP_TOKEN_EOF; - } - - c = lexer->input[lexer->pos]; +/* Parse a float number */ +static int wmap_parse_float(const char** p, const char* end, float* result) { + const char* start; + char buffer[64]; + int len; - if (c == '\n') { - lexer->pos++; - lexer->line++; - lexer->col = 1; - return lexer->current_token = WMAP_TOKEN_NEWLINE; - } - - if (c == '\r') { - lexer->pos++; - if (lexer->input[lexer->pos] == '\n') { - lexer->pos++; - } - lexer->line++; - lexer->col = 1; - return lexer->current_token = WMAP_TOKEN_NEWLINE; - } + wmap_skip_whitespace(p, end); + start = *p; - if (c == '(') { - lexer->pos++; - lexer->col++; - return lexer->current_token = WMAP_TOKEN_LPAREN; + /* Handle optional leading dot or sign */ + if (*p < end && (**p == '.' || **p == '-' || **p == '+')) { + (*p)++; } - if (c == ')') { - lexer->pos++; - lexer->col++; - return lexer->current_token = WMAP_TOKEN_RPAREN; + /* Read digits */ + while (*p < end && (IS_DIGIT(**p) || **p == '.')) { + (*p)++; } - if (c == '[') { - lexer->pos++; - lexer->col++; - return lexer->current_token = WMAP_TOKEN_LBRACKET; - } - - if (c == ']') { - lexer->pos++; - lexer->col++; - return lexer->current_token = WMAP_TOKEN_RBRACKET; - } - - if (c == ',') { - lexer->pos++; - lexer->col++; - return lexer->current_token = WMAP_TOKEN_COMMA; - } - - if (c == '+') { - lexer->pos++; - lexer->col++; - return lexer->current_token = WMAP_TOKEN_PLUS; - } - - if (c == '-') { - if (lexer->input[lexer->pos + 1] == '-') { - lexer->pos += 2; - lexer->col += 2; - return lexer->current_token = WMAP_TOKEN_DASH; - } else if (lexer->input[lexer->pos + 1] == '>') { - lexer->pos += 2; - lexer->col += 2; - return lexer->current_token = WMAP_TOKEN_ARROW; - } else { - lexer->pos++; - lexer->col++; - return lexer->current_token = WMAP_TOKEN_MINUS; - } - } - - if (isdigit((unsigned char)c) || c == '.') { - start = lexer->pos; - has_dot = 0; - - if (c == '.') { - has_dot = 1; - lexer->pos++; - lexer->col++; - if (!isdigit((unsigned char)lexer->input[lexer->pos])) { - return lexer->current_token = WMAP_TOKEN_ERROR; - } - } - - while (isdigit((unsigned char)lexer->input[lexer->pos])) { - lexer->pos++; - lexer->col++; - } - - if (!has_dot && lexer->input[lexer->pos] == '.') { - has_dot = 1; - lexer->pos++; - lexer->col++; - while (isdigit((unsigned char)lexer->input[lexer->pos])) { - lexer->pos++; - lexer->col++; - } - } - - len = lexer->pos - start; - if (len >= WMAP_MAX_NAME_LEN) { - return lexer->current_token = WMAP_TOKEN_ERROR; - } - - strncpy(lexer->token_text, lexer->input + start, len); - lexer->token_text[len] = '\0'; - lexer->token_number = (float)atof(lexer->token_text); - - return lexer->current_token = WMAP_TOKEN_NUMBER; + len = *p - start; + if (len == 0 || len >= 64) { + return 0; } - if (wmap_lexer_is_identifier_char(c)) { - start = lexer->pos; - - while (lexer->input[lexer->pos] != '\0' && - wmap_lexer_is_identifier_char(lexer->input[lexer->pos])) { - lexer->pos++; - lexer->col++; - } - - len = lexer->pos - start; - if (len >= WMAP_MAX_NAME_LEN) { - len = WMAP_MAX_NAME_LEN - 1; - } - - strncpy(lexer->token_text, lexer->input + start, len); - lexer->token_text[len] = '\0'; + memcpy(buffer, start, len); + buffer[len] = '\0'; + *result = (float)atof(buffer); + return 1; +} - return lexer->current_token = WMAP_TOKEN_IDENTIFIER; +/* Case-insensitive string comparison */ +static int wmap_case_insensitive_compare(const char* a, const char* b, int len) { + int i; + for (i = 0; i < len; i++) { + char ca = a[i]; + char cb = b[i]; + if (ca >= 'A' && ca <= 'Z') ca += 32; + if (cb >= 'A' && cb <= 'Z') cb += 32; + if (ca != cb) return 0; } - - lexer->pos++; - lexer->col++; - return lexer->current_token = WMAP_TOKEN_ERROR; + return 1; } -static int wmap_lexer_is_keyword(const char* text, const char* keyword) { - int i = 0; - while (text[i] && keyword[i]) { - char tc = text[i]; - char kc = keyword[i]; - if (tc >= 'A' && tc <= 'Z') tc += 32; - if (kc >= 'A' && kc <= 'Z') kc += 32; - if (tc != kc) { - return 0; - } - i++; - } - return text[i] == keyword[i]; +/* Check if string matches keyword (case-insensitive) */ +static int wmap_is_keyword(const char* str, int len, const char* keyword) { + int klen = strlen(keyword); + if (len != klen) return 0; + return wmap_case_insensitive_compare(str, keyword, len); } -static wmap_shape_t wmap_parse_shape(const char* text) { - if (wmap_lexer_is_keyword(text, "x")) return WMAP_SHAPE_X; - if (wmap_lexer_is_keyword(text, "square")) return WMAP_SHAPE_SQUARE; - if (wmap_lexer_is_keyword(text, "triangle")) return WMAP_SHAPE_TRIANGLE; - if (wmap_lexer_is_keyword(text, "circle")) return WMAP_SHAPE_CIRCLE; +/* Parse shape name */ +static wmap_shape_t wmap_parse_shape_name(const char* str, int len) { + if (wmap_is_keyword(str, len, "x")) return WMAP_SHAPE_X; + if (wmap_is_keyword(str, len, "square")) return WMAP_SHAPE_SQUARE; + if (wmap_is_keyword(str, len, "triangle")) return WMAP_SHAPE_TRIANGLE; + if (wmap_is_keyword(str, len, "circle")) return WMAP_SHAPE_CIRCLE; return WMAP_SHAPE_NONE; } -static wmap_stage_t wmap_parse_stage(const char* text) { - if (wmap_lexer_is_keyword(text, "i")) return WMAP_STAGE_I; - if (wmap_lexer_is_keyword(text, "ii")) return WMAP_STAGE_II; - if (wmap_lexer_is_keyword(text, "iii")) return WMAP_STAGE_III; - if (wmap_lexer_is_keyword(text, "iv")) return WMAP_STAGE_IV; +/* Parse stage name */ +static wmap_stage_t wmap_parse_stage_name(const char* str, int len) { + if (wmap_is_keyword(str, len, "i")) return WMAP_STAGE_I; + if (wmap_is_keyword(str, len, "ii")) return WMAP_STAGE_II; + if (wmap_is_keyword(str, len, "iii")) return WMAP_STAGE_III; + if (wmap_is_keyword(str, len, "iv")) return WMAP_STAGE_IV; return 0; } -static wmap_parser_t* wmap_parser_create(const char* input) { - wmap_parser_t* parser = (wmap_parser_t*)malloc(sizeof(wmap_parser_t)); - if (!parser) return NULL; +/* Parse coordinates (x, y) */ +static int wmap_parse_coordinates(const char** p, const char* end, float* x, float* y) { + wmap_skip_whitespace(p, end); - parser->map = (wmap_map_t*)malloc(sizeof(wmap_map_t)); - if (!parser->map) { - free(parser); - return NULL; - } + if (*p >= end || **p != '(') return 0; + (*p)++; - memset(parser->map, 0, sizeof(wmap_map_t)); - wmap_lexer_init(&parser->lexer, input); - parser->error_count = 0; + if (!wmap_parse_float(p, end, x)) return 0; - wmap_lexer_next_token(&parser->lexer); + wmap_skip_whitespace(p, end); + if (*p >= end || **p != ',') return 0; + (*p)++; - return parser; -} + if (!wmap_parse_float(p, end, y)) return 0; -static void wmap_parser_destroy(wmap_parser_t* parser) { - if (parser) { - if (parser->map) { - free(parser->map); - } - free(parser); - } -} + wmap_skip_whitespace(p, end); + if (*p >= end || **p != ')') return 0; + (*p)++; -static void wmap_parser_skip_line(wmap_parser_t* parser) { - while (parser->lexer.current_token != WMAP_TOKEN_NEWLINE && - parser->lexer.current_token != WMAP_TOKEN_EOF) { - wmap_lexer_next_token(&parser->lexer); - } - if (parser->lexer.current_token == WMAP_TOKEN_NEWLINE) { - wmap_lexer_next_token(&parser->lexer); - } + return 1; } -static int wmap_parser_parse_position(wmap_parser_t* parser, float* x, float* y) { - if (parser->lexer.current_token != WMAP_TOKEN_LPAREN) { - return 0; - } - wmap_lexer_next_token(&parser->lexer); +/* Parse component line: name (x, y) [shape] */ +static int wmap_parse_component_line(wmap_parser_t* parser, const char* line, int len) { + const char* p; + const char* end; + const char* name_start; + const char* name_end; + const char* shape_start; + int name_len, shape_len, i; + wmap_component_t* comp; - if (parser->lexer.current_token != WMAP_TOKEN_NUMBER) { + if (parser->map->component_count >= WMAP_MAX_COMPONENTS) { return 0; } - *x = parser->lexer.token_number; - wmap_lexer_next_token(&parser->lexer); - if (parser->lexer.current_token != WMAP_TOKEN_COMMA) { - return 0; - } - wmap_lexer_next_token(&parser->lexer); + p = line; + end = line + len; - if (parser->lexer.current_token != WMAP_TOKEN_NUMBER) { - return 0; - } - *y = parser->lexer.token_number; - wmap_lexer_next_token(&parser->lexer); + wmap_skip_whitespace(&p, end); + if (p >= end) return 0; - if (parser->lexer.current_token != WMAP_TOKEN_RPAREN) { - return 0; + /* Find component name (everything before '(') */ + name_start = p; + while (p < end && *p != '(') { + p++; } - wmap_lexer_next_token(&parser->lexer); - - return 1; -} + name_end = p; -static int wmap_parser_parse_component(wmap_parser_t* parser) { - wmap_component_t* comp; + /* Trim trailing whitespace from name */ + while (name_end > name_start && IS_WHITESPACE(name_end[-1])) { + name_end--; + } - if (parser->map->component_count >= WMAP_MAX_COMPONENTS) { + name_len = name_end - name_start; + if (name_len == 0 || name_len >= WMAP_MAX_NAME_LEN) { return 0; } comp = &parser->map->components[parser->map->component_count]; - if (parser->lexer.current_token != WMAP_TOKEN_IDENTIFIER) { - return 0; + /* Copy name */ + for (i = 0; i < name_len; i++) { + comp->name[i] = name_start[i]; } - strncpy(comp->name, parser->lexer.token_text, WMAP_MAX_NAME_LEN - 1); - comp->name[WMAP_MAX_NAME_LEN - 1] = '\0'; - wmap_lexer_next_token(&parser->lexer); + comp->name[name_len] = '\0'; - if (!wmap_parser_parse_position(parser, &comp->x, &comp->y)) { + /* Parse coordinates */ + if (!wmap_parse_coordinates(&p, end, &comp->x, &comp->y)) { return 0; } + /* Optional shape */ comp->shape = WMAP_SHAPE_NONE; - if (parser->lexer.current_token == WMAP_TOKEN_LBRACKET) { - wmap_lexer_next_token(&parser->lexer); - if (parser->lexer.current_token == WMAP_TOKEN_IDENTIFIER) { - comp->shape = wmap_parse_shape(parser->lexer.token_text); - wmap_lexer_next_token(&parser->lexer); + wmap_skip_whitespace(&p, end); + if (p < end && *p == '[') { + p++; + shape_start = p; + while (p < end && *p != ']') { + p++; } - if (parser->lexer.current_token == WMAP_TOKEN_RBRACKET) { - wmap_lexer_next_token(&parser->lexer); + shape_len = p - shape_start; + if (shape_len > 0) { + comp->shape = wmap_parse_shape_name(shape_start, shape_len); } } @@ -309,85 +180,136 @@ static int wmap_parser_parse_component(wmap_parser_t* parser) { return 1; } -static int wmap_parser_parse_dependency(wmap_parser_t* parser) { +/* Parse dependency line: from -> to or from -- to */ +static int wmap_parse_dependency_line(wmap_parser_t* parser, const char* line, int len) { + const char* p; + const char* end; + const char* from_start; + const char* from_end; + const char* to_start; + const char* to_end; + int from_len, to_len, is_arrow, i; wmap_dependency_t* dep; if (parser->map->dependency_count >= WMAP_MAX_DEPENDENCIES) { return 0; } - dep = &parser->map->dependencies[parser->map->dependency_count]; + p = line; + end = line + len; - if (parser->lexer.current_token != WMAP_TOKEN_IDENTIFIER) { - return 0; + wmap_skip_whitespace(&p, end); + from_start = p; + + /* Find arrow or dash */ + while (p < end) { + if (*p == '-') { + if (p + 1 < end && p[1] == '>') { + from_end = p; + is_arrow = 1; + p += 2; + goto found_separator; + } else if (p + 1 < end && p[1] == '-') { + from_end = p; + is_arrow = 0; + p += 2; + goto found_separator; + } + } + p++; } - strncpy(dep->from, parser->lexer.token_text, WMAP_MAX_NAME_LEN - 1); - dep->from[WMAP_MAX_NAME_LEN - 1] = '\0'; - wmap_lexer_next_token(&parser->lexer); - if (parser->lexer.current_token == WMAP_TOKEN_ARROW) { - dep->is_arrow = 1; - wmap_lexer_next_token(&parser->lexer); - } else if (parser->lexer.current_token == WMAP_TOKEN_DASH) { - dep->is_arrow = 0; - wmap_lexer_next_token(&parser->lexer); - } else { + return 0; /* No separator found */ + +found_separator: + /* Trim trailing whitespace from 'from' */ + while (from_end > from_start && IS_WHITESPACE(from_end[-1])) { + from_end--; + } + + from_len = from_end - from_start; + if (from_len == 0 || from_len >= WMAP_MAX_NAME_LEN) { return 0; } - if (parser->lexer.current_token != WMAP_TOKEN_IDENTIFIER) { + wmap_skip_whitespace(&p, end); + to_start = p; + + /* Find end of 'to' */ + while (p < end && !IS_WHITESPACE(*p)) { + p++; + } + to_end = p; + + to_len = to_end - to_start; + if (to_len == 0 || to_len >= WMAP_MAX_NAME_LEN) { return 0; } - strncpy(dep->to, parser->lexer.token_text, WMAP_MAX_NAME_LEN - 1); - dep->to[WMAP_MAX_NAME_LEN - 1] = '\0'; - wmap_lexer_next_token(&parser->lexer); + dep = &parser->map->dependencies[parser->map->dependency_count]; + + /* Copy 'from' */ + for (i = 0; i < from_len; i++) { + dep->from[i] = from_start[i]; + } + dep->from[from_len] = '\0'; + + /* Copy 'to' */ + for (i = 0; i < to_len; i++) { + dep->to[i] = to_start[i]; + } + dep->to[to_len] = '\0'; + + dep->is_arrow = is_arrow; parser->map->dependency_count++; return 1; } -static int wmap_parser_parse_note(wmap_parser_t* parser) { +/* Parse note line: [note] (x, y) text */ +static int wmap_parse_note_line(wmap_parser_t* parser, const char* line, int len) { + const char* p; + const char* end; + const char* text_start; + int text_len, i; wmap_note_t* note; - int text_pos; if (parser->map->note_count >= WMAP_MAX_NOTES) { return 0; } + p = line; + end = line + len; + note = &parser->map->notes[parser->map->note_count]; - if (!wmap_parser_parse_position(parser, ¬e->x, ¬e->y)) { + /* Parse coordinates */ + if (!wmap_parse_coordinates(&p, end, ¬e->x, ¬e->y)) { return 0; } - text_pos = 0; - while (parser->lexer.current_token != WMAP_TOKEN_NEWLINE && - parser->lexer.current_token != WMAP_TOKEN_EOF && - text_pos < WMAP_MAX_TEXT_LEN - 1) { + /* Get remaining text */ + wmap_skip_whitespace(&p, end); + text_start = p; + text_len = end - text_start; - if (parser->lexer.current_token == WMAP_TOKEN_IDENTIFIER) { - int len = strlen(parser->lexer.token_text); - if (text_pos + len + 1 < WMAP_MAX_TEXT_LEN - 1) { - strncpy(note->text + text_pos, parser->lexer.token_text, len); - text_pos += len; - note->text[text_pos++] = ' '; - note->text[text_pos] = '\0'; - } - } - wmap_lexer_next_token(&parser->lexer); + if (text_len >= WMAP_MAX_TEXT_LEN) { + text_len = WMAP_MAX_TEXT_LEN - 1; } - if (text_pos > 0) { - note->text[text_pos - 1] = '\0'; - } else { - note->text[0] = '\0'; + for (i = 0; i < text_len; i++) { + note->text[i] = text_start[i]; } + note->text[text_len] = '\0'; parser->map->note_count++; return 1; } -static int wmap_parser_parse_stage(wmap_parser_t* parser) { +/* Parse stage line: [i|ii|iii|iv] value */ +static int wmap_parse_stage_line(wmap_parser_t* parser, const char* stage_name, int name_len, + const char* line, int len) { + const char* p; + const char* end; wmap_stage_data_t* stage; if (parser->map->stage_count >= WMAP_MAX_STAGES) { @@ -395,33 +317,29 @@ static int wmap_parser_parse_stage(wmap_parser_t* parser) { } stage = &parser->map->stages[parser->map->stage_count]; - - if (parser->lexer.current_token != WMAP_TOKEN_IDENTIFIER) { - return 0; - } - - stage->stage = wmap_parse_stage(parser->lexer.token_text); + stage->stage = wmap_parse_stage_name(stage_name, name_len); if (stage->stage == 0) { return 0; } - wmap_lexer_next_token(&parser->lexer); - if (parser->lexer.current_token != WMAP_TOKEN_RBRACKET) { - return 0; - } - wmap_lexer_next_token(&parser->lexer); + p = line; + end = line + len; - if (parser->lexer.current_token != WMAP_TOKEN_NUMBER) { + if (!wmap_parse_float(&p, end, &stage->value)) { return 0; } - stage->value = parser->lexer.token_number; - wmap_lexer_next_token(&parser->lexer); parser->map->stage_count++; return 1; } -static int wmap_parser_parse_group(wmap_parser_t* parser) { +/* Parse group line: [group] member1, member2, ... */ +static int wmap_parse_group_line(wmap_parser_t* parser, const char* line, int len) { + const char* p; + const char* end; + const char* member_start; + const char* member_end; + int member_len, i; wmap_group_t* group; if (parser->map->group_count >= WMAP_MAX_GROUPS) { @@ -431,18 +349,36 @@ static int wmap_parser_parse_group(wmap_parser_t* parser) { group = &parser->map->groups[parser->map->group_count]; group->member_count = 0; - while (parser->lexer.current_token == WMAP_TOKEN_IDENTIFIER && - group->member_count < WMAP_MAX_GROUP_MEMBERS) { + p = line; + end = line + len; - strncpy(group->members[group->member_count], parser->lexer.token_text, WMAP_MAX_NAME_LEN - 1); - group->members[group->member_count][WMAP_MAX_NAME_LEN - 1] = '\0'; - group->member_count++; - wmap_lexer_next_token(&parser->lexer); + while (p < end && group->member_count < WMAP_MAX_GROUP_MEMBERS) { + wmap_skip_whitespace(&p, end); + member_start = p; - if (parser->lexer.current_token == WMAP_TOKEN_COMMA) { - wmap_lexer_next_token(&parser->lexer); - } else { - break; + /* Find end of member (comma or end of line) */ + while (p < end && *p != ',') { + p++; + } + member_end = p; + + /* Trim trailing whitespace */ + while (member_end > member_start && IS_WHITESPACE(member_end[-1])) { + member_end--; + } + + member_len = member_end - member_start; + if (member_len > 0 && member_len < WMAP_MAX_NAME_LEN) { + for (i = 0; i < member_len; i++) { + group->members[group->member_count][i] = member_start[i]; + } + group->members[group->member_count][member_len] = '\0'; + group->member_count++; + } + + /* Skip comma */ + if (p < end && *p == ',') { + p++; } } @@ -454,166 +390,272 @@ static int wmap_parser_parse_group(wmap_parser_t* parser) { return 0; } -static int wmap_parser_parse_inertia(wmap_parser_t* parser) { +/* Parse inertia line: [inertia] name */ +static int wmap_parse_inertia_line(wmap_parser_t* parser, const char* line, int len) { + const char* p; + const char* end; + const char* name_start; + const char* name_end; + int name_len, i; wmap_inertia_t* inertia; if (parser->map->inertia_count >= WMAP_MAX_INERTIAS) { return 0; } - inertia = &parser->map->inertias[parser->map->inertia_count]; + p = line; + end = line + len; + + wmap_skip_whitespace(&p, end); + name_start = p; + name_end = end; + + /* Trim trailing whitespace */ + while (name_end > name_start && IS_WHITESPACE(name_end[-1])) { + name_end--; + } - if (parser->lexer.current_token != WMAP_TOKEN_IDENTIFIER) { + name_len = name_end - name_start; + if (name_len == 0 || name_len >= WMAP_MAX_NAME_LEN) { return 0; } - strncpy(inertia->name, parser->lexer.token_text, WMAP_MAX_NAME_LEN - 1); - inertia->name[WMAP_MAX_NAME_LEN - 1] = '\0'; - wmap_lexer_next_token(&parser->lexer); + + inertia = &parser->map->inertias[parser->map->inertia_count]; + + for (i = 0; i < name_len; i++) { + inertia->name[i] = name_start[i]; + } + inertia->name[name_len] = '\0'; parser->map->inertia_count++; return 1; } -static int wmap_parser_parse_evolution(wmap_parser_t* parser) { +/* Parse evolution line: [evolution] name +/- value */ +static int wmap_parse_evolution_line(wmap_parser_t* parser, const char* line, int len) { + const char* p; + const char* end; + const char* name_start; + const char* name_end; + int name_len, sign, i; + float value; wmap_evolution_t* evolution; - int sign; if (parser->map->evolution_count >= WMAP_MAX_EVOLUTIONS) { return 0; } - evolution = &parser->map->evolutions[parser->map->evolution_count]; + p = line; + end = line + len; - if (parser->lexer.current_token != WMAP_TOKEN_IDENTIFIER) { - return 0; + wmap_skip_whitespace(&p, end); + name_start = p; + + /* Find sign */ + while (p < end && *p != '+' && *p != '-') { + p++; } - strncpy(evolution->name, parser->lexer.token_text, WMAP_MAX_NAME_LEN - 1); - evolution->name[WMAP_MAX_NAME_LEN - 1] = '\0'; - wmap_lexer_next_token(&parser->lexer); + name_end = p; - sign = 1; - if (parser->lexer.current_token == WMAP_TOKEN_PLUS) { - sign = 1; - wmap_lexer_next_token(&parser->lexer); - } else if (parser->lexer.current_token == WMAP_TOKEN_MINUS) { - sign = -1; - wmap_lexer_next_token(&parser->lexer); + /* Trim trailing whitespace from name */ + while (name_end > name_start && IS_WHITESPACE(name_end[-1])) { + name_end--; } - if (parser->lexer.current_token != WMAP_TOKEN_NUMBER) { + name_len = name_end - name_start; + if (name_len == 0 || name_len >= WMAP_MAX_NAME_LEN || p >= end) { return 0; } - evolution->change = sign * parser->lexer.token_number; - wmap_lexer_next_token(&parser->lexer); + + sign = (*p == '+') ? 1 : -1; + p++; + + if (!wmap_parse_float(&p, end, &value)) { + return 0; + } + + evolution = &parser->map->evolutions[parser->map->evolution_count]; + + for (i = 0; i < name_len; i++) { + evolution->name[i] = name_start[i]; + } + evolution->name[name_len] = '\0'; + + evolution->change = sign * value; parser->map->evolution_count++; return 1; } -static int wmap_parser_parse(wmap_parser_t* parser) { - while (parser->lexer.current_token != WMAP_TOKEN_EOF) { - if (parser->lexer.current_token == WMAP_TOKEN_NEWLINE) { - wmap_lexer_next_token(&parser->lexer); - continue; +/* Parse keyword-based line: [keyword] ... */ +static int wmap_parse_keyword_line(wmap_parser_t* parser, const char* line, int len) { + const char* p; + const char* end; + const char* keyword_start; + const char* keyword_end; + const char* content_start; + int keyword_len, content_len; + + p = line; + end = line + len; + + wmap_skip_whitespace(&p, end); + if (p >= end || *p != '[') return 0; + p++; + + keyword_start = p; + while (p < end && *p != ']') { + p++; + } + keyword_end = p; + + keyword_len = keyword_end - keyword_start; + if (keyword_len == 0 || p >= end) return 0; + + p++; /* Skip ']' */ + + wmap_skip_whitespace(&p, end); + content_start = p; + content_len = end - content_start; + + /* Check keyword type */ + if (wmap_is_keyword(keyword_start, keyword_len, "note")) { + return wmap_parse_note_line(parser, content_start, content_len); + } else if (wmap_is_keyword(keyword_start, keyword_len, "group")) { + return wmap_parse_group_line(parser, content_start, content_len); + } else if (wmap_is_keyword(keyword_start, keyword_len, "inertia")) { + return wmap_parse_inertia_line(parser, content_start, content_len); + } else if (wmap_is_keyword(keyword_start, keyword_len, "evolution")) { + return wmap_parse_evolution_line(parser, content_start, content_len); + } else { + return wmap_parse_stage_line(parser, keyword_start, keyword_len, content_start, content_len); + } +} + +/* Determine if line contains dependency or component */ +static int wmap_line_has_dependency(const char* line, int len) { + const char* p; + const char* end; + + p = line; + end = line + len; + + while (p < end) { + if (*p == '(') { + return 0; /* Found '(' first - it's a component */ + } + if (*p == '-' && p + 1 < end && (p[1] == '>' || p[1] == '-')) { + return 1; /* Found dependency marker */ } + p++; + } - if (parser->lexer.current_token == WMAP_TOKEN_LBRACKET) { - wmap_lexer_next_token(&parser->lexer); + return 0; +} - if (parser->lexer.current_token == WMAP_TOKEN_IDENTIFIER) { - if (wmap_lexer_is_keyword(parser->lexer.token_text, "note")) { - wmap_lexer_next_token(&parser->lexer); - if (parser->lexer.current_token == WMAP_TOKEN_RBRACKET) { - wmap_lexer_next_token(&parser->lexer); - if (!wmap_parser_parse_note(parser)) { - parser->error_count++; - wmap_parser_skip_line(parser); - } - } else { - parser->error_count++; - wmap_parser_skip_line(parser); - } - } else if (wmap_lexer_is_keyword(parser->lexer.token_text, "group")) { - wmap_lexer_next_token(&parser->lexer); - if (parser->lexer.current_token == WMAP_TOKEN_RBRACKET) { - wmap_lexer_next_token(&parser->lexer); - if (!wmap_parser_parse_group(parser)) { - parser->error_count++; - wmap_parser_skip_line(parser); - } - } else { - parser->error_count++; - wmap_parser_skip_line(parser); - } - } else if (wmap_lexer_is_keyword(parser->lexer.token_text, "inertia")) { - wmap_lexer_next_token(&parser->lexer); - if (parser->lexer.current_token == WMAP_TOKEN_RBRACKET) { - wmap_lexer_next_token(&parser->lexer); - if (!wmap_parser_parse_inertia(parser)) { - parser->error_count++; - wmap_parser_skip_line(parser); - } - } else { - parser->error_count++; - wmap_parser_skip_line(parser); - } - } else if (wmap_lexer_is_keyword(parser->lexer.token_text, "evolution")) { - wmap_lexer_next_token(&parser->lexer); - if (parser->lexer.current_token == WMAP_TOKEN_RBRACKET) { - wmap_lexer_next_token(&parser->lexer); - if (!wmap_parser_parse_evolution(parser)) { - parser->error_count++; - wmap_parser_skip_line(parser); - } - } else { - parser->error_count++; - wmap_parser_skip_line(parser); - } - } else { - if (!wmap_parser_parse_stage(parser)) { - parser->error_count++; - wmap_parser_skip_line(parser); - } - } - } else { - parser->error_count++; - wmap_parser_skip_line(parser); - } - } else if (parser->lexer.current_token == WMAP_TOKEN_IDENTIFIER) { - wmap_lexer_t lookahead = parser->lexer; - int is_dependency = 0; +/* Parse a single line */ +static void wmap_parse_line(wmap_parser_t* parser, const char* line, int len) { + const char* p; + const char* end; + + if (len == 0) return; + + p = line; + end = line + len; + + /* Skip leading whitespace */ + wmap_skip_whitespace(&p, end); + if (p >= end) return; + + /* Check for keyword line */ + if (*p == '[') { + if (!wmap_parse_keyword_line(parser, line, len)) { + parser->error_count++; + } + return; + } + + /* Check for dependency or component */ + if (wmap_line_has_dependency(line, len)) { + if (!wmap_parse_dependency_line(parser, line, len)) { + parser->error_count++; + } + } else { + if (!wmap_parse_component_line(parser, line, len)) { + parser->error_count++; + } + } +} + +/* Create parser - simplified without line array allocation */ +static wmap_parser_t* wmap_parser_create(const char* input) { + wmap_parser_t* parser; + + parser = (wmap_parser_t*)malloc(sizeof(wmap_parser_t)); + if (!parser) return NULL; + + parser->input = input; + parser->error_count = 0; - wmap_lexer_next_token(&lookahead); - while (lookahead.current_token != WMAP_TOKEN_NEWLINE && - lookahead.current_token != WMAP_TOKEN_EOF) { - if (lookahead.current_token == WMAP_TOKEN_ARROW || - lookahead.current_token == WMAP_TOKEN_DASH) { - is_dependency = 1; - break; - } - wmap_lexer_next_token(&lookahead); + parser->map = (wmap_map_t*)malloc(sizeof(wmap_map_t)); + if (!parser->map) { + free(parser); + return NULL; + } + + memset(parser->map, 0, sizeof(wmap_map_t)); + + return parser; +} + +/* Destroy parser - simplified without line array */ +static void wmap_parser_destroy(wmap_parser_t* parser) { + if (parser) { + if (parser->map) { + free(parser->map); + } + free(parser); + } +} + +/* Parse entry point - now just calls direct parsing */ +static int wmap_parser_parse(wmap_parser_t* parser) { + const char* p; + const char* line_start; + int line_len; + + p = parser->input; + line_start = p; + + while (*p != '\0') { + if (*p == '\n' || *p == '\r') { + /* Parse the line if non-empty */ + line_len = p - line_start; + if (line_len > 0) { + wmap_parse_line(parser, line_start, line_len); } - if (is_dependency) { - if (!wmap_parser_parse_dependency(parser)) { - parser->error_count++; - wmap_parser_skip_line(parser); - } - } else { - if (!wmap_parser_parse_component(parser)) { - parser->error_count++; - wmap_parser_skip_line(parser); - } + /* Handle \r\n */ + if (*p == '\r' && p[1] == '\n') { + p++; } + p++; + line_start = p; } else { - parser->error_count++; - wmap_parser_skip_line(parser); + p++; } } + /* Parse final line if not empty */ + line_len = p - line_start; + if (line_len > 0) { + wmap_parse_line(parser, line_start, line_len); + } + return parser->error_count == 0; } +/* Public API implementation */ wmap_map_t* wmap_parse_string(const char* input) { wmap_parser_t* parser; wmap_map_t* result; diff --git a/src/wmap_parser.h b/src/wmap_parser.h index fa4371b..e1b006f 100644 --- a/src/wmap_parser.h +++ b/src/wmap_parser.h @@ -17,14 +17,6 @@ #define WMAP_MAX_INERTIAS 256 #define WMAP_MAX_EVOLUTIONS 256 -#ifdef __mc68000__ -#define WMAP_68K_INLINE static -#define WMAP_68K_FAST_CALL __attribute__((regparm(2))) -#else -#define WMAP_68K_INLINE static -#define WMAP_68K_FAST_CALL -#endif - /* Public Types */ typedef enum { @@ -98,35 +90,8 @@ typedef struct { /* Internal Types (needed for implementation) */ -typedef enum { - WMAP_TOKEN_EOF = 0, - WMAP_TOKEN_IDENTIFIER, - WMAP_TOKEN_NUMBER, - WMAP_TOKEN_LPAREN, - WMAP_TOKEN_RPAREN, - WMAP_TOKEN_LBRACKET, - WMAP_TOKEN_RBRACKET, - WMAP_TOKEN_COMMA, - WMAP_TOKEN_ARROW, - WMAP_TOKEN_DASH, - WMAP_TOKEN_PLUS, - WMAP_TOKEN_MINUS, - WMAP_TOKEN_NEWLINE, - WMAP_TOKEN_ERROR -} wmap_token_type_t; - typedef struct { const char* input; - int pos; - int line; - int col; - wmap_token_type_t current_token; - char token_text[WMAP_MAX_NAME_LEN]; - float token_number; -} wmap_lexer_t; - -typedef struct { - wmap_lexer_t lexer; wmap_map_t* map; int error_count; } wmap_parser_t; |