NHDT->ANH, nethack->anethack, nhdat->anhdat
[aNetHack.git] / util / dgn_comp.y
blobcd3be86811ca151bca3247c8a58c7f4fbbb5890d
1 %{
2 /* aNetHack 0.0.1 dgn_comp.y $ANH-Date: 1432512785 2015/05/25 00:13:05 $ $ANH-Branch: master $:$ANH-Revision: 1.8 $ */
3 /* Copyright (c) 1989 by Jean-Christophe Collet */
4 /* Copyright (c) 1990 by M. Stephenson */
5 /* aNetHack may be freely redistributed. See license for details. */
7 /*
8 * This file contains the Dungeon Compiler code
9 */
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...).
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.
21 #ifdef _AIX
22 #pragma alloca /* keep leading space! */
23 #endif
25 #include "config.h"
26 #include "date.h"
27 #include "dgn_file.h"
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);
42 #define Free(ptr) free((genericptr_t)ptr)
44 #ifdef AMIGA
45 # undef printf
46 #ifndef LATTICE
47 # define memset(addr,val,len) setmem(addr,len,val)
48 #endif
49 #endif
51 #define ERR (-1)
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];
58 static int in_dungeon = 0, n_dgns = -1, n_levs = -1, n_brs = -1;
60 extern int fatal_error;
61 extern const char *fname;
62 extern FILE *yyin, *yyout; /* from dgn_lex.c */
66 %union
68 int i;
69 char* str;
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
81 file : /* nothing */
82 | dungeons
84 output_dgn();
88 dungeons : dungeon
89 | dungeons dungeon
92 dungeon : dungeonline
93 | dungeondesc
94 | branches
95 | levels
98 dungeonline : A_DUNGEON ':' STRING bones_tag rcouple optional_int
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);
110 optional_int : /* nothing */
112 $$ = 0;
114 | INTEGER
116 $$ = $1;
120 dungeondesc : entry
121 | descriptions
122 | prototype
125 entry : ENTRY ':' INTEGER
127 tmpdungeon[n_dgns].entry_lev = $3;
131 descriptions : desc
134 desc : DESCRIPTION ':' DESCRIPTOR
136 if($<i>3 <= TOWN || $<i>3 >= D_ALIGN_CHAOTIC)
137 yyerror("Illegal description - ignoring!");
138 else
139 tmpdungeon[n_dgns].flags |= $<i>3 ;
141 | ALIGNMENT ':' DESCRIPTOR
143 if($<i>3 && $<i>3 < D_ALIGN_CHAOTIC)
144 yyerror("Illegal alignment - ignoring!");
145 else
146 tmpdungeon[n_dgns].flags |= $<i>3 ;
150 prototype : PROTOFILE ':' STRING
152 Strcpy(tmpdungeon[n_dgns].protoname, $3);
153 Free($3);
157 levels : level1
158 | level2
159 | levdesc
160 | chlevel1
161 | chlevel2
164 level1 : LEVEL ':' STRING bones_tag '@' acouple
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);
174 | RNDLEVEL ':' STRING bones_tag '@' acouple INTEGER
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);
187 level2 : LEVEL ':' STRING bones_tag '@' acouple INTEGER
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);
198 | RNDLEVEL ':' STRING bones_tag '@' acouple INTEGER INTEGER
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);
212 levdesc : LEVELDESC ':' DESCRIPTOR
214 if($<i>3 >= D_ALIGN_CHAOTIC)
215 yyerror("Illegal description - ignoring!");
216 else
217 tmplevel[n_levs].flags |= $<i>3 ;
219 | LEVALIGN ':' DESCRIPTOR
221 if($<i>3 && $<i>3 < D_ALIGN_CHAOTIC)
222 yyerror("Illegal alignment - ignoring!");
223 else
224 tmplevel[n_levs].flags |= $<i>3 ;
228 chlevel1 : CHLEVEL ':' STRING bones_tag STRING '+' rcouple
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);
241 | RNDCHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER
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);
257 chlevel2 : CHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER
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);
271 | RNDCHLEVEL ':' STRING bones_tag STRING '+' rcouple INTEGER INTEGER
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);
288 branches : branch
289 | chbranch
292 branch : BRANCH ':' STRING '@' acouple branch_type direction
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);
306 chbranch : CHBRANCH ':' STRING STRING '+' rcouple branch_type direction
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);
322 branch_type : /* nothing */
324 $$ = TBR_STAIR; /* two way stair */
326 | STAIR
328 $$ = TBR_STAIR; /* two way stair */
330 | NO_UP
332 $$ = TBR_NO_UP; /* no up staircase */
334 | NO_DOWN
336 $$ = TBR_NO_DOWN; /* no down staircase */
338 | PORTAL
340 $$ = TBR_PORTAL; /* portal connection */
344 direction : /* nothing */
346 $$ = 0; /* defaults to down */
348 | UP_OR_DOWN
350 $$ = $1;
354 bones_tag : STRING
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';
362 $$ = *p;
363 Free(p);
368 * acouple rules:
370 * (base, range) where:
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)
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.
384 acouple : '(' INTEGER ',' INTEGER ')'
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;
402 * rcouple rules:
404 * (base, range) where:
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.
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".
418 * There is no practical way of specifying "between here and the
419 * nth / nth last level".
421 rcouple : '(' INTEGER ',' INTEGER ')'
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;
434 void
435 init_dungeon()
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);
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;
456 void
457 init_level()
459 if(++n_levs > LEV_LIMIT) {
461 yyerror("FATAL - Too many special levels defined.");
462 exit(EXIT_FAILURE);
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;
473 void
474 init_branch()
476 if(++n_brs > BRANCH_LIMIT) {
478 yyerror("FATAL - Too many special levels defined.");
479 exit(EXIT_FAILURE);
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;
488 getchain(s)
489 char *s;
491 int i;
493 if(strlen(s)) {
495 for(i = n_levs - tmpdungeon[n_dgns].levels + 1; i <= n_levs; i++)
496 if(!strcmp(tmplevel[i].name, s)) return i;
498 yyerror("Can't locate the specified chain level.");
499 return(-2);
501 return(-1);
505 * Consistancy checking routines:
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)).
514 check_dungeon()
516 int i;
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);
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;
528 if(i >= n_brs - tmpdungeon[n_dgns].branches) {
529 yyerror("Dungeon cannot be reached.");
530 return(0);
534 if(tmpdungeon[n_dgns].lev.base <= 0 ||
535 tmpdungeon[n_dgns].lev.rand < 0) {
536 yyerror("Invalid dungeon depth specified.");
537 return(0);
539 return(1); /* OK */
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).
551 check_level()
553 int i;
555 if(!in_dungeon) {
556 yyerror("Level defined outside of dungeon.");
557 return(0);
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);
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);
579 return(1); /* OK */
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).
593 check_branch()
595 int i;
597 if(!in_dungeon) {
598 yyerror("Branch defined outside of dungeon.");
599 return(0);
602 for(i = 0; i < n_dgns; i++)
603 if(!strcmp(tmpdungeon[i].name, tmpbranch[n_brs].name)) {
605 yyerror("Reverse branching not allowed.");
606 return(0);
609 if(tmpbranch[i].chain == -2) {
611 yyerror("Invaild branch chain reference.");
612 return(0);
613 } else if(tmpbranch[i].chain != -1) { /* it is chained */
615 if(tmplevel[tmpbranch[i].chain].chance != 100) {
616 yyerror("Branch cannot chain from a probabilistic level.");
617 return(0);
620 return(1); /* OK */
624 * Output the dungon definition into a file.
626 * The file will have the following format:
628 * [ anethack 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 * ...
639 void
640 output_dgn()
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, VERSION_SANITY3
649 if(++n_dgns <= 0) {
650 yyerror("FATAL - no dungeons were defined.");
651 exit(EXIT_FAILURE);
654 if (fwrite((char *)&version_data, sizeof version_data, 1, yyout) != 1) {
655 yyerror("FATAL - output failure.");
656 exit(EXIT_FAILURE);
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);
664 nl += tmpdungeon[nd].levels;
665 for(; cl < nl; cl++)
666 (void) fwrite((char *)&tmplevel[cl], sizeof(struct tmplevel),
667 1, yyout);
669 nb += tmpdungeon[nd].branches;
670 for(; cb < nb; cb++)
671 (void) fwrite((char *)&tmpbranch[cb], sizeof(struct tmpbranch),
672 1, yyout);
674 /* apparently necessary for Think C 5.x, otherwise harmless */
675 (void) fflush(yyout);
678 /*dgn_comp.y*/