commit 706e87a12385a678742212a436b5b386b6da5e0c Author: Dane Johnson Date: Fri Dec 17 13:54:05 2021 -0600 Init commit diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..28df1f3 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +CC = gcc +CFLAGS = -g + +all: cyoa +cyoa: cyoa.c +cyoa.c: cyoa.leg + leg -o $@ $^ +clean: + rm -f cyoa.c cyoa diff --git a/cyoa b/cyoa new file mode 100755 index 0000000..005c872 Binary files /dev/null and b/cyoa differ diff --git a/cyoa.c b/cyoa.c new file mode 100644 index 0000000..9ddc47c --- /dev/null +++ b/cyoa.c @@ -0,0 +1,1017 @@ +/* A recursive-descent parser generated by peg 0.1.18 */ + +#include +#include +#include +#define YYRULECOUNT 18 +#line 1 "cyoa.leg" + + +#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(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 + + +#ifndef YY_MALLOC +#define YY_MALLOC(C, N) malloc(N) +#endif +#ifndef YY_REALLOC +#define YY_REALLOC(C, P, N) realloc(P, N) +#endif +#ifndef YY_FREE +#define YY_FREE(C, P) free(P) +#endif +#ifndef YY_LOCAL +#define YY_LOCAL(T) static T +#endif +#ifndef YY_ACTION +#define YY_ACTION(T) static T +#endif +#ifndef YY_RULE +#define YY_RULE(T) static T +#endif +#ifndef YY_PARSE +#define YY_PARSE(T) T +#endif +#ifndef YYPARSE +#define YYPARSE yyparse +#endif +#ifndef YYPARSEFROM +#define YYPARSEFROM yyparsefrom +#endif +#ifndef YYRELEASE +#define YYRELEASE yyrelease +#endif +#ifndef YY_BEGIN +#define YY_BEGIN ( yy->__begin= yy->__pos, 1) +#endif +#ifndef YY_END +#define YY_END ( yy->__end= yy->__pos, 1) +#endif +#ifdef YY_DEBUG +# define yyprintf(args) fprintf args +#else +# define yyprintf(args) +#endif +#ifndef YYSTYPE +#define YYSTYPE int +#endif +#ifndef YY_STACK_SIZE +#define YY_STACK_SIZE 128 +#endif + +#ifndef YY_BUFFER_SIZE +#define YY_BUFFER_SIZE 1024 +#endif + +#ifndef YY_PART + +typedef struct _yycontext yycontext; +typedef void (*yyaction)(yycontext *yy, char *yytext, int yyleng); +typedef struct _yythunk { int begin, end; yyaction action; struct _yythunk *next; } yythunk; + +struct _yycontext { + char *__buf; + int __buflen; + int __pos; + int __limit; + char *__text; + int __textlen; + int __begin; + int __end; + int __textmax; + yythunk *__thunks; + int __thunkslen; + int __thunkpos; + YYSTYPE __; + YYSTYPE *__val; + YYSTYPE *__vals; + int __valslen; +#ifdef YY_CTX_MEMBERS + YY_CTX_MEMBERS +#endif +}; + +#ifdef YY_CTX_LOCAL +#define YY_CTX_PARAM_ yycontext *yyctx, +#define YY_CTX_PARAM yycontext *yyctx +#define YY_CTX_ARG_ yyctx, +#define YY_CTX_ARG yyctx +#ifndef YY_INPUT +#define YY_INPUT(yy, buf, result, max_size) \ + { \ + int yyc= getchar(); \ + result= (EOF == yyc) ? 0 : (*(buf)= yyc, 1); \ + yyprintf((stderr, "<%c>", yyc)); \ + } +#endif +#else +#define YY_CTX_PARAM_ +#define YY_CTX_PARAM +#define YY_CTX_ARG_ +#define YY_CTX_ARG +yycontext _yyctx= { 0, 0 }; +yycontext *yyctx= &_yyctx; +#ifndef YY_INPUT +#define YY_INPUT(buf, result, max_size) \ + { \ + int yyc= getchar(); \ + result= (EOF == yyc) ? 0 : (*(buf)= yyc, 1); \ + yyprintf((stderr, "<%c>", yyc)); \ + } +#endif +#endif + +YY_LOCAL(int) yyrefill(yycontext *yy) +{ + int yyn; + while (yy->__buflen - yy->__pos < 512) + { + yy->__buflen *= 2; + yy->__buf= (char *)YY_REALLOC(yy, yy->__buf, yy->__buflen); + } +#ifdef YY_CTX_LOCAL + YY_INPUT(yy, (yy->__buf + yy->__pos), yyn, (yy->__buflen - yy->__pos)); +#else + YY_INPUT((yy->__buf + yy->__pos), yyn, (yy->__buflen - yy->__pos)); +#endif + if (!yyn) return 0; + yy->__limit += yyn; + return 1; +} + +YY_LOCAL(int) yymatchDot(yycontext *yy) +{ + if (yy->__pos >= yy->__limit && !yyrefill(yy)) return 0; + ++yy->__pos; + return 1; +} + +YY_LOCAL(int) yymatchChar(yycontext *yy, int c) +{ + if (yy->__pos >= yy->__limit && !yyrefill(yy)) return 0; + if ((unsigned char)yy->__buf[yy->__pos] == c) + { + ++yy->__pos; + yyprintf((stderr, " ok yymatchChar(yy, %c) @ %s\n", c, yy->__buf+yy->__pos)); + return 1; + } + yyprintf((stderr, " fail yymatchChar(yy, %c) @ %s\n", c, yy->__buf+yy->__pos)); + return 0; +} + +YY_LOCAL(int) yymatchString(yycontext *yy, const char *s) +{ + int yysav= yy->__pos; + while (*s) + { + if (yy->__pos >= yy->__limit && !yyrefill(yy)) return 0; + if (yy->__buf[yy->__pos] != *s) + { + yy->__pos= yysav; + return 0; + } + ++s; + ++yy->__pos; + } + return 1; +} + +YY_LOCAL(int) yymatchClass(yycontext *yy, unsigned char *bits) +{ + int c; + if (yy->__pos >= yy->__limit && !yyrefill(yy)) return 0; + c= (unsigned char)yy->__buf[yy->__pos]; + if (bits[c >> 3] & (1 << (c & 7))) + { + ++yy->__pos; + yyprintf((stderr, " ok yymatchClass @ %s\n", yy->__buf+yy->__pos)); + return 1; + } + yyprintf((stderr, " fail yymatchClass @ %s\n", yy->__buf+yy->__pos)); + return 0; +} + +YY_LOCAL(void) yyDo(yycontext *yy, yyaction action, int begin, int end) +{ + while (yy->__thunkpos >= yy->__thunkslen) + { + yy->__thunkslen *= 2; + yy->__thunks= (yythunk *)YY_REALLOC(yy, yy->__thunks, sizeof(yythunk) * yy->__thunkslen); + } + yy->__thunks[yy->__thunkpos].begin= begin; + yy->__thunks[yy->__thunkpos].end= end; + yy->__thunks[yy->__thunkpos].action= action; + ++yy->__thunkpos; +} + +YY_LOCAL(int) yyText(yycontext *yy, int begin, int end) +{ + int yyleng= end - begin; + if (yyleng <= 0) + yyleng= 0; + else + { + while (yy->__textlen < (yyleng + 1)) + { + yy->__textlen *= 2; + yy->__text= (char *)YY_REALLOC(yy, yy->__text, yy->__textlen); + } + memcpy(yy->__text, yy->__buf + begin, yyleng); + } + yy->__text[yyleng]= '\0'; + return yyleng; +} + +YY_LOCAL(void) yyDone(yycontext *yy) +{ + int pos; + for (pos= 0; pos < yy->__thunkpos; ++pos) + { + yythunk *thunk= &yy->__thunks[pos]; + int yyleng= thunk->end ? yyText(yy, thunk->begin, thunk->end) : thunk->begin; + yyprintf((stderr, "DO [%d] %p %s\n", pos, thunk->action, yy->__text)); + thunk->action(yy, yy->__text, yyleng); + } + yy->__thunkpos= 0; +} + +YY_LOCAL(void) yyCommit(yycontext *yy) +{ + if ((yy->__limit -= yy->__pos)) + { + memmove(yy->__buf, yy->__buf + yy->__pos, yy->__limit); + } + yy->__begin -= yy->__pos; + yy->__end -= yy->__pos; + yy->__pos= yy->__thunkpos= 0; +} + +YY_LOCAL(int) yyAccept(yycontext *yy, int tp0) +{ + if (tp0) + { + fprintf(stderr, "accept denied at %d\n", tp0); + return 0; + } + else + { + yyDone(yy); + yyCommit(yy); + } + return 1; +} + +YY_LOCAL(void) yyPush(yycontext *yy, char *text, int count) +{ + yy->__val += count; + while (yy->__valslen <= yy->__val - yy->__vals) + { + long offset= yy->__val - yy->__vals; + yy->__valslen *= 2; + yy->__vals= (YYSTYPE *)YY_REALLOC(yy, yy->__vals, sizeof(YYSTYPE) * yy->__valslen); + yy->__val= yy->__vals + offset; + } +} +YY_LOCAL(void) yyPop(yycontext *yy, char *text, int count) { yy->__val -= count; } +YY_LOCAL(void) yySet(yycontext *yy, char *text, int count) { yy->__val[count]= yy->__; } + +#endif /* YY_PART */ + +#define YYACCEPT yyAccept(yy, yythunkpos0) + +YY_RULE(int) yy_StatName(yycontext *yy); /* 18 */ +YY_RULE(int) yy_StatChange(yycontext *yy); /* 17 */ +YY_RULE(int) yy_StatCheck(yycontext *yy); /* 16 */ +YY_RULE(int) yy_Redirect(yycontext *yy); /* 15 */ +YY_RULE(int) yy_Spacing(yycontext *yy); /* 14 */ +YY_RULE(int) yy_Choice(yycontext *yy); /* 13 */ +YY_RULE(int) yy_Goto(yycontext *yy); /* 12 */ +YY_RULE(int) yy_Ending(yycontext *yy); /* 11 */ +YY_RULE(int) yy_TextLine(yycontext *yy); /* 10 */ +YY_RULE(int) yy_Footer(yycontext *yy); /* 9 */ +YY_RULE(int) yy_Newline(yycontext *yy); /* 8 */ +YY_RULE(int) yy_Identifier(yycontext *yy); /* 7 */ +YY_RULE(int) yy_Body(yycontext *yy); /* 6 */ +YY_RULE(int) yy_Header(yycontext *yy); /* 5 */ +YY_RULE(int) yy_EndOfFile(yycontext *yy); /* 4 */ +YY_RULE(int) yy_Page(yycontext *yy); /* 3 */ +YY_RULE(int) yy_BlankLine(yycontext *yy); /* 2 */ +YY_RULE(int) yy_Story(yycontext *yy); /* 1 */ + +YY_ACTION(void) yy_1_Choice(yycontext *yy, char *yytext, int yyleng) +{ +#define __ yy->__ +#define yypos yy->__pos +#define yythunkpos yy->__thunkpos + yyprintf((stderr, "do yy_1_Choice\n")); + { +#line 120 + __.choice = make_choice(); ; + } +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_1_Goto(yycontext *yy, char *yytext, int yyleng) +{ +#define i yy->__val[-1] +#define __ yy->__ +#define yypos yy->__pos +#define yythunkpos yy->__thunkpos + yyprintf((stderr, "do yy_1_Goto\n")); + { +#line 115 + __.string = strndup(yytext, yyleng); ; + } +#undef yythunkpos +#undef yypos +#undef yy +#undef i +} +YY_ACTION(void) yy_3_Footer(yycontext *yy, char *yytext, int yyleng) +{ +#define g yy->__val[-1] +#define __ yy->__ +#define yypos yy->__pos +#define yythunkpos yy->__thunkpos + yyprintf((stderr, "do yy_3_Footer\n")); + { +#line 111 + __.page = emit_ending(); ; + } +#undef yythunkpos +#undef yypos +#undef yy +#undef g +} +YY_ACTION(void) yy_2_Footer(yycontext *yy, char *yytext, int yyleng) +{ +#define g yy->__val[-1] +#define __ yy->__ +#define yypos yy->__pos +#define yythunkpos yy->__thunkpos + yyprintf((stderr, "do yy_2_Footer\n")); + { +#line 110 + __.page = emit_goto(g.string); ; + } +#undef yythunkpos +#undef yypos +#undef yy +#undef g +} +YY_ACTION(void) yy_1_Footer(yycontext *yy, char *yytext, int yyleng) +{ +#define g yy->__val[-1] +#define __ yy->__ +#define yypos yy->__pos +#define yythunkpos yy->__thunkpos + yyprintf((stderr, "do yy_1_Footer\n")); + { +#line 109 + __.page = emit_ending(); ; + } +#undef yythunkpos +#undef yypos +#undef yy +#undef g +} +YY_ACTION(void) yy_1_TextLine(yycontext *yy, char *yytext, int yyleng) +{ +#define __ yy->__ +#define yypos yy->__pos +#define yythunkpos yy->__thunkpos + yyprintf((stderr, "do yy_1_TextLine\n")); + { +#line 107 + __.string = strndup(yytext, yyleng); ; + } +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_1_Body(yycontext *yy, char *yytext, int yyleng) +{ +#define b yy->__val[-1] +#define t yy->__val[-2] +#define __ yy->__ +#define yypos yy->__pos +#define yythunkpos yy->__thunkpos + yyprintf((stderr, "do yy_1_Body\n")); + { +#line 105 + __ = b; append_body(b.page, t.string);; + } +#undef yythunkpos +#undef yypos +#undef yy +#undef b +#undef t +} +YY_ACTION(void) yy_1_Header(yycontext *yy, char *yytext, int yyleng) +{ +#define __ yy->__ +#define yypos yy->__pos +#define yythunkpos yy->__thunkpos + yyprintf((stderr, "do yy_1_Header\n")); + { +#line 100 + __.string = strndup(yytext, yyleng); ; + } +#undef yythunkpos +#undef yypos +#undef yy +} +YY_ACTION(void) yy_1_Page(yycontext *yy, char *yytext, int yyleng) +{ +#define b yy->__val[-1] +#define h yy->__val[-2] +#define __ yy->__ +#define yypos yy->__pos +#define yythunkpos yy->__thunkpos + yyprintf((stderr, "do yy_1_Page\n")); + { +#line 97 + __.page = b.page; __.page->id = h.string; + } +#undef yythunkpos +#undef yypos +#undef yy +#undef b +#undef h +} +YY_ACTION(void) yy_1_Story(yycontext *yy, char *yytext, int yyleng) +{ +#define p yy->__val[-1] +#define __ yy->__ +#define yypos yy->__pos +#define yythunkpos yy->__thunkpos + yyprintf((stderr, "do yy_1_Story\n")); + { +#line 95 + add_page(p.page); ; + } +#undef yythunkpos +#undef yypos +#undef yy +#undef p +} + +YY_RULE(int) yy_StatName(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; + yyprintf((stderr, "%s\n", "StatName")); if (!yymatchClass(yy, (unsigned char *)"\000\000\000\000\000\000\000\000\376\377\377\007\376\377\377\007\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l1; + l2:; + { int yypos3= yy->__pos, yythunkpos3= yy->__thunkpos; if (!yymatchClass(yy, (unsigned char *)"\000\000\000\000\000\000\000\000\376\377\377\007\376\377\377\007\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l3; goto l2; + l3:; yy->__pos= yypos3; yy->__thunkpos= yythunkpos3; + } + yyprintf((stderr, " ok %s @ %s\n", "StatName", yy->__buf+yy->__pos)); + return 1; + l1:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "StatName", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_StatChange(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; + yyprintf((stderr, "%s\n", "StatChange")); if (!yymatchChar(yy, '(')) goto l4; + { int yypos5= yy->__pos, yythunkpos5= yy->__thunkpos; if (!yymatchChar(yy, '+')) goto l6; goto l5; + l6:; yy->__pos= yypos5; yy->__thunkpos= yythunkpos5; if (!yymatchChar(yy, '-')) goto l4; + } + l5:; if (!yymatchClass(yy, (unsigned char *)"\000\000\000\000\000\000\377\003\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l4; + l7:; + { int yypos8= yy->__pos, yythunkpos8= yy->__thunkpos; if (!yymatchClass(yy, (unsigned char *)"\000\000\000\000\000\000\377\003\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l8; goto l7; + l8:; yy->__pos= yypos8; yy->__thunkpos= yythunkpos8; + } if (!yy_Spacing(yy)) goto l4; if (!yy_StatName(yy)) goto l4; if (!yymatchChar(yy, ')')) goto l4; + yyprintf((stderr, " ok %s @ %s\n", "StatChange", yy->__buf+yy->__pos)); + return 1; + l4:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "StatChange", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_StatCheck(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; + yyprintf((stderr, "%s\n", "StatCheck")); if (!yymatchChar(yy, '<')) goto l9; if (!yy_StatName(yy)) goto l9; if (!yy_Spacing(yy)) goto l9; if (!yymatchClass(yy, (unsigned char *)"\000\000\000\000\000\000\377\003\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l9; + l10:; + { int yypos11= yy->__pos, yythunkpos11= yy->__thunkpos; if (!yymatchClass(yy, (unsigned char *)"\000\000\000\000\000\000\377\003\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l11; goto l10; + l11:; yy->__pos= yypos11; yy->__thunkpos= yythunkpos11; + } + { int yypos12= yy->__pos, yythunkpos12= yy->__thunkpos; + { int yypos14= yy->__pos, yythunkpos14= yy->__thunkpos; if (!yymatchChar(yy, '+')) goto l15; goto l14; + l15:; yy->__pos= yypos14; yy->__thunkpos= yythunkpos14; if (!yymatchChar(yy, '-')) goto l12; + } + l14:; goto l13; + l12:; yy->__pos= yypos12; yy->__thunkpos= yythunkpos12; + } + l13:; if (!yymatchChar(yy, '>')) goto l9; + yyprintf((stderr, " ok %s @ %s\n", "StatCheck", yy->__buf+yy->__pos)); + return 1; + l9:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "StatCheck", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_Redirect(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; + yyprintf((stderr, "%s\n", "Redirect")); + { int yypos17= yy->__pos, yythunkpos17= yy->__thunkpos; if (!yy_StatCheck(yy)) goto l17; goto l18; + l17:; yy->__pos= yypos17; yy->__thunkpos= yythunkpos17; + } + l18:; if (!yy_Spacing(yy)) goto l16; if (!yymatchChar(yy, '[')) goto l16; if (!yy_Identifier(yy)) goto l16; if (!yymatchChar(yy, ']')) goto l16; if (!yy_Spacing(yy)) goto l16; + { int yypos19= yy->__pos, yythunkpos19= yy->__thunkpos; if (!yy_StatChange(yy)) goto l19; goto l20; + l19:; yy->__pos= yypos19; yy->__thunkpos= yythunkpos19; + } + l20:; if (!yy_Newline(yy)) goto l16; + yyprintf((stderr, " ok %s @ %s\n", "Redirect", yy->__buf+yy->__pos)); + return 1; + l16:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "Redirect", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_Spacing(yycontext *yy) +{ + yyprintf((stderr, "%s\n", "Spacing")); + l22:; + { int yypos23= yy->__pos, yythunkpos23= yy->__thunkpos; + { int yypos24= yy->__pos, yythunkpos24= yy->__thunkpos; if (!yymatchChar(yy, ' ')) goto l25; goto l24; + l25:; yy->__pos= yypos24; yy->__thunkpos= yythunkpos24; if (!yymatchChar(yy, '\t')) goto l23; + } + l24:; goto l22; + l23:; yy->__pos= yypos23; yy->__thunkpos= yythunkpos23; + } + yyprintf((stderr, " ok %s @ %s\n", "Spacing", yy->__buf+yy->__pos)); + return 1; +} +YY_RULE(int) yy_Choice(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; + yyprintf((stderr, "%s\n", "Choice")); yyDo(yy, yy_1_Choice, yy->__begin, yy->__end); if (!yymatchClass(yy, (unsigned char *)"\000\000\000\000\000\000\377\003\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l26; + l27:; + { int yypos28= yy->__pos, yythunkpos28= yy->__thunkpos; if (!yymatchClass(yy, (unsigned char *)"\000\000\000\000\000\000\377\003\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l28; goto l27; + l28:; yy->__pos= yypos28; yy->__thunkpos= yythunkpos28; + } if (!yymatchChar(yy, ')')) goto l26; if (!yy_Spacing(yy)) goto l26; yyText(yy, yy->__begin, yy->__end); { +#define yytext yy->__text +#define yyleng yy->__textlen +if (!(YY_BEGIN)) goto l26; +#undef yytext +#undef yyleng + } + { int yypos31= yy->__pos, yythunkpos31= yy->__thunkpos; if (!yy_Redirect(yy)) goto l31; goto l26; + l31:; yy->__pos= yypos31; yy->__thunkpos= yythunkpos31; + } if (!yymatchDot(yy)) goto l26; + l29:; + { int yypos30= yy->__pos, yythunkpos30= yy->__thunkpos; + { int yypos32= yy->__pos, yythunkpos32= yy->__thunkpos; if (!yy_Redirect(yy)) goto l32; goto l30; + l32:; yy->__pos= yypos32; yy->__thunkpos= yythunkpos32; + } if (!yymatchDot(yy)) goto l30; goto l29; + l30:; yy->__pos= yypos30; yy->__thunkpos= yythunkpos30; + } yyText(yy, yy->__begin, yy->__end); { +#define yytext yy->__text +#define yyleng yy->__textlen +if (!(YY_END)) goto l26; +#undef yytext +#undef yyleng + } if (!yy_Redirect(yy)) goto l26; + yyprintf((stderr, " ok %s @ %s\n", "Choice", yy->__buf+yy->__pos)); + return 1; + l26:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "Choice", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_Goto(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; yyDo(yy, yyPush, 1, 0); + yyprintf((stderr, "%s\n", "Goto")); if (!yymatchString(yy, "GOTO")) goto l33; if (!yy_Spacing(yy)) goto l33; yyText(yy, yy->__begin, yy->__end); { +#define yytext yy->__text +#define yyleng yy->__textlen +if (!(YY_BEGIN)) goto l33; +#undef yytext +#undef yyleng + } if (!yy_Identifier(yy)) goto l33; yyDo(yy, yySet, -1, 0); yyText(yy, yy->__begin, yy->__end); { +#define yytext yy->__text +#define yyleng yy->__textlen +if (!(YY_END)) goto l33; +#undef yytext +#undef yyleng + } if (!yy_Newline(yy)) goto l33; yyDo(yy, yy_1_Goto, yy->__begin, yy->__end); + yyprintf((stderr, " ok %s @ %s\n", "Goto", yy->__buf+yy->__pos)); yyDo(yy, yyPop, 1, 0); + return 1; + l33:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "Goto", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_Ending(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; + yyprintf((stderr, "%s\n", "Ending")); if (!yymatchString(yy, "THE END")) goto l34; if (!yy_Newline(yy)) goto l34; + yyprintf((stderr, " ok %s @ %s\n", "Ending", yy->__buf+yy->__pos)); + return 1; + l34:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "Ending", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_TextLine(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; + yyprintf((stderr, "%s\n", "TextLine")); yyText(yy, yy->__begin, yy->__end); { +#define yytext yy->__text +#define yyleng yy->__textlen +if (!(YY_BEGIN)) goto l35; +#undef yytext +#undef yyleng + } + l36:; + { int yypos37= yy->__pos, yythunkpos37= yy->__thunkpos; + { int yypos38= yy->__pos, yythunkpos38= yy->__thunkpos; if (!yy_Newline(yy)) goto l38; goto l37; + l38:; yy->__pos= yypos38; yy->__thunkpos= yythunkpos38; + } if (!yymatchDot(yy)) goto l37; goto l36; + l37:; yy->__pos= yypos37; yy->__thunkpos= yythunkpos37; + } if (!yy_Newline(yy)) goto l35; yyText(yy, yy->__begin, yy->__end); { +#define yytext yy->__text +#define yyleng yy->__textlen +if (!(YY_END)) goto l35; +#undef yytext +#undef yyleng + } yyDo(yy, yy_1_TextLine, yy->__begin, yy->__end); + yyprintf((stderr, " ok %s @ %s\n", "TextLine", yy->__buf+yy->__pos)); + return 1; + l35:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "TextLine", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_Footer(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; yyDo(yy, yyPush, 1, 0); + yyprintf((stderr, "%s\n", "Footer")); + { int yypos40= yy->__pos, yythunkpos40= yy->__thunkpos; if (!yy_Ending(yy)) goto l41; yyDo(yy, yy_1_Footer, yy->__begin, yy->__end); goto l40; + l41:; yy->__pos= yypos40; yy->__thunkpos= yythunkpos40; if (!yy_Goto(yy)) goto l42; yyDo(yy, yySet, -1, 0); yyDo(yy, yy_2_Footer, yy->__begin, yy->__end); goto l40; + l42:; yy->__pos= yypos40; yy->__thunkpos= yythunkpos40; if (!yy_Choice(yy)) goto l39; + l43:; + { int yypos44= yy->__pos, yythunkpos44= yy->__thunkpos; if (!yy_Choice(yy)) goto l44; goto l43; + l44:; yy->__pos= yypos44; yy->__thunkpos= yythunkpos44; + } yyDo(yy, yy_3_Footer, yy->__begin, yy->__end); + } + l40:; + yyprintf((stderr, " ok %s @ %s\n", "Footer", yy->__buf+yy->__pos)); yyDo(yy, yyPop, 1, 0); + return 1; + l39:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "Footer", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_Newline(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; + yyprintf((stderr, "%s\n", "Newline")); + { int yypos46= yy->__pos, yythunkpos46= yy->__thunkpos; if (!yymatchString(yy, "\r\n")) goto l47; goto l46; + l47:; yy->__pos= yypos46; yy->__thunkpos= yythunkpos46; if (!yymatchChar(yy, '\r')) goto l48; goto l46; + l48:; yy->__pos= yypos46; yy->__thunkpos= yythunkpos46; if (!yymatchChar(yy, '\n')) goto l45; + } + l46:; + yyprintf((stderr, " ok %s @ %s\n", "Newline", yy->__buf+yy->__pos)); + return 1; + l45:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "Newline", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_Identifier(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; + yyprintf((stderr, "%s\n", "Identifier")); if (!yymatchClass(yy, (unsigned char *)"\000\000\000\000\000\000\000\000\376\377\377\007\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l49; if (!yymatchClass(yy, (unsigned char *)"\000\000\000\000\000\000\377\003\376\377\377\207\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l49; + l50:; + { int yypos51= yy->__pos, yythunkpos51= yy->__thunkpos; if (!yymatchClass(yy, (unsigned char *)"\000\000\000\000\000\000\377\003\376\377\377\207\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000")) goto l51; goto l50; + l51:; yy->__pos= yypos51; yy->__thunkpos= yythunkpos51; + } + yyprintf((stderr, " ok %s @ %s\n", "Identifier", yy->__buf+yy->__pos)); + return 1; + l49:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "Identifier", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_Body(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; yyDo(yy, yyPush, 2, 0); + yyprintf((stderr, "%s\n", "Body")); + { int yypos53= yy->__pos, yythunkpos53= yy->__thunkpos; if (!yy_Footer(yy)) goto l54; goto l53; + l54:; yy->__pos= yypos53; yy->__thunkpos= yythunkpos53; if (!yy_TextLine(yy)) goto l52; yyDo(yy, yySet, -2, 0); if (!yy_Body(yy)) goto l52; yyDo(yy, yySet, -1, 0); yyDo(yy, yy_1_Body, yy->__begin, yy->__end); + } + l53:; + yyprintf((stderr, " ok %s @ %s\n", "Body", yy->__buf+yy->__pos)); yyDo(yy, yyPop, 2, 0); + return 1; + l52:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "Body", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_Header(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; + yyprintf((stderr, "%s\n", "Header")); yyText(yy, yy->__begin, yy->__end); { +#define yytext yy->__text +#define yyleng yy->__textlen +if (!(YY_BEGIN)) goto l55; +#undef yytext +#undef yyleng + } if (!yy_Identifier(yy)) goto l55; yyText(yy, yy->__begin, yy->__end); { +#define yytext yy->__text +#define yyleng yy->__textlen +if (!(YY_END)) goto l55; +#undef yytext +#undef yyleng + } yyDo(yy, yy_1_Header, yy->__begin, yy->__end); if (!yy_Newline(yy)) goto l55; + l56:; + { int yypos57= yy->__pos, yythunkpos57= yy->__thunkpos; if (!yy_BlankLine(yy)) goto l57; goto l56; + l57:; yy->__pos= yypos57; yy->__thunkpos= yythunkpos57; + } + yyprintf((stderr, " ok %s @ %s\n", "Header", yy->__buf+yy->__pos)); + return 1; + l55:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "Header", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_EndOfFile(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; + yyprintf((stderr, "%s\n", "EndOfFile")); + { int yypos59= yy->__pos, yythunkpos59= yy->__thunkpos; if (!yymatchDot(yy)) goto l59; goto l58; + l59:; yy->__pos= yypos59; yy->__thunkpos= yythunkpos59; + } + yyprintf((stderr, " ok %s @ %s\n", "EndOfFile", yy->__buf+yy->__pos)); + return 1; + l58:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "EndOfFile", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_Page(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; yyDo(yy, yyPush, 2, 0); + yyprintf((stderr, "%s\n", "Page")); if (!yy_Header(yy)) goto l60; yyDo(yy, yySet, -2, 0); if (!yy_Body(yy)) goto l60; yyDo(yy, yySet, -1, 0); yyDo(yy, yy_1_Page, yy->__begin, yy->__end); + l61:; + { int yypos62= yy->__pos, yythunkpos62= yy->__thunkpos; if (!yy_BlankLine(yy)) goto l62; goto l61; + l62:; yy->__pos= yypos62; yy->__thunkpos= yythunkpos62; + } + yyprintf((stderr, " ok %s @ %s\n", "Page", yy->__buf+yy->__pos)); yyDo(yy, yyPop, 2, 0); + return 1; + l60:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "Page", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_BlankLine(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; + yyprintf((stderr, "%s\n", "BlankLine")); if (!yy_Spacing(yy)) goto l63; if (!yy_Newline(yy)) goto l63; + yyprintf((stderr, " ok %s @ %s\n", "BlankLine", yy->__buf+yy->__pos)); + return 1; + l63:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "BlankLine", yy->__buf+yy->__pos)); + return 0; +} +YY_RULE(int) yy_Story(yycontext *yy) +{ int yypos0= yy->__pos, yythunkpos0= yy->__thunkpos; yyDo(yy, yyPush, 1, 0); + yyprintf((stderr, "%s\n", "Story")); + l65:; + { int yypos66= yy->__pos, yythunkpos66= yy->__thunkpos; if (!yy_BlankLine(yy)) goto l66; goto l65; + l66:; yy->__pos= yypos66; yy->__thunkpos= yythunkpos66; + } if (!yy_Page(yy)) goto l64; yyDo(yy, yySet, -1, 0); yyDo(yy, yy_1_Story, yy->__begin, yy->__end); + l67:; + { int yypos68= yy->__pos, yythunkpos68= yy->__thunkpos; if (!yy_Page(yy)) goto l68; yyDo(yy, yySet, -1, 0); yyDo(yy, yy_1_Story, yy->__begin, yy->__end); goto l67; + l68:; yy->__pos= yypos68; yy->__thunkpos= yythunkpos68; + } if (!yy_EndOfFile(yy)) goto l64; + yyprintf((stderr, " ok %s @ %s\n", "Story", yy->__buf+yy->__pos)); yyDo(yy, yyPop, 1, 0); + return 1; + l64:; yy->__pos= yypos0; yy->__thunkpos= yythunkpos0; + yyprintf((stderr, " fail %s @ %s\n", "Story", yy->__buf+yy->__pos)); + return 0; +} + +#ifndef YY_PART + +typedef int (*yyrule)(yycontext *yy); + +YY_PARSE(int) YYPARSEFROM(YY_CTX_PARAM_ yyrule yystart) +{ + int yyok; + if (!yyctx->__buflen) + { + yyctx->__buflen= YY_BUFFER_SIZE; + yyctx->__buf= (char *)YY_MALLOC(yyctx, yyctx->__buflen); + yyctx->__textlen= YY_BUFFER_SIZE; + yyctx->__text= (char *)YY_MALLOC(yyctx, yyctx->__textlen); + yyctx->__thunkslen= YY_STACK_SIZE; + yyctx->__thunks= (yythunk *)YY_MALLOC(yyctx, sizeof(yythunk) * yyctx->__thunkslen); + yyctx->__valslen= YY_STACK_SIZE; + yyctx->__vals= (YYSTYPE *)YY_MALLOC(yyctx, sizeof(YYSTYPE) * yyctx->__valslen); + yyctx->__begin= yyctx->__end= yyctx->__pos= yyctx->__limit= yyctx->__thunkpos= 0; + } + yyctx->__begin= yyctx->__end= yyctx->__pos; + yyctx->__thunkpos= 0; + yyctx->__val= yyctx->__vals; + yyok= yystart(yyctx); + if (yyok) yyDone(yyctx); + yyCommit(yyctx); + return yyok; +} + +YY_PARSE(int) YYPARSE(YY_CTX_PARAM) +{ + return YYPARSEFROM(YY_CTX_ARG_ yy_Story); +} + +YY_PARSE(yycontext *) YYRELEASE(yycontext *yyctx) +{ + if (yyctx->__buflen) + { + yyctx->__buflen= 0; + YY_FREE(yyctx, yyctx->__buf); + YY_FREE(yyctx, yyctx->__text); + YY_FREE(yyctx, yyctx->__thunks); + YY_FREE(yyctx, yyctx->__vals); + } + return yyctx; +} + +#endif +#line 138 "cyoa.leg" + + +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: */ + diff --git a/cyoa.leg b/cyoa.leg new file mode 100644 index 0000000..e01f793 --- /dev/null +++ b/cyoa.leg @@ -0,0 +1,250 @@ +%{ + +#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(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: */ diff --git a/demo.story b/demo.story new file mode 100644 index 0000000..d0ada21 --- /dev/null +++ b/demo.story @@ -0,0 +1,61 @@ +START + +You are a young boy at a carnival. The Ferris wheel is in front of you and looks tempting. You can also see the dark ride. + +1) Ride the Ferris wheel [WHEEL] +2) Ride the Dark ride [RIDE] + +RIDE + +You ride the dark ride. It's not really that interesting and by the time it's over you have to go home. + +THE END + +WHEEL + +You get to the front of the line, and you are placed in a gondola with your school crush. The two of you have +been playmates for some time, but you think that you are old enough to be more. + +1) Lean in and kiss them at the top [KISS] (+1 Romance) +2) Let the moment pass [NOKISS] (+1 Willpower) + +KISS + +You lean in and give them a quick kiss on the cheek. It's very romantic in the moonlight. + +GOTO AFTERWHEEL + +NOKISS + +You let the moment pass, and instead look out over the hills towards the moon. You feel like +anything is possible in your life. + +GOTO AFTERWHEEL + +AFTERWHEEL + +After you get off the Ferris Wheel, it is getting a bit late. You still want to go on the dark ride, maybe with +your school crush. You can also go ride the teacups. Or you could just go home. + +1) Go with your crush to the dark ride [RIDE_TOGETHER] +2) Ride the teacups [TEACUPS] +3) Go home [HOME] + +RIDE_TOGETHER + +You ride into the dark tunnel and you can feel your friend squeeze your hand. A warm feeling covers your body. + +THE END + +TEACUPS + +The swirling vortex envelopes you, but you are undeterred. You leave the park feeling like you are ready to take +on the world. + +THE END + +HOME + +You return home to your bed. A soft pillow greets you and you dream of the night's festivities. + +THE END diff --git a/simple.story b/simple.story new file mode 100644 index 0000000..f236047 --- /dev/null +++ b/simple.story @@ -0,0 +1,13 @@ +START + +One day Dane took a really big shit. +Dane: "Wow, that was a really big shit" + +1) Don't die [NEXT] + +NEXT + +Then he died. +Fuck you Dane + +THE END diff --git a/spec.txt b/spec.txt new file mode 100644 index 0000000..8ee06e4 --- /dev/null +++ b/spec.txt @@ -0,0 +1,57 @@ +################################### +# Untitled Storybook Game Project # +################################### + +This will be a project to create a system whereby "Choose Your Own Adventure" (CYOA) style stories can be easily written. + +Each page will read out story elements, and then prompt the user to select a new page based on a decision +they would like to make. + +Making certain choices will also award ad-hoc stat bonuses i.e. +1 Knowledge or something, all +stats are presumed to be 0 until points are awarded. This stat system also includes the ability +to make checks when certain choices become available, or once they are selected. + +Requirements: + 1. A language description and recognizer for creating CYOA-style stories. + 2. An interpreter and interface for interacting with these stories. + 3. Both story-writing and story-interaction tools need to be cross platform and relatively straightforwards for + a non technical user to operate. + +Story language PEG: (with apologies, this is my first PEG) + +Story <- BlankLine* Page+ EndOfFile +Page <- Header Body BlankLine* +Header <- Identifier Newline BlankLine* +Identifier <- [A-Z][A-Z0-9_]* +Body <- Footer / TextLine Body +TextLine <- (!Newline .)+ Newline +Footer <- Ending / Goto / Choice+ +Goto <- 'GOTO' Spacing Identifier Newline +Ending <- 'THE END' Newline +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' + +Work log: + +12/13/21 +Right now I need to make a decision before any of the work has started on how the user would interact with the system. +I think for a MVP it would be okay to just write a console application, but I think for the long term there needs to +be a decision made on how this will work. I'm leaning towards one of three options. + +1) We put the whole thing on the JVM and make it a library or something. +Maybe in the future we can make a frontend for making and playing stories +as a web interface, but we could make the interface pluggable as far as the backend is concerned. +2) I build this non-traditional game in a traditional game engine (Godot probably). This ensures cross-platform +availibility. I'm not 100% sure about my ability to use parsers in Godot, but this looks like a good option. +3) I build this as a reactive web page. I know this sounds like a terrible idea, but there are actually a lot +of cool image and text transition libraries out there. It does mean setting up something like React tho, which +like, just kill me now + +Out of the three of these I think the Java thing would be the easiest and I think the Godot thing would be the best.