/* This is to make emacs edit this in C mode: -*-C-*- */ %{ extern char *outp; #include "std.h" #include "compiler.h" #include "lex.h" #include "scratchpad.h" #include "lpc_incl.h" #include "simul_efun.h" #include "generate.h" #include "master.h" /* gross. Necessary? - Beek */ #ifdef WIN32 #define MSDOS #endif #define YYSTACK_USE_ALLOCA 0 %line /* * This is the grammar definition of LPC, and its parse tree generator. */ /* down to one global :) bits: SWITCH_CONTEXT - we're inside a switch LOOP_CONTEXT - we're inside a loop SWITCH_STRINGS - a string case has been found SWITCH_NUMBERS - a non-zero numeric case has been found SWITCH_RANGES - a range has been found SWITCH_DEFAULT - a default has been found */ int context; int num_refs; int func_present; /* * bison & yacc don't prototype this in y.tab.h */ int yyparse (void); %} /* * Token definitions. * * Appearing in the precedence declarations are: * '+' '-' '/' '*' '%' * '&' '|' '<' '>' '^' * '~' '?' * * Other single character tokens recognized in this grammar: * '{' '}' ',' ';' ':' * '(' ')' '[' ']' '$' */ %token L_STRING L_NUMBER L_REAL %token L_BASIC_TYPE L_TYPE_MODIFIER %token L_DEFINED_NAME L_IDENTIFIER %token L_EFUN %token L_INC L_DEC %token L_ASSIGN %token L_LAND L_LOR %token L_LSH L_RSH %token L_ORDER %token L_NOT %token L_IF L_ELSE %token L_SWITCH L_CASE L_DEFAULT L_RANGE L_DOT_DOT_DOT %token L_WHILE L_DO L_FOR L_FOREACH L_IN %token L_BREAK L_CONTINUE %token L_RETURN %token L_ARROW L_INHERIT L_COLON_COLON %token L_ARRAY_OPEN L_MAPPING_OPEN L_FUNCTION_OPEN L_NEW_FUNCTION_OPEN %token L_SSCANF L_CATCH %ifdef DEBUG %token L_TREE %endif %ifdef ARRAY_RESERVED_WORD %token L_ARRAY %endif %ifdef REF_RESERVED_WORD %token L_REF %endif %token L_PARSE_COMMAND L_TIME_EXPRESSION %token L_CLASS L_NEW %token L_PARAMETER %ifdef COMPAT_32 %token L_LAMBDA %endif /* * 'Dangling else' shift/reduce conflict is well known... * define these precedences to shut yacc up. */ %nonassoc LOWER_THAN_ELSE %nonassoc L_ELSE /* * Operator precedence and associativity... * greatly simplify the grammar. */ %right L_ASSIGN %right '?' %left L_LOR %left L_LAND %left '|' %left '^' %left '&' %left L_EQ L_NE %left L_ORDER '<' %left L_LSH L_RSH %left '+' '-' %left '*' '%' '/' %right L_NOT '~' %nonassoc L_INC L_DEC /* * YYTYPE * * Anything with size > 4 is commented. Sizes assume typical 32 bit * architecture. This size of the largest element of this union should * be kept as small as possible to optimize copying of compiler stack * elements. */ %union { POINTER_INT pointer_int; long number; float real; char *string; struct { short num_arg; char flags; } argument; ident_hash_elem_t *ihe; parse_node_t *node; function_context_t *contextp; struct { parse_node_t *node; char num; } decl; /* 5 */ struct { char num_local; char max_num_locals; short context; short save_current_type; short save_exact_types; } func_block; /* 8 */ } /* * Type declarations. */ /* These hold opcodes */ %type efun_override L_ASSIGN L_ORDER /* Holds a variable index */ %type L_PARAMETER single_new_local_def /* These hold arbitrary numbers */ %type L_NUMBER /* These hold numbers that are going to be stuffed into pointers :) * Don't ask :) */ %type constant /* These hold a real number */ %type L_REAL /* holds a string constant */ %type L_STRING string_con1 string_con2 /* Holds the number of elements in a list and whether it must be a prototype */ %type argument_list argument /* These hold a list of possible interpretations of an identifier */ %type L_DEFINED_NAME /* These hold a type */ %type type optional_star type_modifier_list %type opt_basic_type L_TYPE_MODIFIER L_BASIC_TYPE basic_type atomic_type %type cast arg_type %ifdef ARRAY_RESERVED_WORD %type opt_atomic_type %endif /* This holds compressed and less flexible def_name information */ %type L_NEW_FUNCTION_OPEN l_new_function_open %ifdef COMPAT_32 %type simple_function_pointer %endif /* holds an identifier or some sort */ %type L_IDENTIFIER L_EFUN function_name identifier %type new_local_name /* The following return a parse node */ %type number real string expr0 comma_expr for_expr sscanf catch %type parse_command time_expression expr_list expr_list2 expr_list3 %type expr_list4 assoc_pair expr4 lvalue function_call lvalue_list %type new_local_def statement while cond do switch case %type return optional_else_part block_or_semi %type case_label statements switch_block %type expr_list_node expr_or_block %type single_new_local_def_with_init %type class_init opt_class_init all def %type program modifier_change inheritance type_decl %ifdef DEBUG %type tree %endif /* The following hold information about blocks and local vars */ %type local_declarations local_name_list block decl_block %type foreach_var foreach_vars first_for_expr foreach for /* This holds a flag */ %type new_arg %% %pragma auto_note_compiler_case_start all: program { comp_trees[TREE_MAIN] = $$; } ; program: program def possible_semi_colon { CREATE_TWO_VALUES($$, 0, $1, $2); } | /* empty */ { $$ = 0; } ; possible_semi_colon: /* empty */ | ';' { yywarn("Extra ';'. Ignored."); } ; inheritance: type_modifier_list L_INHERIT string_con1 ';' { object_t *ob; inherit_t inherit; int initializer; %ifdef SENSIBLE_MODIFIERS int acc_mod; %endif $1 |= global_modifiers; %ifdef SENSIBLE_MODIFIERS acc_mod = ($1 & DECL_ACCESS) & ~global_modifiers; if (acc_mod & (acc_mod - 1)) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Multiple access modifiers ("); p = get_type_modifiers(p, end, $1); p = strput(p, end, ") for inheritance"); yyerror(buf); } %endif if (!($1 & DECL_ACCESS)) $1 |= DECL_PUBLIC; #ifndef ALLOW_INHERIT_AFTER_FUNCTION if (func_present) yyerror("Illegal to inherit after defining functions."); #endif if (var_defined) yyerror("Illegal to inherit after defining global variables."); #ifndef ALLOW_INHERIT_AFTER_FUNCTION if (func_present || var_defined){ #else if (var_defined){ #endif inherit_file = 0; YYACCEPT; } #ifdef NEVER } //stupid bison #endif ob = find_object2($3); if (ob == 0) { inherit_file = alloc_cstring($3, "inherit"); /* Return back to load_object() */ YYACCEPT; } scratch_free($3); inherit.prog = ob->prog; if (mem_block[A_INHERITS].current_size){ inherit_t *prev_inherit = INHERIT(NUM_INHERITS - 1); inherit.function_index_offset = prev_inherit->function_index_offset + prev_inherit->prog->num_functions_defined + prev_inherit->prog->last_inherited; if (prev_inherit->prog->num_functions_defined && prev_inherit->prog->function_table[prev_inherit->prog->num_functions_defined - 1].funcname[0] == APPLY___INIT_SPECIAL_CHAR) inherit.function_index_offset--; } else inherit.function_index_offset = 0; inherit.variable_index_offset = mem_block[A_VAR_TEMP].current_size / sizeof (variable_t); inherit.type_mod = $1; add_to_mem_block(A_INHERITS, (char *)&inherit, sizeof inherit); /* The following has to come before copy_vars - Sym */ copy_structures(ob->prog); copy_variables(ob->prog, $1); initializer = copy_functions(ob->prog, $1); if (initializer >= 0) { parse_node_t *node, *newnode; /* initializer is an index into the object we're inheriting's function table; this finds the appropriate entry in our table and generates a call to it */ node = new_node_no_line(); node->kind = NODE_CALL_2; node->r.expr = 0; node->v.number = F_CALL_INHERITED; node->l.number = initializer | ((NUM_INHERITS - 1) << 16); node->type = TYPE_ANY; /* The following illustrates a distinction between */ /* macros and funcs...newnode is needed here - Sym */ newnode = comp_trees[TREE_INIT]; CREATE_TWO_VALUES(comp_trees[TREE_INIT],0, newnode, node); comp_trees[TREE_INIT] = pop_value(comp_trees[TREE_INIT]); } $$ = 0; } ; real: L_REAL { CREATE_REAL($$, $1); } ; number: L_NUMBER { CREATE_NUMBER($$, $1); } ; optional_star: /* empty */ { $$ = 0; } | '*' { $$ = TYPE_MOD_ARRAY; } ; block_or_semi: block { $$ = $1.node; if (!$$) { CREATE_RETURN($$, 0); } } | ';' { $$ = 0; } | error { $$ = 0; } ; identifier: L_DEFINED_NAME { $$ = scratch_copy($1->name); } | L_IDENTIFIER ; def: type optional_star identifier { int flags; %ifdef SENSIBLE_MODIFIERS int acc_mod; %endif func_present = 1; flags = ($1 >> 16); flags |= global_modifiers; %ifdef SENSIBLE_MODIFIERS acc_mod = (flags & DECL_ACCESS) & ~global_modifiers; if (acc_mod & (acc_mod - 1)) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Multiple access modifiers ("); p = get_type_modifiers(p, end, flags); p = strput(p, end, ") for function"); yyerror(buf); } %endif if (!(flags & DECL_ACCESS)) flags |= DECL_PUBLIC; %ifdef SENSIBLE_MODIFIERS if (flags & DECL_NOSAVE) { yywarn("Illegal to declare nosave function."); flags &= ~DECL_NOSAVE; } %endif $1 = (flags << 16) | ($1 & 0xffff); /* Handle type checking here so we know whether to typecheck 'argument' */ if ($1 & 0xffff) { %ifdef OLD_TYPE_BEHAVIOR exact_types = 0; %else exact_types = ($1& 0xffff) | $2; %endif } else { if (pragmas & PRAGMA_STRICT_TYPES) { if (strcmp($3, "create") != 0) yyerror("\"#pragma strict_types\" requires type of function"); else exact_types = TYPE_VOID; /* default for create() */ } else exact_types = 0; } } '(' argument ')' { char *p = $3; $3 = make_shared_string($3); scratch_free(p); /* If we had nested functions, we would need to check */ /* here if we have enough space for locals */ /* * Define a prototype. If it is a real function, then the * prototype will be replaced below. */ $$ = FUNC_PROTOTYPE; if ($6.flags & ARG_IS_VARARGS) { $$ |= (FUNC_TRUE_VARARGS | FUNC_VARARGS); } $$ |= ($1 >> 16); define_new_function($3, $6.num_arg, 0, $$, ($1 & 0xffff)| $2); /* This is safe since it is guaranteed to be in the function table, so it can't be dangling */ free_string($3); context = 0; } block_or_semi { /* Either a prototype or a block */ if ($9) { int fun; $8 &= ~FUNC_PROTOTYPE; if ($9->kind != NODE_RETURN && ($9->kind != NODE_TWO_VALUES || $9->r.expr->kind != NODE_RETURN)) { parse_node_t *replacement; CREATE_STATEMENTS(replacement, $9, 0); CREATE_RETURN(replacement->r.expr, 0); $9 = replacement; } fun = define_new_function($3, $6.num_arg, max_num_locals - $6.num_arg, $8, ($1 & 0xffff) | $2); if (fun != -1) { $$ = new_node_no_line(); $$->kind = NODE_FUNCTION; $$->v.number = fun; $$->l.number = max_num_locals; $$->r.expr = $9; } else $$ = 0; } else $$ = 0; free_all_local_names(!!$9); } | type name_list ';' { if (!($1 & ~(DECL_MODS)) && (pragmas & PRAGMA_STRICT_TYPES)) yyerror("Missing type for global variable declaration"); $$ = 0; } | inheritance | type_decl | modifier_change ; modifier_change: type_modifier_list ':' { if (!$1) yyerror("modifier list may not be empty."); if ($1 & FUNC_VARARGS) { yyerror("Illegal modifier 'varargs' in global modifier list."); $1 &= ~FUNC_VARARGS; } if (!($1 & DECL_ACCESS)) $1 |= DECL_PUBLIC; global_modifiers = $1; $$ = 0; } ; member_name: optional_star identifier { /* At this point, the current_type here is only a basic_type */ /* and cannot be unused yet - Sym */ if (current_type == TYPE_VOID) yyerror("Illegal to declare class member of type void."); add_local_name($2, current_type | $1); scratch_free($2); } ; member_name_list: member_name | member_name ',' member_name_list ; member_list: /* empty */ | member_list basic_type { current_type = $2; } member_name_list ';' ; type_decl: type_modifier_list L_CLASS identifier '{' { ident_hash_elem_t *ihe; ihe = find_or_add_ident( PROG_STRING($$ = store_prog_string($3)), FOA_GLOBAL_SCOPE); if (ihe->dn.class_num == -1) { ihe->sem_value++; ihe->dn.class_num = mem_block[A_CLASS_DEF].current_size / sizeof(class_def_t); if (ihe->dn.class_num > CLASS_NUM_MASK){ char buf[256]; char *p; p = buf; sprintf(p, "Too many classes, max is %d.\n", CLASS_NUM_MASK + 1); yyerror(buf); } scratch_free($3); $2 = 0; } else { $2 = ihe; } } member_list '}' { class_def_t *sd; class_member_entry_t *sme; int i, raise_error = 0; /* check for a redefinition */ if ($2 != 0) { sd = CLASS($2->dn.class_num); if (sd->size != current_number_of_locals) raise_error = 1; else { i = sd->size; sme = (class_member_entry_t *)mem_block[A_CLASS_MEMBER].block + sd->index; while (i--) { /* check for matching names and types */ if (strcmp(PROG_STRING(sme[i].membername), locals_ptr[i].ihe->name) != 0 || sme[i].type != (type_of_locals_ptr[i] & ~LOCAL_MODS)) { raise_error = 1; break; } } } } if (raise_error) { char buf[512]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Illegal to redefine class "); p = strput(p, end, PROG_STRING($$)); yyerror(buf); } else { sd = (class_def_t *)allocate_in_mem_block(A_CLASS_DEF, sizeof(class_def_t)); i = sd->size = current_number_of_locals; sd->index = mem_block[A_CLASS_MEMBER].current_size / sizeof(class_member_entry_t); sd->classname = $5; sme = (class_member_entry_t *)allocate_in_mem_block(A_CLASS_MEMBER, sizeof(class_member_entry_t) * current_number_of_locals); while (i--) { sme[i].membername = store_prog_string(locals_ptr[i].ihe->name); sme[i].type = type_of_locals_ptr[i] & ~LOCAL_MODS; } } free_all_local_names(0); $$ = 0; } ; new_local_name: L_IDENTIFIER | L_DEFINED_NAME { if ($1->dn.local_num != -1) { char buff[256]; char *end = EndOf(buff); char *p; p = strput(buff, end, "Illegal to redeclare local name '"); p = strput(p, end, $1->name); p = strput(p, end, "'"); yyerror(buff); } $$ = scratch_copy($1->name); } ; atomic_type: L_BASIC_TYPE | L_CLASS L_DEFINED_NAME { if ($2->dn.class_num == -1) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Undefined class '"); p = strput(p, end, $2->name); p = strput(p, end, "'"); yyerror(buf); $$ = TYPE_ANY; } else { $$ = $2->dn.class_num | TYPE_MOD_CLASS; } } | L_CLASS L_IDENTIFIER { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Undefined class '"); p = strput(p, end, $2); p = strput(p, end, "'"); yyerror(buf); $$ = TYPE_ANY; } ; %ifdef ARRAY_RESERVED_WORD opt_atomic_type: atomic_type | /* empty */ { $$ = TYPE_ANY; } ; %endif basic_type: atomic_type %ifdef ARRAY_RESERVED_WORD | opt_atomic_type L_ARRAY { $$ = $1 | TYPE_MOD_ARRAY; } %endif ; arg_type: basic_type %ifdef REF_RESERVED_WORD | basic_type ref { $$ = $1 | LOCAL_MOD_REF; } %endif ; new_arg: arg_type optional_star { $$ = $1 | $2; if ($1 != TYPE_VOID) add_local_name("", $1 | $2); } | arg_type optional_star new_local_name { if ($1 == TYPE_VOID) yyerror("Illegal to declare argument of type void."); add_local_name($3, $1 | $2); scratch_free($3); $$ = $1 | $2; } | new_local_name { if (exact_types) { yyerror("Missing type for argument"); } add_local_name($1, TYPE_ANY); scratch_free($1); $$ = TYPE_ANY; } ; argument: /* empty */ { $$.num_arg = 0; $$.flags = 0; } | argument_list | argument_list L_DOT_DOT_DOT { int x = type_of_locals_ptr[max_num_locals-1]; int lt = x & ~LOCAL_MODS; $$ = $1; $$.flags |= ARG_IS_VARARGS; if (x & LOCAL_MOD_REF) { yyerror("Variable to hold remainder of args may not be a reference"); x &= ~LOCAL_MOD_REF; } if (lt != TYPE_ANY && !(lt & TYPE_MOD_ARRAY)) yywarn("Variable to hold remainder of arguments should be an array."); } ; argument_list: new_arg { if (($1 & TYPE_MASK) == TYPE_VOID && !($1 & TYPE_MOD_CLASS)) { if ($1 & ~TYPE_MASK) yyerror("Illegal to declare argument of type void."); $$.num_arg = 0; } else { $$.num_arg = 1; } $$.flags = 0; } | argument_list ',' new_arg { if (!$$.num_arg) /* first arg was void w/no name */ yyerror("argument of type void must be the only argument."); if (($3 & TYPE_MASK) == TYPE_VOID && !($3 & TYPE_MOD_CLASS)) yyerror("Illegal to declare argument of type void."); $$ = $1; $$.num_arg++; } ; type_modifier_list: /* empty */ { $$ = 0; } | L_TYPE_MODIFIER type_modifier_list { %ifdef SENSIBLE_MODIFIERS int acc_mod; %endif $$ = $1 | $2; %ifdef SENSIBLE_MODIFIERS acc_mod = ($$ & DECL_ACCESS) & ~global_modifiers; if (acc_mod & (acc_mod - 1)) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Multiple access modifiers ("); p = get_type_modifiers(p, end, $$); p = strput(p, end, ") "); yyerror(buf); $$ = DECL_PUBLIC; } %endif } ; type: type_modifier_list opt_basic_type { $$ = ($1 << 16) | $2; current_type = $$; } ; cast: '(' basic_type optional_star ')' { $$ = $2 | $3; } ; opt_basic_type: basic_type | /* empty */ { $$ = TYPE_UNKNOWN; } ; name_list: new_name | new_name ',' name_list ; new_name: optional_star identifier { if (current_type & (FUNC_VARARGS << 16)){ yyerror("Illegal to declare varargs variable."); current_type &= ~(FUNC_VARARGS << 16); } /* Now it is ok to merge the two * remember that class_num and varargs was the reason for above * Do the merging once only per row of decls */ if (current_type & 0xffff0000){ current_type = (current_type >> 16) | (current_type & 0xffff); } current_type |= global_modifiers; if (!(current_type & DECL_ACCESS)) current_type |= DECL_PUBLIC; if ((current_type & ~DECL_MODS) == TYPE_VOID) yyerror("Illegal to declare global variable of type void."); define_new_variable($2, current_type | $1); scratch_free($2); } | optional_star identifier L_ASSIGN expr0 { parse_node_t *expr, *newnode; int type; if (current_type & (FUNC_VARARGS << 16)){ yyerror("Illegal to declare varargs variable."); current_type &= ~(FUNC_VARARGS << 16); } if (current_type & 0xffff0000){ current_type = (current_type >> 16) | (current_type & 0xffff); } current_type |= global_modifiers; if (!(current_type & DECL_ACCESS)) current_type |= DECL_PUBLIC; if ((current_type & ~DECL_MODS) == TYPE_VOID) yyerror("Illegal to declare global variable of type void."); if ($3 != F_ASSIGN) yyerror("Only '=' is legal in initializers."); /* ignore current_type == 0, which gets a missing type error later anyway */ if (current_type) { type = (current_type | $1) & ~DECL_MODS; if ((current_type & ~DECL_MODS) == TYPE_VOID) yyerror("Illegal to declare global variable of type void."); if (!compatible_types(type, $4->type)) { char buff[256]; char *end = EndOf(buff); char *p; p = strput(buff, end, "Type mismatch "); p = get_two_types(p, end, type, $4->type); p = strput(p, end, " when initializing "); p = strput(p, end, $2); yyerror(buff); } } else type = 0; $4 = do_promotions($4, type); CREATE_BINARY_OP(expr, F_VOID_ASSIGN, 0, $4, 0); CREATE_OPCODE_1(expr->r.expr, F_GLOBAL_LVALUE, 0, define_new_variable($2, current_type | $1)); newnode = comp_trees[TREE_INIT]; CREATE_TWO_VALUES(comp_trees[TREE_INIT], 0, newnode, expr); scratch_free($2); } ; block: '{' local_declarations statements '}' { if ($2.node && $3) { CREATE_STATEMENTS($$.node, $2.node, $3); } else $$.node = ($2.node ? $2.node : $3); $$.num = $2.num; } ; decl_block: block | for | foreach ; local_declarations: /* empty */ { $$.node = 0; $$.num = 0; } | local_declarations basic_type { if ($2 == TYPE_VOID) yyerror("Illegal to declare local variable of type void."); /* can't do this in basic_type b/c local_name_list contains * expr0 which contains cast which contains basic_type */ current_type = $2; } local_name_list ';' { if ($1.node && $4.node) { CREATE_STATEMENTS($$.node, $1.node, $4.node); } else $$.node = ($1.node ? $1.node : $4.node); $$.num = $1.num + $4.num; } ; new_local_def: optional_star new_local_name { if (current_type & LOCAL_MOD_REF) { yyerror("Illegal to declare local variable as reference"); current_type &= ~LOCAL_MOD_REF; } add_local_name($2, current_type | $1 | LOCAL_MOD_UNUSED); scratch_free($2); $$ = 0; } | optional_star new_local_name L_ASSIGN expr0 { int type = (current_type | $1) & ~DECL_MODS; if (current_type & LOCAL_MOD_REF) { yyerror("Illegal to declare local variable as reference"); current_type &= ~LOCAL_MOD_REF; type &= ~LOCAL_MOD_REF; } if ($3 != F_ASSIGN) yyerror("Only '=' is allowed in initializers."); if (!compatible_types($4->type, type)) { char buff[256]; char *end = EndOf(buff); char *p; p = strput(buff, end, "Type mismatch "); p = get_two_types(p, end, type, $4->type); p = strput(p, end, " when initializing "); p = strput(p, end, $2); yyerror(buff); } $4 = do_promotions($4, type); CREATE_UNARY_OP_1($$, F_VOID_ASSIGN_LOCAL, 0, $4, add_local_name($2, current_type | $1 | LOCAL_MOD_UNUSED)); scratch_free($2); } ; single_new_local_def: arg_type optional_star new_local_name { if ($1 == TYPE_VOID) yyerror("Illegal to declare local variable of type void."); $$ = add_local_name($3, $1 | $2); scratch_free($3); } ; single_new_local_def_with_init: single_new_local_def L_ASSIGN expr0 { int type = type_of_locals_ptr[$1]; if (type & LOCAL_MOD_REF) { yyerror("Illegal to declare local variable as reference"); type_of_locals_ptr[$1] &= ~LOCAL_MOD_REF; } type &= ~LOCAL_MODS; if ($2 != F_ASSIGN) yyerror("Only '=' is allowed in initializers."); if (!compatible_types($3->type, type)) { char buff[256]; char *end = EndOf(buff); char *p; p = strput(buff, end, "Type mismatch "); p = get_two_types(p, end, type, $3->type); p = strput(p, end, " when initializing."); yyerror(buff); } $3 = do_promotions($3, type); /* this is an expression */ CREATE_BINARY_OP($$, F_ASSIGN, 0, $3, 0); CREATE_OPCODE_1($$->r.expr, F_LOCAL_LVALUE, 0, $1); } ; local_name_list: new_local_def { $$.node = $1; $$.num = 1; } | new_local_def ',' local_name_list { if ($1 && $3.node) { CREATE_STATEMENTS($$.node, $1, $3.node); } else $$.node = ($1 ? $1 : $3.node); $$.num = 1 + $3.num; } ; statements: /* empty */ { $$ = 0; } | statement statements { if ($1 && $2) { CREATE_STATEMENTS($$, $1, $2); } else $$ = ($1 ? $1 : $2); } | error ';' { $$ = 0; } ; statement: comma_expr ';' { $$ = pop_value($1); #ifdef DEBUG { parse_node_t *replacement; CREATE_STATEMENTS(replacement, $$, 0); CREATE_OPCODE(replacement->r.expr, F_BREAK_POINT, 0); $$ = replacement; } #endif } | cond | while | do | switch | return | decl_block { $$ = $1.node; pop_n_locals($1.num); } | /* empty */ ';' { $$ = 0; } | L_BREAK ';' { if (context & SPECIAL_CONTEXT) { yyerror("Cannot break out of catch { } or time_expression { }"); $$ = 0; } else if (context & SWITCH_CONTEXT) { CREATE_CONTROL_JUMP($$, CJ_BREAK_SWITCH); } else if (context & LOOP_CONTEXT) { CREATE_CONTROL_JUMP($$, CJ_BREAK); if (context & LOOP_FOREACH) { parse_node_t *replace; CREATE_STATEMENTS(replace, 0, $$); CREATE_OPCODE(replace->l.expr, F_EXIT_FOREACH, 0); $$ = replace; } } else { yyerror("break statement outside loop"); $$ = 0; } } | L_CONTINUE ';' { if (context & SPECIAL_CONTEXT) yyerror("Cannot continue out of catch { } or time_expression { }"); else if (!(context & LOOP_CONTEXT)) yyerror("continue statement outside loop"); CREATE_CONTROL_JUMP($$, CJ_CONTINUE); } ; while: L_WHILE '(' comma_expr ')' { $1 = context; context = LOOP_CONTEXT; } statement { CREATE_LOOP($$, 1, $6, 0, optimize_loop_test($3)); context = $1; } ; do: L_DO { $1 = context; context = LOOP_CONTEXT; } statement L_WHILE '(' comma_expr ')' ';' { CREATE_LOOP($$, 0, $3, 0, optimize_loop_test($6)); context = $1; } ; for: L_FOR '(' first_for_expr ';' for_expr ';' for_expr ')' { $3.node = pop_value($3.node); $1 = context; context = LOOP_CONTEXT; } statement { $$.num = $3.num; /* number of declarations (0/1) */ $7 = pop_value($7); if ($7 && IS_NODE($7, NODE_UNARY_OP, F_INC) && IS_NODE($7->r.expr, NODE_OPCODE_1, F_LOCAL_LVALUE)) { long lvar = $7->r.expr->l.number; CREATE_OPCODE_1($7, F_LOOP_INCR, 0, lvar); } CREATE_STATEMENTS($$.node, $3.node, 0); CREATE_LOOP($$.node->r.expr, 1, $10, $7, optimize_loop_test($5)); context = $1; } ; foreach_var: L_DEFINED_NAME { if ($1->dn.local_num != -1) { CREATE_OPCODE_1($$.node, F_LOCAL_LVALUE, 0, $1->dn.local_num); type_of_locals_ptr[$1->dn.local_num] &= ~LOCAL_MOD_UNUSED; } else if ($1->dn.global_num != -1) { CREATE_OPCODE_1($$.node, F_GLOBAL_LVALUE, 0, $1->dn.global_num); } else { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "'"); p = strput(p, end, $1->name); p = strput(p, end, "' is not a local or a global variable."); yyerror(buf); CREATE_OPCODE_1($$.node, F_GLOBAL_LVALUE, 0, 0); } $$.num = 0; } | single_new_local_def { if (type_of_locals_ptr[$1] & LOCAL_MOD_REF) { CREATE_OPCODE_1($$.node, F_REF_LVALUE, 0, $1); } else { CREATE_OPCODE_1($$.node, F_LOCAL_LVALUE, 0, $1); type_of_locals_ptr[$1] &= ~LOCAL_MOD_UNUSED; } $$.num = 1; } | L_IDENTIFIER { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "'"); p = strput(p, end, $1); p = strput(p, end, "' is not a local or a global variable."); yyerror(buf); CREATE_OPCODE_1($$.node, F_GLOBAL_LVALUE, 0, 0); scratch_free($1); $$.num = 0; } ; foreach_vars: foreach_var { CREATE_FOREACH($$.node, $1.node, 0); $$.num = $1.num; } | foreach_var ',' foreach_var { CREATE_FOREACH($$.node, $1.node, $3.node); $$.num = $1.num + $3.num; if ($1.node->v.number == F_REF_LVALUE) yyerror("Mapping key may not be a reference in foreach()"); } ; foreach: L_FOREACH '(' foreach_vars L_IN expr0 ')' { $3.node->v.expr = $5; $1 = context; context = LOOP_CONTEXT | LOOP_FOREACH; } statement { $$.num = $3.num; CREATE_STATEMENTS($$.node, $3.node, 0); CREATE_LOOP($$.node->r.expr, 2, $8, 0, 0); CREATE_OPCODE($$.node->r.expr->r.expr, F_NEXT_FOREACH, 0); context = $1; } ; for_expr: /* EMPTY */ { $$ = 0; } | comma_expr ; first_for_expr: for_expr { $$.node = $1; $$.num = 0; } | single_new_local_def_with_init { $$.node = $1; $$.num = 1; } ; switch: L_SWITCH '(' comma_expr ')' { $1 = context; context &= LOOP_CONTEXT; context |= SWITCH_CONTEXT; $2 = mem_block[A_CASES].current_size; } '{' local_declarations case switch_block '}' { parse_node_t *node1, *node2; if ($9) { CREATE_STATEMENTS(node1, $8, $9); } else node1 = $8; if (context & SWITCH_STRINGS) { NODE_NO_LINE(node2, NODE_SWITCH_STRINGS); } else if (context & SWITCH_RANGES) { NODE_NO_LINE(node2, NODE_SWITCH_RANGES); } else if ((context & SWITCH_NUMBERS) || (context & SWITCH_NOT_EMPTY)) { NODE_NO_LINE(node2, NODE_SWITCH_NUMBERS); } else { // to prevent crashing during the remaining parsing bits NODE_NO_LINE(node2, NODE_SWITCH_NUMBERS); yyerror("need case statements in switch/case, not just default:"); //just a default case present } node2->l.expr = $3; node2->r.expr = node1; prepare_cases(node2, $2); context = $1; $$ = node2; pop_n_locals($7.num); } ; switch_block: case switch_block { if ($2){ CREATE_STATEMENTS($$, $1, $2); } else $$ = $1; } | statement switch_block { if ($2){ CREATE_STATEMENTS($$, $1, $2); } else $$ = $1; } | /* empty */ { $$ = 0; } ; case: L_CASE case_label ':' { $$ = $2; $$->v.expr = 0; add_to_mem_block(A_CASES, (char *)&($2), sizeof($2)); } | L_CASE case_label L_RANGE case_label ':' { if ( $2->kind != NODE_CASE_NUMBER || $4->kind != NODE_CASE_NUMBER ) yyerror("String case labels not allowed as range bounds"); if ($2->r.number > $4->r.number) break; context |= SWITCH_RANGES; $$ = $2; $$->v.expr = $4; add_to_mem_block(A_CASES, (char *)&($2), sizeof($2)); } | L_CASE case_label L_RANGE ':' { if ( $2->kind != NODE_CASE_NUMBER ) yyerror("String case labels not allowed as range bounds"); context |= SWITCH_RANGES; $$ = $2; $$->v.expr = new_node(); $$->v.expr->kind = NODE_CASE_NUMBER; $$->v.expr->r.number = ((unsigned long)-1)/2; //maxint add_to_mem_block(A_CASES, (char *)&($2), sizeof($2)); } | L_CASE L_RANGE case_label ':' { if ( $3->kind != NODE_CASE_NUMBER ) yyerror("String case labels not allowed as range bounds"); context |= SWITCH_RANGES; $$ = new_node(); $$->kind = NODE_CASE_NUMBER; $$->r.number = (long) 1+ ((unsigned long)-1)/2; //maxint +1 wraps to min_int, on all computers i know, just not in the C standard iirc $$->v.expr = $3; add_to_mem_block(A_CASES, (char *)&($$), sizeof($$)); } | L_DEFAULT ':' { if (context & SWITCH_DEFAULT) { yyerror("Duplicate default"); $$ = 0; break; } $$ = new_node(); $$->kind = NODE_DEFAULT; $$->v.expr = 0; add_to_mem_block(A_CASES, (char *)&($$), sizeof($$)); context |= SWITCH_DEFAULT; } ; case_label: constant { if ((context & SWITCH_STRINGS) && $1) yyerror("Mixed case label list not allowed"); if ($1) context |= SWITCH_NUMBERS; else context |= SWITCH_NOT_EMPTY; $$ = new_node(); $$->kind = NODE_CASE_NUMBER; $$->r.expr = (parse_node_t *)$1; } | string_con1 { int str; str = store_prog_string($1); scratch_free($1); if (context & SWITCH_NUMBERS) yyerror("Mixed case label list not allowed"); context |= SWITCH_STRINGS; $$ = new_node(); $$->kind = NODE_CASE_STRING; $$->r.number = str; } ; constant: constant '|' constant { $$ = $1 | $3; } | constant '^' constant { $$ = $1 ^ $3; } | constant '&' constant { $$ = $1 & $3; } | constant L_EQ constant { $$ = $1 == $3; } | constant L_NE constant { $$ = $1 != $3; } | constant L_ORDER constant { switch($2){ case F_GE: $$ = $1 >= $3; break; case F_LE: $$ = $1 <= $3; break; case F_GT: $$ = $1 > $3; break; } } | constant '<' constant { $$ = $1 < $3; } | constant L_LSH constant { $$ = $1 << $3; } | constant L_RSH constant { $$ = $1 >> $3; } | constant '+' constant { $$ = $1 + $3; } | constant '-' constant { $$ = $1 - $3; } | constant '*' constant { $$ = $1 * $3; } | constant '%' constant { if ($3) $$ = $1 % $3; else yyerror("Modulo by zero"); } | constant '/' constant { if ($3) $$ = $1 / $3; else yyerror("Division by zero"); } | '(' constant ')' { $$ = $2; } | L_NUMBER { $$ = $1; } | '-' L_NUMBER { $$ = -$2; } | L_NOT L_NUMBER { $$ = !$2; } | '~' L_NUMBER { $$ = ~$2; } ; comma_expr: expr0 { $$ = $1; } | comma_expr ',' expr0 { CREATE_TWO_VALUES($$, $3->type, pop_value($1), $3); } ; %ifdef REF_RESERVED_WORD ref: L_REF %ifdef COMPAT_32 | '&' %endif ; %endif expr0: %ifdef REF_RESERVED_WORD ref lvalue { int op; if (!(context & ARG_LIST)) yyerror("ref illegal outside function argument list"); else num_refs++; switch ($2->kind) { case NODE_PARAMETER_LVALUE: op = F_LOCAL_LVALUE; break; case NODE_TERNARY_OP: case NODE_OPCODE_1: case NODE_UNARY_OP_1: case NODE_BINARY_OP: op = $2->v.number; if (op > F_RINDEX_LVALUE) yyerror("Illegal to make reference to range"); break; default: op=0; //0 is harmless, i hope yyerror("unknown lvalue kind"); } CREATE_UNARY_OP_1($$, F_MAKE_REF, TYPE_ANY, $2, op); } | %endif lvalue L_ASSIGN expr0 { parse_node_t *l = $1, *r = $3; /* set this up here so we can change it below */ /* assignments are backwards; rhs is evaluated before lhs, so put the RIGHT hand side on the LEFT hand side of the tree node. */ CREATE_BINARY_OP($$, $2, r->type, r, l); if (exact_types && !compatible_types(r->type, l->type) && !($2 == F_ADD_EQ && l->type == TYPE_STRING && (COMP_TYPE(r->type, TYPE_NUMBER))||r->type == TYPE_OBJECT)) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Bad assignment "); p = get_two_types(p, end, l->type, r->type); p = strput(p, end, "."); yyerror(buf); } if ($2 == F_ASSIGN) $$->l.expr = do_promotions(r, l->type); } | error L_ASSIGN expr0 { yyerror("Illegal LHS"); CREATE_ERROR($$); } | expr0 '?' expr0 ':' expr0 %prec '?' { parse_node_t *p1 = $3, *p2 = $5; if (exact_types && !compatible_types2(p1->type, p2->type)) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Types in ?: do not match "); p = get_two_types(p, end, p1->type, p2->type); p = strput(p, end, "."); yywarn(buf); } /* optimize if last expression did F_NOT */ if (IS_NODE($1, NODE_UNARY_OP, F_NOT)) { /* !a ? b : c --> a ? c : b */ CREATE_IF($$, $1->r.expr, p2, p1); } else { CREATE_IF($$, $1, p1, p2); } $$->type = ((p1->type == p2->type) ? p1->type : TYPE_ANY); } | expr0 L_LOR expr0 { CREATE_LAND_LOR($$, F_LOR, $1, $3); if (IS_NODE($1, NODE_LAND_LOR, F_LOR)) $1->kind = NODE_BRANCH_LINK; } | expr0 L_LAND expr0 { CREATE_LAND_LOR($$, F_LAND, $1, $3); if (IS_NODE($1, NODE_LAND_LOR, F_LAND)) $1->kind = NODE_BRANCH_LINK; } | expr0 '|' expr0 { int t1 = $1->type, t3 = $3->type; if (is_boolean($1) && is_boolean($3)) yywarn("bitwise operation on boolean values."); if ((t1 & TYPE_MOD_ARRAY) || (t3 & TYPE_MOD_ARRAY)) { if (t1 != t3) { if ((t1 != TYPE_ANY) && (t3 != TYPE_ANY) && !(t1 & t3 & TYPE_MOD_ARRAY)) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Incompatible types for | "); p = get_two_types(p, end, t1, t3); p = strput(p, end, "."); yyerror(buf); } t1 = TYPE_ANY | TYPE_MOD_ARRAY; } CREATE_BINARY_OP($$, F_OR, t1, $1, $3); } else $$ = binary_int_op($1, $3, F_OR, "|"); } | expr0 '^' expr0 { $$ = binary_int_op($1, $3, F_XOR, "^"); } | expr0 '&' expr0 { int t1 = $1->type, t3 = $3->type; if (is_boolean($1) && is_boolean($3)) yywarn("bitwise operation on boolean values."); if ((t1 & TYPE_MOD_ARRAY) || (t3 & TYPE_MOD_ARRAY)) { if (t1 != t3) { if ((t1 != TYPE_ANY) && (t3 != TYPE_ANY) && !(t1 & t3 & TYPE_MOD_ARRAY)) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Incompatible types for & "); p = get_two_types(p, end, t1, t3); p = strput(p, end, "."); yyerror(buf); } t1 = TYPE_ANY | TYPE_MOD_ARRAY; } CREATE_BINARY_OP($$, F_AND, t1, $1, $3); } else $$ = binary_int_op($1, $3, F_AND, "&"); } | expr0 L_EQ expr0 { if (exact_types && !compatible_types2($1->type, $3->type)){ char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "== always false because of incompatible types "); p = get_two_types(p, end, $1->type, $3->type); p = strput(p, end, "."); yyerror(buf); } /* x == 0 -> !x */ if (IS_NODE($1, NODE_NUMBER, 0)) { CREATE_UNARY_OP($$, F_NOT, TYPE_NUMBER, $3); } else if (IS_NODE($3, NODE_NUMBER, 0)) { CREATE_UNARY_OP($$, F_NOT, TYPE_NUMBER, $1); } else { CREATE_BINARY_OP($$, F_EQ, TYPE_NUMBER, $1, $3); } } | expr0 L_NE expr0 { if (exact_types && !compatible_types2($1->type, $3->type)){ char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "!= always true because of incompatible types "); p = get_two_types(p, end, $1->type, $3->type); p = strput(p, end, "."); yyerror(buf); } CREATE_BINARY_OP($$, F_NE, TYPE_NUMBER, $1, $3); } | expr0 L_ORDER expr0 { if (exact_types) { int t1 = $1->type; int t3 = $3->type; if (!COMP_TYPE(t1, TYPE_NUMBER) && !COMP_TYPE(t1, TYPE_STRING)) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Bad left argument to '"); p = strput(p, end, query_instr_name($2)); p = strput(p, end, "' : \""); p = get_type_name(p, end, t1); p = strput(p, end, "\""); yyerror(buf); } else if (!COMP_TYPE(t3, TYPE_NUMBER) && !COMP_TYPE(t3, TYPE_STRING)) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Bad right argument to '"); p = strput(p, end, query_instr_name($2)); p = strput(p, end, "' : \""); p = get_type_name(p, end, t3); p = strput(p, end, "\""); yyerror(buf); } else if (!compatible_types2(t1,t3)) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Arguments to "); p = strput(p, end, query_instr_name($2)); p = strput(p, end, " do not have compatible types : "); p = get_two_types(p, end, t1, t3); yyerror(buf); } } CREATE_BINARY_OP($$, $2, TYPE_NUMBER, $1, $3); } | expr0 '<' expr0 { if (exact_types) { int t1 = $1->type, t3 = $3->type; if (!COMP_TYPE(t1, TYPE_NUMBER) && !COMP_TYPE(t1, TYPE_STRING)) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Bad left argument to '<' : \""); p = get_type_name(p, end, t1); p = strput(p, end, "\""); yyerror(buf); } else if (!COMP_TYPE(t3, TYPE_NUMBER) && !COMP_TYPE(t3, TYPE_STRING)) { char buf[200]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Bad right argument to '<' : \""); p = get_type_name(p, end, t3); p = strput(p, end, "\""); yyerror(buf); } else if (!compatible_types2(t1,t3)) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Arguments to < do not have compatible types : "); p = get_two_types(p, end, t1, t3); yyerror(buf); } } CREATE_BINARY_OP($$, F_LT, TYPE_NUMBER, $1, $3); } | expr0 L_LSH expr0 { $$ = binary_int_op($1, $3, F_LSH, "<<"); } | expr0 L_RSH expr0 { $$ = binary_int_op($1, $3, F_RSH, ">>"); } | expr0 '+' expr0 { int result_type; if (exact_types) { int t1 = $1->type, t3 = $3->type; if (t1 == t3){ #ifdef CAST_CALL_OTHERS if (t1 == TYPE_UNKNOWN){ yyerror("Bad arguments to '+' (unknown vs unknown)"); result_type = TYPE_ANY; } else #endif result_type = t1; } else if (t1 == TYPE_ANY) { if (t3 == TYPE_FUNCTION) { yyerror("Bad right argument to '+' (function)"); result_type = TYPE_ANY; } else result_type = t3; } else if (t3 == TYPE_ANY) { if (t1 == TYPE_FUNCTION) { yyerror("Bad left argument to '+' (function)"); result_type = TYPE_ANY; } else result_type = t1; } else { switch(t1) { case TYPE_OBJECT: if(t3 == TYPE_STRING){ result_type = TYPE_STRING; } else goto add_error; break; case TYPE_STRING: { if (t3 == TYPE_REAL || t3 == TYPE_NUMBER || t3 == TYPE_OBJECT){ result_type = TYPE_STRING; } else goto add_error; break; } case TYPE_NUMBER: { if (t3 == TYPE_REAL || t3 == TYPE_STRING) result_type = t3; else goto add_error; break; } case TYPE_REAL: { if (t3 == TYPE_NUMBER) result_type = TYPE_REAL; else if (t3 == TYPE_STRING) result_type = TYPE_STRING; else goto add_error; break; } default: { if (t1 & t3 & TYPE_MOD_ARRAY) { result_type = TYPE_ANY|TYPE_MOD_ARRAY; break; } add_error: { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Invalid argument types to '+' "); p = get_two_types(p, end, t1, t3); yyerror(buf); result_type = TYPE_ANY; } } } } } else result_type = TYPE_ANY; /* TODO: perhaps we should do (string)+(number) and * (number)+(string) constant folding as well. * * codefor string x = "foo" + 1; * * 0000: push string 13, number 1 * 0004: + * 0005: (void)assign_local LV0 */ switch ($1->kind) { case NODE_NUMBER: /* 0 + X */ if ($1->v.number == 0 && ($3->type == TYPE_NUMBER || $3->type == TYPE_REAL)) { $$ = $3; break; } if ($3->kind == NODE_NUMBER) { $$ = $1; $1->v.number += $3->v.number; break; } if ($3->kind == NODE_REAL) { $$ = $3; $3->v.real += $1->v.number; break; } /* swapping the nodes may help later constant folding */ if ($3->type != TYPE_STRING && $3->type != TYPE_ANY) CREATE_BINARY_OP($$, F_ADD, result_type, $3, $1); else CREATE_BINARY_OP($$, F_ADD, result_type, $1, $3); break; case NODE_REAL: if ($3->kind == NODE_NUMBER) { $$ = $1; $1->v.real += $3->v.number; break; } if ($3->kind == NODE_REAL) { $$ = $1; $1->v.real += $3->v.real; break; } /* swapping the nodes may help later constant folding */ if ($3->type != TYPE_STRING && $3->type != TYPE_ANY) CREATE_BINARY_OP($$, F_ADD, result_type, $3, $1); else CREATE_BINARY_OP($$, F_ADD, result_type, $1, $3); break; case NODE_STRING: if ($3->kind == NODE_STRING) { /* Combine strings */ long n1, n2; const char *s1, *s2; char *news; int l; n1 = $1->v.number; n2 = $3->v.number; s1 = PROG_STRING(n1); s2 = PROG_STRING(n2); news = (char *)DXALLOC( (l = strlen(s1))+strlen(s2)+1, TAG_COMPILER, "combine string" ); strcpy(news, s1); strcat(news + l, s2); /* free old strings (ordering may help shrink table) */ if (n1 > n2) { free_prog_string(n1); free_prog_string(n2); } else { free_prog_string(n2); free_prog_string(n1); } $$ = $1; $$->v.number = store_prog_string(news); FREE(news); break; } /* Yes, this can actually happen for absurd code like: * (int)"foo" + 0 * for which I guess we ought to generate (int)"foo" * in order to be consistent. Then shoot the coder. */ /* FALLTHROUGH */ default: /* X + 0 */ if (IS_NODE($3, NODE_NUMBER, 0) && ($1->type == TYPE_NUMBER || $1->type == TYPE_REAL)) { $$ = $1; break; } CREATE_BINARY_OP($$, F_ADD, result_type, $1, $3); break; } } | expr0 '-' expr0 { int result_type; if (exact_types) { int t1 = $1->type, t3 = $3->type; if (t1 == t3){ switch(t1){ case TYPE_ANY: case TYPE_NUMBER: case TYPE_REAL: result_type = t1; break; default: if (!(t1 & TYPE_MOD_ARRAY)){ type_error("Bad argument number 1 to '-'", t1); result_type = TYPE_ANY; } else result_type = t1; } } else if (t1 == TYPE_ANY){ switch(t3){ case TYPE_REAL: case TYPE_NUMBER: result_type = t3; break; default: if (!(t3 & TYPE_MOD_ARRAY)){ type_error("Bad argument number 2 to '-'", t3); result_type = TYPE_ANY; } else result_type = t3; } } else if (t3 == TYPE_ANY){ switch(t1){ case TYPE_REAL: case TYPE_NUMBER: result_type = t1; break; default: if (!(t1 & TYPE_MOD_ARRAY)){ type_error("Bad argument number 1 to '-'", t1); result_type = TYPE_ANY; } else result_type = t1; } } else if ((t1 == TYPE_REAL && t3 == TYPE_NUMBER) || (t3 == TYPE_REAL && t1 == TYPE_NUMBER)){ result_type = TYPE_REAL; } else if (t1 & t3 & TYPE_MOD_ARRAY){ result_type = TYPE_MOD_ARRAY|TYPE_ANY; } else { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Invalid types to '-' "); p = get_two_types(p, end, t1, t3); yyerror(buf); result_type = TYPE_ANY; } } else result_type = TYPE_ANY; switch ($1->kind) { case NODE_NUMBER: if ($1->v.number == 0) { CREATE_UNARY_OP($$, F_NEGATE, $3->type, $3); } else if ($3->kind == NODE_NUMBER) { $$ = $1; $1->v.number -= $3->v.number; } else if ($3->kind == NODE_REAL) { $$ = $3; $3->v.real = $1->v.number - $3->v.real; } else { CREATE_BINARY_OP($$, F_SUBTRACT, result_type, $1, $3); } break; case NODE_REAL: if ($3->kind == NODE_NUMBER) { $$ = $1; $1->v.real -= $3->v.number; } else if ($3->kind == NODE_REAL) { $$ = $1; $1->v.real -= $3->v.real; } else { CREATE_BINARY_OP($$, F_SUBTRACT, result_type, $1, $3); } break; default: /* optimize X-0 */ if (IS_NODE($3, NODE_NUMBER, 0)) { $$ = $1; } CREATE_BINARY_OP($$, F_SUBTRACT, result_type, $1, $3); } } | expr0 '*' expr0 { int result_type; if (exact_types){ int t1 = $1->type, t3 = $3->type; if (t1 == t3){ switch(t1){ case TYPE_MAPPING: case TYPE_ANY: case TYPE_NUMBER: case TYPE_REAL: result_type = t1; break; default: type_error("Bad argument number 1 to '*'", t1); result_type = TYPE_ANY; } } else if (t1 == TYPE_ANY || t3 == TYPE_ANY){ int t = (t1 == TYPE_ANY) ? t3 : t1; switch(t){ case TYPE_NUMBER: case TYPE_REAL: case TYPE_MAPPING: result_type = t; break; default: type_error((t1 == TYPE_ANY) ? "Bad argument number 2 to '*'" : "Bad argument number 1 to '*'", t); result_type = TYPE_ANY; } } else if ((t1 == TYPE_NUMBER && t3 == TYPE_REAL) || (t1 == TYPE_REAL && t3 == TYPE_NUMBER)){ result_type = TYPE_REAL; } else { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Invalid types to '*' "); p = get_two_types(p, end, t1, t3); yyerror(buf); result_type = TYPE_ANY; } } else result_type = TYPE_ANY; switch ($1->kind) { case NODE_NUMBER: if ($3->kind == NODE_NUMBER) { $$ = $1; $$->v.number *= $3->v.number; break; } if ($3->kind == NODE_REAL) { $$ = $3; $3->v.real *= $1->v.number; break; } CREATE_BINARY_OP($$, F_MULTIPLY, result_type, $3, $1); break; case NODE_REAL: if ($3->kind == NODE_NUMBER) { $$ = $1; $1->v.real *= $3->v.number; break; } if ($3->kind == NODE_REAL) { $$ = $1; $1->v.real *= $3->v.real; break; } CREATE_BINARY_OP($$, F_MULTIPLY, result_type, $3, $1); break; default: CREATE_BINARY_OP($$, F_MULTIPLY, result_type, $1, $3); } } | expr0 '%' expr0 { $$ = binary_int_op($1, $3, F_MOD, "%"); } | expr0 '/' expr0 { int result_type; if (exact_types){ int t1 = $1->type, t3 = $3->type; if (t1 == t3){ switch(t1){ case TYPE_NUMBER: case TYPE_REAL: case TYPE_ANY: result_type = t1; break; default: type_error("Bad argument 1 to '/'", t1); result_type = TYPE_ANY; } } else if (t1 == TYPE_ANY || t3 == TYPE_ANY){ int t = (t1 == TYPE_ANY) ? t3 : t1; if (t == TYPE_REAL || t == TYPE_NUMBER) result_type = t; else { type_error(t1 == TYPE_ANY ? "Bad argument 2 to '/'" : "Bad argument 1 to '/'", t); result_type = TYPE_ANY; } } else if ((t1 == TYPE_NUMBER && t3 == TYPE_REAL) || (t1 == TYPE_REAL && t3 == TYPE_NUMBER)) { result_type = TYPE_REAL; } else { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Invalid types to '/' "); p = get_two_types(p, end, t1, t3); yyerror(buf); result_type = TYPE_ANY; } } else result_type = TYPE_ANY; /* constant expressions */ switch ($1->kind) { case NODE_NUMBER: if ($3->kind == NODE_NUMBER) { if ($3->v.number == 0) { yyerror("Divide by zero in constant"); $$ = $1; break; } $$ = $1; $1->v.number /= $3->v.number; break; } if ($3->kind == NODE_REAL) { if ($3->v.real == 0.0) { yyerror("Divide by zero in constant"); $$ = $1; break; } $$ = $3; $3->v.real = ($1->v.number / $3->v.real); break; } CREATE_BINARY_OP($$, F_DIVIDE, result_type, $1, $3); break; case NODE_REAL: if ($3->kind == NODE_NUMBER) { if ($3->v.number == 0) { yyerror("Divide by zero in constant"); $$ = $1; break; } $$ = $1; $1->v.real /= $3->v.number; break; } if ($3->kind == NODE_REAL) { if ($3->v.real == 0.0) { yyerror("Divide by zero in constant"); $$ = $1; break; } $$ = $1; $1->v.real /= $3->v.real; break; } CREATE_BINARY_OP($$, F_DIVIDE, result_type, $1, $3); break; default: CREATE_BINARY_OP($$, F_DIVIDE, result_type, $1, $3); } } | cast expr0 %prec L_NOT { $$ = $2; $$->type = $1; if (exact_types && $2->type != $1 && $2->type != TYPE_ANY && $2->type != TYPE_UNKNOWN && $1 != TYPE_VOID) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Cannot cast "); p = get_type_name(p, end, $2->type); p = strput(p, end, "to "); p = get_type_name(p, end, $1); yyerror(buf); } } | L_INC lvalue %prec L_NOT /* note lower precedence here */ { CREATE_UNARY_OP($$, F_PRE_INC, 0, $2); if (exact_types){ switch($2->type){ case TYPE_NUMBER: case TYPE_ANY: case TYPE_REAL: { $$->type = $2->type; break; } default: { $$->type = TYPE_ANY; type_error("Bad argument 1 to ++x", $2->type); } } } else $$->type = TYPE_ANY; } | L_DEC lvalue %prec L_NOT /* note lower precedence here */ { CREATE_UNARY_OP($$, F_PRE_DEC, 0, $2); if (exact_types){ switch($2->type){ case TYPE_NUMBER: case TYPE_ANY: case TYPE_REAL: { $$->type = $2->type; break; } default: { $$->type = TYPE_ANY; type_error("Bad argument 1 to --x", $2->type); } } } else $$->type = TYPE_ANY; } | L_NOT expr0 { if ($2->kind == NODE_NUMBER) { $$ = $2; $$->v.number = !($$->v.number); } else { CREATE_UNARY_OP($$, F_NOT, TYPE_NUMBER, $2); } } | '~' expr0 { if (exact_types && !IS_TYPE($2->type, TYPE_NUMBER)) type_error("Bad argument to ~", $2->type); if ($2->kind == NODE_NUMBER) { $$ = $2; $$->v.number = ~$$->v.number; } else { CREATE_UNARY_OP($$, F_COMPL, TYPE_NUMBER, $2); } } | '-' expr0 %prec L_NOT { int result_type; if (exact_types){ int t = $2->type; if (!COMP_TYPE(t, TYPE_NUMBER)){ type_error("Bad argument to unary '-'", t); result_type = TYPE_ANY; } else result_type = t; } else result_type = TYPE_ANY; switch ($2->kind) { case NODE_NUMBER: $$ = $2; $$->v.number = -$$->v.number; break; case NODE_REAL: $$ = $2; $$->v.real = -$$->v.real; break; default: CREATE_UNARY_OP($$, F_NEGATE, result_type, $2); } } | lvalue L_INC /* normal precedence here */ { CREATE_UNARY_OP($$, F_POST_INC, 0, $1); $$->v.number = F_POST_INC; if (exact_types){ switch($1->type){ case TYPE_NUMBER: case TYPE_ANY: case TYPE_REAL: { $$->type = $1->type; break; } default: { $$->type = TYPE_ANY; type_error("Bad argument 1 to x++", $1->type); } } } else $$->type = TYPE_ANY; } | lvalue L_DEC { CREATE_UNARY_OP($$, F_POST_DEC, 0, $1); if (exact_types){ switch($1->type){ case TYPE_NUMBER: case TYPE_ANY: case TYPE_REAL: { $$->type = $1->type; break; } default: { $$->type = TYPE_ANY; type_error("Bad argument 1 to x--", $1->type); } } } else $$->type = TYPE_ANY; } | expr4 | sscanf | parse_command | time_expression | number | real ; return: L_RETURN ';' { if (exact_types && !IS_TYPE(exact_types, TYPE_VOID)) yywarn("Non-void functions must return a value."); CREATE_RETURN($$, 0); } | L_RETURN comma_expr ';' { if (exact_types && !compatible_types($2->type, exact_types)) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Type of returned value doesn't match function return type "); p = get_two_types(p, end, $2->type, exact_types); yyerror(buf); } if (IS_NODE($2, NODE_NUMBER, 0)) { CREATE_RETURN($$, 0); } else { CREATE_RETURN($$, $2); } } ; expr_list: /* empty */ { CREATE_EXPR_LIST($$, 0); } | expr_list2 { CREATE_EXPR_LIST($$, $1); } | expr_list2 ',' { CREATE_EXPR_LIST($$, $1); } ; expr_list_node: expr0 { CREATE_EXPR_NODE($$, $1, 0); } | expr0 L_DOT_DOT_DOT { CREATE_EXPR_NODE($$, $1, 1); } ; expr_list2: expr_list_node { $1->kind = 1; $$ = $1; } | expr_list2 ',' expr_list_node { $3->kind = 0; $$ = $1; $$->kind++; $$->l.expr->r.expr = $3; $$->l.expr = $3; } ; expr_list3: /* empty */ { /* this is a dummy node */ CREATE_EXPR_LIST($$, 0); } | expr_list4 { CREATE_EXPR_LIST($$, $1); } | expr_list4 ',' { CREATE_EXPR_LIST($$, $1); } ; expr_list4: assoc_pair { $$ = new_node_no_line(); $$->kind = 2; $$->v.expr = $1; $$->r.expr = 0; $$->type = 0; /* we keep track of the end of the chain in the left nodes */ $$->l.expr = $$; } | expr_list4 ',' assoc_pair { parse_node_t *expr; expr = new_node_no_line(); expr->kind = 0; expr->v.expr = $3; expr->r.expr = 0; expr->type = 0; $1->l.expr->r.expr = expr; $1->l.expr = expr; $1->kind += 2; $$ = $1; } ; assoc_pair: expr0 ':' expr0 { CREATE_TWO_VALUES($$, 0, $1, $3); } ; lvalue: expr4 { #define LV_ILLEGAL 1 #define LV_RANGE 2 #define LV_INDEX 4 /* Restrictive lvalues, but I think they make more sense :) */ $$ = $1; if($$->kind == NODE_BINARY_OP && $$->v.number == F_TYPE_CHECK) $$ = $$->l.expr; switch($$->kind) { default: yyerror("Illegal lvalue"); break; case NODE_PARAMETER: $$->kind = NODE_PARAMETER_LVALUE; break; case NODE_TERNARY_OP: $$->v.number = $$->r.expr->v.number; case NODE_OPCODE_1: case NODE_UNARY_OP_1: case NODE_BINARY_OP: if ($$->v.number >= F_LOCAL && $$->v.number <= F_MEMBER) $$->v.number++; /* make it an lvalue */ else if ($$->v.number >= F_INDEX && $$->v.number <= F_RE_RANGE) { parse_node_t *node = $$; int flag = 0; do { switch(node->kind) { case NODE_PARAMETER: node->kind = NODE_PARAMETER_LVALUE; flag |= LV_ILLEGAL; break; case NODE_TERNARY_OP: node->v.number = node->r.expr->v.number; case NODE_OPCODE_1: case NODE_UNARY_OP_1: case NODE_BINARY_OP: if(node->kind == NODE_BINARY_OP && node->v.number == F_TYPE_CHECK) { node = node->l.expr; continue; } if (node->v.number >= F_LOCAL && node->v.number <= F_MEMBER) { node->v.number++; flag |= LV_ILLEGAL; break; } else if (node->v.number == F_INDEX || node->v.number == F_RINDEX) { node->v.number++; flag |= LV_INDEX; break; } else if (node->v.number >= F_ADD_EQ && node->v.number <= F_ASSIGN) { if (!(flag & LV_INDEX)) { yyerror("Illegal lvalue, a possible lvalue is (x y)[a]"); } if (node->r.expr->kind == NODE_BINARY_OP|| node->r.expr->kind == NODE_TERNARY_OP){ if (node->r.expr->v.number >= F_NN_RANGE_LVALUE && node->r.expr->v.number <= F_NR_RANGE_LVALUE) yyerror("Illegal to have (x[a..b] y) to be the beginning of an lvalue"); } flag = LV_ILLEGAL; break; } else if (node->v.number >= F_NN_RANGE && node->v.number <= F_RE_RANGE) { if (flag & LV_RANGE) { yyerror("Can't do range lvalue of range lvalue."); flag |= LV_ILLEGAL; break; } if (flag & LV_INDEX){ yyerror("Can't do indexed lvalue of range lvalue."); flag |= LV_ILLEGAL; break; } if (node->v.number == F_NE_RANGE) { /* x[foo..] -> x[foo..<1] */ parse_node_t *rchild = node->r.expr; node->kind = NODE_TERNARY_OP; CREATE_BINARY_OP(node->r.expr, F_NR_RANGE_LVALUE, 0, 0, rchild); CREATE_NUMBER(node->r.expr->l.expr, 1); } else if (node->v.number == F_RE_RANGE) { /* x[ x[r.expr; node->kind = NODE_TERNARY_OP; CREATE_BINARY_OP(node->r.expr, F_RR_RANGE_LVALUE, 0, 0, rchild); CREATE_NUMBER(node->r.expr->l.expr, 1); } else node->r.expr->v.number++; flag |= LV_RANGE; node = node->r.expr->r.expr; continue; } default: yyerror("Illegal lvalue"); flag = LV_ILLEGAL; break; } if ((flag & LV_ILLEGAL) || !(node = node->r.expr)) break; } while (1); break; } else yyerror("Illegal lvalue"); break; } } ; l_new_function_open: L_NEW_FUNCTION_OPEN | L_FUNCTION_OPEN efun_override { $$ = ($2 << 8) | FP_EFUN; } ; %ifdef COMPAT_32 simple_function_pointer: l_new_function_open ':' ')' { $$ = $1; } | L_LAMBDA L_DEFINED_NAME { int val; if ((val=$2->dn.local_num) >= 0){ $$ = (val << 8) | FP_L_VAR; } else if ((val=$2->dn.global_num) >= 0) { $$ = (val << 8) | FP_G_VAR; } else if ((val=$2->dn.function_num) >=0) { $$ = (val << 8)|FP_LOCAL; } else if ((val=$2->dn.simul_num) >=0) { $$ = (val << 8)|FP_SIMUL; } else if ((val=$2->dn.efun_num) >=0) { $$ = (val << 8)|FP_EFUN; } } ; %endif expr4: function_call | L_DEFINED_NAME { int i; if ((i = $1->dn.local_num) != -1) { type_of_locals_ptr[i] &= ~LOCAL_MOD_UNUSED; if (type_of_locals_ptr[i] & LOCAL_MOD_REF) CREATE_OPCODE_1($$, F_REF, type_of_locals_ptr[i] & ~LOCAL_MOD_REF,i & 0xff); else CREATE_OPCODE_1($$, F_LOCAL, type_of_locals_ptr[i], i & 0xff); if (current_function_context) current_function_context->num_locals++; } else if ((i = $1->dn.global_num) != -1) { if (current_function_context) current_function_context->bindable = FP_NOT_BINDABLE; CREATE_OPCODE_1($$, F_GLOBAL, VAR_TEMP(i)->type & ~DECL_MODS, i); if (VAR_TEMP(i)->type & DECL_HIDDEN) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Illegal to use private variable '"); p = strput(p, end, $1->name); p = strput(p, end, "'"); yyerror(buf); } } else { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Undefined variable '"); p = strput(p, end, $1->name); p = strput(p, end, "'"); if (current_number_of_locals < CFG_MAX_LOCAL_VARIABLES) { add_local_name($1->name, TYPE_ANY); } CREATE_ERROR($$); yyerror(buf); } } | L_IDENTIFIER { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Undefined variable '"); p = strput(p, end, $1); p = strput(p, end, "'"); if (current_number_of_locals < CFG_MAX_LOCAL_VARIABLES) { add_local_name($1, TYPE_ANY); } CREATE_ERROR($$); yyerror(buf); scratch_free($1); } | L_PARAMETER { CREATE_PARAMETER($$, TYPE_ANY, $1); } | '$' '(' { $$ = current_function_context; /* already flagged as an error */ if (current_function_context) current_function_context = current_function_context->parent; } comma_expr ')' { parse_node_t *node; current_function_context = $3; if (!current_function_context || current_function_context->num_parameters == -2) { /* This was illegal, and error'ed when the '$' token * was returned. */ CREATE_ERROR($$); } else { CREATE_OPCODE_1($$, F_LOCAL, $4->type, current_function_context->values_list->kind++); node = new_node_no_line(); node->type = 0; current_function_context->values_list->l.expr->r.expr = node; current_function_context->values_list->l.expr = node; node->r.expr = 0; node->v.expr = $4; } } | expr4 L_ARROW identifier { if ($1->type == TYPE_ANY) { int cmi; unsigned char tp; if ((cmi = lookup_any_class_member($3, &tp)) != -1) { CREATE_UNARY_OP_1($$, F_MEMBER, tp, $1, 0); $$->l.number = cmi; } else { CREATE_ERROR($$); } } else if (!IS_CLASS($1->type)) { yyerror("Left argument of -> is not a class"); CREATE_ERROR($$); } else { CREATE_UNARY_OP_1($$, F_MEMBER, 0, $1, 0); $$->l.number = lookup_class_member(CLASS_IDX($1->type), $3, &($$->type)); } scratch_free($3); } | expr4 '[' comma_expr L_RANGE comma_expr ']' { %ifndef OLD_RANGE_BEHAVIOR %ifdef WARN_OLD_RANGE_BEHAVIOR if ($1->type != TYPE_MAPPING && $5->kind == NODE_NUMBER && $5->v.number < 0) yywarn("A negative constant as the second element of arr[x..y] no longer means indexing from the end. Use arr[x..kind == NODE_NUMBER && $7->v.number <= 1) $$ = make_range_node(F_RE_RANGE, $1, $4, 0); else $$ = make_range_node(F_RR_RANGE, $1, $4, $7); } | expr4 '[' comma_expr L_RANGE '<' comma_expr ']' { if ($6->kind == NODE_NUMBER && $6->v.number <= 1) $$ = make_range_node(F_NE_RANGE, $1, $3, 0); else $$ = make_range_node(F_NR_RANGE, $1, $3, $6); } | expr4 '[' comma_expr L_RANGE ']' { $$ = make_range_node(F_NE_RANGE, $1, $3, 0); } | expr4 '[' '<' comma_expr L_RANGE ']' { $$ = make_range_node(F_RE_RANGE, $1, $4, 0); } | expr4 '[' '<' comma_expr ']' { if (IS_NODE($1, NODE_CALL, F_AGGREGATE) && $4->kind == NODE_NUMBER) { int i = $4->v.number; if (i < 1 || i > $1->l.number) yyerror("Illegal index to array constant."); else { parse_node_t *node = $1->r.expr; i = $1->l.number - i; while (i--) node = node->r.expr; $$ = node->v.expr; break; } } CREATE_BINARY_OP($$, F_RINDEX, 0, $4, $1); if (exact_types) { switch($1->type) { case TYPE_MAPPING: yyerror("Illegal index for mapping."); case TYPE_ANY: $$->type = TYPE_ANY; break; case TYPE_STRING: case TYPE_BUFFER: $$->type = TYPE_NUMBER; if (!IS_TYPE($4->type,TYPE_NUMBER)) type_error("Bad type of index", $4->type); break; default: if ($1->type & TYPE_MOD_ARRAY) { $$->type = $1->type & ~TYPE_MOD_ARRAY; if ($$->type != TYPE_ANY) $$ = add_type_check($$, $$->type); if (!IS_TYPE($4->type,TYPE_NUMBER)) type_error("Bad type of index", $4->type); } else { type_error("Value indexed has a bad type ", $1->type); $$->type = TYPE_ANY; } } } else $$->type = TYPE_ANY; } | expr4 '[' comma_expr ']' { /* Something stupid like ({ 1, 2, 3 })[1]; we take the * time to optimize this because people who don't understand * the preprocessor often write things like: * * #define MY_ARRAY ({ "foo", "bar", "bazz" }) * ... * ... MY_ARRAY[1] ... * * which of course expands to the above. */ if (IS_NODE($1, NODE_CALL, F_AGGREGATE) && $3->kind == NODE_NUMBER) { int i = $3->v.number; if (i < 0 || i >= $1->l.number) yyerror("Illegal index to array constant."); else { parse_node_t *node = $1->r.expr; while (i--) node = node->r.expr; $$ = node->v.expr; break; } } %ifndef OLD_RANGE_BEHAVIOR if ($3->kind == NODE_NUMBER && $3->v.number < 0) yywarn("A negative constant in arr[x] no longer means indexing from the end. Use arr[type) { case TYPE_MAPPING: case TYPE_ANY: $$->type = TYPE_ANY; break; case TYPE_STRING: case TYPE_BUFFER: $$->type = TYPE_NUMBER; if (!IS_TYPE($3->type,TYPE_NUMBER)) type_error("Bad type of index", $3->type); break; default: if ($1->type & TYPE_MOD_ARRAY) { $$->type = $1->type & ~TYPE_MOD_ARRAY; if($$->type != TYPE_ANY) $$ = add_type_check($$, $$->type); if (!IS_TYPE($3->type,TYPE_NUMBER)) type_error("Bad type of index", $3->type); } else { type_error("Value indexed has a bad type ", $1->type); $$->type = TYPE_ANY; } } } else $$->type = TYPE_ANY; } | string | '(' comma_expr ')' { $$ = $2; } | catch %ifdef DEBUG | tree %endif | L_BASIC_TYPE { if ($1 != TYPE_FUNCTION) yyerror("Reserved type name unexpected."); $$.num_local = current_number_of_locals; $$.max_num_locals = max_num_locals; $$.context = context; $$.save_current_type = current_type; $$.save_exact_types = exact_types; if (type_of_locals_ptr + max_num_locals + CFG_MAX_LOCAL_VARIABLES >= &type_of_locals[type_of_locals_size]) reallocate_locals(); deactivate_current_locals(); locals_ptr += current_number_of_locals; type_of_locals_ptr += max_num_locals; max_num_locals = current_number_of_locals = 0; push_function_context(); current_function_context->num_parameters = -1; exact_types = TYPE_ANY; context = 0; } '(' argument ')' block { if ($4.flags & ARG_IS_VARARGS) { yyerror("Anonymous varargs functions aren't implemented"); } if (!$6.node) { CREATE_RETURN($6.node, 0); } else if ($6.node->kind != NODE_RETURN && ($6.node->kind != NODE_TWO_VALUES || $6.node->r.expr->kind != NODE_RETURN)) { parse_node_t *replacement; CREATE_STATEMENTS(replacement, $6.node, 0); CREATE_RETURN(replacement->r.expr, 0); $6.node = replacement; } $$ = new_node(); $$->kind = NODE_ANON_FUNC; $$->type = TYPE_FUNCTION; $$->l.number = (max_num_locals - $4.num_arg); $$->r.expr = $6.node; $$->v.number = $4.num_arg; if (current_function_context->bindable) $$->v.number |= 0x10000; free_all_local_names(1); current_number_of_locals = $2.num_local; max_num_locals = $2.max_num_locals; context = $2.context; current_type = $2.save_current_type; exact_types = $2.save_exact_types; pop_function_context(); locals_ptr -= current_number_of_locals; type_of_locals_ptr -= max_num_locals; reactivate_current_locals(); } %ifdef COMPAT_32 | simple_function_pointer %else | l_new_function_open ':' ')' %endif { #ifdef WOMBLES if(*(outp-2) != ':') yyerror("End of functional not found"); #endif $$ = new_node(); $$->kind = NODE_FUNCTION_CONSTRUCTOR; $$->type = TYPE_FUNCTION; $$->r.expr = 0; switch ($1 & 0xff) { case FP_L_VAR: yyerror("Illegal to use local variable in a functional."); CREATE_NUMBER($$->l.expr, 0); $$->l.expr->r.expr = 0; $$->l.expr->l.expr = 0; $$->v.number = FP_FUNCTIONAL; break; case FP_G_VAR: CREATE_OPCODE_1($$->l.expr, F_GLOBAL, 0, $1 >> 8); $$->v.number = FP_FUNCTIONAL | FP_NOT_BINDABLE; if (VAR_TEMP($$->l.expr->l.number)->type & DECL_HIDDEN) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Illegal to use private variable '"); p = strput(p, end, VAR_TEMP($$->l.expr->l.number)->name); p = strput(p, end, "'"); yyerror(buf); } break; default: $$->v.number = $1; break; } } | l_new_function_open ',' expr_list2 ':' ')' { #ifdef WOMBLES if(*(outp-2) != ':') yyerror("End of functional not found"); #endif $$ = new_node(); $$->kind = NODE_FUNCTION_CONSTRUCTOR; $$->type = TYPE_FUNCTION; $$->v.number = $1; $$->r.expr = $3; switch ($1 & 0xff) { case FP_EFUN: { int *argp; int f = $1 >>8; int num = $3->kind; int max_arg = predefs[f].max_args; if(f!=-1){ if (num > max_arg && max_arg != -1) { parse_node_t *pn = $3; while (pn) { if (pn->type & 1) break; pn = pn->r.expr; } if (!pn) { char bff[256]; char *end = EndOf(bff); char *p; p = strput(bff, end, "Too many arguments to "); p = strput(p, end, predefs[f].word); yyerror(bff); } } else if (max_arg != -1 && exact_types) { /* * Now check all types of arguments to efuns. */ int i, argn, tmp; parse_node_t *enode = $3; argp = &efun_arg_types[predefs[f].arg_index]; for (argn = 0; argn < num; argn++) { if (enode->type & 1) break; tmp = enode->v.expr->type; for (i=0; !compatible_types(tmp, argp[i]) && argp[i] != 0; i++) ; if (argp[i] == 0) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Bad argument "); p = strput_int(p, end, argn+1); p = strput(p, end, " to efun "); p = strput(p, end, predefs[f].word); p = strput(p, end, "()"); yyerror(buf); } else { /* this little section necessary b/c in the case float | int we dont want to do promoting. */ if (tmp == TYPE_NUMBER && argp[i] == TYPE_REAL) { for (i++; argp[i] && argp[i] != TYPE_NUMBER; i++) ; if (!argp[i]) enode->v.expr = promote_to_float(enode->v.expr); } if (tmp == TYPE_REAL && argp[i] == TYPE_NUMBER) { for (i++; argp[i] && argp[i] != TYPE_REAL; i++) ; if (!argp[i]) enode->v.expr = promote_to_int(enode->v.expr); } } while (argp[i] != 0) i++; argp += i + 1; enode = enode->r.expr; } } } break; } case FP_L_VAR: case FP_G_VAR: yyerror("Can't give parameters to functional."); break; } } | L_FUNCTION_OPEN comma_expr ':' ')' { #ifdef WOMBLES if(*(outp-2) != ':') yyerror("End of functional not found"); #endif if (current_function_context->num_locals) yyerror("Illegal to use local variable in functional."); if (current_function_context->values_list->r.expr) current_function_context->values_list->r.expr->kind = current_function_context->values_list->kind; $$ = new_node(); $$->kind = NODE_FUNCTION_CONSTRUCTOR; $$->type = TYPE_FUNCTION; $$->l.expr = $2; if ($2->kind == NODE_STRING) yywarn("Function pointer returning string constant is NOT a function call"); $$->r.expr = current_function_context->values_list->r.expr; $$->v.number = FP_FUNCTIONAL + current_function_context->bindable + (current_function_context->num_parameters << 8); pop_function_context(); } | L_MAPPING_OPEN expr_list3 ']' ')' { #ifdef WOMBLES if(*(outp-2) != ']') yyerror("End of mapping not found"); #endif CREATE_CALL($$, F_AGGREGATE_ASSOC, TYPE_MAPPING, $2); } | L_ARRAY_OPEN expr_list '}' ')' { #ifdef WOMBLES if(*(outp-2) != '}') yyerror("End of array not found"); #endif CREATE_CALL($$, F_AGGREGATE, TYPE_ANY | TYPE_MOD_ARRAY, $2); } ; expr_or_block: block { $$ = $1.node; } | '(' comma_expr ')' { $$ = insert_pop_value($2); } ; catch: L_CATCH { $$ = context; context = SPECIAL_CONTEXT; } expr_or_block { CREATE_CATCH($$, $3); context = $2; } ; %ifdef DEBUG tree: L_TREE block { $$ = new_node_no_line(); lpc_tree_form($2.node, $$); } | L_TREE '(' comma_expr ')' { $$ = new_node_no_line(); lpc_tree_form($3, $$); } ; %endif sscanf: L_SSCANF '(' expr0 ',' expr0 lvalue_list ')' { int p = $6->v.number; CREATE_LVALUE_EFUN($$, TYPE_NUMBER, $6); CREATE_BINARY_OP_1($$->l.expr, F_SSCANF, 0, $3, $5, p); } ; parse_command: L_PARSE_COMMAND '(' expr0 ',' expr0 ',' expr0 lvalue_list ')' { int p = $8->v.number; CREATE_LVALUE_EFUN($$, TYPE_NUMBER, $8); CREATE_TERNARY_OP_1($$->l.expr, F_PARSE_COMMAND, 0, $3, $5, $7, p); } ; time_expression: L_TIME_EXPRESSION { $$ = context; context = SPECIAL_CONTEXT; } expr_or_block { CREATE_TIME_EXPRESSION($$, $3); context = $2; } ; lvalue_list: /* empty */ { $$ = new_node_no_line(); $$->r.expr = 0; $$->v.number = 0; } | ',' lvalue lvalue_list { parse_node_t *insert; $$ = $3; insert = new_node_no_line(); insert->r.expr = $3->r.expr; insert->l.expr = $2; $3->r.expr = insert; $$->v.number++; } ; string: string_con2 { CREATE_STRING($$, $1); scratch_free($1); } ; string_con1: string_con2 | '(' string_con1 ')' { $$ = $2; } | string_con1 '+' string_con1 { $$ = scratch_join($1, $3); } ; string_con2: L_STRING | string_con2 L_STRING { $$ = scratch_join($1, $2); } ; class_init: identifier ':' expr0 { $$ = new_node(); $$->l.expr = (parse_node_t *)$1; $$->v.expr = $3; $$->r.expr = 0; } ; opt_class_init: /* empty */ { $$ = 0; } | opt_class_init ',' class_init { $$ = $3; $$->r.expr = $1; } ; function_call: efun_override '(' { $$ = context; $2 = num_refs; context |= ARG_LIST; } expr_list ')' { context = $3; $$ = validate_efun_call($1,$4); $$ = check_refs(num_refs - $2, $4, $$); num_refs = $2; } | L_NEW '(' { $$ = context; $2 = num_refs; context |= ARG_LIST; } expr_list ')' { ident_hash_elem_t *ihe; int f; context = $3; ihe = lookup_ident("clone_object"); if ((f = ihe->dn.simul_num) != -1) { $$ = $4; $$->kind = NODE_CALL_1; $$->v.number = F_SIMUL_EFUN; $$->l.number = f; $$->type = (SIMUL(f)->type) & ~DECL_MODS; } else { $$ = validate_efun_call(lookup_predef("clone_object"), $4); #ifdef CAST_CALL_OTHERS $$->type = TYPE_UNKNOWN; #else $$->type = TYPE_ANY; #endif } $$ = check_refs(num_refs - $2, $4, $$); num_refs = $2; } | L_NEW '(' L_CLASS L_DEFINED_NAME opt_class_init ')' { parse_node_t *node; if ($4->dn.class_num == -1) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Undefined class '"); p = strput(p, end, $4->name); p = strput(p, end, "'"); yyerror(buf); CREATE_ERROR($$); node = $5; while (node) { scratch_free((char *)node->l.expr); node = node->r.expr; } } else { int type = $4->dn.class_num | TYPE_MOD_CLASS; if ((node = $5)) { CREATE_TWO_VALUES($$, type, 0, 0); $$->l.expr = reorder_class_values($4->dn.class_num, node); CREATE_OPCODE_1($$->r.expr, F_NEW_CLASS, type, $4->dn.class_num); } else { CREATE_OPCODE_1($$, F_NEW_EMPTY_CLASS, type, $4->dn.class_num); } } } | L_NEW '(' L_CLASS L_IDENTIFIER opt_class_init ')' { parse_node_t *node; char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Undefined class '"); p = strput(p, end, $4); p = strput(p, end, "'"); yyerror(buf); CREATE_ERROR($$); node = $5; while (node) { scratch_free((char *)node->l.expr); node = node->r.expr; } } | L_DEFINED_NAME '(' { $$ = context; $2 = num_refs; context |= ARG_LIST; } expr_list ')' { int f; context = $3; $$ = $4; if ((f = $1->dn.function_num) != -1) { if (current_function_context) current_function_context->bindable = FP_NOT_BINDABLE; $$->kind = NODE_CALL_1; $$->v.number = F_CALL_FUNCTION_BY_ADDRESS; $$->l.number = f; $$->type = validate_function_call(f, $4->r.expr); } else if ((f=$1->dn.simul_num) != -1) { $$->kind = NODE_CALL_1; $$->v.number = F_SIMUL_EFUN; $$->l.number = f; $$->type = (SIMUL(f)->type) & ~DECL_MODS; } else if ((f=$1->dn.efun_num) != -1) { $$ = validate_efun_call(f, $4); } else { /* This here is a really nasty case that only occurs with * exact_types off. The user has done something gross like: * * func() { int f; f(); } // if f was prototyped we wouldn't * f() { } // need this case * * Don't complain, just grok it. */ if (current_function_context) current_function_context->bindable = FP_NOT_BINDABLE; f = define_new_function($1->name, 0, 0, DECL_PUBLIC|FUNC_UNDEFINED, TYPE_ANY); $$->kind = NODE_CALL_1; $$->v.number = F_CALL_FUNCTION_BY_ADDRESS; $$->l.number = f; $$->type = TYPE_ANY; /* just a guess */ if (exact_types) { char buf[256]; char *end = EndOf(buf); char *p; const char *n = $1->name; if (*n == ':') n++; /* prevent some errors; by making it look like an * inherited function we prevent redeclaration errors * if it shows up later */ FUNCTION_FLAGS(f) &= ~FUNC_UNDEFINED; FUNCTION_FLAGS(f) |= (FUNC_INHERITED | FUNC_VARARGS); p = strput(buf, end, "Undefined function "); p = strput(p, end, n); yyerror(buf); } } $$ = check_refs(num_refs - $2, $4, $$); num_refs = $2; } | function_name '(' { $$ = context; $2 = num_refs; context |= ARG_LIST; } expr_list ')' { char *name = $1; context = $3; $$ = $4; if (current_function_context) current_function_context->bindable = FP_NOT_BINDABLE; if (*name == ':') { int f; if ((f = arrange_call_inherited(name + 1, $$)) != -1) /* Can't do this; f may not be the correct function entry. It might be overloaded. validate_function_call(f, $$->r.expr) */ ; } else { int f; ident_hash_elem_t *ihe; f = (ihe = lookup_ident(name)) ? ihe->dn.function_num : -1; $$->kind = NODE_CALL_1; $$->v.number = F_CALL_FUNCTION_BY_ADDRESS; if (f!=-1) { /* The only way this can happen is if function_name * below made the function name. The lexer would * return L_DEFINED_NAME instead. */ $$->type = validate_function_call(f, $4->r.expr); } else { f = define_new_function(name, 0, 0, DECL_PUBLIC|FUNC_UNDEFINED, TYPE_ANY); } $$->l.number = f; /* * Check if this function has been defined. * But, don't complain yet about functions defined * by inheritance. */ if (exact_types && (FUNCTION_FLAGS(f) & FUNC_UNDEFINED)) { char buf[256]; char *end = EndOf(buf); char *p; char *n = $1; if (*n == ':') n++; /* prevent some errors */ FUNCTION_FLAGS(f) &= ~FUNC_UNDEFINED; FUNCTION_FLAGS(f) |= (FUNC_INHERITED | FUNC_VARARGS); p = strput(buf, end, "Undefined function "); p = strput(p, end, n); yyerror(buf); } if (!(FUNCTION_FLAGS(f) & FUNC_UNDEFINED)) $$->type = FUNCTION_DEF(f)->type; else $$->type = TYPE_ANY; /* Just a guess */ } $$ = check_refs(num_refs - $2, $4, $$); num_refs = $2; scratch_free(name); } | expr4 L_ARROW identifier '(' { $$ = context; $4 = num_refs; context |= ARG_LIST; } expr_list ')' { ident_hash_elem_t *ihe; int f; parse_node_t *pn1, *pn2; $6->v.number += 2; pn1 = new_node_no_line(); pn1->type = 0; pn1->v.expr = $1; pn1->kind = $6->v.number; pn2 = new_node_no_line(); pn2->type = 0; CREATE_STRING(pn2->v.expr, $3); scratch_free($3); /* insert the two nodes */ pn2->r.expr = $6->r.expr; pn1->r.expr = pn2; $6->r.expr = pn1; if (!$6->l.expr) $6->l.expr = pn2; context = $5; ihe = lookup_ident("call_other"); if ((f = ihe->dn.simul_num) != -1) { $$ = $6; $$->kind = NODE_CALL_1; $$->v.number = F_SIMUL_EFUN; $$->l.number = f; $$->type = (SIMUL(f)->type) & ~DECL_MODS; } else { $$ = validate_efun_call(arrow_efun, $6); #ifdef CAST_CALL_OTHERS $$->type = TYPE_UNKNOWN; #else $$->type = TYPE_ANY; #endif } $$ = check_refs(num_refs - $4, $6, $$); num_refs = $4; } | '(' '*' comma_expr ')' '(' { $$ = context; $5 = num_refs; context |= ARG_LIST; } expr_list ')' { parse_node_t *expr; context = $6; $$ = $7; $$->kind = NODE_EFUN; $$->l.number = $$->v.number + 1; $$->v.number = predefs[evaluate_efun].token; #ifdef CAST_CALL_OTHERS $$->type = TYPE_UNKNOWN; #else $$->type = TYPE_ANY; #endif expr = new_node_no_line(); expr->type = 0; expr->v.expr = $3; expr->r.expr = $$->r.expr; $$->r.expr = expr; $$ = check_refs(num_refs - $5, $7, $$); num_refs = $5; } ; efun_override: L_EFUN L_COLON_COLON identifier { svalue_t *res; ident_hash_elem_t *ihe; $$ = (ihe = lookup_ident($3)) ? ihe->dn.efun_num : -1; if ($$ == -1) { char buf[256]; char *end = EndOf(buf); char *p; p = strput(buf, end, "Unknown efun: "); p = strput(p, end, $3); yyerror(buf); } else { push_malloced_string(the_file_name(current_file)); share_and_push_string($3); push_malloced_string(add_slash(main_file_name())); res = safe_apply_master_ob(APPLY_VALID_OVERRIDE, 3); if (!MASTER_APPROVED(res)) { yyerror("Invalid simulated efunction override"); $$ = -1; } } scratch_free($3); } | L_EFUN L_COLON_COLON L_NEW { svalue_t *res; push_malloced_string(the_file_name(current_file)); push_constant_string("new"); push_malloced_string(add_slash(main_file_name())); res = safe_apply_master_ob(APPLY_VALID_OVERRIDE, 3); if (!MASTER_APPROVED(res)) { yyerror("Invalid simulated efunction override"); $$ = -1; } else $$ = new_efun; } ; function_name: L_IDENTIFIER | L_COLON_COLON identifier { int l = strlen($2) + 1; char *p; /* here we be a bit cute. we put a : on the front so we * don't have to strchr for it. Here we do: * "name" -> ":::name" */ $$ = scratch_realloc($2, l + 3); p = $$ + l; while (p--,l--) *(p+3) = *p; strncpy($$, ":::", 3); } | L_BASIC_TYPE L_COLON_COLON identifier { int z, l = strlen($3) + 1; char *p; /* and "name" -> ":type::name" */ z = strlen(compiler_type_names[$1]) + 3; /* length of :type:: */ $$ = scratch_realloc($3, l + z); p = $$ + l; while (p--,l--) *(p+z) = *p; $$[0] = ':'; strncpy($$ + 1, compiler_type_names[$1], z - 3); $$[z-2] = ':'; $$[z-1] = ':'; } | identifier L_COLON_COLON identifier { int l = strlen($1); /* "ob" and "name" -> ":ob::name" */ $$ = scratch_alloc(l + strlen($3) + 4); *($$) = ':'; strcpy($$ + 1, $1); strcpy($$ + l + 1, "::"); strcpy($$ + l + 3, $3); scratch_free($1); scratch_free($3); } ; cond: L_IF '(' comma_expr ')' statement optional_else_part { /* x != 0 -> x */ if (IS_NODE($3, NODE_BINARY_OP, F_NE)) { if (IS_NODE($3->r.expr, NODE_NUMBER, 0)) $3 = $3->l.expr; else if (IS_NODE($3->l.expr, NODE_NUMBER, 0)) $3 = $3->r.expr; } /* TODO: should optimize if (0), if (1) here. * Also generalize this. */ if ($5 == 0) { if ($6 == 0) { /* if (x) ; -> x; */ $$ = pop_value($3); break; } else { /* if (x) {} else y; -> if (!x) y; */ parse_node_t *repl; CREATE_UNARY_OP(repl, F_NOT, TYPE_NUMBER, $3); $3 = repl; $5 = $6; $6 = 0; } } CREATE_IF($$, $3, $5, $6); } ; optional_else_part: /* empty */ %prec LOWER_THAN_ELSE { $$ = 0; } | L_ELSE statement { $$ = $2; } ; %% %line