%{ #include #include #include #include typedef struct page_t page_t; typedef struct footer_t footer_t; typedef struct page_node_t page_node_t; typedef struct choicelist choicelist; struct page_node_t { page_t *page; page_node_t *next; }; #define FOOTER_END 1 #define FOOTER_GOTO 2 #define FOOTER_CHOICES 3 struct footer_t { int type; union { choicelist *choices; char *link; }; }; struct page_t { char *id; char *body; footer_t footer; }; typedef struct statcheck_t statcheck_t; typedef struct statchange_t statchange_t; typedef struct choice_t choice_t; #define STATCHECK_GT 1 #define STATCHECK_LT 2 struct statcheck_t { char *stat; int value; int rel; }; struct statchange_t { char *stat; int addend; }; #define CHOICE_STATCHECK 0x1 #define CHOICE_STATCHANGE 0x2 struct choice_t { int flags; int option; char *flavor; statcheck_t statcheck; char *redirect; statchange_t statchange; }; struct choicelist { choice_t *choice; choicelist *next; }; page_node_t *pages; choice_t *make_choice(); void add_page(page_t*); void add_choice(choicelist*, choice_t*); page_t *emit_ending(); page_t *emit_goto(char*); page_t *emit_choices(); void append_body(page_t *page, char *str); union value { page_t *page; char *string; choice_t *choice; choicelist *choices; int num; }; #define YYSTYPE union value %} Story = BlankLine* (p:Page { add_page(p.page); })+ EndOfFile; Page = h:Header b:Body { $$.page = b.page; $$.page->id = h.string} BlankLine*; Header = < Identifier > { $$.string = strndup(yytext, yyleng); } Newline BlankLine*; Identifier = [A-Z][A-Z0-9_]+; Body = Footer | t:TextLine b:Body { $$ = b; append_body(b.page, t.string);}; TextLine = < (!Newline .)* Newline > { $$.string = strndup(yytext, yyleng); }; Footer = Ending { $$.page = emit_ending(); } | g:Goto { $$.page = emit_goto(g.string); } | c:ChoiceList { $$.page = emit_choices(); } ; Goto = 'GOTO' Spacing < i:Identifier > Newline { $$.string = strndup(yytext, yyleng); } ; Ending = 'THE END' Newline; ChoiceList = c:Choice l:ChoiceList { $$ = l; add_choice($$.choices, c.choice); } | c:Choice { $$.choices = NULL; add_choice($$.choices, c.choice); } Choice = b:Bullet f:Freetext r:Redirect { $$ = r; $$.choice->option = b.num; $$.choice->flavor = f.string; }; Bullet = < [0-9]+ > ')' Spacing { $$.num = atoi(yytext); }; Freetext = < (!Redirect .)+ > { $$.string = strndup(yytext, yyleng); }; Redirect = StatCheck? Spacing '[' Identifier ']' Spacing StatChange? Newline; StatCheck = '<' StatName Spacing [0-9]+ ('+' | '-')? '>'; StatChange = '(' ('+' | '-') [0-9]+ Spacing StatName ')'; StatName = [A-Za-z]+; EndOfFile = !.; BlankLine = Spacing Newline; Spacing = (' ' | '\t')*; Newline = '\r\n' | '\r' | '\n'; %% void add_page(page_t *page) { page_node_t *mynode = (page_node_t*) malloc(sizeof(page_node_t)); mynode->page = page; mynode->next = NULL; page_node_t *curr = pages; page_node_t *prev = NULL; if (curr) { for(; curr; prev = curr, curr = curr->next); prev->next = mynode; } else { pages = mynode; } } void add_choice(choicelist *choices, choice_t* choice) { choicelist *mychoices = (choicelist *) malloc(sizeof(choicelist)); mychoices->choice = choice; mychoices->next = NULL; choicelist *curr = choices; choicelist *prev = NULL; if (curr) { for(; curr; prev = curr, curr = curr->next); prev->next = mychoices; } else { choices = mychoices; } } static inline page_t *make_page() { page_t *mypage = (page_t*) malloc(sizeof(page_t)); mypage->id = NULL; mypage->body = NULL; return mypage; } inline choice_t *make_choice() { choice_t *mychoice = (choice_t*) malloc(sizeof(choice_t)); mychoice->flags = 0x0; printf("choice 0x%x\n", mychoice); return mychoice; } page_t *emit_goto(char *id) { page_t *mypage = make_page(); mypage->footer.type = FOOTER_GOTO; mypage->footer.link = id; return mypage; } page_t *emit_ending() { page_t *mypage = make_page(); mypage->footer.type = FOOTER_END; return mypage; } page_t *emit_choices() { page_t *mypage = make_page(); mypage->footer.type = FOOTER_CHOICES; mypage->footer.choices = NULL; printf("page 0x%x\n", mypage); return mypage; } void append_body(page_t *page, char *str) { if (page->body) { int size = strlen(page->body) + strlen(str) + 1; char *newstring = (char*) calloc(size, sizeof(char)); strcpy(newstring, str); strcat(newstring, page->body); free(page->body); page->body = newstring; } else { page->body = str; } } void print_page(page_t *page) { printf("HEADER: %s\nBODY: %s\n", page->id, page->body); switch (page->footer.type) { case FOOTER_END: printf("THE END\n"); break; case FOOTER_GOTO: printf("GOTO %s\n", page->footer.link); break; case FOOTER_CHOICES: printf("CHOICES:\n"); break; } } int main() { if (!yyparse()) { printf("Parsing Error!\n"); return 1; } else { for (page_node_t *curr = pages; curr; curr = curr->next) { print_page(curr->page); } } return 0; } /* Local Variables: */ /* mode: text */ /* End: */