%{ %line /*--------------------------------------------------------------------------- * LPC compiler * *--------------------------------------------------------------------------- * TODO: Some code parts 'know' which instructions are xcodes and which normal. * TODO:: Conditional compiles would be nice there. * TODO: The handling of virtual inherits is a bit vague, too. * * This is the grammar definition and bytecode compiler of LPC. However, this * file is not passed to byacc directly, but first preprocessed by make_func, * among other things to synchronise the tokens with the other bytecodes * (reason being that yacc doesn't know an include construct). The following * keywords are recognized and replaced by make_func: * * %line: generates a #line statement to synchronize the C compiler. * * %typemap TYPE<name>:<value>,...,TYPE_<name>:<value> * Generates a lookup table TYPE_<name> -> <value>. Unspecified * TYPEs are given the value 0. * * %hookmap <hookname>:<value>,...,<hookname>:<value> * Generates a lookup table <hookname> -> <value>. Unspecified * driverhook entries are given the value 0. * * In addition, make_func implements a simple preprocessor using the * keywords %if, %elif, %else and %endif; mainly to activate the proper * parsing rules for INITIALIZATION_BY___INIT. *--------------------------------------------------------------------------- * A compile file, set its filename into <current_file>, open it to yield a * filedescriptor 'fd', then call * * compile_file(fd); * * then close the file again. The compiled program is 'returned' in * the global compiled_prog - on error, this variable is returned as NULL. * If after the compilation the variable inherit_file is * not NULL, the compilation failed because it encountered an * "inherit 'name'" statement for which no object could be found: the * 'name' was stored in inherit_file and has to be compiled first. * * It is the task of the caller to make sure not to call the compiler * recursively. * %ifdef INITIALIZATION_BY___INIT * If there is any initialization of a global variable, a function '__INIT' * is generated with the initialization code. The code is generated in * fragments whenever a variable initialization is encountered; the fragments * are therefore potentially spread over the whole program code. The fragments * are linked by JUMP instructions with jump to the next fragment, just * the last fragment ends in a RETURN0. * * When inheriting from another object, a call will automatically be made * to call __INIT in that code from the current __INIT. %else * The variable initializers are returned as svalue_t[] in the global * variable prog_variable_values. It is the task of the call to free * that memory. %endif *--------------------------------------------------------------------------- * The compiler is a simple one-pass compiler with immediate code generation. * The problem of forward references is solved with various backpatching * structures (explained where declared). * * The most tricky part is that of lvalue (and with it reference) generation * in contexts where rvalues are sensible as well. This is so especially * because the order of arguments on the stack differs between the * instructions :-(. The approach is to generate rvalues, but keep the * position, and size and alternatives of the instruction(s) in a struct * lrvalue, so that a later change into lvalues is possible. Additionally * these instructions can be modified to generated protected lvalues as well. * TODO: This whole thing is quite complex and not very well documented. * TODO:: It's propably easier to rewrite interpreter and compiler... *--------------------------------------------------------------------------- */ #undef lint /* undef so that precompiled headers can be used */ #include "driver.h" #include "typedefs.h" #include "my-alloca.h" #include <stdio.h> #include <stdarg.h> #include "prolang.h" #include "array.h" #include "backend.h" #include "closure.h" #include "exec.h" #include "gcollect.h" #include "interpret.h" #include "instrs.h" #include "lex.h" #include "main.h" #include "mapping.h" #include "object.h" #include "simulate.h" #include "simul_efun.h" #include "stdstrings.h" #include "stralloc.h" #include "svalue.h" #include "swap.h" #include "switch.h" #include "wiz_list.h" #include "xalloc.h" #include "../mudlib/sys/driver_hook.h" #define lint /* redef again to prevent spurious warnings */ #define YYMAXDEPTH 600 /*-------------------------------------------------------------------------*/ typedef struct block_scope_s block_scope_t; typedef struct const_list_s const_list_t; typedef struct const_list_svalue_s const_list_svalue_t; typedef struct efun_shadow_s efun_shadow_t; typedef struct mem_block_s mem_block_t; /*-------------------------------------------------------------------------*/ /* Exported result variables */ int32 current_id_number = 0; /* The id-number of the compiled program. */ int num_virtual_variables; /* Number of virtual variables. * When creating the bytecode, the non-virtual variable indices * are offset by this value, in effect collecting the virtual * variables at the start of the variable indices. */ program_t *compiled_prog; /* After yyparse(), the finished program. */ char *inherit_file; /* 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. */ int num_parse_error; /* Number of errors in the compile. */ %ifndef INITIALIZATION_BY___INIT svalue_t *prog_variable_values; /* After epilog(), the variable initializers. */ %endif /* INITIALIZATION_BY___INIT */ /*-------------------------------------------------------------------------*/ /* Table which hook may be of which type. * It is here because make_func has to touch this file anyway, but * it will be used by simulate:f_set_driver_hook(). */ #define SH(x) - -(1 << (x)) short hook_type_map[NUM_DRIVER_HOOKS] = %hookmap \ H_MOVE_OBJECT0: 0, \ H_MOVE_OBJECT1: 0, \ H_LOAD_UIDS: SH(T_CLOSURE), \ H_CLONE_UIDS: SH(T_CLOSURE), \ H_CREATE_SUPER: SH(T_STRING), \ H_CREATE_OB: SH(T_STRING), \ H_CREATE_CLONE: SH(T_STRING), \ H_RESET: SH(T_STRING), \ H_CLEAN_UP: SH(T_CLOSURE) SH(T_STRING), \ H_MODIFY_COMMAND: SH(T_CLOSURE) SH(T_STRING) SH(T_MAPPING), \ H_NOTIFY_FAIL: SH(T_CLOSURE) SH(T_STRING), \ H_NO_IPC_SLOT: SH(T_STRING), \ H_INCLUDE_DIRS: SH(T_CLOSURE) SH(T_POINTER), \ H_TELNET_NEG: SH(T_CLOSURE) SH(T_STRING), \ H_NOECHO: SH(T_CLOSURE) SH(T_STRING), \ H_ERQ_STOP: SH(T_CLOSURE), \ H_MODIFY_COMMAND_FNAME: SH(T_STRING), \ H_COMMAND: SH(T_CLOSURE) SH(T_STRING), \ H_SEND_NOTIFY_FAIL: SH(T_CLOSURE) SH(T_STRING), \ H_AUTO_INCLUDE: SH(T_CLOSURE) SH(T_STRING), \ #undef SH /*-------------------------------------------------------------------------*/ /* Types */ /* --- struct const_list_s: One element in a constant list --- * * When initializing variables statically with arrays ({ ... }), * a list of these structures is used to collect the information about * the array content. */ struct const_list_s { const_list_t *next; svalue_t val; }; /* --- struct const_list_svalue_s: Head of a constant list --- * * When initializing variables statically with arrays ({ ... }), * the initializer-svalue_t* will point to an instance of this c_l_svalue_s. * In fact, the initializer points to the .head member. * * The .head svalue_t is a T_ERROR_HANDLER pointing to a deallocation * function for the list. */ struct const_list_svalue_s { svalue_t head; /* the error handler */ const_list_t list; /* First element of the list */ }; /* --- struct efun_shadow_s: Store info about masked efuns --- * * This structure is used when global identifiers shadow efun names. */ struct efun_shadow_s { efun_shadow_t *next; /* Linkpointer for the list of shadows */ ident_t *shadow; /* Identifier of the shadow */ }; /*-------------------------------------------------------------------------*/ /* Macros */ #define NON_VIRTUAL_OFFSET_TAG 0x4000 /* Tag or'ed on inherit.variable_index_offset for non-virtual * inherits for the duration of the compilation. * The variable_index_offsets of such marked variables do not * yet the the num_virtual_variables offset into account. */ #define align(x) (((x) + (sizeof(char*)-1) ) & ~(sizeof(char*)-1) ) #define defined_function(s) \ ((s)->type == I_TYPE_GLOBAL ? (s)->u.global.function : -1) /* Return the index of the function <s> if global (and therefore existing), * and -1 otherwise. */ #define NEW_INHERITED_INDEX (0xfffff) /* While inserting a new inherit, this marks the newly inherited * things. */ /*-------------------------------------------------------------------------*/ /* The generated information (code and otherwise) is kept in several * memory areas, each of which can grow dynamically and independent * from the others. * * The first NUMPAREAS are save with the program code after compilation, * the others are of internal use for the compiler only. */ #define A_PROGRAM 0 /* (bytecode_t): Program code. */ #define A_STRINGS 1 /* (shared char*) Strings used by the program. */ #define A_VARIABLES 2 /* (variable_t) The information for all non-virtual variables. */ #define A_VIRTUAL_VAR 3 /* (variable_t) The information for all virtual variables. */ #define A_LINENUMBERS 4 /* (char) The linenumber information. */ #define A_INHERITS 5 /* (inherit_t) The information for the inherited programs. */ #define A_ARGUMENT_TYPES 6 /* (vartype_t) Types of the arguments of all functions with * typechecking. The argument types for a specific function * can be found using the ARGUMENT_INDEX */ #define A_ARGUMENT_INDEX 7 /* (unsigned short) Index of the first argument type of function <n>. * INDEX_START_NONE is used for functions with no type information. */ #define A_INCLUDES 8 /* (include_t) Tabled descriptors of all included files, in the order * of appearance. */ #define NUMPAREAS 9 /* Number of saved areas */ #define A_FUNCTIONS 9 /* (function_t): Function definitions */ %ifndef INITIALIZATION_BY___INIT # define A_VARIABLE_VALUES 10 /* (svalue_t) Initializers for non-virtual variables. */ # define A_VIRTUAL_VAR_VALUES 11 /* (svalue_t) Initializers for virtual variables. */ %endif #define A_STRING_NEXT 12 /* (int) During compilation, the strings in A_STRINGS are organized * in a hash table (prog_string_indizes/_tags). The hash chains are * linked together using the indizes in this area. The end of * a chain is marked by a negative next-index. */ #define NUMAREAS 13 /* Total number of areas */ /* --- struct mem_block_s: One memory area --- * Every mem_block keeps one memory area. As it grows by using realloc(), * no pointers should be kept into such an area (offsets are ok). */ struct mem_block_s { char *block; /* Pointer to the allocated memory */ mp_uint current_size; /* Used size of the mem_block */ mp_uint max_size; /* Allocated size of the mem_block */ }; #define START_BLOCK_SIZE 2048 /* Initial size of an area/mem_block. */ static mem_block_t mem_block[NUMAREAS]; /* All memory areas. */ #define PROGRAM_BLOCK ((bytecode_p)(mem_block[A_PROGRAM].block)) /* The current program block, properly typed. */ #define CURRENT_PROGRAM_SIZE (mem_block[A_PROGRAM].current_size) /* The current program size. */ #define FUNCTION(n) ((function_t *)mem_block[A_FUNCTIONS].block + (n)) /* Return the function_t* for function number <n>. */ #define FUNCTION_COUNT (mem_block[A_FUNCTIONS].current_size / sizeof (function_t)) /* Number of function_t stored so far in A_FUNCTIONS. */ #define INHERIT_COUNT (mem_block[A_INHERITS].current_size / sizeof(inherit_t)) /* Number of inherit_t stored so far in A_INHERITS. */ #define ARGUMENT_INDEX(n) ((unsigned short *)mem_block[A_ARGUMENT_INDEX].block)[n] /* Lookup the start index of the types for function number <n>. */ #define ARGTYPE_COUNT (mem_block[A_ARGUMENT_TYPES].current_size / sizeof(vartype_t)) /* Number of vartype_t stored so far in A_ARGUMENT_TYPES. */ #define NV_VARIABLE(n) ((variable_t *)mem_block[A_VARIABLES].block + (n)) /* Return the variable_t* for the non-virtual variable <n>. */ #define NV_VARIABLE_COUNT (mem_block[A_VARIABLES].current_size / sizeof(variable_t)) #define V_VARIABLE_COUNT (mem_block[A_VIRTUAL_VAR].current_size / sizeof(variable_t)) /* Number of variables stored so var in A_VARIABLES resp. A_VIRTUAL_VAR. */ #define V_VARIABLE(n) ((variable_t *)mem_block[A_VIRTUAL_VAR].block + \ (n) - VIRTUAL_VAR_TAG) /* Return the variable_t* for the virtual variable <n> (still including * the offset). */ #define VARIABLE(n) ((n) & VIRTUAL_VAR_TAG ? V_VARIABLE(n) : NV_VARIABLE(n)) /* Return the variable_t* for the variable <n>, virtual or not. */ %ifndef INITIALIZATION_BY___INIT #define V_VAR_VALUE(n) ((svalue_t *)mem_block[A_VARIABLE_VALUES].block + \ (n) - VIRTUAL_VAR_TAG) /* Return the svalue_t* for the initializer of virtual variable <n> * (still including the offset). */ #define NV_VAR_VALUE(n) ((svalue_t *)mem_block[A_VARIABLE_VALUES].block + (n)) /* Return the svalue_t* for the initializer of non-virtual variable <n>. */ %endif #define INHERIT(n) ((inherit_t *)mem_block[A_INHERITS].block)[n] /* Index the inherit_t <n>. */ #define INHERIT_COUNT (mem_block[A_INHERITS].current_size / sizeof(inherit_t)) /* Return the number of inherits encountered so far. */ #define PROG_STRING(n) ((char **)mem_block[A_STRINGS].block)[n] /* Index the pointer for program string <n>. */ #define STRING_COUNT (mem_block[A_STRINGS].current_size / sizeof(char *)) /* Return the number of program strings encountered so far. */ #define PROG_STRING_NEXT(n) ((int *)mem_block[A_STRING_NEXT].block)[n] /* Index the chain-index for program string <n>. */ #define INCLUDE_COUNT (mem_block[A_INCLUDES].current_size / sizeof(include_t)) /* Return the total number of include files encountered so far. */ /*-------------------------------------------------------------------------*/ /* Information describing nested local blocks (scopes). */ struct block_scope_s { int first_local; /* Number of first local defined in this scope */ int num_locals; /* Number of locals defined in this scope */ mp_uint addr; /* Address of CLEAR_LOCALS instruction, needed for backpatching */ }; static block_scope_t block_scope[COMPILER_STACK_SIZE]; /* A static stack of block scopes, indexed by <block_depth>-1. * TODO: This should be dynamic. */ static int block_depth; /* The nesting depth of blocks ( '{ ... }' ), used to distinguish * local variable definitions. * block_depth = 0: not used, would mean 'global' * = 1: function arguments * = 2: function local variables * > 2: vars of nested blocks within the function */ static Bool use_local_scopes; /* Copy of pragma_use_local_scopes, updated at every entry into * a function. Reason is that the pragma must not change inside * a function. */ /*-------------------------------------------------------------------------*/ /* Other Variables */ static char *last_yalloced = NULL; /* Head of blocklist allocated with yalloc(). */ static program_t NULL_program; /* Empty program_t structure for initialisations. */ static p_int comp_stack[COMPILER_STACK_SIZE]; /* A stack of addresses (offsets) in the generated program code for * later backpatching. */ static size_t comp_stackp; /* Index of the next unused entry in <comp_stack>. */ %ifdef INITIALIZATION_BY___INIT static p_int last_initializer_end; /* Address of the argument of the final JUMP instruction of the * previous INIT fragment. * A negative value if there is no previous fragment (this also means * that the INIT functions hasn't been created yet). */ static p_int first_initializer_start; /* Address of the 'num_arg' byte in the function header of the first * INIT fragment. */ static Bool variables_initialized; /* TRUE if the code for all variables has been created. */ %else static svalue_t *currently_initialized; /* The variable for which currently the initializer is compiled. */ %endif /* INITIALIZATION_BY___INIT */ static mem_block_t type_of_arguments; /* The vartypes of arguments when calling functions must be saved, * to be used afterwards for checking. And because function calls * can be done as an argument to a function calls, a stack of argument types * is needed. This stack does not need to be freed between compilations, * but will be reused. */ static vartype_t type_of_locals[MAX_LOCAL]; /* The short type (ie: just the type, no visibility information) of * the local variables. */ static fulltype_t full_type_of_locals[MAX_LOCAL]; /* The full types of the local variables. */ static int current_number_of_locals = 0; /* Current (active) number of local variables at this point in the * function. */ static int max_number_of_locals = 0; /* Total number of local variables used in this function so far. */ static ident_t *all_locals = NULL; /* List of defined local variables, listed in reverse order of definition. * This also means that the variables are listed in reverse order of * nested block scopes. */ fulltype_t exact_types; /* If 0, don't check nor require argument and function types. * Otherwise it's the full return type of the function, including * visibility. The lexer reads this variable when scanning an * inline closure. */ static fulltype_t default_varmod; static fulltype_t default_funmod; /* Default visibility modifiers for variables resp. function. */ static int heart_beat; /* Number of the heart_beat() function, or < 0 if none. */ static int call_other_sefun; /* Index of the call_other() sefun, or < 0 if none; */ static ident_t *all_globals = NULL; /* List of all created global identifiers (variables and functions). */ static efun_shadow_t *all_efun_shadows = NULL; /* List of all shadow markers for efuns shadowed by global identifiers. */ static p_int switch_pc; /* When compiling a switch, this is the address of the first byte * after the SWITCH instruction. */ static p_int current_break_address; /* If != 0, the compiler is in a break-able environment and this * variable points to the first offset-part of a series of LBRANCHes * which implement the break statement. Stored in every offset-part * is the address of the offset of the next LBRANCH in the series. The * last LBRANCH is marked by having a negative offset value. * * There are a few special values/flags for this variable: */ #define BREAK_ON_STACK 0x04000000 /* Bitflag: true when the break-address is stored on the break stack, * and therefore the BREAK instruction has to be used. */ #define BREAK_FROM_SWITCH 0x08000000 /* TODO: We are compiling a switch instruction. */ #define CASE_LABELS_ENABLED 0x10000000 /* The "case" and "default" statements are allowed since we're * compiling a switch(). This flag is turned off for loops or * conditions embedded in a switch(). */ #define BREAK_DELIMITER -0x20000000 /* Special value: no break encountered (yet). */ static p_int current_continue_address; /* If != 0, the compiler is in a continue-able environment and this * variable points to the first offset-part of a series of LBRANCHes * which implement the continue statement. Stored in every offset-part * is the address of the offset of the next LBRANCH in the series. The * last LBRANCH is marked by having a negative offset value. * * A special case are continues inside a switch, as for these the * switch()es have to be terminated too using the BREAK_CONTINUE * instructions (which also have an offset-part). The c_c_a therefore * also encodes the switch()-nesting depth in the top bits of the * variable. */ #define CONTINUE_ADDRESS_MASK 0x0003ffff /* Mask for the offset-address part of the variable. */ #define SWITCH_DEPTH_UNIT 0x00040000 /* The switch depth is encoded in multiples of this value. * This way we don't have to shift. */ #define SWITCH_DEPTH_MASK 0x3ffc0000 /* Mask for the switch-nesting depth part of the variable. */ #define CONTINUE_DELIMITER -0x40000000 /* Special value: no continue encountered (yet). */ static fulltype_t current_type; /* The current basic type. */ static p_uint last_expression; /* If >= 0, the address of the last instruction which by itself left * a value on the stack. If there is no such instruction, the value * is negative. */ static Bool last_string_is_new; /* TRUE: the last string stored with store_prog_string() was indeed * a new string. */ static int prog_string_indizes[0x100]; /* Hash table for the program strings holding the initial indices * for the hash chains. */ static char prog_string_tags[32]; /* Bitflags showing which entries in prog_string_indizes[] are valid: * if (_tags[n] & (1 << b)) then _indizes[n*8 + b] is valid. */ static char *last_string_constant = NULL; /* The current (last) string constant, a shared string. * It is also used to optimize "foo"+"bar" constructs. */ static int current_break_stack_need = 0; /* Current depth of the required switch/break stack at this point * in a function. */ static int max_break_stack_need = 0; /* Total depth of the required switch/break stack for this function. * This information is required when computing the 'num_locals' * for the function header. */ static p_int stored_bytes; /* Size of the program at the last time of store_line_number_info(). */ static p_int stored_lines; /* Current linenumber at the last time of store_line_number_info(). */ static int simple_includes; /* Number of simple includes since the last real one. */ static p_uint last_include_start; /* Address in A_LINENUMBERS of the last include information. * It is used to remove information about includes which do * not generate information ('simple includes'). */ /*-------------------------------------------------------------------------*/ /* Forward declarations */ struct lvalue_s; /* Defined within YYSTYPE aka %union */ static Bool add_lvalue_code ( struct lvalue_s * lv, int instruction); static void insert_pop_value(void); static void arrange_protected_lvalue(p_int, int, p_int, int); static int insert_inherited(char *,char *, program_t **, function_t *, int, bytecode_p); /* Returnvalues from insert_inherited(): */ # define INHERITED_NOT_FOUND (-1) # define INHERITED_WILDCARDED_ARGS (-2) # define INHERITED_WILDCARDED_NOT_FOUND (-3) static void store_line_number_relocation(int relocated_from); int yyparse(void); %ifdef INITIALIZATION_BY___INIT static void add_new_init_jump(void); static void transfer_init_control(void); static void copy_variables(program_t *, fulltype_t); static int copy_functions(program_t *, fulltype_t type); %else static void copy_variables(program_t *, fulltype_t, svalue_t *); static void copy_functions(program_t *, fulltype_t type); %endif static void fix_function_inherit_indices(program_t *); static void fix_variable_index_offsets(program_t *); /*-------------------------------------------------------------------------*/ void yyerror (char *str) /* Raise the parse error <str>: usually generate the error message and log it. * If this is the first error in this file, account the wizard with an error. * If too many errors occured already, do nothing. */ { char *context; if (num_parse_error > 5) return; context = lex_error_context(); fprintf(stderr, "%s %s line %d: %s%s.\n" , time_stamp(), current_file, current_line, str, context); /* TODO: lex should implement a function get_include_stack() which * TODO:: returns an svalue-array with the current include stack. * TODO:: This could be printed, and also passed to parse_error(). */ fflush(stderr); parse_error(MY_FALSE, current_file, current_line, str, context); if (num_parse_error == 0) save_error(str, current_file, current_line); num_parse_error++; } /* yyerror() */ /*-------------------------------------------------------------------------*/ void yyerrorf (char *format, ...) /* Generate an yyerror() using printf()-style arguments. */ { va_list va; char buff[5120]; char fixed_fmt[1000]; format = limit_error_format(fixed_fmt, sizeof(fixed_fmt), format); va_start(va, format); vsprintf(buff, format, va); va_end(va); yyerror(buff); } /* yyerrorf() */ /*-------------------------------------------------------------------------*/ void yywarn (char *str) /* Raise the parse warning <str>: usually generate the warning message and * log it. */ { char *context; context = lex_error_context(); fprintf(stderr, "%s %s line %d: Warning: %s%s\n" , time_stamp(), current_file, current_line, str, context); /* TODO: lex should implement a function get_include_stack() which * TODO:: returns an svalue-array with the current include stack. * TODO:: This could be printed, and also passed to parse_error(). */ fflush(stderr); parse_error(MY_TRUE, current_file, current_line, str, context); if (num_parse_error == 0) save_error(str, current_file, current_line); } /* yywarn() */ /*-------------------------------------------------------------------------*/ void yywarnf (char *format, ...) /* Generate an yywarn() using printf()-style arguments. */ { va_list va; char buff[5120]; char fixed_fmt[1000]; format = limit_error_format(fixed_fmt, sizeof(fixed_fmt), format); va_start(va, format); vsprintf(buff, format, va); va_end(va); yywarn(buff); } /* yywarnf() */ /*-------------------------------------------------------------------------*/ static void * yalloc (size_t size) /* Allocate a block of <size>, add it at the head of the last_yalloced * list, and return the pointer. * * Together with yfree(), this allocator is able to free intermediate * results in the epilog() which were thrown away due to an error. * TODO: A stack'ish mempool could do this? */ { char **p; p = xalloc(size+sizeof(char*)); if (!p) { fatal("Out of memory in compiler.\n"); return NULL; } *p++ = last_yalloced; last_yalloced = (char *)p; return p; } /* yalloc() */ /*-------------------------------------------------------------------------*/ static void yfree (void *block) /* Free the block last allocated by yalloc(). */ { char **p; p = (char **)block; if (p != (char **)last_yalloced) { debug_message("%s Block mismatch", time_stamp()); return; } last_yalloced = *--p; xfree(p); } /* yfree() */ /*-------------------------------------------------------------------------*/ static char * ystring_copy (char *str) /* Duplicate the string <str> using yalloc() and return the new one. */ { char *p; p = yalloc(strlen(str)+1); strcpy(p, str); return p; } /* ystring_copy() */ /*-------------------------------------------------------------------------*/ static void add_string_constant (void) /* Add the string <last_lex_string> to the string in <last_string_constant>. * This is used to optimize "foo" + "bar" constructs. */ { size_t len1; char *tmp; len1 = strlen(last_string_constant); tmp = alloca(len1 + strlen(last_lex_string) + 1); strcpy(tmp, last_string_constant); strcpy(tmp + len1, last_lex_string); free_string(last_string_constant); free_string(last_lex_string); last_string_constant = make_shared_string(tmp); last_lex_string = NULL; } /* add_string_constant() */ /*-------------------------------------------------------------------------*/ static char * realloc_mem_block (mem_block_t *mbp, mp_int size) /* Resize memblock <mbp> to hold at least <size> bytes, but at least * double its current size. * * Return NULL when out of memory, or a pointer to the newly allocated * memory area (ie. mbp->block). */ { mp_uint max_size; char *p; max_size = mbp->max_size; do { max_size *= 2; } while (size > max_size); p = rexalloc(mbp->block, max_size); if (!p) { lex_close("Out of memory"); return NULL; } mbp->block = p; mbp->max_size = max_size; return p; } /* realloc_mem_block() */ /*-------------------------------------------------------------------------*/ static INLINE void add_to_mem_block (int n, void *data, size_t size) /* Add the <data> block of <size> bytes to the memory area <n>. */ { mem_block_t *mbp = &mem_block[n]; if (size) { if (mbp->current_size + size > mbp->max_size) { if (!realloc_mem_block(mbp, mbp->current_size + size)) return; } memcpy(mbp->block + mbp->current_size, data, size); mbp->current_size += size; } } /* add_to_mem_block() */ /*-------------------------------------------------------------------------*/ #define byte_to_mem_block(n, b) \ ((void)((mem_block[n].current_size == mem_block[n].max_size \ ? !!realloc_mem_block(&mem_block[n],0) : 1) \ ? (mem_block[n].block[mem_block[n].current_size++] = (char)(b)) \ : 0)\ ) /* Add the byte <b> to the memory area <n>, which is resized * if necessary. */ /* ============================== TYPES ============================== */ /*-------------------------------------------------------------------------*/ #define BASIC_TYPE(e,t) \ ((e) == TYPE_ANY || (e) == (t) || (t) == TYPE_ANY) /* Return TRUE if <e> and <t> are compatible basic types. */ #define TYPE(e,t) \ ( BASIC_TYPE((e) & TYPE_MOD_MASK, (t) & TYPE_MOD_MASK) \ || ( ((e) & TYPE_MOD_POINTER) && ((t) & TYPE_MOD_POINTER) \ && BASIC_TYPE((e) & (TYPE_MOD_MASK & ~TYPE_MOD_POINTER),\ (t) & (TYPE_MOD_MASK & ~TYPE_MOD_POINTER))\ )) /* Return TRUE if <e> and <t> are compatible basic xor pointer types. */ #define MASKED_TYPE(e,t) \ ( BASIC_TYPE( (e) , (t) ) \ || ( (e) == (TYPE_MOD_POINTER|TYPE_ANY) && (t) & TYPE_MOD_POINTER ) \ || ( (t) == (TYPE_MOD_POINTER|TYPE_ANY) && (e) & TYPE_MOD_POINTER ) \ ) /* Return TRUE if <e> and <t> are compatible basic types, or if both * are pointer types and one of them is a *ANY. */ #define REDEFINED_TYPE(e,t) \ ( BASIC_TYPE( (e), (t) ) \ || ( (t) == (TYPE_MOD_POINTER|TYPE_ANY) ) \ || ( (e) == (TYPE_MOD_POINTER|TYPE_ANY) ) \ ) /* Return TRUE if type <t> is a proper redefinition of <e>. * This is the case if <e> and <t> are compatible base types, * or if one of them is *ANY. */ /*-------------------------------------------------------------------------*/ static char * get_visibility (fulltype_t type) /* Return (in a static buffer) a textual representation of the visibility * portion of <type>. */ { static char buff[100]; size_t len; buff[0] = '\0'; if (type & TYPE_MOD_STATIC) strcat(buff, "static "); if (type & TYPE_MOD_NO_MASK) strcat(buff, "nomask "); if (type & TYPE_MOD_PRIVATE) strcat(buff, "private "); if (type & TYPE_MOD_PROTECTED) strcat(buff, "protected "); if (type & TYPE_MOD_PUBLIC) strcat(buff, "public "); if (type & TYPE_MOD_VARARGS) strcat(buff, "varargs "); len = strlen(buff); if (len) buff[len-1] = '\0'; return buff; } /* get_visibility() */ /*-------------------------------------------------------------------------*/ static char * get_type_name (fulltype_t type) /* Return (in a static buffer) a textual representation of <type>. */ { static char buff[100]; static char *type_name[] = { "unknown", "int", "string", "void", "object", "mapping", "float", "mixed", "closure", "symbol", "quoted_array", }; Bool pointer = MY_FALSE, reference = MY_FALSE; buff[0] = '\0'; if (type & TYPE_MOD_STATIC) strcat(buff, "static "); if (type & TYPE_MOD_NO_MASK) strcat(buff, "nomask "); if (type & TYPE_MOD_PRIVATE) strcat(buff, "private "); if (type & TYPE_MOD_PROTECTED) strcat(buff, "protected "); if (type & TYPE_MOD_PUBLIC) strcat(buff, "public "); if (type & TYPE_MOD_VARARGS) strcat(buff, "varargs "); type &= TYPE_MOD_MASK; if (type & TYPE_MOD_POINTER) { pointer = MY_TRUE; type &= ~TYPE_MOD_POINTER; } if (type & TYPE_MOD_REFERENCE) { reference = MY_TRUE; type &= ~TYPE_MOD_REFERENCE; } if (type >= sizeof type_name / sizeof type_name[0]) fatal("Bad type %ld\n", (long)type); strcat(buff, type_name[type]); strcat(buff," "); if (pointer) strcat(buff, "* "); if (reference) strcat(buff, "& "); return buff; } /* get_type_name() */ /*-------------------------------------------------------------------------*/ static char * get_two_types (fulltype_t type1, fulltype_t type2) /* Return (in a static buffer) the text "(<type1> vs. <type2>)". */ { static char buff[100]; strcpy(buff, "( "); strcat(buff, get_type_name(type1)); strcat(buff, "vs "); strcat(buff, get_type_name(type2)); strcat(buff, ")"); return buff; } /* get_two_types() */ /*-------------------------------------------------------------------------*/ static void type_error (char *str, fulltype_t type) /* Generate an yyerror with the message "<str>: <type>". */ { char *p; p = get_type_name(type); yyerrorf("%s: \"%s\"", str, p); } /* type_error() */ /*-------------------------------------------------------------------------*/ static void argument_type_error (int instr, fulltype_t type) /* Generate an yyerror with the message "Bad argument to <instr>: <type>". */ { char *p; p = get_type_name(type); yyerrorf("Bad argument to %s: \"%s\"", instrs[instr].name, p); } /* argument_type_error() */ /*-------------------------------------------------------------------------*/ static Bool compatible_types (fulltype_t t1, fulltype_t t2) /* Compare the two types <t1> and <t2> and return TRUE if they are compatible. * Rules: * - every type is compatible to itself * - TYPE_UNKNOWN is incompatible to everything * - TYPE_ANY is compatible to everything * - two POINTER types are compatible if at least one is *TYPE_ANY. */ { if (t1 == TYPE_UNKNOWN || t2 == TYPE_UNKNOWN) return MY_FALSE; if (t1 == t2) return MY_TRUE; if (t1 == TYPE_ANY || t2 == TYPE_ANY) return MY_TRUE; if ((t1 & TYPE_MOD_POINTER) && (t2 & TYPE_MOD_POINTER)) { if ((t1 & TYPE_MOD_MASK) == (TYPE_ANY|TYPE_MOD_POINTER) || (t2 & TYPE_MOD_MASK) == (TYPE_ANY|TYPE_MOD_POINTER)) return MY_TRUE; } return MY_FALSE; } /* compatible_types() */ /*-------------------------------------------------------------------------*/ static INLINE void add_arg_type (vartype_t type) /* Add another function argument type to the argument type stack. */ { mem_block_t *mbp = &type_of_arguments; if (mbp->current_size + sizeof type > mbp->max_size) { mbp->max_size *= 2; mbp->block = rexalloc((char *)mbp->block, mbp->max_size); } *(vartype_t*)(mbp->block + mbp->current_size) = type; mbp->current_size += sizeof type; } /* add_arg_type() */ /*-------------------------------------------------------------------------*/ static INLINE void pop_arg_stack (int n) /* Pop (remove) the last <n> types from the argument stack. */ { type_of_arguments.current_size -= sizeof (vartype_t) * n; } /* pop_arg_stack() */ /*-------------------------------------------------------------------------*/ static INLINE vartype_t * get_argument_types_start (int n) /* Get the type of the <n>th last argument from the stack. * <n> must be >= 1. */ { return &((vartype_t *) (type_of_arguments.block + type_of_arguments.current_size))[ - n]; } /* get_arguments_type_start() */ /*-------------------------------------------------------------------------*/ static INLINE void check_aggregate_types (int n) /* The last <n> types on the argument stack are an aggregate type. * Combine the single types and make sure that none is a reference type. */ { vartype_t *argp, mask; argp = (vartype_t *) (type_of_arguments.block + (type_of_arguments.current_size -= sizeof (vartype_t) * n) ); /* We're just interested in TYPE_MOD_REFERENCE, so we preset all * other bits with 1. */ for (mask = ~TYPE_MOD_REFERENCE; --n >= 0; ) { mask |= *argp++; } if (!(~mask & 0xffff)) yyerror("Can't trace reference assignments."); } /* check_aggregate_types() */ /* ============================= CODEGEN ============================= */ /*-------------------------------------------------------------------------*/ static INLINE char * realloc_a_program (size_t size) /* If necessary, increase the allocated size of the A_PROGRAM area so that at * least <size> more bytes can be stored in it. * * Return NULL when out of memory, or a pointer to the (possibly newly * allocated) memory area (ie. mem_block[A_PROGRAM].block). */ { mem_block_t * mbp = &mem_block[A_PROGRAM]; mp_uint new_size = mbp->current_size + size; if (new_size <= mbp->max_size) return mbp->block; return realloc_mem_block(mbp, new_size); } /* realloc_a_program() */ /*-------------------------------------------------------------------------*/ #define ins_byte(b) byte_to_mem_block(A_PROGRAM, b) #ifndef ins_byte static INLINE void ins_byte (unsigned char b) /* Add the byte <b> to the A_PROGRAM area. */ { if (mem_block[A_PROGRAM].current_size == mem_block[A_PROGRAM].max_size ) { if (!realloc_a_program(1)) { yyerrorf("Out of memory: program size %lu\n" , mem_block[A_PROGRAM].current_size + 1); return; } } mem_block[A_PROGRAM].block[mem_block[A_PROGRAM].current_size++] = b; } /* ins_byte() */ #endif /*-------------------------------------------------------------------------*/ static void ins_f_code (unsigned int b) /* Add the instruction <b> to the A_PROGRAM area, taking care of encoding * multi-byte instructions properly. */ { if (b > 0x100) ins_byte(b >> F_ESCAPE_BITS); ins_byte(b); } /* ins_f_code() */ /*-------------------------------------------------------------------------*/ static void ins_short (short l) /* Add the 2-byte number <l> to the A_PROGRAM area in a fixed byteorder. */ { if (realloc_a_program(2)) { mp_uint current_size; char *dest; current_size = CURRENT_PROGRAM_SIZE; CURRENT_PROGRAM_SIZE = current_size + 2; dest = mem_block[A_PROGRAM].block + current_size; PUT_SHORT(dest, l); } else { yyerrorf("Out of memory: program size %lu\n" , mem_block[A_PROGRAM].current_size + 2); } } /* ins_short() */ /*-------------------------------------------------------------------------*/ static void upd_short (mp_uint offset, short l) /* Store the 2-byte number <l> at <offset> in the A_PROGRAM are in * a fixed byteorder. */ { char *dest; dest = mem_block[A_PROGRAM].block + offset; PUT_SHORT(dest, l); } /* upd_short() */ /*-------------------------------------------------------------------------*/ static short read_short (mp_uint offset) /* Return the 2-byte number stored at <offset> in the A_PROGRAM area. */ { short l; char *dest; dest = mem_block[A_PROGRAM].block + offset; GET_SHORT(l, dest); return l; } /* read_short() */ %ifdef INITIALIZATION_BY___INIT /*-------------------------------------------------------------------------*/ static void upd_offset (mp_uint offset, long l) /* Store the 3-byte number <l> at <offset> in the A_PROGRAM are in * a fixed byteorder. */ { char *dest; dest = mem_block[A_PROGRAM].block + offset; STORE_UINT8(dest, l>>16); PUT_SHORT(dest, l & 0xffff); } /* upd_offset() */ %endif /* INITIALIZATION_BY___INIT */ /*-------------------------------------------------------------------------*/ static void ins_long (int32 l) /* Add the 4-byte number <l> to the A_PROGRAM area in a fixed byteorder. */ { if (realloc_a_program(4)) { mp_uint current_size; char *dest; current_size = CURRENT_PROGRAM_SIZE; CURRENT_PROGRAM_SIZE = current_size + 4; dest = mem_block[A_PROGRAM].block + current_size; PUT_INT32(dest, l); } else { yyerrorf("Out of memory: program size %lu\n" , mem_block[A_PROGRAM].current_size + 4); } } /* ins_long() */ /*-------------------------------------------------------------------------*/ /* The following macros are used for a speedy codegeneration within bigger * functions. * * To insert at max <n> bytes, the function has to declare * * PREPARE_INSERT(n) * * among the variables and can the use the following macros to add bytes: * * add_byte(b): to add byte <b> to the program * add_short(s): to add short <s> to the program */ #define PREPARE_INSERT(n) \ bytecode_p __PREPARE_INSERT__p = (\ realloc_a_program(n) ? (PROGRAM_BLOCK + CURRENT_PROGRAM_SIZE) : NULL); #define add_byte(b) (void) STORE_INT8(__PREPARE_INSERT__p, (b)) #define add_short(s) STORE_SHORT(__PREPARE_INSERT__p, (s)) /*-------------------------------------------------------------------------*/ static void push_address (void) /* Push the current program size as address onto the compiler stack. */ { if (comp_stackp >= COMPILER_STACK_SIZE) { yyerror("Compiler stack overflow"); /* Don't store the address, but keep proper track of the depth. */ comp_stackp++; return; } comp_stack[comp_stackp++] = mem_block[A_PROGRAM].current_size; } /* push_address() */ /*-------------------------------------------------------------------------*/ static void push_explicit (p_int address) /* Push the program <address> onto the compiler stack. */ { if (comp_stackp >= COMPILER_STACK_SIZE) { yyerror("Compiler stack overflow"); /* Don't store the address, but keep proper track of the depth. */ comp_stackp++; return; } comp_stack[comp_stackp++] = address; } /* push_explicit() */ /*-------------------------------------------------------------------------*/ static p_int pop_address (void) /* Pop the most recent stored address from the compiler stack and return * it. */ { if (comp_stackp == 0) fatal("Compiler stack underflow.\n"); if (comp_stackp > COMPILER_STACK_SIZE) { /* Nothing to retrieve, but keep track of the depth */ --comp_stackp; return 0; } return comp_stack[--comp_stackp]; } /* pop_address() */ /*-------------------------------------------------------------------------*/ static Bool fix_branch (int ltoken, p_int dest, p_int loc) /* Backpatch a branch instruction at <loc> to jump to <dest>. * If the offset exceeds the 255 range, the branch instruction is changed * into its long-branch variant <ltoken>. * * Return TRUE if the long branch had to be used, FALSE otherwise. * TODO: This really confuses the line number detection code, as suddenly * TODO:: the recorded offset are no longer accurate. */ { p_int offset; /* The branch offset */ offset = dest - (loc +1); if (offset > 0xff) { /* We need a long branch. That also means that we have to * move the following code and adapt remembered addresses. */ p_int i, j; bytecode_p p; mem_block[A_PROGRAM].block[loc] = 0; /* Init it */ /* Update the break address */ if ( current_break_address > loc && !(current_break_address & (BREAK_ON_STACK|BREAK_DELIMITER) ) ) { for (i = current_break_address; (j = read_short(i)) > loc; ) { upd_short(i, j+1); i = j; } current_break_address++; } /* Update the continue address */ if ( (current_continue_address & CONTINUE_ADDRESS_MASK) > loc && !(current_continue_address & CONTINUE_DELIMITER ) ) { for(i = current_continue_address & CONTINUE_ADDRESS_MASK; (j=read_short(i)) > loc; ) { upd_short(i, j+1); i = j; } current_continue_address++; } ins_byte(0); /* Just to make sure the memory is there */ /* Move the code */ p = PROGRAM_BLOCK + mem_block[A_PROGRAM].current_size-1; i = mem_block[A_PROGRAM].current_size - loc; for( ; --i >= 0; --p ) { PUT_CODE(p, GET_CODE(p-1)); } /* Store the new branch instruction */ PUT_CODE(p, ltoken); upd_short(loc, offset+2); if (offset > 0x7ffd) yyerror("offset overflow"); return MY_TRUE; } else { /* Just update the offset */ mem_block[A_PROGRAM].block[loc] = offset; return MY_FALSE; } } /* fix_branch() */ /*-------------------------------------------------------------------------*/ static bytecode_p yyget_space (p_int size) /* Callback function for switch: return a pointer to <size> more bytes * in the program area. */ { if (realloc_a_program(size)) { CURRENT_PROGRAM_SIZE += size; return PROGRAM_BLOCK + CURRENT_PROGRAM_SIZE - size; } yyerrorf("Out of memory: program size %lu\n" , mem_block[A_PROGRAM].current_size + size); return NULL; } /* yyget_space() */ /*-------------------------------------------------------------------------*/ static void yymove_switch_instructions (int len, p_int blocklen) /* Callback function for switch: move the <blocklen> bytecodes at <switch_pc> * back by <len> bytes to <switch_pc>+<len>. A continue address in the * affected area is corrected. */ { mp_int i, j; if (realloc_a_program(len)) { CURRENT_PROGRAM_SIZE += len; /* Adjust the continue address, if any */ if ( (current_continue_address & CONTINUE_ADDRESS_MASK) > switch_pc && !(current_continue_address & CONTINUE_DELIMITER ) ) { for(i = current_continue_address & CONTINUE_ADDRESS_MASK; (j=read_short(i)) > switch_pc; ) { upd_short(i, j+len); i = j; } current_continue_address += len; } move_memory( mem_block[A_PROGRAM].block + switch_pc + len, mem_block[A_PROGRAM].block + switch_pc, blocklen ); } else { yyerrorf("Out of memory: program size %lu\n" , mem_block[A_PROGRAM].current_size + len); } } /* yymove_switch_instructions() */ /*-------------------------------------------------------------------------*/ static void yycerrorl (char *s1, char *s2, int line1, int line2) /* Callback function for switch: Raise an error <s1> in file <s2> at * lines <line1> and <line2>. * <s1> may contain one '%s' to insert s2, <s2> may contain one or * or two '%d' to insert line1 and line2. */ { char buff[100]; sprintf(buff, s2, line1, line2); yyerrorf(s1, buff); } /* yycerrorl() */ /*-------------------------------------------------------------------------*/ static void update_lop_branch ( p_uint address, int instruction ) /* <address> points to the branch offset value of an LAND/LOR operation, * currently set to 0. Update that offset to branch to the current end * of the program. * * If that branch is too long, the code is rewritten: * * Original: Rewritten: * * <expr1> <expr1> * LOR/LAND l DUP * <expr2> LBRANCH_<instruction> * l: POP_VALUE * <expr2> * l: * * The extra DUP compensates the svalue the LBRANCH eats. * The LBRANCH_<instruction> needs to be passed suiting the logical * operator: LBRANCH_WHEN_ZERO for LAND, LBRANCH_WHEN_NON_ZERO for LOR. */ { p_int offset; last_expression = -1; offset = mem_block[A_PROGRAM].current_size - ( address + 1); if (offset > 0xff) { /* A long branch is needed */ int i; bytecode_p p; ins_short(0); ins_byte(0); p = PROGRAM_BLOCK + mem_block[A_PROGRAM].current_size-1; for (i = offset; --i >= 0; --p ) *p = p[-3]; p[-4] = F_DUP; p[-3] = instruction; upd_short(address+1, offset+3); if (offset > 0x7ffc) yyerror("offset overflow"); p[0] = F_POP_VALUE; } else { mem_block[A_PROGRAM].block[address] = offset; } } /* update_lop_branch() */ /* ======================== LOCALS and SCOPES ======================== */ /*-------------------------------------------------------------------------*/ void free_all_local_names (void) /* Free all local names, and reset the counters. */ { ident_t *p,*q; for (q = all_locals; NULL != (p = q);) { q = p->next_all; free_shared_identifier(p); } all_locals = NULL; current_number_of_locals = 0; max_number_of_locals = 0; current_break_stack_need = 0; max_break_stack_need = 0; } /* free_all_local_names() */ /*-------------------------------------------------------------------------*/ static void free_local_names (int depth) /* Free all locals in the all_locals list which are of higher or * the same <depth>, and adjust the counters. * A <depth> of 0 is equivalent to calling free_all_local_names(). */ { ident_t *q; if (!depth) { free_all_local_names(); return; } /* Are the locals of the given depth? */ if (!all_locals || all_locals->u.local.depth < depth) return; if (all_locals->u.local.depth > depth) fatal("List of locals clobbered: depth %d, block_depth %d\n" , all_locals->u.local.depth, depth); while (all_locals != NULL && all_locals->u.local.depth >= depth) { q = all_locals; all_locals = q->next_all; free_shared_identifier(q); current_number_of_locals--; } } /* free_local_names() */ /*-------------------------------------------------------------------------*/ static ident_t * add_local_name (ident_t *ident, fulltype_t type, int depth) /* Declare a new local variable <ident> with the type <type> on * the scope depth <depth>. * Return the (adjusted) ident for the new variable. */ { if ((type & PRIMARY_TYPE_MASK) == TYPE_VOID) { yyerrorf( "Illegal to define variable '%s' as type 'void'" , ident->name); } if (current_number_of_locals >= MAX_LOCAL || current_number_of_locals >= 256) yyerror("Too many local variables"); else { if (ident->type != I_TYPE_UNKNOWN) { /* We're overlaying some other definition */ ident = make_shared_identifier(ident->name, I_TYPE_LOCAL, depth); } /* Initialize the ident */ ident->type = I_TYPE_LOCAL; ident->u.local.num = current_number_of_locals; ident->u.local.depth = depth; /* Put the ident into the list of all locals */ if (all_locals && all_locals->u.local.depth > depth) fatal("List of locals clobbered: depth %d, adding depth %d\n" , all_locals->u.local.depth, depth); ident->next_all = all_locals; all_locals = ident; /* Record the type */ type_of_locals[current_number_of_locals] = type; full_type_of_locals[current_number_of_locals++] = type; /* And update the scope information */ if (current_number_of_locals > max_number_of_locals) max_number_of_locals = current_number_of_locals; block_scope[depth-1].num_locals++; } return ident; } /* add_local_name() */ /*-------------------------------------------------------------------------*/ static void init_scope (int depth) /* Initialize the block_scope entry for block_depth <depth>. */ { block_scope[depth-1].num_locals = 0; block_scope[depth-1].first_local = current_number_of_locals; block_scope[depth-1].addr = 0; } /* init_scope() */ /*-------------------------------------------------------------------------*/ static void enter_block_scope (void) /* Enter a new scope and initialize it (if use_local_scopes requires it). */ { if (block_depth == COMPILER_STACK_SIZE) yyerror("Too deep nesting of local blocks.\n"); if (use_local_scopes) { block_depth++; init_scope(block_depth); } } /* enter_block_scope() */ /*-------------------------------------------------------------------------*/ static void leave_block_scope (void) /* Leave the current scope (if use_local_scopes requires it), freeing * all local names defined in that scope. */ { if (use_local_scopes) { free_local_names(block_depth); block_depth--; } } /* leave_block_scope() */ /*-------------------------------------------------------------------------*/ static ident_t * lookup_local (int num) /* Lookup the ident_t structure for local variable <num>. */ { ident_t *p, *q; /* First, find the declaration of this local */ q = NULL; for (p = all_locals; p != NULL; p = p->next_all) { if (p->u.local.num == num) { q = p; break; } } /* q should be set here and point to the previous declaration. */ if (!q) fatal("Local identifier %ld not found in list.\n", (long)num); return q; } /* lookup_local() */ /*-------------------------------------------------------------------------*/ static ident_t * redeclare_local (int num, fulltype_t type, int depth) /* Redeclare a local name, identified by <num>, to <type> at <depth>. * If this happens on a deeper level, it is legal: the new declaration * is added and the new identifier is returned. * If it is illegal, an yyerror() is risen and the ident of the older * declaration is returned for error recovery. */ { ident_t *p, *q; if (all_locals && all_locals->u.local.depth > depth) { fatal("List of locals clobbered: list depth %d, " "block depth %d\n" , all_locals->u.local.depth, depth); } /* First, find the previous declaration of this local */ q = NULL; for (p = all_locals; p != NULL; p = p->next_all) { if (p->u.local.num == num) { /* We found the identifier, and due to the list properties * it is also the one with deepest depth. */ q = p; break; } } /* q should be set here and point to the previous declaration. * If it is of lower depth, it may be shadowed. However, it * is not possible to shadow an argument (depth 1) with * a function-local variable (depth 2). */ if (!q) fatal("Local identifier %ld not found in list.\n", (long)num); if (q->u.local.depth >= depth || (q->u.local.depth == 1 && depth == 2) ) { yyerrorf("Illegal to redeclare local name '%s'", q->name); } else { /* TODO: Add a warning for shadowed variable */ q = add_local_name(q, type, depth); } return q; } /* redeclare_local() */ /* ====================== GLOBALS and FUNCTIONS ====================== */ /*-------------------------------------------------------------------------*/ static unsigned short store_argument_types ( int num_arg ) /* Store the <num_arg> argument types from global type_of_locals[] into * the proper memblock and return the new argument start index. * It is task of the caller to store this start index where it belongs. * * If exact_types are not required, the function just returns * INDEX_START_NONE. */ { unsigned short argument_start_index; /* Store the function arguments, if required. */ if (!exact_types) { argument_start_index = INDEX_START_NONE; } else { int i; /* Save the argument types. */ argument_start_index = ARGTYPE_COUNT; for (i = 0; i < num_arg; i++) { add_to_mem_block(A_ARGUMENT_TYPES, &type_of_locals[i], sizeof type_of_locals[i]); } } return argument_start_index; } /* store_argument_types() */ /*-------------------------------------------------------------------------*/ static int define_new_function ( Bool complete, ident_t *p, int num_arg, int num_local , p_int offset, funflag_t flags, fulltype_t type) /* Define a new function <p> with the characteristics <num_arg>, <num_local>, * program <offset>, <flags> and <type>. * Result is the number (index) of the function. * * The function is called whenever a function header (return type, name * and arguments) has been parsed - <complete> is FALSE then. Additionally, * the function is called as well after a functionbody has been parsed, * <complete> is TRUE then. * * This function is called at least twice for all function definitions: * first as prototype (flags & NAME_PROTOTYPE) when the function def is * encountered, then a second time for real when the function has been * completed. Explicite prototypes can cause additional calls. */ { int num; function_t fun; unsigned short argument_start_index; flags |= type & ~TYPE_MOD_MASK; /* Move the visibility-info into flags */ do { function_t *funp; Bool args_differ, compare_args; if (p->type != I_TYPE_GLOBAL) break; if ((num = p->u.global.function) < 0) break; funp = FUNCTION(num); if ((funp->flags & (NAME_INHERITED|TYPE_MOD_PRIVATE)) == (NAME_INHERITED|TYPE_MOD_PRIVATE)) { break; } /* The function was already defined. It may be one of several reasons: * * 1. There has been a prototype. * 2. There was the same function defined by inheritance. * 3. This function has been called, but not yet defined. * 4. The function is defined twice. * 5. A "late" prototype has been encountered. */ args_differ = MY_FALSE; compare_args = MY_FALSE; /* The following checks are useful only when done before * a functionbody appears, otherwise the warning/error message * line numbers will be misleading. */ if (!complete) { if ((funp->flags & TYPE_MOD_NO_MASK) && !(funp->flags & (NAME_PROTOTYPE|NAME_UNDEFINED)) && ((flags & (NAME_PROTOTYPE|NAME_UNDEFINED)) == (NAME_PROTOTYPE|NAME_UNDEFINED)) ) yyerrorf("Illegal to redefine 'nomask' function \"%s\"", p->name); if (!(funp->flags & (NAME_UNDEFINED|NAME_PROTOTYPE|NAME_INHERITED) ) ) { yyerrorf("Redeclaration of function %s.", p->name); if ( !(flags & NAME_PROTOTYPE) ) free_string(p->name); return num; } /* It was either an undefined but used function, or an inherited * function. In both cases, we now consider this to be THE new * definition. It might also have been a prototype to an already * defined function. * * Check arguments only when types are supposed to be tested, * and if this function really has been defined already. * * 'nomask' functions may not be redefined. */ if (exact_types && funp->type != TYPE_UNKNOWN) { fulltype_t t1, t2; if (funp->num_arg > num_arg && !(funp->flags & TYPE_MOD_VARARGS)) yyerrorf("Incorrect number of arguments in redefinition of '%s'.", p->name); else if (funp->num_arg == num_arg && ((funp->flags ^ flags) & TYPE_MOD_XVARARGS) && !(funp->flags & TYPE_MOD_VARARGS)) yyerrorf("Incorrect number of arguments in redefinition of '%s'.", p->name); else { unsigned short first_arg; first_arg = ARGUMENT_INDEX(num); if (first_arg == INDEX_START_NONE) { if (num_arg && !(funp->flags & NAME_TYPES_LOST) ) yyerrorf( "Redefined function '%s' not compiled with type testing." , p->name ); } else { /* We can compare the arguments */ compare_args = MY_TRUE; } } /* cases (number of arguments) */ /* If it's a prototype->function redefinition, check if the * visibility is conserved. */ { # define TYPE_MOD_VIS \ ( TYPE_MOD_NO_MASK \ | TYPE_MOD_PRIVATE | TYPE_MOD_PUBLIC \ | TYPE_MOD_PROTECTED) fulltype_t f1 = funp->flags; fulltype_t f2 = flags; /* Smooth out irrelevant differences */ if (f1 & TYPE_MOD_STATIC) f1 |= TYPE_MOD_PROTECTED; if (f2 & TYPE_MOD_STATIC) f2 |= TYPE_MOD_PROTECTED; if (!(f1 & (NAME_INHERITED|NAME_TYPES_LOST)) && ((f1 ^ f2) & TYPE_MOD_VIS) ) { char buff[100]; strcpy(buff, get_visibility(funp->flags)); yywarnf("Inconsistent declaration of '%s': Visibility changed from '%s' to '%s'" , p->name, buff, get_visibility(flags)); } # undef TYPE_MOD_VIS } /* Check if the 'varargs' attribute is conserved */ t1 = type & TYPE_MOD_MASK; t2 = funp->type & TYPE_MOD_MASK; if (!MASKED_TYPE(t1, t2)) { if (pragma_pedantic) yyerrorf("Inconsistent declaration of '%s': Return type mismatch %s", p->name, get_two_types(t2, t1)); else yywarnf("Inconsistent declaration of '%s': Return type mismatch %s", p->name, get_two_types(t2, t1)); } if (pragma_pedantic && (funp->flags ^ flags) & TYPE_MOD_VARARGS && funp->flags & TYPE_MOD_VARARGS ) { yywarnf("Redefinition of '%s' loses 'varargs' modifier." , p->name); } /* Check that the two argument lists are compatible */ if (compare_args) { int i; unsigned short first_arg; vartype_t *argp; int num_args = num_arg; /* Don't check newly added arguments */ if (num_args > funp->num_arg) num_args = funp->num_arg; first_arg = ARGUMENT_INDEX(num); argp = (vartype_t *)mem_block[A_ARGUMENT_TYPES].block + first_arg; if (funp->flags & TYPE_MOD_XVARARGS) num_args--; /* last argument is ok */ for (i = 0; i < num_args; i++ ) { t1 = type_of_locals[i] & TYPE_MOD_RMASK; t2 = argp[i] & TYPE_MOD_MASK; if (!MASKED_TYPE(t1, t2)) { args_differ = MY_TRUE; if (pragma_pedantic) yyerrorf("Argument type mismatch in " "redefinition of '%s': arg %d %s" , p->name, i+1, get_two_types(t1, t2) ); else yywarnf("Argument type mismatch in " "redefinition of '%s': arg %d %s" , p->name, i+1, get_two_types(t1, t2) ); } } /* for (all args) */ } /* if (compare_args) */ } /* if (exact_types && already defined) */ } /* if (!complete) */ if (strcmp(p->name, "heart_beat") == 0) heart_beat = num; /* If it was yet another prototype, then simply return. */ if (flags & NAME_PROTOTYPE) { return num; } /* This is the completion of an earlier prototype: check * and update the arguments if necessary, and flesh * out the function structure. */ if (funp->num_arg != num_arg || args_differ) { /* Arguments changed. The only reasonable way this can happen * is if this function redefined an inherited one. * For that case, we re-create the arguments, for all other cases * (to be on the safe side), we turn off type * checking as we have no way of deciding which definition is the * correct one. */ if (funp->flags & NAME_INHERITED) { funp->num_arg = num_arg; ARGUMENT_INDEX(num) = store_argument_types(num_arg); } else { funp->num_arg = num_arg; ARGUMENT_INDEX(num) = INDEX_START_NONE; flags |= NAME_TYPES_LOST; } } funp->num_local = num_local; funp->flags = flags; funp->offset.pc = offset; funp->type = type; /* That's it */ return num; } while(0); /* Test and handle for already defined functions */ /* It's a new function! */ if (strcmp(p->name, "heart_beat") == 0) heart_beat = FUNCTION_COUNT; /* Fill in the function_t */ fun.name = p->name; /* adopt the ref */ fun.offset.pc = offset; fun.flags = flags; fun.num_arg = num_arg; fun.num_local = num_local; /* will be updated later */ fun.type = type; num = FUNCTION_COUNT; if (p->type != I_TYPE_GLOBAL) { /* This is the first _GLOBAL use of this identifier: * make an appropriate entry in the identifier table. */ if (p->type != I_TYPE_UNKNOWN) { /* The ident has been used before otherwise, so * get a fresh structure. */ p = make_shared_identifier(p->name, I_TYPE_GLOBAL, 0); } /* should be I_TYPE_UNKNOWN now. */ p->type = I_TYPE_GLOBAL; p->u.global.variable = I_GLOBAL_VARIABLE_OTHER; p->u.global.efun = I_GLOBAL_EFUN_OTHER; p->u.global.sim_efun = I_GLOBAL_SEFUN_OTHER; p->next_all = all_globals; all_globals = p; } else if (p->u.global.variable == I_GLOBAL_VARIABLE_FUN) { /* The previous _GLOBAL use is the permanent efun definition: * mark the efun as shadowed. */ efun_shadow_t *q; q = xalloc(sizeof(efun_shadow_t)); q->shadow = p; q->next = all_efun_shadows; all_efun_shadows = q; } /* else: Other cases don't need special treatment */ p->u.global.function = num; /* Store the function_t in the functions area */ add_to_mem_block(A_FUNCTIONS, &fun, sizeof fun); /* Store the function arguments, if required, * and save the position of the argument types. */ argument_start_index = store_argument_types(num_arg); add_to_mem_block( A_ARGUMENT_INDEX, &argument_start_index , sizeof argument_start_index); return num; } /* define_new_function() */ /*-------------------------------------------------------------------------*/ %ifdef INITIALIZATION_BY___INIT static void define_variable (ident_t *name, fulltype_t flags) %else /* then !INITIALIZATION_BY___INIT */ static void define_variable (ident_t *name, fulltype_t flags, svalue_t *svp) %endif /* INITIALIZATION_BY___INIT */ /* Define a new global variable <name> of type <flags>. * If !INITIALIZATION_BY___INIT, then <svp> is the initializer for the * variable. */ { variable_t dummy; int n; if ((flags & PRIMARY_TYPE_MASK) == TYPE_VOID) { yyerrorf( "Illegal to define variable '%s' as type 'void'" , name->name); } if (name->type != I_TYPE_GLOBAL) { /* This is the first _GLOBAL use of this identifier: * make an appropriate entry in the identifier table. */ if (name->type != I_TYPE_UNKNOWN) { /* The ident has been used before otherwise, so * get a fresh structure. */ name = make_shared_identifier(name->name, I_TYPE_GLOBAL, 0); } name->type = I_TYPE_GLOBAL; name->u.global.function = I_GLOBAL_FUNCTION_VAR; name->u.global.variable = I_GLOBAL_VARIABLE_OTHER; /* mark it as 'yet undef' for now */ name->u.global.efun = I_GLOBAL_EFUN_OTHER; name->u.global.sim_efun = I_GLOBAL_SEFUN_OTHER; name->next_all = all_globals; all_globals = name; } else if (name->u.global.function == I_GLOBAL_FUNCTION_EFUN) { /* The previous _GLOBAL use is the permanent efun definition: * mark the efun as shadowed. */ efun_shadow_t *q; q = xalloc(sizeof(efun_shadow_t)); q->shadow = name; q->next = all_efun_shadows; all_efun_shadows = q; } /* If the variable already exists, make sure that we can redefine it */ if ( (n = name->u.global.variable) >= 0) { /* Visible nomask variables can't be redefined */ if ( VARIABLE(n)->flags & TYPE_MOD_NO_MASK && !(flags & NAME_HIDDEN)) yyerrorf( "Illegal to redefine 'nomask' variable '%s'" , name->name); /* We can redefine inherited variables if they are private or hidden, * or if one of them is static. */ if ( ( !(VARIABLE(n)->flags & NAME_INHERITED) || ( !(VARIABLE(n)->flags & (TYPE_MOD_PRIVATE|NAME_HIDDEN)) && !((flags ^ VARIABLE(n)->flags) & TYPE_MOD_STATIC) ) ) && !(flags & NAME_INHERITED) ) { if (VARIABLE(n)->flags & NAME_INHERITED) yyerrorf("Illegal to redefine inherited variable '%s'", name->name); else yyerrorf("Illegal to redefine global variable '%s'", name->name); } if (((flags ^ VARIABLE(n)->flags) & (TYPE_MOD_STATIC|TYPE_MOD_PRIVATE)) == TYPE_MOD_STATIC && !(flags & NAME_INHERITED) ) { yywarnf("Redefining inherited %s variable '%s' with a %s variable" , (VARIABLE(n)->flags & TYPE_MOD_STATIC) ? "nosave" : "non-nosave" , name->name , (flags & TYPE_MOD_STATIC) ? "nosave" : "non-nosave" ); } /* Make sure that at least one of the two definitions is 'static'. * The variable which has not been inherited gets first pick. */ if (flags & NAME_INHERITED) { flags |= ~(VARIABLE(n)->flags) & TYPE_MOD_STATIC; } else { VARIABLE(n)->flags |= ~flags & TYPE_MOD_STATIC; } } /* Prepare the new variable_t */ if (flags & TYPE_MOD_NOSAVE) { /* 'nosave' is internally saved as 'static' (historical reason) */ flags |= TYPE_MOD_STATIC; flags ^= TYPE_MOD_NOSAVE; } dummy.name = ref_string(name->name); dummy.flags = flags; if (flags & TYPE_MOD_VIRTUAL) { if (!(flags & NAME_HIDDEN)) name->u.global.variable = VIRTUAL_VAR_TAG | V_VARIABLE_COUNT; add_to_mem_block(A_VIRTUAL_VAR, &dummy, sizeof dummy); %ifndef INITIALIZATION_BY___INIT add_to_mem_block(A_VIRTUAL_VAR_VALUES, svp, sizeof *svp); %endif /* INITIALIZATION_BY___INIT */ } else { if (!(flags & NAME_HIDDEN)) name->u.global.variable = NV_VARIABLE_COUNT; add_to_mem_block(A_VARIABLES, &dummy, sizeof dummy); %ifndef INITIALIZATION_BY___INIT add_to_mem_block(A_VARIABLE_VALUES, svp, sizeof *svp); %endif /* INITIALIZATION_BY___INIT */ } } /* define_variable() */ /*-------------------------------------------------------------------------*/ static void redeclare_variable (ident_t *name, fulltype_t flags, int n) /* The variable <name> is inherited virtually with number <n>. * Redeclare it from its original type to <flags>. */ { if (name->type != I_TYPE_GLOBAL) { /* This is the first _GLOBAL use of this identifier: * make an appropriate entry in the identifier table. */ /* I_TYPE_UNKNOWN */ name->type = I_TYPE_GLOBAL; name->u.global.function = I_GLOBAL_FUNCTION_VAR; name->u.global.variable = I_GLOBAL_VARIABLE_OTHER; /* default: it's hidden */ name->u.global.efun = I_GLOBAL_EFUN_OTHER; name->u.global.sim_efun = I_GLOBAL_SEFUN_OTHER; name->next_all = all_globals; all_globals = name; } else if (name->u.global.function == I_GLOBAL_FUNCTION_EFUN) { /* The previous _GLOBAL use is the permanent efun definition: * mark the efun as shadowed. */ efun_shadow_t *q; q = xalloc(sizeof(efun_shadow_t)); q->shadow = name; q->next = all_efun_shadows; all_efun_shadows = q; } /* else: the variable is inherited after it has been defined * in the child program. */ /* The variable is hidden, do nothing else */ if (flags & NAME_HIDDEN) return; if (name->u.global.variable >= 0 && name->u.global.variable != n) { if (VARIABLE(name->u.global.variable)->flags & TYPE_MOD_NO_MASK ) yyerrorf( "Illegal to redefine 'nomask' variable '%s'" , name->name); } else if (V_VARIABLE(n)->flags & TYPE_MOD_NO_MASK && !(V_VARIABLE(n)->flags & NAME_HIDDEN) && (V_VARIABLE(n)->flags ^ flags) & TYPE_MOD_STATIC ) { yyerrorf("Illegal to redefine 'nomask' variable \"%s\"", name->name); } if (flags & TYPE_MOD_NOSAVE) { /* 'nosave' is internally saved as 'static' (historical reason) */ flags |= TYPE_MOD_STATIC; flags ^= TYPE_MOD_NOSAVE; } name->u.global.variable = n; V_VARIABLE(n)->flags = flags; } /* redeclare_variable() */ /*-------------------------------------------------------------------------*/ static int verify_declared (ident_t *p) /* Check that <p> is a global variable. * If yes, return the index of that variable, -1 otherwise. */ { int r; if (p->type != I_TYPE_GLOBAL || (r = p->u.global.variable) < 0) { yyerrorf("Variable %s not declared !", p->name); return -1; } return r; } /* verify_declared() */ /* ========================= PROGRAM STRINGS ========================= */ /*-------------------------------------------------------------------------*/ static short store_prog_string (char *str) /* Add the shared string <str> to the strings used by the program. * The function takes care that the same string is not stored twice. * Result is the index of the string in the table, the function * adopts the reference of <str>. */ { mp_uint str_size, next_size; long hash; char mask, *tagp; int i, *indexp; /* Compute the hash and the tagmask for the hash table */ /* TODO: This assumes 32-Bit pointers */ hash = (long)str ^ (long)str >> 16; hash = (hash ^ hash >> 8); mask = 1 << (hash & 7); hash = hash & 0xff; indexp = &prog_string_indizes[hash]; tagp = &prog_string_tags[hash >> 3]; if (*tagp & mask) { /* There is a hash chain for this hash: search the * string in there. */ i = *indexp; for(;;) { if ( PROG_STRING(i) == str ) { free_string(str); /* Drop the extra ref. */ last_string_is_new = MY_FALSE; return i; } if ((i = PROG_STRING_NEXT(i)) < 0) break; } /* Not found: re-get the initial 'next'-index */ i = *indexp; } else { /* The first time this hash shows up (which also implies * that <str> is a new string. */ *tagp |= mask; i = -1; } /* Add a totally new string */ str_size = mem_block[A_STRINGS].current_size; next_size = mem_block[A_STRING_NEXT].current_size; /* Make sure we have enough memory */ if (str_size + sizeof(char *) > mem_block[A_STRINGS].max_size || next_size + sizeof(int) > mem_block[A_STRING_NEXT].max_size ) { if (!realloc_mem_block(&mem_block[A_STRINGS], 0) || !realloc_mem_block(&mem_block[A_STRING_NEXT], 0)) { if (i < 0) *tagp &= ~mask; last_string_is_new = MY_FALSE; return 0; } } /* Add the string pointer */ mem_block[A_STRINGS].current_size = str_size + sizeof(char *); *((char **)(mem_block[A_STRINGS].block+str_size)) = str; /* Add the old prog_string_index[] */ mem_block[A_STRING_NEXT].current_size = next_size + sizeof(int); *((int *)(mem_block[A_STRING_NEXT].block+next_size)) = i; /* Store the string index as new prog_string_index[] */ *indexp = str_size / sizeof str; last_string_is_new = MY_TRUE; return *indexp; } /* store_prog_string() */ /*-------------------------------------------------------------------------*/ static void delete_prog_string (void) /* Remove the program string last added with store_prog_string(). */ { char *str; int size; long hash; char mask, *tagp; int *indexp; /* Remove the string from the A_STRINGS area and free it */ size = mem_block[A_STRINGS].current_size - sizeof(char *); free_string( str = *(char**)(mem_block[A_STRINGS].block+size) ); mem_block[A_STRINGS].current_size = size; /* Remove the string from the hash table */ size = (mem_block[A_STRING_NEXT].current_size -= sizeof(int)); /* TODO: Assumes 32-Bit pointers */ hash = (long)str ^ (long)str >> 16; hash = (hash ^ hash >> 8); mask = 1 << (hash & 7); hash = hash & 0xff; indexp = &prog_string_indizes[hash]; tagp = &prog_string_tags[hash >> 3]; if ( ( *indexp = *((int *)(mem_block[A_STRING_NEXT].block+size)) ) < 0) /* Hash chain empty */ *tagp &= ~mask; } /* delete_prog_string() */ /* ========================== INITIALIZATION ========================== */ %ifndef INITIALIZATION_BY___INIT /*-------------------------------------------------------------------------*/ static INLINE fulltype_t type_rtoc (svalue_t *svp) /* Return the proper TYPE_ value for the type given by svalue <svp>. */ { switch (svp->type) { case T_NUMBER: return !svp->u.number ? TYPE_ANY : TYPE_NUMBER; case T_STRING: return TYPE_STRING; case T_POINTER: return TYPE_MOD_POINTER | TYPE_ANY; case T_FLOAT: return TYPE_FLOAT; case T_CLOSURE: return TYPE_CLOSURE; case T_SYMBOL: return TYPE_SYMBOL; case T_QUOTED_ARRAY: return TYPE_QUOTED_ARRAY; case T_MAPPING: return TYPE_MAPPING; default: fatal("Bad svalue type at compile time.\n"); } /* NOTREACHED */ return TYPE_ANY; } /* type_rtoc() */ /*-------------------------------------------------------------------------*/ static INLINE svalue_t * copy_svalue (svalue_t *svp) /* Create another reference to the value <svp> and return it. * Of course this only works for numbers, floats and shareable svalues * like shared strings or arrays. * If the <svp> is not shareable, the function will return a reference * to the svalue-0. * * The function is used to store svalues in the initializer table for * a program. */ { switch (svp->type) { case T_NUMBER: case T_FLOAT: break; case T_STRING: if (svp->x.string_type != STRING_SHARED) return &const0; /* FALLTHROUGH */ case T_SYMBOL: ref_string(svp->u.string); break; case T_POINTER: case T_QUOTED_ARRAY: svp->u.vec->ref++; break; case T_MAPPING: svp->u.map->ref++; break; case T_CLOSURE: addref_closure(svp, "ass to var"); break; default: return &const0; } return svp; } /* copy_svalue() */ /*-------------------------------------------------------------------------*/ static vector_t * list_to_vector (size_t length, svalue_t *initialized) /* <initialized>.u.lvalue points to a const_list of <length> elements: create * a vector from this list, store it in <initialized> and also return it. */ { const_list_t *list; vector_t *vec; svalue_t *svp; void *block; const_list_svalue_t *clsv; %line vec = allocate_array(length); if (length) { /* Unravel and copy the constants from the list into the vector */ clsv = initialized->u.const_list; list = &clsv->list; block = clsv; svp = vec->item; do { *svp++ = list->val; list = list->next; xfree(block); } while ( NULL != (block = list) ); } /* Return the array */ put_array(initialized, vec); return vec; } /* list_to_vector() */ /*-------------------------------------------------------------------------*/ static void free_const_list_svalue (svalue_t *svp) /* Function used as error-handler for const lists: <svp> is in fact * a const_list_svalue_t* and this function deallocates all memory * associated with the list. */ { const_list_t *list; void *block; %line list = &((const_list_svalue_t *)svp)->list; block = svp; do { free_svalue(&list->val); list = list->next; xfree(block); } while ( NULL != (block = list) ); } /* free_const_list_svalue() */ %endif /* !INITIALIZATION_BY___INIT */ #if defined(__MWERKS__) && !defined(WARN_ALL) # pragma warn_possunwant off # pragma warn_implicitconv off #endif %} /*=========================================================================*/ /* P A R S E R */ /*-------------------------------------------------------------------------*/ %token L_ASSIGN %token L_ARROW %token L_BREAK %token L_CASE %token L_CATCH %token L_CLOSURE %token L_CLOSURE_DECL %token L_COLON_COLON %token L_CONTINUE %token L_DEC %token L_DEFAULT %token L_DO %token L_ELSE %token L_EQ %token L_FLOAT %token L_FLOAT_DECL %token L_FOR %token L_FOREACH %token L_GE %token L_IDENTIFIER %token L_IF %token L_INC %token L_INHERIT %token L_INLINE_FUN %token L_INT %token L_LAND %token L_LE %token L_LOCAL %token L_LOR %token L_LSH %token L_MAPPING %token L_MIXED %token L_NE %token L_NO_MASK %token L_NOSAVE %token L_NOT %token L_NUMBER %token L_OBJECT %ifdef SUPPLY_PARSE_COMMAND %token L_PARSE_COMMAND %endif %token L_PRIVATE %token L_PROTECTED %token L_PUBLIC %token L_QUOTED_AGGREGATE %token L_RANGE %token L_RETURN %token L_RSH %token L_RSHL %token L_SSCANF %token L_STATIC %token L_STATUS %token L_STRING %token L_STRING_DECL %token L_SWITCH %token L_SYMBOL %token L_SYMBOL_DECL %token L_VARARGS %token L_VIRTUAL %token L_VOID %token L_WHILE /* Textbook solution to the 'dangling else' shift/reduce conflict. */ %nonassoc LOWER_THAN_ELSE %nonassoc L_ELSE /*-------------------------------------------------------------------------*/ /* The yacc stack type */ %union { %line p_int number; /* Literal numbers, or whereever a number is required. */ double float_number; /* Literal floats */ struct { p_int number; } closure; /* A closure (#'xxx). The .number determines the exact * nature of the closure. */ struct { char *name; /* The shared string with the name */ int quotes; /* Number of quotes */ } symbol; /* A literal symbol. */ ident_t *ident; /* L_IDENTIFIER, L_INLINE_FUN: The recognized identifier */ vartype_t type; /* The datatype. */ fulltype_t fulltype; /* The fulltype (datatype plus visibility) of entities. */ fulltype_t fulltypes[2]; /* Inheritance: [0]: code inheritance qualifiers * [1]: variable inheritance qualifiers */ svalue_t *initialized; /* Position where to store the variable initializer. */ p_int numbers[2]; /* Often used to save the current break/continue address. */ p_uint address; /* Address of an instruction. */ struct { bytecode_p p; /* The condition code */ unsigned short length; /* Length of the condition code */ unsigned short line; /* Last source line of the condition code */ } expression; /* Expressions are used to save the code for a loop-condition * while the body is compiled. */ struct s_lrvalue { vartype_t type; /* Type of the expression */ uint32 start; /* Startaddress of the instruction */ short code; /* Alternative instruction */ uint32 end; /* Endaddress+1 of the instruction */ } lrvalue; /* Used for expressions which may return a rvalue or lvalues. * It is also used by the index range generation to move around * the index expressions. * Lvalue generation in places where either a r- or an lvalue * is acceptible first generates the rvalue code, but stores * the necessary information to patch the code to produce * lvalues in this structure. * For more information, see arrange_protected_lvalue(). */ struct s_index { int inst; /* Type of the index */ uint32 start; /* Startaddress of the index */ uint32 end; /* Endaddress+1 of the index */ vartype_t type1; /* Type of index, resp. lower bound */ vartype_t type2; /* Type of other index, resp. upper bound */ } index; /* This is used to parse and return the indexing operation * of an array or mapping. * .inst gives the type of the operation: * F_INDEX: [x] * F_RINDEX: [<x] * F_RANGE: [ x.. y] * F_RN_RANGE: [<x.. y] * F_NR_RANGE: [ x..<y] * F_RR_RANGE: [<x..<y] * F_NX_RANGE: [ x.. ] * F_RX_RANGE: [<x.. ] * .start and .end are the bytecode limits of the whole * operation. * .type1 and optionally .type2 are the types of the * index values. */ struct lvalue_s { union { bytecode_p p; bytecode_t simple[2]; } u; unsigned short length; vartype_t type; } lvalue; /* Used in assigns to communicate how an lvalue has to be accessed * (by passing on the bytecode to create) and what type it is. * .length = 0: u.simple contains the bytecode to create * .length != 0: u.p points to the bytecode of .length bytes. */ struct { p_int key; /* shared string ptr, or a number */ Bool numeric; /* TRUE: .key is a number */ } case_label; /* Used to return the value of a 'case' label. */ char *string; /* An allocated string */ char *sh_string; /* A shared string */ struct { char *super; /* NULL, or the allocated qualifier */ ident_t *real; /* The function identifier */ } function_name; /* A qualified function name: "<super>::<func>" */ struct { int simul_efun; /* -1, or index of the simul_efun */ p_int start; /* Address of the function call */ } function_call_head; /* Used to save address and possible sefun-index over * the argument parsing in a function call. */ %ifndef INITIALIZATION_BY___INIT struct { p_int length; /* Length of the list */ struct const_list_s * l; /* The list of constants */ } const_list; /* Used to hold the constant elements of an array * initializer. */ struct { int function; /* efun index */ svalue_t *initialized; /* svalue to initialize */ } const_call_head; /* Used to hold the information of a constant efun call * over the parsing of the arguments. */ svalue_t svalue; /* Used for constant float initializers: the float value */ %endif } /* YYSTYPE */ /*-------------------------------------------------------------------------*/ %type <number> L_NUMBER constant %type <float_number> L_FLOAT %type <closure> L_CLOSURE %type <symbol> L_SYMBOL %type <number> L_QUOTED_AGGREGATE %type <ident> L_IDENTIFIER L_INLINE_FUN %type <fulltype> optional_star type type_modifier_list type_modifier %type <fulltype> opt_basic_type basic_type %type <fulltype> non_void_type opt_basic_non_void_type basic_non_void_type %type <fulltypes> inheritance_qualifier inheritance_qualifiers %type <fulltype> inheritance_modifier_list inheritance_modifier %type <type> decl_cast cast %type <lrvalue> note_start comma_expr expr0 expr4 %type <lrvalue> function_call inline_fun %type <lrvalue> catch sscanf %type <lrvalue> for_init_expr for_expr %type <lrvalue> comma_expr_decl expr_decl %ifdef SUPPLY_PARSE_COMMAND %type <lrvalue> parse_command %endif %type <lvalue> lvalue name_lvalue local_name_lvalue foreach_var_lvalue %type <lvalue> local_name_list new_local new_local_name %type <index> index_range index_expr %type <case_label> case_label %type <address> optional_else %type <string> anchestor %type <sh_string> call_other_name %type <function_name> function_name %ifndef INITIALIZATION_BY___INIT %type <svalue> float_constant %type <const_list> const_expr_list const_expr_list2 const_expr_list3 %endif /* Special uses of <number> */ %type <number> function_body /* program address or -1 */ %type <number> argument argument_list lvalue_list /* number of arguments */ %type <number> expr_list expr_list3 expr_list2 /* Number of expressions in an expression list */ %type <number> m_expr_values /* Number of values for a mapping entry (ie the 'width') */ %type <number> L_ASSIGN /* Instruction code of the assignment, e.g. F_ADD_EQ */ %type <number> L_LOCAL /* Index number of the local variable */ %type <number> foreach_vars /* Number of variables given to foreach */ %type <number> opt_catch_mods /* Bitflags for catch() modes: 1: nolog */ /* Special uses of <numbers> */ %type <numbers> condStart /* [0]: current_break_address * [1]: address of the branch-offset of the if */ %type <numbers> m_expr_list m_expr_list2 /* [0]: number of entries in a mapping literal * [1]: width of the mapping literal */ /* Special uses of <lrvalue> */ %type <lrvalue> pre_inc_dec /* .code: The instruction F_PRE_INC or F_PRE_DEC. * .start: The CURRENT_PROGRAM_SIZE where this inst was encountered. */ /*-------------------------------------------------------------------------*/ %right L_ASSIGN %right '?' %left L_LOR %left L_LAND %left '|' %left '^' %left '&' %left L_EQ L_NE %left '<' L_LE '>' L_GE %left L_LSH L_RSH L_RSHL %left '+' '-' %left '*' '/' '%' %right '~' L_NOT %nonassoc L_INC L_DEC %left L_ARROW '[' %% /*-------------------------------------------------------------------------*/ all: program ; program: program def possible_semi_colon | /* empty */ ; possible_semi_colon: /* empty */ | ';' { yywarn("Extra ';'. Ignored."); } ; note_start: { $$.start = CURRENT_PROGRAM_SIZE; } ; /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* Function prototypes * Function definitions * Variable definitions * Inheritance * Default visibility */ def: type optional_star L_IDENTIFIER /* Function definition or prototype */ { use_local_scopes = pragma_use_local_scopes; block_depth = 1; init_scope(block_depth); if (!($1 & (TYPE_MOD_PRIVATE | TYPE_MOD_PUBLIC | TYPE_MOD_PROTECTED | TYPE_MOD_STATIC))) { $1 |= default_funmod; } $2 |= $1; /* $2 is now the complete type */ /* Require exact types? */ if ($1 & TYPE_MOD_MASK) { exact_types = $2; } else { if (pragma_strict_types != PRAGMA_WEAK_TYPES) yyerror("\"#pragma strict_types\" requires type of function"); exact_types = 0; } if ($1 & TYPE_MOD_NOSAVE) { yyerror("can't declare a function as nosave"); $1 &= ~TYPE_MOD_NOSAVE; } if ($3->type == I_TYPE_UNKNOWN) { /* prevent freeing by exotic name clashes */ ident_t *p = $3; p->type = I_TYPE_GLOBAL; p->u.global.variable = I_GLOBAL_VARIABLE_OTHER; p->u.global.efun = I_GLOBAL_EFUN_OTHER; p->u.global.sim_efun = I_GLOBAL_SEFUN_OTHER; p->u.global.function = I_GLOBAL_FUNCTION_VAR; p->next_all = all_globals; all_globals = p; } } '(' argument ')' { /* We got the complete prototype: define it */ if ( current_number_of_locals && (full_type_of_locals[current_number_of_locals-1] & TYPE_MOD_VARARGS) ) { %line /* The last argument has to allow an array. */ vartype_t *t; $2 |= TYPE_MOD_XVARARGS; t = type_of_locals + (current_number_of_locals-1); if (!(*t & TYPE_MOD_POINTER) && (*t & TYPE_MOD_RMASK) != TYPE_ANY ) { if ((*t & TYPE_MOD_RMASK) != TYPE_UNKNOWN) yyerror( "varargs parameter must be declared array or mixed"); /* Keep the visibility, but change the type to * '&any' */ *t &= ~TYPE_MOD_RMASK; *t |= TYPE_ANY; } } /* Define a prototype. If it is a real function, then the * prototype will be updated below. */ define_new_function(MY_FALSE, $3, $6, 0, 0, NAME_UNDEFINED|NAME_PROTOTYPE, $2); } function_body { /* The function is complete */ p_int start; bytecode_p p; %line if ( (start = $9) < 0) { /* function_body was a ';' -> prototype * Just norm the visibility flags unless it is a prototype * for an already inherited function. */ funflag_t *flagp; flagp = (funflag_t *)(&FUNCTION($3->u.global.function)->flags); if (!(*flagp & NAME_INHERITED)) { *flagp |= $1 & (*flagp & TYPE_MOD_PUBLIC ? (TYPE_MOD_NO_MASK) : (TYPE_MOD_NO_MASK|TYPE_MOD_PRIVATE |TYPE_MOD_STATIC|TYPE_MOD_PROTECTED |TYPE_MOD_PUBLIC) ); } } else { /* function_body was a block: generate the * function header and update the ident-table entry. */ p = &(PROGRAM_BLOCK[start]); /* FUNCTION_NAME */ memcpy(p, &$3->name, sizeof $3->name); p += sizeof $3->name; /* FUNCTION_TYPE */ *p++ = $2; /* FUNCTION_NUM_ARGS */ if ($2 & TYPE_MOD_XVARARGS) *p++ = $6 | ~0x7f; else *p++ = $6; /* FUNCTION_NUM_VARS */ *p = max_number_of_locals - $6+ max_break_stack_need; define_new_function(MY_TRUE, $3, $6, max_number_of_locals - $6+ max_break_stack_need, start + sizeof $3->name + 1, 0, $2); ref_string($3->name); ins_byte(F_RETURN0); /* catch a missing return */ } /* Clean up */ free_all_local_names(); if (first_inline_fun) insert_inline_fun_now = MY_TRUE; block_depth = 0; } | type name_list ';' /* Variable definition */ { if ($1 == 0) yyerror("Missing type"); if (first_inline_fun) insert_inline_fun_now = MY_TRUE; } | inheritance | default_visibility ; /* def */ function_body: /* A function with code: align the function and * make space for the function header. * Result is the address of the FUNCTION_NAME space. */ { %line #ifdef ALIGN_FUNCTIONS CURRENT_PROGRAM_SIZE = align(CURRENT_PROGRAM_SIZE); #endif $<number>$ = CURRENT_PROGRAM_SIZE; if (realloc_a_program(FUNCTION_HDR_SIZE)) { CURRENT_PROGRAM_SIZE += FUNCTION_HDR_SIZE; } else { yyerrorf("Out of memory: program size %lu\n" , mem_block[A_PROGRAM].current_size + FUNCTION_HDR_SIZE); YYACCEPT; } } block %ifdef YACC_CANNOT_MIX_ANONYMOUS_WITH_DEFAULT { $$ = $<number>1; } %endif | ';' { $$ = -1; } ; /* function_body */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* Inheritance specification */ inheritance: inheritance_qualifiers L_INHERIT string_constant ';' { %line /* We got an inheritance: look up the name object and copy * the functions and variables into this program. * * If the inherited object hasn't been loaded yet, store the * name in inherit_file and abort the compile. * * copy_variables() might add extra inherits for virtual inheritance. * For this reason, copy_functions() can't know the actual index * of the new inherit, so it sets it to NEW_INHERITED_INDEX instead. * This is changed later to the actual value by * fix_function_inherit_indices() . */ object_t *ob; inherit_t inherit; %ifdef INITIALIZATION_BY___INIT int initializer; %endif /* INITIALIZATION_BY___INIT */ if (CURRENT_PROGRAM_SIZE %ifdef INITIALIZATION_BY___INIT && !(((function_t *)(mem_block[A_FUNCTIONS].block+ mem_block[A_FUNCTIONS].current_size))[-1].flags & NAME_INHERITED) %endif /* INITIALIZATION_BY___INIT */ ) { yyerror("illegal to inherit after defining functions"); } /* Check the inheritance qualifiers. * A variable 'nosave' inherit is internally stored as 'static', * a functions 'nosave' inherit is not allowed. */ if ($1[1] & TYPE_MOD_NOSAVE) { $1[1] |= TYPE_MOD_STATIC; $1[1] ^= TYPE_MOD_NOSAVE; } if ($1[0] & TYPE_MOD_NOSAVE) { $1[0] ^= TYPE_MOD_NOSAVE; yyerror("illegal to inherit code as 'nosave'"); } /* First, try to call master->inherit_file(). * Since simulate::load_object() makes sure that the master has been * loaded, this test can only fail when the master is compiled. */ if (master_ob && !(master_ob->flags & O_DESTRUCTED) && (!max_eval_cost || eval_cost < max_eval_cost) ) { svalue_t *res; push_string_shared(last_string_constant); if (!compat_mode) { char * filename; filename = alloca(strlen(current_file)+2); *filename = '/'; strcpy(filename+1, current_file); push_volatile_string(filename); } else push_volatile_string(current_file); res = apply_master(STR_INHERIT_FILE, 2); if (res && !(res->type == T_NUMBER && !res->u.number)) { /* We got a result - either a new name or a "reject it" * value. */ char * cp; if (res->type != T_STRING) { yyerrorf("Illegal to inherit file '%s'.", last_string_constant); YYACCEPT; } for (cp = res->u.string; *cp == '/'; cp++) NOOP; if (!legal_path(cp)) { yyerrorf("Illegal path '%s'.", res->u.string); YYACCEPT; } /* Ok, now replace the parsed string with the name * we just got. */ free_string(last_string_constant); last_string_constant = make_shared_string(cp); } /* else: no result - use the string as it is */ } else if (max_eval_cost && eval_cost >= max_eval_cost) { yyerrorf("Can't call master::%s for " "'%s': eval cost too big" , STR_INHERIT_FILE, last_string_constant); /* use the string as it is */ } /* Look up the inherited object and swap it in. */ ob = find_object(last_string_constant); if (ob == 0) { inherit_file = last_string_constant; last_string_constant = NULL; /* Return back to load_object() */ YYACCEPT; } ob->time_of_ref = current_time; if (ob->flags & O_SWAPPED && load_ob_from_swap(ob) < 0) { free_string(last_string_constant); last_string_constant = NULL; yyerrorf("Out of memory when unswapping '%s'", ob->name); YYACCEPT; } /* Legal to inherit? */ if (ob->prog->flags & P_NO_INHERIT) { yyerror("Illegal to inherit an object which sets " "'#pragma no_inherit'"); YYACCEPT; } free_string(last_string_constant); last_string_constant = NULL; /* Set up the inherit structure */ inherit.prog = ob->prog; if ($1[1] & TYPE_MOD_VIRTUAL) inherit.inherit_type = INHERIT_TYPE_VIRTUAL; else inherit.inherit_type = INHERIT_TYPE_NORMAL; inherit.function_index_offset = FUNCTION_COUNT; inherit.inherit_depth = 1; /* If it's a virtual inherit, check if it has been * inherited virtually before. If yes, don't bother to insert it * again. * For all types of inherits, check if the same program has already * been inherited at the toplevel. */ { inherit_t *inheritp; int j; Bool duplicate_toplevel = MY_FALSE; inheritp = (inherit_t *)(mem_block[A_INHERITS].block); j = mem_block[A_INHERITS].current_size; for (; (j -= sizeof(inherit_t)) >= 0; inheritp++) { if (inheritp->prog == ob->prog) { /* Check for duplicate toplevel inherit. * Since the check for duplicate virtual inherits * may change the inherit_depth, this test must * come first */ if (inheritp->inherit_depth == 1) duplicate_toplevel = MY_TRUE; /* Check for duplicate virtual inherit */ if (($1[1] & TYPE_MOD_VIRTUAL) && !(inheritp->variable_index_offset & NON_VIRTUAL_OFFSET_TAG) && !(inherit.inherit_type & INHERIT_TYPE_DUPLICATE) ) { inherit.inherit_type |= INHERIT_TYPE_DUPLICATE; inheritp->inherit_depth = 1; } } } if (duplicate_toplevel) { if (pragma_pedantic) { yyerrorf("Program '%s' already inherited" , inherit.prog->name); YYACCEPT; } else yywarnf("Program '%s' already inherited" , inherit.prog->name); } } if (!(inherit.inherit_type & INHERIT_TYPE_DUPLICATE)) { /* Copy the functions and variables, and take * care of the initializer. */ %ifdef INITIALIZATION_BY___INIT initializer = copy_functions(ob->prog, $1[0]); copy_variables(ob->prog, $1[1]); if (initializer > -1) { /* We inherited a __INIT() function: create a call */ transfer_init_control(); ins_byte(F_CALL_EXPLICIT_INHERITED); ins_short(INHERIT_COUNT); ins_short(initializer); ins_byte(0); /* Actual number of arguments */ ins_byte(F_POP_VALUE); add_new_init_jump(); } %else /* INITIALIZATION_BY___INIT */ copy_functions(ob->prog, $1[0]); copy_variables(ob->prog, $1[1], ob->variables); %endif /* INITIALIZATION_BY___INIT */ /* Fix up the inherit indices */ fix_function_inherit_indices(ob->prog); /* Update and store the inherit structure. * * If the program was inherited non-virtual, the v_i_offset * may become negative here if the program itself inherits * other programs with variables virtually. That is ok * because in the final program the sub-inherited virtual * variables no longer are immediately before the programs * non-virtual variables, but the program's code doesn't know * that and continues to 'offset over' them. */ inherit.variable_index_offset = $1[1] & TYPE_MOD_VIRTUAL ? V_VARIABLE_COUNT - ob->prog->num_variables : (NV_VARIABLE_COUNT - ob->prog->num_variables) | NON_VIRTUAL_OFFSET_TAG; add_to_mem_block(A_INHERITS, &inherit, sizeof inherit); num_virtual_variables = V_VARIABLE_COUNT; } /* if (!(inherit.inherit_type & INHERIT_TYPE_DUPLICATE)) */ } ; /* inheritance */ inheritance_qualifiers: /* Inheritance can be qualified simple ("public inherit...") * or separate for code and variables. */ inheritance_modifier_list { $$[0] = $$[1] = $1; /* Allow 'static nosave inherit foo' as the short form * of 'static functions nosave variables inherit foo'; meaning * that we have to prevent the qualifier test in the * inheritance rule from triggering. */ if ($1 & TYPE_MOD_NOSAVE) { $$[0] ^= TYPE_MOD_NOSAVE; } } | inheritance_qualifier inheritance_qualifiers { $$[0] = $1[0] | $2[0]; $$[1] = $1[1] | $2[1]; } ; /* inheritance_qualifiers */ inheritance_modifier: L_VIRTUAL { $$ = TYPE_MOD_VIRTUAL; } ; inheritance_modifier_list: type_modifier_list | inheritance_modifier_list inheritance_modifier type_modifier_list { $$ = $1 | $2 | $3; } ; /* inheritance_modifier_list */ inheritance_qualifier: type optional_star L_IDENTIFIER { static ident_t *last_identifier; static fulltype_t last_modifier; %line /* The inherit statement must only specify visibility * e.g. not "inherit int * foobar" */ if ($1 & TYPE_MOD_MASK) { yyerror("syntax error"); } /* Check if there were any modifiers at all */ if ( !($1 & ~TYPE_MOD_MASK) ) { /* take lookahead into account */ if ($3 == last_identifier) { last_identifier = NULL; $$[0] = $$[1] = 0; break; /* TODO: Assumes that byacc uses a switch() */ } } else { last_modifier = $1 & ~TYPE_MOD_MASK; } last_identifier = $3; if ($2) /* No "*" allowed TODO: So why it's there? */ { yyerror("syntax error"); } /* The L_IDENTIFIER must be one of "functions" or "variables" */ if (strcmp(last_identifier->name, "functions") == 0) { $$[0] = last_modifier; $$[1] = 0; } else if (strcmp(last_identifier->name, "variables") == 0) { $$[0] = 0; $$[1] = last_modifier; } else { yyerrorf("Unrecognized inheritance modifier '%s'" , last_identifier->name); $$[0] = $$[1] = 0; } /* Free the identifier again if this statement generated it */ if (last_identifier->type == I_TYPE_UNKNOWN) free_shared_identifier(last_identifier); } ; /* inheritance_qualifier */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* Default visibility. * * We use the inheritance modifier notation to specify the default * visibility of functions and variables. */ default_visibility: L_DEFAULT inheritance_qualifiers ';' { if ($2[0] & ~( TYPE_MOD_PRIVATE | TYPE_MOD_PUBLIC | TYPE_MOD_PROTECTED | TYPE_MOD_STATIC) ) { yyerror("Default visibility specification for functions " "accepts only 'private', 'protected', 'public' or " "'static'"); YYACCEPT; } if ($2[1] & ~( TYPE_MOD_PRIVATE | TYPE_MOD_PUBLIC | TYPE_MOD_PROTECTED) ) { yyerror("Default visibility specification for variables " "accepts only 'private', 'protected' or 'public'" ); YYACCEPT; } default_funmod = $2[0]; default_varmod = $2[1]; } ; /* default_visibility */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* Type specifications and casts * * The type rules are used to parse variable and function types, casts, * or just visibility e.g for inheritance. */ optional_star: /* empty */ { $$ = 0; } | '*' { $$ = TYPE_MOD_POINTER; } ; type: type_modifier_list opt_basic_type { $$ = $1 | $2; current_type = $$; } ; non_void_type: type_modifier_list opt_basic_non_void_type { $$ = $1 | $2; current_type = $$; } ; type_modifier_list: /* empty */ { $$ = 0; } | type_modifier_list type_modifier { $$ = $1 | $2; } ; type_modifier: L_NO_MASK { $$ = TYPE_MOD_NO_MASK; } | L_STATIC { $$ = TYPE_MOD_STATIC; } | L_PRIVATE { $$ = TYPE_MOD_PRIVATE; } | L_PUBLIC { $$ = TYPE_MOD_PUBLIC; } | L_VARARGS { $$ = TYPE_MOD_VARARGS; } | L_PROTECTED { $$ = TYPE_MOD_PROTECTED; } | L_NOSAVE { $$ = TYPE_MOD_NOSAVE; } ; opt_basic_type: basic_type | /* empty */ { $$ = TYPE_UNKNOWN; } ; opt_basic_non_void_type: basic_non_void_type | /* empty */ { $$ = TYPE_UNKNOWN; } ; basic_non_void_type: L_STATUS { $$ = TYPE_NUMBER; current_type = $$; } | L_INT { $$ = TYPE_NUMBER; current_type = $$; } | L_STRING_DECL { $$ = TYPE_STRING; current_type = $$; } | L_OBJECT { $$ = TYPE_OBJECT; current_type = $$; } | L_CLOSURE_DECL { $$ = TYPE_CLOSURE; current_type = $$; } | L_SYMBOL_DECL { $$ = TYPE_SYMBOL; current_type = $$; } | L_FLOAT_DECL { $$ = TYPE_FLOAT; current_type = $$; } | L_MAPPING { $$ = TYPE_MAPPING; current_type = $$; } | L_MIXED { $$ = TYPE_ANY; current_type = $$; } ; /* basic_type */ basic_type: basic_non_void_type | L_VOID { $$ = TYPE_VOID; current_type = $$; } ; /* basic_non_void_type */ cast: '(' basic_type optional_star ')' { $$ = $2 | $3; } ; decl_cast: '(' '{' basic_type optional_star '}' ')' { $$ = $3 | $4; } ; /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* Argument and variable definitions */ argument: /* empty */ { $$ = 0; } | L_VOID { $$ = 0; } | argument_list ; argument_list: new_arg_name { $$ = 1; } | argument_list ',' new_arg_name { $$ = $1 + 1; } ; new_arg_name: non_void_type optional_star L_IDENTIFIER { if (exact_types && $1 == 0) { yyerror("Missing type for argument"); add_local_name($3, TYPE_ANY, block_depth); /* Supress more errors */ } else { add_local_name($3, $1 | $2, block_depth); } } | non_void_type optional_star L_LOCAL { /* A local name is redeclared. Since this is the argument * list, it can't be legal. */ yyerror("Illegal to redeclare local name"); } ; /* new_arg_name */ name_list: new_name | name_list ',' new_name; new_name: /* Simple variable definition */ optional_star L_IDENTIFIER { %line fulltype_t actual_type = current_type; if (!(actual_type & (TYPE_MOD_PRIVATE | TYPE_MOD_PUBLIC | TYPE_MOD_PROTECTED))) { actual_type |= default_varmod; } if (actual_type & TYPE_MOD_VARARGS) { yyerror("can't declare a variable as varargs"); actual_type &= ~TYPE_MOD_VARARGS; } %ifdef INITIALIZATION_BY___INIT define_variable($2, actual_type | $1); %else /* then !INITIALIZATION_BY___INIT */ define_variable($2, actual_type | $1, &const0); %endif } /* Variable definition with initialization */ %ifdef INITIALIZATION_BY___INIT | optional_star L_IDENTIFIER { fulltype_t actual_type = current_type; if (!(actual_type & (TYPE_MOD_PRIVATE | TYPE_MOD_PUBLIC | TYPE_MOD_PROTECTED))) { actual_type |= default_varmod; } define_variable($2, actual_type | $1); $<number>$ = verify_declared($2); /* Is the var declared? */ transfer_init_control(); /* Prepare INIT code */ } L_ASSIGN expr0 { int i = $<number>3; PREPARE_INSERT(4) fulltype_t actual_type = current_type; if (!(actual_type & (TYPE_MOD_PRIVATE | TYPE_MOD_PUBLIC | TYPE_MOD_PROTECTED))) { actual_type |= default_varmod; } #ifdef DEBUG if (i & VIRTUAL_VAR_TAG) { /* When we want to allow 'late' initializers for * inherited variables, it must have a distinct syntax, * lest name clashs remain undetected, making LPC code * hard to debug. */ fatal("Newly declared variable is virtual\n"); } #endif variables_initialized = MY_TRUE; /* We have __INIT code */ /* Push the variable reference and create the assignment */ if (i + num_virtual_variables > 0xff) { add_byte(F_PUSH_IDENTIFIER16_LVALUE); add_short(i + num_virtual_variables); CURRENT_PROGRAM_SIZE += 1; } else { add_byte(F_PUSH_IDENTIFIER_LVALUE); add_byte(i + num_virtual_variables); } /* Only simple assigns are allowed */ if ($4 != F_ASSIGN) yyerror("Illegal initialization"); /* Do the types match? */ if (!compatible_types((actual_type | $1) & TYPE_MOD_MASK, $5.type)) { yyerrorf("Type mismatch %s when initializing %s", get_two_types(actual_type | $1, $5.type), $2->name); } /* Ok, assign */ add_byte(F_VOID_ASSIGN); CURRENT_PROGRAM_SIZE += 3; add_new_init_jump(); } %else /* then !INITIALIZATION_BY___INIT */ | optional_star L_IDENTIFIER { /* svalue_constant can contain identifiers, so define the variable * now, lest the identifier could get freed by a name clash. */ %line int n; fulltype_t actual_type = current_type; if (!(actual_type & (TYPE_MOD_PRIVATE | TYPE_MOD_PUBLIC | TYPE_MOD_PROTECTED))) { actual_type |= default_varmod; } define_variable($2, actual_type | $1 | NAME_INITIALIZED, &const0); n = $2->u.global.variable; $<initialized>$ = currently_initialized = n & VIRTUAL_VAR_TAG ? V_VAR_VALUE(n) : NV_VAR_VALUE(n); } L_ASSIGN svalue_constant { %line /* The parsing of the svalue_constant assigned the value * to the currently_initialized buffer set above, so * we just have to check the validity. */ fulltype_t actual_type = current_type; if (!(actual_type & (TYPE_MOD_PRIVATE | TYPE_MOD_PUBLIC | TYPE_MOD_PROTECTED))) { actual_type |= default_varmod; } if ($4 != F_ASSIGN) yyerror("Illegal initialization"); if (exact_types) if (!TYPE( actual_type | $1 , type_rtoc($<initialized>3)) ) { yyerror("Bad initializer type"); } } %endif /* INITIALIZATION_BY___INIT */ ; /* new_name */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* Blocks and simple statements. */ block: '{' { enter_block_scope(); } statements { /* If this is a local block, the declarations inserted * a code fragment to zero out the locals (previous blocks * may have left values in them). Complete the fragment * with the number of locals to clear, now that we * know it. */ { block_scope_t *scope = block_scope + block_depth - 1; if (use_local_scopes && scope->num_locals) { mem_block[A_PROGRAM].block[scope->addr+2] = (char)scope->num_locals; } } } '}' { leave_block_scope(); } ; /* block */ statements: /* empty */ | statements basic_type local_name_list ';' | statements statement ; local_name_list: new_local | local_name_list ',' new_local ; new_local : new_local_name | new_local_name L_ASSIGN expr0 { /* We got a "<name> = <expr>" type declaration. */ p_int length; vartype_t type2; %line /* Check the assignment for validity */ type2 = $3.type; if (exact_types && !compatible_types($1.type, type2)) { yyerrorf("Bad assignment %s", get_two_types($1.type, $3.type)); } if ($2 != F_ASSIGN) { yyerror("Only plain assignments allowed here."); } if (type2 & TYPE_MOD_REFERENCE) yyerror("Can't trace reference assignments."); /* Add the bytecode to create the lvalue and do the * assignment. */ length = $1.length; if (length) { add_to_mem_block(A_PROGRAM, $1.u.p, length+1); yfree($1.u.p); last_expression = CURRENT_PROGRAM_SIZE-1; mem_block[A_PROGRAM].block[last_expression] = F_VOID_ASSIGN; } else { bytecode_p source, dest; mp_uint current_size; source = $1.u.simple; current_size = CURRENT_PROGRAM_SIZE; if (!realloc_a_program(3)) { yyerrorf("Out of memory: program size %lu", current_size+3); YYACCEPT; } CURRENT_PROGRAM_SIZE = (last_expression = current_size + 2) + 1; dest = PROGRAM_BLOCK + current_size; *dest++ = *source++; *dest++ = *source; *dest = F_VOID_ASSIGN; } } ; /* new_local */ new_local_name: optional_star L_IDENTIFIER { /* A new local variable */ block_scope_t *scope = block_scope + block_depth - 1; ident_t *q; q = add_local_name($2, current_type | $1, block_depth); if (use_local_scopes && scope->num_locals == 1) { /* First definition of a local, so insert the * clear_locals bytecode and remember its position */ scope->addr = mem_block[A_PROGRAM].current_size; ins_f_code(F_CLEAR_LOCALS); ins_byte(scope->first_local); ins_byte(0); } $$.u.simple[0] = F_PUSH_LOCAL_VARIABLE_LVALUE; $$.u.simple[1] = q->u.local.num; $$.length = 0; $$.type = current_type | $1; } | optional_star L_LOCAL { /* A local name is redeclared. If this happens on a deeper * level, it is even legal. */ ident_t *q; block_scope_t *scope = block_scope + block_depth - 1; q = redeclare_local($2, current_type | $1, block_depth); if (use_local_scopes && scope->num_locals == 1) { /* First definition of a local, so insert the * clear_locals bytecode and remember its position */ scope->addr = mem_block[A_PROGRAM].current_size; ins_f_code(F_CLEAR_LOCALS); ins_byte(scope->first_local); ins_byte(0); } $$.u.simple[0] = F_PUSH_LOCAL_VARIABLE_LVALUE; $$.u.simple[1] = q->u.local.num; $$.length = 0; $$.type = current_type | $1; } ; /* new_local_name */ statement: comma_expr ';' { insert_pop_value(); #ifdef F_BREAK_POINT if (d_flag) ins_byte(F_BREAK_POINT); #endif /* F_BREAK_POINT */ /* if (exact_types && !BASIC_TYPE($1.type, TYPE_VOID)) * yyerror("Value thrown away"); */ } | error ';' /* Synchronisation point */ | cond | while | do | for | foreach | switch | case | default | return ';' | block | /* empty */ ';' | L_BREAK ';' { /* Compile the break statement */ if (current_break_address == 0) yyerror("break statement outside loop"); if (current_break_address & BREAK_ON_STACK) { /* We break from a switch() */ ins_byte(F_BREAK); } else { /* A normal loop break: add the LBRANCH to the list */ ins_byte(F_LBRANCH); ins_short(current_break_address); current_break_address = CURRENT_PROGRAM_SIZE - 2; } } | L_CONTINUE ';' /* This code is a jump */ { p_int depth; %line if (current_continue_address == 0) yyerror("continue statement outside loop"); if ( 0 != (depth = (current_continue_address & SWITCH_DEPTH_MASK)) ) { /* A continue inside a switch */ /* For more than 255 nested switches, generate a series * of BREAKN_CONTINUE instructions. */ while (depth > SWITCH_DEPTH_UNIT*256) { ins_f_code(F_BREAKN_CONTINUE); ins_byte(255); ins_short(2); depth -= SWITCH_DEPTH_UNIT*256; } /* BREAK_CONTINUE the last switches */ if (depth > SWITCH_DEPTH_UNIT) { depth /= SWITCH_DEPTH_UNIT; ins_f_code(F_BREAKN_CONTINUE); ins_byte(depth-1); } else { ins_f_code(F_BREAK_CONTINUE); } } else { /* Normal continue */ ins_byte(F_LBRANCH); } /* In either case, handle the list of continues alike */ ins_short(current_continue_address); current_continue_address = ( current_continue_address & SWITCH_DEPTH_MASK ) | ( CURRENT_PROGRAM_SIZE - 2 ); } ; /* statement */ return: L_RETURN { if (exact_types && !BASIC_TYPE(exact_types & TYPE_MOD_MASK, TYPE_VOID)) type_error("Must return a value for a function declared", exact_types); ins_byte(F_RETURN0); } | L_RETURN comma_expr { %line if (exact_types) { fulltype_t rtype = exact_types & TYPE_MOD_MASK; /* More checks, ie. mixed vs non-mixed, would be nice, * but the general type tracking is too lacking for it. */ if (!MASKED_TYPE($2.type, rtype)) { char tmp[100]; strcpy(tmp, get_type_name($2.type)); yyerrorf("Return type not matching: got %s, expected %s" , tmp, get_type_name(rtype)); } } if ($2.type & TYPE_MOD_REFERENCE) { yyerror("May not return a reference"); } if (last_expression == CURRENT_PROGRAM_SIZE - 1 && mem_block[A_PROGRAM].block[last_expression] == F_CONST0 ) { /* Optimize "CONST0 RETURN" to "RETURN0" */ mem_block[A_PROGRAM].block[last_expression] = F_RETURN0; last_expression = -1; } else ins_byte(F_RETURN); } ; /* return */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* The while() statement * * It is compiled into: * * BRANCH c * l: <body> * c: <cond> * BBRANCH_WHEN_NON_ZERO l */ while: { /* Save the previous environment */ $<numbers>$[0] = current_continue_address; $<numbers>$[1] = current_break_address; push_address(); /* Remember the starting address */ } L_WHILE '(' comma_expr ')' { %line p_int addr = pop_address(); p_int length = CURRENT_PROGRAM_SIZE - addr; bytecode_p expression; /* Take the <cond> code, add the BBRANCH instruction and * store all of it outside the program. After the <body> * has been compiled, the code will be put back in. */ expression = yalloc(length+2); memcpy(expression, mem_block[A_PROGRAM].block+addr, length); if (last_expression == CURRENT_PROGRAM_SIZE - 1 && expression[length-1] == F_NOT ) { /* Optimizize * NOT * BBRANCH_WHEN_NON_ZERO * into * BBRANCH_WHEN_ZERO */ length--; expression[length] = F_BBRANCH_WHEN_ZERO; } else { expression[length] = F_BBRANCH_WHEN_NON_ZERO; } /* Save the code as 'expression' */ $<expression>$.p = expression; $<expression>$.length = length; $<expression>$.line = current_line; /* Restart codegeneration for the body where we began */ CURRENT_PROGRAM_SIZE = addr; last_expression = -1; /* The initial branch to the condition code */ ins_byte(F_BRANCH); push_address(); ins_byte(0); current_continue_address = CONTINUE_DELIMITER; current_break_address = BREAK_DELIMITER; } statement { %line /* The body compiled ok. Now patch up the breaks and continues * and insert the condition checking. */ p_int offset; p_int next_addr; p_int addr = pop_address(); /* Update the offsets of all continue BRANCHes * (resp BREAK_CONTINUEs) to branch to the current address. */ for ( ; current_continue_address > 0 ; current_continue_address = next_addr) { next_addr = read_short(current_continue_address); upd_short(current_continue_address, CURRENT_PROGRAM_SIZE - current_continue_address); } /* If necessary, update the leading BRANCH to an LBRANCH */ offset = fix_branch( F_LBRANCH, CURRENT_PROGRAM_SIZE, addr); /* Add the condition code to the program */ if ($<expression>6.line != current_line) store_line_number_info(); add_to_mem_block(A_PROGRAM, $<expression>6.p, $<expression>6.length+2); yfree($<expression>6.p); /* Complete the branch at the end of the condition code */ offset += addr + 1 - ( CURRENT_PROGRAM_SIZE - 1 ); if (offset < -0xff) { /* We need a LBRANCH instead of the BBRANCH */ bytecode_p codep; if (offset < -0x8000) yyerror("offset overflow"); codep = PROGRAM_BLOCK + --CURRENT_PROGRAM_SIZE - 1; *codep = *codep == F_BBRANCH_WHEN_NON_ZERO ? F_LBRANCH_WHEN_NON_ZERO : F_LBRANCH_WHEN_ZERO ; ins_short(offset); } else { /* Just add the short offset */ mem_block[A_PROGRAM].block[CURRENT_PROGRAM_SIZE-1] = -offset; } if ($<expression>6.line != current_line) store_line_number_relocation($<expression>6.line); /* Now that we have the end of the while(), we can finish * up the breaks. */ for( ; current_break_address > 0 ; current_break_address = next_addr) { next_addr = read_short(current_break_address); upd_short(current_break_address, CURRENT_PROGRAM_SIZE - current_break_address); } /* Restore the previous environment */ current_continue_address = $<numbers>1[0]; current_break_address = $<numbers>1[1]; } ; /* while */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* The do-while() statement * * It is compiled into: * * l: <body> * <cond> * BBRANCH_WHEN_NON_ZERO l */ do: { /* Save the previous environment */ $<numbers>$[0] = current_continue_address; $<numbers>$[1] = current_break_address; current_break_address = BREAK_DELIMITER; current_continue_address = CONTINUE_DELIMITER; push_address(); /* Address to branch back to */ } L_DO statement L_WHILE { /* The body is complete - we can already patch up * the continue statements. */ p_int next_addr; p_int current; %line current = CURRENT_PROGRAM_SIZE; for(; current_continue_address > 0 ; current_continue_address = next_addr) { next_addr = read_short(current_continue_address); upd_short(current_continue_address, current - current_continue_address); } } '(' comma_expr ')' ';' { %line /* The loop is complete - we just need the final branch * instruction and to patch up the breaks. */ p_int offset; p_int next_addr; p_int addr = pop_address(); mp_uint current; bytecode_p dest; current = CURRENT_PROGRAM_SIZE; if (!realloc_a_program(3)) { yyerrorf("Out of memory: program size %lu\n", current+3); YYACCEPT; } /* Add the branch statement */ dest = PROGRAM_BLOCK + current; if (current == last_expression + 1 && dest[-1] == F_NOT) { /* Optimize 'NOT BBRANCH_WHEN_NON_ZERO' to 'BBRANCH_WHEN_ZERO' */ offset = addr - current; if (offset < -0xff) { if (offset < -0x8000) yyerror("offset overflow"); PUT_CODE(dest-1, F_LBRANCH_WHEN_ZERO); PUT_SHORT(dest, offset); current += 2; } else { PUT_CODE(dest-1, F_BBRANCH_WHEN_ZERO); PUT_UINT8(dest, -offset); current++; } } else { offset = addr - ( current + 1 ); if (offset < -0xff) { if (offset < -0x8000) yyerror("offset overflow"); STORE_CODE(dest, F_LBRANCH_WHEN_NON_ZERO); STORE_SHORT(dest, offset); current += 3; } else { STORE_CODE(dest, F_BBRANCH_WHEN_NON_ZERO); STORE_UINT8(dest, -offset); current += 2; } } CURRENT_PROGRAM_SIZE = current; /* Now that we have the end of the do-while(), we can finish * up the breaks. */ for (; current_break_address > 0 ; current_break_address = next_addr) { next_addr = read_short(current_break_address); upd_short(current_break_address, current - current_break_address); } /* Restore the previous environment */ current_continue_address = $<numbers>1[0]; current_break_address = $<numbers>1[1]; } ; /* do */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* The for() statement. * * It is compiled as: * * CLEAR_LOCALS * <init> * POP * BRANCH c * l: <body> * <incr> * POP * c: <cond> * BBRANCH_WHEN_NON_ZERO l */ for: L_FOR '(' { /* Save the previous environment */ $<numbers>$[0] = current_continue_address; $<numbers>$[1] = current_break_address; /* Open a new scope to all variables local to the * for-statement as a whole. */ enter_block_scope(); } for_init_expr ';' { /* Get rid of whatever init_expr computed */ insert_pop_value(); /* From here, the <body> will be placed eventually */ current_continue_address = CONTINUE_DELIMITER; $<number>$ = CURRENT_PROGRAM_SIZE; } for_expr ';' { %line /* Add the BBRANCH to the condition and save it all * in an 'expression' on the compiler stack for later * re-insertion. */ p_int start, length; bytecode_p expression; start = $<number>6; length = CURRENT_PROGRAM_SIZE - start; expression = yalloc(length+2); memcpy(expression, mem_block[A_PROGRAM].block + start, length ); /* Add the branch instruction */ if (last_expression == CURRENT_PROGRAM_SIZE - 1 && expression[length-1] == F_NOT ) { /* Optimize 'NOT BBRANCH_WHEN_NON_ZERO' * to 'BBRANCH_WHEN_ZERO' */ length--; expression[length] = F_BBRANCH_WHEN_ZERO; } else { expression[length] = F_BBRANCH_WHEN_NON_ZERO; } /* Save the codeblock on the stack */ $<expression>$.p = expression; $<expression>$.length = length; $<expression>$.line = current_line; /* Restart codegeneration from here */ CURRENT_PROGRAM_SIZE = start; last_expression = -1; } for_expr ')' { %line /* Save the <incr> code block on the compiler stack * for later re-insertion and start the compilation * of the loop body. */ p_int length; /* Save the code block */ insert_pop_value(); length = CURRENT_PROGRAM_SIZE - $<number>6; $<expression>$.p = yalloc(length); if (length) memcpy( $<expression>$.p , mem_block[A_PROGRAM].block + $<number>6 , length ); $<expression>$.length = length; $<expression>$.line = current_line; /* Restart the codegeneration for the body */ CURRENT_PROGRAM_SIZE = $<number>6; last_expression = -1; current_break_address = BREAK_DELIMITER; ins_byte(F_BRANCH); /* over the body to the condition */ ins_byte(0); /* Fix the number of locals to clear, now that we know it */ { block_scope_t *scope = block_scope + block_depth - 1; if (use_local_scopes && scope->num_locals) { mem_block[A_PROGRAM].block[scope->addr+2] = (char)scope->num_locals; } } } statement { %line /* The loop is complete, now add the <incr> and <cond> * code save on the compiler stack and patch up * the break and continues. */ p_int offset; p_int next_addr; /* Patch up the continues */ for (; current_continue_address > 0 ; current_continue_address = next_addr) { next_addr = read_short(current_continue_address); upd_short(current_continue_address, CURRENT_PROGRAM_SIZE - current_continue_address); } if ( $<expression>9.line != current_line || ( $<expression>12.line != current_line && $<expression>12.length) ) store_line_number_info(); /* Add the <incr> code block if needed */ if ($<expression>12.length) { add_to_mem_block(A_PROGRAM, $<expression>12.p , $<expression>12.length); if ($<expression>12.line != $<expression>9.line) store_line_number_relocation($<expression>12.line); } yfree($<expression>12.p); /* Fix the branch over the body */ offset = fix_branch( F_LBRANCH, CURRENT_PROGRAM_SIZE, $<number>6 + 1); /* Add the <cond> code block */ add_to_mem_block(A_PROGRAM, $<expression>9.p, $<expression>9.length+2); yfree($<expression>9.p); /* Create the branch back after the condition */ offset += $<number>6 + 2 - ( CURRENT_PROGRAM_SIZE - 1 ); if (offset < -0xff) { bytecode_p codep; if (offset < -0x8000) yyerror("offset overflow"); codep = PROGRAM_BLOCK + --CURRENT_PROGRAM_SIZE - 1; *codep = *codep == F_BBRANCH_WHEN_NON_ZERO ? F_LBRANCH_WHEN_NON_ZERO : F_LBRANCH_WHEN_ZERO ; ins_short(offset); } else { mem_block[A_PROGRAM].block[CURRENT_PROGRAM_SIZE-1] = -offset; } if ($<expression>9.line != current_line) store_line_number_relocation($<expression>9.line); /* Now complete the break instructions. */ for (; current_break_address > 0 ; current_break_address = next_addr) { next_addr = read_short(current_break_address); upd_short(current_break_address, CURRENT_PROGRAM_SIZE - current_break_address); } /* Restore the previous environment */ current_continue_address = $<numbers>3[0]; current_break_address = $<numbers>3[1]; /* and leave the for scope */ leave_block_scope(); } ; /* for */ /* Special rules for 'int <name> = <expr>' declarations in the first * for() expression. */ for_init_expr: /* EMPTY */ { last_expression = mem_block[A_PROGRAM].current_size; ins_byte(F_CONST1); /* insert_pop_value() will optimize this away */ } | comma_expr_decl ; /* for_init_expr */ comma_expr_decl: expr_decl | comma_expr_decl { insert_pop_value(); } ',' expr_decl ; /* comma_expr_decl */ expr_decl: expr0 /* compile the expression as usual */ | local_name_lvalue L_ASSIGN expr0 { /* We got a "int <name> = <expr>" type expression. */ p_int length; vartype_t type2; %line /* Check the assignment for validity */ type2 = $3.type; if (exact_types && !compatible_types($1.type, type2)) { yyerrorf("Bad assignment %s", get_two_types($1.type, $3.type)); } if ($2 != F_ASSIGN) { yyerror("Only plain assignments allowed here."); } if (type2 & TYPE_MOD_REFERENCE) yyerror("Can't trace reference assignments."); /* Add the bytecode to create the lvalue and do the * assignment. */ length = $1.length; if (length) { add_to_mem_block(A_PROGRAM, $1.u.p, length+1); yfree($1.u.p); last_expression = CURRENT_PROGRAM_SIZE-1; mem_block[A_PROGRAM].block[last_expression] = $2; } else { bytecode_p source, dest; mp_uint current_size; source = $1.u.simple; current_size = CURRENT_PROGRAM_SIZE; if (!realloc_a_program(3)) { yyerrorf("Out of memory: program size %lu", current_size+3); YYACCEPT; } CURRENT_PROGRAM_SIZE = (last_expression = current_size + 2) + 1; dest = PROGRAM_BLOCK + current_size; *dest++ = *source++; *dest++ = *source; *dest = $2; } } | local_name_lvalue { /* We got a "int <name>" type expression. Compile it as if * it has been "int <name> = 0". */ p_int length; %line /* Add the bytecode for pushing the number 0 onto the stack */ ins_f_code(F_CONST0); /* Add the bytecode to create the lvalue and do the * assignment. */ length = $1.length; if (length) { add_to_mem_block(A_PROGRAM, $1.u.p, length+1); yfree($1.u.p); last_expression = CURRENT_PROGRAM_SIZE-1; mem_block[A_PROGRAM].block[last_expression] = F_ASSIGN; } else { bytecode_p source, dest; mp_uint current_size; source = $1.u.simple; current_size = CURRENT_PROGRAM_SIZE; if (!realloc_a_program(3)) { yyerrorf("Out of memory: program size %lu", current_size+3); YYACCEPT; } CURRENT_PROGRAM_SIZE = (last_expression = current_size + 2) + 1; dest = PROGRAM_BLOCK + current_size; *dest++ = *source++; *dest++ = *source; *dest = F_ASSIGN; } } ; /* expr_decl */ for_expr: /* EMPTY */ { last_expression = mem_block[A_PROGRAM].current_size; ins_byte(F_CONST1); } | comma_expr ; /* for_expr */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* The foreach() statement * * It is compiled into: or when <statement> is empty: * * CLEAR_LOCALS CLEAR_LOCALS * PUSH_(LOCAL_)LVALUE <var1> <expr> * ... POP_VALUE * PUSH_(LOCAL_)LVALUE <varn> * <expr> * FOREACH <numargs> c * l: <body> * c: FOREACH_NEXT l * e: FOREACH_END * * continue's branch to c, break's to e. */ foreach: L_FOREACH '(' { /* Save the previous environment */ $<numbers>$[0] = current_continue_address; $<numbers>$[1] = current_break_address; current_break_address = BREAK_DELIMITER; current_continue_address = CONTINUE_DELIMITER; /* Open a new scope to all variables local to the * foreach-statement as a whole. */ enter_block_scope(); } foreach_vars foreach_in { %line /* Remember the starting address of the expression */ $<address>$ = CURRENT_PROGRAM_SIZE; } expr0 ')' { vartype_t dtype; %line dtype = $7.type & TYPE_MOD_RMASK; if (!(dtype & TYPE_MOD_POINTER) && dtype != TYPE_ANY && dtype != TYPE_STRING && dtype != TYPE_MAPPING && (exact_types || dtype != TYPE_UNKNOWN) ) { type_error("Expression for foreach() of wrong type", $7.type); } /* Fix the number of locals to clear, now that we know it */ { block_scope_t *scope = block_scope + block_depth - 1; if (use_local_scopes && scope->num_locals) { mem_block[A_PROGRAM].block[scope->addr+2] = (char)scope->num_locals; } } /* Create the FOREACH instruction, leaving the branch field * blank. */ ins_f_code(F_FOREACH); ins_byte($4+1); ins_short(0); push_address(); /* Address to branch back to */ } statement { /* The body is complete - patch up the continue and * break statements and generate the remaining statements. */ p_int next_addr; p_int addr; mp_uint current; %line current = CURRENT_PROGRAM_SIZE; addr = pop_address(); /* Where the body began */ /* One obvious optimisation: when there is no code in * the body, we can save space and even more time by * just compiling the expression. * Too bad that we can't find out whether the expression * has side effects or not, otherwise we could try to * remove it, too. */ if (addr == current) { p_int expr_addr; /* Address of the expr0 */ p_int start_addr; /* Address of the first PUSH_LOCAL_LVALUE */ bytecode_p src, dest; expr_addr = $<address>6; start_addr = expr_addr - $4*2; current = start_addr + (addr - 4 - expr_addr); for ( src = PROGRAM_BLOCK + expr_addr, dest = PROGRAM_BLOCK + start_addr ; expr_addr < addr-4 ; src++, dest++, expr_addr++) *dest = *src; CURRENT_PROGRAM_SIZE = current; ins_f_code(F_POP_VALUE); current++; } else /* Create the full statement */ { /* First patch up the continue statements */ for(; current_continue_address > 0 ; current_continue_address = next_addr) { next_addr = read_short(current_continue_address); upd_short(current_continue_address, current - current_continue_address); } /* Create the FOREACH_NEXT instruction and update * the branch of the earlier F_FOREACH. */ upd_short(addr - 2, current - addr); ins_f_code(F_FOREACH_NEXT); ins_short(current + 3 - addr); current += 3; /* Finish up the breaks. */ for (; current_break_address > 0 ; current_break_address = next_addr) { next_addr = read_short(current_break_address); upd_short(current_break_address, current - current_break_address); } /* Finish with the FOREACH_END. */ ins_f_code(F_FOREACH_END); } /* Restore the previous environment */ current_continue_address = $<numbers>3[0]; current_break_address = $<numbers>3[1]; /* and leave the scope */ leave_block_scope(); } ; /* foreach */ foreach_vars : /* Parse and count the number of lvalues */ foreach_var_decl { $$ = 1; } | foreach_vars ',' foreach_var_decl { $$ = $1 + 1; } ; /* foreach_vars */ foreach_var_decl: /* Generate the code for one lvalue */ /* TODO: It is tempting to add an alternative "| lvalue", * TODO:: but then we get masses of reduce/reduce conflicts * TODO:: between lvalue and expr4. Dunno why. */ foreach_var_lvalue { /* Add the bytecode to create the lvalue, and good is. */ p_int length; %line length = $1.length; if (length) { add_to_mem_block(A_PROGRAM, $1.u.p, length); yfree($1.u.p); } else { bytecode_p source, dest; mp_uint current_size; source = $1.u.simple; current_size = CURRENT_PROGRAM_SIZE; if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %lu", current_size+2); YYACCEPT; } CURRENT_PROGRAM_SIZE = current_size + 2; dest = PROGRAM_BLOCK + current_size; *dest++ = *source++; *dest++ = *source; } } ; /* foreach_var_decl */ foreach_var_lvalue: /* Gather the code for one lvalue */ local_name_lvalue | name_lvalue ; /* foreach_var_lvalue */ foreach_in: /* The purpose of this rule is to avoid making "in" a reserved * word. Instead we require an identifier/local with the * name "in" as alternative to ":". Main reason to allow "in" * is MudOS compatibility. * TODO: Make MudOS-compats switchable. */ L_IDENTIFIER { if (strcmp($1->name, "in")) yyerror("Expected keyword 'in' in foreach()"); if ($1->type == I_TYPE_UNKNOWN) free_shared_identifier($1); } | L_LOCAL { ident_t *id; /* Find the ident structure for this local */ for (id = all_locals; id; id = id->next_all) if (id->u.local.num == $1) break; if (id && strcmp(id->name, "in")) yyerror("Expected keyword 'in' in foreach()"); } | ':' ; /* foreach_in */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* The switch statement. * * switch.h explains how the bytecode looks like. * * Note that the actual switch rule is: * * switch: L_SWITCH ( comma_expr ) statement * * and that case and default are both just special kinds of statement * which mark addresses with in the statement code to which the * switch statement may jump. * * That also means that the code * * switch(x); * or switch(x) write("Foo"); * * is syntactically ok, even though it wouldn't do anything. */ switch: L_SWITCH '(' comma_expr ')' { /* We start a new switch(), which might be nested into * an outer switch(). */ case_state_t *statep; %line current_break_stack_need++; if ( current_break_stack_need > max_break_stack_need ) max_break_stack_need = current_break_stack_need; /* Save the previous switch state */ if ( !(statep = yalloc(sizeof(case_state_t))) ) { yyerrorf("Out of memory: case state (%lu bytes)" , (unsigned long) sizeof(case_state_t)); YYACCEPT; } *statep = case_state; case_state.previous = statep; push_explicit(current_break_address); push_explicit(switch_pc); /* Create the SWITCH instruction plus two empty bytes */ ins_byte(F_SWITCH); switch_pc = mem_block[A_PROGRAM].current_size; ins_short(0); /* Set up the new switch generation */ case_state.list0 = case_state.list1 = NULL; case_state.zero = NULL; case_state.no_string_labels = MY_TRUE; case_state.some_numeric_labels = MY_FALSE; case_state.default_addr = 0; current_break_address = BREAK_ON_STACK | BREAK_FROM_SWITCH | CASE_LABELS_ENABLED ; if (current_continue_address) current_continue_address += SWITCH_DEPTH_UNIT; } statement { %line /* The statement (which hopefully contained cases) is complete. * Now create the lookup tables and restore the previous state. */ case_state_t *statep; current_break_address &= ~(BREAK_ON_STACK|BREAK_FROM_SWITCH|CASE_LABELS_ENABLED); if (!case_state.default_addr) { /* no default given -> create one */ case_state.default_addr = CURRENT_PROGRAM_SIZE-switch_pc; } /* it isn't unusual that the last case/default has no break */ ins_byte(F_BREAK); /* Create the lookup tables */ store_case_labels( CURRENT_PROGRAM_SIZE-switch_pc, case_state.default_addr, case_state.no_string_labels || case_state.some_numeric_labels, case_state.zero, yyget_space, yymove_switch_instructions, yyerror, yycerrorl ); /* Restore the previous state */ switch_pc = pop_address(); current_break_address = pop_address(); statep = case_state.previous; case_state = *statep; yfree(statep); if (current_continue_address) current_continue_address -= SWITCH_DEPTH_UNIT; current_break_stack_need--; } ; /* switch */ case: L_CASE case_label ':' { %line /* Mark the current program address as another * case target for the current switch. */ case_list_entry_t *temp; if ( !( current_break_address & CASE_LABELS_ENABLED ) ) { yyerror("Case outside switch"); break; } /* Get and fill in a new case entry structure */ if ( !(temp = new_case_entry()) ) { yyerror("Out of memory: new case entry"); break; } if ( !(temp->key = $2.key) ) { case_state.zero = temp; } temp->addr = mem_block[A_PROGRAM].current_size - switch_pc; temp->line = current_line; } | L_CASE case_label L_RANGE case_label ':' { %line /* Mark the current program address as another * range-case target for the current switch. */ case_list_entry_t *temp; if ( !$2.numeric || !$4.numeric ) yyerror("String case labels not allowed as range bounds"); if ( !( current_break_address & CASE_LABELS_ENABLED ) ) { yyerror("Case range outside switch"); break; } /* A range like "case 4..2" is illegal, * a range like "case 4..4" counts as simple "case 4". */ if ($2.key >= $4.key) { if ($2.key > $4.key) break; if ( !(temp = new_case_entry()) ) { yyerror("Out of memory: new case entry"); break; } temp->key = $2.key; temp->addr = CURRENT_PROGRAM_SIZE - switch_pc; temp->line = current_line; } /* Get and fill in the two case entries */ if ( !(temp = new_case_entry()) ) { yyerror("Out of memory: new case entry"); break; } temp->key = $2.key; temp->addr = 1; /* marks the lower bound of the range */ temp->line = current_line; if ( !(temp = new_case_entry()) ) { yyerror("Out of memory: new case entry"); break; } temp->key = $4.key; temp->addr = CURRENT_PROGRAM_SIZE - switch_pc; temp->line = 0; /* marks the upper bound of the range */ } ; /* case */ case_label: constant { %line if ( 0 != ($$.key = $1) ) { if ( !(case_state.no_string_labels) ) yyerror("Mixed case label list not allowed"); case_state.some_numeric_labels = 1; } $$.numeric = MY_TRUE; } | string_constant { %line if ( case_state.some_numeric_labels ) yyerror("Mixed case label list not allowed"); case_state.no_string_labels = MY_FALSE; store_prog_string(last_string_constant); $$.key = (p_int)last_string_constant; $$.numeric = MY_FALSE; last_string_constant = NULL; } ; /* case_label */ default: L_DEFAULT ':' { %line /* Mark the current program address as the default target * for the current switch. */ if ( !( current_break_address & CASE_LABELS_ENABLED ) ) { yyerror("Default outside switch"); break; } if (case_state.default_addr) yyerror("Duplicate default"); case_state.default_addr = CURRENT_PROGRAM_SIZE - switch_pc; } ; /* default */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* The if()-statement. * * This is compiled as: resp. as: * * <cond> <cond> * BRANCH_WHEN_ZERO e BRANCH_WHEN_ZERO e * <if-part> <if-part> * e: BRANCH f * e: <else-part> * f: * */ condStart: L_IF '(' comma_expr ')' { /* When we enter a condition, we must not allow case labels * anymore. */ mp_uint current; bytecode_p current_code; /* Turn off the case labels */ $$[0] = current_break_address; current_break_address &= ~CASE_LABELS_ENABLED; current = CURRENT_PROGRAM_SIZE; if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %lu\n", current+3); YYACCEPT; } current_code = PROGRAM_BLOCK + current; /* Add the branch instruction, with the usual optimization */ if (last_expression == current - 1 && current_code[-1] == F_NOT) { current_code[-1] = F_BRANCH_WHEN_NON_ZERO; } else { *current_code = F_BRANCH_WHEN_ZERO; current++; } $$[1] = current; CURRENT_PROGRAM_SIZE = current + 1; } ; /* condStart */ cond: condStart statement optional_else { p_int destination, location, offset; /* Complete the branch over the if-part */ destination = (p_int)$3; location = $1[1]; if ( (offset = destination - location) > 0x100) { fix_branch( mem_block[A_PROGRAM].block[location-1] == F_BRANCH_WHEN_ZERO ? F_LBRANCH_WHEN_ZERO : F_LBRANCH_WHEN_NON_ZERO , destination, location ); } else { mem_block[A_PROGRAM].block[location] = offset - 1; } /* Restore the previous case-labels status without * changing the actual break-address. */ current_break_address |= $1[0] & CASE_LABELS_ENABLED; } ; /* cond */ optional_else: /* empty */ %prec LOWER_THAN_ELSE { /* The if-part ends here */ $$ = CURRENT_PROGRAM_SIZE; } | L_ELSE { /* Add the branch over the else part */ ins_byte(F_BRANCH); $<address>$ = CURRENT_PROGRAM_SIZE; ins_byte(0); } statement { /* Fix up the branch over the else part and return * the start address of the else part. */ $$ = fix_branch( F_LBRANCH, CURRENT_PROGRAM_SIZE, $<address>2); $$ += $<address>2 + 1; } ; /* optional_else */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* Constants * * The rules here implement constant folding for numeric and string constants. */ 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 '>' constant { $$ = $1 > $3; } | constant L_GE constant { $$ = $1 >= $3; } | constant '<' constant { $$ = $1 < $3; } | constant L_LE constant { $$ = $1 <= $3; } | constant L_LSH constant { $$ = (p_uint)$3 > MAX_SHIFT ? 0 : $1 << $3; } | constant L_RSH constant { $$ = (p_uint)$3 > MAX_SHIFT ? ($1 >= 0 ? 0 : -1) : ($1 >> $3); } | constant L_RSHL constant { $$ = (p_uint)$3 > MAX_SHIFT ? 0 : ((p_uint)$1 >> $3); } | constant '+' constant { $$ = $1 + $3; } | constant '-' constant { $$ = $1 - $3; } | constant '*' constant { $$ = $1 * $3; } | constant '%' constant { if ($3) { $$ = $1 % $3; } else { yyerror("division by zero"); $$ = 0; } } | constant '/' constant { if ($3) { $$ = $1 / $3; } else { yyerror("division by zero"); $$ = 0; } } | '(' constant ')' { $$ = $2; } | '-' constant %prec '~' { $$ = -$2; } | L_NOT constant { $$ = !$2; } | '~' constant { $$ = ~$2; } | L_NUMBER ; /* constant */ string_constant: L_STRING { last_string_constant = last_lex_string; last_lex_string = NULL; } | string_constant '+' L_STRING { add_string_constant(); } | L_STRING L_STRING { fatal("presence of rule should prevent its reduction"); } | string_constant '+' L_STRING L_STRING { fatal("presence of rule should prevent its reduction"); } | '(' string_constant ')' ; /* string_constant */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* Expressions * * expr0 (with the help of the precedence and assoc specifications) handles * most of the expressions, and returns normal rvalues (as lrvalues). * * expr4 contains the expressions atoms (literal values), function calls * and expressions returning values which might be used as rvalues * as well as lvalues. It returns full lrvalues. * * lvalue contains expressions for unprotected lvalues and returns lvalues. * * name_lvalue is a subrule of lvalue and can be used where lvalues of * variables are needed (foreach() is one example). * * local_name_lvalue is to be used in contexts where new local variables * may be defined on the fly (for example "for(int i..."). * * index_expr and index_range are used to parse and compile the two * forms of array indexing operations. */ comma_expr: expr0 | comma_expr { insert_pop_value(); } ',' expr0 { $$.type = $4.type; } ; /* comma_expr */ expr0: /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* Normal assign: ||= (&&= analog): * * <expr0> <lvalue> <lvalue> * <lvalue> LDUP LDUP * ASSIGN-operator LOR l DUP * <expr0> LBRANCH_WHEN_NON_ZERO l * l: SWAP_VALUES POP_VALUE * ASSIGN <expr0> * l: SWAP_VALUES * ASSIGN */ lvalue L_ASSIGN { if ($2 == F_LAND_EQ || $2 == F_LOR_EQ) { if (!add_lvalue_code(&$1, 0)) YYACCEPT; /* Add the operator specific code */ if ($2 == F_LAND_EQ) { /* Insert the LDUP, LAND and remember the position */ ins_f_code(F_LDUP); ins_f_code(F_LAND); $<address>$ = CURRENT_PROGRAM_SIZE; ins_byte(0); } else if ($2 == F_LOR_EQ) { /* Insert the LDUP, LOR and remember the position */ ins_f_code(F_LDUP); ins_f_code(F_LOR); $<address>$ = CURRENT_PROGRAM_SIZE; ins_byte(0); } } } expr0 %prec L_ASSIGN { vartype_t type1, type2, restype; %line $$ = $4; type1 = $1.type; type2 = $4.type; restype = type2; /* Assume normal assignment */ /* Check the validity of the assignment */ if (exact_types && !compatible_types($1.type, type2) ) { Bool ok = MY_FALSE; switch($2) { case F_LAND_EQ: case F_LOR_EQ: ok = MY_TRUE; break; case F_ADD_EQ: switch(type1) { case TYPE_STRING: if (type2 == TYPE_NUMBER || type2 == TYPE_FLOAT) { ok = MY_TRUE; } break; case TYPE_FLOAT: if (type2 == TYPE_NUMBER) { ok = MY_TRUE; } break; } break; case F_SUB_EQ: switch(type1) { case TYPE_FLOAT: if (type2 == TYPE_NUMBER) { ok = MY_TRUE; } break; } break; case F_MULT_EQ: switch(type1) { case TYPE_STRING: if (type2 == TYPE_NUMBER) { ok = MY_TRUE; } break; case TYPE_FLOAT: if (type2 == TYPE_NUMBER) { ok = MY_TRUE; } break; default: if ((type1 & TYPE_MOD_POINTER) && type2 == TYPE_NUMBER) { ok = MY_TRUE; } } break; case F_DIV_EQ: switch(type1) { case TYPE_FLOAT: if (type2 == TYPE_NUMBER) { ok = MY_TRUE; } break; } break; } /* switch(assign op) */ if (!ok) { yyerrorf("Bad assignment %s", get_two_types(type1, type2)); } /* Operator assignment: result type is determined by assigned-to * type. */ restype = type1; } if (type2 & TYPE_MOD_REFERENCE) yyerror("Can't trace reference assignments."); if ($2 == F_LAND_EQ || $2 == F_LOR_EQ) { /* Update the offset the earlier LAND/LOR instruction */ if ($2 == F_LAND_EQ) { update_lop_branch($<address>3, F_LBRANCH_WHEN_ZERO); } else if ($2 == F_LOR_EQ) { update_lop_branch($<address>3, F_LBRANCH_WHEN_NON_ZERO); } /* Insert the SWAP and the ASSIGN */ ins_f_code(F_SWAP_VALUES); ins_f_code(F_ASSIGN); } else { if (!add_lvalue_code(&$1, $2)) YYACCEPT; } $$.end = CURRENT_PROGRAM_SIZE; $$.type = restype; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | error L_ASSIGN expr0 %prec L_ASSIGN { yyerror("Illegal LHS"); $$ = $3; $$.type = TYPE_ANY; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '?' { /* Insert the branch to the :-part and remember this address */ ins_byte(F_BRANCH_WHEN_ZERO); $<address>$ = CURRENT_PROGRAM_SIZE; ins_byte(0); } expr0 { /* Insert the branch over the :-part, and update * the earlier branch to the :-part. */ p_int address, offset; address = (p_int)$<address>3; /* The branch to the end */ ins_byte(F_BRANCH); $<address>$ = CURRENT_PROGRAM_SIZE; ins_byte(0); /* Update the earlier branch to point here */ offset = CURRENT_PROGRAM_SIZE - ( address + 1); if (offset > 0xff - 1) { /* We have to make it a long branch and move the code * generated so far. */ int i; bytecode_p p; $<address>$ = CURRENT_PROGRAM_SIZE; ins_byte(0); p = PROGRAM_BLOCK + mem_block[A_PROGRAM].current_size-1; for (i = offset; --i >= 0; --p ) *p = p[-1]; p[-2] = F_LBRANCH_WHEN_ZERO; upd_short(address, offset+2); if (offset > 0x7ffd) yyerror("offset overflow"); } else { mem_block[A_PROGRAM].block[address] = offset; } } ':' expr0 %prec '?' { /* Update the earlier branch skipping the :-part * and check the types of the two parts. */ p_int address, old_address; int offset; vartype_t type1, type2; last_expression = -1; old_address = $<address>3; address = $<address>5; offset = mem_block[A_PROGRAM].current_size - ( address + 1); if (offset > 0xff) { /* We have to make the branch a long branch. * This could also mean that the first branch now * have to become a long branch, too. */ int i; bytecode_p p; ins_byte(0); p = PROGRAM_BLOCK + mem_block[A_PROGRAM].current_size-1; for( i = offset; --i >= 0; --p ) *p = p[-1]; p[-2] = F_LBRANCH; upd_short(address, offset+2); if (offset > 0x7ffd) yyerror("offset overflow"); if ( mem_block[A_PROGRAM].block[old_address-1] == F_BRANCH_WHEN_ZERO ) mem_block[A_PROGRAM].block[old_address]++; else upd_short(old_address,read_short(old_address)+1); } else { mem_block[A_PROGRAM].block[address] = offset; } $$ = $1; $$.end = CURRENT_PROGRAM_SIZE; /* Check the types and determine the result type */ type1 = $4.type; type2 = $7.type; if (!compatible_types(type1, type2)) { $$.type = TYPE_ANY; if ((type1 & TYPE_MOD_POINTER) != 0 && (type2 & TYPE_MOD_POINTER) != 0) $$.type |= TYPE_MOD_POINTER; /* TODO: yyinfof("Different types to ?: */ } else if (type1 == TYPE_ANY) $$.type = type2; else if (type2 == TYPE_ANY) $$.type = type1; else if (type1 == (TYPE_MOD_POINTER|TYPE_ANY) ) $$.type = type2; else if (type2 == (TYPE_MOD_POINTER|TYPE_ANY) ) $$.type = type1; else $$.type = type1; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 L_LOR %prec L_LOR { /* Insert the LOR and remember the position */ ins_byte(F_LOR); $<address>$ = CURRENT_PROGRAM_SIZE; ins_byte(0); } expr0 { /* Update the offset the earlier LOR instruction */ update_lop_branch($<address>3, F_LBRANCH_WHEN_NON_ZERO); $$ = $1; $$.end = CURRENT_PROGRAM_SIZE; /* Determine the result type */ if ($1.type == $4.type) $$.type = $1.type; else $$.type = TYPE_ANY; /* Return type can't be known */ } /* LOR */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 L_LAND %prec L_LAND { /* Insert the LAND and remember the position */ ins_byte(F_LAND); $<address>$ = CURRENT_PROGRAM_SIZE; ins_byte(0); } expr0 { /* Update the offset the earlier LAND instruction */ update_lop_branch($<address>3, F_LBRANCH_WHEN_ZERO); $$ = $1; $$.end = CURRENT_PROGRAM_SIZE; /* Determine the return type */ if ($1.type == $4.type) $$.type = $1.type; else $$.type = TYPE_ANY; /* Return type can't be known */ } /* LAND */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '|' expr0 { if (exact_types && !BASIC_TYPE($1.type,TYPE_NUMBER)) type_error("Bad argument 1 to |", $1.type); if (exact_types && !BASIC_TYPE($3.type,TYPE_NUMBER)) type_error("Bad argument 2 to |", $3.type); $$ = $1; $$.type = TYPE_NUMBER; ins_byte(F_OR); $$.end = CURRENT_PROGRAM_SIZE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '^' expr0 { if (exact_types && !BASIC_TYPE($1.type,TYPE_NUMBER)) type_error("Bad argument 1 to ^", $1.type); if (exact_types && !BASIC_TYPE($3.type,TYPE_NUMBER)) type_error("Bad argument 2 to ^", $3.type); $$ = $1; $$.type = TYPE_NUMBER; ins_byte(F_XOR); $$.end = CURRENT_PROGRAM_SIZE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '&' expr0 { $$ = $1; ins_byte(F_AND); $$.type = TYPE_ANY; $$.end = CURRENT_PROGRAM_SIZE; /* Check the types */ if (exact_types) { vartype_t first_type = $1.type; vartype_t second_type = $3.type; if ( first_type == TYPE_ANY && second_type == TYPE_ANY ) { /* $$ == TYPE_ANY is correct */ } else if ( (first_type | second_type) & TYPE_MOD_POINTER) { if (first_type == TYPE_NUMBER || second_type == TYPE_NUMBER) { yyerrorf("Incompatible types for arguments to & %s" , get_two_types(first_type, second_type)); } else if (( !( first_type & TYPE_MOD_POINTER ) || first_type & TYPE_MOD_REFERENCE) && first_type != TYPE_ANY) { type_error("Bad argument 1 to &", first_type ); } else if (( !( second_type & TYPE_MOD_POINTER ) || second_type & TYPE_MOD_REFERENCE) && second_type != TYPE_ANY) { type_error("Bad argument 2 to &", first_type ); } else if ( !BASIC_TYPE(first_type &~TYPE_MOD_POINTER, second_type &~TYPE_MOD_POINTER) ) { yyerrorf("Incompatible types for arguments to & %s" , get_two_types(first_type, second_type)); } else { $$.type = TYPE_ANY | TYPE_MOD_POINTER; } } else { if ( !BASIC_TYPE(first_type ,TYPE_NUMBER) && !BASIC_TYPE(first_type ,TYPE_STRING) ) type_error("Bad argument 1 to &", first_type ); if ( !BASIC_TYPE(second_type,TYPE_NUMBER) && !BASIC_TYPE(second_type ,TYPE_STRING) ) type_error("Bad argument 2 to &", second_type); if ( first_type == TYPE_ANY ) $$.type = BASIC_TYPE(second_type ,TYPE_NUMBER) ? TYPE_NUMBER : TYPE_STRING; else $$.type = BASIC_TYPE(first_type ,TYPE_NUMBER) ? TYPE_NUMBER : TYPE_STRING; } } /* end of exact_types code */ } /* end of '&' code */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 L_EQ expr0 { vartype_t t1 = $1.type, t2 = $3.type; $$ = $1; if (exact_types && t1 != t2 && t1 != TYPE_ANY && t2 != TYPE_ANY && !(t1 == TYPE_NUMBER && t2 == TYPE_FLOAT) && !(t1 == TYPE_FLOAT && t2 == TYPE_NUMBER) ) { yyerrorf("== always false because of different types %s" , get_two_types($1.type, $3.type)); } ins_byte(F_EQ); $$.type = TYPE_NUMBER; $$.end = CURRENT_PROGRAM_SIZE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 L_NE expr0 { vartype_t t1 = $1.type, t2 = $3.type; $$ = $1; if (exact_types && t1 != t2 && t1 != TYPE_ANY && t2 != TYPE_ANY && !(t1 == TYPE_NUMBER && t2 == TYPE_FLOAT) && !(t1 == TYPE_FLOAT && t2 == TYPE_NUMBER) ) { yyerrorf("!= always true because of different types %s" , get_two_types($1.type, $3.type)); } ins_byte(F_NE); $$.type = TYPE_NUMBER; $$.end = CURRENT_PROGRAM_SIZE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '>' expr0 { $$ = $1; $$.type = TYPE_NUMBER; ins_f_code(F_GT); $$.end = CURRENT_PROGRAM_SIZE; } | expr0 L_GE expr0 { $$ = $1; $$.type = TYPE_NUMBER; ins_f_code(F_GE); $$.end = CURRENT_PROGRAM_SIZE; } | expr0 '<' expr0 { $$ = $1; $$.type = TYPE_NUMBER; ins_f_code(F_LT); $$.end = CURRENT_PROGRAM_SIZE; } | expr0 L_LE expr0 { $$ = $1; $$.type = TYPE_NUMBER; ins_f_code(F_LE); $$.end = CURRENT_PROGRAM_SIZE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 L_LSH expr0 { $$ = $1; ins_byte(F_LSH); $$.type = TYPE_NUMBER; $$.end = CURRENT_PROGRAM_SIZE; if (exact_types) { if (!BASIC_TYPE($1.type, TYPE_NUMBER)) type_error("Bad argument number 1 to '<<'", $1.type); if (!BASIC_TYPE($3.type, TYPE_NUMBER)) type_error("Bad argument number 2 to '<<'", $3.type); } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 L_RSH expr0 { $$ = $1; ins_byte(F_RSH); $$.type = TYPE_NUMBER; $$.end = CURRENT_PROGRAM_SIZE; if (exact_types) { if (!BASIC_TYPE($1.type, TYPE_NUMBER)) type_error("Bad argument number 1 to '>>'", $1.type); if (!BASIC_TYPE($3.type, TYPE_NUMBER)) type_error("Bad argument number 2 to '>>'", $3.type); } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 L_RSHL expr0 { $$ = $1; ins_byte(F_RSHL); $$.type = TYPE_NUMBER; $$.end = CURRENT_PROGRAM_SIZE; if (exact_types) { if (!BASIC_TYPE($1.type, TYPE_NUMBER)) type_error("Bad argument number 1 to '>>>'", $1.type); if (!BASIC_TYPE($3.type, TYPE_NUMBER)) type_error("Bad argument number 2 to '>>>'", $3.type); } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '+' { %line $<numbers>$[0] = last_expression; $<numbers>$[1] = last_string_is_new; } expr0 { /* Type checks of this case are complicated, therefore * we'll do them at run-time. * Here we just try to fold "string" + "string". */ mp_uint current_size; bytecode_p p; %line $$ = $1; current_size = CURRENT_PROGRAM_SIZE; p = &(PROGRAM_BLOCK[current_size]); /* Check if we can combine strings: the pragma must agree * and the last four bytes must be two CSTRINGx instructions. */ if (pragma_combine_strings && last_expression + 2 == current_size && $<numbers>3[0] + 4 == (mp_int)current_size && ((p[-2]-(F_CSTRING0)) & ~3) == 0 && ((p[-4]-(F_CSTRING0)) & ~3) == 0 ) { /* Yup, we can combine the two strings. */ char *str1, *str2, *sum; int i; /* Retrieve both strings from the A_STRINGS area * and catenate them. */ str1 = ((char**)(mem_block[A_STRINGS].block)) [p[-3] | (p[-4]-(F_CSTRING0))<<8 ]; str2 = ((char**)(mem_block[A_STRINGS].block)) [p[-1] | (p[-2]-(F_CSTRING0))<<8 ]; sum = xalloc(strlen(str1) + strlen(str2) + 1); strcpy(sum, str1); strcat(sum, str2); /* If possible, try to delete the constituent strings * from the string area. */ if (last_string_is_new) delete_prog_string(); if ($<numbers>3[1]) delete_prog_string(); /* Store the new string and update the CSTRING * instructions. */ i = store_prog_string(make_shared_string(sum)); xfree(sum); last_expression = current_size - 4; if (i < 0x400) { p[-4] = F_CSTRING0 + (i>>8); p[-3] = i; CURRENT_PROGRAM_SIZE = current_size - 2; } else { p[-4] = F_STRING; upd_short(current_size - 3, i); CURRENT_PROGRAM_SIZE = current_size - 1; } $$.type = TYPE_STRING; } else { /* Just add */ ins_byte(F_ADD); $$.type = TYPE_ANY; if ($1.type == $4.type) $$.type = $1.type; else if ($1.type == TYPE_STRING) $$.type = TYPE_STRING; else if (($1.type == TYPE_NUMBER || $1.type == TYPE_FLOAT) && $4.type == TYPE_STRING) $$.type = TYPE_STRING; else if ($1.type == TYPE_FLOAT && ($4.type == TYPE_NUMBER || $4.type == TYPE_ANY)) $$.type = TYPE_FLOAT; else if (($1.type == TYPE_NUMBER || $1.type == TYPE_ANY) && $4.type == TYPE_FLOAT) $$.type = TYPE_FLOAT; } $$.end = CURRENT_PROGRAM_SIZE; } /* '+' */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '-' expr0 { %line $$ = $1; $$.type = TYPE_ANY; if (exact_types) { vartype_t type1 = $1.type; vartype_t type2 = $3.type; if (type1 == type2) { static char matchok[] = %typemap TYPE_ANY:1,TYPE_NUMBER:1,TYPE_FLOAT:1,TYPE_MAPPING:1,TYPE_STRING:1 if ( type1 & (TYPE_MOD_POINTER|TYPE_MOD_REFERENCE) ? (type1 & (TYPE_MOD_POINTER|TYPE_MOD_REFERENCE)) == TYPE_MOD_POINTER : matchok[type1] ) { $$.type = type1; } else { type_error("Bad arguments to '-'", type1); } } else if ( (type1 & (TYPE_MOD_POINTER|TYPE_MOD_REFERENCE)) == TYPE_MOD_POINTER) { if ((type2 | TYPE_MOD_POINTER) == (TYPE_MOD_POINTER|TYPE_ANY) || ( type2 & TYPE_MOD_POINTER && type1 == (TYPE_MOD_POINTER|TYPE_ANY)) ) { $$.type = type1; } else { yyerror("Arguments to '-' don't match"); } } else switch (type1) { case TYPE_ANY: switch (type2) { case TYPE_NUMBER: /* number or float -> TYPE_ANY */ break; case TYPE_MAPPING: case TYPE_FLOAT: case TYPE_STRING: $$.type = type2; break; default: if ( (type2 & (TYPE_MOD_POINTER|TYPE_MOD_REFERENCE)) == TYPE_MOD_POINTER) { $$.type = TYPE_ANY | TYPE_MOD_POINTER; break; } else { type_error("Bad argument number 2 to '-'", type2); break; } } break; case TYPE_NUMBER: if (type2 == TYPE_FLOAT || type2 == TYPE_ANY) { $$.type = type2; } else { yyerror("Arguments to '-' don't match"); } break; case TYPE_FLOAT: if (type2 == TYPE_NUMBER || type2 == TYPE_ANY) { $$.type = TYPE_FLOAT; } else { yyerror("Arguments to '-' don't match"); } break; case TYPE_STRING: if (type2 == TYPE_STRING || type2 == TYPE_ANY) { $$.type = TYPE_STRING; } else { yyerror("Arguments to '-' don't match"); } break; case TYPE_MAPPING: if (type2 == TYPE_ANY) { $$.type = type1; } else { yyerror("Arguments to '-' don't match"); } break; default: type_error("Bad argument number 1 to '-'", type1); break; } } /* if (exact_types) */ ins_byte(F_SUBTRACT); $$.end = CURRENT_PROGRAM_SIZE; } /* '-' */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '*' expr0 { vartype_t type1, type2; $$ = $1; type1 = $1.type; type2 = $3.type; if (exact_types) { if (!BASIC_TYPE(type1, TYPE_NUMBER) && type1 != TYPE_FLOAT && type1 != TYPE_STRING && !(type1 & TYPE_MOD_POINTER) ) type_error("Bad argument number 1 to '*'", type1); if (!BASIC_TYPE(type2, TYPE_NUMBER) && type2 != TYPE_FLOAT && type2 != TYPE_STRING && !(type2 & TYPE_MOD_POINTER) ) type_error("Bad argument number 2 to '*'", type2); } ins_byte(F_MULTIPLY); $$.end = CURRENT_PROGRAM_SIZE; if (type1 == TYPE_FLOAT || type2 == TYPE_FLOAT ) { $$.type = TYPE_FLOAT; } else if (type1 == TYPE_STRING || type2 == TYPE_STRING) { $$.type = TYPE_STRING; } else if (type1 & TYPE_MOD_POINTER) { $$.type = type1; } else if (type2 & TYPE_MOD_POINTER) { $$.type = type2; } else if (type1 == TYPE_ANY || type2 == TYPE_ANY) { $$.type = TYPE_ANY; } else { $$.type = TYPE_NUMBER; } } /* '*' */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '%' expr0 { if (exact_types) { if (!BASIC_TYPE($1.type, TYPE_NUMBER)) type_error("Bad argument number 1 to '%'", $1.type); if (!BASIC_TYPE($3.type, TYPE_NUMBER)) type_error("Bad argument number 2 to '%'", $3.type); } $$ = $1; ins_byte(F_MOD); $$.end = CURRENT_PROGRAM_SIZE; $$.type = TYPE_NUMBER; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '/' expr0 { vartype_t type1, type2; $$ = $1; type1 = $1.type; type2 = $3.type; if (exact_types) { if ( !BASIC_TYPE(type1, TYPE_NUMBER) && type1 != TYPE_FLOAT) type_error("Bad argument number 1 to '/'", type1); if ( !BASIC_TYPE(type2, TYPE_NUMBER) && type2 != TYPE_FLOAT) type_error("Bad argument number 2 to '/'", type2); } ins_byte(F_DIVIDE); $$.end = CURRENT_PROGRAM_SIZE; if (type1 == TYPE_FLOAT || type2 == TYPE_FLOAT ) { $$.type = TYPE_FLOAT; } else { $$.type = TYPE_NUMBER; } } /* '/' */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | decl_cast expr0 %prec '~' { $$ = $2; $$.type = $1; if (exact_types && $2.type != TYPE_ANY && $2.type != TYPE_UNKNOWN && $1 != TYPE_VOID ) type_error("Casts are only legal for type mixed, or when unknown", $2.type); } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | cast expr0 %prec '~' { $$ = $2; $$.type = $1; if ($2.type != TYPE_ANY && $2.type != TYPE_UNKNOWN && $1 != TYPE_VOID && $1 != $2.type ) { switch($1) { default: type_error("Illegal cast", $1); break; case TYPE_ANY: /* Do nothing, just adapt the type information */ break; case TYPE_NUMBER: ins_f_code(F_TO_INT); break; case TYPE_FLOAT: ins_f_code(F_TO_FLOAT); break; case TYPE_STRING: ins_f_code(F_TO_STRING); break; case TYPE_OBJECT: ins_f_code(F_TO_OBJECT); break; case TYPE_NUMBER|TYPE_MOD_POINTER: ins_f_code(F_TO_ARRAY); break; } } $$.end = CURRENT_PROGRAM_SIZE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | pre_inc_dec L_IDENTIFIER %prec L_INC { /* ++/-- of a global variable. * We have to distinguish virtual and non-virtual * variables here. */ int i; PREPARE_INSERT(4) %line $$.start = $1.start; i = verify_declared($2); if (i != -1) { if (i & VIRTUAL_VAR_TAG) { add_byte(F_PUSH_VIRTUAL_VARIABLE_LVALUE); add_byte(i); i = V_VARIABLE(i)->flags & TYPE_MOD_MASK; } else { if ((i + num_virtual_variables) & ~0xff) { add_byte(F_PUSH_IDENTIFIER16_LVALUE); add_short(i + num_virtual_variables); CURRENT_PROGRAM_SIZE += 1; } else { add_byte(F_PUSH_IDENTIFIER_LVALUE); add_byte(i + num_virtual_variables); } i = NV_VARIABLE(i)->flags & TYPE_MOD_MASK; } if (exact_types && !BASIC_TYPE(i, TYPE_NUMBER) && !BASIC_TYPE(i, TYPE_FLOAT)) { argument_type_error($1.code, i); } CURRENT_PROGRAM_SIZE += 2; } else { /* Variable not declared - try to recover */ YYACCEPT; } last_expression = CURRENT_PROGRAM_SIZE; CURRENT_PROGRAM_SIZE += 1; add_byte($1.code); $$.end = CURRENT_PROGRAM_SIZE; $$.type = i; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | pre_inc_dec L_LOCAL %prec L_INC { int i; PREPARE_INSERT(3) %line $$.start = $1.start; add_byte(F_PUSH_LOCAL_VARIABLE_LVALUE); add_byte($2); CURRENT_PROGRAM_SIZE = (last_expression = CURRENT_PROGRAM_SIZE + 2) + 1; add_byte($1.code); i = type_of_locals[$2]; if (exact_types && !BASIC_TYPE(i, TYPE_NUMBER) && !BASIC_TYPE(i, TYPE_FLOAT)) { argument_type_error($1.code, i); } $$.type = i; $$.end = CURRENT_PROGRAM_SIZE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | pre_inc_dec expr4 index_expr %prec '[' { mp_uint current; bytecode_p p; int start, restype; %line if ($3.type1 & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); $$.start = $1.start; restype = TYPE_ANY; /* Check the types */ if (exact_types) { vartype_t type; type = $2.type; if (type & TYPE_MOD_POINTER) { if (type != (TYPE_MOD_POINTER|TYPE_ANY) && type != (TYPE_MOD_POINTER|TYPE_NUMBER) ) argument_type_error($1.code, type); } else switch (type) { case TYPE_MAPPING: if ($3.inst == F_INDEX) break; /* FALLTHROUGH */ default: type_error("Bad type to indexed lvalue", type); case TYPE_ANY: if ($3.inst == F_INDEX) break; /* FALLTHROUGH */ case TYPE_STRING: if (!BASIC_TYPE($3.type1, TYPE_NUMBER)) type_error("Bad type of index", $3.type1); restype = TYPE_NUMBER; break; } } /* if (exact_types) */ /* Create the code to index the lvalue */ /* TODO: How does this lvalue-indexing work? */ current = CURRENT_PROGRAM_SIZE; start = $2.start; if ($2.code >= 0) { if ($2.end) { int length; bytecode_p q; length = $2.end - start + 1; if (!realloc_a_program(length)) { yyerrorf("Out of memory: program size %lu\n" , current+length); YYACCEPT; } p = PROGRAM_BLOCK; memcpy(p + current, p + start, length); p += start; q = p + length; length = current - start; for( ; --length >= 0; ) *p++ = *q++; if ($2.code == F_PUSH_IDENTIFIER16_LVALUE) p[-3] = $2.code; else p[-1] = $2.code; *p++ = ($3.inst == F_INDEX) ? F_INDEX_LVALUE : F_RINDEX_LVALUE; } else { int i; int length; if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %lu\n", current+2); YYACCEPT; } p = PROGRAM_BLOCK + start; i = p[1]; length = current - start - 2; for( ; --length >= 0; p++) *p = p[2]; *p++ = $2.code; *p++ = i; *p++ = ($3.inst == F_INDEX) ? F_INDEX_LVALUE : F_RINDEX_LVALUE; } } else { if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %lu\n", current+2); YYACCEPT; } p = PROGRAM_BLOCK + start; *p++ = ($3.inst == F_INDEX) ? F_PUSH_INDEXED_LVALUE : F_PUSH_RINDEXED_LVALUE; } /* Finally store the actual instruction */ *p = $1.code; last_expression = current + 1; CURRENT_PROGRAM_SIZE = current + 2; $$.end = CURRENT_PROGRAM_SIZE; $$.type = restype; } /* pre_inc_dec expr4 [index_expr] */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | pre_inc_dec expr4 '[' expr0 ',' expr0 ']' %prec '[' { mp_uint current; bytecode_p p; %line if ($4.type & TYPE_MOD_REFERENCE || $6.type & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); $$.start = $1.start; /* Check the types */ if (exact_types) { vartype_t type; type = $2.type; switch (type) { default: type_error("Bad type to indexed lvalue", type); break; case TYPE_ANY: case TYPE_MAPPING: break; } } /* if (exact_types) */ /* We don't have to do much: we can take the rvalue * produced by <expr4> and add our PUSH_INDEXED_MAP_LVALUE */ current = CURRENT_PROGRAM_SIZE; if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %lu\n", current+2); YYACCEPT; } p = PROGRAM_BLOCK + current; *p++ = F_PUSH_INDEXED_MAP_LVALUE; /* Finally store the actual instruction */ *p = $1.code; last_expression = current + 1; CURRENT_PROGRAM_SIZE = current + 2; $$.type = TYPE_ANY; $$.end = CURRENT_PROGRAM_SIZE; } /* pre_inc_dec expr4 [expr0 ',' expr0] */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | L_NOT expr0 { $$ = $2; last_expression = CURRENT_PROGRAM_SIZE; ins_byte(F_NOT); /* Any type is valid here. */ $$.type = TYPE_NUMBER; $$.end = CURRENT_PROGRAM_SIZE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '~' expr0 { %line $$ = $2; ins_byte(F_COMPL); if (exact_types && !BASIC_TYPE($2.type, TYPE_NUMBER)) type_error("Bad argument to ~", $2.type); $$.type = TYPE_NUMBER; $$.end = CURRENT_PROGRAM_SIZE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '-' expr0 %prec '~' { vartype_t type; %line $$ = $2; if (CURRENT_PROGRAM_SIZE - last_expression == 2 && mem_block[A_PROGRAM].block[last_expression] == F_CLIT ) { mem_block[A_PROGRAM].block[last_expression] = F_NCLIT; } else if (CURRENT_PROGRAM_SIZE - last_expression == 1 && mem_block[A_PROGRAM].block[last_expression] == F_CONST1 ) { mem_block[A_PROGRAM].block[last_expression] = F_NCONST1; } else if (CURRENT_PROGRAM_SIZE - last_expression == 1 + sizeof(p_int) && mem_block[A_PROGRAM].block[last_expression] == F_NUMBER ) { mem_block[A_PROGRAM].block[last_expression] = F_NNUMBER; } else { ins_byte(F_NEGATE); } $$.end = CURRENT_PROGRAM_SIZE; type = $2.type; if (exact_types && !BASIC_TYPE(type, TYPE_NUMBER) && type != TYPE_FLOAT ) type_error("Bad argument to unary '-'", type); } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | lvalue L_INC %prec L_INC { %line $$.start = CURRENT_PROGRAM_SIZE; /* Create the code to push the lvalue plus POST_INC */ if ($1.length) { add_to_mem_block(A_PROGRAM, $1.u.p, $1.length); yfree($1.u.p); last_expression = CURRENT_PROGRAM_SIZE; ins_byte(F_POST_INC); } else { PREPARE_INSERT(3) bytecode_p source; CURRENT_PROGRAM_SIZE = (last_expression = CURRENT_PROGRAM_SIZE+2) + 1; source = $1.u.simple; add_byte(*source++); add_byte(*source); add_byte(F_POST_INC); } /* Check the types */ if (exact_types && !BASIC_TYPE($1.type, TYPE_NUMBER) && !BASIC_TYPE($1.type, TYPE_FLOAT) ) type_error("Bad argument to ++", $1.type); $$.end = CURRENT_PROGRAM_SIZE; $$.type = $1.type; } /* post-inc */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | lvalue L_DEC %prec L_DEC { %line $$.start = CURRENT_PROGRAM_SIZE; /* Create the code to push the lvalue plus POST_DEC */ if ($1.length) { add_to_mem_block(A_PROGRAM, $1.u.p, $1.length+1); yfree($1.u.p); mem_block[A_PROGRAM].block[ last_expression = CURRENT_PROGRAM_SIZE-1 ] = F_POST_DEC; } else { PREPARE_INSERT(3) bytecode_p source; CURRENT_PROGRAM_SIZE = (last_expression = CURRENT_PROGRAM_SIZE+2) + 1; source = $1.u.simple; add_byte(*source++); add_byte(*source); add_byte(F_POST_DEC); } /* Check the types */ if (exact_types && !BASIC_TYPE($1.type, TYPE_NUMBER) && !BASIC_TYPE($1.type, TYPE_FLOAT) ) type_error("Bad argument to --", $1.type); $$.end = CURRENT_PROGRAM_SIZE; $$.type = $1.type; } /* post-dec */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr4 { $$ = $1; } ; /* expr0 */ pre_inc_dec: L_INC { $$.code = F_PRE_INC; $$.start = CURRENT_PROGRAM_SIZE; } | L_DEC { $$.code = F_PRE_DEC; $$.start = CURRENT_PROGRAM_SIZE; } ; expr4: function_call %prec '~' | inline_fun | catch %prec '~' | sscanf %prec '~' %ifdef SUPPLY_PARSE_COMMAND | parse_command %prec '~' %endif /* SUPPLY_PARSE_COMMAND */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | L_STRING { /* Push a constant string */ int string_number; PREPARE_INSERT(3) char *p; %line p = last_lex_string; last_lex_string = NULL; $$.start = last_expression = CURRENT_PROGRAM_SIZE; $$.type = TYPE_STRING; $$.code = -1; string_number = store_prog_string(p); if ( string_number <= 0xff ) { add_byte(F_CSTRING0); add_byte(string_number); } else if ( string_number <= 0x1ff ) { add_byte(F_CSTRING1); add_byte(string_number); } else if ( string_number <= 0x2ff ) { add_byte(F_CSTRING2); add_byte(string_number); } else if ( string_number <= 0x3ff ) { add_byte(F_CSTRING3); add_byte(string_number); } else { add_byte(F_STRING); add_short(string_number); CURRENT_PROGRAM_SIZE++; } CURRENT_PROGRAM_SIZE += 2; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | L_NUMBER { /* Store a number */ p_int current; p_int number; PREPARE_INSERT(1 + sizeof (p_int)) %line $$.start = last_expression = current = CURRENT_PROGRAM_SIZE; $$.code = -1; number = $1; if ( number == 0 ) { current++; add_byte(F_CONST0); $$.type = TYPE_ANY; /* TODO: TYPE_NULL would be better */ } else if ( number == 1 ) { add_byte(F_CONST1); current++; $$.type = TYPE_NUMBER; } else if ( number >= 0 && number <= 0xff ) { add_byte(F_CLIT); add_byte(number); current += 2; $$.type = TYPE_NUMBER; } else { add_byte(F_NUMBER); memcpy(__PREPARE_INSERT__p, &$1, sizeof $1); current += 1 + sizeof (p_int); $$.type = TYPE_NUMBER; } CURRENT_PROGRAM_SIZE = current; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | L_CLOSURE { int ix; $$.start = CURRENT_PROGRAM_SIZE; $$.code = -1; ix = $1.number; ins_byte(F_CLOSURE); ins_short(ix); $$.type = TYPE_CLOSURE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | L_SYMBOL { /* Generate a symbol */ int string_number; int quotes; $$.start = CURRENT_PROGRAM_SIZE; $$.code = -1; quotes = $1.quotes; string_number = store_prog_string($1.name); if (quotes == 1 && string_number < 0x100) { /* One byte shorter than the other way */ ins_byte(F_CSTRING0); ins_byte(string_number); ins_byte(F_QUOTE); } else { ins_byte(F_SYMBOL); ins_short(string_number); ins_byte(quotes); } $$.type = TYPE_SYMBOL; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | L_FLOAT { /* Generate a float literal */ int exponent; $$.start = CURRENT_PROGRAM_SIZE; $$.code = -1; ins_byte(F_FLOAT); ins_long ( SPLIT_DOUBLE( $1, &exponent) ); ins_short( exponent ); $$.type = TYPE_FLOAT; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '(' note_start comma_expr ')' %prec '~' { /* A nested expression */ $$.type = $3.type; $$.start = $2.start; $$.code = -1; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '(' '{' note_start expr_list '}' ')' %prec '~' { /* Generate an array */ check_aggregate_types($4); /* We don't care about these types, * unless a reference appears */ ins_byte(F_AGGREGATE); ins_short($4); if (max_array_size && $4 > max_array_size) yyerror("Illegal array size"); $$.type = TYPE_MOD_POINTER | TYPE_ANY; $$.start = $3.start; $$.code = -1; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | L_QUOTED_AGGREGATE note_start expr_list '}' ')' %prec '~' { /* Generate a quoted array by generating a normal * array first and then applying QUOTE as often * as possible. */ int quotes; check_aggregate_types($3); /* We don't care about these types, * unless a reference appears */ ins_byte(F_AGGREGATE); ins_short($3); if (max_array_size && $3 > max_array_size) yyerror("Illegal array size"); $$.type = TYPE_QUOTED_ARRAY; $$.start = $2.start; $$.code = -1; quotes = $1; do { ins_byte(F_QUOTE); } while (--quotes); } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '(' '[' ':' note_start /* Generate an empty mapping of given width */ { ins_byte(F_CONST0); } expr0 ']' ')' { ins_f_code(F_M_ALLOCATE); $$.type = TYPE_MAPPING; $$.start = $4.start; $$.code = -1; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '(' '[' note_start m_expr_list ']' ')' { /* Generate a mapping */ mp_int num_keys; check_aggregate_types($4[0]); num_keys = $4[0] / ($4[1]+1); if ((num_keys|$4[1]) & ~0xffff) yyerror("cannot handle more than 65535 keys/values " "in mapping aggregate"); if ( (num_keys | $4[1]) &~0xff) { ins_byte(F_M_AGGREGATE); ins_short(num_keys); ins_short($4[1]); } else { ins_byte(F_M_CAGGREGATE); ins_byte(num_keys); ins_byte($4[1]); } $$.type = TYPE_MAPPING; $$.start = $3.start; $$.code = -1; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr4 index_range %prec '[' { %line /* Generate a range expression */ $$.start = $1.start; $$.code = -1; ins_f_code($2.inst); /* Check the types */ if (exact_types) { vartype_t type; $$.type = type = $1.type; if ((type & TYPE_MOD_POINTER) == 0 && type != TYPE_ANY && type != TYPE_STRING) { type_error("Bad type of argument used for range", type); $$.type = TYPE_ANY; } type = $2.type1; if (type != TYPE_ANY && type != TYPE_NUMBER) type_error("Bad type of index", type); type = $2.type2; if (type != TYPE_ANY && type != TYPE_NUMBER) type_error("Bad type of index", type); } else { $$.type = TYPE_ANY; } } /* expr4 index_range */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '&' L_IDENTIFIER %prec '~' { /* Reference to a global variable, virtual or non-virtual. * We generate PUSH_LVALUE code and mark the type * as TYPE_MOD_REFERENCE. */ int i; mp_uint current; bytecode_p p; %line i = verify_declared($2); $$.start = current = CURRENT_PROGRAM_SIZE; $$.code = -1; if (!realloc_a_program(3)) { yyerrorf("Out of memory: program size %lu\n", current+3); YYACCEPT; } p = PROGRAM_BLOCK + current; if (i & VIRTUAL_VAR_TAG) { *p++ = F_PUSH_VIRTUAL_VARIABLE_LVALUE; *p = i; } else { if ((i + num_virtual_variables) & ~0xff) { *p = F_PUSH_IDENTIFIER16_LVALUE; upd_short(++current, i + num_virtual_variables); } else { *p++ = F_PUSH_IDENTIFIER_LVALUE; *p = i + num_virtual_variables; } } CURRENT_PROGRAM_SIZE = current + 2; if (i == -1) $$.type = TYPE_ANY | TYPE_MOD_REFERENCE; else $$.type = (VARIABLE(i)->flags & TYPE_MOD_MASK) | TYPE_MOD_REFERENCE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '&' L_LOCAL %prec '~' { /* Reference to a local variable. * We generate PUSH_LVALUE code and mark the type * as TYPE_MOD_REFERENCE. */ mp_uint current; bytecode_p p; %line $$.start = current = CURRENT_PROGRAM_SIZE; $$.code = -1; if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %lu\n", current+2); YYACCEPT; } p = PROGRAM_BLOCK + current; *p++ = F_PUSH_LOCAL_VARIABLE_LVALUE; *p = $2; CURRENT_PROGRAM_SIZE = current + 2; $$.type = type_of_locals[$2] | TYPE_MOD_REFERENCE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '&' '(' expr4 index_expr ')' %prec '~' { %line /* Generate the proper indexing operator */ if ($4.inst == F_INDEX) arrange_protected_lvalue($3.start, $3.code, $3.end, F_PROTECTED_INDEX_LVALUE ); else arrange_protected_lvalue($3.start, $3.code, $3.end, F_PROTECTED_RINDEX_LVALUE ); $$.start = $3.start; $$.code = -1; if ($4.type1 & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); /* Compute the result type */ if (!exact_types) { $$.type = TYPE_ANY | TYPE_MOD_REFERENCE; } else { vartype_t type; type = $3.type; if (type & TYPE_MOD_POINTER) { $$.type = type & ~TYPE_MOD_POINTER; } else if (type == TYPE_MAPPING && $4.inst == F_INDEX) { $4.type1 = TYPE_ANY; $$.type = TYPE_ANY | TYPE_MOD_REFERENCE; } else switch (type) { default: type_error("Bad type to indexed reference", type); /* FALLTHROUGH */ case TYPE_ANY: if ($4.inst == F_INDEX) $4.type1 = TYPE_ANY; $$.type = TYPE_ANY | TYPE_MOD_REFERENCE; break; case TYPE_STRING: $$.type = TYPE_NUMBER | TYPE_MOD_REFERENCE; break; } if (!BASIC_TYPE($4.type1, TYPE_NUMBER)) type_error("Bad type of index", $4.type1); } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '&' '(' expr4 '[' expr0 ',' expr0 ']' ')' { %line /* Generate the proper indexing operator */ $$.start = $3.start; $$.code = -1; $$.type = TYPE_ANY | TYPE_MOD_REFERENCE; ins_f_code(F_PUSH_PROTECTED_INDEXED_MAP_LVALUE); if ($5.type & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); /* Compute the result type */ if (exact_types) { vartype_t type; type = $3.type; if (type != TYPE_ANY && type != TYPE_MAPPING) { type_error("Bad type to indexed value", type); } type = $7.type; if (type != TYPE_ANY && type != TYPE_NUMBER) type_error("Bad type of index", type); } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '&' '(' expr4 index_range ')' %prec '~' { %line /* Generate the proper indexing operator */ int prot_op; switch($4.inst) { case F_RANGE: prot_op = F_PROTECTED_RANGE_LVALUE; break; case F_NR_RANGE: prot_op = F_PROTECTED_NR_RANGE_LVALUE; break; case F_RN_RANGE: prot_op = F_PROTECTED_RN_RANGE_LVALUE; break; case F_RR_RANGE: prot_op = F_PROTECTED_RR_RANGE_LVALUE; break; case F_NX_RANGE: prot_op = F_PROTECTED_NX_RANGE_LVALUE; break; case F_RX_RANGE: prot_op = F_PROTECTED_RX_RANGE_LVALUE; break; default: fatal("Unsupported range type %d %s\n" , $4.inst, get_f_name($4.inst)); } arrange_protected_lvalue($3.start, $3.code, $3.end , prot_op ); $$.start = $3.start; $$.code = -1; /* Compute the result type */ if (!exact_types) { $$.type = TYPE_ANY | TYPE_MOD_REFERENCE; } else { vartype_t type; $$.type = type = $3.type; if ((type & TYPE_MOD_POINTER) == 0 && type != TYPE_ANY && type != TYPE_STRING) { type_error("Bad type of argument used for range", type); $$.type = TYPE_ANY; } type = $4.type1; if (type != TYPE_ANY && type != TYPE_NUMBER) type_error("Bad type of index", type); type = $4.type2; if (type != TYPE_ANY && type != TYPE_NUMBER) type_error("Bad type of index", type); } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ %// The following expressions can be patched to lvalues for use in index_lvalue. | L_IDENTIFIER { /* Access a global variable */ int i; mp_uint current; bytecode_p p; %line i = verify_declared($1); $$.start = current = CURRENT_PROGRAM_SIZE; $$.end = 0; if (!realloc_a_program(3)) { yyerrorf("Out of memory: program size %lu\n", current+3); YYACCEPT; } p = PROGRAM_BLOCK + current; if (i & VIRTUAL_VAR_TAG) { /* Access a virtual variable */ $$.code = F_PUSH_VIRTUAL_VARIABLE_LVALUE; *p++ = F_VIRTUAL_VARIABLE; *p = i; $$.type = V_VARIABLE(i)->flags & TYPE_MOD_MASK; } else { /* Access a non-virtual variable */ if ((i + num_virtual_variables) & ~0xff) { $$.code = F_PUSH_IDENTIFIER16_LVALUE; *p = F_IDENTIFIER16; upd_short(++current, i + num_virtual_variables); $$.end = current+1; } else { $$.code = F_PUSH_IDENTIFIER_LVALUE; *p++ = F_IDENTIFIER; *p = i + num_virtual_variables; } $$.type = NV_VARIABLE(i)->flags & TYPE_MOD_MASK; } CURRENT_PROGRAM_SIZE = current + 2; if (i == -1) $$.type = TYPE_ANY; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | L_LOCAL { /* Access a local variable */ mp_uint current; bytecode_p p; %line $$.start = current = CURRENT_PROGRAM_SIZE; $$.code = F_PUSH_LOCAL_VARIABLE_LVALUE; $$.end = 0; if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %lu\n", current+2); YYACCEPT; } p = PROGRAM_BLOCK + current; *p++ = F_LOCAL; *p = $1; CURRENT_PROGRAM_SIZE = current + 2; $$.type = type_of_locals[$1]; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr4 index_expr %prec '[' { %line /* Generate (R)INDEX/PUSH_(R)INDEXED_LVALUE */ $$.start = $1.start; $$.end = CURRENT_PROGRAM_SIZE; if ($2.inst == F_INDEX) { $$.code = F_PUSH_INDEXED_LVALUE; ins_byte(F_INDEX); } else { $$.code = F_PUSH_RINDEXED_LVALUE; ins_byte(F_RINDEX); } if ($2.type1 & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); /* Check and compute the types */ if (exact_types) { int type; type = $1.type; if (type & TYPE_MOD_POINTER) { $$.type = type & ~TYPE_MOD_POINTER; } else if (type == TYPE_MAPPING && $2.inst == F_INDEX) { $2.type1 = TYPE_ANY; $$.type = TYPE_ANY; } else switch (type) { default: type_error("Bad type to indexed value", type); /* FALLTHROUGH */ case TYPE_ANY: if ($2.inst == F_INDEX) $2.type1 = TYPE_ANY; $$.type = TYPE_ANY; break; case TYPE_STRING: $$.type = TYPE_NUMBER; break; } if (!BASIC_TYPE($2.type1, TYPE_NUMBER)) type_error("Bad type of index", $2.type1); } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr4 '[' expr0 ',' expr0 ']' %prec '[' { %line /* Generate MAP_INDEX/PUSH_INDEXED_MAP_LVALUE */ $$.start = $1.start; $$.end = CURRENT_PROGRAM_SIZE; $$.code = F_PUSH_INDEXED_MAP_LVALUE; $$.type = TYPE_ANY; ins_byte(F_MAP_INDEX); if ($3.type & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); /* Check and compute types */ if (exact_types) { vartype_t type; type = $1.type; if (type != TYPE_ANY && type != TYPE_MAPPING) { type_error("Bad type to indexed value", type); } type = $5.type; if (type != TYPE_ANY && type != TYPE_NUMBER) type_error("Bad type of index", type); } } ; /* expr4 */ lvalue: /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ name_lvalue /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr4 index_expr %prec '[' { /* Generate/add an (R)INDEX_LVALUE */ bytecode_p p, q; p_int start, current; %line start = $1.start; current = CURRENT_PROGRAM_SIZE; p = PROGRAM_BLOCK; q = yalloc(current-start+2); /* assign uses an extra byte */ /* First change the rvalue 'expr4' into an lvalue. */ if ($1.code >= 0) { p_int end, start2; if ( 0 != (end = $1.end) ) { /* Multibyte instruction */ start2 = end+1; if ($1.code == F_PUSH_IDENTIFIER16_LVALUE) p[start] = $1.code; else p[end] = $1.code; memcpy(q, p + start2, current - start2); memcpy(q + current - start2, p + start, start2 - start); q[current - start] = ($2.inst == F_INDEX) ? F_INDEX_LVALUE : F_RINDEX_LVALUE; } else { /* Simple relocation/insertion */ bytecode_t c; start2 = start + 2; c = p[start+1]; memcpy(q, p + start2, current - start2); p = q + current - start2; *p++ = $1.code; *p++ = c; *p = ($2.inst == F_INDEX) ? F_INDEX_LVALUE : F_RINDEX_LVALUE; } } else { /* We can just copy the instruction block * and add a PUSH_(R)INDEXED_LVALUE */ memcpy(q, p + start, current - start); q[current - start] = ($2.inst == F_INDEX) ? F_PUSH_INDEXED_LVALUE : F_PUSH_RINDEXED_LVALUE; } /* This is what we return */ $$.length = current + 1 - start; $$.u.p = q; CURRENT_PROGRAM_SIZE = start; last_expression = -1; if ($2.type1 & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); /* Check and compute types */ if (exact_types) { vartype_t type; type = $1.type; if (type & TYPE_MOD_POINTER) { $$.type = type & ~TYPE_MOD_POINTER; } else if (type == TYPE_MAPPING && $2.inst == F_INDEX) { $2.type1 = TYPE_ANY; $$.type = TYPE_ANY; } else switch (type) { default: type_error("Bad type to indexed lvalue", type); /* FALLTHROUGH */ case TYPE_ANY: if ($2.inst == F_INDEX) $2.type1 = TYPE_ANY; $$.type = TYPE_ANY; break; case TYPE_STRING: $$.type = TYPE_NUMBER; break; } if (!BASIC_TYPE($2.type1, TYPE_NUMBER)) type_error("Bad type of index", $2.type1); } else { $$.type = TYPE_ANY; } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr4 '[' expr0 ',' expr0 ']' %prec '[' { /* Generate/add an PUSH_INDEXED_MAP_LVALUE */ bytecode_p p, q; p_int start, current; %line /* Well, just generate the code: expr4 must be * a mapping, or a runtime error will occur. */ start = $1.start; current = CURRENT_PROGRAM_SIZE; p = PROGRAM_BLOCK; q = yalloc(current-start+2); /* assign uses an extra byte */ memcpy(q, p + start, current - start); q[current - start] = F_PUSH_INDEXED_MAP_LVALUE; $$.length = current + 1 - start; $$.u.p = q; $$.type = TYPE_ANY; CURRENT_PROGRAM_SIZE = start; last_expression = -1; if ($3.type & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); /* Check and compute types */ if (exact_types) { vartype_t type; type = $1.type; if (type != TYPE_ANY && type != TYPE_MAPPING) { type_error("Bad type to indexed value", type); } type = $5.type; if (type != TYPE_ANY && type != TYPE_NUMBER) type_error("Bad type of index", type); } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr4 index_range %prec '[' { /* RANGE_LVALUE generation */ bytecode_p p, q; p_int start, current; int indexing_code; %line switch ($2.inst) { case F_RANGE: indexing_code = F_RANGE_LVALUE; break; case F_NR_RANGE: indexing_code = F_NR_RANGE_LVALUE; break; case F_RN_RANGE: indexing_code = F_RN_RANGE_LVALUE; break; case F_RR_RANGE: indexing_code = F_RR_RANGE_LVALUE; break; case F_NX_RANGE: indexing_code = F_NX_RANGE_LVALUE; break; case F_RX_RANGE: indexing_code = F_RX_RANGE_LVALUE; break; default: error("Unsupported range type %d %s\n" , $2.inst, get_f_name($2.inst)); } start = $1.start; current = CURRENT_PROGRAM_SIZE; p = PROGRAM_BLOCK; q = yalloc(current-start+3); /* Change the expr4 into an lvalue */ if ($1.code < 0) { yyerror("Need lvalue for range lvalue."); } else { p_int end, start2; if ( 0 != (end = $1.end) ) { /* Multibyte instruction */ start2 = end+1; if ($1.code == F_PUSH_IDENTIFIER16_LVALUE) { p[start] = $1.code; } else { p[end] = $1.code; } } else { /* Simple relocation/replacement */ start2 = start+2; p[start] = $1.code; } /* Do the actual relocation */ memcpy(q, p + start2, current - start2); memcpy(q + current - start2, p + start, start2 - start); current -= start; /* Insert the indexing code */ if (indexing_code > 0xff) { q[current++] = indexing_code >> F_ESCAPE_BITS; } q[current] = indexing_code; } /* This is what we return */ $$.length = current + 1; $$.u.p = q; CURRENT_PROGRAM_SIZE = start; last_expression = -1; /* Compute and check the types */ if (exact_types) { vartype_t type; $$.type = type = $1.type; if ((type & TYPE_MOD_POINTER) == 0 && type != TYPE_ANY && type != TYPE_STRING) { type_error("Bad type of argument used for range", type); $$.type = TYPE_ANY; } type = $2.type1; if (type != TYPE_ANY && type != TYPE_NUMBER) type_error("Bad type of index", type); type = $2.type2; if (type != TYPE_ANY && type != TYPE_NUMBER) type_error("Bad type of index", type); } } ; /* lvalue */ name_lvalue: /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ L_IDENTIFIER { /* Generate the lvalue for a global */ int i; %line $$.length = 0; i = verify_declared($1); if (i & VIRTUAL_VAR_TAG) { $$.u.simple[0] = F_PUSH_VIRTUAL_VARIABLE_LVALUE; $$.u.simple[1] = i; $$.type = V_VARIABLE(i)->flags & TYPE_MOD_MASK; if (i == -1) $$.type = TYPE_ANY; } else { if ((i + num_virtual_variables) & ~0xff) { bytecode_p q; q = yalloc(4); /* assign uses an extra byte */ $$.length = 3; $$.u.p = q; q[0] = F_PUSH_IDENTIFIER16_LVALUE; PUT_SHORT(q+1, i + num_virtual_variables); $$.type = NV_VARIABLE(i)->flags & TYPE_MOD_MASK; } else { $$.u.simple[0] = F_PUSH_IDENTIFIER_LVALUE; $$.u.simple[1] = i + num_virtual_variables; } $$.type = NV_VARIABLE(i)->flags & TYPE_MOD_MASK; } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | L_LOCAL { %line /* Generate the lvalue for a local */ $$.u.simple[0] = F_PUSH_LOCAL_VARIABLE_LVALUE; $$.u.simple[1] = $1; $$.length = 0; $$.type = type_of_locals[$1]; } ; /* name_lvalue */ local_name_lvalue: basic_type new_local_name { $$ = $2; } ; /* local_name_lvalue */ /* The following rules are used to parse and compile the various * forms of array indexing/ranging operations. * They used at various places in the rules of expr0, expr4 and lvalue. */ index_expr : '[' expr0 ']' { $$.inst = F_INDEX; $$.start = $2.start; $$.end = $2.end; $$.type1 = $2.type; } | '[' '<' expr0 ']' { $$.inst = F_RINDEX; $$.start = $3.start; $$.end = $3.end; $$.type1 = $3.type; } ; /* index_expr */ index_range : /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ '[' L_RANGE expr0 ']' { /* Simulate an expression yielding 0 for the lower bound. * We pretend that it's part of the upper bound expr. */ p_int current; p_int length; bytecode_p mark, p; current = CURRENT_PROGRAM_SIZE; if (!realloc_a_program(1)) { yyerrorf("Out of memory: program size %lu\n", current+1); YYACCEPT; } mark = PROGRAM_BLOCK + $3.start; p = PROGRAM_BLOCK + current; length = current - $3.start; for( ; --length >= 0; p--) PUT_CODE(p, GET_CODE(p-1)); STORE_CODE(mark, F_CONST0); CURRENT_PROGRAM_SIZE++; $3.end++; /* Return the data */ $$.inst = F_RANGE; $$.start = $3.start; $$.end = $3.end; $$.type1 = TYPE_NUMBER; $$.type2 = $3.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' L_RANGE '<' expr0 ']' { /* Simulate an expression yielding 0 for the lower bound. * We pretend that it's part of the upper bound expr. */ p_int current; p_int length; bytecode_p mark, p; current = CURRENT_PROGRAM_SIZE; if (!realloc_a_program(1)) { yyerrorf("Out of memory: program size %lu\n", current+1); YYACCEPT; } mark = PROGRAM_BLOCK + $4.start; p = PROGRAM_BLOCK + current; length = current - $4.start; for( ; --length >= 0; p--) PUT_CODE(p, GET_CODE(p-1)); STORE_CODE(mark, F_CONST0); CURRENT_PROGRAM_SIZE++; $4.end++; /* Return the data */ $$.inst = F_NR_RANGE; $$.start = $4.start; $$.end = $4.end; $$.type1 = TYPE_NUMBER; $$.type2 = $4.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' expr0 L_RANGE expr0 ']' { $$.inst = F_RANGE; $$.start = $2.start; $$.end = $4.end; $$.type1 = $2.type; $$.type2 = $4.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' expr0 L_RANGE '<' expr0 ']' { $$.inst = F_NR_RANGE; $$.start = $2.start; $$.end = $5.end; $$.type1 = $2.type; $$.type2 = $5.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' '<' expr0 L_RANGE expr0 ']' { $$.inst = F_RN_RANGE; $$.start = $3.start; $$.end = $5.end; $$.type1 = $3.type; $$.type2 = $5.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' '<' expr0 L_RANGE '<' expr0 ']' { $$.inst = F_RR_RANGE; $$.start = $3.start; $$.end = $6.end; $$.type1 = $3.type; $$.type2 = $6.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' expr0 L_RANGE ']' { $$.inst = F_NX_RANGE; $$.start = $2.start; $$.end = $2.end; $$.type1 = $2.type; $$.type2 = TYPE_NUMBER; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' '<' expr0 L_RANGE ']' { /* Simulate an expression yielding <1 for the upper bound. * We pretend that it's part of the lower bound expr. */ $$.inst = F_RX_RANGE; $$.start = $3.start; $$.end = $3.end; $$.type1 = $3.type; $$.type2 = TYPE_NUMBER; } ; /* index_range */ /* The following rules are used to construct array and * mapping literals in expr4, and argument lists in function calls. * Besides compiling the values, the rules count the number * of values generated and add the types to the arg_types[]. */ expr_list: /* empty */ { $$ = 0; } | expr_list2 { $$ = $1; } | expr_list2 ',' { $$ = $1; } /* Allow a terminating comma */ ; /* expr_list */ expr_list2: expr0 { $$ = 1; add_arg_type($1.type); } | expr_list2 ',' expr0 { $$ = $1 + 1; add_arg_type($3.type); } ; /* expr_list2 */ expr_list3: /* empty */ { $$ = 0; } | expr0 { $$ = 1; add_arg_type($1.type); } | expr_list2 ',' expr0 { $$ = $1 + 1; add_arg_type($3.type); } ; /* expr_list3 */ m_expr_list: /* empty */ { $$[0] = 0; $$[1]= 1; } | m_expr_list2 /* { $$ = $1; } */ | m_expr_list2 ',' /* { $$ = $1; } Allow a terminating comma */ | expr_list2 { $$[0] = $1; $$[1] = 0; } | expr_list2 ',' { $$[0] = $1; $$[1] = 0; } ; /* m_expr_list */ m_expr_list2: expr0 m_expr_values { $$[0] = 1 + $2; $$[1] = $2; add_arg_type($1.type); /* order doesn't matter */ } | m_expr_list2 ',' expr0 m_expr_values { if ($1[1] != $4) { yyerror("Inconsistent number of values in mapping literal"); } $$[0] = $1[0] + 1 + $4; $$[1] = $1[1]; add_arg_type($3.type); } ; /* m_expr_list2 */ m_expr_values: ':' expr0 { $$ = 1; add_arg_type($2.type); } | m_expr_values ';' expr0 { $$ = $1 + 1; add_arg_type($3.type); } ; /* m_expr_values */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* Function calls and inline functions. */ function_call: /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ function_name { %line /* The generic function call by name. * * It may be an ordinary intra-object function call. * But, if the function is not defined, then it might be a call * to a simul_efun. If it is, then we make it a simul_efun or * even call_other(), of which the latter requires the function * name as argument. * It might even be a real efun. */ ident_t *real_name; /* Save the (simple) state */ $<function_call_head>$.start = CURRENT_PROGRAM_SIZE; $<function_call_head>$.simul_efun = -1; real_name = $1.real; /* we rely on the fact that $1.real->type is either * I_TYPE_UNKNOWN or I_TYPE_GLOBAL here. All others are filtered * by the lexical analysis. */ if (real_name->type == I_TYPE_UNKNOWN) { /* prevent freeing by exotic name clashes */ /* also makes life easier below */ real_name->type = I_TYPE_GLOBAL; real_name->u.global.function = I_GLOBAL_FUNCTION_VAR; real_name->u.global.variable = I_GLOBAL_VARIABLE_OTHER; real_name->u.global.efun = I_GLOBAL_EFUN_OTHER; real_name->u.global.sim_efun = I_GLOBAL_SEFUN_OTHER; real_name->next_all = all_globals; all_globals = real_name; } else if (!$1.super && real_name->u.global.function < 0 && real_name->u.global.sim_efun >= 0) { /* It's a real simul-efun */ $<function_call_head>$.simul_efun = real_name->u.global.sim_efun; if (real_name->u.global.sim_efun & ~0xff) { /* The simul-efun has to be called by name: * prepare the extra args for the call_other */ PREPARE_INSERT(6) char *p; p = ref_string(real_name->name); add_byte(F_STRING); add_short(store_prog_string( make_shared_string(query_simul_efun_file_name()))); add_byte(F_STRING); add_short(store_prog_string(p)); CURRENT_PROGRAM_SIZE += 6; } } } '(' expr_list3 ')' { /* We got the arguments. Now we have to generate the * proper instructions to call the function. */ %line PREPARE_INSERT(6) int f = 0; /* Function index */ Bool efun_override; /* TRUE on explicite efun calls */ int simul_efun; vartype_t *arg_types = NULL; /* Argtypes from the program */ int first_arg; /* Startindex in arg_types[] */ efun_override = ($1.super && strcmp($1.super, "efun") == 0); $$.start = $<function_call_head>2.start; $$.code = -1; if ( $4 >= 0xff ) /* since num_arg is encoded in just one byte, and 0xff * is taken for SIMUL_EFUN_VARARG */ yyerrorf("Too many arguments to function"); if ( (simul_efun = $<function_call_head>2.simul_efun) >= 0) { /* SIMUL EFUN */ function_t *funp; funp = &simul_efunp[simul_efun]; if (funp->num_arg != SIMUL_EFUN_VARARGS && !(funp->flags & TYPE_MOD_XVARARGS)) { if ($4 > funp->num_arg) yyerrorf("Too many arguments to simul_efun %s" , funp->name); if ($4 < funp->num_arg) { if (pragma_pedantic) yyerrorf("Missing arguments to simul_efun %s" , funp->name); else yywarnf("Missing arguments to simul_efun %s" , funp->name); } } if (simul_efun & ~0xff) { /* call-other: the number of arguments will be * corrected at runtime. */ add_byte(F_CALL_OTHER); add_byte($4 + 2); CURRENT_PROGRAM_SIZE += 2; } else { /* Direct call: we have to add the missing arguments. * resp. encode the number of arguments passed. */ if (funp->num_arg != SIMUL_EFUN_VARARGS && !(funp->flags & TYPE_MOD_XVARARGS)) { int i; i = funp->num_arg - $4; if (funp->flags & TYPE_MOD_XVARARGS) i--; /* Last argument may be omitted */ if (i > 4) { if (!realloc_a_program(i+2)) { yyerrorf("Out of memory: program size %lu\n" , mem_block[A_PROGRAM].current_size + i+2); YYACCEPT; } __PREPARE_INSERT__p = PROGRAM_BLOCK + CURRENT_PROGRAM_SIZE; } CURRENT_PROGRAM_SIZE += i; while ( --i >= 0 ) { add_byte(F_CONST0); } } add_byte(F_SIMUL_EFUN); add_byte(simul_efun); if (funp->num_arg == SIMUL_EFUN_VARARGS || funp->flags & TYPE_MOD_XVARARGS) { add_byte($4); CURRENT_PROGRAM_SIZE += 3; } else { CURRENT_PROGRAM_SIZE += 2; } } $$.type = funp->type & TYPE_MOD_MASK; } /* if (simul-efun) */ else if ($1.super ? !efun_override : (f = defined_function($1.real)) >= 0 ) { /* LFUN or INHERITED LFUN */ function_t *funp; function_t inherited_function; if ($1.super) { /* Inherited lfun: check its existance and call it */ program_t *super_prog; int ix; ix = insert_inherited( $1.super, $1.real->name , &super_prog, &inherited_function , $4, (bytecode_p)__PREPARE_INSERT__p ); if ($1.real->type == I_TYPE_UNKNOWN) { free_shared_identifier($1.real); } if (ix < 0) { switch(ix) { case INHERITED_NOT_FOUND: yyerror("function not defined by inheritance as specified"); break; case INHERITED_WILDCARDED_ARGS: yyerror("wildcarded call to inherited function can't pass arguments"); break; case INHERITED_WILDCARDED_NOT_FOUND: /* Not an error, but we can't do argument * checks either. */ break; default: fatal("Unknown return code %d from insert_inherited()\n", ix); break; } $$.type = TYPE_ANY; if ($1.super) yfree($1.super); pop_arg_stack($4); /* Argument types no longer needed */ break; /* TODO: this assumes a switch by byacc */ } /* Find the argument types */ if (super_prog && NULL != (arg_types = super_prog->argument_types)) { first_arg = super_prog->type_start[ix]; } else { first_arg = INDEX_START_NONE; } funp = &inherited_function; } else { /* Normal lfun in this program */ add_byte(F_CALL_FUNCTION_BY_ADDRESS); add_short(f); funp = FUNCTION(f); arg_types = (vartype_t *)mem_block[A_ARGUMENT_TYPES].block; first_arg = ARGUMENT_INDEX(f); add_byte($4); /* Actual number of arguments */ CURRENT_PROGRAM_SIZE += 4; } /* Verify that the function has been defined already. * For inherited functions this is a no-brainer. */ if (funp->flags & (NAME_UNDEFINED|NAME_HIDDEN)) { if ( !(funp->flags & (NAME_PROTOTYPE|NAME_INHERITED)) && exact_types ) { yyerrorf("Function %.50s undefined", funp->name); } else if ((funp->flags & (NAME_UNDEFINED|NAME_PROTOTYPE|NAME_HIDDEN)) == NAME_HIDDEN) { yyerrorf("Function %.50s is private", funp->name); } } $$.type = funp->type & TYPE_MOD_MASK; /* Result type */ /* Check number of arguments. */ if (funp->num_arg != $4 && !(funp->flags & TYPE_MOD_VARARGS) && (first_arg != INDEX_START_NONE) && exact_types) { if (funp->num_arg-1 > $4 || !(funp->flags & TYPE_MOD_XVARARGS)) yyerrorf("Wrong number of arguments to %.60s", $1.real->name); } /* Check the argument types. */ if (exact_types && first_arg != INDEX_START_NONE) { int i; vartype_t *argp; int num_arg, anum_arg; if ( 0 != (num_arg = funp->num_arg) ) { /* There are arguments to check */ int argno; /* Argument number for error message */ if (funp->flags & TYPE_MOD_XVARARGS) num_arg--; /* last argument is checked separately */ if (num_arg > (anum_arg = $4) ) num_arg = anum_arg; arg_types += first_arg; argp = get_argument_types_start(anum_arg); for (argno = 1, i = num_arg; --i >= 0; argno++) { fulltype_t tmp1, tmp2; tmp1 = *argp++ & TYPE_MOD_RMASK; tmp2 = *arg_types++ & TYPE_MOD_MASK; if (!REDEFINED_TYPE(tmp1, tmp2)) { yyerrorf("Bad type for argument %d of %s %s", argno, funp->name, get_two_types(tmp2, tmp1)); } } /* for (all args) */ if (funp->flags & TYPE_MOD_XVARARGS) { fulltype_t tmp1, tmp2; /* varargs argument is either a pointer type or mixed */ tmp2 = *arg_types & TYPE_MOD_MASK; tmp2 &= ~TYPE_MOD_POINTER; for (i = anum_arg - num_arg; --i >=0; ) { tmp1 = *argp++ & TYPE_MOD_RMASK; if (!MASKED_TYPE(tmp1,tmp2)) { yyerrorf("Bad type for argument %d of %s %s", anum_arg - i, funp->name, get_two_types(tmp2, tmp1)); } } } /* if (xvarargs) */ } /* if (has args) */ } /* if (check types) */ } /* if (inherited lfun) */ else if ( (f = lookup_predef($1.real)) != -1 ) { /* EFUN */ fulltype_t *argp; int min, max, def, num_arg; int f2; /* Get the information from the efun table */ min = instrs[f].min_arg; max = instrs[f].max_arg; def = instrs[f].Default; $$.type = instrs[f].ret_type; argp = &efun_arg_types[instrs[f].arg_index]; /* Warn if the efun is deprecated */ if (pragma_warn_deprecated && instrs[f].deprecated != NULL) yywarnf("%s() is deprecated: %s" , instrs[f].name, instrs[f].deprecated); num_arg = $4; /* Check and/or complete number of arguments */ if (def && num_arg == min-1) { /* Default argument */ add_byte(def); CURRENT_PROGRAM_SIZE++; max--; min--; } else if (num_arg < min && ( (f2 = proxy_efun(f, num_arg)) < 0 || (f = f2, MY_FALSE) ) ) { /* Not enough args, and no proxy_efun to replace this */ yyerrorf("Too few arguments to %s", instrs[f].name); } else if (num_arg > max && max != -1) { yyerrorf("Too many arguments to %s", instrs[f].name); pop_arg_stack (num_arg - max); $4 -= num_arg - max; /* Don't forget this for the final pop */ num_arg = max; } /* Check the types of the arguments */ if (max != -1 && exact_types && num_arg) { int argn; vartype_t *aargp; aargp = get_argument_types_start(num_arg); /* Loop over all arguments and compare each given * type against all allowed types in efun_arg_types() */ for (argn = 0; argn < num_arg; argn++) { fulltype_t tmp1, tmp2; tmp1 = *aargp++ & TYPE_MOD_MASK; for (;;) { if ( !(tmp2 = *argp) ) { /* Possible types for this arg exhausted */ yyerrorf("Bad argument %d type to efun %s()", argn+1, instrs[f].name); break; } argp++; /* break if types are compatible; take care to * handle references correctly */ if (tmp1 == tmp2) break; if ((tmp1 & ~(TYPE_MOD_POINTER|TYPE_MOD_REFERENCE)) == TYPE_ANY) { if (tmp1 & TYPE_MOD_POINTER & ~tmp2) { if ((tmp2 & ~TYPE_MOD_REFERENCE) != TYPE_ANY) { continue; } } if ( !( (tmp1 ^ tmp2) & TYPE_MOD_REFERENCE) ) break; } else if ((tmp2 & ~(TYPE_MOD_POINTER|TYPE_MOD_REFERENCE)) == TYPE_ANY) { if (tmp2 & TYPE_MOD_POINTER & ~tmp1) continue; if ( !( (tmp1 ^ tmp2) & TYPE_MOD_REFERENCE) ) break; } } /* end for (efun_arg_types) */ /* Advance argp to point to the allowed argtypes * of the next arg. */ while(*argp++) NOOP; } /* for (all args) */ } /* if (check arguments) */ /* Alias for an efun? */ if (f > LAST_INSTRUCTION_CODE) f = efun_aliases[f-LAST_INSTRUCTION_CODE-1]; if (f > 255) { /* This efun needs a prefix byte */ add_byte(f >> F_ESCAPE_BITS); CURRENT_PROGRAM_SIZE++; } add_byte(f); CURRENT_PROGRAM_SIZE++; /* Only store number of arguments for instructions * that allowed a variable number. */ if (max != min) { add_byte($4);/* Number of actual arguments */ CURRENT_PROGRAM_SIZE++; } /* If the efun doesn't return a value, fake a 0 */ if ( instrs[f].ret_type == TYPE_VOID ) { last_expression = mem_block[A_PROGRAM].current_size; add_byte(F_CONST0); CURRENT_PROGRAM_SIZE++; } } /* efun */ else if (efun_override) { yyerrorf("Unknown efun: %s", $1.real->name); $$.type = TYPE_ANY; } else { /* There is no such function, but maybe it's defined later, * maybe it's resolved through (cross-)inheritance. * epilog() will take care of it. */ function_t *funp; f = define_new_function(MY_FALSE, $1.real, 0, 0, 0, NAME_UNDEFINED, TYPE_UNKNOWN ); add_byte(F_CALL_FUNCTION_BY_ADDRESS); add_short(f); add_byte($4); /* Number of actual arguments */ CURRENT_PROGRAM_SIZE += 4; funp = FUNCTION(f); if (exact_types) { yyerrorf("Undefined function '%.50s'", $1.real->name); } else if (pragma_pedantic) { yywarnf("Undefined function '%.50s'", $1.real->name); } $$.type = TYPE_ANY; /* Just a guess */ } if ($1.super) yfree($1.super); pop_arg_stack($4); /* Argument types no longer needed */ } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr4 L_ARROW { %line /* If call_other() has been replaced by a sefun, and * if we need to use F_CALL_OTHER to call it, we have * to insert additional code before the <expr4> already parsed. * Putting this block before the <expr4> yields a * faulty grammar. */ if (call_other_sefun >= 0 && call_other_sefun & ~0xff) { /* The simul-efun has to be called by name: * insert the extra args for the call_other */ char *p, *q; p_int left; /* Move the generated code forward by 6 */ p = mem_block[A_PROGRAM].block + CURRENT_PROGRAM_SIZE; q = p + 6; for (left = CURRENT_PROGRAM_SIZE - $1.start ; left > 0 ; left--) { *--q = *--p; } /* p now points to program[$1.start]. * Store the first two call-other args there. */ p[0] = F_STRING; upd_short($1.start+1, store_prog_string( make_shared_string(query_simul_efun_file_name()))); p[3] = F_STRING; upd_short($1.start+4, store_prog_string(ref_string(STR_CALL_OTHER))); CURRENT_PROGRAM_SIZE += 6; } } call_other_name %prec L_ARROW { %line int string_number; char *p; /* If we received a string, it's a constant call. */ p = $4; if (p) { /* Push the function name (the expr4 is already on the stack */ string_number = store_prog_string(p); if (string_number <= 0x0ff ) { ins_byte(F_CSTRING0); ins_byte(string_number); } else if ( string_number <= 0x1ff ) { ins_byte(F_CSTRING1); ins_byte(string_number); } else if ( string_number <= 0x2ff ) { ins_byte(F_CSTRING2); ins_byte(string_number); } else if ( string_number <= 0x3ff ) { ins_byte(F_CSTRING3); ins_byte(string_number); } else { ins_byte(F_STRING); ins_short(string_number); } } /* if (p) */ /* otherwise the name was given by an expression for which * the code and value have been already generated. */ } '(' expr_list3 ')' { /* Now generate the CALL_OTHER resp. the SIMUL_EFUN instruction. */ if (call_other_sefun >= 0) { /* SIMUL EFUN */ PREPARE_INSERT(6) function_t *funp; int num_arg; num_arg = $7 + 2; /* Don't forget the obj and the fun! */ funp = &simul_efunp[call_other_sefun]; if (num_arg > funp->num_arg && !(funp->flags & TYPE_MOD_XVARARGS)) yyerrorf("Too many arguments to simul_efun %s", funp->name); if (call_other_sefun & ~0xff) { /* call-other: the number of arguments will be * corrected at runtime. */ add_byte(F_CALL_OTHER); add_byte(num_arg + 2); CURRENT_PROGRAM_SIZE += 2; } else { /* Direct call: we have to add the missing arguments. * resp. encode the number of arguments passed. */ if (funp->num_arg != SIMUL_EFUN_VARARGS && !(funp->flags & TYPE_MOD_XVARARGS)) { int i; i = funp->num_arg - num_arg; if (funp->flags & TYPE_MOD_XVARARGS) i--; /* Last argument may be omitted */ if (i > 4) { if (!realloc_a_program(i+2)) { yyerrorf("Out of memory: program size %lu\n" , mem_block[A_PROGRAM].current_size + i+2); YYACCEPT; } __PREPARE_INSERT__p = PROGRAM_BLOCK + CURRENT_PROGRAM_SIZE; } CURRENT_PROGRAM_SIZE += i; while ( --i >= 0 ) { add_byte(F_CONST0); } } add_byte(F_SIMUL_EFUN); add_byte(call_other_sefun); if (funp->num_arg == SIMUL_EFUN_VARARGS || funp->flags & TYPE_MOD_XVARARGS) { add_byte(num_arg); CURRENT_PROGRAM_SIZE += 3; } else CURRENT_PROGRAM_SIZE += 2; } $$.type = funp->type & TYPE_MOD_MASK; } else /* true call_other */ { ins_byte(F_CALL_OTHER); ins_byte($7 + 2); $$.type = instrs[F_CALL_OTHER].ret_type; } $$.code = -1; $$.start = $1.start; pop_arg_stack($7); /* No good need of these arguments because we don't * know what we are going to call. */ } ; /* function_call */ call_other_name: L_IDENTIFIER { char *p; /* Extract the string from the ident structure */ p = ref_string($1->name); if ($1->type == I_TYPE_UNKNOWN) free_shared_identifier($1); $$ = p; } | L_LOCAL { ident_t *p; /* First, find the declaration of this local */ for (p = all_locals; p != NULL; p = p->next_all) { if (p->u.local.num == $1) { /* We found the identifier. */ break; } } if (p) $$ = ref_string(p->name); else fatal("Local variable %ld vanished.\n", (long)$1); } | L_STRING L_STRING { fatal("presence of rule should prevent its reduction"); } | L_STRING { $$ = last_lex_string; /* Adopt the reference */ last_lex_string = NULL; } | '(' expr0 ')' { $$ = NULL; if ($2.type != TYPE_STRING && (pragma_strict_types != PRAGMA_WEAK_TYPES || $2.type != TYPE_UNKNOWN) && $2.type != TYPE_ANY) type_error("Illegal type for lfun name", (p_int)$2.type); } ; /* call_other_name */ function_name: L_IDENTIFIER { $$.super = NULL; $$.real = $1; } | L_LOCAL { ident_t *lvar = lookup_local($1); ident_t *fun = find_shared_identifier(lvar->name, I_TYPE_UNKNOWN, 0); /* Search the inferior list for this identifier for a global * (function) definition. */ while (fun && fun->type > I_TYPE_GLOBAL) fun = fun->inferior; if (!fun || fun->type != I_TYPE_GLOBAL) { yyerrorf("Undefined function '%.50s'\n", lvar->name); YYACCEPT; } $$.super = NULL; $$.real = fun; } | L_COLON_COLON L_IDENTIFIER { *($$.super = yalloc(1)) = '\0'; $$.real = $2; } | L_COLON_COLON L_LOCAL { ident_t *lvar = lookup_local($2); *($$.super = yalloc(1)) = '\0'; $$.real = lvar; } | anchestor L_COLON_COLON L_LOCAL { %line ident_t *lvar = lookup_local($3); /* Attempt to call an efun directly even though there * is a nomask simul-efun for it? */ if ( !strcmp($1, "efun") && lvar->type == I_TYPE_GLOBAL && lvar->u.global.sim_efun >= 0 && simul_efunp[lvar->u.global.sim_efun].flags & TYPE_MOD_NO_MASK && master_ob && (!max_eval_cost || eval_cost < max_eval_cost) ) { /* Yup, check it with a privilege violation. * If it's denied, ignore the "efun::" qualifier. */ svalue_t *res; push_volatile_string("nomask simul_efun"); push_volatile_string(current_file); push_volatile_string(lvar->name); res = apply_master(STR_PRIVILEGE, 3); if (!res || res->type != T_NUMBER || res->u.number < 0) { yyerrorf("Privilege violation: nomask simul_efun %s" , lvar->name); yfree($1); $$.super = NULL; } else if (!res->u.number) { yfree($1); $$.super = NULL; } else { $$.super = $1; } } else if (max_eval_cost && eval_cost >= max_eval_cost) { yyerrorf("Can't call master::%s for " "'nomask simul_efun %s': eval cost too big" , STR_PRIVILEGE, lvar->name); yfree($1); $$.super = NULL; } else /* the qualifier is ok */ $$.super = $1; $$.real = lvar; /* and don't forget the function ident */ } | anchestor L_COLON_COLON L_IDENTIFIER { %line /* Attempt to call an efun directly even though there * is a nomask simul-efun for it? */ if ( !strcmp($1, "efun") && $3->type == I_TYPE_GLOBAL && $3->u.global.sim_efun >= 0 && simul_efunp[$3->u.global.sim_efun].flags & TYPE_MOD_NO_MASK && master_ob && (!max_eval_cost || eval_cost < max_eval_cost) ) { /* Yup, check it with a privilege violation. * If it's denied, ignore the "efun::" qualifier. */ svalue_t *res; push_volatile_string("nomask simul_efun"); push_volatile_string(current_file); push_volatile_string($3->name); res = apply_master(STR_PRIVILEGE, 3); if (!res || res->type != T_NUMBER || res->u.number < 0) { yyerrorf("Privilege violation: nomask simul_efun %s" , $3->name); yfree($1); $$.super = NULL; } else if (!res->u.number) { yfree($1); $$.super = NULL; } else { $$.super = $1; } } else if (max_eval_cost && eval_cost >= max_eval_cost) { yyerrorf("Can't call master::%s for " "'nomask simul_efun %s': eval cost too big" , STR_PRIVILEGE, $3->name); yfree($1); $$.super = NULL; } else /* the qualifier is ok */ $$.super = $1; $$.real = $3; /* and don't forget the function ident */ } ; /* function_name */ anchestor: L_IDENTIFIER { $$ = ystring_copy($1->name); if ($1->type == I_TYPE_UNKNOWN) free_shared_identifier($1); } | L_STRING L_STRING { fatal("presence of rule should prevent its reduction"); } | L_STRING { $$ = ystring_copy(last_lex_string); free_string(last_lex_string); last_lex_string = NULL; } ; /* anchestor */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* The inline function expression. * * This expression synthesizes a prototype for the inline function * and yields a closure-lrvalue suitable for expr4. * The function definition will be provided by the lexer at the next * opportunity. */ inline_fun: L_INLINE_FUN { /* Synthesize the prototype of the inline function * Since we have to declare the function arguments for that, * first save the existing locals. */ ident_t * save_all_locals; int save_current_number_of_locals; int save_max_number_of_locals; int save_tol[10], save_ftol[10]; char name[3]; int num, i; /* Save the old locals information */ save_all_locals = all_locals; save_current_number_of_locals = current_number_of_locals; save_max_number_of_locals = max_number_of_locals; /* Simulate 'no locals' */ all_locals = NULL; current_number_of_locals = 0; max_number_of_locals = 0; use_local_scopes = MY_TRUE; enter_block_scope(); /* Declare the 9 parameters (saving the types of the old ones) */ name[0] = '$'; name[2] = '\0'; for (i = 0; i < 9; i++) { save_tol[i] = type_of_locals[i]; save_ftol[i] = full_type_of_locals[i]; name[1] = (char)('1' + i); add_local_name(make_shared_identifier( name, I_TYPE_UNKNOWN , block_depth) , TYPE_ANY, block_depth); } /* Declare the function */ num = define_new_function(MY_FALSE, /* id */ $1, 9, 0, 0 , NAME_UNDEFINED|NAME_PROTOTYPE , TYPE_UNKNOWN|TYPE_MOD_VARARGS|TYPE_MOD_PRIVATE ); /* Restore the old locals information */ leave_block_scope(); use_local_scopes = pragma_use_local_scopes; all_locals = save_all_locals; current_number_of_locals = save_current_number_of_locals; max_number_of_locals = save_max_number_of_locals; for (i = 0; i < 9; i++) { type_of_locals[i] = save_tol[i]; full_type_of_locals[i] = save_ftol[i]; } /* Insert the call to the lfun closure */ $$.start = CURRENT_PROGRAM_SIZE; $$.code = -1; ins_byte(F_CLOSURE); ins_short(num); $$.type = TYPE_CLOSURE; } ; /* inline_fun */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* The catch()-statement */ catch: L_CATCH { $<address>$ = CURRENT_PROGRAM_SIZE; ins_byte(F_CATCH); ins_byte(0); } '(' comma_expr opt_catch_mods ')' { %line p_int start, offset; ins_f_code(F_END_CATCH); /* Get the address of the CATCH instruction */ start = $<address>2; /* Modify the instruction if necessary */ if ($5) { bytecode_p p; p = PROGRAM_BLOCK + start; *p = F_CATCH_NO_LOG; } /* Update the offset field of the CATCH instruction */ offset = CURRENT_PROGRAM_SIZE - (start + 2); if (offset >= 0x100) { /* Too big offset, change * * CATCH l * <expr> * l: END_CATCH * * to * * CATCH l0 * BRANCH l1 * l0: LBRANCH l2 * l1: <expr> * l2: END_CATCH */ int i; bytecode_p p; if (!realloc_a_program(5)) { yyerrorf("Out of memory: program size %lu\n" , CURRENT_PROGRAM_SIZE + 5); YYACCEPT; } CURRENT_PROGRAM_SIZE += 5; p = PROGRAM_BLOCK + CURRENT_PROGRAM_SIZE - 1; for( i = offset; --i >= 0; --p ) *p = p[-5]; p[-5] = 2; p[-4] = F_BRANCH ; p[-3] = 3; p[-2] = F_LBRANCH; upd_short(start + 5, offset+2); if (offset > 0x7ffd) yyerror("offset overflow"); } else { mem_block[A_PROGRAM].block[start+1] = offset; } $$.start = start; $$.type = TYPE_ANY; $$.code = -1; } ; /* catch */ opt_catch_mods : ';' L_IDENTIFIER { if (strcmp($2->name, "nolog")) yyerror("Expected keyword 'nolog' in catch()"); if ($2->type == I_TYPE_UNKNOWN) free_shared_identifier($2); $$ = 1; } | ';' L_LOCAL { ident_t *id; /* Find the ident structure for this local */ for (id = all_locals; id; id = id->next_all) if (id->u.local.num == $2) break; if (id && strcmp(id->name, "nolog")) yyerror("Expected keyword 'nolog' in catch()"); $$ = 1; } | /* empty */ { $$ = 0; } ; /* opt_catch_mods */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* sscanf() and parse_command() * * Both sscanf() and parse_command() are special in that they take * unmarked lvalues as arguments. Parsing the lvalue arguments * is the biggest part of the problem. * * TODO: Make a special efun-argument type "lvalue" so that this * TODO:: problem can be solved generically? */ sscanf: L_SSCANF note_start '(' expr0 ',' expr0 lvalue_list ')' { ins_byte(F_SSCANF); ins_byte($7 + 2); $$.start = $2.start; $$.type = TYPE_NUMBER; $$.code = -1; } ; /* sscanf */ %ifdef SUPPLY_PARSE_COMMAND parse_command: L_PARSE_COMMAND note_start '(' expr0 ',' expr0 ',' expr0 lvalue_list ')' { ins_byte(F_PARSE_COMMAND); ins_byte($9 + 3); $$.start = $2.start; $$.type = TYPE_NUMBER; $$.code = -1; } ; /* parse_command */ %endif /* SUPPLY_PARSE_COMMAND */ lvalue_list: /* empty */ { $$ = 0; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | lvalue_list ',' L_IDENTIFIER { /* Push the lvalue for a global variable */ int i; %line $$ = 1 + $1; i = verify_declared($3); if (i & VIRTUAL_VAR_TAG) { ins_byte(F_PUSH_VIRTUAL_VARIABLE_LVALUE); ins_byte(i); } else { if ((i + num_virtual_variables) & ~0xff) { ins_byte(F_PUSH_IDENTIFIER16_LVALUE); ins_short(i + num_virtual_variables); } else { ins_byte(F_PUSH_IDENTIFIER_LVALUE); ins_byte(i + num_virtual_variables); } } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | lvalue_list ',' L_LOCAL { %line /* Push the lvalue for a local variable */ $$ = 1 + $1; ins_byte(F_PUSH_LOCAL_VARIABLE_LVALUE); ins_byte($3); } | lvalue_list ',' expr4 index_expr { /* Generate a PROTECTED_(R)INDEX_LVALUE */ %line $$ = 1 + $1; if ($4.inst == F_INDEX) arrange_protected_lvalue($3.start, $3.code, $3.end, F_PROTECTED_INDEX_LVALUE ); else arrange_protected_lvalue($3.start, $3.code, $3.end, F_PROTECTED_RINDEX_LVALUE ); if ($4.type1 & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); if (exact_types) { vartype_t type; type = $3.type; if ( !(type & TYPE_MOD_POINTER) ) switch (type) { case TYPE_MAPPING: if ($4.inst == F_INDEX) { $4.type1 = TYPE_ANY; break; } /* FALLTHROUGH */ default: type_error("Bad type to indexed lvalue", type); /* FALLTHROUGH */ case TYPE_ANY: if ($4.inst == F_INDEX) $4.type1 = TYPE_ANY; $4.type1 = TYPE_ANY; break; case TYPE_STRING: break; } if (!BASIC_TYPE($4.type1, TYPE_NUMBER)) type_error("Bad type of index", $4.type1); } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | lvalue_list ',' expr4 '[' expr0 ',' expr0 ']' { %line /* Generate a PUSH_PROTECTED_INDEXED_MAP_LVALUE */ $$ = 1 + $1; ins_f_code(F_PUSH_PROTECTED_INDEXED_MAP_LVALUE); if ($5.type & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); /* Compute and check types */ if (exact_types) { vartype_t type; type = $3.type; if (type != TYPE_ANY && type != TYPE_MAPPING) { type_error("Bad type to indexed value", type); } type = $7.type; if (type != TYPE_ANY && type != TYPE_NUMBER) type_error("Bad type of index", type); } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | lvalue_list ',' expr4 index_range { %line /* Generate/add the PROTECTED range LVALUE */ int prot_op; switch ($4.inst) { case F_RANGE: prot_op = F_PROTECTED_RANGE_LVALUE; break; case F_NR_RANGE: prot_op = F_PROTECTED_NR_RANGE_LVALUE; break; case F_RN_RANGE: prot_op = F_PROTECTED_RN_RANGE_LVALUE; break; case F_RR_RANGE: prot_op = F_PROTECTED_RR_RANGE_LVALUE; break; case F_NX_RANGE: prot_op = F_PROTECTED_NX_RANGE_LVALUE; break; case F_RX_RANGE: prot_op = F_PROTECTED_RX_RANGE_LVALUE; break; default: error("Unsupported range type %d %s\n" , $4.inst, get_f_name($4.inst)); } $$ = 1 + $1; arrange_protected_lvalue($3.start, $3.code, $3.end , prot_op ); /* Compute and check types */ if (exact_types) { vartype_t type; type = $3.type; if ((type & TYPE_MOD_POINTER) == 0 && type != TYPE_ANY && type != TYPE_STRING) { type_error("Bad type of argument used for range", type); } type = $4.type1; if (type != TYPE_ANY && type != TYPE_NUMBER) type_error("Bad type of index", type); type = $4.type2; if (type != TYPE_ANY && type != TYPE_NUMBER) type_error("Bad type of index", type); } } ; /* lvalue_list */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ %ifndef INITIALIZATION_BY___INIT /* svalue_constant parses the constant variable initializers. * The generated value is stored in *currently_initialized. * * The constant initialization may contain function calls, however, * so far only the now rather useless order_alist() is supported. */ svalue_constant: constant_function_call | array_constant | constant { svalue_t *svp = currently_initialized; %line put_number(svp, $1); } | string_constant { svalue_t *svp = currently_initialized; %line put_string(svp, last_string_constant); last_string_constant = NULL; } | L_SYMBOL { svalue_t *svp = currently_initialized; %line svp->type = T_SYMBOL; svp->x.quotes = $1.quotes; svp->u.string = $1.name; } | L_QUOTED_AGGREGATE { $<initialized>$ = currently_initialized; } const_expr_list '}' ')' { svalue_t *svp = $<initialized>2; %line list_to_vector($3.length, svp); svp->type = T_QUOTED_ARRAY; svp->x.quotes = $1; } | float_constant { *currently_initialized = $1; } | L_CLOSURE { p_int ix; svalue_t *svp = currently_initialized; %line ix = $1.number; svp->type = T_CLOSURE; if (ix < CLOSURE_EFUN_OFFS) { /* Lfun closure */ lambda_t *l; l = xalloc(sizeof *l); l->ref = 1; l->ob = ref_object(current_object, "closure"); l->function.index = ix; svp->u.lambda = l; svp->x.closure_type = CLOSURE_PRELIMINARY; } else if (ix >= CLOSURE_SIMUL_EFUN_OFFS) { /* Sefun closure */ svp->x.closure_type = (short)ix; svp->u.ob = ref_object(current_object, "closure"); } else { /* Efun or operator closure */ if (pragma_warn_deprecated && instrs[ix - CLOSURE_EFUN_OFFS].deprecated != NULL) yywarnf("%s() is deprecated: %s" , instrs[ix - CLOSURE_EFUN_OFFS].name , instrs[ix - CLOSURE_EFUN_OFFS].deprecated ); svp->x.closure_type = (short)( instrs[ix - CLOSURE_EFUN_OFFS].Default == -1 ? ix + CLOSURE_OPERATOR-CLOSURE_EFUN : ix); svp->u.ob = ref_object(current_object, "closure"); } } | '(' '[' ']' ')' { svalue_t *svp = currently_initialized; %line put_mapping(svp, allocate_mapping(0, 1)); } | '(' '[' ':' constant ']' ')' { svalue_t *svp = currently_initialized; %line put_mapping(svp, allocate_mapping(0, $4)); } ; /* svalue_constant */ array_constant: '(' '{' { $<initialized>$ = currently_initialized; } const_expr_list '}' ')' { %line list_to_vector($4.length, $<initialized>3); } ; /* array_constant */ float_constant: L_FLOAT { %line STORE_DOUBLE_USED $$.type = T_FLOAT; STORE_DOUBLE(&$$, $1); } | '-' float_constant { %line STORE_DOUBLE_USED double d; d = -READ_DOUBLE(&$2); $$.type = T_FLOAT; STORE_DOUBLE(&$$, d); } ; /* float_constant */ const_expr_list: /* empty */ { $$.length = 0; } | const_expr_list2 { $$ = $1; } | const_expr_list2 ',' { $$ = $1; } /* Allow a trailing comma */ ; const_expr_list2: /* empty */ { /* The end of a const_list (or a const_list with just one * element) - this is the first rule reduced. * * Prepare the const list svalue to return the value. */ svalue_t *svp; const_list_svalue_t *clsv; %line clsv = xalloc(sizeof *clsv); svp = currently_initialized; svp->type = T_LVALUE; svp->u.lvalue = &clsv->head; clsv->head.type = T_ERROR_HANDLER; clsv->head.u.error_handler = free_const_list_svalue; clsv->list.next = NULL; clsv->list.val.type = T_INVALID; currently_initialized = &clsv->list.val; $<const_list>$.l = &clsv->list; $<const_list>$.length = 1; } svalue_constant %ifdef YACC_CANNOT_MIX_ANONYMOUS_WITH_DEFAULT { $$ = $<const_list>1; } %endif | const_expr_list2 ',' { /* One more element to the const_list */ const_list_t *l; %line l = xalloc(sizeof (const_list_t)); l->next = NULL; l->val.type = T_INVALID; currently_initialized = &l->val; $1.l->next = l; } svalue_constant { $$.l = $1.l->next; $$.length = $1.length+1; } ; /* const_expr_list2 */ constant_function_call: L_IDENTIFIER { /* I_TYPE_UNKNOWN must not be overrun by annother one, so * evaluate the identifier now. * We rely on the fact that $1.real->type is either * I_TYPE_UNKNOWN or I_TYPE_GLOBAL here. All others are filtered * by the lexical analysis. */ $<const_call_head>$.function = $1->u.global.efun; $<const_call_head>$.initialized = currently_initialized; if ($1->type == I_TYPE_UNKNOWN) { free_shared_identifier($1); $<number>$ = -1; } } '(' const_expr_list3 ')' { svalue_t *svp; const_list_svalue_t *list; %line svp = $<const_call_head>2.initialized; list = svp->u.const_list; switch($<const_call_head>2.function) { case F_ORDER_ALIST: { size_t i, listsize; vector_t *vec; if ($4.length == 1 && list->list.val.type == T_POINTER && VEC_SIZE(vec = list->list.val.u.vec) && vec->item[0].type == T_POINTER ) { xfree(list); } else { vec = list_to_vector($4.length, svp); } if ((listsize = VEC_SIZE(vec)) && vec->item[0].type == T_POINTER) { size_t keynum = VEC_SIZE(vec->item[0].u.vec); for (i = 0; i < VEC_SIZE(vec); i++) { if (vec->item[i].type != T_POINTER || VEC_SIZE(vec->item[i].u.vec) != keynum) { yyerrorf("bad data array %ld for alist", (long)i); free_array(vec); *svp = const0; break; } } } else { yyerror("missing argument for order_alist"); } if (listsize) { put_array(svp, order_alist(vec->item, listsize, 1)); } else { *svp = const0; } free_array(vec); break; } default: yyerror("Illegal function call in initialization"); free_svalue(svp); *svp = const0; } } ; /* constant_function_call */ const_expr_list3: /* empty */ { $$.length =0; $$.l = NULL; } | const_expr_list2 { $$ = $1; } ; /* const_expr_list3 */ %endif /* INITIALIZATION_BY___INIT */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ %% %line #ifdef __MWERKS__ # pragma warn_possunwant reset # pragma warn_implicitconv reset #endif /*=========================================================================*/ /*-------------------------------------------------------------------------*/ static Bool add_lvalue_code ( struct lvalue_s * lv, int instruction) /* Add the lvalue code held in * <lv> to the end of the program. * If <instruction> is not zero, it is the code for an instruction * to be added after the lvalue code. * Return TRUE on success, and FALSE on failure. */ { p_int length; /* Create the code to push the lvalue */ length = lv->length; if (length) { add_to_mem_block(A_PROGRAM, lv->u.p, length); yfree(lv->u.p); last_expression = CURRENT_PROGRAM_SIZE; } else { bytecode_p source, dest; mp_uint current_size; source = lv->u.simple; current_size = CURRENT_PROGRAM_SIZE; if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %lu" , current_size+2); return MY_FALSE; } CURRENT_PROGRAM_SIZE = (last_expression = current_size + 2); dest = PROGRAM_BLOCK + current_size; *dest++ = *source++; *dest++ = *source; } if (instruction != 0) ins_f_code(instruction); return MY_TRUE; } /* add_lvalue_code() */ /*-------------------------------------------------------------------------*/ static void insert_pop_value (void) /* Remove the last value computed from the stack. If possible, use * last_expression to prohibit that value from being generated * in the first place. */ { if (last_expression == CURRENT_PROGRAM_SIZE-1) { /* We don't have to fear sideeffects and try to prevent * the value from being generated. */ switch ( mem_block[A_PROGRAM].block[last_expression]) { case F_ASSIGN: mem_block[A_PROGRAM].block[last_expression] = F_VOID_ASSIGN; break; case F_ADD_EQ: mem_block[A_PROGRAM].block[last_expression] = F_VOID_ADD_EQ; break; case F_PRE_INC: case F_POST_INC: mem_block[A_PROGRAM].block[last_expression] = F_INC; break; case F_PRE_DEC: case F_POST_DEC: mem_block[A_PROGRAM].block[last_expression] = F_DEC; break; case F_CONST0: case F_CONST1: case F_NCONST1: mem_block[A_PROGRAM].current_size = last_expression; break; default: ins_byte(F_POP_VALUE); } last_expression = -1; } else /* The last expression is too long ago: just pop whatever there * is on the stack. */ ins_byte(F_POP_VALUE); } /* insert_pop_value() */ /*-------------------------------------------------------------------------*/ static void arrange_protected_lvalue (p_int start, int code, p_int end, int newcode) /* Arrange the creation of a (protected) lvalue instead of a normal lvalue * or even rvalue (mostly used when passing arguments by reference). * The arguments mean in general: * start: start address of the instruction * end: end address+1 of the instruction * code: lvalue-generating instruction alternative to the one now * stored at <start> * newcode: additional instruction to insert. * * The following scenarios exist: * * code >= 0 && end != 0: * The multi-byte instruction in [<start>..<end>[ (which might * be a complete indexing operation, but always excludes the * actual instruction bytes to change) is moved to the end of * the current program, then its alternative <code> and <newcode> * are appended. * * Cases are: * global * IDENTIFIER16 -> PUSH_IDENTIFIER16_LVALUE * expr4[x] * INDEX -> PUSH_PROTECTED_INDEXED_LVALUE * expr4[<x] * RINDEX -> PUSH_PROTECTED_RINDEXED_LVALUE * expr4[x,y] * MAP_INDEX -> PUSH_PROTECTED_INDEXED_MAP_LVALUE * * The 'global' case is special in that the code block only * consists of the instruction and its 2-byte argument - all * other cases are much bigger and the instruction to change * is right at the end without argument. * * code >= 0 && end == 0: * The instruction at <start> (1 byte code, 1 byte argument) * has to be replaced by its alternative <code> (the argument byte * is preserved), and the two-byte instruction <newcode> is then * inserted after the replaced instruction and the following code. * * Cases are: * global: * VIRTUAL_VARIABLE -> PUSH_VIRTUAL_VARIABLE_LVALUE * IDENTIFIER -> PUSH_IDENTIFIER_LVALUE * local * LOCAL -> PUSH_LOCAL_LVALUE * * code < 0: * The original instruction doesn't need or have an alternative, * and <newcode> is a protected-index-lvalue code, for which * the appropriate push-protected-index-lvalue code has * to be appended to the program. * * Cases where this code is generated: * F_STRING, F_NUMBER, F_CLOSURE, F_FLOAT, F_SYMBOL, * (expr0), ({ expr,... }), '({ expr,... }), ([...]), * x[a..b], x[<a..b], x[a..<b], x[<a..<b], x[a..], x[<a..], * &global, &local, &(expr4[x]), &(expr4[<x]), &(expr4[x,y]), * &(expr4[x..y]), &(expr4[<x..y]), &(expr4[x..<y]), * &(expr4[<x..<y]). * * Cases accepted by the function: * &(expr4[x]): F_PROTECTED_INDEX_LVALUE * -> F_PUSH_PROTECTED_INDEXED_LVALUE; * &(expr4[<x]): F_PROTECTED_INDEX_LVALUE * -> F_PUSH_PROTECTED_INDEXED_LVALUE; * * TODO: I am surprised this works at all. */ { mp_uint current; bytecode_p p; current = CURRENT_PROGRAM_SIZE; if (code >= 0) { if (end) { /* Variant 1: cycle a codeblock and modify instructions */ p_int length; bytecode_p q; length = end - start + 1; /* Get enough memory */ if (!realloc_a_program(length)) { yyerrorf("Out of memory: program size %lu\n" , CURRENT_PROGRAM_SIZE + length); return; } /* Cycle the indexing code to the end, where it belongs */ p = PROGRAM_BLOCK; memcpy(p + current, p + start, length); p += start; q = p + length; length = current - start; do *p++ = *q++; while (--length); /* Adjust the code... */ switch(code) { case F_PUSH_INDEXED_LVALUE: code = F_PUSH_PROTECTED_INDEXED_LVALUE; break; case F_PUSH_RINDEXED_LVALUE: code = F_PUSH_PROTECTED_RINDEXED_LVALUE; break; case F_PUSH_INDEXED_MAP_LVALUE: code = F_PUSH_PROTECTED_INDEXED_MAP_LVALUE; break; case F_PUSH_IDENTIFIER16_LVALUE: PUT_CODE(p-3, code); goto code_stored; default: fatal("Unexpected lvalue code\n"); } /* ...and store it */ PUT_CODE(p-1, code >> F_ESCAPE_BITS); STORE_CODE(p, code); current++; code_stored: STORE_CODE(p, newcode >> F_ESCAPE_BITS); PUT_CODE(p, newcode); } else { /* Variant 2: Overwrite the old <code> and insert <newcode> */ int i; p_int length; if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %lu\n" , CURRENT_PROGRAM_SIZE + 2); return; } p = PROGRAM_BLOCK + start; i = p[1]; length = current - start - 2; for( ; --length >= 0; p++) PUT_CODE(p, GET_CODE(p+2)); STORE_CODE(p, code); STORE_CODE(p, i); STORE_CODE(p, newcode >> F_ESCAPE_BITS); PUT_CODE(p, newcode); } } else { /* Variant 3: Just add a modified <newcode> */ switch(newcode) { case F_PROTECTED_INDEX_LVALUE: newcode = F_PUSH_PROTECTED_INDEXED_LVALUE; break; case F_PROTECTED_RINDEX_LVALUE: newcode = F_PUSH_PROTECTED_RINDEXED_LVALUE; break; default: yyerror("Need lvalue for range lvalue."); } if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %lu\n" , CURRENT_PROGRAM_SIZE + 2); return; } p = PROGRAM_BLOCK + current; STORE_CODE(p, newcode >> F_ESCAPE_BITS); PUT_CODE(p, newcode); } /* Correct the program size */ CURRENT_PROGRAM_SIZE = current + 2; } /* arrange_protected_lvalue() */ /*-------------------------------------------------------------------------*/ int proxy_efun (int function, int num_arg UNUSED) /* If the number of arguments doesn't fit the <function>, maybe there * is an alternative. * Return the code of the alternative efun, or -1 if there is none. */ { #if defined(__MWERKS__) && !defined(F_EXTRACT) # pragma unused(num_arg) #endif #ifdef F_EXTRACT if (function == F_EXTRACT) { if (num_arg == 2) { return F_EXTRACT2; } if (num_arg == 1) { return F_EXTRACT1; } } #endif if (function == F_PREVIOUS_OBJECT) { /* num_arg == 0 */ return F_PREVIOUS_OBJECT0; } return -1; } /* proxy_efun() */ /*-------------------------------------------------------------------------*/ %ifdef INITIALIZATION_BY___INIT static void transfer_init_control (void) /* The compiler is about to generate another INIT fragment at the current * address: update the JUMP of the last INIT fragment to point to this * address. * If this is the first call, the function header for __INIT is generated * as well. */ { if (last_initializer_end < 0) { /* First call: we have to generate the __INIT function * header. */ #ifdef ALIGN_FUNCTIONS CURRENT_PROGRAM_SIZE = align(CURRENT_PROGRAM_SIZE); /* Must happen before PREPARE_INSERT()! */ #endif { char *name; PREPARE_INSERT(sizeof name + 3); name = ref_string(STR_VARINIT); memcpy(__PREPARE_INSERT__p , (char *)&name, sizeof name); __PREPARE_INSERT__p += sizeof(name); add_byte(TYPE_ANY); /* return type */ add_byte(0); /* num_arg */ add_byte(0); /* num_local */ first_initializer_start = (CURRENT_PROGRAM_SIZE += sizeof name + 3) - 2; } } else if ((p_int)(CURRENT_PROGRAM_SIZE - 3) == last_initializer_end) { /* The news INIT fragment directly follows the old one, so * just overwrite the JUMP instruction of the last. */ mem_block[A_PROGRAM].current_size -= 4; } else { /* Change the address of the last jump after the last * initializer to this point. */ upd_offset(last_initializer_end, mem_block[A_PROGRAM].current_size); } } /* transfer_init_control() */ /*-------------------------------------------------------------------------*/ static void add_new_init_jump (void) /* The compiler just finished an INIT fragment: add a JUMP instruction * and let last_initializer_end point to its offset. */ { ins_byte(F_JUMP); last_initializer_end = (p_int)mem_block[A_PROGRAM].current_size; ins_byte(0); ins_short(0); } /* add_new_init_jump() */ %endif /* INITIALIZATION_BY___INIT */ /*-------------------------------------------------------------------------*/ static short lookup_inherited (char *super_name, char *real_name , inherit_t **pIP, funflag_t *pFlags) /* Lookup an inherited function <super_name>::<real_name> and return * it's function index, setting *pIP to the inherit_t pointer and * *pFlags to the function flags. * Return -1 if not found, *pIP set to NULL, and *pFlags set to 0. * * This function is called by the lexer to resolve #'<inherited_fun> closures, * so both strings are not shared. * * <super_name> can be an empty string or the (partial) name of one * of the inherits. <real_name> must be shared string. */ { inherit_t *ip, *foundp; int num_inherits, super_length; short found_ix; found_ix = -1; *pIP = NULL; *pFlags = 0; /* Strip leading '/' */ while (*super_name == '/') super_name++; super_length = strlen(super_name); num_inherits = INHERIT_COUNT; real_name = findstring(real_name); /* Search the function in all inherits. * For normal inherits its sufficient to search the inherits * from the back in order to get the topmost definition; however, * with virtual inherits the order gets messed up. */ ip = (inherit_t *)mem_block[A_INHERITS].block; for ( foundp = NULL ; num_inherits > 0 ; ip++, num_inherits--) { short i; if (ip->inherit_type & INHERIT_TYPE_DUPLICATE) /* this is a duplicate inherit */ continue; /* Test if super_name matches the end of the name of the inherit. */ if (super_length > 0) { /* ip->prog->name includes .c */ int l = strlen(ip->prog->name) - 2; if (l < super_length) continue; if (l > super_length && ip->prog->name[l-super_length-1] != '/') continue; if (strncmp(super_name, ip->prog->name + l - super_length, super_length) != 0) continue; } /* Look for the function */ if ( (i = find_function(real_name, ip->prog)) < 0) continue; /* Found one */ if (foundp == NULL || ip->inherit_depth < foundp->inherit_depth ) { foundp = ip; found_ix = i; if (foundp->inherit_depth < 2) /* toplevel inherit */ break; } } /* for (all inherits) */ if (foundp != NULL) { funflag_t flags; /* Found it! */ ip = foundp; *pFlags = flags = ip->prog->functions[found_ix]; if (flags & NAME_INHERITED) { /* The parent inherits the function itself: we have to * check if it's a virtual inheritance. */ inherit_t *ip2; program_t *prog1, *prog2; int numvar2; prog1 = ip->prog; ip2 = &prog1->inherit[flags & INHERIT_MASK]; prog2 = ip2->prog; if ( 0 != (numvar2 = prog2->num_variables) && prog1->variable_names[ip2->variable_index_offset+numvar2-1].flags & TYPE_MOD_VIRTUAL && !(prog2->variable_names[numvar2-1].flags & TYPE_MOD_VIRTUAL) ) { /* The source was virtually inherited - we have to find * the first inheritance of the program. * And adjust the function index, of course. */ do --ip; while (ip->prog != prog2); found_ix -= ip2->function_index_offset; } } *pIP = ip; } /* if (foundp) */ return found_ix; } /* lookup_inherited() */ /*-------------------------------------------------------------------------*/ short find_inherited (char *super_name, char *real_name) /* Lookup an inherited function <super_name>::<real_name> and return * it's function index. Return -1 if not found. * This function is called by the lexer to resolve #'<inherited_fun> closures, * so both strings are not shared. * * <super_name> can be an empty string or the (partial) name of one * of the inherits. */ { inherit_t *ip; funflag_t flags; short ix; real_name = findstring(real_name); ix = real_name ? lookup_inherited(super_name, real_name, &ip, &flags) : -1; if (ix >= 0) /* Correct the index for the toplevel program */ ix += ip->function_index_offset; return ix; } /* find_inherited() */ /*-------------------------------------------------------------------------*/ static int insert_inherited (char *super_name, char *real_name , program_t **super_p, function_t *fun_p , int num_arg, bytecode_p __prepare_insert__p ) /* The compiler encountered a <super_name>::<real_name>() call with * <num_arg> arguments; the codepointer is <__prepare_insert__p>. * * Look up the function information and set *<super_p> and *<fun_p> * the program pointer and the function_t information. Also compile * the function call(s). * * Result is the function index, or one of the negative error codes: * INHERITED_NOT_FOUND (-1): the function wasn't found. * INHERITED_WILDCARDED_ARGS (-2): it was a wildcarded supercall with * arguments * INHERITED_WILDCARDED_NOT_FOUND (-3): it was a wildcarded supercall, * but not a single function was found. * * <real_name> must be a shared string (this function doesn't change * the reference count). * * <super_name> can be an empty string, the (partial) name of one * of the inherits, or a wildcarded name (and no args). In the latter * case, the function is called in all inherits matching the pattern. * The results from such a wildcarded call are returned in an array, * <super_p>, <fun_p> and the returned function index are those of * the first function found. */ { inherit_t *ip; funflag_t flags; short found_ix; found_ix = lookup_inherited(super_name, real_name, &ip, &flags); if (ip != NULL) { /* Found it! */ bytecode_p __PREPARE_INSERT__p = __prepare_insert__p; /* Generate the function call */ add_byte(F_CALL_EXPLICIT_INHERITED); add_short(ip - (inherit_t *)mem_block[A_INHERITS].block); add_short(found_ix); add_byte(num_arg); /* Return the program pointer */ *super_p = ip->prog; /* Return a copy of the function structure */ fun_p->name = real_name; fun_p->flags = flags & ~INHERIT_MASK; { int i2 = found_ix; fun_hdr_p funstart; /* Find the real function code */ while ( (flags = ip->prog->functions[i2]) & NAME_INHERITED) { ip = &ip->prog->inherit[flags & INHERIT_MASK]; i2 -= ip->function_index_offset; } funstart = &ip->prog->program[flags & FUNSTART_MASK]; fun_p->type = FUNCTION_TYPE(funstart); fun_p->num_arg = (FUNCTION_NUM_ARGS(funstart) & 0x7f); if (FUNCTION_NUM_ARGS(funstart) & ~0x7f) fun_p->type |= TYPE_MOD_XVARARGS; } CURRENT_PROGRAM_SIZE += 6; return found_ix; } /* if (ip) */ /* Inherit not found, maybe it's a wildcarded call */ if (strpbrk(super_name, "*?")) { int num_inherits; Bool *was_called; /* Flags which inh. fun has been called already */ inherit_t *ip0; int calls = 0; int ip_index; int first_index; short i; /* Wildcarded supercalls only work without arguments */ if (num_arg) return INHERITED_WILDCARDED_ARGS; *super_p = NULL; num_inherits = INHERIT_COUNT; was_called = alloca(sizeof(*was_called)*num_inherits); for (i = 0; i < num_inherits; i++) was_called[i] = MY_FALSE; /* Test every inherit if the name matches and if * it does, generate the function call. */ ip0 = (inherit_t *)mem_block[A_INHERITS].block; first_index = num_inherits > 0 ? INHERITED_WILDCARDED_NOT_FOUND : INHERITED_NOT_FOUND; for (; num_inherits > 0; ip0++, num_inherits--) { PREPARE_INSERT(6) /* ip->prog->name includes .c */ int l = strlen(ip0->prog->name + 2); ip = ip0; /* ip will be changed in the body */ if (ip->inherit_type & INHERIT_TYPE_DUPLICATE) /* duplicate inherit */ continue; if (ip->inherit_depth > 1) /* Only consider direct inherits, otherwise we would even * call functions in sub-inherits which have been redefined. */ continue; if ( !match_string(super_name, ip->prog->name, l) ) continue; if ( (i = find_function(real_name, ip->prog)) < 0) continue; /* Found a match */ flags = ip->prog->functions[i]; if (flags & NAME_INHERITED) { /* The parent inherits the function itself: we have to * check if it's a virtual inheritance. */ inherit_t *ip2; program_t *prog1, *prog2; int numvar2; prog1 = ip->prog; ip2 = &prog1->inherit[flags & INHERIT_MASK]; prog2 = ip2->prog; if ( 0 != (numvar2 = prog2->num_variables) && prog1->variable_names[ip2->variable_index_offset+numvar2-1].flags & TYPE_MOD_VIRTUAL && !(prog2->variable_names[numvar2-1].flags & TYPE_MOD_VIRTUAL) ) { /* The function was virtually inherited - we have to find * the first inheritance of that program and adjust the * function index, of course. */ do --ip; while (ip->prog != prog2); i -= ip2->function_index_offset; } /* if (virtually inherited) */ } /* if (inherited) */ ip_index = ip - (inherit_t *)mem_block[A_INHERITS].block; /* The (new) ip might be duplicate inherit, or point to * a virtually inherited function we called already. */ if ((ip->inherit_type & INHERIT_TYPE_DUPLICATE) || was_called[ip_index]) /* duplicate inherit */ continue; if (!calls) /* First function found */ first_index = i; /* Generate the function call */ add_byte(F_CALL_EXPLICIT_INHERITED); add_short(ip_index); add_short(i); add_byte(num_arg); /* Mark this function as called */ was_called[ip_index] = MY_TRUE; /* Return the program pointer to the caller */ *super_p = ip->prog; /* Return a copy of the function structure to the caller */ fun_p->name = real_name; fun_p->flags = flags & ~INHERIT_MASK; { inherit_t *ip2 = ip; int i2 = i; fun_hdr_p funstart; /* Find the real function code */ while ( (flags = ip2->prog->functions[i2]) & NAME_INHERITED) { ip2 = &ip2->prog->inherit[flags & INHERIT_MASK]; i2 -= ip2->function_index_offset; } funstart = &ip2->prog->program[flags & FUNSTART_MASK]; fun_p->type = FUNCTION_TYPE(funstart); fun_p->num_arg = FUNCTION_NUM_ARGS(funstart); } calls++; CURRENT_PROGRAM_SIZE += 6; } /* for() */ /* The calls above left their results on the stack. * Combine them into a single array (which might be empty). */ { PREPARE_INSERT(3) add_byte(F_AGGREGATE); add_short(calls); CURRENT_PROGRAM_SIZE += 3; } return first_index; } /* No such function */ return INHERITED_NOT_FOUND; } /* insert_inherited() */ /*-------------------------------------------------------------------------*/ static void cross_define (function_t *from, function_t *to, int32 offset) /* The function <to> is a cross-definition from real function <from>, * separated by <offset>. * Set the flags and offset of <to> accordingly to point to <from>, and * synchronize the NO_MASK flag of both. */ { short nomask; to->flags = (to->flags & ~NAME_UNDEFINED) | (from->flags & (NAME_UNDEFINED|NAME_PROTOTYPE)) | NAME_CROSS_DEFINED | NAME_HIDDEN | NAME_INHERITED; to->offset.func = MAKE_CROSSDEF_OFFSET(offset); nomask = (from->flags|to->flags) & TYPE_MOD_NO_MASK; from->flags |= nomask; to ->flags |= nomask; } /* cross_define() */ /*-------------------------------------------------------------------------*/ static funflag_t * get_function_id (program_t *progp, int fx) /* Return a pointer to the function flags of function <fx> in <progp>. * This function takes care of resolving cross-definitions and inherits * to the real function flag. */ { funflag_t flags; flags = progp->functions[fx]; /* Handle a cross-define */ if (flags & NAME_CROSS_DEFINED) { fx += CROSSDEF_NAME_OFFSET(flags); flags = progp->functions[fx]; } /* Walk the inherit chain */ while(flags & NAME_INHERITED) { inherit_t *inheritp; inheritp = &progp->inherit[flags & INHERIT_MASK]; progp = inheritp->prog; fx -= inheritp->function_index_offset; flags = progp->functions[fx]; } /* This is the one */ return &progp->functions[fx]; } /* get_function_id() */ /*-------------------------------------------------------------------------*/ static %ifdef INITIALIZATION_BY___INIT int %else void %endif copy_functions (program_t *from, fulltype_t type) /* The functions of the program <from> are inherited with visibility <type>. * Copy all the function definitions into this program, but as UNDEFINED * so that they can be redefined in the current program. The epilog() * will later update the non-redefined inherited functions and also copy * the types. * * An explicit call to an inherited function will not be * done through this entry (because this entry can be replaced by a new * definition). If an function defined by inheritance is called, * this is done with F_CALL_EXPLICIT_INHERITED * %ifdef INITIALIZATION_BY___INIT * The result is the function index of the inherited __INIT function, * or -1 if the inherited program doesn't have an initializer. %endif */ { %ifdef INITIALIZATION_BY___INIT int initializer = -1; %endif int i; uint32 first_func_index, current_func_index; function_t *fun_p; unsigned short *ixp; /* Make space for the inherited function structures */ if (mem_block[A_FUNCTIONS].max_size < mem_block[A_FUNCTIONS].current_size + from->num_functions * sizeof(function_t) ) { if (!realloc_mem_block(&mem_block[A_FUNCTIONS], mem_block[A_FUNCTIONS].current_size + from->num_functions * sizeof(function_t))) return %ifdef INITIALIZATION_BY___INIT 0 %endif ; } /* The new functions will be stored from here */ fun_p = (function_t *) (mem_block[A_FUNCTIONS].block + mem_block[A_FUNCTIONS].current_size); /* Copy the function definitions one by one and adjust the flags. * For now, we mask out the INHERIT field in the flags and * use NEW_INHERITED_INDEX for the value. */ for (i = 0; i < from->num_functions; i++, fun_p++) { program_t *defprog; inherit_t *ip; fun_hdr_p funstart; funflag_t flags; int i2; /* The index of the real function */ flags = from->functions[i]; fun_p->offset.inherit = NEW_INHERITED_INDEX; i2 = i; if (flags & NAME_INHERITED) { /* The inherit-index has to be recomputed */ fun_p->flags = (flags & ~INHERIT_MASK) | NAME_INHERITED | NAME_HIDDEN; /* If cross-defined, get the real function index */ if (flags & NAME_CROSS_DEFINED) { fun_p->offset.func = flags & INHERIT_MASK; i2 += CROSSDEF_NAME_OFFSET(flags); } } else { /* Also, the function-code offset needs adjustment */ fun_p->flags = (flags & ~FUNSTART_MASK) | NAME_INHERITED | NAME_HIDDEN; } /* Look up the defining program for the inherited function */ defprog = from; while ( (flags = defprog->functions[i2]) & NAME_INHERITED) { ip = &defprog->inherit[flags & INHERIT_MASK]; i2 -= ip->function_index_offset; defprog = ip->prog; } /* Copy the function information */ funstart = &defprog->program[flags & FUNSTART_MASK]; memcpy(&fun_p->name, FUNCTION_NAMEP(funstart), sizeof fun_p->name); fun_p->type = FUNCTION_TYPE(funstart); fun_p->num_arg = FUNCTION_NUM_ARGS(funstart) & 0x7f; if (FUNCTION_NUM_ARGS(funstart) & ~0x7f) fun_p->type |= TYPE_MOD_XVARARGS; if (FUNCTION_CODE(funstart)[0] == F_ESCAPE && FUNCTION_CODE(funstart)[1] == F_UNDEF -0x100) { fun_p->flags |= NAME_UNDEFINED; } } /* for (inherited functions) pass 1 */ /* Point back to the begin of the copied function data */ fun_p = (function_t *) (mem_block[A_FUNCTIONS].block + mem_block[A_FUNCTIONS].current_size); /* Unhide all function for which names exist */ ixp = from->function_names; for (i = from->num_function_names; --i >= 0; ) { fun_p[*ixp++].flags &= ~NAME_HIDDEN; } first_func_index = current_func_index = mem_block[A_FUNCTIONS].current_size / sizeof (function_t); mem_block[A_FUNCTIONS].current_size += sizeof *fun_p * from->num_functions; /* Loop again over the inherited functions, checking visibility * and re/crossdefinition, and updating their function indices. * Do not call define_new_function() from here, as duplicates would * be removed. */ for (i = 0; i < from->num_functions; i++, current_func_index++) { function_t fun; funflag_t new_type; unsigned short tmp_short; ident_t* p; fun = fun_p[i]; /* Prepare some data to be used if this function will not be * redefined. * fun.name has already it's ref as a newly defined function in from */ fun.flags |= type & TYPE_MOD_NO_MASK; /* Perform a lot of tests and actions for the visibility * and definitiability. The switch() allows us to abort * easily without using gotos. */ switch (0) { default: /* Test if the function is visible at all. * For this test, 'private nomask' degenerates to 'private' * if we didn't do that, the driver would crash on a second * level inherit (possible on a multiple second-level inherit). * TODO: Find out why it crashes. */ { fulltype_t fflags = fun.flags; if ((fflags & (TYPE_MOD_PRIVATE|TYPE_MOD_NO_MASK)) == (TYPE_MOD_PRIVATE|TYPE_MOD_NO_MASK) ) fflags &= ~(TYPE_MOD_NO_MASK); if ( (fflags & (NAME_HIDDEN|TYPE_MOD_NO_MASK|NAME_UNDEFINED) ) == (NAME_HIDDEN|TYPE_MOD_NO_MASK) ) { break; } } /* Visible: create a new identifier for it */ p = make_global_identifier(fun.name, I_TYPE_GLOBAL); if (!p) break; if (p->type != I_TYPE_UNKNOWN) { /* We got this ident already somewhere */ int32 n; /* existing function index */ if ( (n = p->u.global.function) >= 0) { /* Already inherited from somewhere else. * Don't try to resolve cross-references inside the * currently inherited program; not only is this superflous, * but it can also lead to circular cross-inheritance * when there was a misplaced prototype or an explicit * directive to inherit a multiply inherited function * from a particular base class (the latter is not * implemented). In these cases, the information that lead * to the non-standard preference would be very hard to * reconstruct. */ if ((uint32)n < first_func_index) { /* We already have a function definition/prototype * for this name. */ function_t *OldFunction = FUNCTION(n); if ( !(OldFunction->flags & NAME_INHERITED) ) { /* Since inherits are not possible after * functions have been compiled, the only * way to get here is when we had a prototype * for the function. * It's not fatal, but annoying. */ yywarnf( "Misplaced prototype for %s in %s\n" , fun.name, current_file ); cross_define( &fun, OldFunction , current_func_index - n ); p->u.global.function = current_func_index; } else if ((fun.flags | type) & TYPE_MOD_VIRTUAL && OldFunction->flags & TYPE_MOD_VIRTUAL && !((fun.flags | OldFunction->flags) & NAME_HIDDEN) && get_function_id(from, i) == get_function_id(INHERIT(OldFunction->offset.inherit).prog , n - INHERIT(OldFunction->offset.inherit).function_index_offset ) ) { /* Entries denote the same function. We have to use * cross_define nonetheless, to get consistant * redefinition (and we prefer the first one) */ OldFunction->flags |= fun.flags & (TYPE_MOD_PUBLIC|TYPE_MOD_NO_MASK); OldFunction->flags &= fun.flags | ~TYPE_MOD_STATIC; cross_define( OldFunction, &fun , n - current_func_index ); } else if ( (fun.flags & OldFunction->flags & TYPE_MOD_NO_MASK) && !( (fun.flags|OldFunction->flags) & (TYPE_MOD_PRIVATE|NAME_UNDEFINED) ) ) { yyerrorf( "Illegal to inherit 'nomask' function '%s' twice", fun.name); } else if (( fun.flags & TYPE_MOD_NO_MASK || OldFunction->flags & (NAME_HIDDEN|NAME_UNDEFINED|TYPE_MOD_PRIVATE)) && !(fun.flags & (NAME_HIDDEN|NAME_UNDEFINED)) ) { /* This function is visible and existing, but the * inherited one is not, or this one is also nomask: * prefer the inherited one. */ cross_define( &fun, OldFunction , current_func_index - n ); p->u.global.function = current_func_index; } else if (( fun.flags & TYPE_MOD_NO_MASK || OldFunction->flags & (NAME_HIDDEN|NAME_UNDEFINED|TYPE_MOD_PROTECTED)) && !(fun.flags & (NAME_HIDDEN|NAME_UNDEFINED)) ) { /* This function is visible and existing, but the * inherited one is not, or this one is also nomask: * prefer the inherited one. */ cross_define( &fun, OldFunction , current_func_index - n ); p->u.global.function = current_func_index; } else if ( (fun.flags & TYPE_MOD_PRIVATE) == 0 || (OldFunction->flags & TYPE_MOD_PRIVATE) == 0 || ((OldFunction->flags|fun.flags) & TYPE_MOD_VIRTUAL) != 0 ) { /* At least one of the functions is visible * or redefinable: prefer the first one. * TODO: The whole if-condition is more a kludge, * TODO:: developed iteratively from .367 * TODO:: through .370. It should be reconsidered, * TODO:: which of course implies a deeper * TODO:: analysis of the going ons here. */ cross_define( OldFunction, &fun , n - current_func_index ); } } /* if (n < first_func_index) */ else if ( !(fun.flags & NAME_CROSS_DEFINED) ) { /* This is the dominant definition in the superclass, * inherit this one. */ #ifdef DEBUG /* The definition we picked before should be * cross-defined to the definition we have now; or * it should be nominally invisible so we can redefine * it. */ if (( !(FUNCTION(n)->flags & NAME_CROSS_DEFINED) || FUNCTION(n)->offset.func != MAKE_CROSSDEF_OFFSET(((int32)current_func_index) - n) ) && ((FUNCTION(n)->flags & TYPE_MOD_PRIVATE) == 0 ) ) { fatal( "Inconsistent definition of %s() within " "superclass '%s'.\n" , fun.name, from->name ); } #endif p->u.global.function = current_func_index; } } else /* n < 0: not an lfun */ { if (n != I_GLOBAL_FUNCTION_EFUN || (fun.flags & (TYPE_MOD_PRIVATE|NAME_HIDDEN)) == 0 || (fun.flags & (NAME_UNDEFINED)) != 0 ) { if (n == I_GLOBAL_FUNCTION_EFUN) { /* This inherited function shadows * an (simul-)efun. */ efun_shadow_t *q; q = xalloc(sizeof(efun_shadow_t)); if (!q) { yyerrorf("Out of memory: efun shadow (%lu bytes)" , (unsigned long) sizeof(efun_shadow_t)); break; } q->shadow = p; q->next = all_efun_shadows; all_efun_shadows = q; } /* Update the symbol table entry to point * to the newly read function. */ p->u.global.function = current_func_index; } /* else: inherited private defined function must not hide * the (simul-)efun and is thusly not added to * the symbol-table. */ } } /* if (p != I_TYPE_UNKNOWN) */ if (p->type == I_TYPE_UNKNOWN) { /* First time this function-ident was ever encountered. * Just make a new global. */ p->type = I_TYPE_GLOBAL; p->u.global.variable = I_GLOBAL_VARIABLE_OTHER; p->u.global.efun = I_GLOBAL_EFUN_OTHER; p->u.global.sim_efun = I_GLOBAL_SEFUN_OTHER; p->u.global.function = current_func_index; p->next_all = all_globals; all_globals = p; } /* Done with re/crossdefinition, now handle visibility. * Especially: public functions should not become private * when inherited 'private'. */ new_type = type; if (fun.flags & TYPE_MOD_PUBLIC) new_type &= ~(TYPE_MOD_PRIVATE|TYPE_MOD_STATIC); fun.flags |= new_type; /* Recognize an inherited heart_beat(), making it possible * to mask it. */ if ((heart_beat == -1) && fun.name[0] == 'h' && (strcmp(fun.name, "heart_beat") == 0) && !(fun.flags & (NAME_HIDDEN|NAME_UNDEFINED)) ) { heart_beat = current_func_index; } %ifdef INITIALIZATION_BY___INIT /* Recognize the initializer function */ if (fun.name[0] == '_' && strcmp(fun.name+1, "_INIT") == 0) { initializer = i; fun.flags |= NAME_UNDEFINED; } %endif } /* switch() for visibility/redefinability */ /* Copy information about the types of the arguments, if it is * available. */ tmp_short = INDEX_START_NONE; /* Presume not available. */ if (from->type_start != 0) { if (from->type_start[i] != INDEX_START_NONE) { /* They are available for function number 'i'. Copy types of * all arguments, and remember where they started. */ tmp_short = ARGTYPE_COUNT; if (fun.num_arg) add_to_mem_block( A_ARGUMENT_TYPES, &from->argument_types[from->type_start[i]], (sizeof (unsigned short)) * fun.num_arg ); } } else { fun.flags |= NAME_TYPES_LOST; } /* Save the index where they started. Every function will have an * index where the type info of arguments starts. */ add_to_mem_block(A_ARGUMENT_INDEX, &tmp_short, sizeof tmp_short); /* Finally update the entry in the A_FUNCTIONS area */ fun_p[i] = fun; } /* for (inherited functions), pass 2 */ %ifdef INITIALIZATION_BY___INIT return initializer; %endif } /* copy_functions() */ /*-------------------------------------------------------------------------*/ static void copy_variables (program_t *from, fulltype_t type %ifndef INITIALIZATION_BY___INIT , svalue_t *initializers %endif ) /* Inherit the variables of <from> with visibility <type>. * The variables are copied into our program, and it is important that * they are stored in the same order with the same index. */ { int i, j; int new_bound, last_bound; int variable_index_offset, fun_index_offset; uint inheritc; inherit_t *inheritp; int previous_variable_index_offset; int from_variable_index_offset; type &= ~TYPE_MOD_VARARGS; /* aka NAME_INITIALIZED */ /* If this is a virtual inherit, find the first inherit * for this program and set the from_variable_index_offset. */ from_variable_index_offset = -1; if (type & TYPE_MOD_VIRTUAL) { inheritp = (inherit_t *)(mem_block[A_INHERITS].block); j = mem_block[A_INHERITS].current_size; for (; (j -= sizeof(inherit_t)) >= 0; inheritp++) { if (inheritp->prog == from && !(inheritp->variable_index_offset & NON_VIRTUAL_OFFSET_TAG) ) { from_variable_index_offset = inheritp->variable_index_offset + VIRTUAL_VAR_TAG; break; } } #ifdef INITIALIZATION_BY___INIT if (variables_initialized && from_variable_index_offset < 0) yyerror( "illegal to inherit virtually after initializing variables\n" ); #endif } fun_index_offset = FUNCTION_COUNT - from->num_functions; variable_index_offset = V_VARIABLE_COUNT; /* Loop through the inherits and copy the variables, * and also in the last run the variables of the inherited program. */ last_bound = 0; /* Last variable index handled in the previous run */ i = from->num_inherited; for (inheritc = 0, inheritp = from->inherit; MY_TRUE; inheritc++, inheritp++) { if (--i >= 0) { /* It's an inherit */ program_t *progp; progp = inheritp->prog; new_bound = inheritp->variable_index_offset + progp->num_variables; /* The end of this program's variables in the inherited * program <from>. This way we can compare the variables * original type with the type they got through inheritance. */ /* Has a new virtual variable been introduced in this program? */ if (progp->num_variables && from->variable_names[new_bound-1].flags & TYPE_MOD_VIRTUAL && !(progp->variable_names[progp->num_variables-1].flags & TYPE_MOD_VIRTUAL) ) { inherit_t inherit, *inheritp2; int k, inherit_index; funflag_t *flagp; function_t *funp, *funp2; #ifdef INITIALIZATION_BY___INIT if (variables_initialized) yyerror( "illegal to inherit virtually after initializing variables\n" ); #endif inherit = *inheritp; inherit.inherit_type = INHERIT_TYPE_EXTRA; inherit.inherit_depth++; /* Find the first (virtual) inheritance of this * program. */ inheritp2 = (inherit_t *)(mem_block[A_INHERITS].block); j = mem_block[A_INHERITS].current_size; for (; (j -= sizeof(inherit_t)) >= 0; inheritp2++) { if (inheritp2->prog == inherit.prog && !(inheritp2->variable_index_offset & NON_VIRTUAL_OFFSET_TAG) ) { /* Found it: copy the variable_index_offset */ inherit.variable_index_offset = inheritp2->variable_index_offset; break; } } if (j < 0) { /* First occurence of these virtual variables, we're * going to copy them into our variables. */ inheritp2 = &inherit; variable_index_offset += new_bound - last_bound; inherit.variable_index_offset = variable_index_offset - progp->num_variables; } else inherit.inherit_type |= INHERIT_TYPE_DUPLICATE; inherit_index = (mem_block[A_INHERITS].current_size - j) / sizeof(inherit_t) - 1; inherit.function_index_offset += fun_index_offset; add_to_mem_block(A_INHERITS, (char *)&inherit, sizeof inherit); /* If a function is directly inherited from a program that * introduces a virtual variable, the code therein is not * aware of virtual inheritance. For this reason, there are * the extra inherit_ts with an appropriate * variable_index_offset; we have to redirect inheritance * to these inherit_ts. */ /* Update the offset.inherit in all these functions to point * to the first (virtual) inherit of the program. */ flagp = from->functions + inheritp->function_index_offset; funp = (function_t *)mem_block[A_FUNCTIONS].block + inherit.function_index_offset; funp2 = (function_t *)mem_block[A_FUNCTIONS].block + inheritp2->function_index_offset; /* Usually funp2 == funp, but if the program is inherited * virtually several times with differing visibilities, * the two pointers differ. */ for (k = inherit.prog->num_functions; --k >= 0; funp++, funp2++) { if ( !(funp->flags & NAME_CROSS_DEFINED) && !(funp2->flags & NAME_CROSS_DEFINED) && (*flagp & (NAME_INHERITED|NAME_CROSS_DEFINED)) == NAME_INHERITED && (*flagp & INHERIT_MASK) == inheritc ) { funp->offset.inherit = inherit_index; } flagp++; } if (j >= 0) { /* There has been another instance of this virtual * superclass before: no need to check the visibility * of the variables again. */ if (new_bound > last_bound) last_bound = new_bound; continue; } previous_variable_index_offset = -1; } else { /* Normal, nonvirtual inherit. * We wait with the visibility check until it's really * useful, and then do several inherits in one go. */ continue; } } else { /* Handle the variables of <from>. * After that, we will loop once more in here, but * the if() below will notice that. * As a side effect we terminate immediately if <from> * had no variables on its own. */ previous_variable_index_offset = from_variable_index_offset; new_bound = from->num_variables; if (new_bound == last_bound) break; } /* Check the visibility of the newly inspected variables * [last_bound..new_bound[. */ for (j = last_bound; j < new_bound; j++) { ident_t *p; fulltype_t new_type; p = make_global_identifier(from->variable_names[j].name , I_TYPE_GLOBAL); if (!p) return; new_type = type; /* 'public' variables should not become private when inherited * 'private'. */ if (from->variable_names[j].flags & TYPE_MOD_PUBLIC) new_type &= ~TYPE_MOD_PRIVATE; /* define_variable checks for previous 'nomask' definition. */ if (previous_variable_index_offset >= 0) { if ( !(from->variable_names[j].flags & TYPE_MOD_PRIVATE) ) redeclare_variable(p, new_type | from->variable_names[j].flags | NAME_INHERITED, previous_variable_index_offset + j ); } else { define_variable(p, new_type | from->variable_names[j].flags | (from->variable_names[j].flags & TYPE_MOD_PRIVATE ? (NAME_HIDDEN|NAME_INHERITED) : NAME_INHERITED ) %ifndef INITIALIZATION_BY___INIT ,from->variable_names[j].flags & NAME_INITIALIZED ? copy_svalue(&initializers[j]) : &const0 %endif ); } } /* end loop through variables */ last_bound = new_bound; /* Mark how far we got */ } /* end of loop through inherits */ } /* copy_variables() */ /*-------------------------------------------------------------------------*/ static void fix_function_inherit_indices (program_t *from) /* All functions inherited from <from>, which haven't been resolved * to belong to some other inherit, are now assigned to the current * inherit. */ { int i, inherit_index; function_t *funp; inherit_index = INHERIT_COUNT; funp = (function_t *) (mem_block[A_FUNCTIONS].block+mem_block[A_FUNCTIONS].current_size) - from->num_functions; for (i = from->num_functions; --i >= 0; funp++) { if ( funp->offset.inherit == NEW_INHERITED_INDEX && !(funp->flags & NAME_CROSS_DEFINED) ) { funp->offset.inherit = inherit_index; } } } /* fix_function_inherit_indices() */ /*-------------------------------------------------------------------------*/ static void fix_variable_index_offsets (program_t *new_prog) /* Add num_virtual_variables to the index_offset of all variables * in <new_prog> marked with NON_VIRTUAL_OFFSET_TAG. The tag is removed. * * Reason is that the non-virtual variables have to be put after * the virtual variables, so the offsets of these variables are * first counted from 0 up and then corrected in this function after * the last virtual inherit. */ { int i; inherit_t *inheritp; i = new_prog->num_inherited; for (inheritp = new_prog->inherit; --i >= 0; inheritp++) { if (inheritp->variable_index_offset & NON_VIRTUAL_OFFSET_TAG) { inheritp->variable_index_offset += num_virtual_variables; inheritp->variable_index_offset &= ~NON_VIRTUAL_OFFSET_TAG; } } } /* fix_variable_index_offsets() */ /*-------------------------------------------------------------------------*/ void store_line_number_info (void) { unsigned char c; short offset; /* Was code generated since the last call? * If not, return. */ offset = mem_block[A_PROGRAM].current_size - stored_bytes; if (offset <= 0) return; stored_bytes = mem_block[A_PROGRAM].current_size; /* Less than 8 bytes code in 2..9 lines */ if (offset <= 8 && current_line - stored_lines >= 2 && current_line - stored_lines <= 9) { c = offset + 8*(current_line - stored_lines) + 47; /* == (lineincr+6) << 3 | (codesize-1) */ byte_to_mem_block(A_LINENUMBERS, c); stored_lines = current_line; return; } /* Use up the excessive amounts of lines */ stored_lines++; while (stored_lines < current_line) { int lines; lines = current_line - stored_lines; if (lines > LI_MAXEMPTY) lines = LI_MAXEMPTY; stored_lines += lines; c = 256 - lines; byte_to_mem_block(A_LINENUMBERS, c); } while (offset >= LI_MAXOFFSET) { byte_to_mem_block(A_LINENUMBERS, LI_MAXOFFSET); offset -= LI_MAXOFFSET; } byte_to_mem_block(A_LINENUMBERS, offset); } /* store_line_number_info() */ /*-------------------------------------------------------------------------*/ static void store_line_number_relocation (int relocated_from) /* Since the last store_line_number_info(), the compiler added a code * block which was compiled out of order at the earlier line <relocated_from>. * Add the relocation marker with the offset to <relocated_from>, call * store_line_number_info() for the modified linenumbers and the added * codeblock, then restore the current line number. */ { int save_current, offset; save_current = current_line; stored_lines -= 2; current_line = stored_lines+1; offset = current_line - relocated_from; if (offset >= LI_SMALL_REL) { byte_to_mem_block(A_LINENUMBERS, LI_L_RELOCATED); byte_to_mem_block(A_LINENUMBERS, offset >> 8); byte_to_mem_block(A_LINENUMBERS, offset); /* trailing LI_L_RELOCATED allows bidirectional traversal */ byte_to_mem_block(A_LINENUMBERS, LI_L_RELOCATED); } else { byte_to_mem_block(A_LINENUMBERS, LI_RELOCATED + offset); } store_line_number_info(); current_line = save_current; } /* store_line_number_relocation() */ /*-------------------------------------------------------------------------*/ void store_line_number_backward (int offset) /* The current line counter is set back by <offset> lines. * Adapted the stored_lines counter and add the LI_BACK linenumber entry. */ { if (offset > 0) { store_line_number_info(); stored_lines -= offset; while (offset > 256) { byte_to_mem_block(A_LINENUMBERS, LI_BACK); byte_to_mem_block(A_LINENUMBERS, 255); offset -= 256; } byte_to_mem_block(A_LINENUMBERS, LI_BACK); byte_to_mem_block(A_LINENUMBERS, offset-1); } } /* store_line_number_backward() */ /*-------------------------------------------------------------------------*/ mp_uint store_include_info (char *name, char * filename, char delim, int depth) /* The lexer is going to include <name>, which can be the filename given * in an #include directive, or a descriptive name for a different source. * The full (file)name of the source as seen by the lexer is <filename>. * This will be include depth <depth>. * <delim> is either '"' or '>' if this include is from a file, or ')' * if it's a different source. * * Result is the offset of the include information in the mem_block. * It is to be considered a handle and has to be passed to * store_include_end(). */ { mp_uint rc; /* Generate and store the plain include information */ { include_t inc; char * tmp; size_t len; /* Make sure that the filename starts with a leading slash, * then make it a tabled string and store it. */ if (*filename != '/') { tmp = alloca(strlen(filename)+2); if (tmp == NULL) { yyerror("Out of stack memory: copy of filename"); } else { *tmp = '/'; strcpy(tmp+1, filename); filename = tmp; } } inc.filename = make_shared_string(filename); if (inc.filename == NULL) { inc.filename = ref_string(STR_DEFAULT); yyerror("Out of memory: sharing include filename"); } /* Surround the <name> with the delimiters, then * make it a tabled string and store it. */ len = strlen(name); tmp = alloca(len+3); if (tmp == NULL) { yyerror("Out of stack memory: copy of name"); } else { *tmp = delim == '"' ? delim : (delim == '>' ? '<' : '('); strcpy(tmp+1, name); tmp[len+1] = delim; tmp[len+2] = '\0'; inc.name = make_shared_string(tmp); if (inc.name == NULL) { inc.name = ref_string(STR_DEFAULT); yyerror("Out of memory: sharing include name"); } } /* Complete the structure and store it */ inc.depth = depth; rc = mem_block[A_INCLUDES].current_size; add_to_mem_block(A_INCLUDES, &inc, sizeof inc); } /* Store the information for the linenumber tracing */ { if (last_include_start == mem_block[A_LINENUMBERS].current_size) { simple_includes++; } else { simple_includes = 0; } stored_lines++; /* don't count the #include line */ /* Use up the amounts of lines collected */ while (stored_lines < current_line) { int lines; lines = current_line - stored_lines; if (lines > LI_MAXEMPTY) lines = LI_MAXEMPTY; stored_lines += lines; byte_to_mem_block(A_LINENUMBERS, 256 - lines); } /* Store the bytecode and mark the position */ byte_to_mem_block(A_LINENUMBERS, LI_INCLUDE); last_include_start = mem_block[A_LINENUMBERS].current_size; /* Restart linecount */ stored_lines = 0; } return rc; } /* store_include_info() */ /*-------------------------------------------------------------------------*/ void store_include_end (mp_uint inc_offset) /* The current include ended. <inc_offset> has to be the offset returned by * store_include_info() for this include file. * <current_line> is already supposed to be the restored value from the * including file. */ { unsigned char c; stored_lines = current_line-1; if (last_include_start == mem_block[A_LINENUMBERS].current_size) { include_t * inc = (include_t *)(mem_block[A_INCLUDES].block + inc_offset); /* No code was generated in this include - remove the * information stored by store_include_info(). * line number information stored by store_include_info() * and tag the include information in A_INCLUDES. */ last_include_start = mem_block[A_LINENUMBERS].current_size - 1; stored_lines--; while (last_include_start && (c = mem_block[A_LINENUMBERS].block[last_include_start - 1]) >= 0x100 - LI_MAXEMPTY) { stored_lines += c - 0x100; last_include_start--; } mem_block[A_LINENUMBERS].current_size = last_include_start; if (--simple_includes < 0) { last_include_start--; } inc->depth = -inc->depth; } else { /* Store the include end and correct the linenumber */ byte_to_mem_block(A_LINENUMBERS, LI_INCLUDE_END); } } /* store_include_end() */ /*-------------------------------------------------------------------------*/ static void prolog (void) /* Initialize the compiler environment prior to a compile. */ { int i; ident_t *id; /* Initialize the memory for the argument types */ if (type_of_arguments.block == NULL) { type_of_arguments.max_size = 100; type_of_arguments.block = xalloc(type_of_arguments.max_size); } type_of_arguments.current_size = 0; /* Initialize all the globals */ last_expression = -1; compiled_prog = NULL; /* NULL means fail to load. */ heart_beat = -1; comp_stackp = 0; /* Local temp stack used by compiler */ current_continue_address = 0; current_break_address = 0; num_parse_error = 0; block_depth = 0; use_local_scopes = MY_TRUE; default_varmod = 0; default_funmod = 0; free_all_local_names(); /* In case of earlier error */ /* Initialize memory blocks where the result of the compilation * will be stored. */ for (i = 0; i < NUMAREAS; i++) { mem_block[i].block = xalloc(START_BLOCK_SIZE); mem_block[i].current_size = 0; mem_block[i].max_size = START_BLOCK_SIZE; } stored_lines = 0; stored_bytes = 0; last_include_start = -1; memset(prog_string_tags, 0, sizeof prog_string_tags); num_virtual_variables = 0; case_state.free_block = NULL; case_state.next_free = NULL; %ifdef INITIALIZATION_BY___INIT last_initializer_end = -4; /* To pass the test in transfer_init_control() */ variables_initialized = 0; %endif /* Check if call_other() has been replaced by a sefun. */ call_other_sefun = -1; id = make_shared_identifier(STR_CALL_OTHER, I_TYPE_UNKNOWN, 0); if (!id) fatal("Out of memory: identifier '%s'.\n", STR_CALL_OTHER); if (id->type == I_TYPE_UNKNOWN) { /* No such identifier, therefor no such sefun */ free_shared_identifier(id); } else { /* This shouldn't be necessary, but just in case... */ while (id && id->type > I_TYPE_GLOBAL) id = id->inferior; if ( id && id->u.global.function < 0 && id->u.global.sim_efun >= 0) { /* There is a sefun for call_other() */ call_other_sefun = id->u.global.sim_efun; } } } /* prolog() */ /*-------------------------------------------------------------------------*/ static void epilog (void) /* The parser finished - now collect the information and generate * the program structure, if the parse was successful. */ { int size, i; mp_int num_functions; mp_int num_strings; mp_int num_variables; bytecode_p p; ident_t *g, *q; function_t *f; function_t *funname_start1; /* The name chains (to sort) */ function_t *funname_start2; mp_int num_function_names; program_t *prog; /* First, clean up */ #ifdef DEBUG if (num_parse_error == 0 && type_of_arguments.current_size != 0) fatal("Failed to deallocate argument type stack\n"); #endif if (last_string_constant) { free_string(last_string_constant); last_string_constant = NULL; } free_case_blocks(); /* Append the non-virtual variable block to the virtual ones, * and take care of the initializers. */ if (V_VARIABLE_COUNT > 0x100) { yyerror("Too many virtual variables"); } add_to_mem_block( A_VIRTUAL_VAR, mem_block[A_VARIABLES].block, mem_block[A_VARIABLES].current_size ); mem_block[A_VARIABLES].current_size = 0; %ifndef INITIALIZATION_BY___INIT /* Just add the non-virtual values to the virtual block */ add_to_mem_block( A_VIRTUAL_VAR_VALUES, mem_block[A_VARIABLE_VALUES].block, mem_block[A_VARIABLE_VALUES].current_size ); mem_block[A_VARIABLE_VALUES].current_size = 0; %else /* Define the __INIT function, but only if there was any code * to initialize. */ if (last_initializer_end > 0) { ident_t *ip; ip = make_global_identifier("__INIT", I_TYPE_UNKNOWN); if (ip) define_new_function(MY_FALSE, ip, 0, 0, first_initializer_start, TYPE_MOD_PROTECTED, 0); /* ref count for ip->name was incremented by transfer_init_control() */ /* Change the last jump after the last initializer into a * return(1) statement. */ mem_block[A_PROGRAM].block[last_initializer_end-1] = F_CONST1; mem_block[A_PROGRAM].block[last_initializer_end-0] = F_RETURN; } /* if (has initializer) */ %endif /* INITIALIZATION_BY___INIT */ /* Check the string block. We don't have to count the include file names * as those won't be accessed from the program code. */ if (mem_block[A_STRINGS].current_size > 0x10000 * sizeof (char *)) yyerror("Too many strings"); /* Get and check the numbers of functions, strings, and variables */ num_functions = FUNCTION_COUNT; if (num_functions > 0x10000) { yyerror("Too many functions"); } num_strings = STRING_COUNT; num_variables = V_VARIABLE_COUNT; if (num_variables >= VIRTUAL_VAR_TAG) { yyerror("Too many variables"); } num_function_names = 0; if (!num_parse_error && !inherit_file) { /* If the parse was successful, fill in undefined functions, * resolve cross-defines, and sort the program names with mergesort. */ function_t **link1, **link2; /* Linkpointer for the sort */ f = (function_t *)mem_block[A_FUNCTIONS].block; link1 = &funname_start2; link2 = &funname_start1; for (i = num_functions; --i >= 0; f++) { funflag_t flags; /* If the function was cross-defined, the targeted function might * be a cross-definition itself. Unravel such a cross-definition * chain and let f->offset.func point to the actual definition. */ if ( f->flags & NAME_CROSS_DEFINED ) { int32 offset; offset = GET_CROSSDEF_OFFSET(f->offset.func); while (f[offset].flags & NAME_CROSS_DEFINED) { f->offset.func = offset + f[offset].offset.func; offset = GET_CROSSDEF_OFFSET(f->offset.func); } } /* If the function is undefined, generate a dummy function * with ESCAPE UNDEF as body. * Except __INIT, which is created as CONST1 RETURN. */ if ((f->flags & (NAME_UNDEFINED|NAME_INHERITED)) == NAME_UNDEFINED) { #ifdef ALIGN_FUNCTIONS CURRENT_PROGRAM_SIZE = align(CURRENT_PROGRAM_SIZE); #endif if (!realloc_a_program(FUNCTION_HDR_SIZE + 2)) { yyerrorf("Out of memory: program size %lu\n" , CURRENT_PROGRAM_SIZE + FUNCTION_HDR_SIZE + 2); } else { ref_string(f->name); f->offset.pc = CURRENT_PROGRAM_SIZE + sizeof f->name + 1; p = PROGRAM_BLOCK + CURRENT_PROGRAM_SIZE; memcpy(p, (char *)&f->name, sizeof f->name); p += sizeof f->name; *p++ = f->type; *p++ = f->num_arg; *p++ = f->num_local; %ifdef INITIALIZATION_BY___INIT /* If __INIT() is undefined (i.e. there was a prototype, but * no explicit function nor the automagic initialization code, * then a dummy function is generated. This prevents crashes * when this program is inherited later. */ if (f->name[0] == '_' && !strcmp(f->name, "__INIT") && !f->num_arg) { f->flags &= ~NAME_UNDEFINED; *p++ = F_CONST1; *p = F_RETURN; } else { %endif *p++ = F_ESCAPE; *p = F_UNDEF-0x100; %ifdef INITIALIZATION_BY___INIT } %endif CURRENT_PROGRAM_SIZE += sizeof f->name + 5; } } /* Set the function address resp. inherit index in * the function's flags. */ flags = f->flags; f->flags = flags & NAME_INHERITED ? (flags & ~INHERIT_MASK) | (f->offset.inherit & INHERIT_MASK) : (flags & ~FUNSTART_MASK) | (f->offset.pc & FUNSTART_MASK); /* If the function is visible, add it to the list of names * to be sorted. */ if ( !(flags & (NAME_HIDDEN|NAME_UNDEFINED|TYPE_MOD_PRIVATE) ) ) { *link1 = f; link1 = link2; link2 = &f->offset.next; num_function_names++; } } /* End the two chains */ *link1 = NULL; *link2 = NULL; /* Store line number info for undefined functions */ store_line_number_info(); /* Sort the function names */ if (num_function_names <= 1) { /* Nothing to sort */ funname_start1 = funname_start2; } else { /* Mergesort again. * TODO: Make this a standard function. */ int runlength; runlength = 1; do { function_t *out_start1, *out_start2, **out1, **out2; int count1, count2; count1 = num_function_names & (runlength-1); count2 = num_function_names & runlength; if (!count1) { out2 = &out_start1; *out2 = funname_start2; while (--count2 >= 0) { out2 = &(*out2)->offset.next; } funname_start2 = *out2; count1 = count2 = runlength; out1 = &out_start2; } else if (!count2) { out2 = &out_start1; *out2 = funname_start1; do { out2 = &(*out2)->offset.next; } while (--count1); funname_start1 = *out2; count1 = count2 = runlength; out1 = &out_start2; } else { out1 = &out_start1; out2 = &out_start2; } while (funname_start1) { while (1) { #ifdef ALIGN_FUNCTIONS if ((funname_start1->name - funname_start2->name) < 0) #else /* must use memcmp(), because it is used later for the * program. byteorder is non-portable. */ if (memcmp( &funname_start2->name, &funname_start1->name, sizeof(char *) ) < 0) #endif { *out1 = funname_start2; out1 = &funname_start2->offset.next; funname_start2 = *out1; if (!--count2) { *out1 = funname_start1; do { out1 = &(*out1)->offset.next; } while (--count1); funname_start1 = *out1; break; } } else { *out1 = funname_start1; out1 = &funname_start1->offset.next; funname_start1 = *out1; if (!--count1) { *out1 = funname_start2; do { out1 = &(*out1)->offset.next; } while (--count2); funname_start2 = *out1; break; } } } { function_t **temp; temp = out1; out1 = out2; out2 = temp; } count1 = count2 = runlength; } *out1 = NULL; *out2 = NULL; funname_start1 = out_start1; funname_start2 = out_start2; runlength <<= 1; } while (runlength < num_function_names); } /* end of sort */ /* either funname_start1 or funname_start2 now has the * sorted list of function names. */ /* If the program is too large, make sure that the * name strings are freed again. */ if (CURRENT_PROGRAM_SIZE > FUNSTART_MASK) { function_t *functions; yyerror("Program too large"); functions = (function_t *)mem_block[A_FUNCTIONS].block; for (i = num_functions; --i >= 0; functions++) { if ( !(functions->flags & (NAME_UNDEFINED|NAME_INHERITED)) == NAME_UNDEFINED) { free_string(functions->name); } } } /* Done: functions are sorted, resolved, etc etc */ } /* if (parse successful) */ %ifndef INITIALIZATION_BY___INIT /* Return the variable initializers to the caller */ prog_variable_values = (svalue_t *)mem_block[A_VIRTUAL_VAR_VALUES].block; %endif /* INITIALIZATION_BY___INIT */ /* Free unneeded memory */ free_all_local_names(); for (q = all_globals; NULL != (g = q); ) { q = g->next_all; free_shared_identifier(g); } while(last_yalloced) { yfree(last_yalloced); debug_message("%s freeing lost block\n", time_stamp()); } if (all_efun_shadows) { efun_shadow_t *s, *t; for (t = all_efun_shadows; NULL != (s = t); ) { s->shadow->u.global.function = I_GLOBAL_FUNCTION_EFUN; s->shadow->u.global.variable = I_GLOBAL_VARIABLE_FUN; t = s->next; xfree(s); } all_efun_shadows = NULL; } all_globals = NULL; /* Now create the program structure */ switch (0) { default: /* One error, don't create anything */ if (num_parse_error > 0 || inherit_file) break; /* Compute the size of the program. * Right now, we allocate everything in one block. */ size = align(sizeof (program_t)); if (!pragma_save_types) { mem_block[A_ARGUMENT_TYPES].current_size = 0; mem_block[A_ARGUMENT_INDEX].current_size = 0; } for (i=0; i<NUMPAREAS; i++) { if (i != A_LINENUMBERS) size += align(mem_block[i].current_size); } size += align(num_function_names * sizeof *prog->function_names); size += align(num_functions * sizeof *prog->functions); /* Get the program structure */ if ( !(p = xalloc(size)) ) { yyerrorf("Out of memory: program structure (%u bytes)", size); break; } prog = (program_t *)p; *prog = NULL_program; /* Set up the program structure */ if ( !(prog->name = string_copy(current_file)) ) { xfree(prog); yyerrorf("Out of memory: filename '%s'", current_file); break; } prog->blueprint = NULL; prog->total_size = size; prog->ref = 0; prog->heart_beat = heart_beat; prog->id_number = ++current_id_number ? current_id_number : renumber_programs(); prog->flags = (pragma_no_clone ? P_NO_CLONE : 0) | (pragma_no_inherit ? P_NO_INHERIT : 0) | (pragma_no_shadow ? P_NO_SHADOW : 0); prog->load_time = current_time; total_prog_block_size += prog->total_size + strlen(prog->name)+1; total_num_prog_blocks += 1; p += align(sizeof (program_t)); /* Add the program code */ prog->program = p; if (mem_block[A_PROGRAM].current_size) memcpy(p, mem_block[A_PROGRAM].block, mem_block[A_PROGRAM].current_size); p += align(mem_block[A_PROGRAM].current_size); /* Add the function names right after the program code */ prog->num_function_names = num_function_names; prog->function_names = (unsigned short *)p; { unsigned short *namep; namep = (unsigned short *)p; if ( NULL != (f = funname_start1) || NULL != (f = funname_start2) ) { do { *namep++ = f - (function_t *)mem_block[A_FUNCTIONS].block; } while ( NULL != (f = f->offset.next) ); } } p += align(num_function_names * sizeof *prog->function_names); /* Add the function flags */ prog->num_functions = num_functions; prog->functions = (funflag_t *)p; { funflag_t *flagp; f = (function_t *)mem_block[A_FUNCTIONS].block; flagp = (funflag_t *)p; for (i = num_functions; --i >= 0; f++) { *flagp++ = f->flags; } } p += align(num_functions * sizeof *prog->functions); /* Add the program strings */ prog->strings = (char **)p; prog->num_strings = num_strings; if (mem_block[A_STRINGS].current_size) memcpy(p, mem_block[A_STRINGS].block, mem_block[A_STRINGS].current_size); p += align(mem_block[A_STRINGS].current_size); /* Add the variable descriptions */ prog->variable_names = (variable_t *)p; prog->num_variables = num_variables; if (mem_block[A_VIRTUAL_VAR].current_size) memcpy(p, mem_block[A_VIRTUAL_VAR].block, mem_block[A_VIRTUAL_VAR].current_size); p += align(mem_block[A_VIRTUAL_VAR].current_size); /* Add the inheritance information, and don't forget * to delete our internal flags. */ prog->num_inherited = mem_block[A_INHERITS].current_size / sizeof (inherit_t); if (prog->num_inherited) { memcpy(p, mem_block[A_INHERITS].block, mem_block[A_INHERITS].current_size); prog->inherit = (inherit_t *)p; } else { prog->inherit = NULL; } p += align(mem_block[A_INHERITS].current_size); /* Add the include file information */ prog->num_includes = INCLUDE_COUNT; if (prog->num_includes) { memcpy(p, mem_block[A_INCLUDES].block , mem_block[A_INCLUDES].current_size); prog->includes = (include_t *)p; } else prog->includes = NULL; p += align(mem_block[A_INCLUDES].current_size); /* Add the argument type information */ if (pragma_save_types) { if (mem_block[A_ARGUMENT_TYPES].current_size) memcpy(p, mem_block[A_ARGUMENT_TYPES].block, mem_block[A_ARGUMENT_TYPES].current_size); prog->argument_types = (unsigned short *)p; p += align(mem_block[A_ARGUMENT_TYPES].current_size); if (mem_block[A_ARGUMENT_INDEX].current_size) memcpy(p, mem_block[A_ARGUMENT_INDEX].block, mem_block[A_ARGUMENT_INDEX].current_size); prog->type_start = (unsigned short *)p; p += align(mem_block[A_ARGUMENT_INDEX].current_size); } else { prog->argument_types = NULL; prog->type_start = NULL; } /* Add the linenumber information. */ { size_t linenumber_size; linenumber_size = mem_block[A_LINENUMBERS].current_size + sizeof(linenumbers_t); if ( !(prog->line_numbers = xalloc(linenumber_size)) ) { total_prog_block_size -= prog->total_size + strlen(prog->name)+1; total_num_prog_blocks -= 1; xfree(prog); yyerrorf("Out of memory: linenumber structure (%lu bytes)" , (unsigned long)linenumber_size); break; } total_prog_block_size += linenumber_size; prog->line_numbers->size = linenumber_size; if (mem_block[A_LINENUMBERS].current_size) memcpy( prog->line_numbers->line_numbers , mem_block[A_LINENUMBERS].block , mem_block[A_LINENUMBERS].current_size); } /* Correct the variable index offsets */ fix_variable_index_offsets(prog); prog->swap_num = -1; /* Free the memareas */ for (i = 0; i < NUMAREAS; i++) { %ifndef INITIALIZATION_BY___INIT /* Don't free this, the caller is going to * need them. */ if (i == A_VIRTUAL_VAR_VALUES) continue; %endif /* INITIALIZATION_BY___INIT */ xfree(mem_block[i].block); } /* Reference the program and all inherits, but avoid multiple * referencing when an object inherits more than one object * and one of the inherited is already loaded and not the * last inherited. */ reference_prog(prog, "epilog"); for (i = 0; i < prog->num_inherited; i++) { reference_prog(prog->inherit[i].prog, "inheritance"); } /* Return the value */ compiled_prog = prog; return; } /* If we come here, the program couldn't be created - just * free all memory. */ { function_t *functions; %ifndef INITIALIZATION_BY___INIT for (i = 0; i < num_variables; i++) free_svalue(&prog_variable_values[i]); %endif /* INITIALIZATION_BY___INIT */ /* Free all function names. */ functions = (function_t *)mem_block[A_FUNCTIONS].block; for (i = num_functions; --i >= 0; functions++) if ( !(functions->flags & (NAME_INHERITED|NAME_UNDEFINED)) && functions->name ) { free_string(functions->name); } do_free_sub_strings( num_strings , (char **)mem_block[A_STRINGS].block , num_variables , (variable_t *)mem_block[A_VIRTUAL_VAR].block , INCLUDE_COUNT , (include_t *)mem_block[A_INCLUDES].block ); compiled_prog = NULL; for (i=0; i<NUMAREAS; i++) xfree(mem_block[i].block); return; } /* NOTREACHED */ } /* epilog() */ /*-------------------------------------------------------------------------*/ void compile_file (int fd) /* Compile an LPC file. See the head comment for instructions. */ { prolog(); start_new_file(fd); yyparse(); /* If the parse failed, either num_parse_error != 0 * or inherit_file != NULL here. */ epilog(); end_new_file(); } /* compile_file() */ /*-------------------------------------------------------------------------*/ #if defined( DEBUG ) && defined ( TRACE_CODE ) static int code_window_offset = -1; void set_code_window (void) /* #pragma set_code_window: Remember the current program position. */ { code_window_offset = CURRENT_PROGRAM_SIZE; } void show_code_window (void) /* #pragma show_code_window: Print 32 bytes following the last * position remembered with set_code_window to stdout. */ { int i; bytecode_p p; if (code_window_offset < 0) return; p = (bytecode_p)mem_block[A_PROGRAM].block + code_window_offset; for (i = 0; i < 32; i++) { printf("%3d ", p[i]); } printf("\n"); fflush(stdout); } /* show_code_window() */ #endif /*-------------------------------------------------------------------------*/ #ifdef GC_SUPPORT void count_compiler_refs (void) /* GC support: mark the memory held by the compiler environment. */ { if (type_of_arguments.block) { note_malloced_block_ref(type_of_arguments.block); } } #endif #if defined(__MWERKS__) && !defined(WARN_ALL) # pragma warn_possunwant off # pragma warn_implicitconv off #endif /*-------------------------------------------------------------------------*/ /***************************************************************************/