OSDN Git Service

shrink mine
[nethackexpress/trunk.git] / util / dgn_comp.y
1 %{
2 /*      SCCS Id: @(#)dgn_comp.c 3.4     1996/06/22      */
3 /*      Copyright (c) 1989 by Jean-Christophe Collet */
4 /*      Copyright (c) 1990 by M. Stephenson                               */
5 /* NetHack may be freely redistributed.  See license for details. */
6
7 /*
8  * This file contains the Dungeon Compiler code
9  */
10
11 /* In case we're using bison in AIX.  This definition must be
12  * placed before any other C-language construct in the file
13  * excluding comments and preprocessor directives (thanks IBM
14  * for this wonderful feature...).
15  *
16  * Note: some cpps barf on this 'undefined control' (#pragma).
17  * Addition of the leading space seems to prevent barfage for now,
18  * and AIX will still see the directive in its non-standard locale.
19  */
20
21 #ifdef _AIX
22  #pragma alloca         /* keep leading space! */
23 #endif
24
25 #include "config.h"
26 #include "date.h"
27 #include "dgn_file.h"
28
29 void FDECL(yyerror, (const char *));
30 void FDECL(yywarning, (const char *));
31 int NDECL(yylex);
32 int NDECL(yyparse);
33 int FDECL(getchain, (char *));
34 int NDECL(check_dungeon);
35 int NDECL(check_branch);
36 int NDECL(check_level);
37 void NDECL(init_dungeon);
38 void NDECL(init_branch);
39 void NDECL(init_level);
40 void NDECL(output_dgn);
41
42 #define Free(ptr)               free((genericptr_t)ptr)
43
44 #ifdef AMIGA
45 # undef printf
46 #ifndef LATTICE
47 # define    memset(addr,val,len)    setmem(addr,len,val)
48 #endif
49 #endif
50
51 #define ERR             (-1)
52
53 static struct couple couple;
54 static struct tmpdungeon tmpdungeon[MAXDUNGEON];
55 static struct tmplevel tmplevel[LEV_LIMIT];
56 static struct tmpbranch tmpbranch[BRANCH_LIMIT];
57
58 static int in_dungeon = 0, n_dgns = -1, n_levs = -1, n_brs = -1;
59
60 extern int fatal_error;
61 extern const char *fname;
62 extern FILE *yyin, *yyout;      /* from dgn_lex.c */
63
64 %}
65
66 %union
67 {
68         int     i;
69         char*   str;
70 }
71
72 %token  <i>     INTEGER
73 %token  <i>     A_DUNGEON BRANCH CHBRANCH LEVEL RNDLEVEL CHLEVEL RNDCHLEVEL
74 %token  <i>     UP_OR_DOWN PROTOFILE DESCRIPTION DESCRIPTOR LEVELDESC
75 %token  <i>     ALIGNMENT LEVALIGN ENTRY STAIR NO_UP NO_DOWN PORTAL
76 %token  <str>   STRING
77 %type   <i>     optional_int direction branch_type bones_tag
78 %start  file
79
80 %%
81 file            : /* nothing */
82                 | dungeons
83                   {
84                         output_dgn();
85                   }
86                 ;
87
88 dungeons        : dungeon
89                 | dungeons dungeon
90                 ;
91
92 dungeon         : dungeonline
93                 | dungeondesc
94                 | branches
95                 | levels
96                 ;
97
98 dungeonline     : A_DUNGEON ':' STRING bones_tag rcouple optional_int
99                   {
100                         init_dungeon();
101                         Strcpy(tmpdungeon[n_dgns].name, $3);
102                         tmpdungeon[n_dgns].boneschar = (char)$4;
103                         tmpdungeon[n_dgns].lev.base = couple.base;
104                         tmpdungeon[n_dgns].lev.rand = couple.rand;
105                         tmpdungeon[n_dgns].chance = $6;
106                         Free($3);
107                   }
108                 ;
109
110 optional_int    : /* nothing */
111                   {
112                         $$ = 0;
113                   }
114                 | INTEGER
115                   {
116                         $$ = $1;
117                   }
118                 ;
119
120 dungeondesc     : entry
121                 | descriptions
122                 | prototype
123                 ;
124
125 entry           : ENTRY ':' INTEGER
126                   {
127                         tmpdungeon[n_dgns].entry_lev = $3;
128                   }
129                 ;
130
131 descriptions    : desc
132                 ;
133
134 desc            : DESCRIPTION ':' DESCRIPTOR
135                   {
136                         if($<i>3 <= TOWN || $<i>3 >= D_ALIGN_CHAOTIC)
137                             yyerror("Illegal description - ignoring!");
138                         else
139                             tmpdungeon[n_dgns].flags |= $<i>3 ;
140                   }
141                 | ALIGNMENT ':' DESCRIPTOR
142                   {
143                         if($<i>3 && $<i>3 < D_ALIGN_CHAOTIC)
144                             yyerror("Illegal alignment - ignoring!");
145                         else
146                             tmpdungeon[n_dgns].flags |= $<i>3 ;
147                   }
148                 ;
149
150 prototype       : PROTOFILE ':' STRING
151                   {
152                         Strcpy(tmpdungeon[n_dgns].protoname, $3);
153                         Free($3);
154                   }
155                 ;
156
157 levels          : level1
158                 | level2
159                 | levdesc
160                 | chlevel1
161                 | chlevel2
162                 ;
163
164 level1          : LEVEL ':' STRING bones_tag '@' acouple
165                   {
166                         init_level();
167                         Strcpy(tmplevel[n_levs].name, $3);
168                         tmplevel[n_levs].boneschar = (char)$4;
169                         tmplevel[n_levs].lev.base = couple.base;
170                         tmplevel[n_levs].lev.rand = couple.rand;
171                         tmpdungeon[n_dgns].levels++;
172                         Free($3);
173                   }
174                 | RNDLEVEL ':' STRING bones_tag '@' acouple INTEGER
175                   {
176                         init_level();
177                         Strcpy(tmplevel[n_levs].name, $3);
178                         tmplevel[n_levs].boneschar = (char)$4;
179                         tmplevel[n_levs].lev.base = couple.base;
180                         tmplevel[n_levs].lev.rand = couple.rand;
181                         tmplevel[n_levs].rndlevs = $7;
182                         tmpdungeon[n_dgns].levels++;
183                         Free($3);
184                   }
185                 ;
186
187 level2          : LEVEL ':' STRING bones_tag '@' acouple INTEGER
188                   {
189                         init_level();
190                         Strcpy(tmplevel[n_levs].name, $3);
191                         tmplevel[n_levs].boneschar = (char)$4;
192                         tmplevel[n_levs].lev.base = couple.base;
193                         tmplevel[n_levs].lev.rand = couple.rand;
194                         tmplevel[n_levs].chance = $7;
195                         tmpdungeon[n_dgns].levels++;
196                         Free($3);
197                   }
198                 | RNDLEVEL ':' STRING bones_tag '@' acouple INTEGER INTEGER
199                   {
200                         init_level();
201                         Strcpy(tmplevel[n_levs].name, $3);
202                         tmplevel[n_levs].boneschar = (char)$4;
203                         tmplevel[n_levs].lev.base = couple.base;
204                         tmplevel[n_levs].lev.rand = couple.rand;
205                         tmplevel[n_levs].chance = $7;
206                         tmplevel[n_levs].rndlevs = $8;
207                         tmpdungeon[n_dgns].levels++;
208                         Free($3);
209                   }
210                 ;
211
212 levdesc         : LEVELDESC ':' DESCRIPTOR
213                   {
214                         if($<i>3 >= D_ALIGN_CHAOTIC)
215                             yyerror("Illegal description - ignoring!");
216                         else
217                             tmplevel[n_levs].flags |= $<i>3 ;
218                   }
219                 | LEVALIGN ':' DESCRIPTOR
220                   {
221                         if($<i>3 && $<i>3 < D_ALIGN_CHAOTIC)
222                             yyerror("Illegal alignment - ignoring!");
223                         else
224                             tmplevel[n_levs].flags |= $<i>3 ;
225                   }
226                 ;
227
228 chlevel1        : CHLEVEL ':' STRING bones_tag STRING '+' rcouple
229                   {
230                         init_level();
231                         Strcpy(tmplevel[n_levs].name, $3);
232                         tmplevel[n_levs].boneschar = (char)$4;
233                         tmplevel[n_levs].chain = getchain($5);
234                         tmplevel[n_levs].lev.base = couple.base;
235                         tmplevel[n_levs].lev.rand = couple.rand;
236                         if(!check_level()) n_levs--;
237                         else tmpdungeon[n_dgns].levels++;
238                         Free($3);
239                         Free($5);
240                   }
241                 | RNDCHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER
242                   {
243                         init_level();
244                         Strcpy(tmplevel[n_levs].name, $3);
245                         tmplevel[n_levs].boneschar = (char)$4;
246                         tmplevel[n_levs].chain = getchain($5);
247                         tmplevel[n_levs].lev.base = couple.base;
248                         tmplevel[n_levs].lev.rand = couple.rand;
249                         tmplevel[n_levs].rndlevs = $8;
250                         if(!check_level()) n_levs--;
251                         else tmpdungeon[n_dgns].levels++;
252                         Free($3);
253                         Free($5);
254                   }
255                 ;
256
257 chlevel2        : CHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER
258                   {
259                         init_level();
260                         Strcpy(tmplevel[n_levs].name, $3);
261                         tmplevel[n_levs].boneschar = (char)$4;
262                         tmplevel[n_levs].chain = getchain($5);
263                         tmplevel[n_levs].lev.base = couple.base;
264                         tmplevel[n_levs].lev.rand = couple.rand;
265                         tmplevel[n_levs].chance = $8;
266                         if(!check_level()) n_levs--;
267                         else tmpdungeon[n_dgns].levels++;
268                         Free($3);
269                         Free($5);
270                   }
271                 | RNDCHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER INTEGER
272                   {
273                         init_level();
274                         Strcpy(tmplevel[n_levs].name, $3);
275                         tmplevel[n_levs].boneschar = (char)$4;
276                         tmplevel[n_levs].chain = getchain($5);
277                         tmplevel[n_levs].lev.base = couple.base;
278                         tmplevel[n_levs].lev.rand = couple.rand;
279                         tmplevel[n_levs].chance = $8;
280                         tmplevel[n_levs].rndlevs = $9;
281                         if(!check_level()) n_levs--;
282                         else tmpdungeon[n_dgns].levels++;
283                         Free($3);
284                         Free($5);
285                   }
286                 ;
287
288 branches        : branch
289                 | chbranch
290                 ;
291
292 branch          : BRANCH ':' STRING '@' acouple branch_type direction
293                   {
294                         init_branch();
295                         Strcpy(tmpbranch[n_brs].name, $3);
296                         tmpbranch[n_brs].lev.base = couple.base;
297                         tmpbranch[n_brs].lev.rand = couple.rand;
298                         tmpbranch[n_brs].type = $6;
299                         tmpbranch[n_brs].up = $7;
300                         if(!check_branch()) n_brs--;
301                         else tmpdungeon[n_dgns].branches++;
302                         Free($3);
303                   }
304                 ;
305
306 chbranch        : CHBRANCH ':' STRING STRING '+' rcouple branch_type direction
307                   {
308                         init_branch();
309                         Strcpy(tmpbranch[n_brs].name, $3);
310                         tmpbranch[n_brs].chain = getchain($4);
311                         tmpbranch[n_brs].lev.base = couple.base;
312                         tmpbranch[n_brs].lev.rand = couple.rand;
313                         tmpbranch[n_brs].type = $7;
314                         tmpbranch[n_brs].up = $8;
315                         if(!check_branch()) n_brs--;
316                         else tmpdungeon[n_dgns].branches++;
317                         Free($3);
318                         Free($4);
319                   }
320                 ;
321
322 branch_type     : /* nothing */
323                   {
324                         $$ = TBR_STAIR; /* two way stair */
325                   }
326                 | STAIR
327                   {
328                         $$ = TBR_STAIR; /* two way stair */
329                   }
330                 | NO_UP
331                   {
332                         $$ = TBR_NO_UP; /* no up staircase */
333                   }
334                 | NO_DOWN
335                   {
336                         $$ = TBR_NO_DOWN;       /* no down staircase */
337                   }
338                 | PORTAL
339                   {
340                         $$ = TBR_PORTAL;        /* portal connection */
341                   }
342                 ;
343
344 direction       : /* nothing */
345                   {
346                         $$ = 0; /* defaults to down */
347                   }
348                 | UP_OR_DOWN
349                   {
350                         $$ = $1;
351                   }
352                 ;
353
354 bones_tag       : STRING
355                   {
356                         char *p = $1;
357                         if (strlen(p) != 1) {
358                             if (strcmp(p, "none") != 0)
359                    yyerror("Bones marker must be a single char, or \"none\"!");
360                             *p = '\0';
361                         }
362                         $$ = *p;
363                         Free(p);
364                   }
365                 ;
366
367 /*
368  *      acouple rules:
369  *
370  *      (base, range) where:
371  *
372  *          base is either a positive or negative integer with a value
373  *          less than or equal to MAXLEVEL.
374  *          base > 0 indicates the base level.
375  *          base < 0 indicates reverse index (-1 == lowest level)
376  *
377  *          range is the random component.
378  *          if range is zero, there is no random component.
379  *          if range is -1 the dungeon loader will randomize between
380  *          the base and the end of the dungeon.
381  *          during dungeon load, range is always *added* to the base,
382  *          therefore range + base(converted) must not exceed MAXLEVEL.
383  */
384 acouple         : '(' INTEGER ',' INTEGER ')'
385                   {
386                         if ($2 < -MAXLEVEL || $2 > MAXLEVEL) {
387                             yyerror("Abs base out of dlevel range - zeroing!");
388                             couple.base = couple.rand = 0;
389                         } else if ($4 < -1 ||
390                                 (($2 < 0) ? (MAXLEVEL + $2 + $4 + 1) > MAXLEVEL :
391                                         ($2 + $4) > MAXLEVEL)) {
392                             yyerror("Abs range out of dlevel range - zeroing!");
393                             couple.base = couple.rand = 0;
394                         } else {
395                             couple.base = $2;
396                             couple.rand = $4;
397                         }
398                   }
399                 ;
400
401 /*
402  *      rcouple rules:
403  *
404  *      (base, range) where:
405  *
406  *          base is either a positive or negative integer with a value
407  *          less than or equal to MAXLEVEL.
408  *          base > 0 indicates a forward index.
409  *          base < 0 indicates a reverse index.
410  *          base == 0 indicates on the parent level.
411  *
412  *          range is the random component.
413  *          if range is zero, there is no random component.
414  *          during dungeon load, range is always *added* to the base,
415  *          range + base(converted) may be very large.  The dungeon
416  *          loader will then correct to "between here and the top/bottom".
417  *
418  *          There is no practical way of specifying "between here and the
419  *          nth / nth last level".
420  */
421 rcouple         : '(' INTEGER ',' INTEGER ')'
422                   {
423                         if ($2 < -MAXLEVEL || $2 > MAXLEVEL) {
424                             yyerror("Rel base out of dlevel range - zeroing!");
425                             couple.base = couple.rand = 0;
426                         } else {
427                             couple.base = $2;
428                             couple.rand = $4;
429                         }
430                   }
431                 ;
432 %%
433
434 void
435 init_dungeon()
436 {
437         if(++n_dgns > MAXDUNGEON) {
438             (void) fprintf(stderr, "FATAL - Too many dungeons (limit: %d).\n",
439                     MAXDUNGEON);
440             (void) fprintf(stderr, "To increase the limit edit MAXDUNGEON in global.h\n");
441             exit(EXIT_FAILURE);
442         }
443
444         in_dungeon = 1;
445         tmpdungeon[n_dgns].lev.base = 0;
446         tmpdungeon[n_dgns].lev.rand = 0;
447         tmpdungeon[n_dgns].chance = 100;
448         Strcpy(tmpdungeon[n_dgns].name, "");
449         Strcpy(tmpdungeon[n_dgns].protoname, "");
450         tmpdungeon[n_dgns].flags = 0;
451         tmpdungeon[n_dgns].levels = 0;
452         tmpdungeon[n_dgns].branches = 0;
453         tmpdungeon[n_dgns].entry_lev = 0;
454 }
455
456 void
457 init_level()
458 {
459         if(++n_levs > LEV_LIMIT) {
460
461                 yyerror("FATAL - Too many special levels defined.");
462                 exit(EXIT_FAILURE);
463         }
464         tmplevel[n_levs].lev.base = 0;
465         tmplevel[n_levs].lev.rand = 0;
466         tmplevel[n_levs].chance = 100;
467         tmplevel[n_levs].rndlevs = 0;
468         tmplevel[n_levs].flags = 0;
469         Strcpy(tmplevel[n_levs].name, "");
470         tmplevel[n_levs].chain = -1;
471 }
472
473 void
474 init_branch()
475 {
476         if(++n_brs > BRANCH_LIMIT) {
477
478                 yyerror("FATAL - Too many special levels defined.");
479                 exit(EXIT_FAILURE);
480         }
481         tmpbranch[n_brs].lev.base = 0;
482         tmpbranch[n_brs].lev.rand = 0;
483         Strcpy(tmpbranch[n_brs].name, "");
484         tmpbranch[n_brs].chain = -1;
485 }
486
487 int
488 getchain(s)
489         char    *s;
490 {
491         int i;
492
493         if(strlen(s)) {
494
495             for(i = n_levs - tmpdungeon[n_dgns].levels + 1; i <= n_levs; i++)
496                 if(!strcmp(tmplevel[i].name, s)) return i;
497
498             yyerror("Can't locate the specified chain level.");
499             return(-2);
500         }
501         return(-1);
502 }
503
504 /*
505  *      Consistancy checking routines:
506  *
507  *      - A dungeon must have a unique name.
508  *      - A dungeon must have a originating "branch" command
509  *        (except, of course, for the first dungeon).
510  *      - A dungeon must have a proper depth (at least (1, 0)).
511  */
512
513 int
514 check_dungeon()
515 {
516         int i;
517
518         for(i = 0; i < n_dgns; i++)
519             if(!strcmp(tmpdungeon[i].name, tmpdungeon[n_dgns].name)) {
520                 yyerror("Duplicate dungeon name.");
521                 return(0);
522             }
523
524         if(n_dgns)
525           for(i = 0; i < n_brs - tmpdungeon[n_dgns].branches; i++) {
526             if(!strcmp(tmpbranch[i].name, tmpdungeon[n_dgns].name)) break;
527
528             if(i >= n_brs - tmpdungeon[n_dgns].branches) {
529                 yyerror("Dungeon cannot be reached.");
530                 return(0);
531             }
532           }
533
534         if(tmpdungeon[n_dgns].lev.base <= 0 ||
535            tmpdungeon[n_dgns].lev.rand < 0) {
536                 yyerror("Invalid dungeon depth specified.");
537                 return(0);
538         }
539         return(1);      /* OK */
540 }
541
542 /*
543  *      - A level must have a unique level name.
544  *      - If chained, the level used as reference for the chain
545  *        must be in this dungeon, must be previously defined, and
546  *        the level chained from must be "non-probabilistic" (ie.
547  *        have a 100% chance of existing).
548  */
549
550 int
551 check_level()
552 {
553         int i;
554
555         if(!in_dungeon) {
556                 yyerror("Level defined outside of dungeon.");
557                 return(0);
558         }
559
560         for(i = 0; i < n_levs; i++)
561             if(!strcmp(tmplevel[i].name, tmplevel[n_levs].name)) {
562                 yyerror("Duplicate level name.");
563                 return(0);
564             }
565
566         if(tmplevel[i].chain == -2) {
567                 yyerror("Invaild level chain reference.");
568                 return(0);
569         } else if(tmplevel[i].chain != -1) {    /* there is a chain */
570             /* KMH -- tmplevel[tmpbranch[i].chain].chance was in error */
571             if(tmplevel[tmplevel[i].chain].chance != 100) {
572                 yyerror("Level cannot chain from a probabilistic level.");
573                 return(0);
574             } else if(tmplevel[i].chain == n_levs) {
575                 yyerror("A level cannot chain to itself!");
576                 return(0);
577             }
578         }
579         return(1);      /* OK */
580 }
581
582 /*
583  *      - A branch may not branch backwards - to avoid branch loops.
584  *      - A branch name must be unique.
585  *        (ie. You can only have one entry point to each dungeon).
586  *      - If chained, the level used as reference for the chain
587  *        must be in this dungeon, must be previously defined, and
588  *        the level chained from must be "non-probabilistic" (ie.
589  *        have a 100% chance of existing).
590  */
591
592 int
593 check_branch()
594 {
595         int i;
596
597         if(!in_dungeon) {
598                 yyerror("Branch defined outside of dungeon.");
599                 return(0);
600         }
601
602         for(i = 0; i < n_dgns; i++)
603             if(!strcmp(tmpdungeon[i].name, tmpbranch[n_brs].name)) {
604
605                 yyerror("Reverse branching not allowed.");
606                 return(0);
607             }
608
609         if(tmpbranch[i].chain == -2) {
610
611                 yyerror("Invaild branch chain reference.");
612                 return(0);
613         } else if(tmpbranch[i].chain != -1) {   /* it is chained */
614
615             if(tmplevel[tmpbranch[i].chain].chance != 100) {
616                 yyerror("Branch cannot chain from a probabilistic level.");
617                 return(0);
618             }
619         }
620         return(1);      /* OK */
621 }
622
623 /*
624  *      Output the dungon definition into a file.
625  *
626  *      The file will have the following format:
627  *
628  *      [ nethack version ID ]
629  *      [ number of dungeons ]
630  *      [ first dungeon struct ]
631  *      [ levels for the first dungeon ]
632  *        ...
633  *      [ branches for the first dungeon ]
634  *        ...
635  *      [ second dungeon struct ]
636  *        ...
637  */
638
639 void
640 output_dgn()
641 {
642         int     nd, cl = 0, nl = 0,
643                     cb = 0, nb = 0;
644         static struct version_info version_data = {
645                         VERSION_NUMBER, VERSION_FEATURES,
646                         VERSION_SANITY1, VERSION_SANITY2
647         };
648
649         if(++n_dgns <= 0) {
650             yyerror("FATAL - no dungeons were defined.");
651             exit(EXIT_FAILURE);
652         }
653
654         if (fwrite((char *)&version_data, sizeof version_data, 1, yyout) != 1) {
655             yyerror("FATAL - output failure.");
656             exit(EXIT_FAILURE);
657         }
658
659         (void) fwrite((char *)&n_dgns, sizeof(int), 1, yyout);
660         for (nd = 0; nd < n_dgns; nd++) {
661             (void) fwrite((char *)&tmpdungeon[nd], sizeof(struct tmpdungeon),
662                                                         1, yyout);
663
664             nl += tmpdungeon[nd].levels;
665             for(; cl < nl; cl++)
666                 (void) fwrite((char *)&tmplevel[cl], sizeof(struct tmplevel),
667                                                         1, yyout);
668
669             nb += tmpdungeon[nd].branches;
670             for(; cb < nb; cb++)
671                 (void) fwrite((char *)&tmpbranch[cb], sizeof(struct tmpbranch),
672                                                         1, yyout);
673         }
674         /* apparently necessary for Think C 5.x, otherwise harmless */
675         (void) fflush(yyout);
676 }
677
678 /*dgn_comp.y*/