diff options
| author | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-09-19 12:54:29 +0200 |
|---|---|---|
| committer | Ruben Beltran del Rio <git@r.bdr.sh> | 2025-09-19 12:54:29 +0200 |
| commit | 00ea2943c81d2b3a8531e8b7b591de3443b1250c (patch) | |
| tree | d3fd1d87f52388a062e42ddbd49fb8dece2dc9df /src | |
Initial commit
Diffstat (limited to 'src')
| -rw-r--r-- | src/test.c | 162 | ||||
| -rw-r--r-- | src/wmap_parser.c | 687 | ||||
| -rw-r--r-- | src/wmap_parser.h | 164 |
3 files changed, 1013 insertions, 0 deletions
diff --git a/src/test.c b/src/test.c new file mode 100644 index 0000000..858aa74 --- /dev/null +++ b/src/test.c @@ -0,0 +1,162 @@ +#include "wmap_parser.h" +#include <time.h> + +void print_component(const wmap_component_t* comp) { + const char* shape_names[] = {"", "X", "Square", "Triangle", "Circle"}; + printf("Component: %s (%.2f, %.2f)", comp->name, comp->x, comp->y); + if (comp->shape != WMAP_SHAPE_NONE) { + printf(" [%s]", shape_names[comp->shape]); + } + printf("\n"); +} + +void print_dependency(const wmap_dependency_t* dep) { + printf("Dependency: %s %s %s\n", dep->from, dep->is_arrow ? "->" : "--", dep->to); +} + +void print_note(const wmap_note_t* note) { + printf("Note: (%.2f, %.2f) %s\n", note->x, note->y, note->text); +} + +void print_stage(const wmap_stage_data_t* stage) { + const char* stage_names[] = {"", "I", "II", "III", "IV"}; + printf("Stage: [%s] %.2f\n", stage_names[stage->stage], stage->value); +} + +void print_group(const wmap_group_t* group) { + int i; + printf("Group: "); + for (i = 0; i < group->member_count; i++) { + printf("%s", group->members[i]); + if (i < group->member_count - 1) printf(", "); + } + printf("\n"); +} + +void print_inertia(const wmap_inertia_t* inertia) { + printf("Inertia: %s\n", inertia->name); +} + +void print_evolution(const wmap_evolution_t* evolution) { + printf("Evolution: %s %+.2f\n", evolution->name, evolution->change); +} + +void print_map_stats(const wmap_map_t* map) { + printf("\n=== Map Statistics ===\n"); + printf("Components: %d\n", map->component_count); + printf("Dependencies: %d\n", map->dependency_count); + printf("Notes: %d\n", map->note_count); + printf("Stages: %d\n", map->stage_count); + printf("Groups: %d\n", map->group_count); + printf("Inertias: %d\n", map->inertia_count); + printf("Evolutions: %d\n", map->evolution_count); + printf("\n"); +} + +void print_map_contents(const wmap_map_t* map) { + int i; + + printf("=== Components ===\n"); + for (i = 0; i < map->component_count; i++) { + print_component(&map->components[i]); + } + + printf("\n=== Dependencies ===\n"); + for (i = 0; i < map->dependency_count; i++) { + print_dependency(&map->dependencies[i]); + } + + printf("\n=== Notes ===\n"); + for (i = 0; i < map->note_count; i++) { + print_note(&map->notes[i]); + } + + printf("\n=== Stages ===\n"); + for (i = 0; i < map->stage_count; i++) { + print_stage(&map->stages[i]); + } + + printf("\n=== Groups ===\n"); + for (i = 0; i < map->group_count; i++) { + print_group(&map->groups[i]); + } + + printf("\n=== Inertias ===\n"); + for (i = 0; i < map->inertia_count; i++) { + print_inertia(&map->inertias[i]); + } + + printf("\n=== Evolutions ===\n"); + for (i = 0; i < map->evolution_count; i++) { + print_evolution(&map->evolutions[i]); + } +} + +int benchmark_parser(const char* filename, int iterations) { + clock_t start, end; + double total_time, avg_time; + int i; + + printf("=== Benchmarking Parser ===\n"); + printf("File: %s\n", filename); + printf("Iterations: %d\n", iterations); + + start = clock(); + + for (i = 0; i < iterations; i++) { + wmap_map_t* map = wmap_parse_file(filename); + if (!map) { + printf("Error: Failed to parse file on iteration %d\n", i); + return 1; + } + wmap_map_free(map); + } + + end = clock(); + total_time = ((double)(end - start)) / CLOCKS_PER_SEC; + avg_time = total_time / iterations; + + printf("Total time: %.4f seconds\n", total_time); + printf("Average time per parse: %.4f seconds\n", avg_time); + printf("Parses per second: %.2f\n", 1.0 / avg_time); + + return 0; +} + +int main(int argc, char* argv[]) { + const char* filename; + wmap_map_t* map; + + if (argc < 2) { + printf("Usage: %s <wmap_file> [benchmark_iterations]\n", argv[0]); + return 1; + } + + filename = argv[1]; + + printf("=== WMAP Parser Test ===\n"); + printf("Parsing file: %s\n", filename); + + map = wmap_parse_file(filename); + if (!map) { + printf("Error: Failed to parse file %s\n", filename); + return 1; + } + + print_map_stats(map); + + if (argc > 2 && strcmp(argv[2], "benchmark") == 0) { + int iterations = 1000; + if (argc > 3) { + iterations = atoi(argv[3]); + } + benchmark_parser(filename, iterations); + } else { + print_map_contents(map); + } + + wmap_map_free(map); + + printf("=== Test Complete ===\n"); + return 0; +} diff --git a/src/wmap_parser.c b/src/wmap_parser.c new file mode 100644 index 0000000..e0cf6d6 --- /dev/null +++ b/src/wmap_parser.c @@ -0,0 +1,687 @@ +#include "wmap_parser.h" + +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; +} + +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++; + } +} + +WMAP_68K_INLINE int wmap_lexer_is_identifier_char(char c) WMAP_68K_FAST_CALL { + 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]; + + 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; + } + + if (c == '(') { + lexer->pos++; + lexer->col++; + return lexer->current_token = WMAP_TOKEN_LPAREN; + } + + if (c == ')') { + lexer->pos++; + lexer->col++; + return lexer->current_token = WMAP_TOKEN_RPAREN; + } + + 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(c) || c == '.') { + start = lexer->pos; + has_dot = 0; + + if (c == '.') { + has_dot = 1; + lexer->pos++; + lexer->col++; + if (!isdigit(lexer->input[lexer->pos])) { + return lexer->current_token = WMAP_TOKEN_ERROR; + } + } + + while (isdigit(lexer->input[lexer->pos])) { + lexer->pos++; + lexer->col++; + } + + if (!has_dot && lexer->input[lexer->pos] == '.') { + has_dot = 1; + lexer->pos++; + lexer->col++; + while (isdigit(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; + } + + 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'; + + return lexer->current_token = WMAP_TOKEN_IDENTIFIER; + } + + lexer->pos++; + lexer->col++; + return lexer->current_token = WMAP_TOKEN_ERROR; +} + +WMAP_68K_INLINE int wmap_lexer_is_keyword(const char* text, const char* keyword) WMAP_68K_FAST_CALL { + 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]; +} + +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; + 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; + 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; + + 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)); + wmap_lexer_init(&parser->lexer, input); + parser->error_count = 0; + + wmap_lexer_next_token(&parser->lexer); + + return parser; +} + +static void wmap_parser_destroy(wmap_parser_t* parser) { + if (parser) { + if (parser->map) { + free(parser->map); + } + free(parser); + } +} + +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); + } +} + +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); + + if (parser->lexer.current_token != WMAP_TOKEN_NUMBER) { + 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); + + if (parser->lexer.current_token != WMAP_TOKEN_NUMBER) { + return 0; + } + *y = parser->lexer.token_number; + wmap_lexer_next_token(&parser->lexer); + + if (parser->lexer.current_token != WMAP_TOKEN_RPAREN) { + return 0; + } + wmap_lexer_next_token(&parser->lexer); + + return 1; +} + +static int wmap_parser_parse_component(wmap_parser_t* parser) { + wmap_component_t* comp; + + if (parser->map->component_count >= WMAP_MAX_COMPONENTS) { + return 0; + } + + comp = &parser->map->components[parser->map->component_count]; + + if (parser->lexer.current_token != WMAP_TOKEN_IDENTIFIER) { + return 0; + } + 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); + + if (!wmap_parser_parse_position(parser, &comp->x, &comp->y)) { + return 0; + } + + 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); + } + if (parser->lexer.current_token == WMAP_TOKEN_RBRACKET) { + wmap_lexer_next_token(&parser->lexer); + } + } + + parser->map->component_count++; + return 1; +} + +static int wmap_parser_parse_dependency(wmap_parser_t* parser) { + wmap_dependency_t* dep; + + if (parser->map->dependency_count >= WMAP_MAX_DEPENDENCIES) { + return 0; + } + + dep = &parser->map->dependencies[parser->map->dependency_count]; + + if (parser->lexer.current_token != WMAP_TOKEN_IDENTIFIER) { + return 0; + } + 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; + } + + if (parser->lexer.current_token != WMAP_TOKEN_IDENTIFIER) { + 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); + + parser->map->dependency_count++; + return 1; +} + +static int wmap_parser_parse_note(wmap_parser_t* parser) { + wmap_note_t* note; + int text_pos; + + if (parser->map->note_count >= WMAP_MAX_NOTES) { + return 0; + } + + note = &parser->map->notes[parser->map->note_count]; + + if (!wmap_parser_parse_position(parser, ¬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) { + + 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_pos > 0) { + note->text[text_pos - 1] = '\0'; + } else { + note->text[0] = '\0'; + } + + parser->map->note_count++; + return 1; +} + +static int wmap_parser_parse_stage(wmap_parser_t* parser) { + wmap_stage_data_t* stage; + + if (parser->map->stage_count >= WMAP_MAX_STAGES) { + return 0; + } + + 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); + 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); + + if (parser->lexer.current_token != WMAP_TOKEN_NUMBER) { + 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) { + wmap_group_t* group; + + if (parser->map->group_count >= WMAP_MAX_GROUPS) { + return 0; + } + + 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) { + + 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); + + if (parser->lexer.current_token == WMAP_TOKEN_COMMA) { + wmap_lexer_next_token(&parser->lexer); + } else { + break; + } + } + + if (group->member_count > 0) { + parser->map->group_count++; + return 1; + } + + return 0; +} + +static int wmap_parser_parse_inertia(wmap_parser_t* parser) { + wmap_inertia_t* inertia; + + if (parser->map->inertia_count >= WMAP_MAX_INERTIAS) { + return 0; + } + + inertia = &parser->map->inertias[parser->map->inertia_count]; + + if (parser->lexer.current_token != WMAP_TOKEN_IDENTIFIER) { + 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); + + parser->map->inertia_count++; + return 1; +} + +static int wmap_parser_parse_evolution(wmap_parser_t* parser) { + wmap_evolution_t* evolution; + int sign; + + if (parser->map->evolution_count >= WMAP_MAX_EVOLUTIONS) { + return 0; + } + + evolution = &parser->map->evolutions[parser->map->evolution_count]; + + if (parser->lexer.current_token != WMAP_TOKEN_IDENTIFIER) { + return 0; + } + 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); + + 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); + } + + if (parser->lexer.current_token != WMAP_TOKEN_NUMBER) { + return 0; + } + evolution->change = sign * parser->lexer.token_number; + wmap_lexer_next_token(&parser->lexer); + + 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; + } + + if (parser->lexer.current_token == WMAP_TOKEN_LBRACKET) { + wmap_lexer_next_token(&parser->lexer); + + 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; + + 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); + } + + 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); + } + } + } else { + parser->error_count++; + wmap_parser_skip_line(parser); + } + } + + return parser->error_count == 0; +} + +wmap_map_t* wmap_parse_string(const char* input) { + wmap_parser_t* parser; + wmap_map_t* result; + + if (!input) return NULL; + + if (!wmap_validate_input(input, 1024 * 1024)) { + return NULL; + } + + parser = wmap_parser_create(input); + if (!parser) return NULL; + + wmap_parser_parse(parser); + + result = parser->map; + parser->map = NULL; + wmap_parser_destroy(parser); + + return result; +} + +wmap_map_t* wmap_parse_file(const char* filename) { + FILE* file; + long size; + char* buffer; + wmap_map_t* result; + + file = fopen(filename, "r"); + if (!file) return NULL; + + fseek(file, 0, SEEK_END); + size = ftell(file); + fseek(file, 0, SEEK_SET); + + if (size < 0 || size > 10 * 1024 * 1024) { + fclose(file); + return NULL; + } + + buffer = (char*)malloc(size + 1); + if (!buffer) { + fclose(file); + return NULL; + } + + fread(buffer, 1, size, file); + buffer[size] = '\0'; + fclose(file); + + result = wmap_parse_string(buffer); + free(buffer); + + return result; +} + +void wmap_map_free(wmap_map_t* map) { + if (map) { + free(map); + } +} + +int wmap_validate_input(const char* input, size_t max_size) { + size_t len; + if (!input) { + return 0; + } + + len = strlen(input); + if (len > max_size) { + return 0; + } + + return 1; +} diff --git a/src/wmap_parser.h b/src/wmap_parser.h new file mode 100644 index 0000000..fa4371b --- /dev/null +++ b/src/wmap_parser.h @@ -0,0 +1,164 @@ +#ifndef WMAP_PARSER_H +#define WMAP_PARSER_H + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#define WMAP_MAX_NAME_LEN 64 +#define WMAP_MAX_TEXT_LEN 256 +#define WMAP_MAX_COMPONENTS 1024 +#define WMAP_MAX_DEPENDENCIES 2048 +#define WMAP_MAX_NOTES 256 +#define WMAP_MAX_STAGES 4 +#define WMAP_MAX_GROUPS 128 +#define WMAP_MAX_GROUP_MEMBERS 32 +#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 { + WMAP_SHAPE_NONE = 0, + WMAP_SHAPE_X, + WMAP_SHAPE_SQUARE, + WMAP_SHAPE_TRIANGLE, + WMAP_SHAPE_CIRCLE +} wmap_shape_t; + +typedef enum { + WMAP_STAGE_I = 1, + WMAP_STAGE_II = 2, + WMAP_STAGE_III = 3, + WMAP_STAGE_IV = 4 +} wmap_stage_t; + +typedef struct { + char name[WMAP_MAX_NAME_LEN]; + float x, y; + wmap_shape_t shape; +} wmap_component_t; + +typedef struct { + char from[WMAP_MAX_NAME_LEN]; + char to[WMAP_MAX_NAME_LEN]; + int is_arrow; +} wmap_dependency_t; + +typedef struct { + float x, y; + char text[WMAP_MAX_TEXT_LEN]; +} wmap_note_t; + +typedef struct { + wmap_stage_t stage; + float value; +} wmap_stage_data_t; + +typedef struct { + char members[WMAP_MAX_GROUP_MEMBERS][WMAP_MAX_NAME_LEN]; + int member_count; +} wmap_group_t; + +typedef struct { + char name[WMAP_MAX_NAME_LEN]; +} wmap_inertia_t; + +typedef struct { + char name[WMAP_MAX_NAME_LEN]; + float change; +} wmap_evolution_t; + +typedef struct { + wmap_component_t components[WMAP_MAX_COMPONENTS]; + wmap_dependency_t dependencies[WMAP_MAX_DEPENDENCIES]; + wmap_note_t notes[WMAP_MAX_NOTES]; + wmap_stage_data_t stages[WMAP_MAX_STAGES]; + wmap_group_t groups[WMAP_MAX_GROUPS]; + wmap_inertia_t inertias[WMAP_MAX_INERTIAS]; + wmap_evolution_t evolutions[WMAP_MAX_EVOLUTIONS]; + + int component_count; + int dependency_count; + int note_count; + int stage_count; + int group_count; + int inertia_count; + int evolution_count; +} wmap_map_t; + +/* 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; + +/* Public API */ + +/** + * Parse WMAP content from a string + * @param input The WMAP content as a null-terminated string + * @return Pointer to parsed map structure, or NULL on failure + */ +wmap_map_t* wmap_parse_string(const char* input); + +/** + * Parse WMAP content from a file + * @param filename Path to the WMAP file + * @return Pointer to parsed map structure, or NULL on failure + */ +wmap_map_t* wmap_parse_file(const char* filename); + +/** + * Free memory allocated for a parsed map + * @param map Pointer to the map structure to free + */ +void wmap_map_free(wmap_map_t* map); + +/** + * Validate input size and format before parsing + * @param input The input string to validate + * @param max_size Maximum allowed size in bytes + * @return 1 if valid, 0 if invalid + */ +int wmap_validate_input(const char* input, size_t max_size); + +#endif |