%{ /* * This is the grammar definition of LPC. Expect one shift/reduce conflict, * because of if-then-else. The default, to shift this conflict, is the * correct solution. */ #include <string.h> #include <stdio.h> #include "lint.h" #include "lnode.h" #include "config.h" #include "interpret.h" #include "object.h" #define YYMAXDEPTH 600 struct lnode *prog, *heart_beat; int variable_count; void yyerror(), free_all_local_names(), add_local_name(), smart_log(); extern int yylex(), check_deklared(); static void copy_variables(); int static_variable_flag; char *argument_name; /* The name of the current argument. */ char *xalloc(), *string_copy(); extern int current_line; /* * 'inherit_file' is used as a flag. If it is set to a string * after yyparse(), this string should be loaded as an object, * and the original object must be loaded again. * 'inherit_ob' will point to the super class object. This value is saved * so that load_object() can set up pointers to it. */ extern char *current_file, *inherit_file; extern struct object *find_object2(); struct object *inherit_ob; char *local_names[MAX_LOCAL]; int current_number_of_locals = 0; static int num_arg; %} %token F_THIS_PLAYER F_IF F_IDENTIFIER F_LAND F_LOR F_STATUS F_SET_TRUE %token F_SET_FALSE F_CONS F_RETURN F_NOT F_WRITE F_STRING F_ADD_VERB %token F_ADD_ADJ F_ADD_SUBST F_ADD_ACTION F_MOVE_OBJECT F_INC F_DEC %token F_POST_INC F_POST_DEC F_COMMA F_COND F_QUERY_LOAD_AVERAGE F_THIS_VERB %token F_NUMBER F_ASSIGN F_INT F_CALL_OTHER F_ADD F_SUBTRACT F_MULTIPLY %token F_DIVIDE F_LT F_GT F_EQ F_GE F_LE F_ARGUMENT F_FUNCTION %token F_CLONE_OBJECT F_THIS_OBJECT F_SAVE_OBJECT F_RESTORE_OBJECT F_NE %token F_ENVIRONMENT F_ADD_EQ F_SUB_EQ F_DIV_EQ F_MULT_EQ F_PRESENT %token F_COMMAND F_SET_LIGHT F_DESTRUCT F_CREATE_WIZARD F_NEGATE F_SAY %token F_STRLEN F_SUBSCRIPT F_WHILE F_BREAK F_SHUTDOWN F_LOG_FILE %token F_DO F_FOR F_ADD_XVERB F_FIND_CALL_OUT F_READ_FILE F_FILTER_OBJECTS %token F_SSCANF F_SHOUT F_STRING_DECL F_LOCAL_NAME F_FIRST_INVENTORY %token F_NEXT_INVENTORY F_ENABLE_COMMANDS F_RANDOM F_INPUT_TO F_CRYPT %token F_LS F_CAT F_FIND_LIVING F_TELL_OBJECT F_PEOPLE F_ED F_LIVING %token F_LOWER_CASE F_ELSE F_CAPITALIZE F_SET_HEART_BEAT F_SNOOP F_TELL_ROOM %token F_FIND_OBJECT F_RM F_CONST0 F_CONST1 F_BLOCK F_TRANSFER %token F_REGCOMP F_REGEXEC F_LOCALCMD F_SWAP F_CONTINUE F_ADD_WORTH F_TIME %token F_MOD F_MOD_EQ F_QUERY_IP_NUMBER F_INHERIT F_COLON_COLON F_CREATOR %token F_STATIC F_CALL_OUT F_REMOVE_CALL_OUT F_COMBINE_FREE_LIST F_ALLOCATE %token F_SIZEOF F_DISABLE_COMMANDS F_CTIME F_INTP F_STRINGP F_OBJECTP %token F_POINTERP F_USERS F_ARROW F_PREVIOUS_OBJECT F_AGGREGATE F_EXTRACT %token F_FILE_NAME F_QUERY_VERB F_TAIL F_QUERY_HOST_NAME F_EXPLODE %token F_COMPL F_AND F_AND_EQ F_OR F_OR_EQ F_XOR F_XOR_EQ F_CP F_RENAME %token F_LSH F_LSH_EQ F_RSH F_RSH_EQ F_MKDIR F_RMDIR F_QUERY_SNOOP F_THROW %token F_FIND_PLAYER F_WRITE_FILE F_FILE_SIZE F_PARSE_COMMAND F_IMPLODE %token F_QUERY_IDLE F_REMOTE_COMMAND F_INTERACTIVE F_CALLER F_TYPEOF F_CATCH %token F_NOTIFY_FAIL F_SET_LIVING_NAME F_SET_BIT F_CLEAR_BIT F_TEST_BIT %token F_REALLOCATE F_INDEX F_SEARCHA F_CONTENTS F_IN_EDITOR F_GREP %union { struct lnode *lnode; int number; char *string; } %type <lnode> cond expr1 expr_list expr2 expr3 function_call variable expr11 %type <lnode> statements statement program def return expr4 expr31 %type <lnode> string expr0 F_NUMBER expr25 expr27 expr23 expr22 while do for %type <lnode> expr24 expr211 expr212 expr213 comma_expr for_expr %type <lnode> F_LOCAL_NAME block number F_CONST0 F_CONST1 %type <number> name F_SET_FALSE F_SET_TRUE F_THIS_PLAYER F_WRITE F_THIS_OBJECT %type <number> assign static F_CATCH F_THROW %type <string> F_IDENTIFIER F_STRING F_ARGUMENT function_name %% all: program { prog = $1; }; program: def possible_semi_colon program { if ($1 != 0) { struct lnode_def *p = (struct lnode_def *)$1; p->next = (struct lnode_def *)$3; $$ = (struct lnode *)p; } else $$ = $3; } | /* empty */ { $$ = 0; }; possible_semi_colon: /* empty */ | ';' { yyerror("Extra ';'. Ignored."); }; inheritance: F_INHERIT F_STRING ';' { if (inherit_ob || inherit_file) { yyerror("Multiple inheritance not allowed.\n"); free($2); } else if (prog) { yyerror("inherit must come first.\n"); free($2); } else { inherit_ob = find_object2($2); if (inherit_ob == 0) { inherit_file = $2; return 0; } free($2); copy_variables(inherit_ob); } } number: F_NUMBER | F_CONST0 | F_CONST1; def: static F_IDENTIFIER '(' argument ')' { num_arg = current_number_of_locals; } block { struct lnode_def *p; p = alloc_lnode_def(F_IDENTIFIER,$2,$7, current_number_of_locals); p->num_arg = num_arg; if ($1) p->is_static = 1; $$ = (struct lnode *)p; if (strcmp(p->name, "heart_beat") == 0) heart_beat = $$; if (argument_name) free(argument_name); argument_name = 0; free_all_local_names(); } | name '(' argument ')' block { yyerror("Predefined function name.\n"); $$ = 0; } | static { static_variable_flag = $1; } type name_list ';' { $$ = 0; static_variable_flag = 0; } | inheritance { $$ = 0; } ; static: F_STATIC { $$ = 1; } | /* empty */ { $$ = 0; } ; req_semi: ';' | /* empty */ { yyerror("Missing ';'"); }; argument: /* empty */ | local_list ; type: F_STATUS | F_INT | F_STRING_DECL; name_list: new_name | new_name ',' name_list; new_name: optional_star F_IDENTIFIER { alloc_lnode_var_def(F_STATUS, $2, variable_count++); }; optional_star: '*' | /* empty */ ; block: '{' local_declarations statements '}' { $$ = (struct lnode *)alloc_lnode_block($3); }; local_declarations: /* empty */ | local_declarations type local_list req_semi ; local_list: F_IDENTIFIER { add_local_name($1); } | local_list ',' F_IDENTIFIER { add_local_name($3); } | F_ARGUMENT { yyerror("Illegal to redeclare argument"); } | F_LOCAL_NAME { yyerror("Illegal to redeclare local name"); }; statements: /* empty */ { $$ = 0; } | statement statements { if ($1 == 0) $$ = $2; else $$ = (struct lnode *)alloc_lnode_2(F_CONS, $1, $2); } | error ';' { $$ = 0; }; statement: comma_expr ';' { $$ = $1; } | cond | while | do | for | return ';' { $$ = $1; } | block | F_THROW comma_expr ';' { $$ = (struct lnode *)alloc_lnode_1(F_THROW, $2); } | F_BREAK req_semi { $$ = (struct lnode *)alloc_lnode_single(F_BREAK); }; | F_CONTINUE req_semi { $$ = (struct lnode *)alloc_lnode_single(F_CONTINUE); }; while: F_WHILE '(' comma_expr ')' statement { $$ = (struct lnode *)alloc_lnode_2(F_WHILE, $3, $5); }; do: F_DO statement F_WHILE '(' comma_expr ')' req_semi { $$ = (struct lnode *)alloc_lnode_2(F_DO, $2, $5); }; for: F_FOR '(' for_expr ';' for_expr ';' for_expr ')' statement { $$ = (struct lnode *) alloc_lnode_2(F_CONS,$3, (struct lnode *) alloc_lnode_2(F_CONS, (struct lnode *)alloc_lnode_3(F_FOR,$5, $7,$9), (struct lnode *)0)); } for_expr: /* EMPTY */ { $$ = (struct lnode *)alloc_lnode_single(F_CONST1); } | comma_expr { $$=$1; } comma_expr: expr0 { $$=$1; }; | expr0 ',' comma_expr { if($3->type==F_COMMA) $$ = (struct lnode *)alloc_lnode_2(F_COMMA, $1, $3); else $$ = (struct lnode *)alloc_lnode_2(F_COMMA, $1, (struct lnode *)alloc_lnode_2(F_COMMA, $3, 0)); } expr0: expr1 | variable assign expr0 { $$ = (struct lnode *)alloc_lnode_2($2, $1, $3); } | F_CATCH statement { $$ = (struct lnode *)alloc_lnode_1(F_CATCH, $2); } | error assign expr0 { yyerror("Illegal LHS"); $$ = 0; } assign: '=' { $$ = F_ASSIGN; } | F_AND_EQ { $$ = F_AND_EQ; } | F_OR_EQ { $$ = F_OR_EQ; } | F_XOR_EQ { $$ = F_XOR_EQ; } | F_LSH_EQ { $$ = F_LSH_EQ; } | F_RSH_EQ { $$ = F_RSH_EQ; } | F_ADD_EQ { $$ = F_ADD_EQ; } | F_SUB_EQ { $$ = F_SUB_EQ; } | F_MULT_EQ { $$ = F_MULT_EQ; } | F_MOD_EQ { $$ = F_MOD; } | F_DIV_EQ { $$ = F_DIV_EQ; }; return: F_RETURN { $$ = (struct lnode *)alloc_lnode_1(F_RETURN, 0); } | F_RETURN comma_expr { $$ = (struct lnode *)alloc_lnode_1(F_RETURN, $2); }; expr_list: /* empty */ { $$ = 0; } | expr0 { $$ = (struct lnode *)alloc_lnode_2(F_CONS, $1, 0); } | expr0 ',' expr_list { $$ = (struct lnode *)alloc_lnode_2(F_CONS, $1, $3); }; expr1: expr11 | expr11 '?' expr1 ':' expr1 { $$ = (struct lnode *)alloc_lnode_3(F_COND, $1, $3, $5); } expr11: expr2 | expr11 F_LOR expr2 { $$ = (struct lnode *)alloc_lnode_2(F_LOR, $1, $3); }; expr2: expr211 | expr211 F_LAND expr2 { $$ = (struct lnode *)alloc_lnode_2(F_LAND, $1, $3); }; expr211: expr212 | expr211 '|' expr24 { $$ = (struct lnode *)alloc_lnode_2(F_OR, $1, $3); }; expr212: expr213 | expr212 '^' expr24 { $$ = (struct lnode *)alloc_lnode_2(F_XOR, $1, $3); }; expr213: expr22 | expr213 '&' expr24 { $$ = (struct lnode *)alloc_lnode_2(F_AND, $1, $3); }; expr22: expr23 | expr24 F_EQ expr24 { $$ = (struct lnode *)alloc_lnode_2(F_EQ, $1, $3); } | expr24 F_NE expr24 { $$ = (struct lnode *)alloc_lnode_2(F_NE, $1, $3); }; expr23: expr24 | expr24 '>' expr24 { $$ = (struct lnode *)alloc_lnode_2(F_GT, $1, $3); } | expr24 F_GE expr24 { $$ = (struct lnode *)alloc_lnode_2(F_GE, $1, $3); } | expr24 '<' expr24 { $$ = (struct lnode *)alloc_lnode_2(F_LT, $1, $3); } | expr24 F_LE expr24 { $$ = (struct lnode *)alloc_lnode_2(F_LE, $1, $3); }; expr24: expr25 | expr24 F_LSH expr25 { $$ = (struct lnode *)alloc_lnode_2(F_LSH, $1, $3); } | expr24 F_RSH expr25 { $$ = (struct lnode *)alloc_lnode_2(F_RSH, $1, $3); }; expr25: expr27 | expr25 '+' expr27 { $$ = (struct lnode *)alloc_lnode_2(F_ADD, $1, $3); } | expr25 '-' expr27 { $$ = (struct lnode *)alloc_lnode_2(F_SUBTRACT, $1, $3); }; expr27: expr3 | expr27 '*' expr3 { $$ = (struct lnode *)alloc_lnode_2(F_MULTIPLY, $1, $3); } | expr27 '%' expr3 { $$ = (struct lnode *)alloc_lnode_2(F_MOD, $1, $3); } | expr27 '/' expr3 { $$ = (struct lnode *)alloc_lnode_2(F_DIVIDE, $1, $3); }; expr3: expr31 | F_INC variable { $$ = (struct lnode *)alloc_lnode_1(F_INC, $2); }; | F_DEC variable { $$ = (struct lnode *)alloc_lnode_1(F_DEC, $2); }; | F_NOT expr3 { $$ = (struct lnode *)alloc_lnode_1(F_NOT, $2); }; | '~' expr3 { $$ = (struct lnode *)alloc_lnode_1(F_COMPL, $2); }; | '-' expr3 { $$ = (struct lnode *)alloc_lnode_1(F_NEGATE, $2); }; expr31: expr4 | variable F_INC { $$ = (struct lnode *)alloc_lnode_1(F_POST_INC, $1); }; | variable F_DEC { $$ = (struct lnode *)alloc_lnode_1(F_POST_DEC, $1); }; expr4: function_call | variable | string | number | '(' comma_expr ')' { $$ = $2; } | '(' '{' expr_list '}' ')' { $$ = (struct lnode *)alloc_lnode_1(F_AGGREGATE, $3); }; variable: F_IDENTIFIER { $$ = (struct lnode *)alloc_lnode_number(F_IDENTIFIER, check_deklared($1)); free($1); } | F_ARGUMENT { $$ = (struct lnode *)alloc_lnode_single(F_ARGUMENT); } | F_LOCAL_NAME | expr4 '[' comma_expr ']' { $$ = (struct lnode *)alloc_lnode_2(F_SUBSCRIPT, $1, $3); }; string: F_STRING { $$ = (struct lnode *)alloc_lnode_name(F_STRING, $1); }; function_call: name '(' expr_list ')' { $$ = (struct lnode *)alloc_lnode_1($1, $3); } | function_name '(' expr_list ')' { $$ = (struct lnode *)alloc_lnode_funcall(F_FUNCTION, $1, $3); } | expr4 F_ARROW function_name '(' expr_list ')' { struct lnode *p; p = (struct lnode *)alloc_lnode_name(F_STRING, $3), p = (struct lnode *)alloc_lnode_2(F_CONS, p, $5); p = (struct lnode *)alloc_lnode_2(F_CONS, $1, p); $$ = (struct lnode *)alloc_lnode_1(F_CALL_OTHER, p); }; function_name: F_IDENTIFIER | F_COLON_COLON F_IDENTIFIER { char *p = xalloc(strlen($2) + 3); strcpy(p, "::"); strcat(p, $2); free($2); $$ = p; }; name: F_THIS_PLAYER { $$ = F_THIS_PLAYER; } | F_THIS_OBJECT { $$ = F_THIS_OBJECT; } | F_SET_FALSE { $$ = F_SET_FALSE; } | F_WRITE { $$ = F_WRITE; } | F_ADD_VERB { $$ = F_ADD_VERB; } | F_ADD_XVERB { $$ = F_ADD_XVERB; } | F_ADD_SUBST { $$ = F_ADD_SUBST; } | F_ED { $$ = F_ED; } | F_ADD_ADJ { $$ = F_ADD_ADJ; } | F_SHUTDOWN { $$ = F_SHUTDOWN; } | F_CALL_OTHER { $$ = F_CALL_OTHER; } | F_CLONE_OBJECT { $$ = F_CLONE_OBJECT; } | F_ADD_ACTION { $$ = F_ADD_ACTION; } | F_MOVE_OBJECT { $$ = F_MOVE_OBJECT; } | F_LOG_FILE { $$ = F_LOG_FILE; } | F_SET_TRUE { $$ = F_SET_TRUE; } | F_SAVE_OBJECT { $$ = F_SAVE_OBJECT; } | F_ENVIRONMENT { $$ = F_ENVIRONMENT; } | F_PRESENT { $$ = F_PRESENT; } | F_CREATOR { $$ = F_CREATOR; } | F_SET_HEART_BEAT { $$ = F_SET_HEART_BEAT; } | F_COMMAND { $$ = F_COMMAND; } | F_FILE_NAME { $$ = F_FILE_NAME; } | F_QUERY_VERB { $$ = F_QUERY_VERB; } | F_THIS_VERB { $$ = F_QUERY_VERB; } | F_PARSE_COMMAND { $$ = F_PARSE_COMMAND; } | F_EXPLODE { $$ = F_EXPLODE; } | F_IMPLODE { $$ = F_IMPLODE; } | F_QUERY_IDLE { $$ = F_QUERY_IDLE; } | F_QUERY_LOAD_AVERAGE { $$ = F_QUERY_LOAD_AVERAGE; } | F_FILTER_OBJECTS { $$ = F_FILTER_OBJECTS; } | F_TAIL { $$ = F_TAIL; } | F_MKDIR { $$ = F_MKDIR; } | F_RMDIR { $$ = F_RMDIR; } | F_QUERY_SNOOP { $$ = F_QUERY_SNOOP; } | F_QUERY_HOST_NAME { $$ = F_QUERY_HOST_NAME; } | F_PREVIOUS_OBJECT { $$ = F_PREVIOUS_OBJECT; } | F_CRYPT { $$ = F_CRYPT; } | F_CAT { $$ = F_CAT; } | F_GREP { $$ = F_GREP; } | F_LS { $$ = F_LS; } | F_RM { $$ = F_RM; } | F_REALLOCATE { $$ = F_REALLOCATE; } | F_CONTENTS { $$ = F_CONTENTS; } | F_INDEX { $$ = F_INDEX; } | F_SEARCHA { $$ = F_SEARCHA; } | F_IN_EDITOR { $$ = F_IN_EDITOR; } | F_FIND_LIVING { $$ = F_FIND_LIVING; } | F_FIND_PLAYER { $$ = F_FIND_PLAYER; } | F_WRITE_FILE { $$ = F_WRITE_FILE; } | F_FILE_SIZE { $$ = F_FILE_SIZE; } | F_LOWER_CASE { $$ = F_LOWER_CASE; } | F_TELL_OBJECT { $$ = F_TELL_OBJECT; } | F_FIND_OBJECT { $$ = F_FIND_OBJECT; } | F_TRANSFER { $$ = F_TRANSFER; } | F_REGCOMP { $$ = F_REGCOMP; } | F_REGEXEC { $$ = F_REGEXEC; } | F_LOCALCMD { $$ = F_LOCALCMD; } | F_SWAP { $$ = F_SWAP; } | F_ADD_WORTH { $$ = F_ADD_WORTH; } | F_TIME { $$ = F_TIME; } | F_PEOPLE { $$ = F_PEOPLE; } | F_INPUT_TO { $$ = F_INPUT_TO; } | F_ENABLE_COMMANDS { $$ = F_ENABLE_COMMANDS; } | F_CAPITALIZE { $$ = F_CAPITALIZE; } | F_LIVING { $$ = F_LIVING; } | F_RANDOM { $$ = F_RANDOM; } | F_SNOOP { $$ = F_SNOOP; } | F_FIRST_INVENTORY { $$ = F_FIRST_INVENTORY; } | F_NEXT_INVENTORY { $$ = F_NEXT_INVENTORY; } | F_SET_LIGHT { $$ = F_SET_LIGHT; } | F_DESTRUCT { $$ = F_DESTRUCT; } | F_CREATE_WIZARD { $$ = F_CREATE_WIZARD; } | F_SAY { $$ = F_SAY; } | F_TELL_ROOM { $$ = F_TELL_ROOM; } | F_SHOUT { $$ = F_SHOUT; } | F_RESTORE_OBJECT { $$ = F_RESTORE_OBJECT; } | F_SSCANF { $$ = F_SSCANF; } | F_QUERY_IP_NUMBER { $$ = F_QUERY_IP_NUMBER; } | F_ALLOCATE { $$ = F_ALLOCATE; } | F_SIZEOF { $$ = F_SIZEOF; } | F_CTIME { $$ = F_CTIME; } | F_DISABLE_COMMANDS { $$ = F_DISABLE_COMMANDS; } | F_CALL_OUT { $$ = F_CALL_OUT; } | F_STRINGP { $$ = F_STRINGP; } | F_INTP { $$ = F_INTP; } | F_OBJECTP { $$ = F_OBJECTP; } | F_POINTERP { $$ = F_POINTERP; } | F_USERS { $$ = F_USERS; } | F_EXTRACT { $$ = F_EXTRACT; } | F_REMOVE_CALL_OUT { $$ = F_REMOVE_CALL_OUT; } | F_FIND_CALL_OUT { $$ = F_FIND_CALL_OUT; } | F_COMBINE_FREE_LIST { $$ = F_COMBINE_FREE_LIST; } | F_STRLEN { $$ = F_STRLEN; }; | F_REMOTE_COMMAND { $$ = F_REMOTE_COMMAND; } | F_INTERACTIVE { $$ = F_INTERACTIVE; } | F_CALLER { $$ = F_CALLER; } | F_TYPEOF { $$ = F_TYPEOF; } | F_CP { $$ = F_CP; } | F_RENAME { $$ = F_RENAME; } | F_READ_FILE { $$ = F_READ_FILE; } | F_SET_BIT { $$ = F_SET_BIT; } | F_CLEAR_BIT { $$ = F_CLEAR_BIT; } | F_TEST_BIT { $$ = F_TEST_BIT; } | F_SET_LIVING_NAME { $$ = F_SET_LIVING_NAME; } | F_NOTIFY_FAIL { $$ = F_NOTIFY_FAIL; } cond: F_IF '(' comma_expr ')' statement { $$ = (struct lnode *)alloc_lnode_3(F_IF, $3, $5, 0); } | F_IF '(' comma_expr ')' statement F_ELSE statement { $$ = (struct lnode *)alloc_lnode_3(F_IF, $3, $5, $7); }; %% void yyerror(str) char *str; { extern int num_parse_error; if (num_parse_error > 5) return; (void)fprintf(stderr, "%s: %s line %d\n", current_file, str, current_line); fflush(stderr); smart_log(current_file, current_line, str); if (num_parse_error == 0) save_error(str, current_file, current_line); num_parse_error++; } int check_deklared(str) char *str; { struct lnode_var_def *p; char buff[100]; for (p = prog_status; p; p = p->next) { if (strcmp(p->name, str) == 0) return p->num_var; } (void)sprintf(buff, "Variable %s not declared !", str); yyerror(buff); return 0; } void free_all_local_names() { int i; for (i=0; i<current_number_of_locals; i++) { free(local_names[i]); local_names[i] = 0; } current_number_of_locals = 0; } void add_local_name(str) char *str; { if (current_number_of_locals == MAX_LOCAL) yyerror("Too many local variables\n"); else local_names[current_number_of_locals++] = str; } int num_val(p) struct lnode_number *p; { switch(p->type) { case F_NUMBER: return p->number; case F_CONST0: return 0; case F_CONST1: return 1; default: fatal("Illegal type of number.\n"); } /*NOTREACHED*/ } /* * It could be possible to do '#include "/etc/passwd"' in the definition * of an object. The compilation would fail, but the .i file would remain * with interesting data. * Prevent this by checking the name of teh include file. */ int illegal_include_file(str) char *str; { char *p; int i; if (str[0] == '/') return 1; if (strlen(str) < 3) return 0; for (p = str + 3, i = 0; *p; p++) if (p[0] == '/' && p[-1] == '.' && p[-2] == '.' && p[-3] == '/') i += 1; if (i > 3) return 1; return 0; } static void copy_one_variable(p) struct lnode_var_def *p; { if (p->next) copy_one_variable(p->next); static_variable_flag = p->is_static; alloc_lnode_var_def(F_STATUS, string_copy(p->name), variable_count++); } static void copy_variables(from) struct object *from; { if (variable_count > 0) yyerror("Illegal to declare variables before the inherit statement"); if (!from->status) return; copy_one_variable(from->status); }