home *** CD-ROM | disk | FTP | other *** search
Lex Description | 1993-08-01 | 15.9 KB | 684 lines | [TEXT/R*ch] |
- %{
- /* SCCS Id: @(#)dgn_comp.c 3.1 93/05/15 */
- /* Copyright (c) 1989 by Jean-Christophe Collet */
- /* Copyright (c) 1990 by M. Stephenson */
- /* NetHack may be freely redistributed. See license for details. */
-
- /*
- * This file contains the Dungeon Compiler code
- */
-
- /* In case we're using bison in AIX. This definition must be
- * placed before any other C-language construct in the file
- * excluding comments and preprocessor directives (thanks IBM
- * for this wonderful feature...).
- *
- * Note: some cpps barf on this 'undefined control' (#pragma).
- * Addition of the leading space seems to prevent barfage for now,
- * and AIX will still see the directive in its non-standard locale.
- */
-
- #ifdef _AIX
- #pragma alloca /* keep leading space! */
- #endif
-
- #include "config.h"
- #include "dgn_file.h"
-
- void FDECL(yyerror, (const char *));
- void FDECL(yywarning, (const char *));
- int NDECL(yylex);
- int NDECL(yyparse);
- int FDECL(getchain, (char *));
- int NDECL(check_dungeon);
- int NDECL(check_branch);
- int NDECL(check_level);
- void NDECL(init_dungeon);
- void NDECL(init_branch);
- void NDECL(init_level);
- void NDECL(output_dgn);
-
- #ifdef AMIGA
- # undef printf
- #ifndef LATTICE
- # define memset(addr,val,len) setmem(addr,len,val)
- #endif
- #endif
-
- #ifdef MICRO
- # undef exit
- extern void FDECL(exit, (int));
- #endif
-
- #undef NULL
-
- #define ERR (-1)
-
- static struct couple couple;
- static struct tmpdungeon tmpdungeon[MAXDUNGEON];
- static struct tmplevel tmplevel[LEV_LIMIT];
- static struct tmpbranch tmpbranch[BRANCH_LIMIT];
-
- static int in_dungeon = 0, n_dgns = -1, n_levs = -1, n_brs = -1;
-
- extern int fatal_error;
- extern const char *fname;
-
- %}
-
- %union
- {
- int i;
- char* str;
- }
-
- %token <i> INTEGER
- %token <i> A_DUNGEON BRANCH CHBRANCH LEVEL RNDLEVEL CHLEVEL RNDCHLEVEL
- %token <i> UP_OR_DOWN PROTOFILE DESCRIPTION DESCRIPTOR LEVELDESC
- %token <i> ALIGNMENT LEVALIGN ENTRY STAIR NO_UP NO_DOWN PORTAL
- %token <str> STRING
- %type <i> optional_int direction branch_type
- %start file
-
- %%
- file : /* nothing */
- | dungeons
- {
- output_dgn();
- }
- ;
-
- dungeons : dungeon
- | dungeons dungeon
- ;
-
- dungeon : dungeonline
- | dungeondesc
- | branches
- | levels
- ;
-
- dungeonline : A_DUNGEON ':' STRING STRING rcouple optional_int
- {
- init_dungeon();
- strcpy(tmpdungeon[n_dgns].name, $3);
- if (!strcmp($4, "none"))
- tmpdungeon[n_levs].boneschar = '\0';
- else if ($4[1])
- yyerror("Bones marker must be a single char, or \"none\"!");
- else
- tmpdungeon[n_dgns].boneschar = $4[0];
- tmpdungeon[n_dgns].lev.base = couple.base;
- tmpdungeon[n_dgns].lev.rand = couple.rand;
- tmpdungeon[n_dgns].chance = $6;
- }
- ;
-
- optional_int : /* nothing */
- {
- $$ = 0;
- }
- | INTEGER
- {
- $$ = $1;
- }
- ;
-
- dungeondesc : entry
- | descriptions
- | prototype
- ;
-
- entry : ENTRY ':' INTEGER
- {
- tmpdungeon[n_dgns].entry_lev = $3;
- }
- ;
-
- descriptions : desc
- ;
-
- desc : DESCRIPTION ':' DESCRIPTOR
- {
- if($<i>3 <= TOWN || $<i>3 >= D_ALIGN_CHAOTIC)
- yyerror("Illegal description - ignoring!");
- else
- tmpdungeon[n_dgns].flags |= $<i>3 ;
- }
- | ALIGNMENT ':' DESCRIPTOR
- {
- if($<i>3 && $<i>3 < D_ALIGN_CHAOTIC)
- yyerror("Illegal alignment - ignoring!");
- else
- tmpdungeon[n_dgns].flags |= $<i>3 ;
- }
- ;
-
- prototype : PROTOFILE ':' STRING
- {
- strcpy(tmpdungeon[n_dgns].protoname, $3);
- }
- ;
-
- levels : level1
- | level2
- | levdesc
- | chlevel1
- | chlevel2
- ;
-
- level1 : LEVEL ':' STRING STRING '@' acouple
- {
- init_level();
- strcpy(tmplevel[n_levs].name, $3);
- if (!strcmp($4, "none"))
- tmplevel[n_levs].boneschar = '\0';
- else if ($4[1])
- yyerror("Bones marker must be a single char, or \"none\"!");
- else
- tmplevel[n_levs].boneschar = $4[0];
- tmplevel[n_levs].lev.base = couple.base;
- tmplevel[n_levs].lev.rand = couple.rand;
- tmpdungeon[n_dgns].levels++;
- }
- | RNDLEVEL ':' STRING STRING '@' acouple INTEGER
- {
- init_level();
- strcpy(tmplevel[n_levs].name, $3);
- if (!strcmp($4, "none"))
- tmplevel[n_levs].boneschar = '\0';
- else if ($4[1])
- yyerror("Bones marker must be a single char, or \"none\"!");
- else
- tmplevel[n_levs].boneschar = $4[0];
- tmplevel[n_levs].lev.base = couple.base;
- tmplevel[n_levs].lev.rand = couple.rand;
- tmplevel[n_levs].rndlevs = $7;
- tmpdungeon[n_dgns].levels++;
- }
- ;
-
- level2 : LEVEL ':' STRING STRING '@' acouple INTEGER
- {
- init_level();
- strcpy(tmplevel[n_levs].name, $3);
- if (!strcmp($4, "none"))
- tmplevel[n_levs].boneschar = '\0';
- else if ($4[1])
- yyerror("Bones marker must be a single char, or \"none\"!");
- else
- tmplevel[n_levs].boneschar = $4[0];
- tmplevel[n_levs].lev.base = couple.base;
- tmplevel[n_levs].lev.rand = couple.rand;
- tmplevel[n_levs].chance = $7;
- tmpdungeon[n_dgns].levels++;
- }
- | RNDLEVEL ':' STRING STRING '@' acouple INTEGER INTEGER
- {
- init_level();
- strcpy(tmplevel[n_levs].name, $3);
- if (!strcmp($4, "none"))
- tmplevel[n_levs].boneschar = '\0';
- else if ($4[1])
- yyerror("Bones marker must be a single char, or \"none\"!");
- else
- tmplevel[n_levs].boneschar = $4[0];
- tmplevel[n_levs].lev.base = couple.base;
- tmplevel[n_levs].lev.rand = couple.rand;
- tmplevel[n_levs].chance = $7;
- tmplevel[n_levs].rndlevs = $8;
- tmpdungeon[n_dgns].levels++;
- }
- ;
-
- levdesc : LEVELDESC ':' DESCRIPTOR
- {
- if($<i>3 >= D_ALIGN_CHAOTIC)
- yyerror("Illegal description - ignoring!");
- else
- tmplevel[n_levs].flags |= $<i>3 ;
- }
- | LEVALIGN ':' DESCRIPTOR
- {
- if($<i>3 && $<i>3 < D_ALIGN_CHAOTIC)
- yyerror("Illegal alignment - ignoring!");
- else
- tmplevel[n_levs].flags |= $<i>3 ;
- }
- ;
-
- chlevel1 : CHLEVEL ':' STRING STRING STRING '+' rcouple
- {
- init_level();
- strcpy(tmplevel[n_levs].name, $3);
- if (!strcmp($4, "none"))
- tmplevel[n_levs].boneschar = '\0';
- else if ($4[1])
- yyerror("Bones marker must be a single char, or \"none\"!");
- else
- tmplevel[n_levs].boneschar = $4[0];
- tmplevel[n_levs].chain = getchain($5);
- tmplevel[n_levs].lev.base = couple.base;
- tmplevel[n_levs].lev.rand = couple.rand;
- if(!check_level()) n_levs--;
- else tmpdungeon[n_dgns].levels++;
- }
- | RNDCHLEVEL ':' STRING STRING STRING '+' rcouple INTEGER
- {
- init_level();
- strcpy(tmplevel[n_levs].name, $3);
- if (!strcmp($4, "none"))
- tmplevel[n_levs].boneschar = '\0';
- else if ($4[1])
- yyerror("Bones marker must be a single char, or \"none\"!");
- else
- tmplevel[n_levs].boneschar = $4[0];
- tmplevel[n_levs].chain = getchain($5);
- tmplevel[n_levs].lev.base = couple.base;
- tmplevel[n_levs].lev.rand = couple.rand;
- tmplevel[n_levs].rndlevs = $8;
- if(!check_level()) n_levs--;
- else tmpdungeon[n_dgns].levels++;
- }
- ;
-
- chlevel2 : CHLEVEL ':' STRING STRING STRING '+' rcouple INTEGER
- {
- init_level();
- strcpy(tmplevel[n_levs].name, $3);
- if (!strcmp($4, "none"))
- tmplevel[n_levs].boneschar = '\0';
- else if ($4[1])
- yyerror("Bones marker must be a single char, or \"none\"!");
- else
- tmplevel[n_levs].boneschar = $4[0];
- tmplevel[n_levs].chain = getchain($5);
- tmplevel[n_levs].lev.base = couple.base;
- tmplevel[n_levs].lev.rand = couple.rand;
- tmplevel[n_levs].chance = $8;
- if(!check_level()) n_levs--;
- else tmpdungeon[n_dgns].levels++;
- }
- | RNDCHLEVEL ':' STRING STRING STRING '+' rcouple INTEGER INTEGER
- {
- init_level();
- strcpy(tmplevel[n_levs].name, $3);
- if (!strcmp($4, "none"))
- tmplevel[n_levs].boneschar = '\0';
- else if ($4[1])
- yyerror("Bones marker must be a single char, or \"none\"!");
- else
- tmplevel[n_levs].boneschar = $4[0];
- tmplevel[n_levs].chain = getchain($5);
- tmplevel[n_levs].lev.base = couple.base;
- tmplevel[n_levs].lev.rand = couple.rand;
- tmplevel[n_levs].chance = $8;
- tmplevel[n_levs].rndlevs = $9;
- if(!check_level()) n_levs--;
- else tmpdungeon[n_dgns].levels++;
- }
- ;
-
- branches : branch
- | chbranch
- ;
-
- branch : BRANCH ':' STRING '@' acouple branch_type direction
- {
- init_branch();
- strcpy(tmpbranch[n_brs].name, $3);
- tmpbranch[n_brs].lev.base = couple.base;
- tmpbranch[n_brs].lev.rand = couple.rand;
- tmpbranch[n_brs].type = $6;
- tmpbranch[n_brs].up = $7;
- if(!check_branch()) n_brs--;
- else tmpdungeon[n_dgns].branches++;
- }
- ;
-
- chbranch : CHBRANCH ':' STRING STRING '+' rcouple branch_type direction
- {
- init_branch();
- strcpy(tmpbranch[n_brs].name, $3);
- tmpbranch[n_brs].chain = getchain($4);
- tmpbranch[n_brs].lev.base = couple.base;
- tmpbranch[n_brs].lev.rand = couple.rand;
- tmpbranch[n_brs].type = $7;
- tmpbranch[n_brs].up = $8;
- if(!check_branch()) n_brs--;
- else tmpdungeon[n_dgns].branches++;
- }
- ;
-
- branch_type : /* nothing */
- {
- $$ = TBR_STAIR; /* two way stair */
- }
- | STAIR
- {
- $$ = TBR_STAIR; /* two way stair */
- }
- | NO_UP
- {
- $$ = TBR_NO_UP; /* no up staircase */
- }
- | NO_DOWN
- {
- $$ = TBR_NO_DOWN; /* no down staircase */
- }
- | PORTAL
- {
- $$ = TBR_PORTAL; /* portal connection */
- }
- ;
-
- direction : /* nothing */
- {
- $$ = 0; /* defaults to down */
- }
- | UP_OR_DOWN
- {
- $$ = $1;
- }
- ;
-
- /*
- * acouple rules:
- *
- * (base, range) where:
- *
- * base is either a positive or negative integer with a value
- * less than or equal to MAXLEVEL.
- * base > 0 indicates the base level.
- * base < 0 indicates reverse index (-1 == lowest level)
- *
- * range is the random component.
- * if range is zero, there is no random component.
- * if range is -1 the dungeon loader will randomize between
- * the base and the end of the dungeon.
- * during dungeon load, range is always *added* to the base,
- * therefore range + base(converted) must not exceed MAXLEVEL.
- */
- acouple : '(' INTEGER ',' INTEGER ')'
- {
- if ($2 < -MAXLEVEL || $2 > MAXLEVEL) {
- yyerror("Abs base out of dlevel range - zeroing!");
- couple.base = couple.rand = 0;
- } else if ($4 < -1 ||
- (($2 < 0) ? (MAXLEVEL + $2 + $4 + 1) > MAXLEVEL :
- ($2 + $4) > MAXLEVEL)) {
- yyerror("Abs range out of dlevel range - zeroing!");
- couple.base = couple.rand = 0;
- } else {
- couple.base = $2;
- couple.rand = $4;
- }
- }
- ;
-
- /*
- * rcouple rules:
- *
- * (base, range) where:
- *
- * base is either a positive or negative integer with a value
- * less than or equal to MAXLEVEL.
- * base > 0 indicates a forward index.
- * base < 0 indicates a reverse index.
- * base == 0 indicates on the parent level.
- *
- * range is the random component.
- * if range is zero, there is no random component.
- * during dungeon load, range is always *added* to the base,
- * range + base(converted) may be very large. The dungeon
- * loader will then correct to "between here and the top/bottom".
- *
- * There is no practical way of specifying "between here and the
- * nth / nth last level".
- */
- rcouple : '(' INTEGER ',' INTEGER ')'
- {
- if ($2 < -MAXLEVEL || $2 > MAXLEVEL) {
- yyerror("Rel base out of dlevel range - zeroing!");
- couple.base = couple.rand = 0;
- } else {
- couple.base = $2;
- couple.rand = $4;
- }
- }
- ;
- %%
-
- void
- init_dungeon()
- {
- if(++n_dgns > MAXDUNGEON) {
- fprintf(stderr, "FATAL - Too many dungeons (limit: %d).\n",
- MAXDUNGEON);
- fprintf(stderr, "To increase the limit edit MAXDUNGEON in global.h\n");
- exit(1);
- }
-
- in_dungeon = 1;
- tmpdungeon[n_dgns].lev.base = 0;
- tmpdungeon[n_dgns].lev.rand = 0;
- tmpdungeon[n_dgns].chance = 100;
- strcpy(tmpdungeon[n_dgns].name, "");
- strcpy(tmpdungeon[n_dgns].protoname, "");
- tmpdungeon[n_dgns].flags = 0;
- tmpdungeon[n_dgns].levels = 0;
- tmpdungeon[n_dgns].branches = 0;
- tmpdungeon[n_dgns].entry_lev = 0;
- }
-
- void
- init_level()
- {
- if(++n_levs > LEV_LIMIT) {
-
- yyerror("FATAL - Too many special levels defined.");
- exit(1);
- }
- tmplevel[n_levs].lev.base = 0;
- tmplevel[n_levs].lev.rand = 0;
- tmplevel[n_levs].chance = 100;
- tmplevel[n_levs].rndlevs = 0;
- tmplevel[n_levs].flags = 0;
- strcpy(tmplevel[n_levs].name, "");
- tmplevel[n_levs].chain = -1;
- }
-
- void
- init_branch()
- {
- if(++n_brs > BRANCH_LIMIT) {
-
- yyerror("FATAL - Too many special levels defined.");
- exit(1);
- }
- tmpbranch[n_brs].lev.base = 0;
- tmpbranch[n_brs].lev.rand = 0;
- strcpy(tmpbranch[n_brs].name, "");
- tmpbranch[n_brs].chain = -1;
- }
-
- int
- getchain(s)
- char *s;
- {
- int i;
-
- if(strlen(s)) {
-
- for(i = n_levs - tmpdungeon[n_dgns].levels + 1; i <= n_levs; i++)
- if(!strcmp(tmplevel[i].name, s)) return i;
-
- yyerror("Can't locate the specified chain level.");
- return(-2);
- }
- return(-1);
- }
-
- /*
- * Consistancy checking routines:
- *
- * - A dungeon must have a unique name.
- * - A dungeon must have a originating "branch" command
- * (except, of course, for the first dungeon).
- * - A dungeon must have a proper depth (at least (1, 0)).
- */
-
- int
- check_dungeon()
- {
- int i;
-
- for(i = 0; i < n_dgns; i++)
- if(!strcmp(tmpdungeon[i].name, tmpdungeon[n_dgns].name)) {
- yyerror("Duplicate dungeon name.");
- return(0);
- }
-
- if(n_dgns)
- for(i = 0; i < n_brs - tmpdungeon[n_dgns].branches; i++) {
- if(!strcmp(tmpbranch[i].name, tmpdungeon[n_dgns].name)) break;
-
- if(i >= n_brs - tmpdungeon[n_dgns].branches) {
- yyerror("Dungeon cannot be reached.");
- return(0);
- }
- }
-
- if(tmpdungeon[n_dgns].lev.base <= 0 ||
- tmpdungeon[n_dgns].lev.rand < 0) {
- yyerror("Invalid dungeon depth specified.");
- return(0);
- }
- return(1); /* OK */
- }
-
- /*
- * - A level must have a unique level name.
- * - If chained, the level used as reference for the chain
- * must be in this dungeon, must be previously defined, and
- * the level chained from must be "non-probabalistic" (ie.
- * have a 100% chance of existing).
- */
-
- int
- check_level()
- {
- int i;
-
- if(!in_dungeon) {
- yyerror("Level defined outside of dungeon.");
- return(0);
- }
-
- for(i = 0; i < n_levs; i++)
- if(!strcmp(tmplevel[i].name, tmplevel[n_levs].name)) {
- yyerror("Duplicate level name.");
- return(0);
- }
-
- if(tmplevel[i].chain == -2) {
- yyerror("Invaild level chain reference.");
- return(0);
- } else if(tmplevel[i].chain != -1) { /* there is a chain */
- if(tmplevel[tmpbranch[i].chain].chance != 100) {
- yyerror("Level cannot chain from a probabalistic level.");
- return(0);
- } else if(tmplevel[i].chain == n_levs) {
- yyerror("A level cannot chain to itself!");
- return(0);
- }
- }
- return(1); /* OK */
- }
-
- /*
- * - A branch may not branch backwards - to avoid branch loops.
- * - A branch name must be unique.
- * (ie. You can only have one entry point to each dungeon).
- * - If chained, the level used as reference for the chain
- * must be in this dungeon, must be previously defined, and
- * the level chained from must be "non-probabalistic" (ie.
- * have a 100% chance of existing).
- */
-
- int
- check_branch()
- {
- int i;
-
- if(!in_dungeon) {
- yyerror("Branch defined outside of dungeon.");
- return(0);
- }
-
- for(i = 0; i < n_dgns; i++)
- if(!strcmp(tmpdungeon[i].name, tmpbranch[n_brs].name)) {
-
- yyerror("Reverse branching not allowed.");
- return(0);
- }
-
- if(tmpbranch[i].chain == -2) {
-
- yyerror("Invaild branch chain reference.");
- return(0);
- } else if(tmpbranch[i].chain != -1) { /* it is chained */
-
- if(tmplevel[tmpbranch[i].chain].chance != 100) {
- yyerror("Branch cannot chain from a probabalistic level.");
- return(0);
- }
- }
- return(1); /* OK */
- }
-
- /*
- * Output the dungon definition into a file.
- *
- * The file will have the following format:
- *
- * [ number of dungeons ]
- * [ first dungeon struct ]
- * [ levels for the first dungeon ]
- * ...
- * [ branches for the first dungeon ]
- * ...
- * [ second dungeon struct ]
- * ...
- */
-
- void
- output_dgn()
- {
- int nd, cl = 0, nl = 0,
- cb = 0, nb = 0;
-
- if(++n_dgns <= 0) {
-
- yyerror("FATAL - no dungeons were defined.");
- exit(1);
- }
-
- fwrite((char *)(&n_dgns), sizeof(int), 1, stdout);
- for(nd = 0; nd < n_dgns; nd++) {
-
- fwrite((char *)&tmpdungeon[nd], sizeof(struct tmpdungeon), 1,
- stdout);
-
- nl += tmpdungeon[nd].levels;
- for(; cl < nl; cl++)
- fwrite((char *)&tmplevel[cl], sizeof(struct tmplevel), 1,
- stdout);
-
- nb += tmpdungeon[nd].branches;
- for(; cb < nb; cb++)
- fwrite((char *)&tmpbranch[cb], sizeof(struct tmpbranch), 1,
- stdout);
- }
- }
-