265 lines
5.3 KiB
Plaintext
265 lines
5.3 KiB
Plaintext
%{
|
|
|
|
#include <vector>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cassert>
|
|
|
|
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: */
|