aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/test.c162
-rw-r--r--src/wmap_parser.c687
-rw-r--r--src/wmap_parser.h164
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, &note->x, &note->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