%{
/* NetHack 3.6  lev_comp.y	$NHDT-Date: 1455746893 2016/02/17 22:08:13 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.21 $ */
/*      Copyright (c) 1989 by Jean-Christophe Collet */
/* NetHack may be freely redistributed.  See license for details. */

/*
 * This file contains the Level Compiler code
 * It may handle special mazes & special room-levels
 */

/* 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.
 */
#ifdef _AIX
 #pragma alloca         /* keep leading space! */
#endif

#define SPEC_LEV    /* for USE_OLDARGS (sp_lev.h) */
#include "hack.h"
#include "sp_lev.h"

#define ERR             (-1)
/* many types of things are put in chars for transference to NetHack.
 * since some systems will use signed chars, limit everybody to the
 * same number for portability.
 */
#define MAX_OF_TYPE     128

#define MAX_NESTED_IFS   20
#define MAX_SWITCH_CASES 20

#define New(type) \
        (type *) memset((genericptr_t) alloc(sizeof (type)), 0, sizeof (type))
#define NewTab(type, size)      (type **) alloc(sizeof (type *) * size)
#define Free(ptr)               free((genericptr_t) ptr)

extern void VDECL(lc_error, (const char *, ...));
extern void VDECL(lc_warning, (const char *, ...));
extern void FDECL(yyerror, (const char *));
extern void FDECL(yywarning, (const char *));
extern int NDECL(yylex);
int NDECL(yyparse);

extern int FDECL(get_floor_type, (CHAR_P));
extern int FDECL(get_room_type, (char *));
extern int FDECL(get_trap_type, (char *));
extern int FDECL(get_monster_id, (char *,CHAR_P));
extern int FDECL(get_object_id, (char *,CHAR_P));
extern boolean FDECL(check_monster_char, (CHAR_P));
extern boolean FDECL(check_object_char, (CHAR_P));
extern char FDECL(what_map_char, (CHAR_P));
extern void FDECL(scan_map, (char *, sp_lev *));
extern void FDECL(add_opcode, (sp_lev *, int, genericptr_t));
extern genericptr_t FDECL(get_last_opcode_data1, (sp_lev *, int));
extern genericptr_t FDECL(get_last_opcode_data2, (sp_lev *, int, int));
extern boolean FDECL(check_subrooms, (sp_lev *));
extern boolean FDECL(write_level_file, (char *,sp_lev *));
extern struct opvar *FDECL(set_opvar_int, (struct opvar *, long));
extern void VDECL(add_opvars, (sp_lev *, const char *, ...));
extern void FDECL(start_level_def, (sp_lev * *, char *));

extern struct lc_funcdefs *FDECL(funcdef_new, (long,char *));
extern void FDECL(funcdef_free_all, (struct lc_funcdefs *));
extern struct lc_funcdefs *FDECL(funcdef_defined, (struct lc_funcdefs *,
                                                   char *, int));
extern char *FDECL(funcdef_paramtypes, (struct lc_funcdefs *));
extern char *FDECL(decode_parm_str, (char *));

extern struct lc_vardefs *FDECL(vardef_new, (long,char *));
extern void FDECL(vardef_free_all, (struct lc_vardefs *));
extern struct lc_vardefs *FDECL(vardef_defined, (struct lc_vardefs *,
                                                 char *, int));

extern void NDECL(break_stmt_start);
extern void FDECL(break_stmt_end, (sp_lev *));
extern void FDECL(break_stmt_new, (sp_lev *, long));

extern void FDECL(splev_add_from, (sp_lev *, sp_lev *));

extern void FDECL(check_vardef_type, (struct lc_vardefs *, char *, long));
extern void FDECL(vardef_used, (struct lc_vardefs *, char *));
extern struct lc_vardefs *FDECL(add_vardef_type, (struct lc_vardefs *,
                                                  char *, long));

extern int FDECL(reverse_jmp_opcode, (int));

struct coord {
    long x;
    long y;
};

struct forloopdef {
    char *varname;
    long jmp_point;
};
static struct forloopdef forloop_list[MAX_NESTED_IFS];
static short n_forloops = 0;


sp_lev *splev = NULL;

static struct opvar *if_list[MAX_NESTED_IFS];

static short n_if_list = 0;

unsigned int max_x_map, max_y_map;
int obj_containment = 0;

int in_container_obj = 0;

/* integer value is possibly an inconstant value (eg. dice notation or a variable) */
int is_inconstant_number = 0;

int in_switch_statement = 0;
static struct opvar *switch_check_jump = NULL;
static struct opvar *switch_default_case = NULL;
static struct opvar *switch_case_list[MAX_SWITCH_CASES];
static long switch_case_value[MAX_SWITCH_CASES];
int n_switch_case_list = 0;

int allow_break_statements = 0;
struct lc_breakdef *break_list = NULL;

extern struct lc_vardefs *variable_definitions;


struct lc_vardefs *function_tmp_var_defs = NULL;
extern struct lc_funcdefs *function_definitions;
struct lc_funcdefs *curr_function = NULL;
struct lc_funcdefs_parm * curr_function_param = NULL;
int in_function_definition = 0;
sp_lev *function_splev_backup = NULL;

extern int fatal_error;
extern int got_errors;
extern int line_number;
extern const char *fname;

extern char curr_token[512];

%}

%union
{
	long	i;
	char*	map;
	struct {
		long room;
		long wall;
		long door;
	} corpos;
    struct {
	long area;
	long x1;
	long y1;
	long x2;
	long y2;
    } lregn;
    struct {
	long x;
	long y;
    } crd;
    struct {
	long ter;
	long lit;
    } terr;
    struct {
	long height;
	long width;
    } sze;
    struct {
	long die;
	long num;
    } dice;
    struct {
	long cfunc;
	char *varstr;
    } meth;
}


%token	<i> CHAR INTEGER BOOLEAN PERCENT SPERCENT
%token	<i> MINUS_INTEGER PLUS_INTEGER
%token	<i> MAZE_GRID_ID SOLID_FILL_ID MINES_ID ROGUELEV_ID
%token	<i> MESSAGE_ID MAZE_ID LEVEL_ID LEV_INIT_ID GEOMETRY_ID NOMAP_ID
%token	<i> OBJECT_ID COBJECT_ID MONSTER_ID TRAP_ID DOOR_ID DRAWBRIDGE_ID
%token	<i> object_ID monster_ID terrain_ID
%token	<i> MAZEWALK_ID WALLIFY_ID REGION_ID FILLING IRREGULAR JOINED
%token	<i> ALTAR_ID LADDER_ID STAIR_ID NON_DIGGABLE_ID NON_PASSWALL_ID ROOM_ID
%token	<i> PORTAL_ID TELEPRT_ID BRANCH_ID LEV MINERALIZE_ID
%token	<i> CORRIDOR_ID GOLD_ID ENGRAVING_ID FOUNTAIN_ID POOL_ID SINK_ID NONE
%token	<i> RAND_CORRIDOR_ID DOOR_STATE LIGHT_STATE CURSE_TYPE ENGRAVING_TYPE
%token	<i> DIRECTION RANDOM_TYPE RANDOM_TYPE_BRACKET A_REGISTER
%token	<i> ALIGNMENT LEFT_OR_RIGHT CENTER TOP_OR_BOT ALTAR_TYPE UP_OR_DOWN
%token	<i> SUBROOM_ID NAME_ID FLAGS_ID FLAG_TYPE MON_ATTITUDE MON_ALERTNESS
%token	<i> MON_APPEARANCE ROOMDOOR_ID IF_ID ELSE_ID
%token	<i> TERRAIN_ID HORIZ_OR_VERT REPLACE_TERRAIN_ID
%token	<i> EXIT_ID SHUFFLE_ID
%token	<i> QUANTITY_ID BURIED_ID LOOP_ID
%token	<i> FOR_ID TO_ID
%token	<i> SWITCH_ID CASE_ID BREAK_ID DEFAULT_ID
%token	<i> ERODED_ID TRAPPED_STATE RECHARGED_ID INVIS_ID GREASED_ID
%token	<i> FEMALE_ID CANCELLED_ID REVIVED_ID AVENGE_ID FLEEING_ID BLINDED_ID
%token	<i> PARALYZED_ID STUNNED_ID CONFUSED_ID SEENTRAPS_ID ALL_ID
%token	<i> MONTYPE_ID
%token	<i> GRAVE_ID ERODEPROOF_ID
%token	<i> FUNCTION_ID
%token	<i> MSG_OUTPUT_TYPE
%token	<i> COMPARE_TYPE
%token  <i> UNKNOWN_TYPE
%token	<i> rect_ID fillrect_ID line_ID randline_ID grow_ID selection_ID flood_ID
%token	<i> rndcoord_ID circle_ID ellipse_ID filter_ID complement_ID
%token	<i> gradient_ID GRADIENT_TYPE LIMITED HUMIDITY_TYPE
%token	<i> ',' ':' '(' ')' '[' ']' '{' '}'
%token	<map> STRING MAP_ID
%token	<map> NQSTRING VARSTRING
%token	<map> CFUNC CFUNC_INT CFUNC_STR CFUNC_COORD CFUNC_REGION
%token	<map> VARSTRING_INT VARSTRING_INT_ARRAY
%token	<map> VARSTRING_STRING VARSTRING_STRING_ARRAY
%token	<map> VARSTRING_VAR VARSTRING_VAR_ARRAY
%token	<map> VARSTRING_COORD VARSTRING_COORD_ARRAY
%token	<map> VARSTRING_REGION VARSTRING_REGION_ARRAY
%token	<map> VARSTRING_MAPCHAR VARSTRING_MAPCHAR_ARRAY
%token	<map> VARSTRING_MONST VARSTRING_MONST_ARRAY
%token	<map> VARSTRING_OBJ VARSTRING_OBJ_ARRAY
%token	<map> VARSTRING_SEL VARSTRING_SEL_ARRAY
%token	<meth> METHOD_INT METHOD_INT_ARRAY
%token	<meth> METHOD_STRING METHOD_STRING_ARRAY
%token	<meth> METHOD_VAR METHOD_VAR_ARRAY
%token	<meth> METHOD_COORD METHOD_COORD_ARRAY
%token	<meth> METHOD_REGION METHOD_REGION_ARRAY
%token	<meth> METHOD_MAPCHAR METHOD_MAPCHAR_ARRAY
%token	<meth> METHOD_MONST METHOD_MONST_ARRAY
%token	<meth> METHOD_OBJ METHOD_OBJ_ARRAY
%token	<meth> METHOD_SEL METHOD_SEL_ARRAY
%token	<dice> DICE
%type	<i> h_justif v_justif trap_name room_type door_state light_state
%type	<i> alignment altar_type a_register roomfill door_pos
%type	<i> alignment_prfx
%type	<i> door_wall walled secret
%type	<i> dir_list teleprt_detail
%type	<i> object_infos object_info monster_infos monster_info
%type	<i> levstatements stmt_block region_detail_end
%type	<i> engraving_type flag_list roomregionflag roomregionflags optroomregionflags
%type	<i> humidity_flags
%type	<i> comparestmt encodecoord encoderegion mapchar
%type	<i> seen_trap_mask
%type	<i> encodemonster encodeobj encodeobj_list
%type	<i> integer_list string_list encodecoord_list encoderegion_list mapchar_list encodemonster_list
%type	<i> opt_percent opt_fillchar
%type	<i> all_integers
%type	<i> ter_selection ter_selection_x
%type	<i> func_param_type
%type	<i> objectid monsterid terrainid
%type	<i> opt_coord_or_var opt_limited
%type   <i> mazefiller
%type	<map> level_def
%type	<map> any_var any_var_array any_var_or_arr any_var_or_unk
%type	<map> func_call_params_list func_call_param_list
%type	<i> func_call_param_part
%type	<corpos> corr_spec
%type	<lregn> region lev_region
%type	<crd> room_pos subroom_pos room_align
%type	<sze> room_size
%type	<terr> terrain_type
%left  '+' '-'
%left  '*' '/' '%'
%start	file

%%
file		: /* nothing */
		| levels
		;

levels		: level
		| level levels
		;

level		: level_def flags levstatements
		  {
			if (fatal_error > 0) {
				(void) fprintf(stderr,
				"%s: %d errors detected for level \"%s\". No output created!\n",
					       fname, fatal_error, $1);
				fatal_error = 0;
				got_errors++;
			} else if (!got_errors) {
				if (!write_level_file($1, splev)) {
				    lc_error("Can't write output file for '%s'!", $1);
				    exit(EXIT_FAILURE);
				}
			}
			Free($1);
			Free(splev);
			splev = NULL;
			vardef_free_all(variable_definitions);
			variable_definitions = NULL;
		  }
		;

level_def	: LEVEL_ID ':' STRING
		  {
		      start_level_def(&splev, $3);
		      $$ = $3;
		  }
		| MAZE_ID ':' STRING ',' mazefiller
		  {
		      start_level_def(&splev, $3);
		      if ($5 == -1) {
			  add_opvars(splev, "iiiiiiiio",
				     VA_PASS9(LVLINIT_MAZEGRID,HWALL,0,0,
					      0,0,0,0, SPO_INITLEVEL));
		      } else {
			  long bg = what_map_char((char) $5);
			  add_opvars(splev, "iiiiiiiio",
				     VA_PASS9(LVLINIT_SOLIDFILL, bg, 0,0,
					      0,0,0,0, SPO_INITLEVEL));
		      }
		      add_opvars(splev, "io",
				 VA_PASS2(MAZELEVEL, SPO_LEVEL_FLAGS));
		      max_x_map = COLNO-1;
		      max_y_map = ROWNO;
		      $$ = $3;
		  }
		;

mazefiller	: RANDOM_TYPE
		  {
		      $$ = -1;
		  }
		| CHAR
		  {
		      $$ = what_map_char((char) $1);
		  }
		;

lev_init	: LEV_INIT_ID ':' SOLID_FILL_ID ',' terrain_type
		  {
		      long filling = $5.ter;
		      if (filling == INVALID_TYPE || filling >= MAX_TYPE)
			  lc_error("INIT_MAP: Invalid fill char type.");
		      add_opvars(splev, "iiiiiiiio",
				 LVLINIT_SOLIDFILL,filling,0,(long)$5.lit, 0,0,0,0, SPO_INITLEVEL);
		      max_x_map = COLNO-1;
		      max_y_map = ROWNO;
		  }
		| LEV_INIT_ID ':' MAZE_GRID_ID ',' CHAR
		  {
		      long filling = what_map_char((char) $5);
		      if (filling == INVALID_TYPE || filling >= MAX_TYPE)
			  lc_error("INIT_MAP: Invalid fill char type.");
		      add_opvars(splev, "iiiiiiiio",
				 VA_PASS9(LVLINIT_MAZEGRID,filling,0,0,
					  0,0,0,0, SPO_INITLEVEL));
		      max_x_map = COLNO-1;
		      max_y_map = ROWNO;
		  }
		| LEV_INIT_ID ':' ROGUELEV_ID
		  {
		      add_opvars(splev, "iiiiiiiio",
				 VA_PASS9(LVLINIT_ROGUE,0,0,0,
					  0,0,0,0, SPO_INITLEVEL));
		  }
		| LEV_INIT_ID ':' MINES_ID ',' CHAR ',' CHAR ',' BOOLEAN ',' BOOLEAN ',' light_state ',' walled opt_fillchar
		  {
		      long fg = what_map_char((char) $5);
		      long bg = what_map_char((char) $7);
		      long smoothed = $9;
		      long joined = $11;
		      long lit = $13;
		      long walled = $15;
		      long filling = $16;
		      if (fg == INVALID_TYPE || fg >= MAX_TYPE)
			  lc_error("INIT_MAP: Invalid foreground type.");
		      if (bg == INVALID_TYPE || bg >= MAX_TYPE)
			  lc_error("INIT_MAP: Invalid background type.");
		      if (joined && fg != CORR && fg != ROOM)
			  lc_error("INIT_MAP: Invalid foreground type for joined map.");

		      if (filling == INVALID_TYPE)
			  lc_error("INIT_MAP: Invalid fill char type.");

		      add_opvars(splev, "iiiiiiiio",
				 VA_PASS9(LVLINIT_MINES,filling,walled,lit,
					  joined,smoothed,bg,fg,
					  SPO_INITLEVEL));
			max_x_map = COLNO-1;
			max_y_map = ROWNO;
		  }
		;

opt_limited	: /* nothing */
		  {
		      $$ = 0;
		  }
		| ',' LIMITED
		  {
		      $$ = $2;
		  }
		;

opt_coord_or_var	: /* nothing */
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_COPY));
		      $$ = 0;
		  }
		| ',' coord_or_var
		  {
		      $$ = 1;
		  }
		;

opt_fillchar	: /* nothing */
		  {
		      $$ = -1;
		  }
		| ',' CHAR
		  {
		      $$ = what_map_char((char) $2);
		  }
		;


walled		: BOOLEAN
		| RANDOM_TYPE
		;

flags		: /* nothing */
		  {
		      add_opvars(splev, "io", VA_PASS2(0, SPO_LEVEL_FLAGS));
		  }
		| FLAGS_ID ':' flag_list
		  {
		      add_opvars(splev, "io", VA_PASS2($3, SPO_LEVEL_FLAGS));
		  }
		;

flag_list	: FLAG_TYPE ',' flag_list
		  {
		      $$ = ($1 | $3);
		  }
		| FLAG_TYPE
		  {
		      $$ = $1;
		  }
		;

levstatements	: /* nothing */
		  {
		      $$ = 0;
		  }
		| levstatement levstatements
		  {
		      $$ = 1 + $2;
		  }
		;

stmt_block	: '{' levstatements '}'
		  {
		      $$ = $2;
		  }
		;

levstatement 	: message
		| lev_init
		| altar_detail
		| grave_detail
		| branch_region
		| corridor
		| variable_define
		| shuffle_detail
		| diggable_detail
		| door_detail
		| drawbridge_detail
		| engraving_detail
		| mineralize
		| fountain_detail
		| gold_detail
		| switchstatement
		| forstatement
		| loopstatement
		| ifstatement
		| chancestatement
		| exitstatement
		| breakstatement
		| function_define
		| function_call
		| ladder_detail
		| map_definition
		| mazewalk_detail
		| monster_detail
		| object_detail
		| passwall_detail
		| pool_detail
		| portal_region
		| random_corridors
		| region_detail
		| room_def
		| subroom_def
		| sink_detail
		| terrain_detail
		| replace_terrain_detail
		| stair_detail
		| stair_region
		| teleprt_region
		| trap_detail
		| wallify_detail
		;

any_var_array	: VARSTRING_INT_ARRAY
		| VARSTRING_STRING_ARRAY
		| VARSTRING_VAR_ARRAY
		| VARSTRING_COORD_ARRAY
		| VARSTRING_REGION_ARRAY
		| VARSTRING_MAPCHAR_ARRAY
		| VARSTRING_MONST_ARRAY
		| VARSTRING_OBJ_ARRAY
		| VARSTRING_SEL_ARRAY
		;

any_var		: VARSTRING_INT
		| VARSTRING_STRING
		| VARSTRING_VAR
		| VARSTRING_COORD
		| VARSTRING_REGION
		| VARSTRING_MAPCHAR
		| VARSTRING_MONST
		| VARSTRING_OBJ
		| VARSTRING_SEL
		;

any_var_or_arr	: any_var_array
		| any_var
		| VARSTRING
		;

any_var_or_unk	: VARSTRING
		| any_var
		;

shuffle_detail	: SHUFFLE_ID ':' any_var_array
		  {
		      struct lc_vardefs *vd;
		      if ((vd = vardef_defined(variable_definitions, $3, 1))) {
			  if (!(vd->var_type & SPOVAR_ARRAY))
			      lc_error("Trying to shuffle non-array variable '%s'", $3);
		      } else lc_error("Trying to shuffle undefined variable '%s'", $3);
		      add_opvars(splev, "so", VA_PASS2($3, SPO_SHUFFLE_ARRAY));
		      Free($3);
		  }
		;

variable_define	: any_var_or_arr '=' math_expr_var
		  {
		      variable_definitions = add_vardef_type(variable_definitions, $1, SPOVAR_INT);
		      add_opvars(splev, "iso", VA_PASS3(0, $1, SPO_VAR_INIT));
		      Free($1);
		  }
		| any_var_or_arr '=' selection_ID ':' ter_selection
		  {
		      variable_definitions = add_vardef_type(variable_definitions, $1, SPOVAR_SEL);
		      add_opvars(splev, "iso", VA_PASS3(0, $1, SPO_VAR_INIT));
		      Free($1);
		  }
		| any_var_or_arr '=' string_expr
		  {
		      variable_definitions = add_vardef_type(variable_definitions, $1, SPOVAR_STRING);
		      add_opvars(splev, "iso", VA_PASS3(0, $1, SPO_VAR_INIT));
		      Free($1);
		  }
		| any_var_or_arr '=' terrainid ':' mapchar_or_var
		  {
		      variable_definitions = add_vardef_type(variable_definitions, $1, SPOVAR_MAPCHAR);
		      add_opvars(splev, "iso", VA_PASS3(0, $1, SPO_VAR_INIT));
		      Free($1);
		  }
		| any_var_or_arr '=' monsterid ':' monster_or_var
		  {
		      variable_definitions = add_vardef_type(variable_definitions, $1, SPOVAR_MONST);
		      add_opvars(splev, "iso", VA_PASS3(0, $1, SPO_VAR_INIT));
		      Free($1);
		  }
		| any_var_or_arr '=' objectid ':' object_or_var
		  {
		      variable_definitions = add_vardef_type(variable_definitions, $1, SPOVAR_OBJ);
		      add_opvars(splev, "iso", VA_PASS3(0, $1, SPO_VAR_INIT));
		      Free($1);
		  }
		| any_var_or_arr '=' coord_or_var
		  {
		      variable_definitions = add_vardef_type(variable_definitions, $1, SPOVAR_COORD);
		      add_opvars(splev, "iso", VA_PASS3(0, $1, SPO_VAR_INIT));
		      Free($1);
		  }
		| any_var_or_arr '=' region_or_var
		  {
		      variable_definitions = add_vardef_type(variable_definitions, $1, SPOVAR_REGION);
		      add_opvars(splev, "iso", VA_PASS3(0, $1, SPO_VAR_INIT));
		      Free($1);
		  }
		| any_var_or_arr '=' '{' integer_list '}'
		  {
		      long n_items = $4;
		      variable_definitions = add_vardef_type(variable_definitions, $1, SPOVAR_INT|SPOVAR_ARRAY);
		      add_opvars(splev, "iso",
				 VA_PASS3(n_items, $1, SPO_VAR_INIT));
		      Free($1);
		  }
		| any_var_or_arr '=' '{' encodecoord_list '}'
		  {
		      long n_items = $4;
		      variable_definitions = add_vardef_type(variable_definitions, $1, SPOVAR_COORD|SPOVAR_ARRAY);
		      add_opvars(splev, "iso",
				 VA_PASS3(n_items, $1, SPO_VAR_INIT));
		      Free($1);
		  }
		| any_var_or_arr '=' '{' encoderegion_list '}'
		  {
		      long n_items = $4;
		      variable_definitions = add_vardef_type(variable_definitions, $1, SPOVAR_REGION|SPOVAR_ARRAY);
		      add_opvars(splev, "iso",
				 VA_PASS3(n_items, $1, SPO_VAR_INIT));
		      Free($1);
		  }
		| any_var_or_arr '=' terrainid ':' '{' mapchar_list '}'
		  {
		      long n_items = $6;
		      variable_definitions = add_vardef_type(variable_definitions, $1, SPOVAR_MAPCHAR|SPOVAR_ARRAY);
		      add_opvars(splev, "iso",
				 VA_PASS3(n_items, $1, SPO_VAR_INIT));
		      Free($1);
		  }
		| any_var_or_arr '=' monsterid ':' '{' encodemonster_list '}'
		  {
		      long n_items = $6;
		      variable_definitions = add_vardef_type(variable_definitions, $1, SPOVAR_MONST|SPOVAR_ARRAY);
		      add_opvars(splev, "iso",
				 VA_PASS3(n_items, $1, SPO_VAR_INIT));
		      Free($1);
		  }
		| any_var_or_arr '=' objectid ':' '{' encodeobj_list '}'
		  {
		      long n_items = $6;
		      variable_definitions = add_vardef_type(variable_definitions, $1, SPOVAR_OBJ|SPOVAR_ARRAY);
		      add_opvars(splev, "iso",
				 VA_PASS3(n_items, $1, SPO_VAR_INIT));
		      Free($1);
		  }
		| any_var_or_arr '=' '{' string_list '}'
		  {
		      long n_items = $4;
		      variable_definitions = add_vardef_type(variable_definitions, $1, SPOVAR_STRING|SPOVAR_ARRAY);
		      add_opvars(splev, "iso",
				 VA_PASS3(n_items, $1, SPO_VAR_INIT));
		      Free($1);
		  }
		;

encodeobj_list	: encodeobj
		  {
		      add_opvars(splev, "O", VA_PASS1($1));
		      $$ = 1;
		  }
		| encodeobj_list ',' encodeobj
		  {
		      add_opvars(splev, "O", VA_PASS1($3));
		      $$ = 1 + $1;
		  }
		;

encodemonster_list	: encodemonster
		  {
		      add_opvars(splev, "M", VA_PASS1($1));
		      $$ = 1;
		  }
		| encodemonster_list ',' encodemonster
		  {
		      add_opvars(splev, "M", VA_PASS1($3));
		      $$ = 1 + $1;
		  }
		;

mapchar_list	: mapchar
		  {
		      add_opvars(splev, "m", VA_PASS1($1));
		      $$ = 1;
		  }
		| mapchar_list ',' mapchar
		  {
		      add_opvars(splev, "m", VA_PASS1($3));
		      $$ = 1 + $1;
		  }
		;

encoderegion_list	: encoderegion
		  {
		      $$ = 1;
		  }
		| encoderegion_list ',' encoderegion
		  {
		      $$ = 1 + $1;
		  }
		;

encodecoord_list	: encodecoord
		  {
		      add_opvars(splev, "c", VA_PASS1($1));
		      $$ = 1;
		  }
		| encodecoord_list ',' encodecoord
		  {
		      add_opvars(splev, "c", VA_PASS1($3));
		      $$ = 1 + $1;
		  }
		;

integer_list	: math_expr_var
		  {
		      $$ = 1;
		  }
		| integer_list ',' math_expr_var
		  {
		      $$ = 1 + $1;
		  }
		;

string_list	: string_expr
		  {
		      $$ = 1;
		  }
		| string_list ',' string_expr
		  {
		      $$ = 1 + $1;
		  }
		;

function_define	: FUNCTION_ID NQSTRING '('
		  {
		      struct lc_funcdefs *funcdef;

		      if (in_function_definition)
			  lc_error("Recursively defined functions not allowed (function %s).", $2);

		      in_function_definition++;

		      if (funcdef_defined(function_definitions, $2, 1))
			  lc_error("Function '%s' already defined once.", $2);

		      funcdef = funcdef_new(-1, $2);
		      funcdef->next = function_definitions;
		      function_definitions = funcdef;
		      function_splev_backup = splev;
		      splev = &(funcdef->code);
		      Free($2);
		      curr_function = funcdef;
		      function_tmp_var_defs = variable_definitions;
		      variable_definitions = NULL;
		  }
		func_params_list ')'
		  {
		      /* nothing */
		  }
		stmt_block
		  {
		      add_opvars(splev, "io", VA_PASS2(0, SPO_RETURN));
		      splev = function_splev_backup;
		      in_function_definition--;
		      curr_function = NULL;
		      vardef_free_all(variable_definitions);
		      variable_definitions = function_tmp_var_defs;
		  }
		;

function_call	: NQSTRING '(' func_call_params_list ')'
		  {
		      struct lc_funcdefs *tmpfunc;
		      tmpfunc = funcdef_defined(function_definitions, $1, 1);
		      if (tmpfunc) {
			  long l;
			  long nparams = strlen( $3 );
			  char *fparamstr = funcdef_paramtypes(tmpfunc);
			  if (strcmp($3, fparamstr)) {
			      char *tmps = strdup(decode_parm_str(fparamstr));
			      lc_error("Function '%s' requires params '%s', got '%s' instead.", $1, tmps, decode_parm_str($3));
			      Free(tmps);
			  }
			  Free(fparamstr);
			  Free($3);
			  if (!(tmpfunc->n_called)) {
			      /* we haven't called the function yet, so insert it in the code */
			      struct opvar *jmp = New(struct opvar);
			      set_opvar_int(jmp, splev->n_opcodes+1);
			      add_opcode(splev, SPO_PUSH, jmp);
			      add_opcode(splev, SPO_JMP, NULL); /* we must jump past it first, then CALL it, due to RETURN. */

			      tmpfunc->addr = splev->n_opcodes;

			      { /* init function parameter variables */
				  struct lc_funcdefs_parm *tfp = tmpfunc->params;
				  while (tfp) {
				      add_opvars(splev, "iso",
						 VA_PASS3(0, tfp->name,
							  SPO_VAR_INIT));
				      tfp = tfp->next;
				  }
			      }

			      splev_add_from(splev, &(tmpfunc->code));
			      set_opvar_int(jmp, splev->n_opcodes - jmp->vardata.l);
			  }
			  l = tmpfunc->addr - splev->n_opcodes - 2;
			  add_opvars(splev, "iio",
				     VA_PASS3(nparams, l, SPO_CALL));
			  tmpfunc->n_called++;
		      } else {
			  lc_error("Function '%s' not defined.", $1);
		      }
		      Free($1);
		  }
		;

exitstatement	: EXIT_ID
		  {
		      add_opcode(splev, SPO_EXIT, NULL);
		  }
		;

opt_percent	: /* nothing */
		  {
		      $$ = 100;
		  }
		| PERCENT
		  {
		      $$ = $1;
		  }
		;

comparestmt     : PERCENT
                  {
		      /* val > rn2(100) */
		      add_opvars(splev, "iio",
				 VA_PASS3((long)$1, 100, SPO_RN2));
		      $$ = SPO_JG;
                  }
		| '[' math_expr_var COMPARE_TYPE math_expr_var ']'
                  {
		      $$ = $3;
                  }
		| '[' math_expr_var ']'
                  {
		      /* boolean, explicit foo != 0 */
		      add_opvars(splev, "i", VA_PASS1(0));
		      $$ = SPO_JNE;
                  }
		;

switchstatement	: SWITCH_ID
		  {
		      is_inconstant_number = 0;
		  }
		  '[' integer_or_var ']'
		  {
		      struct opvar *chkjmp;
		      if (in_switch_statement > 0)
			  lc_error("Cannot nest switch-statements.");

		      in_switch_statement++;

		      n_switch_case_list = 0;
		      switch_default_case = NULL;

		      if (!is_inconstant_number)
			  add_opvars(splev, "o", VA_PASS1(SPO_RN2));
		      is_inconstant_number = 0;

		      chkjmp = New(struct opvar);
		      set_opvar_int(chkjmp, splev->n_opcodes+1);
		      switch_check_jump = chkjmp;
		      add_opcode(splev, SPO_PUSH, chkjmp);
		      add_opcode(splev, SPO_JMP, NULL);
		      break_stmt_start();
		  }
		'{' switchcases '}'
		  {
		      struct opvar *endjump = New(struct opvar);
		      int i;

		      set_opvar_int(endjump, splev->n_opcodes+1);

		      add_opcode(splev, SPO_PUSH, endjump);
		      add_opcode(splev, SPO_JMP, NULL);

		      set_opvar_int(switch_check_jump,
			     splev->n_opcodes - switch_check_jump->vardata.l);

		      for (i = 0; i < n_switch_case_list; i++) {
			  add_opvars(splev, "oio",
				     VA_PASS3(SPO_COPY,
					      switch_case_value[i], SPO_CMP));
			  set_opvar_int(switch_case_list[i],
			 switch_case_list[i]->vardata.l - splev->n_opcodes-1);
			  add_opcode(splev, SPO_PUSH, switch_case_list[i]);
			  add_opcode(splev, SPO_JE, NULL);
		      }

		      if (switch_default_case) {
			  set_opvar_int(switch_default_case,
			 switch_default_case->vardata.l - splev->n_opcodes-1);
			  add_opcode(splev, SPO_PUSH, switch_default_case);
			  add_opcode(splev, SPO_JMP, NULL);
		      }

		      set_opvar_int(endjump, splev->n_opcodes - endjump->vardata.l);

		      break_stmt_end(splev);

		      add_opcode(splev, SPO_POP, NULL); /* get rid of the value in stack */
		      in_switch_statement--;


		  }
		;

switchcases	: /* nothing */
		| switchcase switchcases
		;

switchcase	: CASE_ID all_integers ':'
		  {
		      if (n_switch_case_list < MAX_SWITCH_CASES) {
			  struct opvar *tmppush = New(struct opvar);
			  set_opvar_int(tmppush, splev->n_opcodes);
			  switch_case_value[n_switch_case_list] = $2;
			  switch_case_list[n_switch_case_list++] = tmppush;
		      } else lc_error("Too many cases in a switch.");
		  }
		levstatements
		  {
		  }
		| DEFAULT_ID ':'
		  {
		      struct opvar *tmppush = New(struct opvar);

		      if (switch_default_case)
			  lc_error("Switch default case already used.");

		      set_opvar_int(tmppush, splev->n_opcodes);
		      switch_default_case = tmppush;
		  }
		levstatements
		  {
		  }
		;

breakstatement	: BREAK_ID
		  {
		      if (!allow_break_statements)
			  lc_error("Cannot use BREAK outside a statement block.");
		      else {
			  break_stmt_new(splev, splev->n_opcodes);
		      }
		  }
		;

for_to_span	: '.' '.'
		| TO_ID
		;

forstmt_start	: FOR_ID any_var_or_unk '=' math_expr_var for_to_span math_expr_var
		  {
		      char buf[256], buf2[256];

		      if (n_forloops >= MAX_NESTED_IFS) {
			  lc_error("FOR: Too deeply nested loops.");
			  n_forloops = MAX_NESTED_IFS - 1;
		      }

		      /* first, define a variable for the for-loop end value */
		      Sprintf(buf, "%s end", $2);
		      /* the value of which is already in stack (the 2nd math_expr) */
		      add_opvars(splev, "iso", VA_PASS3(0, buf, SPO_VAR_INIT));

		      variable_definitions = add_vardef_type(variable_definitions,
							     $2, SPOVAR_INT);
		      /* define the for-loop variable. value is in stack (1st math_expr) */
		      add_opvars(splev, "iso", VA_PASS3(0, $2, SPO_VAR_INIT));

		      /* calculate value for the loop "step" variable */
		      Sprintf(buf2, "%s step", $2);
		      /* end - start */
		      add_opvars(splev, "vvo",
				 VA_PASS3(buf, $2, SPO_MATH_SUB));
		      /* sign of that */
		      add_opvars(splev, "o", VA_PASS1(SPO_MATH_SIGN));
		      /* save the sign into the step var */
		      add_opvars(splev, "iso",
				 VA_PASS3(0, buf2, SPO_VAR_INIT));

		      forloop_list[n_forloops].varname = strdup($2);
		      forloop_list[n_forloops].jmp_point = splev->n_opcodes;

		      n_forloops++;
		      Free($2);
		  }
		;

forstatement	: forstmt_start
		  {
		      /* nothing */
		      break_stmt_start();
		  }
		 stmt_block
		  {
		      char buf[256], buf2[256];

		      n_forloops--;
		      Sprintf(buf, "%s step", forloop_list[n_forloops].varname);
		      Sprintf(buf2, "%s end", forloop_list[n_forloops].varname);
		      /* compare for-loop var to end value */
		      add_opvars(splev, "vvo",
				 VA_PASS3(forloop_list[n_forloops].varname,
					  buf2, SPO_CMP));
		      /* var + step */
		      add_opvars(splev, "vvo",
				VA_PASS3(buf, forloop_list[n_forloops].varname,
					 SPO_MATH_ADD));
		      /* for-loop var = (for-loop var + step) */
		      add_opvars(splev, "iso",
				 VA_PASS3(0, forloop_list[n_forloops].varname,
					  SPO_VAR_INIT));
		      /* jump back if compared values were not equal */
		      add_opvars(splev, "io",
				 VA_PASS2(
		    forloop_list[n_forloops].jmp_point - splev->n_opcodes - 1,
					  SPO_JNE));
		      Free(forloop_list[n_forloops].varname);
		      break_stmt_end(splev);
		  }
		;

loopstatement	: LOOP_ID '[' integer_or_var ']'
		  {
		      struct opvar *tmppush = New(struct opvar);

		      if (n_if_list >= MAX_NESTED_IFS) {
			  lc_error("LOOP: Too deeply nested conditionals.");
			  n_if_list = MAX_NESTED_IFS - 1;
		      }
		      set_opvar_int(tmppush, splev->n_opcodes);
		      if_list[n_if_list++] = tmppush;

		      add_opvars(splev, "o", VA_PASS1(SPO_DEC));
		      break_stmt_start();
		  }
		 stmt_block
		  {
		      struct opvar *tmppush;

		      add_opvars(splev, "oio", VA_PASS3(SPO_COPY, 0, SPO_CMP));

		      tmppush = (struct opvar *) if_list[--n_if_list];
		      set_opvar_int(tmppush, tmppush->vardata.l - splev->n_opcodes-1);
		      add_opcode(splev, SPO_PUSH, tmppush);
		      add_opcode(splev, SPO_JG, NULL);
		      add_opcode(splev, SPO_POP, NULL); /* get rid of the count value in stack */
		      break_stmt_end(splev);
		  }
		;

chancestatement	: comparestmt ':'
		  {
		      struct opvar *tmppush2 = New(struct opvar);

		      if (n_if_list >= MAX_NESTED_IFS) {
			  lc_error("IF: Too deeply nested conditionals.");
			  n_if_list = MAX_NESTED_IFS - 1;
		      }

		      add_opcode(splev, SPO_CMP, NULL);

		      set_opvar_int(tmppush2, splev->n_opcodes+1);

		      if_list[n_if_list++] = tmppush2;

		      add_opcode(splev, SPO_PUSH, tmppush2);

		      add_opcode(splev, reverse_jmp_opcode( $1 ), NULL);

		  }
		levstatement
		  {
		      if (n_if_list > 0) {
			  struct opvar *tmppush;
			  tmppush = (struct opvar *) if_list[--n_if_list];
			  set_opvar_int(tmppush, splev->n_opcodes - tmppush->vardata.l);
		      } else lc_error("IF: Huh?!  No start address?");
		  }
		;

ifstatement 	: IF_ID comparestmt
		  {
		      struct opvar *tmppush2 = New(struct opvar);

		      if (n_if_list >= MAX_NESTED_IFS) {
			  lc_error("IF: Too deeply nested conditionals.");
			  n_if_list = MAX_NESTED_IFS - 1;
		      }

		      add_opcode(splev, SPO_CMP, NULL);

		      set_opvar_int(tmppush2, splev->n_opcodes+1);

		      if_list[n_if_list++] = tmppush2;

		      add_opcode(splev, SPO_PUSH, tmppush2);

		      add_opcode(splev, reverse_jmp_opcode( $2 ), NULL);

		  }
		 if_ending
		  {
		     /* do nothing */
		  }
		;

if_ending	: stmt_block
		  {
		      if (n_if_list > 0) {
			  struct opvar *tmppush;
			  tmppush = (struct opvar *) if_list[--n_if_list];
			  set_opvar_int(tmppush, splev->n_opcodes - tmppush->vardata.l);
		      } else lc_error("IF: Huh?!  No start address?");
		  }
		| stmt_block
		  {
		      if (n_if_list > 0) {
			  struct opvar *tmppush = New(struct opvar);
			  struct opvar *tmppush2;

			  set_opvar_int(tmppush, splev->n_opcodes+1);
			  add_opcode(splev, SPO_PUSH, tmppush);

			  add_opcode(splev, SPO_JMP, NULL);

			  tmppush2 = (struct opvar *) if_list[--n_if_list];

			  set_opvar_int(tmppush2, splev->n_opcodes - tmppush2->vardata.l);
			  if_list[n_if_list++] = tmppush;
		      } else lc_error("IF: Huh?!  No else-part address?");
		  }
		 ELSE_ID stmt_block
		  {
		      if (n_if_list > 0) {
			  struct opvar *tmppush;
			  tmppush = (struct opvar *) if_list[--n_if_list];
			  set_opvar_int(tmppush, splev->n_opcodes - tmppush->vardata.l);
		      } else lc_error("IF: Huh?! No end address?");
		  }
		;

message		: MESSAGE_ID ':' string_expr
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_MESSAGE));
		  }
		;

random_corridors: RAND_CORRIDOR_ID
		  {
		      add_opvars(splev, "iiiiiio",
			      VA_PASS7(-1,  0, -1, -1, -1, -1, SPO_CORRIDOR));
		  }
		| RAND_CORRIDOR_ID ':' all_integers
		  {
		      add_opvars(splev, "iiiiiio",
			      VA_PASS7(-1, $3, -1, -1, -1, -1, SPO_CORRIDOR));
		  }
		| RAND_CORRIDOR_ID ':' RANDOM_TYPE
		  {
		      add_opvars(splev, "iiiiiio",
			      VA_PASS7(-1, -1, -1, -1, -1, -1, SPO_CORRIDOR));
		  }
		;

corridor	: CORRIDOR_ID ':' corr_spec ',' corr_spec
		  {
		      add_opvars(splev, "iiiiiio",
				 VA_PASS7($3.room, $3.door, $3.wall,
					  $5.room, $5.door, $5.wall,
					  SPO_CORRIDOR));
		  }
		| CORRIDOR_ID ':' corr_spec ',' all_integers
		  {
		      add_opvars(splev, "iiiiiio",
				 VA_PASS7($3.room, $3.door, $3.wall,
					  -1, -1, (long)$5,
					  SPO_CORRIDOR));
		  }
		;

corr_spec	: '(' INTEGER ',' DIRECTION ',' door_pos ')'
		  {
			$$.room = $2;
			$$.wall = $4;
			$$.door = $6;
		  }
		;

room_begin      : room_type opt_percent ',' light_state
                  {
		      if (($2 < 100) && ($1 == OROOM))
			  lc_error("Only typed rooms can have a chance.");
		      else {
			  add_opvars(splev, "iii",
				     VA_PASS3((long)$1, (long)$2, (long)$4));
		      }
                  }
                ;

subroom_def	: SUBROOM_ID ':' room_begin ',' subroom_pos ',' room_size optroomregionflags
		  {
		      long rflags = $8;

		      if (rflags == -1) rflags = (1 << 0);
		      add_opvars(splev, "iiiiiiio",
				 VA_PASS8(rflags, ERR, ERR,
					  $5.x, $5.y, $7.width, $7.height,
					  SPO_SUBROOM));
		      break_stmt_start();
		  }
		  stmt_block
		  {
		      break_stmt_end(splev);
		      add_opcode(splev, SPO_ENDROOM, NULL);
		  }
		;

room_def	: ROOM_ID ':' room_begin ',' room_pos ',' room_align ',' room_size optroomregionflags
		  {
		      long rflags = $8;

		      if (rflags == -1) rflags = (1 << 0);
		      add_opvars(splev, "iiiiiiio",
				 VA_PASS8(rflags,
					  $7.x, $7.y, $5.x, $5.y,
					  $9.width, $9.height, SPO_ROOM));
		      break_stmt_start();
		  }
		  stmt_block
		  {
		      break_stmt_end(splev);
		      add_opcode(splev, SPO_ENDROOM, NULL);
		  }
		;

roomfill	: /* nothing */
		  {
			$$ = 1;
		  }
		| ',' BOOLEAN
		  {
			$$ = $2;
		  }
		;

room_pos	: '(' INTEGER ',' INTEGER ')'
		  {
			if ( $2 < 1 || $2 > 5 ||
			    $4 < 1 || $4 > 5 ) {
			    lc_error("Room positions should be between 1-5: (%li,%li)!", $2, $4);
			} else {
			    $$.x = $2;
			    $$.y = $4;
			}
		  }
		| RANDOM_TYPE
		  {
			$$.x = $$.y = ERR;
		  }
		;

subroom_pos	: '(' INTEGER ',' INTEGER ')'
		  {
			if ( $2 < 0 || $4 < 0) {
			    lc_error("Invalid subroom position (%li,%li)!", $2, $4);
			} else {
			    $$.x = $2;
			    $$.y = $4;
			}
		  }
		| RANDOM_TYPE
		  {
			$$.x = $$.y = ERR;
		  }
		;

room_align	: '(' h_justif ',' v_justif ')'
		  {
		      $$.x = $2;
		      $$.y = $4;
		  }
		| RANDOM_TYPE
		  {
		      $$.x = $$.y = ERR;
		  }
		;

room_size	: '(' INTEGER ',' INTEGER ')'
		  {
			$$.width = $2;
			$$.height = $4;
		  }
		| RANDOM_TYPE
		  {
			$$.height = $$.width = ERR;
		  }
		;

door_detail	: ROOMDOOR_ID ':' secret ',' door_state ',' door_wall ',' door_pos
		  {
			/* ERR means random here */
			if ($7 == ERR && $9 != ERR) {
			    lc_error("If the door wall is random, so must be its pos!");
			} else {
			    add_opvars(splev, "iiiio",
				       VA_PASS5((long)$9, (long)$5, (long)$3,
						(long)$7, SPO_ROOM_DOOR));
			}
		  }
		| DOOR_ID ':' door_state ',' ter_selection
		  {
		      add_opvars(splev, "io", VA_PASS2((long)$3, SPO_DOOR));
		  }
		;

secret		: BOOLEAN
		| RANDOM_TYPE
		;

door_wall	: dir_list
		| RANDOM_TYPE
		;

dir_list	: DIRECTION
		  {
		      $$ = $1;
		  }
		| DIRECTION '|' dir_list
		  {
		      $$ = ($1 | $3);
		  }
		;

door_pos	: INTEGER
		| RANDOM_TYPE
		;

map_definition	: NOMAP_ID
		  {
		      add_opvars(splev, "ciisiio",
				 VA_PASS7(0, 0, 1, (char *)0, 0, 0, SPO_MAP));
		      max_x_map = COLNO-1;
		      max_y_map = ROWNO;
		  }
		| GEOMETRY_ID ':' h_justif ',' v_justif roomfill MAP_ID
		  {
		      add_opvars(splev, "cii",
				 VA_PASS3(SP_COORD_PACK(($3),($5)),
					  1, (long)$6));
		      scan_map($7, splev);
		      Free($7);
		  }
		| GEOMETRY_ID ':' coord_or_var roomfill MAP_ID
		  {
		      add_opvars(splev, "ii", VA_PASS2(2, (long)$4));
		      scan_map($5, splev);
		      Free($5);
		  }
		;

h_justif	: LEFT_OR_RIGHT
		| CENTER
		;

v_justif	: TOP_OR_BOT
		| CENTER
		;

monster_detail	: MONSTER_ID ':' monster_desc
		  {
		      add_opvars(splev, "io", VA_PASS2(0, SPO_MONSTER));
		  }
		| MONSTER_ID ':' monster_desc
		  {
		      add_opvars(splev, "io", VA_PASS2(1, SPO_MONSTER));
		      in_container_obj++;
		      break_stmt_start();
		  }
		stmt_block
		 {
		     break_stmt_end(splev);
		     in_container_obj--;
		     add_opvars(splev, "o", VA_PASS1(SPO_END_MONINVENT));
		 }
		;

monster_desc	: monster_or_var ',' coord_or_var monster_infos
		  {
		      /* nothing */
		  }
		;

monster_infos	: /* nothing */
		  {
		      struct opvar *stopit = New(struct opvar);
		      set_opvar_int(stopit, SP_M_V_END);
		      add_opcode(splev, SPO_PUSH, stopit);
		      $$ = 0x0000;
		  }
		| monster_infos ',' monster_info
		  {
		      if (( $1 & $3 ))
			  lc_error("MONSTER extra info defined twice.");
		      $$ = ( $1 | $3 );
		  }
		;

monster_info	: string_expr
		  {
		      add_opvars(splev, "i", VA_PASS1(SP_M_V_NAME));
		      $$ = 0x0001;
		  }
		| MON_ATTITUDE
		  {
		      add_opvars(splev, "ii",
				 VA_PASS2((long)$<i>1, SP_M_V_PEACEFUL));
		      $$ = 0x0002;
		  }
		| MON_ALERTNESS
		  {
		      add_opvars(splev, "ii",
				 VA_PASS2((long)$<i>1, SP_M_V_ASLEEP));
		      $$ = 0x0004;
		  }
		| alignment_prfx
		  {
		      add_opvars(splev, "ii",
				 VA_PASS2((long)$1, SP_M_V_ALIGN));
		      $$ = 0x0008;
		  }
		| MON_APPEARANCE string_expr
		  {
		      add_opvars(splev, "ii",
				 VA_PASS2((long)$<i>1, SP_M_V_APPEAR));
		      $$ = 0x0010;
		  }
		| FEMALE_ID
		  {
		      add_opvars(splev, "ii", VA_PASS2(1, SP_M_V_FEMALE));
		      $$ = 0x0020;
		  }
		| INVIS_ID
		  {
		      add_opvars(splev, "ii", VA_PASS2(1, SP_M_V_INVIS));
		      $$ = 0x0040;
		  }
		| CANCELLED_ID
		  {
		      add_opvars(splev, "ii", VA_PASS2(1, SP_M_V_CANCELLED));
		      $$ = 0x0080;
		  }
		| REVIVED_ID
		  {
		      add_opvars(splev, "ii", VA_PASS2(1, SP_M_V_REVIVED));
		      $$ = 0x0100;
		  }
		| AVENGE_ID
		  {
		      add_opvars(splev, "ii", VA_PASS2(1, SP_M_V_AVENGE));
		      $$ = 0x0200;
		  }
		| FLEEING_ID ':' integer_or_var
		  {
		      add_opvars(splev, "i", VA_PASS1(SP_M_V_FLEEING));
		      $$ = 0x0400;
		  }
		| BLINDED_ID ':' integer_or_var
		  {
		      add_opvars(splev, "i", VA_PASS1(SP_M_V_BLINDED));
		      $$ = 0x0800;
		  }
		| PARALYZED_ID ':' integer_or_var
		  {
		      add_opvars(splev, "i", VA_PASS1(SP_M_V_PARALYZED));
		      $$ = 0x1000;
		  }
		| STUNNED_ID
		  {
		      add_opvars(splev, "ii", VA_PASS2(1, SP_M_V_STUNNED));
		      $$ = 0x2000;
		  }
		| CONFUSED_ID
		  {
		      add_opvars(splev, "ii", VA_PASS2(1, SP_M_V_CONFUSED));
		      $$ = 0x4000;
		  }
		| SEENTRAPS_ID ':' seen_trap_mask
		  {
		      add_opvars(splev, "ii",
				 VA_PASS2((long)$3, SP_M_V_SEENTRAPS));
		      $$ = 0x8000;
		  }
		;

seen_trap_mask	: STRING
		  {
		      int token = get_trap_type($1);
		      if (token == ERR || token == 0)
			  lc_error("Unknown trap type '%s'!", $1);
                      Free($1);
		      $$ = (1L << (token - 1));
		  }
		| ALL_ID
		  {
		      $$ = (long) ~0;
		  }
		| STRING '|' seen_trap_mask
		  {
		      int token = get_trap_type($1);
		      if (token == ERR || token == 0)
			  lc_error("Unknown trap type '%s'!", $1);

		      if ((1L << (token - 1)) & $3)
			  lc_error("Monster seen_traps, trap '%s' listed twice.", $1);
                      Free($1);
		      $$ = ((1L << (token - 1)) | $3);
		  }
		;

object_detail	: OBJECT_ID ':' object_desc
		  {
		      long cnt = 0;
		      if (in_container_obj) cnt |= SP_OBJ_CONTENT;
		      add_opvars(splev, "io", VA_PASS2(cnt, SPO_OBJECT));
		  }
		| COBJECT_ID ':' object_desc
		  {
		      long cnt = SP_OBJ_CONTAINER;
		      if (in_container_obj) cnt |= SP_OBJ_CONTENT;
		      add_opvars(splev, "io", VA_PASS2(cnt, SPO_OBJECT));
		      in_container_obj++;
		      break_stmt_start();
		  }
		stmt_block
		 {
		     break_stmt_end(splev);
		     in_container_obj--;
		     add_opcode(splev, SPO_POP_CONTAINER, NULL);
		 }
		;

object_desc	: object_or_var object_infos
		  {
		      if (( $2 & 0x4000) && in_container_obj)
                          lc_error("Object cannot have a coord when contained.");
		      else if (!( $2 & 0x4000) && !in_container_obj)
                          lc_error("Object needs a coord when not contained.");
		  }
		;

object_infos	: /* nothing */
		  {
		      struct opvar *stopit = New(struct opvar);
		      set_opvar_int(stopit, SP_O_V_END);
		      add_opcode(splev, SPO_PUSH, stopit);
		      $$ = 0x00;
		  }
		| object_infos ',' object_info
		  {
		      if (( $1 & $3 ))
			  lc_error("OBJECT extra info '%s' defined twice.", curr_token);
		      $$ = ( $1 | $3 );
		  }
		;

object_info	: CURSE_TYPE
		  {
		      add_opvars(splev, "ii",
				 VA_PASS2((long)$1, SP_O_V_CURSE));
		      $$ = 0x0001;
		  }
		| MONTYPE_ID ':' monster_or_var
		  {
		      add_opvars(splev, "i", VA_PASS1(SP_O_V_CORPSENM));
		      $$ = 0x0002;
		  }
		| all_ints_push
		  {
		      add_opvars(splev, "i", VA_PASS1(SP_O_V_SPE));
		      $$ = 0x0004;
		  }
		| NAME_ID ':' string_expr
		  {
		      add_opvars(splev, "i", VA_PASS1(SP_O_V_NAME));
		      $$ = 0x0008;
		  }
		| QUANTITY_ID ':' integer_or_var
		  {
		      add_opvars(splev, "i", VA_PASS1(SP_O_V_QUAN));
		      $$ = 0x0010;
		  }
		| BURIED_ID
		  {
		      add_opvars(splev, "ii", VA_PASS2(1, SP_O_V_BURIED));
		      $$ = 0x0020;
		  }
		| LIGHT_STATE
		  {
		      add_opvars(splev, "ii", VA_PASS2((long)$1, SP_O_V_LIT));
		      $$ = 0x0040;
		  }
		| ERODED_ID ':' integer_or_var
		  {
		      add_opvars(splev, "i", VA_PASS1(SP_O_V_ERODED));
		      $$ = 0x0080;
		  }
		| ERODEPROOF_ID
		  {
		      add_opvars(splev, "ii", VA_PASS2(-1, SP_O_V_ERODED));
		      $$ = 0x0080;
		  }
		| DOOR_STATE
		  {
		      if ($1 == D_LOCKED) {
			  add_opvars(splev, "ii", VA_PASS2(1, SP_O_V_LOCKED));
			  $$ = 0x0100;
		      } else if ($1 == D_BROKEN) {
			  add_opvars(splev, "ii", VA_PASS2(1, SP_O_V_BROKEN));
			  $$ = 0x0200;
		      } else
			  lc_error("DOOR state can only be locked or broken.");
		  }
		| TRAPPED_STATE
		  {
		      add_opvars(splev, "ii", VA_PASS2($1, SP_O_V_TRAPPED));
		      $$ = 0x0400;
		  }
		| RECHARGED_ID ':' integer_or_var
		  {
		      add_opvars(splev, "i", VA_PASS1(SP_O_V_RECHARGED));
		      $$ = 0x0800;
		  }
		| INVIS_ID
		  {
		      add_opvars(splev, "ii", VA_PASS2(1, SP_O_V_INVIS));
		      $$ = 0x1000;
		  }
		| GREASED_ID
		  {
		      add_opvars(splev, "ii", VA_PASS2(1, SP_O_V_GREASED));
		      $$ = 0x2000;
		  }
		| coord_or_var
		  {
		      add_opvars(splev, "i", VA_PASS1(SP_O_V_COORD));
		      $$ = 0x4000;
		  }
		;

trap_detail	: TRAP_ID ':' trap_name ',' coord_or_var
		  {
		      add_opvars(splev, "io", VA_PASS2((long)$3, SPO_TRAP));
		  }
		;

drawbridge_detail: DRAWBRIDGE_ID ':' coord_or_var ',' DIRECTION ',' door_state
		   {
		       long dir, state = 0;

		       /* convert dir from a DIRECTION to a DB_DIR */
		       dir = $5;
		       switch (dir) {
		       case W_NORTH: dir = DB_NORTH; break;
		       case W_SOUTH: dir = DB_SOUTH; break;
		       case W_EAST:  dir = DB_EAST;  break;
		       case W_WEST:  dir = DB_WEST;  break;
		       default:
			   lc_error("Invalid drawbridge direction.");
			   break;
		       }

		       if ( $7 == D_ISOPEN )
			   state = 1;
		       else if ( $7 == D_CLOSED )
			   state = 0;
		       else if ( $7 == -1 )
			   state = -1;
		       else
			   lc_error("A drawbridge can only be open, closed or random!");
		       add_opvars(splev, "iio",
				  VA_PASS3(state, dir, SPO_DRAWBRIDGE));
		   }
		;

mazewalk_detail : MAZEWALK_ID ':' coord_or_var ',' DIRECTION
		  {
		      add_opvars(splev, "iiio",
				 VA_PASS4((long)$5, 1, 0, SPO_MAZEWALK));
		  }
		| MAZEWALK_ID ':' coord_or_var ',' DIRECTION ',' BOOLEAN opt_fillchar
		  {
		      add_opvars(splev, "iiio",
				 VA_PASS4((long)$5, (long)$<i>7,
					  (long)$8, SPO_MAZEWALK));
		  }
		;

wallify_detail	: WALLIFY_ID
		  {
		      add_opvars(splev, "rio",
				 VA_PASS3(SP_REGION_PACK(-1,-1,-1,-1),
					  0, SPO_WALLIFY));
		  }
		| WALLIFY_ID ':' ter_selection
		  {
		      add_opvars(splev, "io", VA_PASS2(1, SPO_WALLIFY));
		  }
		;

ladder_detail	: LADDER_ID ':' coord_or_var ',' UP_OR_DOWN
		  {
		      add_opvars(splev, "io",
				 VA_PASS2((long)$<i>5, SPO_LADDER));
		  }
		;

stair_detail	: STAIR_ID ':' coord_or_var ',' UP_OR_DOWN
		  {
		      add_opvars(splev, "io",
				 VA_PASS2((long)$<i>5, SPO_STAIR));
		  }
		;

stair_region	: STAIR_ID ':' lev_region ',' lev_region ',' UP_OR_DOWN
		  {
		      add_opvars(splev, "iiiii iiiii iiso",
				 VA_PASS14($3.x1, $3.y1, $3.x2, $3.y2, $3.area,
					   $5.x1, $5.y1, $5.x2, $5.y2, $5.area,
				     (long) (($7) ? LR_UPSTAIR : LR_DOWNSTAIR),
					   0, (char *) 0, SPO_LEVREGION));
		  }
		;

portal_region	: PORTAL_ID ':' lev_region ',' lev_region ',' STRING
		  {
		      add_opvars(splev, "iiiii iiiii iiso",
				 VA_PASS14($3.x1, $3.y1, $3.x2, $3.y2, $3.area,
					   $5.x1, $5.y1, $5.x2, $5.y2, $5.area,
					   LR_PORTAL, 0, $7, SPO_LEVREGION));
		      Free($7);
		  }
		;

teleprt_region	: TELEPRT_ID ':' lev_region ',' lev_region teleprt_detail
		  {
		      long rtyp = 0;
		      switch($6) {
		      case -1: rtyp = LR_TELE; break;
		      case  0: rtyp = LR_DOWNTELE; break;
		      case  1: rtyp = LR_UPTELE; break;
		      }
		      add_opvars(splev, "iiiii iiiii iiso",
				 VA_PASS14($3.x1, $3.y1, $3.x2, $3.y2, $3.area,
					   $5.x1, $5.y1, $5.x2, $5.y2, $5.area,
					   rtyp, 0, (char *)0, SPO_LEVREGION));
		  }
		;

branch_region	: BRANCH_ID ':' lev_region ',' lev_region
		  {
		      add_opvars(splev, "iiiii iiiii iiso",
				 VA_PASS14($3.x1, $3.y1, $3.x2, $3.y2, $3.area,
					   $5.x1, $5.y1, $5.x2, $5.y2, $5.area,
					   (long) LR_BRANCH, 0,
					   (char *) 0, SPO_LEVREGION));
		  }
		;

teleprt_detail	: /* empty */
		  {
			$$ = -1;
		  }
		| ',' UP_OR_DOWN
		  {
			$$ = $2;
		  }
		;

fountain_detail : FOUNTAIN_ID ':' ter_selection
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_FOUNTAIN));
		  }
		;

sink_detail : SINK_ID ':' ter_selection
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_SINK));
		  }
		;

pool_detail : POOL_ID ':' ter_selection
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_POOL));
		  }
		;

terrain_type	: CHAR
		  {
		      $$.lit = -2;
		      $$.ter = what_map_char((char) $<i>1);
		  }
		| '(' CHAR ',' light_state ')'
		  {
		      $$.lit = $4;
		      $$.ter = what_map_char((char) $<i>2);
		  }
		;

replace_terrain_detail : REPLACE_TERRAIN_ID ':' region_or_var ',' mapchar_or_var ',' mapchar_or_var ',' SPERCENT
		  {
		      add_opvars(splev, "io",
				 VA_PASS2($9, SPO_REPLACETERRAIN));
		  }
		;

terrain_detail : TERRAIN_ID ':' ter_selection ',' mapchar_or_var
		 {
		     add_opvars(splev, "o", VA_PASS1(SPO_TERRAIN));
		 }
	       ;

diggable_detail : NON_DIGGABLE_ID ':' region_or_var
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_NON_DIGGABLE));
		  }
		;

passwall_detail : NON_PASSWALL_ID ':' region_or_var
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_NON_PASSWALL));
		  }
		;

region_detail	: REGION_ID ':' region_or_var ',' light_state ',' room_type optroomregionflags
		  {
		      long irr;
		      long rt = $7;
		      long rflags = $8;

		      if (rflags == -1) rflags = (1 << 0);
		      if (!(rflags & 1)) rt += MAXRTYPE+1;
		      irr = ((rflags & 2) != 0);
		      add_opvars(splev, "iiio",
				 VA_PASS4((long)$5, rt, rflags, SPO_REGION));
		      $<i>$ = (irr || (rflags & 1) || rt != OROOM);
		      break_stmt_start();
		  }
		  region_detail_end
		  {
		      break_stmt_end(splev);
		      if ( $<i>9 ) {
			  add_opcode(splev, SPO_ENDROOM, NULL);
		      } else if ( $<i>10 )
			  lc_error("Cannot use lev statements in non-permanent REGION");
		  }
		;

region_detail_end : /* nothing */
		  {
		      $$ = 0;
		  }
		| stmt_block
		  {
		      $$ = $1;
		  }
		;

altar_detail	: ALTAR_ID ':' coord_or_var ',' alignment ',' altar_type
		  {
		      add_opvars(splev, "iio",
				 VA_PASS3((long)$7, (long)$5, SPO_ALTAR));
		  }
		;

grave_detail	: GRAVE_ID ':' coord_or_var ',' string_expr
		  {
		      add_opvars(splev, "io", VA_PASS2(2, SPO_GRAVE));
		  }
		| GRAVE_ID ':' coord_or_var ',' RANDOM_TYPE
		  {
		      add_opvars(splev, "sio",
				 VA_PASS3((char *)0, 1, SPO_GRAVE));
		  }
		| GRAVE_ID ':' coord_or_var
		  {
		      add_opvars(splev, "sio",
				 VA_PASS3((char *)0, 0, SPO_GRAVE));
		  }
		;

gold_detail	: GOLD_ID ':' math_expr_var ',' coord_or_var
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_GOLD));
		  }
		;

engraving_detail: ENGRAVING_ID ':' coord_or_var ',' engraving_type ',' string_expr
		  {
		      add_opvars(splev, "io",
				 VA_PASS2((long)$5, SPO_ENGRAVING));
		  }
		;

mineralize	: MINERALIZE_ID ':' integer_or_var ',' integer_or_var ',' integer_or_var ',' integer_or_var
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_MINERALIZE));
		  }
		| MINERALIZE_ID
		  {
		      add_opvars(splev, "iiiio",
				 VA_PASS5(-1L, -1L, -1L, -1L, SPO_MINERALIZE));
		  }
		;

trap_name	: STRING
		  {
			int token = get_trap_type($1);
			if (token == ERR)
			    lc_error("Unknown trap type '%s'!", $1);
			$$ = token;
			Free($1);
		  }
		| RANDOM_TYPE
		;

room_type	: STRING
		  {
			int token = get_room_type($1);
			if (token == ERR) {
			    lc_warning("Unknown room type \"%s\"!  Making ordinary room...", $1);
				$$ = OROOM;
			} else
				$$ = token;
			Free($1);
		  }
		| RANDOM_TYPE
		;

optroomregionflags : /* empty */
		  {
			$$ = -1;
		  }
		| ',' roomregionflags
		  {
			$$ = $2;
		  }
		;

roomregionflags : roomregionflag
		  {
			$$ = $1;
		  }
		| roomregionflag ',' roomregionflags
		  {
			$$ = $1 | $3;
		  }
		;

/* 0 is the "default" here */
roomregionflag : FILLING
		  {
		      $$ = ($1 << 0);
		  }
		| IRREGULAR
		  {
		      $$ = ($1 << 1);
		  }
		| JOINED
		  {
		      $$ = ($1 << 2);
		  }
		;

door_state	: DOOR_STATE
		| RANDOM_TYPE
		;

light_state	: LIGHT_STATE
		| RANDOM_TYPE
		;

alignment	: ALIGNMENT
		| a_register
		| RANDOM_TYPE
		  {
			$$ = - MAX_REGISTERS - 1;
		  }
		;

alignment_prfx	: ALIGNMENT
		| a_register
		| A_REGISTER ':' RANDOM_TYPE
		  {
			$$ = - MAX_REGISTERS - 1;
		  }
		;

altar_type	: ALTAR_TYPE
		| RANDOM_TYPE
		;

a_register	: A_REGISTER '[' INTEGER ']'
		  {
			if ( $3 >= 3 )
				lc_error("Register Index overflow!");
			else
				$$ = - $3 - 1;
		  }
		;

string_or_var	: STRING
		  {
		      add_opvars(splev, "s", VA_PASS1($1));
		      Free($1);
		  }
		| VARSTRING_STRING
		  {
		      check_vardef_type(variable_definitions, $1, SPOVAR_STRING);
		      vardef_used(variable_definitions, $1);
		      add_opvars(splev, "v", VA_PASS1($1));
		      Free($1);
		  }
		| VARSTRING_STRING_ARRAY '[' math_expr_var ']'
		  {
		      check_vardef_type(variable_definitions, $1, SPOVAR_STRING|SPOVAR_ARRAY);
		      vardef_used(variable_definitions, $1);
		      add_opvars(splev, "v", VA_PASS1($1));
		      Free($1);
		  }
		;


integer_or_var	: math_expr_var
		  {
		      /* nothing */
		  }
		;

coord_or_var	: encodecoord
		  {
		      add_opvars(splev, "c", VA_PASS1($1));
		  }
		| rndcoord_ID '(' ter_selection ')'
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_SEL_RNDCOORD));
		  }
		| VARSTRING_COORD
		  {
		      check_vardef_type(variable_definitions, $1, SPOVAR_COORD);
		      vardef_used(variable_definitions, $1);
		      add_opvars(splev, "v", VA_PASS1($1));
		      Free($1);
		  }
		| VARSTRING_COORD_ARRAY '[' math_expr_var ']'
		  {
		      check_vardef_type(variable_definitions, $1, SPOVAR_COORD|SPOVAR_ARRAY);
		      vardef_used(variable_definitions, $1);
		      add_opvars(splev, "v", VA_PASS1($1));
		      Free($1);
		  }
		;

encodecoord	: '(' INTEGER ',' INTEGER ')'
		  {
		      if ($2 < 0 || $4 < 0 || $2 >= COLNO || $4 >= ROWNO)
			  lc_error("Coordinates (%li,%li) out of map range!", $2, $4);
		      $$ = SP_COORD_PACK($2, $4);
		  }
		| RANDOM_TYPE
		  {
		      $$ = SP_COORD_PACK_RANDOM(0);
		  }
		| RANDOM_TYPE_BRACKET humidity_flags ']'
		  {
		      $$ = SP_COORD_PACK_RANDOM( $2 );
		  }
		;

humidity_flags	: HUMIDITY_TYPE
		  {
		      $$ = $1;
		  }
		| HUMIDITY_TYPE ',' humidity_flags
		  {
		      if (($1 & $3))
			  lc_warning("Humidity flag used twice.");
		      $$ = ($1 | $3);
		  }
		;

region_or_var	: encoderegion
		  {
		      /* nothing */
		  }
		| VARSTRING_REGION
		  {
		      check_vardef_type(variable_definitions, $1, SPOVAR_REGION);
		      vardef_used(variable_definitions, $1);
		      add_opvars(splev, "v", VA_PASS1($1));
		      Free($1);
		  }
		| VARSTRING_REGION_ARRAY '[' math_expr_var ']'
		  {
		      check_vardef_type(variable_definitions, $1, SPOVAR_REGION|SPOVAR_ARRAY);
		      vardef_used(variable_definitions, $1);
		      add_opvars(splev, "v", VA_PASS1($1));
		      Free($1);
		  }
		;

encoderegion	: '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')'
		  {
		      long r = SP_REGION_PACK($2, $4, $6, $8);
		      if ( $2 > $6 || $4 > $8 )
			  lc_error("Region start > end: (%li,%li,%li,%li)!", $2, $4, $6, $8);

		      add_opvars(splev, "r", VA_PASS1(r));
		      $$ = r;
		  }
		;

mapchar_or_var	: mapchar
		  {
		      add_opvars(splev, "m", VA_PASS1($1));
		  }
		| VARSTRING_MAPCHAR
		  {
		      check_vardef_type(variable_definitions, $1, SPOVAR_MAPCHAR);
		      vardef_used(variable_definitions, $1);
		      add_opvars(splev, "v", VA_PASS1($1));
		      Free($1);
		  }
		| VARSTRING_MAPCHAR_ARRAY '[' math_expr_var ']'
		  {
		      check_vardef_type(variable_definitions, $1, SPOVAR_MAPCHAR|SPOVAR_ARRAY);
		      vardef_used(variable_definitions, $1);
		      add_opvars(splev, "v", VA_PASS1($1));
		      Free($1);
		  }
		;

mapchar		: CHAR
		  {
		      if (what_map_char((char) $1) != INVALID_TYPE)
			  $$ = SP_MAPCHAR_PACK(what_map_char((char) $1), -2);
		      else {
			  lc_error("Unknown map char type '%c'!", $1);
			  $$ = SP_MAPCHAR_PACK(STONE, -2);
		      }
		  }
		| '(' CHAR ',' light_state ')'
		  {
		      if (what_map_char((char) $2) != INVALID_TYPE)
			  $$ = SP_MAPCHAR_PACK(what_map_char((char) $2), $4);
		      else {
			  lc_error("Unknown map char type '%c'!", $2);
			  $$ = SP_MAPCHAR_PACK(STONE, $4);
		      }
		  }
		;

monster_or_var	: encodemonster
		  {
		      add_opvars(splev, "M", VA_PASS1($1));
		  }
		| VARSTRING_MONST
		  {
		      check_vardef_type(variable_definitions, $1, SPOVAR_MONST);
		      vardef_used(variable_definitions, $1);
		      add_opvars(splev, "v", VA_PASS1($1));
		      Free($1);
		  }
		| VARSTRING_MONST_ARRAY '[' math_expr_var ']'
		  {
		      check_vardef_type(variable_definitions, $1, SPOVAR_MONST|SPOVAR_ARRAY);
		      vardef_used(variable_definitions, $1);
		      add_opvars(splev, "v", VA_PASS1($1));
		      Free($1);
		  }
		;

encodemonster	: STRING
                  {
                      long m = get_monster_id($1, (char)0);
                      if (m == ERR) {
                          lc_error("Unknown monster \"%s\"!", $1);
                          $$ = -1;
                      } else
                          $$ = SP_MONST_PACK(m,
                                         def_monsyms[(int) mons[m].mlet].sym);
                      Free($1);
                  }
                | CHAR
                  {
                        if (check_monster_char((char) $1))
                            $$ = SP_MONST_PACK(-1, $1);
                        else {
                            lc_error("Unknown monster class '%c'!", $1);
                            $$ = -1;
                        }
                  }
                | '(' CHAR ',' STRING ')'
                  {
                      long m = get_monster_id($4, (char) $2);
                      if (m == ERR) {
                          lc_error("Unknown monster ('%c', \"%s\")!", $2, $4);
                          $$ = -1;
                      } else
                          $$ = SP_MONST_PACK(m, $2);
                      Free($4);
                  }
                | RANDOM_TYPE
                  {
                      $$ = -1;
                  }
                ;

object_or_var	: encodeobj
		  {
		      add_opvars(splev, "O", VA_PASS1($1));
		  }
		| VARSTRING_OBJ
		  {
		      check_vardef_type(variable_definitions, $1, SPOVAR_OBJ);
		      vardef_used(variable_definitions, $1);
		      add_opvars(splev, "v", VA_PASS1($1));
		      Free($1);
		  }
		| VARSTRING_OBJ_ARRAY '[' math_expr_var ']'
		  {
		      check_vardef_type(variable_definitions, $1, SPOVAR_OBJ|SPOVAR_ARRAY);
		      vardef_used(variable_definitions, $1);
		      add_opvars(splev, "v", VA_PASS1($1));
		      Free($1);
		  }
		;

encodeobj	: STRING
		  {
		      long m = get_object_id($1, (char)0);
		      if (m == ERR) {
			  lc_error("Unknown object \"%s\"!", $1);
			  $$ = -1;
		      } else
			  $$ = SP_OBJ_PACK(m, 1); /* obj class != 0 to force generation of a specific item */
                      Free($1);
		  }
		| CHAR
		  {
			if (check_object_char((char) $1))
			    $$ = SP_OBJ_PACK(-1, $1);
			else {
			    lc_error("Unknown object class '%c'!", $1);
			    $$ = -1;
			}
		  }
		| '(' CHAR ',' STRING ')'
		  {
		      long m = get_object_id($4, (char) $2);
		      if (m == ERR) {
			  lc_error("Unknown object ('%c', \"%s\")!", $2, $4);
			  $$ = -1;
		      } else
			  $$ = SP_OBJ_PACK(m, $2);
                      Free($4);
		  }
		| RANDOM_TYPE
		  {
		      $$ = -1;
		  }
		;


string_expr	: string_or_var                 { }
		| string_expr '.' string_or_var
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_MATH_ADD));
		  }
		;

math_expr_var	: INTEGER
		  {
		      add_opvars(splev, "i", VA_PASS1($1));
		  }
		| dice
		  {
		      is_inconstant_number = 1;
		  }
		| '(' MINUS_INTEGER ')'
		  {
		      add_opvars(splev, "i", VA_PASS1($2));
		  }
		| VARSTRING_INT
		  {
		      check_vardef_type(variable_definitions, $1, SPOVAR_INT);
		      vardef_used(variable_definitions, $1);
		      add_opvars(splev, "v", VA_PASS1($1));
		      Free($1);
		      is_inconstant_number = 1;
		  }
		| VARSTRING_INT_ARRAY '[' math_expr_var ']'
		  {
		      check_vardef_type(variable_definitions,
					$1, SPOVAR_INT|SPOVAR_ARRAY);
		      vardef_used(variable_definitions, $1);
		      add_opvars(splev, "v", VA_PASS1($1));
		      Free($1);
		      is_inconstant_number = 1;
		  }
		| math_expr_var '+' math_expr_var
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_MATH_ADD));
		  }
		| math_expr_var '-' math_expr_var
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_MATH_SUB));
		  }
		| math_expr_var '*' math_expr_var
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_MATH_MUL));
		  }
		| math_expr_var '/' math_expr_var
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_MATH_DIV));
		  }
		| math_expr_var '%' math_expr_var
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_MATH_MOD));
		  }
		| '(' math_expr_var ')'             { }
		;

func_param_type	: CFUNC_INT
		  {
		      if (!strcmp("int", $1) || !strcmp("integer", $1)) {
			  $$ = (int)'i';
		      } else
			  lc_error("Unknown function parameter type '%s'", $1);
		  }
		| CFUNC_STR
		  {
		      if (!strcmp("str", $1) || !strcmp("string", $1)) {
			  $$ = (int)'s';
		      } else
			  lc_error("Unknown function parameter type '%s'", $1);
		  }
		;

func_param_part	: any_var_or_arr ':' func_param_type
		  {
		      struct lc_funcdefs_parm *tmp = New(struct lc_funcdefs_parm);

		      if (!curr_function) {
			  lc_error("Function parameters outside function definition.");
		      } else if (!tmp) {
			  lc_error("Could not alloc function params.");
		      } else {
			  long vt = SPOVAR_NULL;

			  tmp->name = strdup($1);
			  tmp->parmtype = (char) $3;
			  tmp->next = curr_function->params;
			  curr_function->params = tmp;
			  curr_function->n_params++;
			  switch (tmp->parmtype) {
			  case 'i':
                              vt = SPOVAR_INT;
                              break;
			  case 's':
                              vt = SPOVAR_STRING;
                              break;
			  default:
                              lc_error("Unknown func param conversion.");
                              break;
			  }
			  variable_definitions = add_vardef_type(
							 variable_definitions,
								 $1, vt);
		      }
		      Free($1);
		  }
		;

func_param_list		: func_param_part
			| func_param_list ',' func_param_part
			;

func_params_list	: /* nothing */
			| func_param_list
			;

func_call_param_part	: math_expr_var
			  {
			      $$ = (int)'i';
			  }
			| string_expr
			  {
			      $$ = (int)'s';
			  }
			;


func_call_param_list   	: func_call_param_part
			  {
			      char tmpbuf[2];
			      tmpbuf[0] = (char) $1;
			      tmpbuf[1] = '\0';
			      $$ = strdup(tmpbuf);
			  }
			| func_call_param_list ',' func_call_param_part
			  {
			      long len = strlen( $1 );
			      char *tmp = (char *) alloc(len + 2);
			      sprintf(tmp, "%c%s", (char) $3, $1 );
			      Free( $1 );
			      $$ = tmp;
			  }
			;

func_call_params_list	: /* nothing */
			  {
			      $$ = strdup("");
			  }
			| func_call_param_list
			  {
			      char *tmp = strdup( $1 );
			      Free( $1 );
			      $$ = tmp;
			  }
			;

ter_selection_x	: coord_or_var
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_SEL_POINT));
		  }
		| rect_ID region_or_var
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_SEL_RECT));
		  }
		| fillrect_ID region_or_var
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_SEL_FILLRECT));
		  }
		| line_ID coord_or_var ',' coord_or_var
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_SEL_LINE));
		  }
		| randline_ID coord_or_var ',' coord_or_var ',' math_expr_var
		  {
		      /* randline (x1,y1),(x2,y2), roughness */
		      add_opvars(splev, "o", VA_PASS1(SPO_SEL_RNDLINE));
		  }
		| grow_ID '(' ter_selection ')'
		  {
		      add_opvars(splev, "io", VA_PASS2(W_ANY, SPO_SEL_GROW));
		  }
		| grow_ID '(' dir_list ',' ter_selection ')'
		  {
		      add_opvars(splev, "io", VA_PASS2($3, SPO_SEL_GROW));
		  }
		| filter_ID '(' SPERCENT ',' ter_selection ')'
		  {
		      add_opvars(splev, "iio",
			     VA_PASS3($3, SPOFILTER_PERCENT, SPO_SEL_FILTER));
		  }
		| filter_ID '(' ter_selection ',' ter_selection ')'
		  {
		      add_opvars(splev, "io",
			       VA_PASS2(SPOFILTER_SELECTION, SPO_SEL_FILTER));
		  }
		| filter_ID '(' mapchar_or_var ',' ter_selection ')'
		  {
		      add_opvars(splev, "io",
				 VA_PASS2(SPOFILTER_MAPCHAR, SPO_SEL_FILTER));
		  }
		| flood_ID coord_or_var
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_SEL_FLOOD));
		  }
		| circle_ID '(' coord_or_var ',' math_expr_var ')'
		  {
		      add_opvars(splev, "oio",
				 VA_PASS3(SPO_COPY, 1, SPO_SEL_ELLIPSE));
		  }
		| circle_ID '(' coord_or_var ',' math_expr_var ',' FILLING ')'
		  {
		      add_opvars(splev, "oio",
				 VA_PASS3(SPO_COPY, $7, SPO_SEL_ELLIPSE));
		  }
		| ellipse_ID '(' coord_or_var ',' math_expr_var ',' math_expr_var ')'
		  {
		      add_opvars(splev, "io", VA_PASS2(1, SPO_SEL_ELLIPSE));
		  }
		| ellipse_ID '(' coord_or_var ',' math_expr_var ',' math_expr_var ',' FILLING ')'
		  {
		      add_opvars(splev, "io", VA_PASS2($9, SPO_SEL_ELLIPSE));
		  }
		| gradient_ID '(' GRADIENT_TYPE ',' '(' math_expr_var '-' math_expr_var opt_limited ')' ',' coord_or_var opt_coord_or_var ')'
		  {
		      add_opvars(splev, "iio",
				 VA_PASS3($9, $3, SPO_SEL_GRADIENT));
		  }
		| complement_ID ter_selection_x
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_SEL_COMPLEMENT));
		  }
		| VARSTRING_SEL
		  {
		      check_vardef_type(variable_definitions, $1, SPOVAR_SEL);
		      vardef_used(variable_definitions, $1);
		      add_opvars(splev, "v", VA_PASS1($1));
		      Free($1);
		  }
		| '(' ter_selection ')'
		  {
		      /* nothing */
		  }
		;

ter_selection	: ter_selection_x
		  {
		      /* nothing */
		  }
		| ter_selection_x '&' ter_selection
		  {
		      add_opvars(splev, "o", VA_PASS1(SPO_SEL_ADD));
		  }
		;

dice		: DICE
		  {
		      add_opvars(splev, "iio",
				 VA_PASS3($1.num, $1.die, SPO_DICE));
		  }
		;

all_integers	: MINUS_INTEGER
		| PLUS_INTEGER
		| INTEGER
		;

all_ints_push	: MINUS_INTEGER
		  {
		      add_opvars(splev, "i", VA_PASS1($1));
		  }
		| PLUS_INTEGER
		  {
		      add_opvars(splev, "i", VA_PASS1($1));
		  }
		| INTEGER
		  {
		      add_opvars(splev, "i", VA_PASS1($1));
		  }
		| dice
		  {
		      /* nothing */
		  }
		;

objectid	: object_ID
		| OBJECT_ID
		;

monsterid	: monster_ID
		| MONSTER_ID
		;

terrainid	: terrain_ID
		| TERRAIN_ID
		;

engraving_type	: ENGRAVING_TYPE
		| RANDOM_TYPE
		;

lev_region	: region
		  {
			$$ = $1;
		  }
		| LEV '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')'
		  {
			if ($3 <= 0 || $3 >= COLNO)
			    lc_error(
                          "Region (%ld,%ld,%ld,%ld) out of level range (x1)!",
                                     $3, $5, $7, $9);
			else if ($5 < 0 || $5 >= ROWNO)
			    lc_error(
                          "Region (%ld,%ld,%ld,%ld) out of level range (y1)!",
                                     $3, $5, $7, $9);
			else if ($7 <= 0 || $7 >= COLNO)
			    lc_error(
                          "Region (%ld,%ld,%ld,%ld) out of level range (x2)!",
                                     $3, $5, $7, $9);
			else if ($9 < 0 || $9 >= ROWNO)
			    lc_error(
                          "Region (%ld,%ld,%ld,%ld) out of level range (y2)!",
                                     $3, $5, $7, $9);
			$$.x1 = $3;
			$$.y1 = $5;
			$$.x2 = $7;
			$$.y2 = $9;
			$$.area = 1;
		  }
		;

region		: '(' INTEGER ',' INTEGER ',' INTEGER ',' INTEGER ')'
		  {
/* This series of if statements is a hack for MSC 5.1.  It seems that its
   tiny little brain cannot compile if these are all one big if statement. */
			if ($2 < 0 || $2 > (int) max_x_map)
			    lc_error(
                            "Region (%ld,%ld,%ld,%ld) out of map range (x1)!",
                                     $2, $4, $6, $8);
			else if ($4 < 0 || $4 > (int) max_y_map)
			    lc_error(
                            "Region (%ld,%ld,%ld,%ld) out of map range (y1)!",
                                     $2, $4, $6, $8);
			else if ($6 < 0 || $6 > (int) max_x_map)
			    lc_error(
                            "Region (%ld,%ld,%ld,%ld) out of map range (x2)!",
                                     $2, $4, $6, $8);
			else if ($8 < 0 || $8 > (int) max_y_map)
			    lc_error(
                            "Region (%ld,%ld,%ld,%ld) out of map range (y2)!",
                                     $2, $4, $6, $8);
			$$.area = 0;
			$$.x1 = $2;
			$$.y1 = $4;
			$$.x2 = $6;
			$$.y2 = $8;
		  }
		;


%%

/*lev_comp.y*/
