242 lines
4.6 KiB
Plaintext
242 lines
4.6 KiB
Plaintext
%{
|
|
|
|
#include <vector>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cassert>
|
|
|
|
#define STATCHECK_GT 1
|
|
#define STATCHECK_LT 2
|
|
|
|
struct statcheck_t {
|
|
char *stat;
|
|
int value;
|
|
int rel;
|
|
};
|
|
|
|
struct statchange_t {
|
|
char *stat;
|
|
int addend;
|
|
};
|
|
|
|
enum ChoiceFlags : int {
|
|
StatCheck = 0x1,
|
|
StatChange = 0x2,
|
|
};
|
|
|
|
struct Choice {
|
|
int flags;
|
|
int option;
|
|
char *flavor;
|
|
statcheck_t statcheck;
|
|
char *id;
|
|
statchange_t statchange;
|
|
|
|
Choice(char*, statcheck_t*, statchange_t*);
|
|
};
|
|
|
|
enum class FooterType {
|
|
End,
|
|
Goto,
|
|
Choices
|
|
};
|
|
|
|
struct Footer {
|
|
FooterType type;
|
|
union {
|
|
std::vector<Choice*> *choices;
|
|
char *link;
|
|
};
|
|
|
|
Footer();
|
|
~Footer();
|
|
};
|
|
|
|
struct Page {
|
|
char *id;
|
|
char *body;
|
|
Footer footer;
|
|
|
|
Page();
|
|
~Page();
|
|
};
|
|
|
|
std::vector<Page*> pages;
|
|
|
|
Page *emit_ending();
|
|
Page *emit_goto(char*);
|
|
Page *emit_choices(std::vector<Choice*>*);
|
|
|
|
void append_body(Page *page, char *str);
|
|
|
|
union value {
|
|
Page *page;
|
|
char *string;
|
|
Choice *choice;
|
|
std::vector<Choice*> *choices;
|
|
int num;
|
|
statcheck_t *statcheck;
|
|
statchange_t *statchange;
|
|
};
|
|
|
|
#define YYSTYPE union value
|
|
|
|
%}
|
|
|
|
|
|
Story = BlankLine* (p:Page { pages.push_back(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(c.choices); }
|
|
;
|
|
|
|
Goto = 'GOTO' Spacing < i:Identifier > Newline
|
|
{ $$.string = strndup(yytext, yyleng); }
|
|
;
|
|
|
|
Ending = 'THE END' Newline;
|
|
|
|
ChoiceList = c:Choice l:ChoiceList { $$ = l; $$.choices->push_back(c.choice); }
|
|
| c:Choice { $$.choices = new std::vector<Choice*>(); $$.choices->push_back(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 = ck:StatCheck? '[' i:Identifier ']' Spacing cg:StatChange? Newline
|
|
{ $$.choice = new Choice(i.string, ck.statcheck, cg.statchange); }
|
|
|
|
StatCheck = '<' StatName Spacing [0-9]+ ('+' | '-')? '>' Spacing;
|
|
|
|
StatChange = '(' ('+' | '-') [0-9]+ Spacing StatName ')';
|
|
|
|
StatName = [A-Za-z]+;
|
|
|
|
EndOfFile = !.;
|
|
|
|
BlankLine = Spacing Newline;
|
|
|
|
Spacing = (' ' | '\t')*;
|
|
|
|
Newline = '\r\n' | '\r' | '\n';
|
|
|
|
%%
|
|
|
|
Choice::Choice(char *id, statcheck_t *ck, statchange_t *cg) {
|
|
this->id = id;
|
|
if (ck) {
|
|
statcheck = *ck;
|
|
flags |= StatCheck;
|
|
}
|
|
if (cg) {
|
|
statchange = *cg;
|
|
flags |= StatChange;
|
|
}
|
|
}
|
|
|
|
Footer::Footer() {
|
|
}
|
|
|
|
Footer::~Footer() {
|
|
}
|
|
|
|
Page::Page() {
|
|
id = NULL;
|
|
body = NULL;
|
|
}
|
|
|
|
Page::~Page() {
|
|
if (id) {
|
|
free(id);
|
|
}
|
|
if (body) {
|
|
free(body);
|
|
}
|
|
}
|
|
|
|
Page *emit_goto(char *id) {
|
|
Page *mypage = new Page;
|
|
mypage->footer.type = FooterType::Goto;
|
|
mypage->footer.link = id;
|
|
return mypage;
|
|
}
|
|
|
|
Page *emit_ending() {
|
|
Page *mypage = new Page;
|
|
mypage->footer.type = FooterType::End;
|
|
return mypage;
|
|
}
|
|
|
|
Page *emit_choices(std::vector<Choice*> *choices) {
|
|
Page *mypage = new Page;
|
|
mypage->footer.type = FooterType::Choices;
|
|
mypage->footer.choices = choices;
|
|
return mypage;
|
|
}
|
|
|
|
void append_body(Page *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 *page) {
|
|
printf("HEADER: %s\nBODY: %s\n", page->id, page->body);
|
|
switch (page->footer.type) {
|
|
case FooterType::End:
|
|
printf("THE END\n");
|
|
break;
|
|
case FooterType::Goto:
|
|
printf("GOTO %s\n", page->footer.link);
|
|
break;
|
|
case FooterType::Choices:
|
|
printf("CHOICES:\n");
|
|
for (Choice *c : *page->footer.choices) {
|
|
printf("%d) %s\n", c->option, c->flavor);
|
|
}
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
if (!yyparse()) {
|
|
printf("Parsing Error!\n");
|
|
return 1;
|
|
} else {
|
|
for (Page* page : pages) {
|
|
print_page(page);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Local Variables: */
|
|
/* mode: text */
|
|
/* End: */
|