/* You need Bison (or maybe Yacc) to rebuild the .c file from this grammar. * Or you could rewrite it to use Lemon, since the ZDoom source comes with Lemon. * Since I have Bison, and the existing code works fine, there's no motivation * for me to rewrite it. */ %{ #include "xlat.h" #include #include // verbose doesn't seem to help all that much //#define YYERROR_VERBOSE 1 int yyerror (char *s); int yylex (void); typedef struct _Symbol { struct _Symbol *Next; int Value; char Sym[1]; } Symbol; static bool FindToken (char *tok, int *type); static void AddSym (char *sym, int val); static bool FindSym (char *sym, Symbol **val); static int EnumVal; struct ListFilter { WORD filter; BYTE value; }; typedef struct _morefilters { struct _morefilters *next; struct ListFilter filter; } MoreFilters; typedef struct _morelines { struct _morelines *next; BoomArg arg; } MoreLines; %} %union { int val; char sym[80]; char string[80]; struct { BYTE addflags; BYTE args[5]; } args; struct ListFilter filter; MoreFilters *filter_p; struct { BYTE constant; WORD mask; MoreFilters *filters; } boomarg; Symbol *symval; BoomArg boomline; MoreLines *boomlines; } %token NUM %token SYMNUM %token SYM %token STRING %type exp %type special_args %type list_val %type arg_list %type boom_args %type boom_op %type boom_selector %type boom_line %type boom_body %type maybe_argcount %token DEFINE %token INCLUDE %token TAG %token LINEID %token SPECIAL %token FLAGS %token ARG2 %token ARG3 %token ARG4 %token ARG5 %token OR_EQUAL %token ENUM %token PRINT %token ENDL %left '|' %left '^' %left '&' %left '-' '+' %left '*' '/' %left NEG %start translation_unit %% exp: NUM | SYMNUM { $$ = $1->Value; } | exp '+' exp { $$ = $1 + $3; } | exp '-' exp { $$ = $1 - $3; } | exp '*' exp { $$ = $1 * $3; } | exp '/' exp { $$ = $1 / $3; } | exp '|' exp { $$ = $1 | $3; } | exp '&' exp { $$ = $1 & $3; } | exp '^' exp { $$ = $1 ^ $3; } | '-' exp %prec NEG { $$ = -$2 } | '(' exp ')' { $$ = $2; } ; translation_unit: /* empty line */ | translation_unit external_declaration ; external_declaration: define_statement | include_statement | print_statement | enum_statement | linetype_declaration | boom_declaration | special_declaration ; print_statement: PRINT '(' print_list ')' { printf ("\n"); } ; print_list: /* EMPTY */ | print_item | print_item ',' print_list ; print_item: STRING { printf ("%s", $1); } | exp { printf ("%d", $1); } | ENDL { printf ("\n"); } ; define_statement: DEFINE SYM '(' exp ')' { AddSym ($2, $4); } ; include_statement: INCLUDE STRING { IncludeFile ($2); } ; enum_statement: ENUM '{' { EnumVal = 0; } enum_list '}' ; enum_list: /* empty */ | single_enum | single_enum ',' enum_list ; single_enum: SYM { AddSym ($1, EnumVal++); } | SYM '=' exp { AddSym ($1, EnumVal = $3); } ; /* special declarations work just like they do for ACS, so * specials can be defined just by including zspecial.acs */ special_declaration: SPECIAL special_list ';' ; special_list: /* empty */ | special_def | special_def ',' special_list ; special_def: exp ':' SYM '(' maybe_argcount ')' { AddSym ($3, $1); } | exp ':' SYMNUM { printf ("%s, line %d: %s is already defined\n", SourceName, SourceLine, $3->Sym); } '(' maybe_argcount ')' ; maybe_argcount: /* empty */ { $$ = 0; } | exp | exp ',' exp ; linetype_declaration: exp '=' exp ',' exp '(' special_args ')' { Simple[$1].NewSpecial = $5; Simple[$1].Flags = $3 | $7.addflags; Simple[$1].Args[0] = $7.args[0]; Simple[$1].Args[1] = $7.args[1]; Simple[$1].Args[2] = $7.args[2]; Simple[$1].Args[3] = $7.args[3]; Simple[$1].Args[4] = $7.args[4]; } | exp '=' exp ',' SYM '(' special_args ')' { printf ("%s, line %d: %s is undefined\n", SourceName, SourceLine, $5); } ; boom_declaration: '[' exp ']' '(' exp ',' exp ')' '{' boom_body '}' { if (NumBoomish == MAX_BOOMISH) { MoreLines *probe = $10; while (probe != NULL) { MoreLines *next = probe->next; free (probe); probe = next; } printf ("%s, line %d: Too many BOOM translators\n", SourceName, SourceLine); } else { int i; MoreLines *probe; Boomish[NumBoomish].FirstLinetype = $5; Boomish[NumBoomish].LastLinetype = $7; Boomish[NumBoomish].NewSpecial = $2; for (i = 0, probe = $10; probe != NULL; i++) { MoreLines *next = probe->next;; if (i < MAX_BOOMISH_EXEC) { Boomish[NumBoomish].Args[i] = probe->arg; } else if (i == MAX_BOOMISH_EXEC) { printf ("%s, line %d: Too many commands for this BOOM translator\n", SourceName, SourceLine); } free (probe); probe = next; } if (i < MAX_BOOMISH_EXEC) { Boomish[NumBoomish].Args[i].bDefined = 0; } NumBoomish++; } } ; boom_body: /* empty */ { $$ = NULL; } | boom_line boom_body { $$ = malloc (sizeof(MoreLines)); $$->next = $2; $$->arg = $1; } ; boom_line: boom_selector boom_op boom_args { $$.bDefined = 1; $$.bOrExisting = ($2 == OR_EQUAL); $$.bUseConstant = ($3.filters == NULL); $$.ArgNum = $1; $$.ConstantValue = $3.constant; $$.AndValue = $3.mask; if ($3.filters != NULL) { int i; MoreFilters *probe; for (i = 0, probe = $3.filters; probe != NULL; i++) { MoreFilters *next = probe->next; if (i < 15) { $$.ResultFilter[i] = probe->filter.filter; $$.ResultValue[i] = probe->filter.value; } else if (i == 15) { yyerror ("Lists can only have 15 elements"); } free (probe); probe = next; } $$.ListSize = i > 15 ? 15 : i; } } ; boom_selector: FLAGS { $$ = 4; } | ARG2 { $$ = 0; } | ARG3 { $$ = 1; } | ARG4 { $$ = 2; } | ARG5 { $$ = 3; } ; boom_op: '=' { $$ = '='; } | OR_EQUAL { $$ = OR_EQUAL; } ; boom_args: exp { $$.constant = $1; $$.filters = NULL; } | exp '[' arg_list ']' { $$.mask = $1; $$.filters = $3; } ; arg_list: list_val { $$ = malloc (sizeof (MoreFilters)); $$->next = NULL; $$->filter = $1; } | list_val ',' arg_list { $$ = malloc (sizeof (MoreFilters)); $$->next = $3; $$->filter = $1; } ; list_val: exp ':' exp { $$.filter = $1; $$.value = $3; } ; special_args: /* EMPTY LINE */ { $$.addflags = 0; memset ($$.args, 0, 5); } | TAG { $$.addflags = SIMPLE_HASTAGAT1; memset ($$.args, 0, 5); } | TAG ',' exp { $$.addflags = SIMPLE_HASTAGAT1; $$.args[0] = 0; $$.args[1] = $3; $$.args[2] = 0; $$.args[3] = 0; $$.args[4] = 0; } | TAG ',' exp ',' exp { $$.addflags = SIMPLE_HASTAGAT1; $$.args[0] = 0; $$.args[1] = $3; $$.args[2] = $5; $$.args[3] = 0; $$.args[4] = 0; } | TAG ',' exp ',' exp ',' exp { $$.addflags = SIMPLE_HASTAGAT1; $$.args[0] = 0; $$.args[1] = $3; $$.args[2] = $5; $$.args[3] = $7; $$.args[4] = 0; } | TAG ',' exp ',' exp ',' exp ',' exp { $$.addflags = SIMPLE_HASTAGAT1; $$.args[0] = 0; $$.args[1] = $3; $$.args[2] = $5; $$.args[3] = $7; $$.args[4] = $9; } | TAG ',' TAG { $$.addflags = SIMPLE_HAS2TAGS; $$.args[0] = $$.args[1] = 0; $$.args[2] = 0; $$.args[3] = 0; $$.args[4] = 0; } | TAG ',' TAG ',' exp { $$.addflags = SIMPLE_HAS2TAGS; $$.args[0] = $$.args[1] = 0; $$.args[2] = $5; $$.args[3] = 0; $$.args[4] = 0; } | TAG ',' TAG ',' exp ',' exp { $$.addflags = SIMPLE_HAS2TAGS; $$.args[0] = $$.args[1] = 0; $$.args[2] = $5; $$.args[3] = $7; $$.args[4] = 0; } | TAG ',' TAG ',' exp ',' exp ',' exp { $$.addflags = SIMPLE_HAS2TAGS; $$.args[0] = $$.args[1] = 0; $$.args[2] = $5; $$.args[3] = $7; $$.args[4] = $9; } | LINEID { $$.addflags = SIMPLE_HASLINEID; memset ($$.args, 0, 5); } | LINEID ',' exp { $$.addflags = SIMPLE_HASLINEID; $$.args[0] = 0; $$.args[1] = $3; $$.args[2] = 0; $$.args[3] = 0; $$.args[4] = 0; } | LINEID ',' exp ',' exp { $$.addflags = SIMPLE_HASLINEID; $$.args[0] = 0; $$.args[1] = $3; $$.args[2] = $5; $$.args[3] = 0; $$.args[4] = 0; } | LINEID ',' exp ',' exp ',' exp { $$.addflags = SIMPLE_HASLINEID; $$.args[0] = 0; $$.args[1] = $3; $$.args[2] = $5; $$.args[3] = $7; $$.args[4] = 0; } | LINEID ',' exp ',' exp ',' exp ',' exp { $$.addflags = SIMPLE_HASLINEID; $$.args[0] = 0; $$.args[1] = $3; $$.args[2] = $5; $$.args[3] = $7; $$.args[4] = $9; } | exp { $$.addflags = 0; $$.args[0] = $1; $$.args[1] = 0; $$.args[2] = 0; $$.args[3] = 0; $$.args[4] = 0; } | exp ',' exp { $$.addflags = 0; $$.args[0] = $1; $$.args[1] = $3; $$.args[2] = 0; $$.args[3] = 0; $$.args[4] = 0; } | exp ',' exp ',' exp { $$.addflags = 0; $$.args[0] = $1; $$.args[1] = $3; $$.args[2] = $5; $$.args[3] = 0; $$.args[4] = 0; } | exp ',' exp ',' exp ',' exp { $$.addflags = 0; $$.args[0] = $1; $$.args[1] = $3; $$.args[2] = $5; $$.args[3] = $7; $$.args[4] = 0; } | exp ',' exp ',' exp ',' exp ',' exp { $$.addflags = 0; $$.args[0] = $1; $$.args[1] = $3; $$.args[2] = $5; $$.args[3] = $7; $$.args[4] = $9; } | exp ',' TAG { $$.addflags = SIMPLE_HASTAGAT2; $$.args[0] = $1; $$.args[1] = 0; $$.args[2] = 0; $$.args[3] = 0; $$.args[4] = 0; } | exp ',' TAG ',' exp { $$.addflags = SIMPLE_HASTAGAT2; $$.args[0] = $1; $$.args[1] = 0; $$.args[2] = $5; $$.args[3] = 0; $$.args[4] = 0; } | exp ',' TAG ',' exp ',' exp { $$.addflags = SIMPLE_HASTAGAT2; $$.args[0] = $1; $$.args[1] = 0; $$.args[2] = $5; $$.args[3] = $7; $$.args[4] = 0; } | exp ',' TAG ',' exp ',' exp ',' exp { $$.addflags = SIMPLE_HASTAGAT2; $$.args[0] = $1; $$.args[1] = 0; $$.args[2] = $5; $$.args[3] = $7; $$.args[4] = $9; } | exp ',' exp ',' TAG { $$.addflags = SIMPLE_HASTAGAT3; $$.args[0] = $1; $$.args[1] = $3; $$.args[2] = 0; $$.args[3] = 0; $$.args[4] = 0; } | exp ',' exp ',' TAG ',' exp { $$.addflags = SIMPLE_HASTAGAT3; $$.args[0] = $1; $$.args[1] = $3; $$.args[2] = 0; $$.args[3] = $7; $$.args[4] = 0; } | exp ',' exp ',' TAG ',' exp ',' exp { $$.addflags = SIMPLE_HASTAGAT3; $$.args[0] = $1; $$.args[1] = $3; $$.args[2] = 0; $$.args[3] = $7; $$.args[4] = $9; } | exp ',' exp ',' exp ',' TAG { $$.addflags = SIMPLE_HASTAGAT4; $$.args[0] = $1; $$.args[1] = $3; $$.args[2] = $5; $$.args[3] = 0; $$.args[4] = 0; } | exp ',' exp ',' exp ',' TAG ',' exp { $$.addflags = SIMPLE_HASTAGAT4; $$.args[0] = $1; $$.args[1] = $3; $$.args[2] = $5; $$.args[3] = 0; $$.args[4] = $9; } | exp ',' exp ',' exp ',' exp ',' TAG { $$.addflags = SIMPLE_HASTAGAT5; $$.args[0] = $1; $$.args[1] = $3; $$.args[2] = $5; $$.args[3] = $7; $$.args[4] = 0; } ; %% #include #include int yylex (void) { char token[80]; int toksize; int c; loop: while (Source == NULL) { if (!EndFile ()) return 0; } while (isspace (c = fgetc (Source)) && c != EOF) { if (c == '\n') SourceLine++; } if (c == EOF) { if (EndFile ()) goto loop; return 0; } if (isdigit (c)) { int buildup = c - '0'; if (c == '0') { c = fgetc (Source); if (c == 'x' || c == 'X') { for (;;) { c = fgetc (Source); if (isdigit (c)) { buildup = (buildup<<4) + c - '0'; } else if (c >= 'a' && c <= 'f') { buildup = (buildup<<4) + c - 'a' + 10; } else if (c >= 'A' && c <= 'F') { buildup = (buildup<<4) + c - 'A' + 10; } else { ungetc (c, Source); yylval.val = buildup; return NUM; } } } else { ungetc (c, Source); } } while (isdigit (c = fgetc (Source))) { buildup = buildup*10 + c - '0'; } ungetc (c, Source); yylval.val = buildup; return NUM; } if (isalpha (c)) { int buildup = 0; token[0] = c; toksize = 1; while (toksize < 79 && (isalnum (c = fgetc (Source)) || c == '_')) { token[toksize++] = c; } token[toksize] = 0; if (toksize == 79 && isalnum (c)) { while (isalnum (c = fgetc (Source))) ; } ungetc (c, Source); if (FindToken (token, &buildup)) { return buildup; } if (FindSym (token, &yylval.symval)) { return SYMNUM; } strcpy (yylval.sym, token); return SYM; } if (c == '/') { c = fgetc (Source); if (c == '*') { for (;;) { while ((c = fgetc (Source)) != '*' && c != EOF) { if (c == '\n') SourceLine++; } if (c == EOF) return 0; if ((c = fgetc (Source)) == '/') goto loop; if (c == EOF) return 0; ungetc (c, Source); } } else if (c == '/') { while ((c = fgetc (Source)) != '\n' && c != EOF) ; if (c == '\n') SourceLine++; else if (c == EOF) return 0; goto loop; } else { ungetc (c, Source); return '/'; } } if (c == '"') { int tokensize = 0; while ((c = fgetc (Source)) != '"' && c != EOF) { yylval.string[tokensize++] = c; } yylval.string[tokensize] = 0; return STRING; } if (c == '|') { c = fgetc (Source); if (c == '=') return OR_EQUAL; ungetc (c, Source); return '|'; } return c; } static Symbol *FirstSym; static void AddSym (char *sym, int val) { Symbol *syme = malloc (strlen (sym) + sizeof(Symbol)); syme->Next = FirstSym; syme->Value = val; strcpy (syme->Sym, sym); FirstSym = syme; } static bool FindSym (char *sym, Symbol **val) { Symbol *syme = FirstSym; while (syme != NULL) { if (strcmp (syme->Sym, sym) == 0) { *val = syme; return 1; } syme = syme->Next; } return 0; } static bool FindToken (char *tok, int *type) { static const char tokens[][8] = { "endl", "print", "include", "special", "define", "enum", "arg5", "arg4", "arg3", "arg2", "flags", "lineid", "tag" }; static const short types[] = { ENDL, PRINT, INCLUDE, SPECIAL, DEFINE, ENUM, ARG5, ARG4, ARG3, ARG2, FLAGS, LINEID, TAG }; int i; for (i = sizeof(tokens)/sizeof(tokens[0])-1; i >= 0; i--) { if (strcmp (tok, tokens[i]) == 0) { *type = types[i]; return 1; } } return 0; } int yyerror (char *s) { if (SourceName != NULL) printf ("%s, line %d: %s\n", SourceName, SourceLine, s); else printf ("%s\n", s); return 0; }