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