storybook/cyoa.leg

251 lines
4.9 KiB
Plaintext
Raw Normal View History

2021-12-17 13:54:05 -06:00
%{
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
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(page_t*, 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;
};
#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); }
| Choice+ { $$.page = emit_ending(); }
;
Goto = 'GOTO' Spacing < i:Identifier > Newline
{ $$.string = strndup(yytext, yyleng); }
;
Ending = 'THE END' Newline;
Choice = { $$.choice = make_choice(); } [0-9]+ ')' Spacing < (!Redirect .)+ > Redirect;
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(page_t *page, choice_t* choice) {
assert(page != choice);
choicelist *mychoices = (choicelist *) malloc(sizeof(choicelist));
mychoices->choice = choice;
mychoices->next = NULL;
choicelist *curr = page->footer.choices;
choicelist *prev = NULL;
if (curr) {
for(; curr; prev = curr, curr = curr->next);
prev->next = mychoices;
} else {
page->footer.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: */