%{ %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:,...,TYPE_: * Generates a lookup table TYPE_ -> . Unspecified * TYPEs are given the value 0. * * %hookmap :,...,: * Generates a lookup table -> . Unspecified * driverhook entries are given the value 0. * * In addition, make_func implements a simple preprocessor using the * keywords %if, %elif, %else and %endif so that parsing rules can be * activated/deactivated from config.h defines. *--------------------------------------------------------------------------- * To compile a file, open the file 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 that the compiler is not called * recursively. * * 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. *--------------------------------------------------------------------------- * 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... * #ifdef USE_NEW_INLINES * Another challenge is the compilation of inline closures, as they * are implemented as separate lfuns (with synthetic names), but encountered * in the middle of a regular lfun or even another inline closure. The * compiler therefore stores his state when it recognizes another * inline closures, and then resets its state as if a normal lfun is * compiled. When the inline closure is complete, its data is moved into * a backup storage area, and the compiler restores its previous state. #endif *--------------------------------------------------------------------------- */ #undef lint /* undef so that precompiled headers can be used */ #include "driver.h" #include "typedefs.h" #include "my-alloca.h" #include #include #include #include #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 "mstrings.h" #include "object.h" #include "simulate.h" #include "simul_efun.h" #include "stdstrings.h" #ifdef USE_STRUCTS #include "structs.h" #endif #include "svalue.h" #include "swap.h" #include "switch.h" #include "wiz_list.h" #include "xalloc.h" #include "i-eval_cost.h" #include "../mudlib/sys/driver_hook.h" #ifdef USE_NEW_INLINES # undef DEBUG_INLINES /* Define this to activate lots of debugging output during the compilation * of inline closures. */ #endif /* USE_NEW_INLINES */ #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; #ifdef USE_STRUCTS typedef struct struct_init_s struct_init_t; #endif /* USE_STRUCTS */ 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. */ string_t *inherit_file; /* Used as a flag: if it is set to a tabled 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. */ Bool variables_defined; /* TRUE: Variable definitions have been encountered. */ /*-------------------------------------------------------------------------*/ /* 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), \ H_DEFAULT_METHOD: SH(T_CLOSURE) SH(T_STRING), \ H_DEFAULT_PROMPT: SH(T_CLOSURE) SH(T_STRING), \ H_PRINT_PROMPT: SH(T_CLOSURE) SH(T_STRING), \ H_REGEXP_PACKAGE: SH(T_NUMBER), \ #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; #ifdef USE_STRUCTS string_t * member; /* NULL, or the member name to initialize */ #endif }; /* --- 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 */ #ifdef USE_STRUCTS const_list_t *last_member; /* For nested struct initialisations */ #endif /* USE_STRUCTS */ }; #ifdef USE_STRUCTS /* --- struct struct_init_s: Descriptor for one struct literal member * * When createing struct literals at runtime, a list of these structures * keeps the information about the order and type of members encountered. */ struct struct_init_s { struct_init_t * next; /* Next member entry */ vartype_t type; /* Type of expression */ string_t * name; /* Member name, or NULL if unnamed */ }; #endif /* USE_STRUCTS */ /* --- 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 if global (and therefore existing), * and -1 otherwise. */ #ifdef USE_STRUCTS #define set_vartype(t, id, ptr) \ ( t.type = (id), t.t_struct = (ptr) ) #define set_fulltype(t, id, ptr) \ ( t.typeflags = (id), t.t_struct = (ptr) ) #else #define set_vartype(t, id, ptr) \ ( t.type = (id) ) #define set_fulltype(t, id, ptr) \ ( t.typeflags = (id) ) #endif /* Set the full/vartype to type(flags) and the struct * typeobject pointer to . */ #define NEW_INHERITED_INDEX (0xfffff) /* While inserting a new inherit, this marks the newly inherited * things. */ /* Values for %type foreach_expr */ #define FOREACH_LOOP 0 /* Normal foreach loop value */ #define FOREACH_REF 1 /* Referenced foreach loop value */ #define FOREACH_RANGE 2 /* Integer range as loop value */ /*-------------------------------------------------------------------------*/ /* 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. */ enum e_saved_areas { A_PROGRAM = 0 /* (bytecode_t): Program code. */ , A_STRINGS /* (string_t*) Strings used by the program, all tabled. */ , A_VARIABLES /* (variable_t) The information for all non-virtual variables. */ , A_VIRTUAL_VAR /* (variable_t) The information for all virtual variables. */ , A_LINENUMBERS /* (char) The linenumber information. */ , A_INHERITS /* (inherit_t) The information for the inherited programs. */ , A_ARGUMENT_TYPES /* (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 */ , A_ARGUMENT_INDEX /* (unsigned short) Index of the first argument type of function . * INDEX_START_NONE is used for functions with no type information. */ , A_INCLUDES /* (include_t) Tabled descriptors of all included files, in the order * of appearance. */ #ifdef USE_STRUCTS , A_STRUCT_DEFS /* (struct_def_t) Tabled descriptors of all struct definitions. */ #endif /* USE_STRUCTS */ , NUMPAREAS /* Number of saved areas */ }; enum e_internal_areas { A_FUNCTIONS = NUMPAREAS /* (function_t): Function definitions */ , A_STRING_NEXT /* (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. */ , A_LOCAL_TYPES /* (fulltype_t) The full types of local and context variables. #ifdef USE_NEW_INLINES * For normal functions, only the beginning of the area is used. * The rest is used stack-wise for nested inline closures. #endif */ %ifdef USE_NEW_INLINES , A_INLINE_PROGRAM /* (bytecode_t, char): Program and linenumbers saved from the compiled * but not yet inserted inline closures. */ , A_INLINE_CLOSURE /* (inline_closure_t): The currently pending inline closures. The lexical * nesting is achieved with the .prev/.next pointers in the * inline_closure_t structures. */ %endif /* USE_NEW_INLINES */ #ifdef USE_STRUCTS , A_STRUCT_MEMBERS /* (struct_member_t) While a struct definition is parsed, the member * descriptors are collected here. */ #endif /* USE_STRUCTS */ , NUMAREAS /* 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 LINENUMBER_BLOCK ((char *)(mem_block[A_LINENUMBERS].block)) /* The current linenumber block, properly typed. */ #define LINENUMBER_SIZE (mem_block[A_LINENUMBERS].current_size) /* The current linenumber data size. */ #define FUNCTION(n) ((function_t *)mem_block[A_FUNCTIONS].block + (n)) /* Return the function_t* for function number . */ #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 . */ #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 ARGUMENT_TYPE(n) ((vartype_t *)mem_block[A_ARGUMENT_TYPES].block)[n] /* Index the vartype_t . */ #define NV_VARIABLE(n) ((variable_t *)mem_block[A_VARIABLES].block + (n)) /* Return the variable_t* for the non-virtual variable . */ #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 (still including * the offset). */ #define VARIABLE(n) ((n) & VIRTUAL_VAR_TAG ? V_VARIABLE(n) : NV_VARIABLE(n)) /* Return the variable_t* for the variable , virtual or not. */ #define INHERIT(n) ((inherit_t *)mem_block[A_INHERITS].block)[n] /* Index the inherit_t . */ #define INHERIT_COUNT (mem_block[A_INHERITS].current_size / sizeof(inherit_t)) /* Return the number of inherits encountered so far. */ #ifdef USE_STRUCTS #define STRUCT_DEF(n) ((struct_def_t *)mem_block[A_STRUCT_DEFS].block)[n] /* Index the struct_def_t . */ #define STRUCT_COUNT (mem_block[A_STRUCT_DEFS].current_size / sizeof(struct_def_t)) /* Return the number of structs encountered so far. */ #define STRUCT_MEMBER(n) ((struct_member_t *)mem_block[A_STRUCT_MEMBERS].block)[n] /* Index the struct_member_t . */ #define STRUCT_MEMBER_COUNT (mem_block[A_STRUCT_MEMBERS].current_size / sizeof(struct_member_t)) /* Return the number of struct members stored. */ #endif /* USE_STRUCTS */ #define PROG_STRING(n) ((string_t **)mem_block[A_STRINGS].block)[n] /* Index the pointer for program string . */ #define STRING_COUNT (mem_block[A_STRINGS].current_size / sizeof(string_t *)) /* 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 . */ #define INCLUDE_COUNT (mem_block[A_INCLUDES].current_size / sizeof(include_t)) /* Return the total number of include files encountered so far. */ #define LOCAL_TYPE_COUNT (mem_block[A_LOCAL_TYPES].current_size / sizeof(fulltype_t)) /* Return the total number of types. */ #define LOCAL_TYPE(n) ((fulltype_t *)mem_block[A_LOCAL_TYPES].block)[n] /* Return the local/context var type at index . */ %ifdef USE_NEW_INLINES #define INLINE_PROGRAM_BLOCK(n) ((bytecode_p)(mem_block[A_INLINE_PROGRAM].block + (n))) /* Return the inline-closure program block at address , properly typed. */ #define INLINE_PROGRAM_SIZE (mem_block[A_INLINE_PROGRAM].current_size) /* The current program size. */ #define INLINE_CLOSURE(n) ((inline_closure_t *)mem_block[A_INLINE_CLOSURE].block)[n] /* Return the inline-closure program block at address , properly typed. */ #define INLINE_CLOSURE_COUNT (mem_block[A_INLINE_CLOSURE].current_size/sizeof(inline_closure_t)) /* Return the number of saved inline-closures. */ %endif /* USE_NEW_INLINES */ /*-------------------------------------------------------------------------*/ /* 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 */ int num_cleared; /* Number of locals that have been cleared by earlier CLEAR_LOCALS */ Bool clobbered; /* Local variables beyond num_locals may be clobbered */ 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 -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. */ /*-------------------------------------------------------------------------*/ #ifdef USE_NEW_INLINES /* Information describing inline closures. */ typedef struct inline_closure_s inline_closure_t; struct inline_closure_s { mp_int prev; /* Index of the enclosing inline closure, or -1 if none. */ /* --- Compilation information --- */ mp_uint end; /* While compiling the closure: end of the program code before * the closure. It is not identical to .start because of alignment. */ mp_uint start; /* While compiling the closure: start address of the code in A_PROGRAM. * For pending closures: start address of the code in A_INLINE_PROGRAM. */ mp_uint length; /* Length of the compiled code. */ mp_uint li_start; /* While compiling the closure: start address of the data in * A_LINENUMBERS. * For pending closures: start address of the data in A_INLINE_PROGRAM. */ mp_uint li_length; /* Length of the linenumber data. */ int function; /* Function index */ fulltype_t returntype; /* The return type (uncounted reference). */ ident_t * ident; /* The ident entry with the function name. */ int num_args; /* Number of arguments. */ Bool parse_context; /* TRUE if the context variable definitions are parsed. */ int start_line; /* Starting line number, used to adjust the generated linenumbers. */ int end_line; /* Ending line number, used to adjust the generated linenumbers. */ /* --- Saved Globals --- */ void * include_handle; /* Current include state. */ fulltype_t exact_types; /* The enclosing return type setting (reference not counted). */ int block_depth; /* Block depth at definition point. * +1: Context depth * +2: Argument depth */ int num_locals; int max_num_locals; /* Current and max number of locals at definition point. */ int break_stack_size; int max_break_stack_size; /* Current and max break stack size at definition point. */ mp_uint full_local_type_start; mp_uint full_context_type_start; /* Start indices of the local/context variable type information * in A_LOCAL_TYPES. */ mp_uint full_local_type_size; /* Current size of the A_LOCAL_TYPES memblocks. */ }; static inline_closure_t * current_inline; /* NULL, or pointer to the current inline_closure_t structure while * compiling an inline closure. * This variable is also used as flag that we're currently compiling * an inline_closure. */ static unsigned int inline_closure_id; /* ID Number for the inline closure name. */ #endif /* USE_NEW_INLINES */ /*-------------------------------------------------------------------------*/ /* Other Variables */ static Bool disable_sefuns; /* TRUE: Sefuns will be ignored. */ 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 . */ 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. */ static fulltype_t def_function_returntype; static ident_t * def_function_ident; static int def_function_num_args; /* Globals to keep the state while a function is parsed: * _returntype: the returntype (uncounted reference) * _ident: the function's identifier. * _num_args: number of formal arguments. */ 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 fulltype_t * type_of_locals = NULL; /* The full types of the local variables. * Points to a location in mem_block A_LOCAL_TYPES, it is NULL between * compilations. */ #ifdef USE_NEW_INLINES static fulltype_t * type_of_context = NULL; /* The full types of the context variables. * Points to a location in mem_block A_LOCAL_TYPES, it is NULL between * compilations. */ #endif /* USE_NEW_INLINES */ 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. */ static fulltype_t exact_types; /* If .typeflags is 0, don't check nor require argument and function types. * Otherwise it's the full return type of the function, including * visibility (reference not counted). */ static funflag_t default_varmod; static funflag_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 FBRANCH is marked by having a negative offset value. * * There are a few special values/flags for this variable: */ #define BREAK_ADDRESS_MASK 0x0003ffff /* Mask for the offset-address part of the 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 FBRANCHes * which implement the continue statement. Stored in every offset-part * is the address of the offset of the next FBRANCH in the series. The * last FBRANCH 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). */ #ifdef USE_STRUCTS static int current_struct; /* Index of the current structure to be defined. */ #endif /* USE_STRUCTS */ 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 (unsigned)-1. */ 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 string_t *last_string_constant = NULL; /* The current (last) string constant, a tabled 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'). */ static int argument_level; /* Nesting level of function call arguments. * Used to detect nested function calls, like foo( bar () ). */ static Bool got_ellipsis[COMPILER_STACK_SIZE]; /* Flags indexed by , telling if the current function * arguments used the L_ELLIPSIS operator. * TODO: This should be dynamic. */ #ifdef USE_STRUCTS static const char * compiled_file; /* The name of the program to be compiled. While current_loc.file reflects * the name of the source file currently being read, this name is always * the program's name. Set by prolog(). */ static const fulltype_t Type_Any = { TYPE_ANY, NULL }; static const fulltype_t Type_Unknown = { TYPE_UNKNOWN, NULL }; static const vartype_t VType_Unknown = { TYPE_UNKNOWN, NULL }; static const fulltype_t Type_Number = { TYPE_NUMBER, NULL }; static const fulltype_t Type_Float = { TYPE_FLOAT, NULL }; static const fulltype_t Type_String = { TYPE_STRING, NULL }; static const fulltype_t Type_Object = { TYPE_OBJECT, NULL }; static const fulltype_t Type_Closure = { TYPE_CLOSURE, NULL }; static const fulltype_t Type_Mapping = { TYPE_MAPPING, NULL }; static const fulltype_t Type_Symbol = { TYPE_SYMBOL, NULL }; static const fulltype_t Type_Void = { TYPE_VOID, NULL }; static const fulltype_t Type_Quoted_Array = { TYPE_QUOTED_ARRAY, NULL }; static const fulltype_t Type_Ptr_Any = { TYPE_ANY|TYPE_MOD_POINTER, NULL }; static const fulltype_t Type_Ref_Any = { TYPE_ANY|TYPE_MOD_REFERENCE, NULL }; static const fulltype_t Type_Ref_Number = { TYPE_NUMBER|TYPE_MOD_REFERENCE, NULL }; #else static const fulltype_t Type_Any = { TYPE_ANY }; static const fulltype_t Type_Unknown = { TYPE_UNKNOWN }; static const vartype_t VType_Unknown = { TYPE_UNKNOWN }; static const fulltype_t Type_Number = { TYPE_NUMBER }; static const fulltype_t Type_Float = { TYPE_FLOAT }; static const fulltype_t Type_String = { TYPE_STRING }; static const fulltype_t Type_Object = { TYPE_OBJECT }; static const fulltype_t Type_Closure = { TYPE_CLOSURE }; static const fulltype_t Type_Mapping = { TYPE_MAPPING }; static const fulltype_t Type_Symbol = { TYPE_SYMBOL }; static const fulltype_t Type_Void = { TYPE_VOID }; static const fulltype_t Type_Quoted_Array = { TYPE_QUOTED_ARRAY }; static const fulltype_t Type_Ptr_Any = { TYPE_ANY|TYPE_MOD_POINTER }; static const fulltype_t Type_Ref_Any = { TYPE_ANY|TYPE_MOD_REFERENCE }; static const fulltype_t Type_Ref_Number = { TYPE_NUMBER|TYPE_MOD_REFERENCE }; #endif /* USE_STRUCTS */ /* Constants for the known simple types. */ /*-------------------------------------------------------------------------*/ /* Forward declarations */ struct lvalue_s; /* Defined within YYSTYPE aka %union */ static void define_local_variable (ident_t* name, fulltype_t actual_type, typeflags_t opt_star, struct lvalue_s *lv, Bool redeclare, Bool with_init); static void init_local_variable (ident_t* name, struct lvalue_s *lv, int assign_op, fulltype_t type2); 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 *, string_t *, 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); static void add_new_init_jump(void); static void transfer_init_control(void); static void copy_variables(program_t *, funflag_t); static int copy_functions(program_t *, funflag_t type); #ifdef USE_STRUCTS static void copy_structs(program_t *, funflag_t); #endif /* USE_STRUCTS */ #ifdef USE_NEW_INLINES static void new_inline_closure (void); #endif /* USE_NEW_INLINES */ static void fix_function_inherit_indices(program_t *); static void fix_variable_index_offsets(program_t *); static short store_prog_string (string_t *str); /*-------------------------------------------------------------------------*/ void yyerror (const char *str) /* Raise the parse error : 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_loc.file->name, current_loc.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_loc.file->name, current_loc.line , str, context); if (num_parse_error == 0) save_error(str, current_loc.file->name, current_loc.line); num_parse_error++; } /* yyerror() */ /*-------------------------------------------------------------------------*/ void yyerrorf (const 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 (const char *str) /* Raise the parse warning : 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_loc.file->name, current_loc.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_loc.file->name, current_loc.line , str, context); if (master_ob && num_parse_error == 0) save_error(str, current_loc.file->name, current_loc.line); /* TODO: Introduce a 'master_is_loading' flag to prevent this call while * TODO:: the master is inactive. */ } /* yywarn() */ /*-------------------------------------------------------------------------*/ void yywarnf (const 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 , 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 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 to the string in . * This is used to optimize "foo" + "bar" constructs. */ { string_t *tmp; tmp = mstr_add(last_string_constant, last_lex_string); if (!tmp) { yyerrorf("Out of memory for string literal (%zu bytes)" , (mstrsize(last_string_constant) +mstrsize(last_lex_string)) ); return; } free_mstring(last_string_constant); free_mstring(last_lex_string); last_lex_string = NULL; last_string_constant = make_tabled(tmp); if (!last_string_constant) { yyerrorf("Out of memory for string literal (%zu bytes)", mstrsize(tmp)); } } /* add_string_constant() */ /*-------------------------------------------------------------------------*/ static char * realloc_mem_block (mem_block_t *mbp, mp_int size) /* Resize memblock to hold at least 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 ((mp_uint)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 extend_mem_block (int n, size_t size) /* Reserve bytes at the current position in memory area . * This does increase the .current_size . */ { 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; } mbp->current_size += size; } } /* extend_mem_block() */ /*-------------------------------------------------------------------------*/ static INLINE void add_to_mem_block (int n, void *data, size_t size) /* Add the block of bytes to the memory area . */ { 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 to the memory area , which is resized * if necessary. */ /* ============================== TYPES ============================== */ /*-------------------------------------------------------------------------*/ static INLINE void assign_full_to_vartype(vartype_t * dest, fulltype_t src) /* Assign a fulltype_t variable to a vartype_t variable. */ { dest->type = src.typeflags; #ifdef USE_STRUCTS dest->t_struct = src.t_struct; #endif } /* assign_full_to_vartype() */ /*-------------------------------------------------------------------------*/ static INLINE void assign_var_to_fulltype(fulltype_t * dest, vartype_t src) /* Assign a vartype_t variable to a fulltype_t variable. */ { dest->typeflags = src.type; #ifdef USE_STRUCTS dest->t_struct = src.t_struct; #endif } /* assign_var_to_fulltype() */ /*-------------------------------------------------------------------------*/ static INLINE Bool equal_types (fulltype_t e, fulltype_t t) /* Return TRUE if and are compatible basic types. */ { return (e.typeflags & TYPEID_MASK) == (t.typeflags & TYPEID_MASK) #ifdef USE_STRUCTS && e.t_struct == t.t_struct #endif ; } /* equal_types() */ static INLINE Bool basic_type (typeflags_t e, typeflags_t t) /* Return TRUE if and are compatible basic types. */ { e &= TYPEID_MASK; t &= TYPEID_MASK; return e == TYPE_ANY || e == t || t == TYPE_ANY ; } /* basic_type() */ static INLINE Bool BASIC_TYPE (fulltype_t e, fulltype_t t) /* Return TRUE if and are compatible basic types. */ { return basic_type(e.typeflags, t.typeflags); } /* BASIC_TYPE() */ static INLINE Bool TYPE (fulltype_t e, fulltype_t t) /* Return TRUE if and are compatible basic xor pointer types. */ { typeflags_t ef = e.typeflags & TYPEID_MASK; typeflags_t tf = t.typeflags & TYPEID_MASK; return basic_type(ef & TYPE_MOD_MASK, tf & TYPE_MOD_MASK) || ( (ef & TYPE_MOD_POINTER) && (tf & TYPE_MOD_POINTER) && basic_type( ef & (TYPE_MOD_MASK & ~TYPE_MOD_POINTER) , tf & (TYPE_MOD_MASK & ~TYPE_MOD_POINTER)) ) ; } /* TYPE() */ static INLINE Bool vtype (vartype_t e, vartype_t t) /* Return TRUE if and are compatible basic xor pointer types. */ { fulltype_t et, tt; assign_var_to_fulltype(&et, e); assign_var_to_fulltype(&tt, t); return TYPE(et, tt); } /* vtype() */ static INLINE Bool MASKED_TYPE (fulltype_t e, fulltype_t t) /* Return TRUE if and are compatible basic types, or if both * are pointer types and one of them is a *ANY. */ { typeflags_t ef = e.typeflags & TYPEID_MASK; typeflags_t tf = t.typeflags & TYPEID_MASK; return basic_type(ef, tf) || ( ef == (TYPE_MOD_POINTER|TYPE_ANY) && ef & TYPE_MOD_POINTER ) || ( tf == (TYPE_MOD_POINTER|TYPE_ANY) && ef & TYPE_MOD_POINTER ) ; } /* MASKED_TYPE() */ static INLINE Bool REDEFINED_TYPE (fulltype_t e, fulltype_t t) /* Return TRUE if type is a proper redefinition of . * This is the case if and are compatible base types, * or if one of them is *ANY. */ { typeflags_t ef = e.typeflags & TYPEID_MASK; typeflags_t tf = t.typeflags & TYPEID_MASK; return basic_type(ef, tf ) || (tf == (TYPE_MOD_POINTER|TYPE_ANY) ) || (ef == (TYPE_MOD_POINTER|TYPE_ANY) ) ; } /* REDEFINED_TYPE() */ /*-------------------------------------------------------------------------*/ static char * get_f_visibility (funflag_t flags) /* Return (in a static buffer) a textual representation of the visibility * flags in . */ { static char buff[100]; size_t len; buff[0] = '\0'; if (flags & TYPE_MOD_STATIC) strcat(buff, "static "); if (flags & TYPE_MOD_NO_MASK) strcat(buff, "nomask "); if (flags & TYPE_MOD_PRIVATE) strcat(buff, "private "); if (flags & TYPE_MOD_PROTECTED) strcat(buff, "protected "); if (flags & TYPE_MOD_PUBLIC) strcat(buff, "public "); if (flags & TYPE_MOD_VARARGS) strcat(buff, "varargs "); len = strlen(buff); if (len) buff[len-1] = '\0'; return buff; } /* get_f_visibility() */ /*-------------------------------------------------------------------------*/ static char * get_visibility (fulltype_t type) /* Return (in a static buffer) a textual representation of the visibility * portion of . */ { return get_f_visibility(type.typeflags); } /* get_visibility() */ /*-------------------------------------------------------------------------*/ static char * get_type_name (fulltype_t type) /* Return (in a static buffer) a textual representation of . */ { static char buff[100]; static char *type_name[] = { "unknown", "int", "string", "void", "object", "mapping", "float", "mixed", "closure", "symbol", "quoted_array", "struct" }; Bool pointer = MY_FALSE, reference = MY_FALSE; buff[0] = '\0'; if (type.typeflags & TYPE_MOD_STATIC) strcat(buff, "static "); if (type.typeflags & TYPE_MOD_NO_MASK) strcat(buff, "nomask "); if (type.typeflags & TYPE_MOD_PRIVATE) strcat(buff, "private "); if (type.typeflags & TYPE_MOD_PROTECTED) strcat(buff, "protected "); if (type.typeflags & TYPE_MOD_PUBLIC) strcat(buff, "public "); if (type.typeflags & TYPE_MOD_VARARGS) strcat(buff, "varargs "); type.typeflags &= TYPE_MOD_MASK; if (type.typeflags & TYPE_MOD_POINTER) { pointer = MY_TRUE; type.typeflags &= ~TYPE_MOD_POINTER; } if (type.typeflags & TYPE_MOD_REFERENCE) { reference = MY_TRUE; type.typeflags &= ~TYPE_MOD_REFERENCE; } if (type.typeflags >= sizeof type_name / sizeof type_name[0]) fatal("Bad type %"PRIu32": %s line %d\n" , type.typeflags, current_loc.file->name, current_loc.line); strcat(buff, type_name[type.typeflags]); #ifdef USE_STRUCTS if (type.typeflags == TYPE_STRUCT) { strcat(buff, " "); if (type.t_struct) { char buff2[100]; strcat(buff, get_txt(type.t_struct->name)); sprintf(buff2, " %p", type.t_struct); strcat(buff, buff2); } } #endif /* USE_STRUCTS */ 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 "( vs. )". */ { 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() */ #ifdef USE_STRUCTS /*-------------------------------------------------------------------------*/ static char * get_two_vtypes (vartype_t type1, vartype_t type2) /* Return (in a static buffer) the text "( vs. )". */ { fulltype_t ftype1, ftype2; assign_var_to_fulltype(&ftype1, type1); assign_var_to_fulltype(&ftype2, type2); return get_two_types(ftype1, ftype2); } /* get_two_vtypes() */ #endif /* USE_STRUCTS */ /*-------------------------------------------------------------------------*/ static void type_error (char *str, fulltype_t type) /* Generate an yyerror with the message ": ". */ { 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 : ". */ { char *p; p = get_type_name(type); yyerrorf("Bad argument to %s: \"%s\"", instrs[instr].name, p); } /* argument_type_error() */ /*-------------------------------------------------------------------------*/ static void efun_argument_error(int arg, int instr , fulltype_t * expected, fulltype_t got ) { char msg[1024]; msg[0] = '\0'; for (; expected->typeflags; expected++) { if (msg[0] != '\0') strcat(msg, "/"); strcat(msg, get_type_name(*expected)); } yyerrorf("Bad arg %d type to %s(): got %s, expected %s" , arg, instrs[instr].name, get_type_name(got), msg); } /* efun_argument_error() */ /*-------------------------------------------------------------------------*/ static Bool compatible_types (fulltype_t t1, fulltype_t t2, Bool is_assign) /* Compare the two types and 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 is true, it is assumed that will be assigned * to a var of , and the following rules have to match as well: * - a struct is compatible to a derived struct . * - if is a struct, must be a struct or TYPE_ANY. */ { t1.typeflags &= TYPEID_MASK; t2.typeflags &= TYPEID_MASK; if (t1.typeflags == TYPE_UNKNOWN || t2.typeflags == TYPE_UNKNOWN) return MY_FALSE; if (t1.typeflags == TYPE_ANY || t2.typeflags == TYPE_ANY) return MY_TRUE; #ifdef USE_STRUCTS if (is_assign && (t1.typeflags & PRIMARY_TYPE_MASK) == T_STRUCT && (t2.typeflags & PRIMARY_TYPE_MASK) == T_STRUCT ) { struct_type_t * id1, * id2; /* Check if t1 is a base-struct of t2 */ id1 = t1.t_struct; id2 = t2.t_struct; while (id2 != NULL && id1 != id2) { id2 = id2->base; } /* If the base structs match, just pretend that t2 is the * same struct as t1. This will make the following tests * work as normal. */ if (id2 != NULL) t2.t_struct = id1; } #endif if (t1.typeflags == t2.typeflags) { #ifdef USE_STRUCTS if (t1.t_struct == t2.t_struct) return MY_TRUE; #else return MY_TRUE; #endif } if ((t1.typeflags & TYPE_MOD_POINTER) && (t2.typeflags & TYPE_MOD_POINTER)) { if ((t1.typeflags & TYPE_MOD_MASK) == (TYPE_ANY|TYPE_MOD_POINTER) || (t2.typeflags & TYPE_MOD_MASK) == (TYPE_ANY|TYPE_MOD_POINTER)) return MY_TRUE; } #ifdef USE_STRUCTS if (is_assign && (t1.typeflags & PRIMARY_TYPE_MASK) == T_STRUCT) { /* Check if t2 is a struct. If it is, the above * check already made sure that the type matches. */ if ((t2.typeflags & PRIMARY_TYPE_MASK) != T_STRUCT) return MY_FALSE; } #endif return MY_FALSE; } /* compatible_types() */ /*-------------------------------------------------------------------------*/ static INLINE void i_add_arg_type (fulltype_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); } assign_full_to_vartype((vartype_t*)(mbp->block + mbp->current_size), type); mbp->current_size += sizeof(vartype_t); } /* i_add_arg_type() */ #define add_arg_type(t) i_add_arg_type(t) /*-------------------------------------------------------------------------*/ static INLINE void pop_arg_stack (int n) /* Pop (remove) the last types from the argument stack. */ { vartype_t * vp; vp = (vartype_t*)(type_of_arguments.block + type_of_arguments.current_size); while (n > 0) { type_of_arguments.current_size -= sizeof (vartype_t); n--; vp--; } } /* pop_arg_stack() */ /*-------------------------------------------------------------------------*/ static INLINE vartype_t * get_argument_types_start (int n) /* Get the type of the th last argument from the stack. * 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 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; typeid_t 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->type; argp++; } if (!(~mask & 0xffff)) yyerror("Can't trace reference assignments."); } /* check_aggregate_types() */ /*-------------------------------------------------------------------------*/ static INLINE void warn_function_shadow ( const string_t *pubProg, string_t * pubFun , const string_t *privProg, string_t * privFun ) /* Issue a warning that the public function ::() shadows the * private function ::(). * Both and can be NULL. * If the function is __INIT(), no warning is printed. */ { string_t *pubCProg = NULL; string_t *privCProg = NULL; if (mstreq(pubFun, STR_VARINIT) && mstreq(privFun, STR_VARINIT)) return; if (pubProg != NULL) pubCProg = cvt_progname(pubProg); if (privProg != NULL) privCProg = cvt_progname(privProg); if (pubCProg != NULL) { if (privCProg != NULL) yywarnf("public %s::%s() shadows private %s::%s()" , get_txt(pubCProg), get_txt(pubFun) , get_txt(privCProg), get_txt(privFun) ); else yywarnf("public %s::%s() shadows private %s()" , get_txt(pubCProg), get_txt(pubFun) , get_txt(privFun) ); } else if (privCProg != NULL) yywarnf("public %s() shadows private %s::%s()" , get_txt(pubFun) , get_txt(privCProg), get_txt(privFun) ); else yywarnf("public %s() shadows private %s()" , get_txt(pubFun) , get_txt(privFun) ); if (pubCProg != NULL) free_mstring(pubCProg); if (privCProg != NULL) free_mstring(privCProg); } /* warn_function_shadow() */ /* ============================= 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 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 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 %"PRIuMPINT"\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 to the A_PROGRAM area, taking care of encoding * multi-byte instructions properly. */ { if (instrs[b].prefix) ins_byte(instrs[b].prefix); ins_byte(instrs[b].opcode); } /* ins_f_code() */ /*-------------------------------------------------------------------------*/ static void ins_short (long l) /* Add the 2-byte number to the A_PROGRAM area in a fixed byteorder. */ { short s = (short)l; if (l > (long)USHRT_MAX || l < SHRT_MIN) yyerrorf("Compiler error: too large number %lx passed to ins_short()" , l); 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, s); } else { yyerrorf("Out of memory: program size %"PRIuMPINT"\n" , mem_block[A_PROGRAM].current_size + 2); } } /* ins_short() */ /*-------------------------------------------------------------------------*/ static void upd_short (mp_uint offset, long l) /* Store the 2-byte number at in the A_PROGRAM are in * a fixed byteorder. */ { char *dest; short s = (short)l; if (l > (long)USHRT_MAX || l < SHRT_MIN) yyerrorf("Compiler error: too large number %ld passed to upd_short()" , l); dest = mem_block[A_PROGRAM].block + offset; PUT_SHORT(dest, s); } /* upd_short() */ /*-------------------------------------------------------------------------*/ static short read_short (mp_uint offset) /* Return the 2-byte number stored at in the A_PROGRAM area. */ { short l; char *dest; dest = mem_block[A_PROGRAM].block + offset; GET_SHORT(l, dest); return l; } /* read_short() */ /*-------------------------------------------------------------------------*/ static void upd_jump_offset (mp_uint offset, long l) /* Store the 3-byte number at in the A_PROGRAM are in * a fixed byteorder. */ { char *dest; dest = mem_block[A_PROGRAM].block + offset; PUT_3BYTE(dest, l); } /* upd_jump_offset() */ /*-------------------------------------------------------------------------*/ static void ins_int32 (int32 l) /* Add the 4-byte number to the A_PROGRAM area in a fixed byteorder. */ /* TODO: check callers for assumptions that a long is always 4 bytes. */ { 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 %"PRIuMPINT"\n" , mem_block[A_PROGRAM].current_size + 4); } } /* ins_int32() */ /*-------------------------------------------------------------------------*/ static void upd_int32 (mp_uint offset, int32 l) /* Store the 4-byte number at in the A_PROGRAM are in * a fixed byteorder. */ /* TODO: check callers for assumptions that a long is always 4 bytes. */ { char *dest; dest = mem_block[A_PROGRAM].block + offset; PUT_INT32(dest, l); } /* upd_int32() */ /*-------------------------------------------------------------------------*/ static int32 read_int32 (mp_uint offset) /* Return the 4-byte number stored at in the A_PROGRAM area. */ /* TODO: this should probably read in a int32. */ { long l; char *dest; dest = mem_block[A_PROGRAM].block + offset; GET_INT32(l, dest); return l; } /* read_int32() */ /*-------------------------------------------------------------------------*/ static void ins_number (long num) /* Insert code to push number onto the stack. * The function tries to find the shortest sequence to do so. */ { if (num == 0) ins_f_code(F_CONST0); else if (num == 1) { ins_f_code(F_CONST1); } else if (num == -1) ins_f_code(F_NCONST1); else if (num >= 0 && num <= 0x0FF) { ins_f_code(F_CLIT); ins_byte((num & 0xFF)); } else if (num < 0 && num >= -0x0FF) { ins_f_code(F_NCLIT); ins_byte(((-num) & 0xFF)); } else { ins_f_code(F_NUMBER); ins_int32(num); /* this was still ins_long after zippo's patch */ } } /* ins_number() */ /*-------------------------------------------------------------------------*/ /* The following macros are used for a speedy codegeneration within bigger * functions. * * To insert at max bytes, the function has to declare * * PREPARE_INSERT(n) * * among the variables and can the use the following macros to add bytes: * * add_f_code(i): to add instruction to the program * add_byte(b): to add byte to the program * add_short(s): to add short to the program * * Except for add_f_code(), none of the macros adapts CURRENT_PROGRAM_SIZE, * and add_f_code() increments the _SIZE only for the prefix byte if any. */ #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)) #define add_f_code(i) \ do{ if (instrs[i].prefix) { \ add_byte(instrs[i].prefix); \ CURRENT_PROGRAM_SIZE++; \ }\ add_byte(instrs[i].opcode); \ }while(0) /*-------------------------------------------------------------------------*/ 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
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 to jump to . * If the offset exceeds the 255 range, the branch instruction is changed * into its long-branch variant . * * 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 & BREAK_ADDRESS_MASK ; (j = read_int32(i)) > loc; ) { upd_int32(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_int32(i)) > loc; ) { upd_int32(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) yyerrorf("Compiler limit: Too much code to branch over: %" PRIdPINT" bytes", offset); 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 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 %"PRIdMPINT"\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 bytecodes at * back by bytes to +. 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_int32(i)) > switch_pc; ) { upd_int32(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 %"PRIdMPINT"\n" , mem_block[A_PROGRAM].current_size + len); } } /* yymove_switch_instructions() */ /*-------------------------------------------------------------------------*/ static void yycerrorl (const char *s1, const char *s2, int line1, int line2) /* Callback function for switch: Raise an error in file at * lines and . * may contain one '%s' to insert 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 ) /*
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: * * * LOR/LAND l DUP * LBRANCH_ * l: POP_VALUE * * l: * * The extra DUP compensates the svalue the LBRANCH eats. * The LBRANCH_ 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) yyerrorf("Compiler limit: Too much code to skip for ||/&&:" " %"PRIdPINT" bytes" , offset); p[0] = F_POP_VALUE; } else { mem_block[A_PROGRAM].block[address] = offset; } } /* update_lop_branch() */ /*-------------------------------------------------------------------------*/ static void shuffle_code (p_uint start1, p_uint start2, p_uint end) /* Reverse the order of the program blocks [start1..start2[ and [start2..end[ */ { p_uint len1 = start2 - start1; p_uint len2 = end - start2; bytecode_p pStart1 = PROGRAM_BLOCK + start1; bytecode_p pStart2 = PROGRAM_BLOCK + start2; bytecode_p * pTmp; if (!len1 || !len2) return; pTmp = xalloc(len1); if (!pTmp) { yyerror("(shuffle_code) Out of memory"); return; } memmove(pTmp, pStart1, len1); memmove(pStart1, pStart2, len2); memmove(pStart1+len2, pTmp, len1); xfree(pTmp); } /* shuffle_code() */ /* ======================== 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); } while (current_number_of_locals > 0 && type_of_locals) { current_number_of_locals--; free_fulltype_data(&type_of_locals[current_number_of_locals]); } 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 , and adjust the counters. * A 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_fulltype_data(&type_of_locals[current_number_of_locals]); } } /* free_local_names() */ /*-------------------------------------------------------------------------*/ static ident_t * add_local_name (ident_t *ident, fulltype_t type, int depth #ifndef USE_NEW_INLINES , Bool may_shadow #endif /* USE_NEW_INLINES */ ) /* Declare a new local variable with the type on * the scope depth . The references of are NOT adopted. #ifndef USE_NEW_INLINES * If is TRUE, the declaration is * allowed to shadow a previous one without generating a warning (this is * used to declare inline functions on the fly). #endif * Return the (adjusted) ident for the new variable. */ { if ((type.typeflags & PRIMARY_TYPE_MASK) == TYPE_VOID) { yyerrorf( "Illegal to define variable '%s' as type 'void'" , get_txt(ident->name)); } if (current_number_of_locals >= MAX_LOCAL /* size of type recording array */ || current_number_of_locals >= 256) yyerror("Too many local variables"); else { ref_fulltype_data(&type); if (ident->type != I_TYPE_UNKNOWN) { /* We're overlaying some other definition. * If it's a global, or if we are in an inline-closure arg list, * it's ok. */ #ifdef USE_NEW_INLINES #ifdef DEBUG_INLINES if (current_inline && current_inline->block_depth+2 == block_depth && ident->type != I_TYPE_GLOBAL) printf("DEBUG: redeclare local '%s' as inline arg, depth %d\n", get_txt(ident->name), block_depth); #endif /* DEBUG_INLINES */ if (ident->type != I_TYPE_GLOBAL && !(current_inline && current_inline->block_depth+2 == block_depth) ) { yywarnf( "Variable '%s' shadows previous declaration" , get_txt(ident->name)); } #else /* USE_NEW_INLINES */ if (!may_shadow && ident->type != I_TYPE_GLOBAL) { yywarnf( "Variable '%s' shadows previous declaration" , get_txt(ident->name)); } #endif /* USE_NEW_INLINES */ ident = make_shared_identifier(get_txt(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; #ifdef USE_NEW_INLINES ident->u.local.context = -1; #endif /* USE_NEW_INLINES */ /* 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; /* 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 ident_t * redeclare_local (ident_t *ident, fulltype_t type, int depth) /* Redeclare a local name , to at ; the references * of are NOT adopted (nor freed on error). * 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. */ { 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); } if (ident->u.local.depth >= depth || (ident->u.local.depth == 1 && depth == 2) #ifdef USE_NEW_INLINES || (current_inline && ident->u.local.depth == current_inline->block_depth+2 && depth == current_inline->block_depth+3 ) #endif /* USE_NEW_INLINES */ ) { yyerrorf("Illegal to redeclare local name '%s'", get_txt(ident->name)); } else { #ifdef USE_NEW_INLINES ident = add_local_name(ident, type, depth); #else /* USE_NEW_INLINES */ ident = add_local_name(ident, type, depth, MY_FALSE); #endif /* USE_NEW_INLINES */ } return ident; } /* redeclare_local() */ #ifdef USE_NEW_INLINES /*-------------------------------------------------------------------------*/ static ident_t * add_context_name (ident_t *ident, fulltype_t type, int num) /* Declare a new context variable with the type for the * currently compiled inline closure. The references of are NOT adopted. * is -1 for independent context * variables, or the index of the inherited local variable. * Return the (adjusted) ident for the new variable. */ { int depth; block_scope_t * block; depth = current_inline->block_depth+1; block = & block_scope[depth-1]; #ifdef DEBUG_INLINES printf("DEBUG: add_context_name('%s', num %d) depth %d, context %d\n", get_txt(ident->name), num, depth, block->num_locals); #endif /* DEBUG_INLINES */ if (block->num_locals >= 256 || block->num_locals >= MAX_LOCAL /* size of type recording array */ ) { yyerror("Too many context variables"); } else { ref_fulltype_data(&type); if (ident->type != I_TYPE_UNKNOWN) { /* We're overlaying some other definition, but that's ok. */ ident = make_shared_identifier(get_txt(ident->name), I_TYPE_LOCAL, depth); } /* Initialize the ident */ ident->type = I_TYPE_LOCAL; ident->u.local.num = num; ident->u.local.depth = depth; ident->u.local.context = block->num_locals; /* Put the ident into the list of all locals. */ if (all_locals && all_locals->u.local.depth > depth) { /* This context variable was detected after we already * added locals - find the proper insertion point. */ ident_t * prev, *this; for ( prev = all_locals, this = all_locals->next_all ; this && this->u.local.depth > depth ; prev = this, this = this->next_all) NOOP; ident->next_all = this; prev->next_all = ident; } else { ident->next_all = all_locals; all_locals = ident; } /* Record the type */ type_of_context[block->num_locals] = type; block->num_locals++; } return ident; } /* add_context_name() */ /*-------------------------------------------------------------------------*/ static ident_t * check_for_context_local (ident_t *ident, fulltype_t * pType) /* The LPC code uses local variable . If we're compiling * an inline closure, check if it is an inherited local for which * no context variable has been created yet. If yes, create the context * variable. * Return the (possibly updated) ident, and store the variables type * in *. */ { int depth = ident->u.local.depth; if (current_inline && depth <= current_inline->block_depth ) { /* This local has been inherited - create the * proper context variable. */ if (ident->u.local.context >= 0) { if (!current_inline->parse_context && current_inline->prev != -1 && depth <= INLINE_CLOSURE(current_inline->prev).block_depth ) { /* Can't use outer context variables when compiling * an inline closure body. */ yyerrorf("Can't use context variable '%s' " "across closure boundaries", get_txt(ident->name)); ident = redeclare_local(ident, Type_Any, block_depth); *pType = Type_Any; } else { /* We can use outer context variables when compiling * an inline closure context. */ *pType = LOCAL_TYPE(current_inline->full_context_type_start + ident->u.local.context ); } } else /* it's a local */ { fulltype_t type; type = LOCAL_TYPE(current_inline->full_local_type_start + ident->u.local.num ); if (!current_inline->parse_context) { ref_fulltype_data(&type); ident = add_context_name( ident, type, ident->u.local.num); } *pType = type; } } else if (ident->u.local.context >= 0) *pType = type_of_context[ident->u.local.context]; else *pType = type_of_locals[ident->u.local.num]; return ident; } /* check_for_context_local() */ #endif /* USE_NEW_INLINES */ /*-------------------------------------------------------------------------*/ static void init_scope (int depth) /* Initialize the block_scope entry for block_depth . */ { block_scope[depth-1].num_locals = 0; block_scope[depth-1].first_local = current_number_of_locals; block_scope[depth-1].num_cleared = 0; block_scope[depth-1].clobbered = MY_FALSE; 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 (Bool dontclobber) /* Leave the current scope (if use_local_scopes requires it), freeing * all local names defined in that scope. * * should be MY_TRUE if the stack of the to-be-left scope * is independent of the outer scope (i.e. the scope of closures). */ { if (use_local_scopes) { free_local_names(block_depth); block_depth--; if (block_depth && !dontclobber && (block_scope[block_depth].num_locals || block_scope[block_depth].clobbered)) { /* the block we just left may have clobbered local variables */ block_scope[block_depth-1].clobbered = MY_TRUE; } } } /* leave_block_scope() */ /* ====================== GLOBALS and FUNCTIONS ====================== */ /*-------------------------------------------------------------------------*/ static unsigned short store_argument_types ( int num_arg ) /* Store the 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.typeflags) { 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++) { vartype_t vt; assign_full_to_vartype(&vt, type_of_locals[i]); ref_vartype_data(&vt); add_to_mem_block(A_ARGUMENT_TYPES, &vt, sizeof vt); } } 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

with the characteristics , , * program , and . * The references of are NOT adopted. * Result is the number (index) of the function. * * The function is called whenever a function header (return type, name * and arguments) has been parsed - is FALSE then. Additionally, * the function is called as well after a functionbody has been parsed, * 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; /* Move the visibility-info into flags */ flags |= type.typeflags & ~TYPE_MOD_MASK; 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_HIDDEN|NAME_UNDEFINED)) == (NAME_INHERITED|TYPE_MOD_PRIVATE|NAME_HIDDEN)) { 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\"" , get_txt(p->name)); if (!(funp->flags & (NAME_UNDEFINED|NAME_PROTOTYPE|NAME_INHERITED) ) ) { yyerrorf("Redeclaration of function %s.", get_txt(p->name)); if ( !(flags & NAME_PROTOTYPE) ) free_mstring(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.typeflags && funp->type.typeflags != 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'.", get_txt(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'.", get_txt(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." , get_txt(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) funflag_t f1 = funp->flags; funflag_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]; t2 = funp->type; strcpy(buff, get_visibility(t2)); yywarnf("Inconsistent declaration of '%s': Visibility changed from '%s' to '%s'" , get_txt(p->name), buff, get_visibility(type)); } # undef TYPE_MOD_VIS } /* Check if the 'varargs' attribute is conserved */ t1 = type; t1.typeflags &= TYPE_MOD_MASK; t2 = funp->type; t2.typeflags &= TYPE_MOD_MASK; if (!MASKED_TYPE(t1, t2)) { if (pragma_pedantic) yyerrorf("Inconsistent declaration of '%s': Return type mismatch %s", get_txt(p->name), get_two_types(t2, t1)); else if (pragma_check_overloads) yywarnf("Inconsistent declaration of '%s': Return type mismatch %s", get_txt(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." , get_txt(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]; t1.typeflags &= TYPE_MOD_RMASK; assign_var_to_fulltype(&t2, argp[i]); t2.typeflags &= 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" , get_txt(p->name), i+1, get_two_types(t1, t2) ); else if (pragma_check_overloads) yywarnf("Argument type mismatch in " "redefinition of '%s': arg %d %s" , get_txt(p->name), i+1, get_two_types(t1, t2) ); } } /* for (all args) */ } /* if (compare_args) */ } /* if (exact_types && already defined) */ } /* if (!complete) */ /* Remember the heart_beat() function */ if (mstreq(p->name, STR_HEART_BEAT)) 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; free_fulltype_data(&funp->type); funp->type = type; ref_fulltype_data(&funp->type); /* That's it */ return num; } while(0); /* Test and handle for already defined functions */ /* It's a new function! */ if (mstreq(p->name, STR_HEART_BEAT)) 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; ref_fulltype_data(&fun.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(get_txt(p->name), I_TYPE_GLOBAL, 0); } /* should be I_TYPE_UNKNOWN now. */ init_global_identifier(p, /* bVariable: */ MY_TRUE); 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() */ /*-------------------------------------------------------------------------*/ static void define_variable (ident_t *name, fulltype_t type) /* Define a new global variable of type . * The references of are NOT adopted. */ { variable_t dummy; typeflags_t flags = type.typeflags; int n; if ((flags & PRIMARY_TYPE_MASK) == TYPE_VOID) { yyerrorf( "Illegal to define variable '%s' as type 'void'" , get_txt(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(get_txt(name->name), I_TYPE_GLOBAL, 0); } init_global_identifier(name, /* bVariable: */ MY_TRUE); name->next_all = all_globals; all_globals = name; } else if (name->u.global.function == I_GLOBAL_FUNCTION_OTHER && (name->u.global.efun >= 0 || name->u.global.sim_efun >= 0) ) { /* 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; } /* 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; } /* If the variable already exists, make sure that we can redefine it */ if ( (n = name->u.global.variable) >= 0) { typeflags_t vn_flags = VARIABLE(n)->type.typeflags; /* Visible nomask variables can't be redefined */ if ( vn_flags & TYPE_MOD_NO_MASK && !(flags & NAME_HIDDEN)) yyerrorf( "Illegal to redefine 'nomask' variable '%s'" , get_txt(name->name)); /* We can redefine inherited variables if they are private or hidden, * or if at least one of them is static. */ if ( ( !(vn_flags & NAME_INHERITED) || ( !(vn_flags & (TYPE_MOD_PRIVATE|NAME_HIDDEN)) && !((flags | vn_flags) & TYPE_MOD_STATIC) ) ) && !(flags & NAME_INHERITED) ) { if (vn_flags & NAME_INHERITED) yyerrorf("Illegal to redefine inherited variable '%s'" , get_txt(name->name)); else yyerrorf("Illegal to redefine global variable '%s'" , get_txt(name->name)); } if (((flags | vn_flags) & (TYPE_MOD_STATIC|TYPE_MOD_PRIVATE)) == TYPE_MOD_STATIC && !(flags & NAME_INHERITED) ) { yywarnf("Redefining inherited %s variable '%s' with a %s variable" , (vn_flags & TYPE_MOD_STATIC) ? "nosave" : "non-nosave" , get_txt(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 |= ~(vn_flags) & TYPE_MOD_STATIC; } else { vn_flags |= ~flags & TYPE_MOD_STATIC; VARIABLE(n)->type.typeflags = vn_flags; } } type.typeflags = flags; dummy.name = ref_mstring(name->name); dummy.type = type; ref_fulltype_data(&dummy.type); 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); } else { if (!(flags & NAME_HIDDEN)) name->u.global.variable = NV_VARIABLE_COUNT; add_to_mem_block(A_VARIABLES, &dummy, sizeof dummy); } } /* define_variable() */ /*-------------------------------------------------------------------------*/ static void redeclare_variable (ident_t *name, fulltype_t type, int n) /* The variable is inherited virtually with number . * Redeclare it from its original type to . */ { typeflags_t flags = type.typeflags; 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 */ init_global_identifier(name, /* bVariable: */ MY_TRUE); name->next_all = all_globals; all_globals = name; } else if (name->u.global.function == I_GLOBAL_FUNCTION_OTHER && (name->u.global.efun >= 0 || name->u.global.sim_efun >= 0) ) { /* 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)->type.typeflags & TYPE_MOD_NO_MASK ) yyerrorf( "Illegal to redefine 'nomask' variable '%s'" , get_txt(name->name)); } else if (V_VARIABLE(n)->type.typeflags & TYPE_MOD_NO_MASK && !(V_VARIABLE(n)->type.typeflags & NAME_HIDDEN) && (V_VARIABLE(n)->type.typeflags ^ flags) & TYPE_MOD_STATIC ) { yyerrorf("Illegal to redefine 'nomask' variable \"%s\"" , get_txt(name->name)); } if (flags & TYPE_MOD_NOSAVE) { /* 'nosave' is internally saved as 'static' (historical reason) */ flags |= TYPE_MOD_STATIC; flags ^= TYPE_MOD_NOSAVE; } type.typeflags = flags; name->u.global.variable = n; free_fulltype_data(&V_VARIABLE(n)->type); V_VARIABLE(n)->type = type; ref_fulltype_data(&V_VARIABLE(n)->type); } /* redeclare_variable() */ /*-------------------------------------------------------------------------*/ static int verify_declared (ident_t *p) /* Check that

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 !", get_txt(p->name)); return -1; } return r; } /* verify_declared() */ /*-------------------------------------------------------------------------*/ static int define_global_variable (ident_t* name, fulltype_t actual_type, typeflags_t opt_star, Bool with_init) /* This is called directly from a parser rule: [*] * if with_init is true, then an initialization of this variable will follow. * It creates the global variable and returns its index. */ { int i; variables_defined = MY_TRUE; if (!(actual_type.typeflags & (TYPE_MOD_PRIVATE | TYPE_MOD_PUBLIC | TYPE_MOD_PROTECTED))) { actual_type.typeflags |= default_varmod; } if (actual_type.typeflags & TYPE_MOD_VARARGS) { yyerror("can't declare a variable as varargs"); actual_type.typeflags &= ~TYPE_MOD_VARARGS; } actual_type.typeflags |= opt_star; if (!pragma_share_variables) actual_type.typeflags |= VAR_INITIALIZED; define_variable(name, actual_type); i = verify_declared(name); /* Is the var declared? */ /* Initialize float values with 0.0. */ if (with_init || (!(actual_type.typeflags & TYPE_MOD_POINTER) && (actual_type.typeflags & PRIMARY_TYPE_MASK) == TYPE_FLOAT )) { /* Prepare the init code */ transfer_init_control(); /* If this is the first variable initialization and * pragma_share_variables is in effect, insert * the check for blueprint/clone initialisation: * if (clonep(this_object())) return 1; */ if (!variables_initialized && pragma_share_variables) { ins_f_code(F_THIS_OBJECT); ins_f_code(F_CLONEP); ins_f_code(F_BRANCH_WHEN_ZERO); ins_byte(2); ins_f_code(F_CONST1); ins_f_code(F_RETURN); } /* Initialize floats with 0.0 */ if(!with_init) { PREPARE_INSERT(5) /* Must come after the non-local program code inserts! */ add_f_code(F_FCONST0); #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 */ if (!pragma_share_variables) VARIABLE(i)->type.typeflags |= VAR_INITIALIZED; /* Push the variable reference and create the assignment */ if (i + num_virtual_variables > 0xff) { add_f_code(F_PUSH_IDENTIFIER16_LVALUE); add_short(i + num_virtual_variables); CURRENT_PROGRAM_SIZE += 1; } else { add_f_code(F_PUSH_IDENTIFIER_LVALUE); add_byte(i + num_virtual_variables); } /* Ok, assign */ add_f_code(F_VOID_ASSIGN); CURRENT_PROGRAM_SIZE += 4; add_new_init_jump(); } /* PREPARE_INSERT() block */ } /* if (float variable) */ return i; } /* define_global_variable() */ /*-------------------------------------------------------------------------*/ static void init_global_variable (int i, ident_t* name, fulltype_t actual_type , typeflags_t opt_star, int assign_op, fulltype_t exprtype) /* This is called directly from a parser rule: [*] = * It will be called after the call to define_global_variable(). * It assigns the result of to the variable. */ { PREPARE_INSERT(4) if (!(actual_type.typeflags & (TYPE_MOD_PRIVATE | TYPE_MOD_PUBLIC | TYPE_MOD_PROTECTED))) { actual_type.typeflags |= default_varmod; } actual_type.typeflags |= opt_star; #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_f_code(F_PUSH_IDENTIFIER16_LVALUE); add_short(i + num_virtual_variables); CURRENT_PROGRAM_SIZE += 1; } else { add_f_code(F_PUSH_IDENTIFIER_LVALUE); add_byte(i + num_virtual_variables); } /* Only simple assigns are allowed */ if (assign_op != F_ASSIGN) yyerror("Illegal initialization"); /* Do the types match? */ actual_type.typeflags &= TYPE_MOD_MASK; if (!compatible_types(actual_type, exprtype, MY_TRUE)) { yyerrorf("Type mismatch %s when initializing %s" , get_two_types(actual_type, exprtype) , get_txt(name->name)); } /* Ok, assign */ add_f_code(F_VOID_ASSIGN); CURRENT_PROGRAM_SIZE += 3; add_new_init_jump(); } /* init_global_variable() */ /*-------------------------------------------------------------------------*/ static void store_function_header ( p_int start , string_t * name, fulltype_t returntype , int num_args, int num_vars ) /* Store a function header into the program block at address . * The caller has to make sure that there is enough space. * The references of are adopted. */ { bytecode_p p; vartype_t rtype; p = &(PROGRAM_BLOCK[start]); /* FUNCTION_NAME */ memcpy(p, &name, sizeof name); p += sizeof name; (void)ref_mstring(name); /* FUNCTION_TYPE */ assign_full_to_vartype(&rtype, returntype); memcpy(p, &rtype, sizeof(rtype)); p += sizeof(rtype); /* FUNCTION_NUM_ARGS */ if (returntype.typeflags & TYPE_MOD_XVARARGS) *p++ = num_args | ~0x7f; else *p++ = num_args; /* FUNCTION_NUM_VARS */ *p = num_vars; } /* store_function_header() */ /*-------------------------------------------------------------------------*/ static void get_function_information (function_t * fun_p, program_t * prog, int ix) /* Read the function information for function in program * (which may be inherited) and store it in *. It is the callers * responsibility to set ->flags _before_ calling this function. * * In particular, this function sets these fields: .name, .rtype * .num_args, and it modifies .flags. */ { fun_hdr_p funstart; vartype_t rtype; funflag_t flags; /* Find the real function code */ while ( (flags = prog->functions[ix]) & NAME_INHERITED) { inherit_t * ip; ip = &prog->inherit[flags & INHERIT_MASK]; ix -= ip->function_index_offset; prog = ip->prog; } funstart = &prog->program[flags & FUNSTART_MASK]; memcpy(&fun_p->name, FUNCTION_NAMEP(funstart), sizeof fun_p->name); memcpy(&rtype, FUNCTION_TYPEP(funstart), sizeof(rtype)); assign_var_to_fulltype(&fun_p->type, rtype); ref_fulltype_data(&fun_p->type); fun_p->num_arg = FUNCTION_NUM_ARGS(funstart) & 0x7f; if (FUNCTION_NUM_ARGS(funstart) & ~0x7f) fun_p->type.typeflags |= TYPE_MOD_XVARARGS; if (FUNCTION_CODE(funstart)[0] == F_UNDEF) { fun_p->flags |= NAME_UNDEFINED; } } /* get_function_information() */ /*-------------------------------------------------------------------------*/ static void def_function_typecheck (fulltype_t returntype, ident_t * ident #ifdef USE_NEW_INLINES , Bool is_inline #endif /* USE_NEW_INLINES */ ) /* Called after parsing the ' ' part of a function * definition, this function performs the typecheck, makes sure that * the function name is put into the list of globals, and initialises * the block scoping. * * If is TRUE, the function to be compiled is an inline closure, * which requires a slightly different handling. This function is called * after 'func ' has been parsed, and is provided with a synthetic * function name. */ { #ifdef USE_NEW_INLINES if (is_inline) { new_inline_closure(); enter_block_scope(); /* Scope for context */ enter_block_scope(); /* Argument scope */ } else { #endif /* USE_NEW_INLINES */ use_local_scopes = pragma_use_local_scopes; block_depth = 1; init_scope(block_depth); #ifdef USE_NEW_INLINES } #endif /* USE_NEW_INLINES */ if (!(returntype.typeflags & (TYPE_MOD_PRIVATE | TYPE_MOD_PUBLIC | TYPE_MOD_PROTECTED | TYPE_MOD_STATIC))) { returntype.typeflags |= default_funmod; } /* Require exact types? */ if (returntype.typeflags & TYPE_MOD_MASK) { exact_types = returntype; } else { if (pragma_strict_types != PRAGMA_WEAK_TYPES) yyerrorf("\"#pragma %s_types\" requires type of function" , pragma_strict_types == PRAGMA_STRICT_TYPES ? "strict" : "strong" ); exact_types.typeflags = 0; } if (returntype.typeflags & TYPE_MOD_NOSAVE) { yyerror("can't declare a function as nosave"); returntype.typeflags &= ~TYPE_MOD_NOSAVE; } if (ident->type == I_TYPE_UNKNOWN) { /* prevent freeing by exotic name clashes */ init_global_identifier(ident, /* bVariable: */ MY_TRUE); ident->next_all = all_globals; all_globals = ident; } /* Store the data */ #ifdef USE_NEW_INLINES if (is_inline) { current_inline->ident = ident; current_inline->returntype = returntype; } else { #endif /* USE_NEW_INLINES */ def_function_returntype = returntype; def_function_ident = ident; #ifdef USE_NEW_INLINES } #endif /* USE_NEW_INLINES */ } /* def_function_typecheck() */ /*-------------------------------------------------------------------------*/ static void def_function_prototype (int num_args #ifdef USE_NEW_INLINES , Bool is_inline #endif /* USE_NEW_INLINES */ ) /* Called after parsing ' ( ) of a function definition, * this function creates the function prototype entry. * * If is TRUE, the function to be compiled is an inline closure, * which requires a slightly different handling. This function is called * after 'func ' has been parsed. */ { ident_t * ident; fulltype_t returntype; int fun; #ifdef USE_NEW_INLINES if (is_inline) { ident = current_inline->ident; returntype = current_inline->returntype; } else #endif /* USE_NEW_INLINES */ { ident = def_function_ident; returntype = def_function_returntype; } /* We got the complete prototype: define it */ if ( current_number_of_locals && (type_of_locals[current_number_of_locals-1].typeflags & TYPE_MOD_VARARGS) ) { /* The last argument has to allow an array. */ fulltype_t *t; returntype.typeflags |= TYPE_MOD_XVARARGS; t = type_of_locals + (current_number_of_locals-1); if (!(t->typeflags & TYPE_MOD_POINTER) && (t->typeflags & TYPE_MOD_RMASK) != TYPE_ANY ) { if ((t->typeflags & TYPE_MOD_RMASK) != TYPE_UNKNOWN) yyerror( "varargs parameter must be declared array or mixed"); /* Keep the visibility, but change the type to * '&any' */ t->typeflags &= ~TYPE_MOD_RMASK; t->typeflags |= TYPE_ANY; } } /* Define a prototype. If it is a real function, then the * prototype will be updated below. */ fun = define_new_function( MY_FALSE, ident, num_args, 0, 0 , NAME_UNDEFINED|NAME_PROTOTYPE , returntype); /* Store the data */ #ifdef USE_NEW_INLINES if (is_inline) { current_inline->returntype = returntype; current_inline->num_args = num_args; current_inline->function = fun; } else #endif /* USE_NEW_INLINES */ { def_function_returntype = returntype; def_function_num_args = num_args; } } /* def_function_prototype() */ /*-------------------------------------------------------------------------*/ static void def_function_complete ( p_int body_start #ifdef USE_NEW_INLINES , Bool is_inline #endif /* USE_NEW_INLINES */ ) /* Called after completely parsing a function definition, * this function updates the function header and closes all scopes.. * Argument is the program index where the space for the function header * was made, or -1 if there was no body. * * If is TRUE, the function to be compiled is an inline closure, * which requires a slightly different handling. This function is called * after the complete closure has been parsed. */ { ident_t * ident; fulltype_t returntype; int num_args; #ifdef USE_NEW_INLINES if (is_inline) { ident = current_inline->ident; returntype = current_inline->returntype; num_args = current_inline->num_args; } else #endif /* USE_NEW_INLINES */ { ident = def_function_ident; returntype = def_function_returntype; num_args = def_function_num_args; } if (body_start < 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(ident->u.global.function)->flags); if (!(*flagp & NAME_INHERITED)) { *flagp |= returntype.typeflags & (*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. */ int num_vars = max_number_of_locals - num_args + max_break_stack_need; store_function_header( body_start , ident->name , returntype , num_args , num_vars ); define_new_function(MY_TRUE, ident , num_args , num_vars , body_start + FUNCTION_PRE_HDR_SIZE , 0, returntype); /* Catch a missing return if the function has a return type */ if ((returntype.typeflags & PRIMARY_TYPE_MASK) != TYPE_VOID && ( (returntype.typeflags & PRIMARY_TYPE_MASK) != TYPE_UNKNOWN || pragma_strict_types ) ) { /* Check if the previous instruction is a RETURN, or * at least a non-continuing instruction. */ bytecode_t last = PROGRAM_BLOCK[CURRENT_PROGRAM_SIZE-1]; if (F_RETURN == last || F_RETURN0 == last || F_RAISE_ERROR == last || F_THROW == last ) { /* Good, the last instruction seems to be a 'return'. * But just in case we're looking at the data field * of a different opcode or a conditional return: insert a * proper default return as well. */ if (pragma_warn_missing_return) ins_f_code(F_DEFAULT_RETURN); else ins_f_code(F_RETURN0); } else { /* There is no 'return' here: most likely it is missing * altogether. * If warn_missing_return is enabled, issue the warning, * but always insert a normal F_RETURN0: with the pragma * active it's no use to warn again at runtime, and without * the pragma no warning is desired anyway. */ if (pragma_warn_missing_return) yywarnf("Missing 'return ' statement"); ins_f_code(F_RETURN0); } } else { ins_f_code(F_RETURN0); } } /* Clean up for normal functions. * Do not free the function returntype - it is still held in A_FUNCTIONS * and freed after the compile. * Inline closures need some of the information for some more processing. */ #ifdef USE_NEW_INLINES if (is_inline) { /* Keep block_depth, and local names */ } else #endif /* USE_NEW_INLINES */ { free_all_local_names(); block_depth = 0; } } /* def_function_complete() */ /* ============================= STRUCTS ============================= */ #ifdef USE_STRUCTS /*-------------------------------------------------------------------------*/ static int define_new_struct ( Bool proto, ident_t *p, funflag_t flags) /* Define a new struct

with the visibility . * If is TRUE, the function is called for a struct forward * declaration; if is FALSE, the struct is about to be defined. * * Result is the index (id) of the struct in the struct_defs table. * If the struct would be a duplicate, -1 is returned instead of the index. * * If a prototype is encountered, the struct definition is stored * with an additional visibility flag of NAME_PROTOTYPE. * * If NAME_HIDDEN is set in flags, the struct is added to the program * but no visibility checks occur - this is for inherited structs * which are no longer visible, but have to be kept in order to * keep the struct ids intact. */ { int num; struct_def_t sdef; /* If this is a redeclaration, check for consistency. */ if (p->type == I_TYPE_GLOBAL && (num = p->u.global.struct_id) >= 0 && !(flags & NAME_HIDDEN) ) { struct_def_t *pdef; pdef = &STRUCT_DEF(num); /* Check if the visibility is conserved. */ { # define TYPE_MOD_VIS \ ( TYPE_MOD_NO_MASK \ | TYPE_MOD_PRIVATE | TYPE_MOD_PUBLIC \ | TYPE_MOD_PROTECTED) funflag_t f1 = pdef->flags; funflag_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 ^ f2) & TYPE_MOD_VIS) ) { char buff[100]; strcpy(buff, get_f_visibility(pdef->flags)); yywarnf("Inconsistent declaration of struct %s: " "Visibility changed from '%s' to '%s'" , get_txt(p->name), buff, get_f_visibility(flags)); } # undef TYPE_MOD_VIS } /* If this is just another prototype, return */ if (proto) return num; /* If this is a redefinition of a completed struct, complain * and return. */ if (!proto && !(pdef->flags & NAME_PROTOTYPE)) { yyerrorf("Duplicate definition of struct %s" , get_txt(p->name)); return -1; } /* At this point, we have in our hands the definition of a * previously just declared struct. * Update the stored information and return its index. */ pdef->flags = flags & ~NAME_PROTOTYPE; return num; } /* This is a new struct! */ /* Fill in the struct_def_t */ sdef.type = struct_new_prototype(ref_mstring(p->name)); sdef.flags = proto ? (flags | NAME_PROTOTYPE) : (flags & ~NAME_PROTOTYPE); sdef.inh = -1; num = STRUCT_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(get_txt(p->name), I_TYPE_GLOBAL, 0); } /* should be I_TYPE_UNKNOWN now. */ init_global_identifier(p, /* bVariable: */ MY_FALSE); p->next_all = all_globals; all_globals = p; } if (!(flags & NAME_HIDDEN)) p->u.global.struct_id = num; /* Store the function_t in the functions area */ add_to_mem_block(A_STRUCT_DEFS, &sdef, sizeof sdef); return num; } /* define_new_struct() */ /*-------------------------------------------------------------------------*/ static int find_struct ( string_t * name ) /* Find the struct and return its index. Return -1 if not found. */ { ident_t * p; p = find_shared_identifier(get_txt(name), I_TYPE_GLOBAL, 0); /* Find the global struct identifier */ while (p != NULL && p->type != I_TYPE_GLOBAL) p = p->inferior; if (p == NULL || p->u.global.struct_id < 0) return -1; if (STRUCT_DEF(p->u.global.struct_id).flags & NAME_HIDDEN) return -1; return p->u.global.struct_id; } /* find_struct() */ /*-------------------------------------------------------------------------*/ static void add_struct_member ( string_t *name, vartype_t type , struct_type_t * from_struct ) /* Add a new member with type to A_STRUCT_MEMBERS for the * to the most recently defined struct . * If is not NULL, it is the type of the struct from * which the member is inherited. * Raise an error if a member of the same name already exists. */ { struct_def_t *pdef; pdef = &STRUCT_DEF(current_struct); if (STRUCT_MEMBER_COUNT != 0) { /* Not the first member: check if the name already occured */ int i; for ( i = STRUCT_MEMBER_COUNT-1 ; i >= 0 ; i--) { if (mstreq(name, STRUCT_MEMBER(i).name)) { if (pdef->type->base && pdef->type->base->num_members > i ) yyerrorf("Duplicate member '%s' in struct '%s', " "inherited from struct '%s'" , get_txt(name) , get_txt(struct_t_name(pdef->type)) , get_txt(struct_t_name(pdef->type->base)) ); else yyerrorf("Duplicate member '%s' in struct '%s'" , get_txt(name) , get_txt(struct_t_name(pdef->type)) ); return; } } } /* Member is ok: add it */ if (STRUCT_MEMBER_COUNT == 0) { /* First member */ if (from_struct) { pdef->type->base = ref_struct_type(from_struct); } } if (STRUCT_MEMBER_COUNT == STRUCT_MAX_MEMBERS) { yyerrorf("Too many members for struct '%s'" , get_txt(struct_t_name(pdef->type))); } else { struct_member_t member; member.name = ref_mstring(name); member.type = type; ref_vartype_data(&member.type); add_to_mem_block(A_STRUCT_MEMBERS, &member, sizeof member); } } /* add_struct_member() */ /*-------------------------------------------------------------------------*/ static void finish_struct ( const char * prog_name, int32 prog_id) /* The definition for struct has been parsed completely, * now complete the struct type object with the A_STRUCT_MEMBERS data. */ { struct_def_t *pdef; struct_type_t *base; string_t *name; pdef = &STRUCT_DEF(current_struct); /* Retrieve the .base pointer so that the error handling won't * get confused about it. * Also get a safety copy of the name. */ base = pdef->type->base; pdef->type->base = NULL; name = ref_mstring(struct_t_name(pdef->type)); /* Fill in the prototype */ pdef->type = struct_fill_prototype(pdef->type , new_tabled(prog_name) , prog_id , base , STRUCT_MEMBER_COUNT , &STRUCT_MEMBER(0) ); if (pdef->type) { /* Success: Free the safety copies */ free_mstring(name); } else { /* Failure: Recreate the prototype as the old one got deleted */ pdef->type = struct_new_prototype(name); } /* Clear the STRUCT_MEMBER block - the definitions have already * been adopted or cleared by the struct_fill_prototype(). */ mem_block[A_STRUCT_MEMBERS].current_size = 0; } /* finish_struct() */ /*-------------------------------------------------------------------------*/ static Bool create_struct_literal ( struct_def_t * pdef, int length, struct_init_t * list) /* The compiler has created code for expressions in order * to create a struct literal of struct . * Analyze the of member descriptions and generate the appropriate * bytecode. * * Return TRUE on success, and FALSE if an error occured (the caller will * then clean up the bytecode). */ { struct_init_t * p; struct_member_t * pmember; void * block; /* Allocation block for flags and index */ Bool * flags; /* Flag: which struct members have been set */ int * ix; /* For each expr in order, list the struct member index */ int consumed; /* To check if we used all elements */ int count, member; int i; Bool got_error = MY_FALSE; /* Check if there is one member assigned by name */ for (p = list; p != NULL; p = p->next) if (p->name != NULL) break; if (length == 0 || p == NULL) { /* Simplest case: all members assigned by position. */ /* Check the types */ if (exact_types.typeflags && length > 0) { for (member = 0, pmember = pdef->type->member, p = list ; member < length && member < struct_t_size(pdef->type) ; member++, pmember++, p = p->next ) { if (!vtype(pmember->type, p->type) ) { yyerrorf("Type mismatch %s for member '%s' " "in struct '%s'" , get_two_vtypes(pmember->type, p->type) , get_txt(pmember->name) , get_txt(struct_t_name(pdef->type)) ); got_error = MY_TRUE; } } if (got_error) return MY_FALSE; } /* The types check out - create the bytecode */ ins_f_code(F_S_AGGREGATE); ins_short(pdef - &STRUCT_DEF(0)); ins_byte(length); return MY_TRUE; } /* We have named members in there - sort them out */ consumed = 0; block = xalloc( struct_t_size(pdef->type) * sizeof(*flags) + length * sizeof(*ix)); flags = (Bool *)block; ix = (int *)((char *)block + struct_t_size(pdef->type) * sizeof(*flags)); for (i = 0; i < struct_t_size(pdef->type); i++) { flags[i] = MY_FALSE; } for (i = 0; i < length; i++) { ix[i] = -1; } /* Loop through list: assign the named members. */ for (p = list, count = 0; p != NULL; p = p->next, count++) { if (p->name == NULL) { if (!got_error) { yyerrorf( "Can't mix named and unnamed initializers " "in struct '%s'" , get_txt(struct_t_name(pdef->type)) ); got_error = MY_TRUE; } continue; } consumed++; pmember = NULL; /* avoids a warning */ member = struct_find_member(pdef->type, p->name); if (member >= 0) pmember = &pdef->type->member[member]; if (member < 0) { yyerrorf( "No such member '%s' in struct '%s'" , get_txt(p->name) , get_txt(struct_t_name(pdef->type)) ); got_error = MY_TRUE; } else if (flags[member]) { yyerrorf( "Multiple initializations of member '%s' " "in struct '%s'" , get_txt(p->name) , get_txt(struct_t_name(pdef->type)) ); got_error = MY_TRUE; } else if (exact_types.typeflags && !vtype( pmember->type , p->type) ) { yyerrorf("Type mismatch %s when initializing member '%s' " "in struct '%s'" , get_two_vtypes(pmember->type, p->type) , get_txt(p->name) , get_txt(struct_t_name(pdef->type)) ); got_error = MY_TRUE; } else { flags[member] = MY_TRUE; ix[count] = member; } } /* for() */ if (got_error) { xfree(block); return MY_FALSE; } /* Sanity checks */ if (consumed < length) { yyerrorf("Too many elements for struct '%s'" , get_txt(struct_t_name(pdef->type)) ); xfree(block); return MY_FALSE; } for (i = 0; i < length; i++) { if (ix[i] < 0) { fatal("struct literal: expression %d not assigned to any member.\n" , i); /* NOTREACHED */ } } /* Finally, create the code */ ins_f_code(F_S_M_AGGREGATE); ins_short(pdef - &STRUCT_DEF(0)); ins_byte(length); for (i = length-1; i >= 0; i--) ins_byte(ix[i]); /* Done */ xfree(block); return MY_TRUE; } /* create_struct_literal() */ /*-------------------------------------------------------------------------*/ static short get_struct_index (struct_type_t * pType) /* Return the index of struct type in this program's A_STRUCT_DEFS. * Return -1 if not found. */ { short i; for (i = 0; (size_t)i < STRUCT_COUNT; i++) { if (STRUCT_DEF(i).type == pType) return i; } return -1; } /* get_struct_index() */ /*-------------------------------------------------------------------------*/ static short find_struct_by_member (string_t * name, int * pNum) /* Among the structs known by this program, find the (smallest) one * defining member . * Result: * >= 0: Index of the struct in A_STRUCT_DEFS, * index of the member * FSM_NO_STRUCT (-1): No struct with such a member * FSM_AMBIGUOUS (-2): Multiple unrelated structs define the member * In case of the errors, the function also issues a compiler error. */ #define FSM_NO_STRUCT (-1) #define FSM_AMBIGUOUS (-2) { short rc = FSM_NO_STRUCT; struct_def_t * pRC = NULL; int member = -1; int i; for (i = 0; (size_t)i < STRUCT_COUNT; i++) { int num; struct_def_t * pdef = &STRUCT_DEF(i); /* If we already found a struct, check if this one is * a relative. If yes, we can immediately continue * to the next. */ if (pRC != NULL) { struct_type_t * pTest; for ( pTest = pdef->type ; pTest != NULL && pTest != pRC->type ; pTest = pTest->base ) NOOP; if (pTest != NULL) continue; } /* Lookup the member in this struct. If not found, * continue to the next struct. */ num = struct_find_member(pdef->type, name); if (num < 0) continue; /* If we already found a struct, check if this one is * a relative. */ if (pRC != NULL) { struct_type_t * pTest; /* Is the newly found struct a child of the * one we already know? If yes, skip it. */ for ( pTest = pdef->type ; pTest != NULL && pTest != pRC->type ; pTest = pTest->base ) NOOP; if (pTest != NULL) continue; /* Is the newly found struct a parent of * the one we already know? If yes, keep it * instead of the one we have; if no, the two * structs are completely unrelated and the * lookup is ambiguous. */ for ( pTest = pRC->type ; pTest != NULL && pTest != pdef->type ; pTest = pTest->base ) NOOP; if (pTest == NULL) { yyerrorf("Multiple structs found for member '%s': " "struct %s, struct %s" , get_txt(name) , get_txt(struct_t_name(pRC->type)) , get_txt(struct_t_name(pdef->type)) ); *pNum = -1; return FSM_AMBIGUOUS; } } /* if (pRC) */ /* It's a successful lookup */ rc = i; pRC = pdef; member = num; } /* for (all structs) */ if (rc < 0) { yyerrorf("No struct found for member '%s'", get_txt(name)); } *pNum = member; return rc; } /* find_struct_by_member() */ /*-------------------------------------------------------------------------*/ static void struct_epilog (void) /* After a successful parse, make sure that all structs are defined, * try to reactivate existing structs, and publish the new ones. * * If an error occures, num_parse_error will be incremented. */ { int i; /* Check that all structs are defined. */ for (i = 0; (size_t)i < STRUCT_COUNT; i++) { if (STRUCT_DEF(i).flags & NAME_PROTOTYPE) { yyerrorf("struct '%s' defined just as prototype" , get_txt(struct_t_name(STRUCT_DEF(i).type)) ); return; } } /* For all structs defined in this program, check if they just * replicate an existing older struct. */ for (i = 0; (size_t)i < STRUCT_COUNT; i++) { struct_type_t *pSType = STRUCT_DEF(i).type; struct_type_t *pOld; int ii; if (STRUCT_DEF(i).inh >= 0) continue; pOld = struct_lookup_type(pSType); if (!pOld || !struct_type_equivalent(pSType, pOld)) continue; /* pOld has the same structure as pSType, so lets * replace the latter with the former. * First in the structs themselves. */ for (ii = 0; (size_t)ii < STRUCT_COUNT; ii++) { if (ii != i) struct_type_update(STRUCT_DEF(ii).type, pSType, pOld); } /* Update variable types */ for (ii = 0; (size_t)ii < NV_VARIABLE_COUNT; ii++) { fulltype_t * pType = &NV_VARIABLE(ii)->type; if ((pType->typeflags & PRIMARY_TYPE_MASK) == TYPE_STRUCT && pType->t_struct == pSType ) { free_struct_type(pType->t_struct); pType->t_struct = ref_struct_type(pOld); } } for (ii = 0; (size_t)ii < V_VARIABLE_COUNT; ii++) { fulltype_t * pType = &V_VARIABLE(ii)->type; if ((pType->typeflags & PRIMARY_TYPE_MASK) == TYPE_STRUCT && pType->t_struct == pSType ) { free_struct_type(pType->t_struct); pType->t_struct = ref_struct_type(pOld); } } /* Update the function return types */ { int num_functions = FUNCTION_COUNT; function_t * f = (function_t *)mem_block[A_FUNCTIONS].block; for (ii = num_functions; --ii >= 0; f++) { /* Ignore all functions but those actually defined in * this program. */ if (f->flags & (NAME_INHERITED|NAME_UNDEFINED|NAME_CROSS_DEFINED)) continue; if ((f->type.typeflags & PRIMARY_TYPE_MASK) == TYPE_STRUCT && f->type.t_struct == pSType ) { vartype_t type; fun_hdr_p funhdr; free_struct_type(f->type.t_struct); f->type.t_struct = ref_struct_type(pOld); funhdr = (fun_hdr_p) &mem_block[A_PROGRAM].block[f->offset.pc]; memcpy(&type, FUNCTION_TYPEP(funhdr), sizeof(type)); type.t_struct = pOld; memcpy(FUNCTION_TYPEP(funhdr), &type, sizeof(type)); } } /* for(ii) */ } /* Update function argument types */ for (ii = 0; (size_t)ii < ARGTYPE_COUNT; ii++) { vartype_t * pType = &ARGUMENT_TYPE(ii); if ((pType->type & PRIMARY_TYPE_MASK) == TYPE_STRUCT && pType->t_struct == pSType ) { free_struct_type(pType->t_struct); pType->t_struct = ref_struct_type(pOld); } } /* And finally, replace the struct in the A_STRUCT memblock */ free_struct_type(pSType); STRUCT_DEF(i).type = ref_struct_type(pOld); } /* for(i) */ /* Publish all struct types defined in this program. * It is safe to publish types twice. */ for (i = 0; (size_t)i < STRUCT_COUNT; i++) { if (STRUCT_DEF(i).inh < 0) struct_publish_type(STRUCT_DEF(i).type); } /* for(i) */ } /* struct_epilog() */ #endif /* USE_STRUCTS */ #ifdef USE_NEW_INLINES /* ========================= Inline Closures =-======================= */ /*-------------------------------------------------------------------------*/ static void new_inline_closure (void) /* Create a new inline closure structure and push it on top of the stack. */ { inline_closure_t ict; if (current_inline == NULL) { ict.prev = -1; } else { ict.prev = current_inline - &(INLINE_CLOSURE(0)); } #ifdef DEBUG_INLINES printf("DEBUG: new inline #%"PRIuMPINT": prev %"PRIdMPINT"\n", INLINE_CLOSURE_COUNT, ict.prev); #endif /* DEBUG_INLINES */ /* Initialize the other fields */ ict.end = CURRENT_PROGRAM_SIZE; ict.start = CURRENT_PROGRAM_SIZE; ict.length = 0; ict.li_start = LINENUMBER_SIZE; ict.li_length = 0; ict.function = -1; ict.ident = NULL; ict.returntype.typeflags = 0; #ifdef USE_STRUCTS ict.returntype.t_struct = NULL; #endif ict.num_args = 0; ict.parse_context = MY_FALSE; ict.start_line = stored_lines; ict.end_line = stored_lines; #ifdef DEBUG_INLINES printf("DEBUG: start: %"PRIuMPINT", depth %d, locals: %d/%d, break: %d/%d\n", CURRENT_PROGRAM_SIZE, block_depth, current_number_of_locals, max_number_of_locals, current_break_stack_need, max_break_stack_need); #endif /* DEBUG_INLINES */ ict.block_depth = block_depth; ict.break_stack_size = current_break_stack_need; ict.max_break_stack_size = max_break_stack_need; ict.num_locals = current_number_of_locals; ict.max_num_locals = max_number_of_locals; ict.exact_types = exact_types; ict.include_handle = get_include_handle(); ict.full_local_type_start = type_of_locals - &(LOCAL_TYPE(0)); ict.full_context_type_start = type_of_context - &(LOCAL_TYPE(0)); ict.full_local_type_size = mem_block[A_LOCAL_TYPES].current_size; #ifdef DEBUG_INLINES printf("DEBUG: local types: %"PRIuMPINT", context types: %"PRIuMPINT"\n", ict.full_local_type_start, ict.full_context_type_start); #endif /* DEBUG_INLINES */ /* Extend the type memblocks */ { mp_uint type_count = LOCAL_TYPE_COUNT; extend_mem_block(A_LOCAL_TYPES, 2 * MAX_LOCAL * sizeof(fulltype_t)); memset(&LOCAL_TYPE(type_count), 0 , (LOCAL_TYPE_COUNT - type_count) * sizeof(fulltype_t)); type_of_context = &(LOCAL_TYPE(type_count)); type_of_locals = &(LOCAL_TYPE(type_count+MAX_LOCAL)); #ifdef DEBUG_INLINES printf("DEBUG: type ptrs: %p, %p\n", type_of_locals, type_of_context ); #endif /* DEBUG_INLINES */ } max_break_stack_need = current_break_stack_need = 0; max_number_of_locals = current_number_of_locals = 0; /* Add the structure to the memblock */ add_to_mem_block(A_INLINE_CLOSURE, &ict, sizeof(ict)); current_inline = &(INLINE_CLOSURE(INLINE_CLOSURE_COUNT-1)); } /* new_inline_closure() */ /*-------------------------------------------------------------------------*/ static void finish_inline_closure (Bool bAbort) /* The compilation of the current inline closure is finished - move * everything out of the way of the ongoing compilation. * Note that only the codeblock .start/.length is saved; if there is * already code generated afterwards, it is moved forward. Ditto for * the linenumbers. * * If is TRUE, the closure is just finished, but not stored. */ { mp_uint backup_start, start, length, end; int offset; #ifdef DEBUG_INLINES { mp_int index = current_inline - &(INLINE_CLOSURE(0)); printf("DEBUG: %s inline #%"PRIdMPINT": prev %"PRIdMPINT", end %"PRIuMPINT ", start %"PRIuMPINT", length %"PRIuMPINT", function %d pc %"PRIu32"\n", bAbort ? "abort" : "finish", index, current_inline->prev, current_inline->end, current_inline->start, current_inline->length, current_inline->function, FUNCTION(current_inline->function)->offset.pc); printf("DEBUG: depth %d, locals: %d/%d, break: %d/%d\n", current_inline->block_depth, current_inline->num_locals, current_inline->max_num_locals, current_inline->break_stack_size, current_inline->max_break_stack_size); } #endif /* DEBUG_INLINES */ /* Move the program code into the backup storage */ start = current_inline->start; length = current_inline->length; end = current_inline->end; if (!bAbort) { backup_start = INLINE_PROGRAM_SIZE; #ifdef DEBUG_INLINES printf("DEBUG: move code to backup %"PRIuMPINT"\n", backup_start); #endif /* DEBUG_INLINES */ add_to_mem_block( A_INLINE_PROGRAM, PROGRAM_BLOCK+start, length); current_inline->start = backup_start; } else { current_inline->length = 0; /* Marks this one invalid */ } if (start + length < CURRENT_PROGRAM_SIZE) { #ifdef DEBUG_INLINES printf("DEBUG: move code forward: from %"PRIuMPINT", length %"PRIuMPINT ", to %"PRIuMPINT"\n", start+length, CURRENT_PROGRAM_SIZE - length - start, end); #endif /* DEBUG_INLINES */ memmove( PROGRAM_BLOCK+end , PROGRAM_BLOCK+start+length , CURRENT_PROGRAM_SIZE - length - start ); } CURRENT_PROGRAM_SIZE -= length + (start - end); stored_bytes -= length + (start - end); #ifdef DEBUG_INLINES printf("DEBUG: program size: %"PRIuMPINT"\n", CURRENT_PROGRAM_SIZE); #endif /* DEBUG_INLINES */ /* Move the linenumber data into the backup storage */ start = current_inline->li_start; length = current_inline->li_length; if (!bAbort) { backup_start = INLINE_PROGRAM_SIZE; #ifdef DEBUG_INLINES printf("DEBUG: move li data to %"PRIuMPINT", from %"PRIuMPINT" length %" PRIuMPINT"\n", backup_start, start, length); #endif /* DEBUG_INLINES */ add_to_mem_block( A_INLINE_PROGRAM, LINENUMBER_BLOCK+start, length); current_inline->li_start = backup_start; } /* Skip the lines with the closure. */ offset = current_inline->end_line - current_inline->start_line; while (offset > 0) { int lines; lines = offset; if (lines > LI_MAXEMPTY) lines = LI_MAXEMPTY; offset -= lines; LINENUMBER_BLOCK[start++] = (char)(256 - lines); length--; } if (start + length < LINENUMBER_SIZE) { #ifdef DEBUG_INLINES printf("DEBUG: move li data forward: from %"PRIuMPINT", length %"PRIuMPINT ", to %"PRIuMPINT"\n", start+length, LINENUMBER_SIZE - length - start, start); #endif /* DEBUG_INLINES */ memmove( LINENUMBER_BLOCK+start , LINENUMBER_BLOCK+start+length , LINENUMBER_SIZE - length - start ); } LINENUMBER_SIZE -= length; free_local_names(current_inline->block_depth+1); /* Restore the globals */ block_depth = current_inline->block_depth; current_number_of_locals = current_inline->num_locals; max_number_of_locals = current_inline->max_num_locals; current_break_stack_need = current_inline->break_stack_size; max_break_stack_need = current_inline->max_break_stack_size; exact_types = current_inline->exact_types; #ifdef DEBUG_INLINES printf("DEBUG: local types: %"PRIuMPINT", context types: %"PRIuMPINT"\n", current_inline->full_local_type_start, current_inline->full_context_type_start); #endif /* DEBUG_INLINES */ type_of_locals = &(LOCAL_TYPE(current_inline->full_local_type_start)); type_of_context = &(LOCAL_TYPE(current_inline->full_context_type_start)); #ifdef DEBUG_INLINES printf("DEBUG: type ptrs: %p, %p\n", type_of_locals, type_of_context ); #endif /* DEBUG_INLINES */ /* Don't free the current_inline->returntype as it's not counted. */ while (mem_block[A_LOCAL_TYPES].current_size > current_inline->full_local_type_size) { mem_block[A_LOCAL_TYPES].current_size -= sizeof(fulltype_t); free_fulltype_data( (fulltype_t*) (mem_block[A_LOCAL_TYPES].block + mem_block[A_LOCAL_TYPES].current_size) ); } /* Remove the structure from the lexical nesting stack */ if (current_inline->prev == -1) current_inline = NULL; else current_inline = &(INLINE_CLOSURE(current_inline->prev)); } /* finish_inline_closure() */ /*-------------------------------------------------------------------------*/ static void insert_pending_inline_closures (void) /* The compilation is a point where pending inline closures can be * inserted. Do that now. */ { mp_int ix; #ifdef DEBUG_INLINES if (INLINE_CLOSURE_COUNT != 0) printf("DEBUG: insert_inline_closures(): %" PRIuMPINT" pending\n", INLINE_CLOSURE_COUNT); #endif /* DEBUG_INLINES */ for (ix = 0; (size_t)ix < INLINE_CLOSURE_COUNT; ix++) { inline_closure_t * ict = &(INLINE_CLOSURE(ix)); #ifdef DEBUG_INLINES printf("DEBUG: #%"PRIdMPINT": start %"PRIuMPINT", length %"PRIuMPINT ", function %d: new start %"PRIuMPINT"\n", ix, ict->start, ict->length, ict->function, CURRENT_PROGRAM_SIZE); #endif /* DEBUG_INLINES */ if (ict->length != 0) { CURRENT_PROGRAM_SIZE = align(CURRENT_PROGRAM_SIZE); store_line_number_info(); if (stored_lines > ict->start_line) store_line_number_backward(stored_lines - ict->start_line); else while (stored_lines < ict->start_line) { int lines; lines = ict->start_line - stored_lines; if (lines > LI_MAXEMPTY) lines = LI_MAXEMPTY; stored_lines += lines; byte_to_mem_block(A_LINENUMBERS, 256 - lines); } FUNCTION(ict->function)->offset.pc = CURRENT_PROGRAM_SIZE + FUNCTION_PRE_HDR_SIZE; add_to_mem_block(A_PROGRAM, INLINE_PROGRAM_BLOCK(ict->start) , ict->length); #ifdef DEBUG_INLINES printf("DEBUG: li_start %"PRIuMPINT", li_length %"PRIuMPINT ", new li_start %"PRIuMPINT"\n", ict->li_start, ict->li_length, LINENUMBER_SIZE); #endif /* DEBUG_INLINES */ add_to_mem_block(A_LINENUMBERS, INLINE_PROGRAM_BLOCK(ict->li_start) , ict->li_length); stored_lines = ict->end_line; stored_bytes += ict->length; } } /* Empty the datastorages */ mem_block[A_INLINE_CLOSURE].current_size = 0; mem_block[A_INLINE_PROGRAM].current_size = 0; } /* insert_pending_inline_closure() */ /*-------------------------------------------------------------------------*/ static Bool prepare_inline_closure (fulltype_t returntype) /* Called after parsing 'func ', this creates the identifier * with the synthetic function name. The function also sets up the inline * closure structure and block scope. * * If the name can't be generated, FALSE is returned, otherwise TRUE. */ { char name[256+MAXPATHLEN+1]; ident_t * ident; if (!use_local_scopes) { yyerror("Inline closures require local scoping"); return MY_FALSE; } /* Create the name of the new inline function. * We have to make sure the name is really unique. */ do { char * start; sprintf(name, "__inline_%s_%d_#%04x", current_loc.file->name , current_loc.line, inline_closure_id++); /* Convert all non-alnums (but '#') to '_' */ for (start = name; *start != '\0'; start++) { if (!isalnum((unsigned char)(*start)) && '#' != *start) *start = '_'; } } while ( find_shared_identifier(name, 0, 0) && inline_closure_id != 0); if (inline_closure_id == 0) { yyerror("Can't generate unique name for inline closure"); return MY_FALSE; } ident = make_shared_identifier(name, I_TYPE_UNKNOWN, 0); /* The lfuns implementing the inline closures should not * be callable directly (without the CLOSURE svalue), and also not * overrideable. */ returntype.typeflags |= TYPE_MOD_NO_MASK | TYPE_MOD_PRIVATE; def_function_typecheck(returntype, ident, MY_TRUE); #ifdef DEBUG_INLINES printf("DEBUG: New inline closure name: '%s'\n", name); printf("DEBUG: current_inline->depth: %d\n", current_inline->block_depth); printf("DEBUG: context depth: %d\n", current_inline->block_depth+1); printf("DEBUG: arg depth: %d\n", current_inline->block_depth+2); printf("DEBUG: current depth: %d\n", block_depth); #endif /* DEBUG_INLINES */ return MY_TRUE; } /* prepare_inline_closure() */ /*-------------------------------------------------------------------------*/ static Bool inline_closure_prototype (int num_args) /* Called after parsing 'func ', this function * creates the function prototype entry. * * Return FALSE if out of memory (the internal structures have been cleaned * up then), TRUE otherwise. * * TODO: This function shares a lot of code with the generic function * TODO:: setup. To do this, use entry#0 for gathering the normal * TODO:: function information, and entries #1.. for the actual inlines. * TODO:: Or use a handful of globals, and save the in the closure entries * TODO:: as needed. */ { #ifdef DEBUG_INLINES printf("DEBUG: inline_closure_prototype(%d)\n", num_args); #endif /* DEBUG_INLINES */ def_function_prototype(num_args, MY_TRUE); #ifdef DEBUG_INLINES printf("DEBUG: current_inline->depth: %d: %d\n", current_inline->block_depth, block_scope[current_inline->block_depth-1].num_locals); printf("DEBUG: context depth: %d: %d\n", current_inline->block_depth+1, block_scope[current_inline->block_depth+1-1].num_locals); printf("DEBUG: arg depth: %d: %d\n", current_inline->block_depth+2, block_scope[current_inline->block_depth+2-1].num_locals); printf("DEBUG: current depth: %d: %d\n", block_depth, block_scope[block_depth].num_locals); printf("DEBUG: Function index: %d\n", current_inline->function); #endif /* DEBUG_INLINES */ store_line_number_info(); /* A function with code: align the function and * make space for the function header. */ current_inline->end = CURRENT_PROGRAM_SIZE; #ifdef DEBUG_INLINES printf("DEBUG: program size: %"PRIuMPINT" align to %"PRIuMPINT"\n", CURRENT_PROGRAM_SIZE, align(CURRENT_PROGRAM_SIZE)); #endif /* DEBUG_INLINES */ CURRENT_PROGRAM_SIZE = align(CURRENT_PROGRAM_SIZE); current_inline->start = CURRENT_PROGRAM_SIZE; current_inline->li_start = LINENUMBER_SIZE; current_inline->start_line = stored_lines; stored_bytes = CURRENT_PROGRAM_SIZE; /* Ignore the alignment. */ if (realloc_a_program(FUNCTION_HDR_SIZE)) { CURRENT_PROGRAM_SIZE += FUNCTION_HDR_SIZE; } else { yyerrorf("Out of memory: program size %"PRIuMPINT"\n" , mem_block[A_PROGRAM].current_size + FUNCTION_HDR_SIZE); finish_inline_closure(MY_TRUE); return MY_FALSE; } return MY_TRUE; } /* inline_closure_prototype() */ /*-------------------------------------------------------------------------*/ static void complete_inline_closure ( void ) /* Called after parsing 'func ', this function * updates the function header and moves the closure into the pending * area. After that, the function also completes the generation of * the F_CONTEXT_CLOSURE instruction. * * TODO: This function shares a lot of code with the generic function * TODO:: setup. To do this, use entry#0 for gathering the normal * TODO:: function information, and entries #1.. for the actual inlines. * TODO:: Or use a handful of globals, and save the in the closure entries * TODO:: as needed. */ { p_int start, li_start; #ifdef DEBUG_INLINES printf("DEBUG: Generate inline closure function:\n"); #endif /* DEBUG_INLINES */ if (current_inline->include_handle != get_include_handle()) { yyerror("Implementation restriction: Inline closure must not span " "include file limits"); /* Clean up */ leave_block_scope(MY_TRUE); /* Argument scope */ leave_block_scope(MY_TRUE); /* Context scope */ finish_inline_closure(MY_TRUE); return; } start = current_inline->start; li_start = current_inline->li_start; #ifdef DEBUG_INLINES printf("DEBUG: current_inline->depth: %d: %d\n", current_inline->block_depth, block_scope[current_inline->block_depth-1].num_locals); printf("DEBUG: context depth: %d: %d\n", current_inline->block_depth+1, block_scope[current_inline->block_depth+1-1].num_locals); printf("DEBUG: arg depth: %d: %d\n", current_inline->block_depth+2, block_scope[current_inline->block_depth+2-1].num_locals); printf("DEBUG: current depth: %d: %d\n", block_depth, block_scope[block_depth-1].num_locals); #endif /* DEBUG_INLINES */ /* Generate the function header and update the ident-table entry. */ def_function_complete(start, MY_TRUE); current_inline->length = CURRENT_PROGRAM_SIZE - start; store_line_number_info(); current_inline->li_length = LINENUMBER_SIZE - li_start; current_inline->end_line = stored_lines; /* Add the code to push the values of the inherited local * variables onto the stack, followed by the F_CONTEXT_CLOSURE * instruction. Since this code is after the recorded .length, * the finish_inline_closure() call will move it backward into * its rightful place. */ { int depth = current_inline->block_depth+1; block_scope_t * context = &(block_scope[depth-1]); #ifdef DEBUG_INLINES printf("DEBUG: %d context vars, depth %d\n", context->num_locals, depth); #endif /* DEBUG_INLINES */ if (context->num_locals != 0) { Bool got_mapped; /* To get the context->local information in the right order * we read the locals as they are and store the information * in an array. */ int * lcmap = alloca(context->num_locals * sizeof(int)); ident_t * id; int i; for (i = 0; i < context->num_locals; i++) lcmap[i] = -1; for (id = all_locals ; id && id->u.local.depth >= depth ; id = id->next_all) { #ifdef DEBUG_INLINES if (id->u.local.depth == depth) printf("DEBUG: '%s': local %d, context %d\n", get_txt(id->name), id->u.local.num, id->u.local.context); #endif /* DEBUG_INLINES */ if (id->u.local.depth == depth && id->u.local.context >= 0 && id->u.local.num >= 0 ) { lcmap[id->u.local.context] = id->u.local.num; } } /* Got all context->local mappings, now create the bytecode */ got_mapped = MY_FALSE; for (i = 0; i < context->num_locals; i++) { if (lcmap[i] != -1) { ins_f_code(F_LOCAL); ins_byte(lcmap[i]); got_mapped = MY_TRUE; #ifdef DEBUG_INLINES printf("DEBUG: -> F_LOCAL %d\n", lcmap[i]); #endif /* DEBUG_INLINES */ } else if (got_mapped) { /* This shouldn't happen, as all explicite context * variables are created before the first implicite * reference can be encountered. */ fatal("Explicite context var #%d has higher index than " "implicite context variables.", i); } } } /* Push local vars */ /* Add the context_closure instruction */ #ifdef DEBUG_INLINES printf("DEBUG: -> F_CONTEXT_CLOSURE %d %d\n", current_inline->function, context->num_locals); #endif /* DEBUG_INLINES */ ins_f_code(F_CONTEXT_CLOSURE); ins_short(current_inline->function); ins_short(context->num_locals); } /* Complete F_CONTEXT_CLOSURE instruction */ /* Clean up */ leave_block_scope(MY_TRUE); /* Argument scope */ leave_block_scope(MY_TRUE); /* Context scope */ finish_inline_closure(MY_FALSE); } /* complete_inline_closure() */ #endif /* USE_NEW_INLINES */ /* ========================= PROGRAM STRINGS ========================= */ /*-------------------------------------------------------------------------*/ static short store_prog_string (string_t *str) /* Add the tabled string 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 . */ { 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_mstring(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 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(string_t *) > 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(string_t *); *((string_t **)(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(). */ { string_t *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(string_t *); free_mstring( str = *(string_t**)(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() */ /*=========================================================================*/ #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_ELLIPSIS %token L_ELSE %token L_EQ %ifdef USE_NEW_INLINES %token L_FUNC %token L_BEGIN_INLINE %token L_END_INLINE %endif /* USE_NEW_INLINES */ %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 USE_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 %ifdef USE_STRUCTS %token L_STRUCT %endif %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 */ /* Note: vartype_t and fulltype_t references are not counted! * Throughout the compiler fulltype_ts are used even if the values * are not intended to have visibility information. */ %union { %line p_int number; /* Literal numbers, or whereever a number is required. */ double float_number; /* Literal floats */ struct { p_int number; unsigned short inhIndex; } closure; /* A closure (#'xxx). The .number determines the exact * nature of the closure. * For lfun closures, an inhIndex > 0 determines the * (inheritance index + 1) of a direct reference to an * inherited closure. */ struct { string_t *name; /* The tabled string with the name */ int quotes; /* Number of quotes */ } symbol; /* A literal symbol. */ ident_t *ident; /* L_IDENTIFIER, L_INLINE_FUN: The recognized identifier */ typeflags_t typeflags; /* Just the typeflags (reference, pointer, visibility). */ fulltype_t type; /* The datatype, not intended to have visibility flags. */ fulltype_t fulltype; /* The fulltype (datatype plus visibility) of entities. */ funflag_t inh_flags[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 { fulltype_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 */ fulltype_t type1; /* Type of index, resp. lower bound */ fulltype_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_AR_RANGE: [>x..y] * F_RA_RANGE: [y] * F_AA_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; fulltype_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 */ string_t *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: "::" */ 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. */ %ifdef USE_STRUCTS struct { int length; /* Number of initializers parsed */ /* Description of initializers parsed: */ struct struct_init_s * list; /* Head of list */ struct struct_init_s * last; /* Tail of list */ } struct_init_list; /* For runtime struct literals: head of the list describing * the encountered member initializers. */ struct { string_t * name; /* Member name, or NULL if unnamed */ vartype_t type; /* Member expr type */ } struct_init_member; /* For runtime struct literals: information about a single * member initializer. */ %endif /* USE_STRUCTS */ } /* YYSTYPE */ /*-------------------------------------------------------------------------*/ %type L_NUMBER constant %type L_FLOAT %type L_CLOSURE %type L_SYMBOL %type L_QUOTED_AGGREGATE %type L_IDENTIFIER L_INLINE_FUN L_LOCAL %type optional_star type_modifier type_modifier_list %type type %type opt_basic_type basic_type %type non_void_type opt_basic_non_void_type basic_non_void_type %type name_list local_name_list %type inheritance_qualifier inheritance_qualifiers %type inheritance_modifier_list inheritance_modifier %ifdef USE_NEW_INLINES %type inline_opt_type %endif /* USE_NEW_INLINES */ %type decl_cast cast %type note_start comma_expr expr0 expr4 %type function_call %ifdef USE_NEW_INLINES %type inline_func %else /* USE_NEW_INLINES */ %type inline_fun %endif /* USE_NEW_INLINES */ %type catch sscanf %type for_init_expr for_expr %type comma_expr_decl expr_decl %ifdef USE_PARSE_COMMAND %type parse_command %endif %type lvalue name_lvalue local_name_lvalue foreach_var_lvalue %type index_range index_expr %type case_label %type

optional_else %type anchestor %type call_other_name identifier %ifdef USE_STRUCTS %type member_name_list %type struct_init %type opt_struct_init opt_struct_init2 %type struct_member_name %endif /* USE_STRUCTS */ %type function_name /* Special uses of */ %type function_body /* program address or -1 */ %type argument argument_list lvalue_list %ifdef USE_NEW_INLINES %type inline_opt_args %endif /* USE_NEW_INLINES */ /* number of arguments */ %type expr_list expr_list3 e_expr_list2 expr_list2 /* Number of expressions in an expression list */ %type m_expr_values /* Number of values for a mapping entry (ie the 'width') */ %type L_ASSIGN /* Instruction code of the assignment, e.g. F_ADD_EQ */ %type foreach_expr /* FOREACH_LOOP (0) Normal foreach loop value * FOREACH_REF (1) Referenced foreach loop value * FOREACH_RANGE (2) Integer range as loop value */ %type foreach_vars /* Number of variables given to foreach */ %type opt_catch_mods opt_catch_mod_list opt_catch_modifier /* Bitflags for catch() modes: CATCH_FLAG_xxx from simulate.h */ /* Special uses of */ %type condStart /* [0]: current_break_address * [1]: address of the branch-offset of the if */ %type m_expr_list m_expr_list2 /* [0]: number of entries in a mapping literal * [1]: width of the mapping literal */ /* Special uses of */ %type 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 */ { $1.typeflags |= $2; #ifdef USE_NEW_INLINES def_function_typecheck($1, $3, MY_FALSE); #else /* USE_NEW_INLINES */ def_function_typecheck($1, $3); #endif /* USE_NEW_INLINES */ } '(' argument ')' { #ifdef USE_NEW_INLINES def_function_prototype($6, MY_FALSE); #else /* USE_NEW_INLINES */ def_function_prototype($6); #endif /* USE_NEW_INLINES */ } function_body { #ifdef USE_NEW_INLINES def_function_complete($9, MY_FALSE); #else /* USE_NEW_INLINES */ def_function_complete($9); #endif /* USE_NEW_INLINES */ #ifndef USE_NEW_INLINES if (first_inline_fun) insert_inline_fun_now = MY_TRUE; #else insert_pending_inline_closures(); #endif /* USE_NEW_INLINES */ } | name_list ';' /* Variable definition */ { #ifndef USE_NEW_INLINES if (first_inline_fun) insert_inline_fun_now = MY_TRUE; #else insert_pending_inline_closures(); #endif /* USE_NEW_INLINES */ } %ifdef USE_STRUCTS | struct_decl %endif /* USE_STRUCTS */ | 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 CURRENT_PROGRAM_SIZE = align(CURRENT_PROGRAM_SIZE); $$ = CURRENT_PROGRAM_SIZE; if (realloc_a_program(FUNCTION_HDR_SIZE)) { CURRENT_PROGRAM_SIZE += FUNCTION_HDR_SIZE; } else { yyerrorf("Out of memory: program size %"PRIuMPINT"\n" , mem_block[A_PROGRAM].current_size + FUNCTION_HDR_SIZE); YYACCEPT; } } block { $$ = $1; } | ';' { $$ = -1; } ; /* function_body */ %ifdef USE_NEW_INLINES /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* Inline functions */ inline_func: L_FUNC inline_opt_type { #ifdef DEBUG_INLINES printf("DEBUG: After inline_opt_type: program size %"PRIuMPINT"\n", CURRENT_PROGRAM_SIZE); #endif /* DEBUG_INLINES */ if (!prepare_inline_closure($2)) YYACCEPT; } inline_opt_args { #ifdef DEBUG_INLINES printf("DEBUG: After inline_opt_args: program size %"PRIuMPINT"\n", CURRENT_PROGRAM_SIZE); #endif /* DEBUG_INLINES */ current_inline->parse_context = MY_TRUE; } inline_opt_context { #ifdef DEBUG_INLINES printf("DEBUG: After inline_opt_context: program size %"PRIuMPINT"\n", CURRENT_PROGRAM_SIZE); #endif /* DEBUG_INLINES */ current_inline->parse_context = MY_FALSE; if (!inline_closure_prototype($4)) YYACCEPT; } block { #ifdef DEBUG_INLINES printf("DEBUG: After inline block: program size %"PRIuMPINT"\n", CURRENT_PROGRAM_SIZE); #endif /* DEBUG_INLINES */ $$.start = current_inline->end; $$.code = -1; $$.type = Type_Closure; complete_inline_closure(); } | L_BEGIN_INLINE { int i; #ifdef DEBUG_INLINES printf("DEBUG: After L_BEGIN_INLINE: program size %"PRIuMPINT"\n", CURRENT_PROGRAM_SIZE); #endif /* DEBUG_INLINES */ if (!prepare_inline_closure(Type_Any)) YYACCEPT; /* Synthesize $1..$9 as arguments */ for (i = 1; i < 10; i++) { char name[4]; ident_t *ident; sprintf(name, "$%d", i); ident = make_shared_identifier(name, I_TYPE_UNKNOWN, 0); add_local_name(ident, Type_Any, block_depth); } if (!inline_closure_prototype(9)) YYACCEPT; /* Put the code block in its own scope apart from the * parameters, so that define_local_variable doesn't * assume that there are already 9 Variables. */ enter_block_scope(); #ifdef DEBUG_INLINES printf("DEBUG: Before comma_expr: program size %"PRIuMPINT"\n", CURRENT_PROGRAM_SIZE); #endif /* DEBUG_INLINES */ } statements inline_comma_expr L_END_INLINE { #ifdef DEBUG_INLINES printf("DEBUG: After L_END_INLINE: program size %"PRIuMPINT"\n", CURRENT_PROGRAM_SIZE); #endif /* DEBUG_INLINES */ /* Complete the F_CLEAR_LOCALS at the baginning of the block. */ block_scope_t *scope = block_scope + block_depth - 1; if (use_local_scopes && scope->num_locals > scope->num_cleared) { mem_block[A_PROGRAM].block[scope->addr+2] = (char)(scope->num_locals - scope->num_cleared); } leave_block_scope(MY_FALSE); $$.start = current_inline->end; $$.code = -1; $$.type = Type_Closure; complete_inline_closure(); } ; /* inline_func */ inline_opt_args: /* empty */ { int i; /* Synthesize $1..$9 as arguments */ for (i = 1; i < 10; i++) { char name[4]; ident_t *ident; sprintf(name, "$%d", i); ident = make_shared_identifier(name, I_TYPE_UNKNOWN, 0); add_local_name(ident, Type_Any, block_depth); } $$ = 9; } | '(' argument ')' { $$ = $2; } ; /* inline_opt_args */ inline_opt_type: /* empty */ { #ifdef DEBUG_INLINES printf("DEBUG: inline_opt_type default: ANY\n"); #endif /* DEBUG_INLINES */ $$ = Type_Any; } | basic_type optional_star { #ifdef DEBUG_INLINES printf("DEBUG: inline_opt_type: %c%s\n", $2 ? '*' : ' ', get_type_name($1)); #endif /* DEBUG_INLINES */ set_fulltype($$, $1.typeflags | $2, $1.t_struct); } ; /* inline_opt_type */ inline_opt_context: /* empty */ | ':' inline_context_list inline_semi ; /* inline_opt_context */ inline_semi: /* empty */ | ';' ; /* inline_semi */ inline_context_list: /* empty */ | context_decl | inline_context_list ';' context_decl ; /* inline_context_list */ context_decl: local_name_list { /* Empty action to void value from local_name_list */ } ; /* context_decl */ inline_comma_expr: /* Empty: nothing to do */ | comma_expr { /* Add a F_RETURN to complete the statement */ ins_f_code(F_RETURN); } ; /* inline_comma_expr */ %endif /* USE_NEW_INLINES */ %ifdef USE_STRUCTS /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* Definition of a struct */ struct_decl: type_modifier_list L_STRUCT L_IDENTIFIER ';' { (void)define_new_struct(MY_TRUE, $3, $1); } | type_modifier_list L_STRUCT L_IDENTIFIER { size_t i; /* Free any struct members left over from a previous * struct parse. This should happen only in case * of errors. */ for (i = 0; i < STRUCT_MEMBER_COUNT; i++) { free_struct_member_data(&STRUCT_MEMBER(i)); } mem_block[A_STRUCT_MEMBERS].current_size = 0; current_struct = define_new_struct(MY_FALSE, $3, $1); if (current_struct < 0) YYACCEPT; } opt_base_struct '{' opt_member_list '}' ';' { finish_struct(compiled_file, current_id_number+1); } ; /* struct_decl */ opt_base_struct: /* empty */ { } | '(' L_IDENTIFIER ')' { /* Look up the struct id for the given identifier */ int num = -1; if ($2->type == I_TYPE_UNKNOWN) { /* Identifier -> no such struct encountered yet */ yyerrorf("Unknown base struct '%s'", get_txt($2->name)); } else { ident_t *p = $2; /* Find the global struct identifier */ while (p != NULL && p->type != I_TYPE_GLOBAL) p = p->inferior; if (p == NULL || (num = p->u.global.struct_id) < 0) { yyerrorf("Unknown base struct '%s'", get_txt($2->name)); } else if (STRUCT_DEF(num).flags & NAME_PROTOTYPE) { yyerrorf("Undefined base struct '%s'", get_txt($2->name)); } else if (!struct_t_unique_name(STRUCT_DEF(num).type)) { yyerrorf("Incomplete base struct '%s'", get_txt($2->name)); } else { struct_type_t *ptype; ptype = STRUCT_DEF(num).type; if (struct_t_size(ptype) > 0) { int count; struct_member_t *member; member = ptype->member; count = struct_t_size(ptype); for ( ; count > 0; count--, member++ ) add_struct_member(member->name, member->type, ptype); } } } /* if type == UNKNOWN */ } ; /* opt_base_struct */ opt_member_list: /* empty */ | member_list ; /* opt_member_list */ member_list: member | member_list member ; /* member_list */ member: member_name_list ';' { /* The member_name_list adds the struct members. */ } ; /* member */ member_name_list: basic_non_void_type optional_star L_IDENTIFIER { fulltype_t actual_type = $1; vartype_t type; actual_type.typeflags |= $2; assign_full_to_vartype(&type, actual_type); add_struct_member($3->name, type, NULL); $$ = $1; } | member_name_list ',' optional_star L_IDENTIFIER { fulltype_t actual_type = $1; vartype_t type; actual_type.typeflags |= $3; assign_full_to_vartype(&type, actual_type); add_struct_member($4->name, type, NULL); $$ = $1; } ; /* member_name_list */ %endif /* USE_STRUCTS */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* 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; if (CURRENT_PROGRAM_SIZE && !(((function_t *)(mem_block[A_FUNCTIONS].block+ mem_block[A_FUNCTIONS].current_size))[-1].flags & NAME_INHERITED) ) { 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) && !EVALUATION_TOO_LONG() ) { svalue_t *res; push_ref_string(inter_sp, last_string_constant); if (!compat_mode) { char * filename; filename = alloca(strlen(current_loc.file->name)+2); *filename = '/'; strcpy(filename+1, current_loc.file->name); push_c_string(inter_sp, filename); } else push_c_string(inter_sp, current_loc.file->name); 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'." , get_txt(last_string_constant)); YYACCEPT; } for (cp = get_txt(res->u.str); *cp == '/'; cp++) NOOP; if (!legal_path(cp)) { yyerrorf("Illegal path '%s'.", get_txt(res->u.str)); YYACCEPT; } /* Ok, now replace the parsed string with the name * we just got. */ free_mstring(last_string_constant); last_string_constant = new_tabled(cp); } /* else: no result - use the string as it is */ } else if (EVALUATION_TOO_LONG()) { yyerrorf("Can't call master::%s for " "'%s': eval cost too big" , get_txt(STR_INHERIT_FILE) , get_txt(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; #ifdef USE_SWAP if (ob->flags & O_SWAPPED && load_ob_from_swap(ob) < 0) { free_mstring(last_string_constant); last_string_constant = NULL; yyerrorf("Out of memory when unswapping '%s'", get_txt(ob->name)); YYACCEPT; } #endif /* Legal to inherit? */ if (ob->prog->flags & P_NO_INHERIT) { yyerror("Illegal to inherit an object which sets " "'#pragma no_inherit'."); YYACCEPT; } free_mstring(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" , get_txt(inherit.prog->name)); YYACCEPT; } else yywarnf("Program '%s' already inherited" , get_txt(inherit.prog->name)); } } if (!(inherit.inherit_type & INHERIT_TYPE_DUPLICATE)) { /* Copy the functions and variables, and take * care of the initializer. */ int initializer; #ifdef USE_STRUCTS copy_structs(ob->prog, $1[0]); #endif 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_f_code(F_SAVE_ARG_FRAME); ins_f_code(F_CALL_INHERITED); ins_short(INHERIT_COUNT); ins_short(initializer); ins_f_code(F_RESTORE_ARG_FRAME); ins_f_code(F_POP_VALUE); add_new_init_jump(); } /* 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 { $$ = $1; } | 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 typeflags_t last_modifier; %line /* The inherit statement must only specify visibility * e.g. not "inherit int * foobar" */ if ($1.typeflags & TYPE_MOD_MASK) { yyerror("syntax error"); } /* Check if there were any modifiers at all */ if ( !($1.typeflags & ~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.typeflags & ~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 (mstreq(last_identifier->name, STR_FUNCTIONS)) { $$[0] = last_modifier; $$[1] = 0; } else if (mstreq(last_identifier->name, STR_VARIABLES)) { $$[0] = 0; $$[1] = last_modifier; } else { yyerrorf("Unrecognized inheritance modifier '%s'" , get_txt(last_identifier->name)); $$[0] = $$[1] = 0; } } ; /* 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 { set_fulltype($$, $1 | $2.typeflags, $2.t_struct); } ; non_void_type: type_modifier_list opt_basic_non_void_type { set_fulltype($$, $1 | $2.typeflags, $2.t_struct); } ; 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; } | L_INT { $$ = Type_Number; } | L_STRING_DECL { $$ = Type_String; } | L_OBJECT { $$ = Type_Object; } | L_CLOSURE_DECL { $$ = Type_Closure; } | L_SYMBOL_DECL { $$ = Type_Symbol; } | L_FLOAT_DECL { $$ = Type_Float; } | L_MAPPING { $$ = Type_Mapping; } | L_MIXED { $$ = Type_Any; } %ifdef USE_STRUCTS | L_STRUCT identifier { int num; num = find_struct($2); if (num < 0) { yyerrorf("Unknown struct '%s'", get_txt($2)); $$ = Type_Unknown; } else { $$.typeflags = TYPE_STRUCT; $$.t_struct = STRUCT_DEF(num).type; } free_mstring($2); } %endif /* USE_STRUCTS */ ; /* basic_non_void_type */ basic_type: basic_non_void_type | L_VOID { $$ = Type_Void; } ; /* basic_type */ cast: '(' basic_type optional_star ')' { set_fulltype($$, $2.typeflags | $3, $2.t_struct); } ; /* TODO: Remove decl_casts - they are practically useless */ decl_cast: '(' '{' basic_type optional_star '}' ')' { set_fulltype($$, $3.typeflags | $4, $3.t_struct); } ; /* A generic identifier */ identifier: L_IDENTIFIER { string_t *p; /* Extract the string from the ident structure */ p = ref_mstring($1->name); $$ = p; } | L_LOCAL { $$ = ref_mstring($1->name); } ; /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* 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.typeflags && $1.typeflags == 0) { yyerror("Missing type for argument"); #ifdef USE_NEW_INLINES add_local_name($3, Type_Any, block_depth); #else /* USE_NEW_INLINES */ add_local_name($3, Type_Any, block_depth, MY_FALSE); #endif /* USE_NEW_INLINES */ /* Supress more errors */ } else { fulltype_t argtype; set_fulltype(argtype, $1.typeflags | $2, $1.t_struct); #ifdef USE_NEW_INLINES add_local_name($3, argtype, block_depth); #else /* USE_NEW_INLINES */ add_local_name($3, argtype, block_depth, MY_FALSE); #endif /* USE_NEW_INLINES */ } } | non_void_type optional_star L_LOCAL { %ifndef USE_NEW_INLINES /* A local name is redeclared. Since this is the argument list of a * function, it can't be legal. */ yyerror("Illegal to redeclare local name"); %else /* A local name is redeclared. */ if (current_inline == NULL) { /* Since this is the argument list of a function, it can't be * legal. */ yyerror("Illegal to redeclare local name"); } else { /* However, it is legal for the argument list of an inline * closure. */ fulltype_t argtype; set_fulltype(argtype, $1.typeflags | $2, $1.t_struct); redeclare_local($3, argtype, block_depth); } %endif } ; /* new_arg_name */ name_list: /* Simple variable definition */ type optional_star L_IDENTIFIER { %line if ($1.typeflags == 0) yyerror("Missing type"); define_global_variable($3, $1, $2, MY_FALSE); $$ = $1; } /* Variable definition with initialization */ | type optional_star L_IDENTIFIER { if ($1.typeflags == 0) yyerror("Missing type"); $$ = define_global_variable($3, $1, $2, MY_TRUE); } L_ASSIGN expr0 { init_global_variable($4, $3, $1, $2, $5, $6.type); $$ = $1; } | name_list ',' optional_star L_IDENTIFIER { define_global_variable($4, $1, $3, MY_FALSE); $$ = $1; } /* Variable definition with initialization */ | name_list ',' optional_star L_IDENTIFIER { $$ = define_global_variable($4, $1, $3, MY_TRUE); } L_ASSIGN expr0 { init_global_variable($5, $4, $1, $3, $6, $7.type); $$ = $1; } ; /* name_list */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* Blocks and simple statements. */ block: '{' statements_block '}' 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 > scope->num_cleared) { mem_block[A_PROGRAM].block[scope->addr+2] = (char)(scope->num_locals - scope->num_cleared); } } leave_block_scope(MY_FALSE); } ; /* block_statements */ statements: /* empty */ | statements local_name_list ';' | statements statement ; local_name_list: basic_type optional_star L_IDENTIFIER { struct lvalue_s lv; define_local_variable($3, $1, $2, &lv, MY_FALSE, MY_FALSE); $$ = $1; } | basic_type optional_star L_LOCAL { struct lvalue_s lv; define_local_variable($3, $1, $2, &lv, MY_TRUE, MY_FALSE); $$ = $1; } | basic_type optional_star L_IDENTIFIER { define_local_variable($3, $1, $2, &$$, MY_FALSE, MY_TRUE); } L_ASSIGN expr0 { init_local_variable($3, &$4, $5, $6.type); $$ = $1; } | basic_type optional_star L_LOCAL { define_local_variable($3, $1, $2, &$$, MY_TRUE, MY_TRUE); } L_ASSIGN expr0 { init_local_variable($3, &$4, $5, $6.type); $$ = $1; } | local_name_list ',' optional_star L_IDENTIFIER { struct lvalue_s lv; define_local_variable($4, $1, $3, &lv, MY_FALSE, MY_FALSE); $$ = $1; } | local_name_list ',' optional_star L_LOCAL { struct lvalue_s lv; define_local_variable($4, $1, $3, &lv, MY_TRUE, MY_FALSE); $$ = $1; } | local_name_list ',' optional_star L_IDENTIFIER { define_local_variable($4, $1, $3, &$$, MY_FALSE, MY_TRUE); } L_ASSIGN expr0 { init_local_variable($4, &$5, $6, $7.type); $$ = $1; } | local_name_list ',' optional_star L_LOCAL { define_local_variable($4, $1, $3, &$$, MY_TRUE, MY_TRUE); } L_ASSIGN expr0 { init_local_variable($4, &$5, $6, $7.type); $$ = $1; } ; /* local_name_list */ statement: comma_expr ';' { insert_pop_value(); #ifdef F_BREAK_POINT if (d_flag) ins_f_code(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 | 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_f_code(F_BREAK); } else { /* A normal loop break: add the FBRANCH to the list */ ins_f_code(F_FBRANCH); ins_int32(current_break_address & BREAK_ADDRESS_MASK); current_break_address = CURRENT_PROGRAM_SIZE - 4; if (current_break_address > BREAK_ADDRESS_MASK) yyerrorf("Compiler limit: (L_BREAK) value too large: %"PRIdPINT , current_break_address); } } | 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_int32(4); 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_f_code(F_FBRANCH); } /* In either case, handle the list of continues alike */ ins_int32(current_continue_address & CONTINUE_ADDRESS_MASK); current_continue_address = ( current_continue_address & SWITCH_DEPTH_MASK ) | ( CURRENT_PROGRAM_SIZE - 4 ); } ; /* statement */ return: L_RETURN { fulltype_t rtype = exact_types; rtype.typeflags &= TYPE_MOD_MASK; if (exact_types.typeflags && !BASIC_TYPE(rtype, Type_Void)) type_error("Must return a value for a function declared", exact_types); ins_f_code(F_RETURN0); } | L_RETURN comma_expr { %line fulltype_t type2 = $2.type; if (exact_types.typeflags) { fulltype_t rtype = exact_types; rtype.typeflags &= 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(type2, rtype)) { char tmp[100]; strcpy(tmp, get_type_name(type2)); yyerrorf("Return type not matching: got %s, expected %s" , tmp, get_type_name(rtype)); } } if (type2.typeflags & 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_f_code(F_RETURN); } ; /* return */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* The while() statement * * It is compiled into: * * BRANCH c * l: * c: * BBRANCH_WHEN_NON_ZERO l */ while: { /* Save the previous environment */ $$[0] = current_continue_address; $$[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 code, add the BBRANCH instruction and * store all of it outside the program. After the * 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' */ $$.p = expression; $$.length = length; $$.line = current_loc.line; /* Restart codegeneration for the body where we began */ CURRENT_PROGRAM_SIZE = addr; last_expression = -1; /* The initial branch to the condition code */ ins_f_code(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_int32(current_continue_address); upd_int32(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 ($6.line != current_loc.line) store_line_number_info(); add_to_mem_block(A_PROGRAM, $6.p, $6.length+2); yfree($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 ($6.line != current_loc.line) store_line_number_relocation($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_int32(current_break_address); upd_int32(current_break_address, CURRENT_PROGRAM_SIZE - current_break_address); } /* Restore the previous environment */ current_continue_address = $1[0]; current_break_address = $1[1]; } ; /* while */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* The do-while() statement * * It is compiled into: * * l: * * BBRANCH_WHEN_NON_ZERO l */ do: { /* Save the previous environment */ $$[0] = current_continue_address; $$[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_int32(current_continue_address); upd_int32(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 %"PRIuMPINT"\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_int32(current_break_address); upd_int32(current_break_address, current - current_break_address); } /* Restore the previous environment */ current_continue_address = $1[0]; current_break_address = $1[1]; } ; /* do */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* The for() statement. * * It is compiled as: * * CLEAR_LOCALS * * POP * BRANCH c * l: * * POP * c: * BBRANCH_WHEN_NON_ZERO l */ for: L_FOR '(' { %line /* Save the previous environment */ $$[0] = current_continue_address; $$[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 ';' { %line /* Get rid of whatever init_expr computed */ insert_pop_value(); /* From here, the will be placed eventually */ current_continue_address = CONTINUE_DELIMITER; $$ = 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 = $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 */ $$.p = expression; $$.length = length; $$.line = current_loc.line; /* Restart codegeneration from here */ CURRENT_PROGRAM_SIZE = start; last_expression = -1; } for_expr ')' { %line /* Save the 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 - $6; $$.p = yalloc(length); if (length) memcpy( $$.p , mem_block[A_PROGRAM].block + $6 , length ); $$.length = length; $$.line = current_loc.line; /* Restart the codegeneration for the body */ CURRENT_PROGRAM_SIZE = $6; last_expression = -1; current_break_address = BREAK_DELIMITER; ins_f_code(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 > scope->num_cleared) { mem_block[A_PROGRAM].block[scope->addr+2] = (char)(scope->num_locals - scope->num_cleared); } } } statement { %line /* The loop is complete, now add the and * code saved 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_int32(current_continue_address); upd_int32(current_continue_address, CURRENT_PROGRAM_SIZE - current_continue_address); } if ( $9.line != current_loc.line || ( $12.line != current_loc.line && $12.length) ) store_line_number_info(); /* Add the code block if needed */ if ($12.length) { add_to_mem_block(A_PROGRAM, $12.p , $12.length); if ($12.line != $9.line) store_line_number_relocation($12.line); } yfree($12.p); /* Fix the branch over the body */ offset = fix_branch( F_LBRANCH, CURRENT_PROGRAM_SIZE, $6 + 1); /* Add the code block */ add_to_mem_block(A_PROGRAM, $9.p, $9.length+2); yfree($9.p); /* Create the branch back after the condition */ offset += $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 ($9.line != current_loc.line) store_line_number_relocation($9.line); /* Now complete the break instructions. */ for (; current_break_address > 0 ; current_break_address = next_addr) { next_addr = read_int32(current_break_address); upd_int32(current_break_address, CURRENT_PROGRAM_SIZE - current_break_address); } /* Restore the previous environment */ current_continue_address = $3[0]; current_break_address = $3[1]; /* and leave the for scope */ leave_block_scope(MY_FALSE); } ; /* for */ /* Special rules for 'int = ' declarations in the first * for() expression. */ for_init_expr: /* EMPTY */ { last_expression = mem_block[A_PROGRAM].current_size; ins_number(1); /* 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 = " type expression. */ %line fulltype_t type2; /* Check the assignment for validity */ type2 = $3.type; if (exact_types.typeflags && !compatible_types($1.type, type2, MY_TRUE)) { yyerrorf("Bad assignment %s", get_two_types($1.type, type2)); } if ($2 != F_ASSIGN) { yyerror("Only plain assignments allowed here"); } if (type2.typeflags & TYPE_MOD_REFERENCE) yyerror("Can't trace reference assignments"); /* Add the bytecode to create the lvalue and do the * assignment. */ if (!add_lvalue_code(&$1, $2)) YYACCEPT; } | local_name_lvalue { /* We got a "int " type expression. * Compile it as if it was a "int = 0" expression. */ %line /* Insert the implied push of number 0 */ ins_number(0); /* Add the bytecode to create the lvalue and do the * assignment. */ if (!add_lvalue_code(&$1, F_ASSIGN)) YYACCEPT; } ; /* expr_decl */ for_expr: /* EMPTY */ { last_expression = mem_block[A_PROGRAM].current_size; ins_number(1); } | comma_expr ; /* for_expr */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* The foreach() statement * * It is compiled into: or when is empty: * * CLEAR_LOCALS CLEAR_LOCALS * PUSH_(LOCAL_)LVALUE * ... POP_VALUE * PUSH_(LOCAL_)LVALUE [POP_VALUE for integer ranges] * * FOREACH(_REF) c * l: * c: FOREACH_NEXT l * e: FOREACH_END * * continue's branch to c, break's to e. */ foreach: L_FOREACH '(' { /* Save the previous environment */ $$[0] = current_continue_address; $$[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 */ $
$ = CURRENT_PROGRAM_SIZE; } foreach_expr ')' { %line /* 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 > scope->num_cleared) { mem_block[A_PROGRAM].block[scope->addr+2] = (char)(scope->num_locals - scope->num_cleared); } } /* Create the FOREACH instruction, leaving the branch field * blank. */ switch ($7) { case FOREACH_LOOP: ins_f_code(F_FOREACH); break; case FOREACH_REF: ins_f_code(F_FOREACH_REF); break; case FOREACH_RANGE: ins_f_code(F_FOREACH_RANGE); break; default: yyerrorf("Unknown foreach_expr type %ld.\n", (long)$7); fatal("Unknown foreach_expr type %ld.\n", (long)$7); /* NOTREACHED */ } 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 == (p_int)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 = $
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++; if ($7 == FOREACH_RANGE) { 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_int32(current_continue_address); upd_int32(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_int32(current_break_address); upd_int32(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 = $3[0]; current_break_address = $3[1]; /* and leave the scope */ leave_block_scope(MY_FALSE); } ; /* 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. */ %line if (!add_lvalue_code(&$1, 0)) YYACCEPT; } ; /* 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. */ identifier { if (!mstreq($1, STR_IN)) yyerror("Expected keyword 'in' in foreach()"); free_mstring($1); } | ':' ; /* foreach_in */ foreach_expr: expr0 { fulltype_t dtype; Bool gen_refs; %line gen_refs = ($1.type.typeflags & TYPE_MOD_MASK & (~TYPE_MOD_RMASK)) != 0; set_fulltype(dtype, $1.type.typeflags & TYPE_MOD_RMASK, $1.type.t_struct); if (!(dtype.typeflags & TYPE_MOD_POINTER) && dtype.typeflags != TYPE_ANY && dtype.typeflags != TYPE_STRING && dtype.typeflags != TYPE_MAPPING && (dtype.typeflags != TYPE_NUMBER || gen_refs) && (exact_types.typeflags || dtype.typeflags != TYPE_UNKNOWN) ) { type_error("Expression for foreach() of wrong type", $1.type); } $$ = gen_refs ? FOREACH_REF : FOREACH_LOOP; } | expr0 L_RANGE expr0 { fulltype_t dtype; %line if (($1.type.typeflags & (~TYPE_MOD_RMASK)) != 0) { type_error("Expression for foreach() of wrong type", $1.type); } set_fulltype(dtype, $1.type.typeflags & TYPE_MOD_RMASK, $1.type.t_struct); if (dtype.typeflags != TYPE_ANY && dtype.typeflags != TYPE_NUMBER && (exact_types.typeflags || dtype.typeflags != TYPE_UNKNOWN) ) { type_error("Expression for foreach() of wrong type", $1.type); } if (($3.type.typeflags & (~TYPE_MOD_RMASK)) != 0) { type_error("Expression for foreach() of wrong type", $3.type); } set_fulltype(dtype, $3.type.typeflags & TYPE_MOD_RMASK, $3.type.t_struct); if (dtype.typeflags != TYPE_ANY && dtype.typeflags != TYPE_NUMBER && (exact_types.typeflags || dtype.typeflags != TYPE_UNKNOWN) ) { type_error("Expression for foreach() of wrong type", $3.type); } $$ = FOREACH_RANGE; } ; /* foreach_expr */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* The switch statement. * * switch.h explains how the bytecode looks like. * * Note that the actual switch rule is: * * switch: L_SWITCH ( comma_expr ) '{' switch_block '}' * * and that case and default are explicitly parsed in the * switch_block rule. Each group of statements after a * label have their own scope, so that variable declarations * within the switch block may not cross case labels. * * That also means that in contrast to C the code * * switch(x); * or switch(x) write("Foo"); * or switch(x) {{ case "foo": break; }} * * is syntactically not ok. */ 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 (%zu bytes)" , 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_f_code(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; } '{' switch_block '}' { %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_f_code(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 */ switch_block: switch_block switch_statements | switch_statements ; /* switch_block */ switch_statements: switch_label statements_block ; switch_label: case | default ; case: L_CASE case_label ':' { %line /* Mark the current program address as another * case target for the current switch. */ case_list_entry_t *temp; /* Should be within a switch statement. */ assert(current_break_address & CASE_LABELS_ENABLED); /* 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_loc.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"); /* Should be within a switch statement. */ assert(current_break_address & CASE_LABELS_ENABLED); /* 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) { yyerrorf("Illegal case range: lower limit %ld > upper limit %ld" , (long)$2.key, (long)$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_loc.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_loc.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. */ /* Should be within a switch statement. */ assert(current_break_address & CASE_LABELS_ENABLED); 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: * * * BRANCH_WHEN_ZERO e BRANCH_WHEN_ZERO e * * e: BRANCH f * e: * 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 %"PRIuMPINT"\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_f_code(F_BRANCH); $
$ = 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, $
2); $$ += $
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("modulus 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("L_STRING LSTRING: presence of rule should prevent its reduction\n"); } | string_constant '+' L_STRING L_STRING { fatal("L_STRING LSTRING: presence of rule should prevent its reduction\n"); } | '(' 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): * * * LDUP LDUP * ASSIGN-operator LOR l DUP * LBRANCH_WHEN_NON_ZERO l * l: SWAP_VALUES POP_VALUE * ASSIGN * 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); $
$ = 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); $
$ = CURRENT_PROGRAM_SIZE; ins_byte(0); } } } expr0 %prec L_ASSIGN { fulltype_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.typeflags && !compatible_types(type1, type2, MY_TRUE) ) { Bool ok = MY_FALSE; switch($2) { case F_LAND_EQ: case F_LOR_EQ: ok = MY_TRUE; break; case F_ADD_EQ: switch(type1.typeflags) { case TYPE_STRING: if (type2.typeflags == TYPE_NUMBER || type2.typeflags == TYPE_FLOAT) { ok = MY_TRUE; } break; case TYPE_FLOAT: if (type2.typeflags == TYPE_NUMBER) { ok = MY_TRUE; } break; } break; case F_SUB_EQ: switch(type1.typeflags) { case TYPE_FLOAT: if (type2.typeflags == TYPE_NUMBER) { ok = MY_TRUE; } break; } break; case F_MULT_EQ: switch(type1.typeflags) { case TYPE_STRING: if (type2.typeflags == TYPE_NUMBER) { ok = MY_TRUE; } break; case TYPE_FLOAT: if (type2.typeflags == TYPE_NUMBER) { ok = MY_TRUE; } break; default: if ((type1.typeflags & TYPE_MOD_POINTER) && type2.typeflags == TYPE_NUMBER) { ok = MY_TRUE; } } break; case F_DIV_EQ: switch(type1.typeflags) { case TYPE_FLOAT: if (type2.typeflags == TYPE_NUMBER) { ok = MY_TRUE; } break; } break; case F_AND_EQ: switch(type1.typeflags) { case TYPE_MAPPING: if (type2.typeflags & TYPE_MOD_POINTER) { ok = MY_TRUE; } break; default: if ((type1.typeflags & TYPE_MOD_POINTER) && type2.typeflags == TYPE_MAPPING ) { 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.typeflags & TYPE_MOD_REFERENCE) yyerror("Can't trace reference assignments."); #ifdef USE_STRUCTS /* Special checks for struct assignments */ if (IS_TYPE_STRUCT(type1) || IS_TYPE_STRUCT(type2) ) { restype = type1; if ($2 != F_ASSIGN) yyerror("Only plain assigment allowed for structs"); } #endif /* USE_STRUCTS */ 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($
3, F_LBRANCH_WHEN_ZERO); } else if ($2 == F_LOR_EQ) { update_lop_branch($
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("Bad assignment: illegal lhs (target)"); $$ = $3; $$.type = Type_Any; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '?' { /* Insert the branch to the :-part and remember this address */ ins_f_code(F_BRANCH_WHEN_ZERO); $
$ = 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)$
3; /* The branch to the end */ ins_f_code(F_BRANCH); $
$ = 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; $
$ = 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; fulltype_t type1, type2; last_expression = -1; old_address = $
3; 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; } /* Check the types and determine the result type */ type1 = $4.type; type2 = $7.type; $$ = $1; $$.end = CURRENT_PROGRAM_SIZE; if (!compatible_types(type1, type2, MY_FALSE)) { $$.type = Type_Any; if ((type1.typeflags & TYPE_MOD_POINTER) != 0 && (type2.typeflags & TYPE_MOD_POINTER) != 0) $$.type.typeflags |= TYPE_MOD_POINTER; /* TODO: yyinfof("Different types to ?: */ } else if (type1.typeflags == TYPE_ANY) $$.type = type2; else if (type2.typeflags == TYPE_ANY) $$.type = type1; else if (type1.typeflags == (TYPE_MOD_POINTER|TYPE_ANY) ) $$.type = type2; else if (type2.typeflags == (TYPE_MOD_POINTER|TYPE_ANY) ) $$.type = type1; else $$.type = type1; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 L_LOR %prec L_LOR { /* Insert the LOR and remember the position */ ins_f_code(F_LOR); $
$ = CURRENT_PROGRAM_SIZE; ins_byte(0); } expr0 { /* Update the offset the earlier LOR instruction */ update_lop_branch($
3, F_LBRANCH_WHEN_NON_ZERO); $$ = $1; $$.end = CURRENT_PROGRAM_SIZE; /* Determine the result type */ if (equal_types($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_f_code(F_LAND); $
$ = CURRENT_PROGRAM_SIZE; ins_byte(0); } expr0 { /* Update the offset the earlier LAND instruction */ update_lop_branch($
3, F_LBRANCH_WHEN_ZERO); $$ = $1; $$.end = CURRENT_PROGRAM_SIZE; /* Determine the return type */ if (equal_types($1.type, $4.type)) $$.type = $1.type; else $$.type = Type_Any; /* Return type can't be known */ } /* LAND */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '|' expr0 { $$ = $1; if (($1.type.typeflags | $3.type.typeflags) & TYPE_MOD_POINTER) { if (exact_types.typeflags && ($1.type.typeflags ^ $3.type.typeflags) & TYPE_MOD_POINTER ) yyerrorf("Incompatible types for arguments to | %s" , get_two_types($1.type, $3.type)); if (equal_types($1.type, $3.type)) $$.type = $1.type; else { $$.type = Type_Ptr_Any; } } else { if (exact_types.typeflags && !BASIC_TYPE($1.type, Type_Number)) type_error("Bad argument 1 to |", $1.type); if (exact_types.typeflags && !BASIC_TYPE($3.type, Type_Number)) type_error("Bad argument 2 to |", $3.type); $$.type = Type_Number; } ins_f_code(F_OR); $$.end = CURRENT_PROGRAM_SIZE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '^' expr0 { $$ = $1; if (($1.type.typeflags | $3.type.typeflags) & TYPE_MOD_POINTER) { if (exact_types.typeflags && ($1.type.typeflags ^ $3.type.typeflags) & TYPE_MOD_POINTER ) yyerrorf("Incompatible types for arguments to | %s" , get_two_types($1.type, $3.type)); if (equal_types($1.type, $3.type)) $$.type = $1.type; else $$.type = Type_Ptr_Any; } else { if (exact_types.typeflags && !BASIC_TYPE($1.type, Type_Number)) type_error("Bad argument 1 to ^", $1.type); if (exact_types.typeflags && !BASIC_TYPE($3.type, Type_Number)) type_error("Bad argument 2 to ^", $3.type); $$.type = Type_Number; } ins_f_code(F_XOR); $$.end = CURRENT_PROGRAM_SIZE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '&' expr0 { $$ = $1; ins_f_code(F_AND); $$.end = CURRENT_PROGRAM_SIZE; $$.type = Type_Any; /* Check the types */ /* TODO: Implement the typechecks, including result type * TODO:: by table lookups. */ if (exact_types.typeflags) { fulltype_t first_type = $1.type; fulltype_t second_type = $3.type; if ( first_type.typeflags == TYPE_ANY && second_type.typeflags == TYPE_ANY ) { /* $$ == TYPE_ANY is correct */ } else if (first_type.typeflags == TYPE_MAPPING) { if (second_type.typeflags != TYPE_MAPPING && !(second_type.typeflags & TYPE_MOD_POINTER) && second_type.typeflags != TYPE_ANY ) { type_error("Bad argument 2 to &", second_type ); } $$.type = Type_Mapping; } else if ( (first_type.typeflags | second_type.typeflags) & TYPE_MOD_POINTER) { if ((first_type.typeflags & TYPE_MOD_POINTER) && second_type.typeflags == TYPE_MAPPING ) { $$.type = first_type; } else if (first_type.typeflags == TYPE_NUMBER || second_type.typeflags == TYPE_NUMBER) { yyerrorf("Incompatible types for arguments to & %s" , get_two_types(first_type, second_type)); } else if (( !( first_type.typeflags & TYPE_MOD_POINTER ) || first_type.typeflags & TYPE_MOD_REFERENCE) && first_type.typeflags != TYPE_ANY) { type_error("Bad argument 1 to &", first_type ); } else if (( !( second_type.typeflags & TYPE_MOD_POINTER ) || second_type.typeflags & TYPE_MOD_REFERENCE) && second_type.typeflags != TYPE_ANY) { type_error("Bad argument 2 to &", first_type ); } else { fulltype_t f_type = first_type; fulltype_t s_type = second_type; f_type.typeflags &= ~TYPE_MOD_POINTER; s_type.typeflags &= ~TYPE_MOD_POINTER; if ( !BASIC_TYPE(f_type, s_type) ) { yyerrorf("Incompatible types for arguments to & %s" , get_two_types(first_type, second_type)); } else { $$.type = Type_Ptr_Any; } } } 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.typeflags == 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 { fulltype_t t1 = $1.type, t2 = $3.type; $$ = $1; if (exact_types.typeflags && !equal_types(t1, t2) && t1.typeflags != TYPE_ANY && t2.typeflags != TYPE_ANY && !(t1.typeflags == TYPE_NUMBER && t2.typeflags == TYPE_FLOAT) && !(t1.typeflags == TYPE_FLOAT && t2.typeflags == TYPE_NUMBER) ) { yyerrorf("== always false because of different types %s" , get_two_types($1.type, $3.type)); } ins_f_code(F_EQ); $$.type = Type_Number; $$.end = CURRENT_PROGRAM_SIZE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 L_NE expr0 { fulltype_t t1 = $1.type, t2 = $3.type; $$ = $1; if (exact_types.typeflags && !equal_types(t1, t2) && t1.typeflags != TYPE_ANY && t2.typeflags != TYPE_ANY && !(t1.typeflags == TYPE_NUMBER && t2.typeflags == TYPE_FLOAT) && !(t1.typeflags == TYPE_FLOAT && t2.typeflags == TYPE_NUMBER) ) { yyerrorf("!= always true because of different types %s" , get_two_types($1.type, $3.type)); } ins_f_code(F_NE); $$.end = CURRENT_PROGRAM_SIZE; $$.type = Type_Number; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | 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_f_code(F_LSH); $$.end = CURRENT_PROGRAM_SIZE; $$.type = Type_Number; if (exact_types.typeflags) { if (!BASIC_TYPE($1.type, Type_Number)) type_error("Bad argument 1 to '<<'", $1.type); if (!BASIC_TYPE($3.type, Type_Number)) type_error("Bad argument 2 to '<<'", $3.type); } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 L_RSH expr0 { $$ = $1; ins_f_code(F_RSH); $$.type = Type_Number; $$.end = CURRENT_PROGRAM_SIZE; if (exact_types.typeflags) { if (!BASIC_TYPE($1.type, Type_Number)) type_error("Bad argument 1 to '>>'", $1.type); if (!BASIC_TYPE($3.type, Type_Number)) type_error("Bad argument 2 to '>>'", $3.type); } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 L_RSHL expr0 { $$ = $1; ins_byte(F_RSHL); $$.end = CURRENT_PROGRAM_SIZE; $$.type = Type_Number; if (exact_types.typeflags) { if (!BASIC_TYPE($1.type, Type_Number)) type_error("Bad argument 1 to '>>>'", $1.type); if (!BASIC_TYPE($3.type, Type_Number)) type_error("Bad argument 2 to '>>>'", $3.type); } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '+' { %line $$[0] = last_expression; $$[1] = last_string_is_new; } expr0 { /* Type checks of this case are complicated, therefore * we'll do almost all of them at run-time. * Here we just try to fold "string" + "string", and * disallow additions of structs. */ 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 && $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. */ string_t *str1, *str2, *sum; int i; /* Retrieve both strings from the A_STRINGS area * and catenate them. */ str1 = ((string_t**)(mem_block[A_STRINGS].block)) [p[-3] | (p[-4]-(F_CSTRING0))<<8 ]; str2 = ((string_t**)(mem_block[A_STRINGS].block)) [p[-1] | (p[-2]-(F_CSTRING0))<<8 ]; sum = mstr_add(str1, str2); if (!sum) { yyerrorf("Out of memory for string literal (%zu bytes)" , (mstrsize(str1)+mstrsize(str2)) ); YYACCEPT; } /* If possible, try to delete the constituent strings * from the string area. */ if (last_string_is_new) delete_prog_string(); if ($3[1]) delete_prog_string(); /* Store the new string and update the CSTRING * instructions. */ sum = make_tabled(sum); if (!sum) { yyerror("Out of memory for string literal"); YYACCEPT; } i = store_prog_string(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_f_code(F_ADD); $$.type = Type_Any; if (equal_types($1.type, $4.type)) $$.type = $1.type; else if ($1.type.typeflags == TYPE_STRING) $$.type = Type_String; else if (($1.type.typeflags == TYPE_NUMBER || $1.type.typeflags == TYPE_FLOAT) && $4.type.typeflags == TYPE_STRING) $$.type = Type_String; else if ($1.type.typeflags == TYPE_FLOAT && ($4.type.typeflags == TYPE_NUMBER || $4.type.typeflags == TYPE_ANY)) $$.type = Type_Float; else if (($1.type.typeflags == TYPE_NUMBER || $1.type.typeflags == TYPE_ANY) && $4.type.typeflags == TYPE_FLOAT) $$.type = Type_Float; #ifdef USE_STRUCTS else if (IS_TYPE_STRUCT($1.type) || IS_TYPE_STRUCT($4.type)) yyerrorf("Bad arguments to '+': %s" , get_two_types($1.type, $4.type) ); #endif /* USE_STRUCTS */ } $$.end = CURRENT_PROGRAM_SIZE; } /* '+' */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '-' expr0 { %line $$ = $1; $$.type = Type_Any; if (exact_types.typeflags) { fulltype_t type1 = $1.type; fulltype_t type2 = $3.type; if (equal_types(type1, type2)) { static char matchok[] = %typemap TYPE_ANY:1,TYPE_NUMBER:1,TYPE_FLOAT:1,TYPE_MAPPING:1,TYPE_STRING:1 if ( type1.typeflags & (TYPE_MOD_POINTER|TYPE_MOD_REFERENCE) ? (type1.typeflags & (TYPE_MOD_POINTER|TYPE_MOD_REFERENCE)) == TYPE_MOD_POINTER : matchok[type1.typeflags] ) { $$.type = type1; } else { type_error("Bad arguments to '-'", type1); } } else if ( (type1.typeflags & (TYPE_MOD_POINTER|TYPE_MOD_REFERENCE)) == TYPE_MOD_POINTER) { if ((type2.typeflags | TYPE_MOD_POINTER) == (TYPE_MOD_POINTER|TYPE_ANY) || ( type2.typeflags & TYPE_MOD_POINTER && type1.typeflags == (TYPE_MOD_POINTER|TYPE_ANY)) ) { $$.type = type1; } else { yyerror("Arguments to '-' don't match"); } } else switch (type1.typeflags) { case TYPE_ANY: switch (type2.typeflags) { case TYPE_NUMBER: /* number or float -> TYPE_ANY */ break; case TYPE_MAPPING: case TYPE_FLOAT: case TYPE_STRING: $$.type = type2; break; default: if ( (type2.typeflags & (TYPE_MOD_POINTER|TYPE_MOD_REFERENCE)) == TYPE_MOD_POINTER) { $$.type = Type_Ptr_Any; break; } else { type_error("Bad argument number 2 to '-'", type2); break; } } break; case TYPE_NUMBER: if (type2.typeflags == TYPE_FLOAT || type2.typeflags == TYPE_ANY) { $$.type = type2; } else { yyerror("Arguments to '-' don't match"); } break; case TYPE_FLOAT: if (type2.typeflags == TYPE_NUMBER || type2.typeflags == TYPE_ANY) { $$.type = Type_Float; } else { yyerror("Arguments to '-' don't match"); } break; case TYPE_STRING: if (type2.typeflags == TYPE_STRING || type2.typeflags == TYPE_ANY) { $$.type = Type_String; } else { yyerror("Arguments to '-' don't match"); } break; case TYPE_MAPPING: if (type2.typeflags == 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_f_code(F_SUBTRACT); $$.end = CURRENT_PROGRAM_SIZE; } /* '-' */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '*' expr0 { fulltype_t type1, type2; $$ = $1; type1 = $1.type; type2 = $3.type; if (exact_types.typeflags) { if (!BASIC_TYPE(type1, Type_Number) && type1.typeflags != TYPE_FLOAT && type1.typeflags != TYPE_STRING && !(type1.typeflags & TYPE_MOD_POINTER) ) type_error("Bad argument number 1 to '*'", type1); if (!BASIC_TYPE(type2, Type_Number) && type2.typeflags != TYPE_FLOAT && type2.typeflags != TYPE_STRING && !(type2.typeflags & TYPE_MOD_POINTER) ) type_error("Bad argument number 2 to '*'", type2); } ins_f_code(F_MULTIPLY); $$.end = CURRENT_PROGRAM_SIZE; if (type1.typeflags == TYPE_FLOAT || type2.typeflags == TYPE_FLOAT ) { $$.type = Type_Float; } else if (type1.typeflags == TYPE_STRING || type2.typeflags == TYPE_STRING) { $$.type = Type_String;; } else if (type1.typeflags & TYPE_MOD_POINTER) { $$.type = type1; } else if (type2.typeflags & TYPE_MOD_POINTER) { $$.type = type2; } else if (type1.typeflags == TYPE_ANY || type2.typeflags == TYPE_ANY) { $$.type = Type_Any; } else { $$.type = Type_Number; } } /* '*' */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '%' expr0 { if (exact_types.typeflags) { 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_f_code(F_MOD); $$.end = CURRENT_PROGRAM_SIZE; $$.type = Type_Number; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr0 '/' expr0 { fulltype_t type1, type2; $$ = $1; type1 = $1.type; type2 = $3.type; if (exact_types.typeflags) { if ( !BASIC_TYPE(type1, Type_Number) && type1.typeflags != TYPE_FLOAT) type_error("Bad argument number 1 to '/'", type1); if ( !BASIC_TYPE(type2, Type_Number) && type2.typeflags != TYPE_FLOAT) type_error("Bad argument number 2 to '/'", type2); } ins_f_code(F_DIVIDE); $$.end = CURRENT_PROGRAM_SIZE; if (type1.typeflags == TYPE_FLOAT || type2.typeflags == TYPE_FLOAT ) { $$.type = Type_Float; } else { $$.type = Type_Number; } } /* '/' */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | decl_cast expr0 %prec '~' { $$ = $2; $$.type = $1; if (exact_types.typeflags && $2.type.typeflags != TYPE_ANY && $2.type.typeflags != TYPE_UNKNOWN && $1.typeflags != 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.typeflags != TYPE_ANY && $2.type.typeflags != TYPE_UNKNOWN && $1.typeflags != TYPE_VOID && !equal_types($1, $2.type) ) { switch($1.typeflags) { default: #ifdef USE_STRUCTS if (IS_TYPE_STRUCT($1)) break; /* Do nothing, just adapt the type information */ #endif /* USE_STRUCTS */ 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; } } else if (pragma_warn_empty_casts) { if (equal_types($1, $2.type)) { if ($2.type.typeflags != TYPE_ANY) yywarnf("casting a value to its own type: %s" , get_type_name($1)); } else if ($2.type.typeflags != TYPE_UNKNOWN && $2.type.typeflags != TYPE_ANY) yywarnf("cast will not convert the value: %s" , get_two_types($1, $2.type) ); } $$.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. */ fulltype_t lvtype; int i; PREPARE_INSERT(4) %line $$.start = $1.start; i = verify_declared($2); if (i != -1) { if (i & VIRTUAL_VAR_TAG) { add_f_code(F_PUSH_VIRTUAL_VARIABLE_LVALUE); add_byte(i); lvtype = V_VARIABLE(i)->type; lvtype.typeflags &= TYPE_MOD_MASK; } else { if ((i + num_virtual_variables) & ~0xff) { add_f_code(F_PUSH_IDENTIFIER16_LVALUE); add_short(i + num_virtual_variables); CURRENT_PROGRAM_SIZE += 1; } else { add_f_code(F_PUSH_IDENTIFIER_LVALUE); add_byte(i + num_virtual_variables); } lvtype = NV_VARIABLE(i)->type; lvtype.typeflags &= TYPE_MOD_MASK; } if (exact_types.typeflags && !BASIC_TYPE(lvtype, Type_Number) && !BASIC_TYPE(lvtype, Type_Float)) { argument_type_error($1.code, lvtype); } CURRENT_PROGRAM_SIZE += 2; } else { /* Variable not declared - try to recover */ YYACCEPT; lvtype = Type_Any; } last_expression = CURRENT_PROGRAM_SIZE; CURRENT_PROGRAM_SIZE += 1; add_f_code($1.code); $$.end = CURRENT_PROGRAM_SIZE; $$.type = lvtype; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | pre_inc_dec L_LOCAL %prec L_INC { fulltype_t lvtype; PREPARE_INSERT(3) %line $$.start = $1.start; #ifdef USE_NEW_INLINES $2 = check_for_context_local($2, &lvtype); if ($2->u.local.context >= 0) { add_f_code(F_PUSH_CONTEXT_LVALUE); add_byte($2->u.local.context); } else { add_f_code(F_PUSH_LOCAL_VARIABLE_LVALUE); add_byte($2->u.local.num); } CURRENT_PROGRAM_SIZE = (last_expression = CURRENT_PROGRAM_SIZE + 2) + 1; #else /* USE_NEW_INLINES */ add_f_code(F_PUSH_LOCAL_VARIABLE_LVALUE); add_byte($2->u.local.num); CURRENT_PROGRAM_SIZE = (last_expression = CURRENT_PROGRAM_SIZE + 2) + 1; lvtype = type_of_locals[$2->u.local.num]; #endif /* USE_NEW_INLINES */ add_f_code($1.code); if (exact_types.typeflags && !BASIC_TYPE(lvtype, Type_Number) && !BASIC_TYPE(lvtype, Type_Float)) { argument_type_error($1.code, lvtype); } $$.type = lvtype; $$.end = CURRENT_PROGRAM_SIZE; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | pre_inc_dec expr4 index_expr %prec '[' { mp_uint current; bytecode_p p; int start; fulltype_t restype; %line $$.start = $1.start; if ($3.type1.typeflags & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); restype = Type_Any; /* Check the types */ if (exact_types.typeflags) { fulltype_t type; type = $2.type; type.typeflags &= TYPEID_MASK; if (type.typeflags & TYPE_MOD_POINTER) { if (type.typeflags != (TYPE_MOD_POINTER|TYPE_ANY) && type.typeflags != (TYPE_MOD_POINTER|TYPE_NUMBER) ) argument_type_error($1.code, type); } else switch (type.typeflags) { 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 %"PRIuMPINT"\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; if ($3.inst == F_INDEX) *p++ = F_INDEX_LVALUE; else if ($3.inst == F_RINDEX) *p++ = F_RINDEX_LVALUE; else *p++ = F_AINDEX_LVALUE; } else { int i; int length; if (!realloc_a_program(3)) { yyerrorf("Out of memory: program size %"PRIuMPINT"\n", current+3); YYACCEPT; } p = PROGRAM_BLOCK + start; i = p[1]; length = current - start - 2; for( ; --length >= 0; p++) *p = p[2]; *p++ = $2.code; *p++ = i; if ($3.inst == F_INDEX) *p++ = F_INDEX_LVALUE; else if ($3.inst == F_RINDEX) *p++ = F_RINDEX_LVALUE; else *p++ = F_AINDEX_LVALUE; } } else { if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %"PRIuMPINT"\n", current+2); YYACCEPT; } p = PROGRAM_BLOCK + start; if ($3.inst == F_INDEX) *p++ = F_PUSH_INDEXED_LVALUE; else if ($3.inst == F_RINDEX) *p++ = F_PUSH_RINDEXED_LVALUE; else *p++ = F_PUSH_AINDEXED_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 $$.start = $1.start; if ($4.type.typeflags & TYPE_MOD_REFERENCE || $6.type.typeflags & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); /* Check the types */ if (exact_types.typeflags) { fulltype_t type; type = $2.type; type.typeflags &= TYPEID_MASK; switch (type.typeflags) { 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 and add our PUSH_INDEXED_MAP_LVALUE */ current = CURRENT_PROGRAM_SIZE; if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %"PRIuMPINT"\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; $$.end = CURRENT_PROGRAM_SIZE; $$.type = Type_Any; } /* pre_inc_dec expr4 [expr0 ',' expr0] */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | L_NOT expr0 { $$ = $2; last_expression = CURRENT_PROGRAM_SIZE; ins_f_code(F_NOT); /* Any type is valid here. */ $$.end = CURRENT_PROGRAM_SIZE; $$.type = Type_Number; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '~' expr0 { %line $$ = $2; ins_f_code(F_COMPL); if (exact_types.typeflags && !BASIC_TYPE($2.type, Type_Number)) type_error("Bad argument to ~", $2.type); $$.end = CURRENT_PROGRAM_SIZE; $$.type = Type_Number; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '-' expr0 %prec '~' { fulltype_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 ) { p_int number; memcpy(&number, &(mem_block[A_PROGRAM].block[last_expression+1]) , sizeof(number)); number = -number; memcpy(&(mem_block[A_PROGRAM].block[last_expression+1]), &number , sizeof(number)); } else { ins_f_code(F_NEGATE); } $$.end = CURRENT_PROGRAM_SIZE; type = $2.type; if (exact_types.typeflags && !BASIC_TYPE(type, Type_Number) && type.typeflags != TYPE_FLOAT ) type_error("Bad argument to unary '-'", type); } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | lvalue L_INC %prec L_INC { %line /* Create the code to push the lvalue plus POST_INC */ $$.start = CURRENT_PROGRAM_SIZE; if (!add_lvalue_code(&$1, F_POST_INC)) YYACCEPT; $$.end = CURRENT_PROGRAM_SIZE; /* Check the types */ if (exact_types.typeflags && !BASIC_TYPE($1.type, Type_Number) && !BASIC_TYPE($1.type, Type_Float) ) type_error("Bad argument to ++", $1.type); $$.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 (!add_lvalue_code(&$1, F_POST_DEC)) YYACCEPT; /* Check the types */ if (exact_types.typeflags && !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 '~' %ifdef USE_NEW_INLINES | inline_func %prec '~' {} %else /* USE_NEW_INLINES */ | inline_fun %endif /* USE_NEW_INLINES */ | catch %prec '~' | sscanf %prec '~' %ifdef USE_PARSE_COMMAND | parse_command %prec '~' %endif /* USE_PARSE_COMMAND */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | L_STRING { /* Push a constant string */ int string_number; PREPARE_INSERT(3) string_t *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_f_code(F_CSTRING0); add_byte(string_number); } else if ( string_number <= 0x1ff ) { add_f_code(F_CSTRING1); add_byte(string_number); } else if ( string_number <= 0x2ff ) { add_f_code(F_CSTRING2); add_byte(string_number); } else if ( string_number <= 0x3ff ) { add_f_code(F_CSTRING3); add_byte(string_number); } else { add_f_code(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_f_code(F_CONST0); $$.type = Type_Any; /* TODO: Introduce a TYPE_NULL instead */ } else if ( number == 1 ) { add_f_code(F_CONST1); current++; $$.type = Type_Number; } else if ( number >= 0 && number <= 0xff ) { add_f_code(F_CLIT); add_byte(number); current += 2; $$.type = Type_Number; } else if ( number < 0 && number >= -0x0ff ) { add_f_code(F_NCLIT); add_byte(-number); current += 2; $$.type = Type_Number; } else { add_f_code(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, inhIndex; $$.start = CURRENT_PROGRAM_SIZE; $$.code = -1; if (!pragma_warn_deprecated) ins_byte(F_NO_WARN_DEPRECATED); ix = $1.number; inhIndex = $1.inhIndex; ins_f_code(F_CLOSURE); ins_short(ix); ins_short(inhIndex); $$.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_f_code(F_CSTRING0); ins_byte(string_number); ins_f_code(F_QUOTE); } else { ins_f_code(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_f_code(F_FLOAT); ins_int32 ( 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_f_code(F_AGGREGATE); ins_short($4); if (max_array_size && $4 > (p_int)max_array_size) yyerror("Illegal array size"); $$.type = Type_Ptr_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_f_code(F_AGGREGATE); ins_short($3); if (max_array_size && $3 > (p_int)max_array_size) yyerror("Illegal array size"); $$.type = Type_Quoted_Array; $$.start = $2.start; $$.code = -1; quotes = $1; do { ins_f_code(F_QUOTE); } while (--quotes); } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '(' '[' ':' note_start /* Generate an empty mapping of given width */ { ins_number(0); } 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_f_code(F_M_AGGREGATE); ins_short(num_keys); ins_short($4[1]); } else { ins_f_code(F_M_CAGGREGATE); ins_byte(num_keys); ins_byte($4[1]); } $$.type = Type_Mapping; $$.start = $3.start; $$.code = -1; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ %ifdef USE_STRUCTS | '(' '<' note_start '>' ')' { yyerror("Missing identifier for empty struct literal"); $$.type = Type_Unknown; $$.start = $3.start; $$.code = -1; } | '(' '<' note_start error ')' { /* Rule allows the parser to resynchronize after errors */ $$.type = Type_Unknown; $$.start = $3.start; $$.code = -1; } | '(' '<' identifier '>' { int num; num = find_struct($3); if (num < 0) { yyerrorf("Unknown struct '%s'", get_txt($3)); YYACCEPT; } $$ = num; free_mstring($3); } note_start opt_struct_init ')' { /* Generate a literal struct */ int num = $5; struct_def_t *pdef = &(STRUCT_DEF(num)); if ($7.length > STRUCT_MAX_MEMBERS || $7.length > struct_t_size(pdef->type)) { /* Too many elements - create an empty struct */ yyerrorf("Too many elements for literal struct '%s'" , get_txt(struct_t_name(pdef->type))); CURRENT_PROGRAM_SIZE = $6.start; create_struct_literal(pdef, 0, NULL); } else if (!create_struct_literal(pdef, $7.length, $7.list)) { /* Creation failed - create an empty struct */ CURRENT_PROGRAM_SIZE = $6.start; create_struct_literal(pdef, 0, NULL); } /* Free the list of member descriptors */ while ($7.list != NULL) { struct_init_t * p = $7.list; $7.list = p->next; if (p->name != NULL) free_mstring(p->name); xfree(p); } $$.type.typeflags = TYPE_STRUCT; $$.type.t_struct = pdef->type; $$.start = $6.start; $$.code = -1; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr4 L_ARROW struct_member_name { /* Lookup a struct member */ short s_index = -1; $$.start = $1.start; $$.code = -1; $$.type = $1.type; /* default */ if (!IS_TYPE_ANY($1.type) && !IS_TYPE_STRUCT($1.type)) { yyerrorf("Bad type for struct lookup: %s" , get_type_name($1.type)); } else { if (IS_TYPE_STRUCT($1.type)) { s_index = get_struct_index($1.type.t_struct); if (s_index == -1) yyerrorf("Unknown type in struct dereference: %s\n" , get_type_name($1.type) ); } /* At this point: s_index >= 0: $1 is of type struct * < 0: $1 is of type mixed */ if ($3 != NULL) { int num; struct_type_t * ptype = NULL; if (s_index >= 0) { ptype = $1.type.t_struct; num = struct_find_member(ptype, $3); if (num < 0) { yyerrorf("No such member '%s' for struct '%s'" , get_txt($3) , get_txt(struct_t_name(ptype)) ); } } else /* $1 is of type mixed */ { s_index = find_struct_by_member($3, &num); if (s_index >= 0) ptype = STRUCT_DEF(s_index).type; } /* If this is a legal struct lookup, num >= 0 at this point */ if (num >= 0) { ins_number(num); ins_number(s_index); ins_f_code(F_S_INDEX); if (ptype != NULL) assign_var_to_fulltype(&$$.type , ptype->member[num].type); } } else /* Runtime lookup */ { ins_number(s_index); ins_f_code(F_S_INDEX); $$.type = Type_Any; } $$.end = CURRENT_PROGRAM_SIZE-1; } if ($3 != NULL) free_mstring($3); } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '&' '(' expr4 L_ARROW struct_member_name ')' { /* Create a reference to a struct member */ short s_index = -1; $$.start = $3.start; $$.code = -1; $$.type = $3.type; /* default */ if (!IS_TYPE_ANY($3.type) && !IS_TYPE_STRUCT($3.type)) { yyerrorf("Bad type for struct lookup: %s" , get_type_name($3.type)); } else { /* '&(struct->member->member)' generates a simple * F_S_INDEX for the first lookup instead of a suitable * lvalue lookup. I don't understand the lvalue generation * well enough to correct the generated code, so for now * I restrict the lookup to one level. */ if ($3.end != 0 && F_S_INDEX == mem_block[A_PROGRAM].block[$3.end] ) { yyerror("Implementation restriction: Only a single struct " "member lookup allowed inside a &()"); } if (IS_TYPE_STRUCT($3.type)) { s_index = get_struct_index($3.type.t_struct); if (s_index == -1) yyerrorf("Unknown type in lvalue struct derefence: %s\n" , get_type_name($3.type) ); } /* At this point: s_index >= 0: $1 is of type struct * < 0: $1 is of type mixed */ if ($5 != NULL) { int num; struct_type_t * ptype = NULL; if (s_index >= 0) { ptype = $3.type.t_struct; num = struct_find_member(ptype, $5); if (num < 0) { yyerrorf("No such member '%s' for struct '%s'" , get_txt($5) , get_txt(struct_t_name(ptype)) ); } } else /* $3 is of type mixed */ { s_index = find_struct_by_member($5, &num); if (s_index >= 0) ptype = STRUCT_DEF(s_index).type; } /* If this is a legal struct lookup, num >= 0 at this point */ if (num >= 0) { ins_number(num); ins_number(s_index); arrange_protected_lvalue($3.start, $3.code, $3.end, F_PROTECTED_INDEX_S_LVALUE ); if (ptype != NULL) { assign_var_to_fulltype(&$$.type , ptype->member[num].type); $$.type.typeflags |= TYPE_MOD_REFERENCE; } } } else /* Runtime lookup */ { ins_number(s_index); arrange_protected_lvalue($3.start, $3.code, $3.end, F_PROTECTED_INDEX_S_LVALUE ); $$.type = Type_Ref_Any; } $$.end = CURRENT_PROGRAM_SIZE-1; } if ($5 != NULL) free_mstring($5); } %endif /* USE_STRUCTS */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | 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.typeflags) { fulltype_t type; $1.type.typeflags &= TYPEID_MASK; $$.type = type = $1.type; if ((type.typeflags & TYPE_MOD_POINTER) == 0 && type.typeflags != TYPE_ANY && type.typeflags != TYPE_STRING) { type_error("Bad type of argument used for range", type); $$.type = Type_Any; } type = $2.type1; if (type.typeflags != TYPE_ANY && type.typeflags != TYPE_NUMBER) type_error("Bad type of index", type); type = $2.type2; if (type.typeflags != TYPE_ANY && type.typeflags != 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 %"PRIuMPINT"\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_Ref_Any; else { $$.type = VARIABLE(i)->type; $$.type.typeflags = ($$.type.typeflags & 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 #ifdef USE_NEW_INLINES $2 = check_for_context_local($2, &$$.type); #endif /* USE_NEW_INLINES */ $$.start = current = CURRENT_PROGRAM_SIZE; $$.code = -1; if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %"PRIuMPINT"\n", current+2); YYACCEPT; } p = PROGRAM_BLOCK + current; #ifdef USE_NEW_INLINES if ($2->u.local.context >= 0) { *p++ = F_PUSH_CONTEXT_LVALUE; *p = $2->u.local.context; } else { *p++ = F_PUSH_LOCAL_VARIABLE_LVALUE; *p = $2->u.local.num; } $$.type.typeflags |= TYPE_MOD_REFERENCE; CURRENT_PROGRAM_SIZE = current + 2; #else /* USE_NEW_INLINES */ *p++ = F_PUSH_LOCAL_VARIABLE_LVALUE; *p = $2->u.local.num; CURRENT_PROGRAM_SIZE = current + 2; $$.type = type_of_locals[$2->u.local.num]; $$.type.typeflags |= TYPE_MOD_REFERENCE; #endif /* USE_NEW_INLINES */ } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '&' '(' 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 if ($4.inst == F_RINDEX) arrange_protected_lvalue($3.start, $3.code, $3.end, F_PROTECTED_RINDEX_LVALUE ); else arrange_protected_lvalue($3.start, $3.code, $3.end, F_PROTECTED_AINDEX_LVALUE ); $$.start = $3.start; $$.code = -1; if ($4.type1.typeflags & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); /* Compute the result type */ if (!exact_types.typeflags) { $$.type = Type_Ref_Any; } else { fulltype_t type; type = $3.type; type.typeflags &= TYPEID_MASK; if (type.typeflags & TYPE_MOD_POINTER) { $$.type = type; $$.type.typeflags &= ~TYPE_MOD_POINTER; } else if (type.typeflags == TYPE_MAPPING && $4.inst == F_INDEX) { $4.type1 = Type_Any; $$.type = Type_Ref_Any; } else switch (type.typeflags) { default: type_error("Bad type to indexed reference", type); /* FALLTHROUGH */ case TYPE_ANY: if ($4.inst == F_INDEX) $4.type1 = Type_Any; $$.type = Type_Ref_Any; break; case TYPE_STRING: $$.type = Type_Ref_Number; 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_Ref_Any; ins_f_code(F_PUSH_PROTECTED_INDEXED_MAP_LVALUE); if ($5.type.typeflags & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); /* Compute the result type */ if (exact_types.typeflags) { fulltype_t type; type = $3.type; type.typeflags &= TYPEID_MASK; if (type.typeflags != TYPE_ANY && type.typeflags != TYPE_MAPPING) { type_error("Bad type to indexed value", type); } type = $7.type; if (type.typeflags != TYPE_ANY && type.typeflags != 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_NA_RANGE: prot_op = F_PROTECTED_NA_RANGE_LVALUE; break; case F_AN_RANGE: prot_op = F_PROTECTED_AN_RANGE_LVALUE; break; case F_RA_RANGE: prot_op = F_PROTECTED_RA_RANGE_LVALUE; break; case F_AR_RANGE: prot_op = F_PROTECTED_AR_RANGE_LVALUE; break; case F_AA_RANGE: prot_op = F_PROTECTED_AA_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; case F_AX_RANGE: prot_op = F_PROTECTED_AX_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.typeflags) { $$.type = Type_Ref_Any; } else { fulltype_t type; $3.type.typeflags &= TYPEID_MASK; $$.type = type = $3.type; if ((type.typeflags & TYPE_MOD_POINTER) == 0 && type.typeflags != TYPE_ANY && type.typeflags != TYPE_STRING) { type_error("Bad type of argument used for range", type); $$.type = Type_Any; } type = $4.type1; if (type.typeflags != TYPE_ANY && type.typeflags != TYPE_NUMBER) type_error("Bad type of index", type); type = $4.type2; if (type.typeflags != TYPE_ANY && type.typeflags != 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 %"PRIuMPINT"\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)->type; $$.type.typeflags &= 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)->type; $$.type.typeflags &= 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 #ifdef USE_NEW_INLINES $1 = check_for_context_local($1, &$$.type); #endif /* USE_NEW_INLINES */ $$.start = current = CURRENT_PROGRAM_SIZE; $$.end = 0; if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %"PRIuMPINT"\n", current+2); YYACCEPT; } p = PROGRAM_BLOCK + current; #ifdef USE_NEW_INLINES if ($1->u.local.context >= 0) { $$.code = F_PUSH_CONTEXT_LVALUE; *p++ = F_CONTEXT_IDENTIFIER; *p = $1->u.local.context; } else { $$.code = F_PUSH_LOCAL_VARIABLE_LVALUE; *p++ = F_LOCAL; *p = $1->u.local.num; } CURRENT_PROGRAM_SIZE = current + 2; #else /* USE_NEW_INLINES */ *p++ = F_LOCAL; *p = $1->u.local.num; CURRENT_PROGRAM_SIZE = current + 2; $$.type = type_of_locals[$1->u.local.num]; #endif /* USE_NEW_INLINES */ } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | 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_f_code(F_INDEX); } else if ($2.inst == F_RINDEX) { $$.code = F_PUSH_RINDEXED_LVALUE; ins_f_code(F_RINDEX); } else { $$.code = F_PUSH_AINDEXED_LVALUE; ins_f_code(F_AINDEX); } if ($2.type1.typeflags & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); /* Check and compute the types */ if (exact_types.typeflags) { fulltype_t type; type = $1.type; type.typeflags &= TYPEID_MASK; if (type.typeflags & TYPE_MOD_POINTER) { $$.type = type; $$.type.typeflags &= ~TYPE_MOD_POINTER; } else if (type.typeflags == TYPE_MAPPING && $2.inst == F_INDEX) { $2.type1 = Type_Any; $$.type = Type_Any; } else switch (type.typeflags) { 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_f_code(F_MAP_INDEX); if ($3.type.typeflags & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); /* Check and compute types */ if (exact_types.typeflags) { fulltype_t type; type = $1.type; type.typeflags &= TYPEID_MASK; if (type.typeflags != TYPE_ANY && type.typeflags != TYPE_MAPPING) { type_error("Bad type to indexed value", type); } type = $5.type; type.typeflags &= TYPEID_MASK; if (type.typeflags != TYPE_ANY && type.typeflags != 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); if ($2.inst == F_INDEX) q[current - start] = F_INDEX_LVALUE; else if ($2.inst == F_RINDEX) q[current - start] = F_RINDEX_LVALUE; else q[current - start] = F_AINDEX_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; if ($2.inst == F_INDEX) *p = F_INDEX_LVALUE; else if ($2.inst == F_RINDEX) *p = F_RINDEX_LVALUE; else *p = F_AINDEX_LVALUE; } } else { /* We can just copy the instruction block * and add a PUSH_(R)INDEXED_LVALUE */ memcpy(q, p + start, current - start); if ($2.inst == F_INDEX) q[current - start] = F_PUSH_INDEXED_LVALUE; else if ($2.inst == F_RINDEX) q[current - start] = F_PUSH_RINDEXED_LVALUE; else q[current - start] = F_PUSH_AINDEXED_LVALUE; } /* This is what we return */ $$.length = current + 1 - start; $$.u.p = q; CURRENT_PROGRAM_SIZE = start; last_expression = -1; if ($2.type1.typeflags & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); /* Check and compute types */ if (exact_types.typeflags) { fulltype_t type; type = $1.type; type.typeflags &= TYPEID_MASK; if (type.typeflags & TYPE_MOD_POINTER) { $$.type = type; $$.type.typeflags &= ~TYPE_MOD_POINTER; } else if (type.typeflags == TYPE_MAPPING && $2.inst == F_INDEX) { $2.type1 = Type_Any; $$.type = Type_Any; } else switch (type.typeflags) { 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.typeflags & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); /* Check and compute types */ if (exact_types.typeflags) { fulltype_t type; type = $1.type; type.typeflags &= TYPEID_MASK; if (type.typeflags != TYPE_ANY && type.typeflags != TYPE_MAPPING) { type_error("Bad type to indexed value", type); } type = $5.type; type.typeflags &= TYPEID_MASK; if (type.typeflags != TYPE_ANY && type.typeflags != 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_NA_RANGE: indexing_code = F_NA_RANGE_LVALUE; break; case F_AN_RANGE: indexing_code = F_AN_RANGE_LVALUE; break; case F_RA_RANGE: indexing_code = F_RA_RANGE_LVALUE; break; case F_AR_RANGE: indexing_code = F_AR_RANGE_LVALUE; break; case F_AA_RANGE: indexing_code = F_AA_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; case F_AX_RANGE: indexing_code = F_AX_RANGE_LVALUE; break; default: errorf("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 (instrs[indexing_code].prefix) { q[current++] = instrs[indexing_code].prefix; } q[current] = instrs[indexing_code].opcode; } /* 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.typeflags) { fulltype_t type; $1.type.typeflags &= TYPEID_MASK; $$.type = type = $1.type; if ((type.typeflags & TYPE_MOD_POINTER) == 0 && type.typeflags != TYPE_ANY && type.typeflags != TYPE_STRING) { type_error("Bad type of argument used for range", type); $$.type = Type_Any; } type = $2.type1; if (type.typeflags != TYPE_ANY && type.typeflags != TYPE_NUMBER) type_error("Bad type of index", type); type = $2.type2; if (type.typeflags != TYPE_ANY && type.typeflags != TYPE_NUMBER) type_error("Bad type of index", type); } } %ifdef USE_STRUCTS /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr4 L_ARROW struct_member_name { /* Create a struct member lvalue */ short s_index = -1; int num; vartype_t member_type; /* If the struct lookup is ok, set num and member_type */ num = 0; member_type = VType_Unknown; if (!IS_TYPE_ANY($1.type) && !IS_TYPE_STRUCT($1.type)) { yyerrorf("Bad type for struct lookup: %s" , get_type_name($1.type)); } else { if (IS_TYPE_STRUCT($1.type)) { s_index = get_struct_index($1.type.t_struct); if (s_index == -1) yyerrorf("Unknown type in lvalue struct dereference: %s\n" , get_type_name($1.type) ); } /* At this point: s_index >= 0: $1 is of type struct * < 0: $1 is of type mixed */ if ($3 != NULL) { struct_type_t * ptype = NULL; if (s_index >= 0) { ptype = $1.type.t_struct; num = struct_find_member(ptype, $3); if (num < 0) { yyerrorf("No such member '%s' for struct '%s'" , get_txt($3) , get_txt(struct_t_name(ptype)) ); } else member_type = ptype->member[num].type; } else /* $1 is of type mixed */ { s_index = find_struct_by_member($3, &num); if (s_index >= 0) { ptype = STRUCT_DEF(s_index).type; member_type = ptype->member[num].type; } } } else /* Runtime lookup */ { assign_full_to_vartype(&member_type, Type_Any); } } /* We have to generate some code, so if the struct lookup is * invalid, we just play along and generate code to look up * member #0 in whatever we got. */ { bytecode_p p, q; p_int start, current; if ($3 != NULL) { /* Insert the index code */ ins_number(num); } /* Insert the struct type index */ ins_number(s_index); /* Generate/add an INDEX_S_LVALUE */ 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] = F_INDEX_S_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 = F_INDEX_S_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] = F_PUSH_INDEXED_S_LVALUE; } /* This is what we return */ $$.length = current + 1 - start; $$.u.p = q; CURRENT_PROGRAM_SIZE = start; last_expression = -1; assign_var_to_fulltype(&$$.type, member_type); } if ($3 != NULL) free_mstring($3); } %endif /* USE_STRUCTS */ ; /* 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)->type; $$.type.typeflags &= 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)->type; $$.type.typeflags &= TYPE_MOD_MASK; } else { $$.u.simple[0] = F_PUSH_IDENTIFIER_LVALUE; $$.u.simple[1] = i + num_virtual_variables; } $$.type = NV_VARIABLE(i)->type; $$.type.typeflags &= TYPE_MOD_MASK; } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | L_LOCAL { %line /* Generate the lvalue for a local */ #ifdef USE_NEW_INLINES $1 = check_for_context_local($1, &$$.type); if ($1->u.local.context >= 0) { $$.u.simple[0] = F_PUSH_CONTEXT_LVALUE; $$.u.simple[1] = $1->u.local.context; } else { $$.u.simple[0] = F_PUSH_LOCAL_VARIABLE_LVALUE; $$.u.simple[1] = $1->u.local.num; } $$.length = 0; #else /* USE_NEW_INLINES */ $$.u.simple[0] = F_PUSH_LOCAL_VARIABLE_LVALUE; $$.u.simple[1] = $1->u.local.num; $$.length = 0; $$.type = type_of_locals[$1->u.local.num]; #endif /* USE_NEW_INLINES */ } ; /* name_lvalue */ local_name_lvalue: basic_type optional_star L_IDENTIFIER { define_local_variable($3, $1, $2, &$$, MY_FALSE, MY_TRUE); } | basic_type optional_star L_LOCAL { define_local_variable($3, $1, $2, &$$, MY_TRUE, MY_TRUE); } ; /* 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; if (!pragma_warn_deprecated) { ins_byte(F_NO_WARN_DEPRECATED); $$.end++; } } | '[' '<' expr0 ']' { $$.inst = F_RINDEX; $$.start = $3.start; $$.end = $3.end; $$.type1 = $3.type; if (!pragma_warn_deprecated) { ins_byte(F_NO_WARN_DEPRECATED); $$.end++; } } | '[' '>' expr0 ']' { $$.inst = F_AINDEX; $$.start = $3.start; $$.end = $3.end; $$.type1 = $3.type; if (!pragma_warn_deprecated) { ins_byte(F_NO_WARN_DEPRECATED); $$.end++; } } ; /* 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 %"PRIdPINT"\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++; if (pragma_range_check) { ins_byte(F_ARRAY_RANGE_CHECK); $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 %"PRIdPINT"\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++; if (pragma_range_check) { ins_byte(F_ARRAY_RANGE_CHECK); $4.end++; } /* Return the data */ $$.inst = F_NR_RANGE; $$.start = $4.start; $$.end = $4.end; $$.type1 = Type_Number; $$.type2 = $4.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 %"PRIdPINT"\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++; if (pragma_range_check) { ins_byte(F_ARRAY_RANGE_CHECK); $4.end++; } /* Return the data */ $$.inst = F_NA_RANGE; $$.start = $4.start; $$.end = $4.end; $$.type1 = Type_Number; $$.type2 = $4.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' expr0 L_RANGE expr0 ']' { if (pragma_range_check) { ins_byte(F_ARRAY_RANGE_CHECK); $4.end++; } $$.inst = F_RANGE; $$.start = $2.start; $$.end = $4.end; $$.type1 = $2.type; $$.type2 = $4.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' expr0 L_RANGE '<' expr0 ']' { if (pragma_range_check) { ins_byte(F_ARRAY_RANGE_CHECK); $5.end++; } $$.inst = F_NR_RANGE; $$.start = $2.start; $$.end = $5.end; $$.type1 = $2.type; $$.type2 = $5.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' '<' expr0 L_RANGE expr0 ']' { if (pragma_range_check) { ins_byte(F_ARRAY_RANGE_CHECK); $5.end++; } $$.inst = F_RN_RANGE; $$.start = $3.start; $$.end = $5.end; $$.type1 = $3.type; $$.type2 = $5.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' '<' expr0 L_RANGE '<' expr0 ']' { if (pragma_range_check) { ins_byte(F_ARRAY_RANGE_CHECK); $6.end++; } $$.inst = F_RR_RANGE; $$.start = $3.start; $$.end = $6.end; $$.type1 = $3.type; $$.type2 = $6.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' expr0 L_RANGE '>' expr0 ']' { if (pragma_range_check) { ins_byte(F_ARRAY_RANGE_CHECK); $5.end++; } $$.inst = F_NA_RANGE; $$.start = $2.start; $$.end = $5.end; $$.type1 = $2.type; $$.type2 = $5.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' '>' expr0 L_RANGE expr0 ']' { if (pragma_range_check) { ins_byte(F_ARRAY_RANGE_CHECK); $5.end++; } $$.inst = F_AN_RANGE; $$.start = $3.start; $$.end = $5.end; $$.type1 = $3.type; $$.type2 = $5.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' '<' expr0 L_RANGE '>' expr0 ']' { if (pragma_range_check) { ins_byte(F_ARRAY_RANGE_CHECK); $6.end++; } $$.inst = F_RA_RANGE; $$.start = $3.start; $$.end = $6.end; $$.type1 = $3.type; $$.type2 = $6.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' '>' expr0 L_RANGE '<' expr0 ']' { if (pragma_range_check) { ins_byte(F_ARRAY_RANGE_CHECK); $6.end++; } $$.inst = F_AR_RANGE; $$.start = $3.start; $$.end = $6.end; $$.type1 = $3.type; $$.type2 = $6.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' '>' expr0 L_RANGE '>' expr0 ']' { if (pragma_range_check) { ins_byte(F_ARRAY_RANGE_CHECK); $6.end++; } $$.inst = F_AA_RANGE; $$.start = $3.start; $$.end = $6.end; $$.type1 = $3.type; $$.type2 = $6.type; } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | '[' expr0 L_RANGE ']' { if (pragma_range_check) { ins_byte(F_ARRAY_RANGE_CHECK); $2.end++; } $$.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. */ if (pragma_range_check) { ins_byte(F_ARRAY_RANGE_CHECK); $3.end++; } $$.inst = F_RX_RANGE; $$.start = $3.start; $$.end = $3.end; $$.type1 = $3.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. */ if (pragma_range_check) { ins_byte(F_ARRAY_RANGE_CHECK); $3.end++; } $$.inst = F_AX_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 */ e_expr_list2: expr0 { $$ = 1; if (!got_ellipsis[argument_level]) add_arg_type($1.type); else add_arg_type(Type_Any); } | expr0 L_ELLIPSIS { PREPARE_INSERT(2); $$ = 0; got_ellipsis[argument_level] = MY_TRUE; add_f_code(F_FLATTEN_XARG); CURRENT_PROGRAM_SIZE++; } | e_expr_list2 ',' expr0 { $$ = $1 + 1; if (!got_ellipsis[argument_level]) add_arg_type($3.type); else add_arg_type(Type_Any); } | e_expr_list2 ',' expr0 L_ELLIPSIS { PREPARE_INSERT(2); $$ = $1; got_ellipsis[argument_level] = MY_TRUE; add_f_code(F_FLATTEN_XARG); CURRENT_PROGRAM_SIZE++; } ; /* e_expr_list2 */ expr_list3: /* empty */ { $$ = 0; } | expr0 { $$ = 1; if (!got_ellipsis[argument_level]) add_arg_type($1.type); else add_arg_type(Type_Any); } | expr0 L_ELLIPSIS { PREPARE_INSERT(2); $$ = 0; got_ellipsis[argument_level] = MY_TRUE; add_f_code(F_FLATTEN_XARG); CURRENT_PROGRAM_SIZE++; } | e_expr_list2 ',' expr0 { $$ = $1 + 1; if (!got_ellipsis[argument_level]) add_arg_type($3.type); else add_arg_type(Type_Any); } | e_expr_list2 ',' expr0 L_ELLIPSIS { PREPARE_INSERT(2); $$ = $1; got_ellipsis[argument_level] = MY_TRUE; add_f_code(F_FLATTEN_XARG); CURRENT_PROGRAM_SIZE++; } ; /* 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 */ %ifdef USE_STRUCTS /* Rule used to parse a static or dynamic member name in lookups */ struct_member_name: identifier { $$ = $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.typeflags != TYPE_STRING && (pragma_strict_types != PRAGMA_WEAK_TYPES || $2.type.typeflags != TYPE_UNKNOWN) && $2.type.typeflags != TYPE_ANY && $2.type.typeflags != TYPE_NUMBER ) type_error("Illegal type for struct member name", $2.type); } ; /* struct_member_name */ /* The following rules are used to parse struct literals in expressions */ opt_struct_init: /* empty */ { $$.length = 0; $$.list = $$.last = NULL; } | opt_struct_init2 possible_comma { $$ = $1; } | opt_struct_init2 ',' error { /* Allow the parser to resynchronize */ $$.length = 0; $$.list = $$.last = NULL; } ; /* opt_struct_init */ possible_comma : /* empty */ | ',' ; /* possible_comma */ opt_struct_init2: /* empty */ { /* The end of a struct_init (or a list with just one * element) - this is the first rule reduced. */ $$.list = NULL; $$.last = NULL; $$.length = 0; } struct_init { struct_init_t * p; p = xalloc(sizeof(*p)); p->next = NULL; p->name = $2.name; p->type = $2.type; $$.length = 1; $$.list = p; $$.last = p; } | opt_struct_init2 ',' struct_init { struct_init_t * p; p = xalloc(sizeof(*p)); p->next = NULL; p->name = $3.name; p->type = $3.type; $$.length = $1.length + 1; $$.list = $1.list; $1.last->next = p; $$.last = p; } ; /* opt_struct_init2 */ struct_init: identifier ':' expr0 { $$.name = $1; assign_full_to_vartype(&$$.type, $3.type); } | expr0 { $$.name = NULL; assign_full_to_vartype(&$$.type, $1.type); } ; /* struct_init */ %endif /* USE_STRUCTS */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* 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 */ $$.start = CURRENT_PROGRAM_SIZE; $$.simul_efun = -1; /* Insert the save_arg_frame instruction. * If it's not really needed, we'll remove it later. */ { PREPARE_INSERT(2) add_f_code(F_SAVE_ARG_FRAME); CURRENT_PROGRAM_SIZE++; } if (argument_level+1 == sizeof(got_ellipsis)/sizeof(got_ellipsis[0])) { yyerror("Functions nested too deeply."); YYACCEPT; } argument_level++; got_ellipsis[argument_level] = MY_FALSE; 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 */ init_global_identifier(real_name, /* bVariable: */ MY_TRUE); 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 && !disable_sefuns) { /* It's a real simul-efun */ $$.simul_efun = real_name->u.global.sim_efun; /* real_name->u.global.sim_efun is >=0 (see above), so it can * be casted to unsigned long before comparison (SEFUN_TABLE_SIZE * is unsigned long) */ if ((unsigned long)real_name->u.global.sim_efun >= SEFUN_TABLE_SIZE) { /* The simul-efun has to be called by name: * prepare the extra args for the call_other */ PREPARE_INSERT(8) string_t *p; p = ref_mstring(real_name->name); add_f_code(F_STRING); add_short(store_prog_string( ref_mstring(query_simul_efun_file_name()))); add_f_code(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 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[] */ Bool ap_needed; /* TRUE if arg frame is needed */ Bool has_ellipsis; /* TRUE if '...' was used */ has_ellipsis = got_ellipsis[argument_level]; ap_needed = MY_FALSE; efun_override = ($1.super && strcmp($1.super, get_txt(STR_EFUN)) == 0); $$.start = $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"); do { /* The function processing is in a big do...while(0) * block so we can exit out of it prematurely and * still get the required arg-frame handling * afterwards */ if ( !disable_sefuns && (simul_efun = $2.simul_efun) >= 0) { /* SIMUL EFUN */ PREPARE_INSERT(6) 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" , get_txt(funp->name)); if ($4 < funp->num_arg && !has_ellipsis) { if (pragma_pedantic) yyerrorf("Missing arguments to simul_efun %s" , get_txt(funp->name)); else { yywarnf("Missing arguments to simul_efun %s" , get_txt(funp->name)); ap_needed = MY_TRUE; } } } if (funp->num_arg == SIMUL_EFUN_VARARGS || (funp->flags & TYPE_MOD_XVARARGS) || has_ellipsis) ap_needed = MY_TRUE; /* simul_efun is >= 0, see above) */ if ((unsigned long)simul_efun >= SEFUN_TABLE_SIZE) { /* call-other: the number of arguments will be * corrected at runtime. */ add_f_code(F_CALL_DIRECT); CURRENT_PROGRAM_SIZE++; ap_needed = MY_TRUE; } else { /* Direct call */ if (ap_needed) { add_f_code(F_USE_ARG_FRAME); CURRENT_PROGRAM_SIZE++; } add_f_code(F_SIMUL_EFUN); add_short(simul_efun); CURRENT_PROGRAM_SIZE += 3; } $$.type = funp->type; $$.type.typeflags &= TYPE_MOD_MASK; } /* if (simul-efun) */ else if ($1.super ? !efun_override : (f = defined_function($1.real)) >= 0 ) { /* LFUN or INHERITED LFUN */ PREPARE_INSERT(6) function_t *funp; function_t inherited_function; ap_needed = MY_TRUE; 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 (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: ap_needed = MY_FALSE; /* 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; break; /* Out of do..while(0) */ } /* 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 */ ap_needed = MY_TRUE; add_f_code(F_CALL_FUNCTION); add_short(f); funp = FUNCTION(f); arg_types = (vartype_t *)mem_block[A_ARGUMENT_TYPES].block; first_arg = ARGUMENT_INDEX(f); CURRENT_PROGRAM_SIZE += 3; } /* 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.typeflags ) { yyerrorf("Function %.50s undefined", get_txt(funp->name)); } else if ((funp->flags & (NAME_UNDEFINED|NAME_PROTOTYPE|NAME_HIDDEN)) == NAME_HIDDEN) { yyerrorf("Function %.50s is private", get_txt(funp->name)); } } $$.type = funp->type; /* Result type */ $$.type.typeflags &= TYPE_MOD_MASK; /* Check number of arguments. */ if (funp->num_arg != $4 && !(funp->flags & TYPE_MOD_VARARGS) && (first_arg != INDEX_START_NONE) && exact_types.typeflags && !has_ellipsis) { if (funp->num_arg-1 > $4 || !(funp->flags & TYPE_MOD_XVARARGS)) yyerrorf("Wrong number of arguments to %.60s: " "expected %ld, got %ld" , get_txt($1.real->name) , (long)funp->num_arg, (long)$4); } /* Check the argument types. */ if (exact_types.typeflags && 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; assign_var_to_fulltype(&tmp1, *argp); tmp1.typeflags &= TYPE_MOD_RMASK; assign_var_to_fulltype(&tmp2, *arg_types); tmp2.typeflags &= TYPE_MOD_MASK; argp++; arg_types++; if (!REDEFINED_TYPE(tmp1, tmp2)) { yyerrorf("Bad type for argument %d of %s %s", argno, get_txt(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 */ assign_var_to_fulltype(&tmp2, *arg_types); tmp2.typeflags &= TYPE_MOD_MASK; tmp2.typeflags &= ~TYPE_MOD_POINTER; for (i = anum_arg - num_arg; --i >=0; ) { assign_var_to_fulltype(&tmp1, *argp); tmp1.typeflags &= TYPE_MOD_RMASK; argp++; if (!MASKED_TYPE(tmp1,tmp2)) { yyerrorf("Bad type for argument %d of %s %s", anum_arg - i, get_txt(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 */ PREPARE_INSERT(8) 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 && !has_ellipsis) { /* Default argument */ add_f_code(def); CURRENT_PROGRAM_SIZE++; max--; min--; } else if (num_arg < min && !has_ellipsis && ( (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.typeflags && 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; fulltype_t *beginArgp = argp; assign_var_to_fulltype(&tmp1, *aargp); aargp++; tmp1.typeflags &= TYPE_MOD_MASK; for (;;) { tmp2 = *argp; argp++; if ( !tmp2.typeflags ) { /* Possible types for this arg exhausted */ efun_argument_error(argn+1, f, beginArgp , tmp1); break; } /* break if types are compatible; take care to * handle references correctly */ if (equal_types(tmp1, tmp2) #ifdef USE_STRUCTS || (IS_TYPE_STRUCT(tmp1) && IS_TYPE_STRUCT(tmp2)) #endif ) break; if ((tmp1.typeflags & ~(TYPE_MOD_POINTER|TYPE_MOD_REFERENCE)) == TYPE_ANY) { if (tmp1.typeflags & TYPE_MOD_POINTER & ~tmp2.typeflags) { if ((tmp2.typeflags & ~TYPE_MOD_REFERENCE) != TYPE_ANY) { continue; } } if ( !( (tmp1.typeflags ^ tmp2.typeflags) & TYPE_MOD_REFERENCE) ) break; } else if ((tmp2.typeflags & ~(TYPE_MOD_POINTER|TYPE_MOD_REFERENCE)) == TYPE_ANY) { if (tmp2.typeflags & TYPE_MOD_POINTER & ~tmp1.typeflags) continue; if ( !( (tmp1.typeflags ^ tmp2.typeflags) & TYPE_MOD_REFERENCE) ) break; } } /* end for (efun_arg_types) */ /* Advance argp to point to the allowed argtypes * of the next arg. */ while((argp++)->typeflags) NOOP; } /* for (all args) */ } /* if (check arguments) */ /* If the function takes a variable number of arguments * the ap is needed and evaluated automatically. * If the function takes a fixed number of arguments, but * the ellipsis has been used, the ap is needed but not * evaluated automatically. */ if (max != min) { ap_needed = MY_TRUE; } else if (has_ellipsis) { ap_needed = MY_TRUE; add_byte(F_USE_ARG_FRAME); CURRENT_PROGRAM_SIZE++; } /* Alias for an efun? */ if (f > LAST_INSTRUCTION_CODE) f = efun_aliases[f-LAST_INSTRUCTION_CODE-1]; if (instrs[f].prefix) { /* This efun needs a prefix byte */ add_byte(instrs[f].prefix); CURRENT_PROGRAM_SIZE++; } add_byte(instrs[f].opcode); CURRENT_PROGRAM_SIZE++; /* If the efun doesn't return a value, fake a 0. * This is especially important is ap_needed, as the * restore_arg_frame expects a result on the stack. */ if ( instrs[f].ret_type.typeflags == TYPE_VOID ) { last_expression = mem_block[A_PROGRAM].current_size; add_f_code(F_CONST0); CURRENT_PROGRAM_SIZE++; } } /* efun */ else if (efun_override) { yyerrorf("Unknown efun: %s", get_txt($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. */ PREPARE_INSERT(4) function_t *funp; f = define_new_function(MY_FALSE, $1.real, 0, 0, 0, NAME_UNDEFINED, Type_Unknown ); ap_needed = MY_TRUE; add_f_code(F_CALL_FUNCTION); add_short(f); CURRENT_PROGRAM_SIZE += 3; funp = FUNCTION(f); if (exact_types.typeflags) { yyerrorf("Undefined function '%.50s'", get_txt($1.real->name)); } else if (pragma_pedantic) { yywarnf("Undefined function '%.50s'", get_txt($1.real->name)); } $$.type = Type_Any; /* Just a guess */ } } while (0); /* Function handling */ /* Do the post processing of the arg frame handling */ if (ap_needed) { /* Restore the previous arg frame pointer */ PREPARE_INSERT(2) add_f_code(F_RESTORE_ARG_FRAME); CURRENT_PROGRAM_SIZE++; } else if (!ap_needed) { /* Since the arg frame is not needed, remove the * earlier save_arg_frame instruction. */ bytecode_p src, dest; size_t left; dest = PROGRAM_BLOCK + $2.start; src = dest+1; left = CURRENT_PROGRAM_SIZE - $2.start - 1; while (left-- > 0) { *dest++ = *src++; } CURRENT_PROGRAM_SIZE--; last_expression--; } argument_level--; if ($1.super) yfree($1.super); pop_arg_stack($4); /* Argument types no longer needed */ } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | expr4 L_ARROW call_other_name %prec L_ARROW { %line int string_number; string_t *name; /* Save the (simple) state */ $$.start = CURRENT_PROGRAM_SIZE; /* Insert the save_arg_frame instruction. * If it's not really needed, we'll remove it later. * Putting this code block before the in the rule * however yields a faulty grammar. */ { char *p, *q; p_int left; if (!realloc_a_program(1)) { yyerrorf("Out of memory: program size %"PRIuMPINT"\n" , mem_block[A_PROGRAM].current_size + 2); YYACCEPT; } /* Move the generated code forward by 1 */ p = mem_block[A_PROGRAM].block + CURRENT_PROGRAM_SIZE - 1; q = p + 1; for (left = CURRENT_PROGRAM_SIZE - $1.start ; left > 0 ; left--, p--, q--) *q = *p; /* p now points to program[$1.start]-1. * Store the instruction there. */ p[1] = F_SAVE_ARG_FRAME; CURRENT_PROGRAM_SIZE += 1; } if (argument_level+1 == sizeof(got_ellipsis)/sizeof(got_ellipsis[0])) { yyerror("Functions nested too deeply."); YYACCEPT; } argument_level++; got_ellipsis[argument_level] = MY_FALSE; /* If call_other() has been replaced by a sefun, and * if we need to use F_CALL_DIRECT to call it, we have * to insert additional code before the already parsed. * Putting this code block before the in the rule * however yields a faulty grammar. */ if (!disable_sefuns && call_other_sefun >= 0 && (unsigned long)call_other_sefun >= SEFUN_TABLE_SIZE) { /* The simul-efun has to be called by name: * insert the extra args for the call_other */ char *p, *q; p_int left; if (!realloc_a_program(6)) { yyerrorf("Out of memory: program size %"PRIuMPINT"\n" , mem_block[A_PROGRAM].current_size + 2); YYACCEPT; } /* Move the generated code forward by 6 */ p = mem_block[A_PROGRAM].block + CURRENT_PROGRAM_SIZE - 1; q = p + 6; for (left = CURRENT_PROGRAM_SIZE - $1.start ; left > 0 ; left--, p--, q--) *q = *p; /* p now points to program[$1.start]-1. * Store the first two call-other args there. */ p[1] = F_STRING; upd_short($1.start+1, store_prog_string( ref_mstring(query_simul_efun_file_name()))); p[4] = F_STRING; upd_short($1.start+4, store_prog_string(ref_mstring(STR_CALL_OTHER))); CURRENT_PROGRAM_SIZE += 6; } %line /* If we received a string as call_other_name, it's a constant call. */ name = $3; if (name) { /* Push the function name (the expr4 is already on the stack) */ string_number = store_prog_string(name); if (string_number <= 0x0ff ) { ins_f_code(F_CSTRING0); ins_byte(string_number); } else if ( string_number <= 0x1ff ) { ins_f_code(F_CSTRING1); ins_byte(string_number); } else if ( string_number <= 0x2ff ) { ins_f_code(F_CSTRING2); ins_byte(string_number); } else if ( string_number <= 0x3ff ) { ins_f_code(F_CSTRING3); ins_byte(string_number); } else { ins_f_code(F_STRING); ins_short(string_number); } } /* if (name) */ /* 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. */ PREPARE_INSERT(10) Bool has_ellipsis; Bool ap_needed; has_ellipsis = got_ellipsis[argument_level]; ap_needed = MY_TRUE; if (!disable_sefuns && call_other_sefun >= 0) { /* SIMUL EFUN */ function_t *funp; int num_arg; num_arg = $6 + 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) && !has_ellipsis) yyerrorf("Too many arguments to simul_efun %s" , get_txt(funp->name)); /* call_other_sefun is >= 0 (see above) */ if ((unsigned long)call_other_sefun >= SEFUN_TABLE_SIZE) { /* call-other: the number of arguments will be * detected and corrected at runtime. */ add_f_code(F_CALL_DIRECT); CURRENT_PROGRAM_SIZE++; } else { /* Direct call */ if (funp->num_arg != SIMUL_EFUN_VARARGS && !(funp->flags & TYPE_MOD_XVARARGS) && !has_ellipsis) { 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 %"PRIuMPINT"\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_f_code(F_CONST0); } } if (funp->num_arg != SIMUL_EFUN_VARARGS && !(funp->flags & TYPE_MOD_XVARARGS) && !has_ellipsis) ap_needed = MY_FALSE; if (ap_needed) { add_f_code(F_USE_ARG_FRAME); CURRENT_PROGRAM_SIZE++; } add_f_code(F_SIMUL_EFUN); add_short(call_other_sefun); CURRENT_PROGRAM_SIZE += 3; } $$.type = funp->type; $$.type.typeflags &= TYPE_MOD_MASK; } else /* true call_other */ { add_f_code(F_CALL_OTHER); CURRENT_PROGRAM_SIZE++; $$.type = instrs[F_CALL_OTHER].ret_type; } $$.code = -1; $$.start = $1.start; pop_arg_stack($6); /* No good need of these arguments because we don't * know what we are going to call. */ /* Do the post processing of the arg frame handling */ if (ap_needed) { /* Restore the previous arg frame pointer */ add_f_code(F_RESTORE_ARG_FRAME); CURRENT_PROGRAM_SIZE++; } else { /* Since the arg frame is not needed, remove the * earlier save_arg_frame instruction. */ bytecode_p src, dest; size_t left; dest = PROGRAM_BLOCK + $4.start; src = dest+1; left = CURRENT_PROGRAM_SIZE - $4.start - 1; while (left-- > 0) { *dest++ = *src++; } CURRENT_PROGRAM_SIZE--; } argument_level--; } ; /* function_call */ call_other_name: identifier { $$ = $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.typeflags != TYPE_STRING && (pragma_strict_types != PRAGMA_WEAK_TYPES || $2.type.typeflags != TYPE_UNKNOWN) && $2.type.typeflags != TYPE_ANY) type_error("Illegal type for lfun name", $2.type); } ; /* call_other_name */ function_name: L_IDENTIFIER { $$.super = NULL; $$.real = $1; } | L_LOCAL { ident_t *lvar = $1; ident_t *fun = find_shared_identifier(get_txt(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", get_txt(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 = $2; *($$.super = yalloc(1)) = '\0'; $$.real = lvar; } | 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 && (!EVALUATION_TOO_LONG()) ) { /* Yup, check it with a privilege violation. * If it's denied, ignore the "efun::" qualifier. */ svalue_t *res; push_ref_string(inter_sp, STR_NOMASK_SIMUL_EFUN); push_c_string(inter_sp, current_loc.file->name); push_ref_string(inter_sp, $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" , get_txt($3->name)); yfree($1); $$.super = NULL; } else if (!res->u.number) { yfree($1); $$.super = NULL; } else { $$.super = $1; } } else if (EVALUATION_TOO_LONG()) { yyerrorf("Can't call master::%s for " "'nomask simul_efun %s': eval cost too big" , get_txt(STR_PRIVILEGE), get_txt($3->name)); yfree($1); $$.super = NULL; } else /* the qualifier is ok */ $$.super = $1; $$.real = $3; /* and don't forget the function ident */ } | anchestor L_COLON_COLON L_LOCAL { %line ident_t *lvar = $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 && (!EVALUATION_TOO_LONG()) ) { /* Yup, check it with a privilege violation. * If it's denied, ignore the "efun::" qualifier. */ svalue_t *res; push_ref_string(inter_sp, STR_NOMASK_SIMUL_EFUN); push_c_string(inter_sp, current_loc.file->name); push_ref_string(inter_sp, 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" , get_txt(lvar->name)); yfree($1); $$.super = NULL; } else if (!res->u.number) { yfree($1); $$.super = NULL; } else { $$.super = $1; } } else if (EVALUATION_TOO_LONG()) { yyerrorf("Can't call master::%s for " "'nomask simul_efun %s': eval cost too big" , get_txt(STR_PRIVILEGE), get_txt(lvar->name)); yfree($1); $$.super = NULL; } else /* the qualifier is ok */ $$.super = $1; $$.real = lvar; /* and don't forget the function ident */ } ; /* function_name */ anchestor: L_IDENTIFIER { $$ = ystring_copy(get_txt($1->name)); } | L_STRING L_STRING { fatal("presence of rule should prevent its reduction"); } | L_STRING { $$ = ystring_copy(get_txt(last_lex_string)); free_mstring(last_lex_string); last_lex_string = NULL; } ; /* anchestor */ %ifndef USE_NEW_INLINES /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* 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; fulltype_t save_tol[10]; fulltype_t save_ftol[10]; char name[3]; int num, i; fulltype_t ftype; /* 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] = 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, MY_TRUE); } /* Declare the function */ ftype = Type_Unknown; ftype.typeflags |= TYPE_MOD_VARARGS | TYPE_MOD_PRIVATE; num = define_new_function(MY_FALSE, /* id */ $1, 9, 0, 0 , NAME_UNDEFINED|NAME_PROTOTYPE , ftype ); /* Restore the old locals information */ leave_block_scope(MY_TRUE); 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_ftol[i]; } /* Insert the call to the lfun closure */ $$.start = CURRENT_PROGRAM_SIZE; $$.code = -1; ins_f_code(F_CLOSURE); ins_short(num); ins_short(0); $$.type = Type_Closure; } ; /* inline_fun */ %endif /* USE_NEW_INLINES */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* The catch()-statement */ catch: L_CATCH { ins_f_code(F_SAVE_ARG_FRAME); $
$ = CURRENT_PROGRAM_SIZE; ins_f_code(F_CATCH); ins_byte(0); /* Placeholder for flags */ ins_byte(0); /* Placeholder for the jump offset */ } '(' comma_expr note_start opt_catch_mods ')' { %line p_int origstart, start, modstart, offset; p_int flags = $6; /* Get the address of the CATCH instruction * and of the modifications */ origstart = start = $
2; modstart = $5.start; /* If there were code creating modifiers, move their code * before the F_CATCH (currently only 'reserve' does that). * We need to do this before we add the END_CATCH. */ if (flags & CATCH_FLAG_RESERVE) { shuffle_code(start, modstart, CURRENT_PROGRAM_SIZE); start += CURRENT_PROGRAM_SIZE - modstart; } ins_f_code(F_END_CATCH); /* Modify the instruction if necessary */ if (flags) { bytecode_p p; p = PROGRAM_BLOCK + start + 1; *p = flags & 0xff; } /* Update the offset field of the CATCH instruction */ offset = CURRENT_PROGRAM_SIZE - (start + 3); if (offset >= 0x100) { /* Too big offset, change * * CATCH l * * l: END_CATCH * * to * * CATCH l0 * BRANCH l1 * l0: LBRANCH l2 * l1: * l2: END_CATCH */ int i; bytecode_p p; if (!realloc_a_program(5)) { yyerrorf("Out of memory: program size %"PRIuMPINT"\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 + 6, offset+2); if (offset > 0x7ffd) yyerror("offset overflow"); } else { mem_block[A_PROGRAM].block[start+2] = offset; } /* Restore the argument frame */ ins_f_code(F_RESTORE_ARG_FRAME); $$.start = origstart; $$.type = Type_Any; $$.code = -1; } ; /* catch */ opt_catch_mods : ';' opt_catch_mod_list { $$ = $2; } | /* empty */ { $$ = 0; } ; /* opt_catch_mods */ opt_catch_mod_list : opt_catch_mod_list ',' opt_catch_modifier { if ($1 & $3 & CATCH_FLAG_RESERVE) { /* On multiple 'reserve's, use only the first one */ yywarnf("Multiple 'reserve' modifiers in catch()"); insert_pop_value(); } $$ = $1 | $3; } | opt_catch_modifier { $$ = $1; } ; /* opt_catch_mod_list */ opt_catch_modifier : identifier { $$ = 0; if (mstreq($1, STR_NOLOG)) $$ = CATCH_FLAG_NOLOG; else if (mstreq($1, STR_PUBLISH)) $$ = CATCH_FLAG_PUBLISH; else if (mstreq($1, STR_RESERVE)) yyerrorf("Bad 'reserve' modifier in catch(): missing expression"); else yyerrorf("Illegal modifier '%s' in catch() - " "expected 'nolog', 'publish' or 'reserve '" , get_txt($1) ); free_mstring($1); } | identifier expr0 { $$ = 0; if (mstreq($1, STR_NOLOG) || mstreq($1, STR_PUBLISH) ) { yyerrorf("Bad modifier '%s' in catch(): no expression allowed" , get_txt($1) ); } else if (mstreq($1, STR_RESERVE)) { if ($2.type.typeflags != TYPE_NUMBER && $2.type.typeflags != TYPE_UNKNOWN && $2.type.typeflags != TYPE_ANY ) yyerrorf("Bad 'reserve' expression type to catch(): %s, " "expected int" , get_type_name($2.type) ); $$ = CATCH_FLAG_RESERVE; } else yyerrorf("Illegal modifier '%s' in catch() - " "expected 'nolog', 'publish' or 'reserve '" , get_txt($1) ); free_mstring($1); } ; /* opt_catch_modifier */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ /* 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_f_code(F_SSCANF); ins_byte($7 + 2); $$.start = $2.start; $$.type = Type_Number; $$.code = -1; } ; /* sscanf */ %ifdef USE_PARSE_COMMAND parse_command: L_PARSE_COMMAND note_start '(' expr0 ',' expr0 ',' expr0 lvalue_list ')' { ins_f_code(F_PARSE_COMMAND); ins_byte($9 + 3); $$.start = $2.start; $$.type = Type_Number; $$.code = -1; } ; /* parse_command */ %endif /* USE_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_f_code(F_PUSH_VIRTUAL_VARIABLE_LVALUE); ins_byte(i); } else { if ((i + num_virtual_variables) & ~0xff) { ins_f_code(F_PUSH_IDENTIFIER16_LVALUE); ins_short(i + num_virtual_variables); } else { ins_f_code(F_PUSH_IDENTIFIER_LVALUE); ins_byte(i + num_virtual_variables); } } } /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | lvalue_list ',' L_LOCAL { %line /* Push the lvalue for a local variable */ #ifdef USE_NEW_INLINES fulltype_t dummy; $3 = check_for_context_local($3, &dummy); if ($3->u.local.context >= 0) { ins_f_code(F_PUSH_CONTEXT_LVALUE); ins_byte($3->u.local.context); } else { ins_f_code(F_PUSH_LOCAL_VARIABLE_LVALUE); ins_byte($3->u.local.num); } $$ = 1 + $1; #else /* USE_NEW_INLINES */ $$ = 1 + $1; ins_f_code(F_PUSH_LOCAL_VARIABLE_LVALUE); ins_byte($3->u.local.num); #endif /* USE_NEW_INLINES */ } | 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 if ($4.inst == F_RINDEX) arrange_protected_lvalue($3.start, $3.code, $3.end, F_PROTECTED_RINDEX_LVALUE ); else arrange_protected_lvalue($3.start, $3.code, $3.end, F_PROTECTED_AINDEX_LVALUE ); if ($4.type1.typeflags & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); if (exact_types.typeflags) { fulltype_t type; type = $3.type; type.typeflags &= TYPEID_MASK; if ( !(type.typeflags & TYPE_MOD_POINTER) ) switch (type.typeflags) { 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.typeflags & TYPE_MOD_REFERENCE) yyerror("Reference used as index"); /* Compute and check types */ if (exact_types.typeflags) { fulltype_t type; type = $3.type; type.typeflags &= TYPEID_MASK; if (type.typeflags != TYPE_ANY && type.typeflags != TYPE_MAPPING) { type_error("Bad type to indexed value", type); } type = $7.type; type.typeflags &= TYPEID_MASK; if (type.typeflags != TYPE_ANY && type.typeflags != 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_NA_RANGE: prot_op = F_PROTECTED_NA_RANGE_LVALUE; break; case F_AN_RANGE: prot_op = F_PROTECTED_AN_RANGE_LVALUE; break; case F_RA_RANGE: prot_op = F_PROTECTED_RA_RANGE_LVALUE; break; case F_AR_RANGE: prot_op = F_PROTECTED_AR_RANGE_LVALUE; break; case F_AA_RANGE: prot_op = F_PROTECTED_AA_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; case F_AX_RANGE: prot_op = F_PROTECTED_AX_RANGE_LVALUE; break; default: errorf("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.typeflags) { fulltype_t type; type = $3.type; type.typeflags &= TYPEID_MASK; if ((type.typeflags & TYPE_MOD_POINTER) == 0 && type.typeflags != TYPE_ANY && type.typeflags != TYPE_STRING) { type_error("Bad type of argument used for range", type); } type = $4.type1; type.typeflags &= TYPEID_MASK; if (type.typeflags != TYPE_ANY && type.typeflags != TYPE_NUMBER) type_error("Bad type of index", type); type = $4.type2; type.typeflags &= TYPEID_MASK; if (type.typeflags != TYPE_ANY && type.typeflags != TYPE_NUMBER) type_error("Bad type of index", type); } } %ifdef USE_STRUCTS /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ | lvalue_list ',' expr4 L_ARROW struct_member_name { /* Create a reference to a struct member */ short s_index = -1; $$ = 1 + $1; if (!IS_TYPE_ANY($3.type) && !IS_TYPE_STRUCT($3.type)) { yyerrorf("Bad type for struct lookup: %s" , get_type_name($3.type)); } else { if (IS_TYPE_STRUCT($3.type)) { s_index = get_struct_index($3.type.t_struct); if (s_index == -1) yyerrorf("Unknown type in lvalue struct dereference: %s\n" , get_type_name($3.type) ); } /* At this point: s_index >= 0: $1 is of type struct * < 0: $1 is of type mixed */ if ($5 != NULL) { int num; if (s_index >= 0) { struct_type_t * ptype = $3.type.t_struct; num = struct_find_member(ptype, $5); if (num < 0) { yyerrorf("No such member '%s' for struct '%s'" , get_txt($5) , get_txt(struct_t_name(ptype)) ); } } else /* $3 is of type mixed */ { s_index = find_struct_by_member($5, &num); } /* If this is a legal struct lookup, num >= 0 at this point */ if (num >= 0) { ins_number(num); ins_number(s_index); arrange_protected_lvalue($3.start, $3.code, $3.end, F_PROTECTED_INDEX_S_LVALUE ); } } else /* Runtime lookup */ { ins_number(s_index); arrange_protected_lvalue($3.start, $3.code, $3.end, F_PROTECTED_INDEX_S_LVALUE ); } } if ($5 != NULL) free_mstring($5); } %endif /* USE_STRUCTS */ ; /* lvalue_list */ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/ %% %line #ifdef __MWERKS__ # pragma warn_possunwant reset # pragma warn_implicitconv reset #endif /*=========================================================================*/ /*-------------------------------------------------------------------------*/ static void define_local_variable (ident_t* name, fulltype_t actual_type, typeflags_t opt_star, struct lvalue_s *lv, Bool redeclare, Bool with_init) /* This is called directly from a parser rule: [*] * if with_init is true, then an initialization of this variable will follow. * if redeclare is true, then a local name is redeclared. * It creates the local variable and returns the corresponding lvalue * in lv. */ { /* redeclare: * MY_FALSE: A new local variable * MY_TRUE: A local name is redeclared. If this happens * on a deeper level, it is even legal. */ block_scope_t *scope = block_scope + block_depth - 1; ident_t *q; actual_type.typeflags |= opt_star; #ifdef USE_NEW_INLINES if (current_inline && current_inline->parse_context) { #ifdef DEBUG_INLINES printf("DEBUG: context name '%s'\n", get_txt(name->name)); #endif /* DEBUG_INLINES */ if (redeclare && current_inline->block_depth+1 <= name->u.local.depth) yyerrorf("Illegal to redeclare local name '%s'" , get_txt(name->name)); q = add_context_name(name, actual_type, -1); lv->u.simple[0] = F_PUSH_CONTEXT_LVALUE; lv->u.simple[1] = q->u.local.context; } else { if(redeclare) q = redeclare_local(name, actual_type, block_depth); else q = add_local_name(name, actual_type, block_depth); if (use_local_scopes && scope->clobbered) { /* finish the previous CLEAR_LOCALS, if any */ if (scope->num_locals - 1 > scope->num_cleared) mem_block[A_PROGRAM].block[scope->addr+2] = (char)(scope->num_locals - 1 - scope->num_cleared); scope->clobbered = MY_FALSE; scope->num_cleared = scope->num_locals - 1; } if (use_local_scopes && scope->num_locals == scope->num_cleared + 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 + scope->num_cleared); ins_byte(0); } lv->u.simple[0] = F_PUSH_LOCAL_VARIABLE_LVALUE; lv->u.simple[1] = q->u.local.num; } #else /* USE_NEW_INLINES */ if (redeclare) q = redeclare_local(name, actual_type, block_depth); else q = add_local_name(name, actual_type, block_depth, MY_FALSE); if (use_local_scopes && scope->clobbered) { /* finish the previous CLEAR_LOCALS, if any */ if (scope->num_locals - 1 > scope->num_cleared) mem_block[A_PROGRAM].block[scope->addr+2] = (char)(scope->num_locals - 1 - scope->num_cleared); scope->clobbered = MY_FALSE; scope->num_cleared = scope->num_locals - 1; } if (use_local_scopes && scope->num_locals == scope->num_cleared + 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 + scope->num_cleared); ins_byte(0); } lv->u.simple[0] = F_PUSH_LOCAL_VARIABLE_LVALUE; lv->u.simple[1] = q->u.local.num; #endif /* USE_NEW_INLINES */ lv->length = 0; lv->type = actual_type; if (!with_init) { /* If this is a float variable, we need to insert an appropriate * initializer, as the default svalue-0 is not a valid float value. */ Bool need_value = MY_FALSE; %line #ifdef USE_NEW_INLINES /* When parsing context variables, the context_closure instruction * expects a value on the stack. If we do a float initialization, * we leave the 0.0 on the stack, otherwise we'll push a 0. * For normal float locals, we'll create the bytecode to assign * the float 0. */ need_value = current_inline && current_inline->parse_context; #endif /* USE_NEW_INLINES */ if (!(actual_type.typeflags & TYPE_MOD_POINTER) && (actual_type.typeflags & PRIMARY_TYPE_MASK) == TYPE_FLOAT ) { ins_f_code(F_FCONST0); if (!need_value) { if (!add_lvalue_code(lv, F_VOID_ASSIGN)) return; } need_value = MY_FALSE; } /* if (float variable) */ if (need_value) /* If we still need a value... */ { ins_number(0); } } } /* define_local_variable() */ /*-------------------------------------------------------------------------*/ static void init_local_variable ( ident_t* name, struct lvalue_s *lv, int assign_op , fulltype_t type2) /* This is called directly from a parser rule: [*] = * It will be called after the call to define_local_variable(). * It assigns the result of to the variable. */ { /* We got a " = " type declaration. */ %line #ifdef USE_NEW_INLINES #ifdef DEBUG_INLINES if (current_inline && current_inline->parse_context) printf("DEBUG: inline context decl: name = expr, program_size %"PRIuMPINT"\n", CURRENT_PROGRAM_SIZE); #endif /* DEBUG_INLINES */ #endif /* USE_NEW_INLINES */ type2.typeflags &= TYPEID_MASK; /* Check the assignment for validity */ if (exact_types.typeflags && !compatible_types(lv->type, type2, MY_TRUE)) { yyerrorf("Bad assignment %s", get_two_types(lv->type, type2)); } if (assign_op != F_ASSIGN) { yyerror("Only plain assignments allowed here"); } if (type2.typeflags & TYPE_MOD_REFERENCE) yyerror("Can't trace reference assignments"); /* If we're parsing a context variable, just leave the * value on the stack for the context_closure instruction. * For normal locals, add the bytecode to create the lvalue * and do the assignment. */ #ifdef USE_NEW_INLINES if (!current_inline || !current_inline->parse_context) #endif /* USE_NEW_INLINES */ { if (!add_lvalue_code(lv, F_VOID_ASSIGN)) return; } /* parsed context var */ } /* init_local_variable() */ /*-------------------------------------------------------------------------*/ static Bool add_lvalue_code ( struct lvalue_s * lv, int instruction) /* Add the lvalue code held in * to the end of the program. * If 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 %"PRIuMPINT"\n" , 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; case F_CLIT: case F_NCLIT: case F_CSTRING0: case F_CSTRING1: case F_CSTRING2: mem_block[A_PROGRAM].current_size = last_expression-1; break; case F_STRING: mem_block[A_PROGRAM].current_size = last_expression-2; break; case F_NUMBER: mem_block[A_PROGRAM].current_size = last_expression-4; break; default: ins_f_code(F_POP_VALUE); } last_expression = -1; } else /* The last expression is too long ago: just pop whatever there * is on the stack. */ ins_f_code(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 * newcode: additional instruction to insert. * * The following scenarios exist: * * code >= 0 && end != 0: * The multi-byte instruction in [..[ (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 and * are appended. * * Cases are: * global * IDENTIFIER16 -> PUSH_IDENTIFIER16_LVALUE #ifdef USE_STRUCTS * expr4->x * S_INDEX -> PUSH_PROTECTED_INDEXED_S_LVALUE #endif * expr4[x] * INDEX -> PUSH_PROTECTED_INDEXED_LVALUE * expr4[ PUSH_PROTECTED_RINDEXED_LVALUE * expr4[>x] * AINDEX -> PUSH_PROTECTED_AINDEXED_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 (1 byte code, 1 byte argument) * removed (the argument byte is preserved), instead the instructions * plus the preserved argument byte and are appended * to the end of the current 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 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[ F_PUSH_PROTECTED_INDEXED_LVALUE; * &(expr4[ F_PUSH_PROTECTED_RINDEXED_LVALUE; #ifdef USE_STRUCTS * &(expr4->x): F_PROTECTED_INDEX_S_LVALUE * -> F_PUSH_PROTECTED_INDEXED_S_LVALUE; #endif * * 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 %"PRIuMPINT"\n" , CURRENT_PROGRAM_SIZE + length); return; } /* Cycle the indexing code to the end, where it belongs: * * * is changed via * <...> * to * */ 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) { #ifdef USE_STRUCTS case F_PUSH_INDEXED_S_LVALUE: code = F_PUSH_PROTECTED_INDEXED_S_LVALUE; break; #endif /* USE_STRUCTS */ 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_AINDEXED_LVALUE: code = F_PUSH_PROTECTED_AINDEXED_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 in place of the indexing instruction * right before p == current */ PUT_CODE(p-1, instrs[code].opcode); code_stored: /* Append the newcode instruction (current will be adjusted * at the end of the function). */ PUT_CODE(p, instrs[newcode].opcode); } else { /* Variant 2: Change * * to * */ int instr_arg; p_int length; if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %"PRIuMPINT"\n" , CURRENT_PROGRAM_SIZE + 2); return; } p = PROGRAM_BLOCK + start; instr_arg = p[1]; length = current - start - 2; for( ; --length >= 0; p++) PUT_CODE(p, GET_CODE(p+2)); STORE_CODE(p, code); STORE_CODE(p, instr_arg); PUT_CODE(p, instrs[newcode].opcode); } } else { /* Variant 3: Just add a modified */ switch(newcode) { #ifdef USE_STRUCTS case F_PROTECTED_INDEX_S_LVALUE: newcode = F_PUSH_PROTECTED_INDEXED_S_LVALUE; break; #endif /* USE_STRUCTS */ case F_PROTECTED_INDEX_LVALUE: newcode = F_PUSH_PROTECTED_INDEXED_LVALUE; break; case F_PROTECTED_RINDEX_LVALUE: newcode = F_PUSH_PROTECTED_RINDEXED_LVALUE; break; case F_PROTECTED_AINDEX_LVALUE: newcode = F_PUSH_PROTECTED_AINDEXED_LVALUE; break; default: yyerror("Need lvalue for range lvalue."); } if (!realloc_a_program(2)) { yyerrorf("Out of memory: program size %"PRIuMPINT"\n" , CURRENT_PROGRAM_SIZE + 2); return; } p = PROGRAM_BLOCK + current; PUT_CODE(p, instrs[newcode].opcode); } /* Correct the program size */ CURRENT_PROGRAM_SIZE = current + 1; } /* arrange_protected_lvalue() */ /*-------------------------------------------------------------------------*/ int proxy_efun (int function, int num_arg UNUSED) /* If the number of arguments doesn't fit the , maybe there * is an alternative. * Return the code of the alternative efun, or -1 if there is none. */ { #if defined(__MWERKS__) # pragma unused(num_arg) #endif if (function == F_PREVIOUS_OBJECT) { /* num_arg == 0 */ return F_PREVIOUS_OBJECT0; } return -1; } /* proxy_efun() */ /*-------------------------------------------------------------------------*/ 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. */ CURRENT_PROGRAM_SIZE = align(CURRENT_PROGRAM_SIZE); /* Must happen before store_function_header() */ realloc_a_program(FUNCTION_HDR_SIZE); store_function_header( CURRENT_PROGRAM_SIZE , STR_VARINIT , Type_Any , 0 /* num_args */ , 0 /* num_vars */ ); first_initializer_start = CURRENT_PROGRAM_SIZE + FUNCTION_PRE_HDR_SIZE; CURRENT_PROGRAM_SIZE += FUNCTION_HDR_SIZE; } else if ((p_int)(CURRENT_PROGRAM_SIZE - 3) == last_initializer_end) { /* The new 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_jump_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_f_code(F_JUMP); last_initializer_end = (p_int)mem_block[A_PROGRAM].current_size; ins_byte(0); ins_short(0); } /* add_new_init_jump() */ /*-------------------------------------------------------------------------*/ static short lookup_inherited (const char *super_name, string_t *real_name , inherit_t **pIP, funflag_t *pFlags) /* Lookup an inherited function :: 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. * * can be an empty string or the (partial) name of one * of the inherits. must be shared string. */ { inherit_t *ip, *foundp; int num_inherits, super_length; short found_ix; found_ix = -1; *pIP = NULL; *pFlags = 0; if (!real_name) return -1; /* Strip leading '/' */ while (*super_name == '/') super_name++; super_length = strlen(super_name); num_inherits = INHERIT_COUNT; /* TODO: Is this really necessary? real_name should be tabled * already. */ { string_t *tmp; tmp = find_tabled(real_name); #ifdef DEBUG if (!tmp) fprintf(stderr, "DEBUG: insert_inherited(): Can't find function " "'%s'.\n", get_txt(real_name)); else if (tmp != real_name) fprintf(stderr, "DEBUG: insert_inherited(): Function " "'%s' is not a tabled string.\n" , get_txt(real_name)); #endif if (tmp && tmp != real_name) { free_mstring(real_name); real_name = ref_mstring(tmp); } } /* 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) { /* ip->prog->name includes .c */ int l = mstrsize(ip->prog->name)-2; if (l < super_length) continue; if (l > super_length && get_txt(ip->prog->name)[l-super_length-1] != '/') continue; if (strncmp(super_name, get_txt(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->variables[ip2->variable_index_offset+numvar2-1 ].type.typeflags & TYPE_MOD_VIRTUAL && !(prog2->variables[numvar2-1].type.typeflags & 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_function ( const char * super_name , const char * real_name , unsigned short * pInherit ) /* Lookup an inherited function :: and return * it's function index as result, and the inheritance index in *. * Return -1 if not found. * * The returned function index is not adjusted for the compiled program's * function table. * * This function is called by the lexer to resolve #' closures, * and by restore_value()/restore_object() to restore closure values. * * can be an empty string or the (partial) name of one * of the inherits. */ { inherit_t *ip; string_t *rname; funflag_t flags; short ix; rname = find_tabled_str(real_name); ix = rname ? lookup_inherited(super_name, rname, &ip, &flags) : -1; if (ix >= 0) /* Also return the inherit index. */ *pInherit = ip - (inherit_t *)mem_block[A_INHERITS].block; else *pInherit = 0; return ix; } /* find_inherited_function() */ /*-------------------------------------------------------------------------*/ static int insert_inherited (char *super_name, string_t *real_name , program_t **super_p, function_t *fun_p , int num_arg, bytecode_p __prepare_insert__p ) /* The compiler encountered a ::() call with * arguments; the codepointer is <__prepare_insert__p>. * * Look up the function information and set * and * * 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. * Result is -1 if the function wasn't found, -2 if it was a wildcarded * supercall to a function with arguments, otherwise the function index. * * 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, * , 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_f_code(F_CALL_INHERITED); add_short(ip - (inherit_t *)mem_block[A_INHERITS].block); add_short(found_ix); CURRENT_PROGRAM_SIZE += 5; /* Return the program pointer */ *super_p = ip->prog; /* Return a copy of the function structure */ fun_p->flags = flags & ~INHERIT_MASK; get_function_information(fun_p, ip->prog, found_ix); fun_p->name = real_name; return found_ix; } /* if (ip) */ /* Inherit not found, maybe it's a wildcarded call */ if (strpbrk(super_name, "*?")) { Bool *was_called; /* Flags which inh. fun has been called already */ inherit_t *ip0; int num_inherits; 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(10) /* ip->prog->name includes .c */ int l = mstrsize(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, get_txt(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->variables[ip2->variable_index_offset+numvar2-1 ].type.typeflags & TYPE_MOD_VIRTUAL && !(prog2->variables[numvar2-1].type.typeflags & 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_f_code(F_CALL_INHERITED_NOARGS); add_short(ip_index); add_short(i); CURRENT_PROGRAM_SIZE += 5; /* 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->flags = flags & ~INHERIT_MASK; get_function_information(fun_p, ip->prog, i); fun_p->name = real_name; calls++; } /* for() */ /* The calls above left their results on the stack. * Combine them into a single array (which might be empty). */ { PREPARE_INSERT(3) add_f_code(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 is a cross-definition from real function , * separated by . * Set the flags and offset of accordingly to point to , 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_virtual_function_id (program_t *progp, int fx) /* Return a pointer to the flags of the first entry of function in * that is inherited virtual (i.e. the first entry we encounter that doesn't have * TYPE_MOD_VIRTUAL). * * This function takes care of resolving cross-definitions and inherits * to the real function flag. */ { funflag_t flags; funflag_t *last; flags = progp->functions[fx]; /* Handle a cross-define */ if (flags & NAME_CROSS_DEFINED) { fx += CROSSDEF_NAME_OFFSET(flags); flags = progp->functions[fx]; } /* This one is inherited virtual. We wont get called otherwise. */ last = &progp->functions[fx]; /* Walk the inherit chain */ while((flags & (NAME_INHERITED|TYPE_MOD_VIRTUAL)) == (NAME_INHERITED|TYPE_MOD_VIRTUAL)) { 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_virtual_function_id() */ /*-------------------------------------------------------------------------*/ #ifdef USE_STRUCTS static void copy_structs (program_t *from, funflag_t flags) /* Copy the struct definitions from program which is inherited * with visibility . */ { int struct_id; for (struct_id = 0; struct_id < from->num_structs; struct_id++) { int id; ident_t *p; struct_def_t *pdef = from->struct_defs + struct_id; funflag_t f; f = flags | pdef->flags; /* Is this struct visible to us? */ if (pdef->flags & (TYPE_MOD_PRIVATE|NAME_HIDDEN)) { f |= NAME_HIDDEN; } /* Duplicate definition? */ id = find_struct(struct_t_name(pdef->type)); if (!(f & NAME_HIDDEN) && id >= 0) { /* We have a struct with this name. Check if we just * inherited it again, or if it's a name clash. */ if (STRUCT_DEF(id).type != pdef->type) { if (STRUCT_DEF(id).inh >= 0) { inherit_t * pInh = &INHERIT(STRUCT_DEF(id).inh); yyerrorf("Different structs '%s' inherited from '%s' " "and from '%s'" , get_txt(struct_t_name(STRUCT_DEF(id).type)) , get_txt(from->name) , get_txt(pInh->prog->name) ); } else yyerrorf("struct '%s' inherited from '%s' differs " "from existing struct" , get_txt(struct_t_name(STRUCT_DEF(id).type)) , get_txt(from->name) ); continue; } f |= NAME_HIDDEN; } /* New struct */ p = make_global_identifier(get_txt(struct_t_name(pdef->type)), I_TYPE_GLOBAL); if (p == NULL) continue; /* Create a new struct entry, then replace the struct prototype * type with the one we inherited. */ current_struct = define_new_struct( MY_FALSE, p, f); free_struct_type(STRUCT_DEF(current_struct).type); STRUCT_DEF(current_struct).type = ref_struct_type(pdef->type); STRUCT_DEF(current_struct).inh = INHERIT_COUNT; } } /* copy_structs() */ #endif /* USE_STRUCTS */ /*-------------------------------------------------------------------------*/ static int copy_functions (program_t *from, funflag_t type) /* The functions of the program are inherited with visibility . * 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_INHERITED * * The result is the function index of the inherited __INIT function, * or -1 if the inherited program doesn't have an initializer. */ { int initializer = -1; 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 0; } /* 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++) { 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; } /* Copy the function information */ get_function_information(fun_p, from, i2); } /* 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: /* Ignore cross defines. * They are the only complete invisible entries. */ if (fun.flags & NAME_CROSS_DEFINED) break; /* Visible: create a new identifier for it */ p = make_global_identifier(get_txt(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 */ n = p->u.global.function; /* If the identifier is (also) an lfun, handle it, even if * it's overloaded by something else as well. If we didn't * subsequent inheritors would receive illegal function * start offsets. */ if ( n >= 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" , get_txt(fun.name), current_loc.file->name ); cross_define( &fun, OldFunction , current_func_index - n ); p->u.global.function = current_func_index; } else if ( (fun.flags & (TYPE_MOD_PRIVATE|NAME_HIDDEN|NAME_UNDEFINED)) == (TYPE_MOD_PRIVATE|NAME_HIDDEN) ) { /* There is already one function with this * name. Ignore the private one, as we * only need it for useful error messages. */ break; } else if ( (OldFunction->flags & (TYPE_MOD_PRIVATE|NAME_HIDDEN|NAME_UNDEFINED)) == (TYPE_MOD_PRIVATE|NAME_HIDDEN) ) { /* The old one was invisible, ignore it * and take this one. */ p->u.global.function = current_func_index; } else if ((fun.flags | type) & TYPE_MOD_VIRTUAL && OldFunction->flags & TYPE_MOD_VIRTUAL && get_virtual_function_id(from, i) == get_virtual_function_id(INHERIT(OldFunction->offset.inherit).prog , n - INHERIT(OldFunction->offset.inherit).function_index_offset ) ) { /* Entries denote the same function and both * entries are visible. We have to use * cross_define nonetheless, to get consistant * redefinition (and to avoid the nomask * checking that comes next), and we prefer * the first one. * * It is important, that both entries are * indeed visible, because otherwise invisible * (i.e. private) functions would be made * visible again by another visible occurrence * of the same function. The originally invisible * occurrence would then be subject to * redefinition and nomask checking. */ OldFunction->flags |= fun.flags & (TYPE_MOD_PUBLIC|TYPE_MOD_NO_MASK); OldFunction->flags &= fun.flags | ~(TYPE_MOD_STATIC|TYPE_MOD_PRIVATE|TYPE_MOD_PROTECTED|NAME_HIDDEN); cross_define( OldFunction, &fun , n - current_func_index ); } else if ( (fun.flags & OldFunction->flags & TYPE_MOD_NO_MASK) && !( (fun.flags|OldFunction->flags) & NAME_UNDEFINED ) ) { yyerrorf( "Illegal to inherit 'nomask' function '%s' twice", get_txt(fun.name)); } else if (( fun.flags & TYPE_MOD_NO_MASK || OldFunction->flags & NAME_UNDEFINED ) && !(fun.flags & NAME_UNDEFINED) ) { /* This function is visible and existing, but the * inherited one is not, or this one is also nomask: * prefer this one one. */ cross_define( &fun, OldFunction , current_func_index - n ); p->u.global.function = current_func_index; } else { /* At least one of the functions is visible * or redefinable: prefer the first one. */ cross_define( OldFunction, &fun , n - current_func_index ); } } /* if (n < first_func_index) */ else if ( (fun.flags & (TYPE_MOD_PRIVATE|NAME_HIDDEN|NAME_UNDEFINED)) != (TYPE_MOD_PRIVATE|NAME_HIDDEN) ) { /* This is the dominant definition in the superclass, * inherit this one. */ #ifdef DEBUG /* The definition we picked before can't be * cross-defined, because cross-defines won't * be registered as global identifiers. * So the previous definition should be * nominally invisible so we can redefine it. */ if ( (FUNCTION(n)->flags & (TYPE_MOD_PRIVATE|NAME_HIDDEN|NAME_UNDEFINED)) != (TYPE_MOD_PRIVATE|NAME_HIDDEN) ) { fatal( "Inconsistent definition of %s() within " "superclass '%s'.\n" , get_txt(fun.name), get_txt(from->name) ); } #endif p->u.global.function = current_func_index; } } /* Handle the non-lfun aspects of the identifier */ { if (n != I_GLOBAL_FUNCTION_OTHER || (p->u.global.efun < 0 && p->u.global.sim_efun < 0) || (fun.flags & (TYPE_MOD_PRIVATE|NAME_HIDDEN)) == 0 || (fun.flags & (NAME_UNDEFINED)) != 0 ) { /* This is not an inherited private function shadowing * a (simul-)efun. */ if (p->u.global.efun >= 0 || p->u.global.sim_efun >= 0) { /* This inherited function shadows an efun */ efun_shadow_t *q; q = xalloc(sizeof(efun_shadow_t)); if (!q) { yyerrorf("Out of memory: efun shadow (%zu bytes)" , 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, unless of course * the code above already took care of that change. */ if (p->u.global.function < 0) 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. */ init_global_identifier(p, /* bVariable: */ MY_TRUE); 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) && mstreq(fun.name, STR_HEART_BEAT)) { heart_beat = current_func_index; } /* Recognize the initializer function */ if (mstreq(fun.name, STR_VARINIT)) { initializer = i; fun.flags |= NAME_UNDEFINED; } } /* 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) { int ix; ix = ARGTYPE_COUNT; add_to_mem_block( A_ARGUMENT_TYPES, &from->argument_types[from->type_start[i]], (sizeof (vartype_t)) * fun.num_arg ); for ( ; (size_t)ix < ARGTYPE_COUNT; ix++) ref_vartype_data(&ARGUMENT_TYPE(ix)); } } } 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 */ return initializer; } /* copy_functions() */ /*-------------------------------------------------------------------------*/ static void copy_variables (program_t *from, funflag_t type) /* Inherit the variables of with visibility . * 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 &= ~VAR_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; } } if (variables_initialized && from_variable_index_offset < 0) yyerror( "illegal to inherit virtually after initializing variables\n" ); } 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 . 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->variables[new_bound-1].type.typeflags & TYPE_MOD_VIRTUAL && !(progp->variables[progp->num_variables-1].type.typeflags & TYPE_MOD_VIRTUAL) ) { inherit_t inherit, *inheritp2; int k, inherit_index; funflag_t *flagp; function_t *funp; if (variables_initialized) yyerror("illegal to inherit virtually after " "initializing variables\n" ); 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) / sizeof(inherit_t); 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 new inherit_t structure. (But only, if it wasn't * already cross-defined to something else.) */ flagp = from->functions + inheritp->function_index_offset; funp = (function_t *)mem_block[A_FUNCTIONS].block + inherit.function_index_offset; for (k = inherit.prog->num_functions; --k >= 0; funp++) { if ( !(funp->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 . * After that, we will loop once more in here, but * the if() below will notice that. * As a side effect we terminate immediately if * 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; funflag_t new_type; p = make_global_identifier(get_txt(from->variables[j].name) , I_TYPE_GLOBAL); if (!p) return; new_type = type; /* 'public' variables should not become private when inherited * 'private'. */ if (from->variables[j].type.typeflags & TYPE_MOD_PUBLIC) new_type &= ~TYPE_MOD_PRIVATE; /* define_variable checks for previous 'nomask' definition. */ if (previous_variable_index_offset >= 0) { if ( !(from->variables[j].type.typeflags & TYPE_MOD_PRIVATE) ) { fulltype_t vartype = from->variables[j].type; vartype.typeflags |= new_type | NAME_INHERITED; redeclare_variable(p, vartype, previous_variable_index_offset + j ); } } else { fulltype_t vartype = from->variables[j].type; vartype.typeflags |= new_type | (from->variables[j].type.typeflags & TYPE_MOD_PRIVATE ? (NAME_HIDDEN|NAME_INHERITED) : NAME_INHERITED ) ; define_variable(p, vartype); } } /* 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 , 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 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_loc.line - stored_lines >= 2 && current_loc.line - stored_lines <= 9) { c = offset + 8*(current_loc.line - stored_lines) + 47; /* == (lineincr+6) << 3 | (codesize-1) */ byte_to_mem_block(A_LINENUMBERS, c); stored_lines = current_loc.line; return; } /* Use up the excessive amounts of lines */ stored_lines++; while (stored_lines > current_loc.line) { int lines; lines = stored_lines - current_loc.line; if (lines > 256) lines = 256; stored_lines -= lines; byte_to_mem_block(A_LINENUMBERS, LI_BACK); byte_to_mem_block(A_LINENUMBERS, lines-1); } while (stored_lines < current_loc.line) { int lines; lines = current_loc.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 . * Add the relocation marker with the offset to , 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_loc.line; stored_lines -= 2; current_loc.line = stored_lines+1; offset = current_loc.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_loc.line = save_current; } /* store_line_number_relocation() */ /*-------------------------------------------------------------------------*/ void store_line_number_backward (int offset) /* The current line counter is set back by 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 , 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 . * This will be include depth . * 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 = new_tabled(filename); if (inc.filename == NULL) { inc.filename = ref_mstring(STR_DEFAULT); yyerror("Out of memory: sharing include filename"); } /* Surround the 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 = new_tabled(tmp); if (inc.name == NULL) { inc.name = ref_mstring(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_loc.line) { int lines; lines = current_loc.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, int include_line) /* The current include ended. has to be the offset returned by * store_include_info() for this include file, is the * line number of the #include statement in the including file. */ { unsigned char c; stored_lines = include_line; 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 * 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 (const char * fname, Bool isMasterObj) /* Initialize the compiler environment prior to a compile. * is the name of the top LPC file to be compiled. * is TRUE if this compile is part of the compilation of * the master object (in which case sefuns are disabled). */ { 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 */ variables_defined = MY_FALSE; disable_sefuns = isMasterObj; 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; #ifdef USE_NEW_INLINES current_inline = NULL; inline_closure_id = 0; #endif /* USE_NEW_INLINES */ 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; } extend_mem_block(A_LOCAL_TYPES, MAX_LOCAL * sizeof(fulltype_t)); memset(&LOCAL_TYPE(0), 0, LOCAL_TYPE_COUNT * sizeof(fulltype_t)); type_of_locals = &(LOCAL_TYPE(0)); #ifdef USE_NEW_INLINES type_of_context = type_of_locals; #endif /* USE_NEW_INLINES */ #ifdef DEBUG_INLINES printf("DEBUG: prolog: type ptrs: %p, %p\n", type_of_locals, type_of_context ); #endif /* DEBUG_INLINES */ #ifdef USE_STRUCTS compiled_file = fname; #endif /* USE_STRUCTS */ 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; last_initializer_end = -4; /* To pass the test in transfer_init_control() */ variables_initialized = 0; argument_level = 0; got_ellipsis[0] = MY_FALSE; /* Check if call_other() has been replaced by a sefun. */ call_other_sefun = -1; if (!disable_sefuns) { id = make_shared_identifier(get_txt(STR_CALL_OTHER), I_TYPE_UNKNOWN, 0); if (!id) fatal("Out of memory: identifier '%s'.\n", get_txt(STR_CALL_OTHER)); if (id->type != I_TYPE_UNKNOWN) { /* 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; } } } /* if (!disable_sefuns) */ } /* prolog() */ /*-------------------------------------------------------------------------*/ static void epilog (void) /* The parser finished - now collect the information and generate * the program structure, if the parse was successful. */ { int i; p_int size; 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_mstring(last_string_constant); last_string_constant = NULL; } free_case_blocks(); %ifdef USE_STRUCTS for (i = 0; (size_t)i < STRUCT_MEMBER_COUNT; i++) { free_struct_member_data(&STRUCT_MEMBER(i)); } mem_block[A_STRUCT_MEMBERS].current_size = 0; /* If the parse was successful, Make sure that all structs are defined and * reactivate old structs where possible. * If an error occurs, num_parse_error is incremented and epilog() will * bail out below. */ if (!num_parse_error && !inherit_file) { struct_epilog(); } %endif /* USE_STRUCTS */ /* 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; /* 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(get_txt(STR_VARINIT), I_TYPE_UNKNOWN); if (ip) define_new_function(MY_FALSE, ip, 0, 0, first_initializer_start , TYPE_MOD_PROTECTED, Type_Unknown); /* 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) */ /* 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 (string_t *)) 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 UNDEF as body. * Except __INIT, which is created as CONST1 RETURN. */ if ((f->flags & (NAME_UNDEFINED|NAME_INHERITED)) == NAME_UNDEFINED) { CURRENT_PROGRAM_SIZE = align(CURRENT_PROGRAM_SIZE); if (!realloc_a_program(FUNCTION_HDR_SIZE + 2)) { yyerrorf("Out of memory: program size %"PRIuMPINT"\n" , CURRENT_PROGRAM_SIZE + FUNCTION_HDR_SIZE + 2); } else { f->offset.pc = CURRENT_PROGRAM_SIZE + FUNCTION_PRE_HDR_SIZE; store_function_header( CURRENT_PROGRAM_SIZE , f->name, f->type, f->num_arg , f->num_local); p = PROGRAM_BLOCK + CURRENT_PROGRAM_SIZE + FUNCTION_HDR_SIZE; /* 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 (mstreq(f->name, STR_VARINIT) && !f->num_arg) { f->flags &= ~NAME_UNDEFINED; *p++ = F_CONST1; *p = F_RETURN; } else { *p = F_UNDEF; } CURRENT_PROGRAM_SIZE += FUNCTION_HDR_SIZE + 2; } } /* 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) { /* Compare the two pointers. * The comparison operation has to match the * one in closure.c:function_cmp(). */ if (memcmp( &funname_start2->name, &funname_start1->name, sizeof(char *) ) < 0) { *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_mstring(functions->name); } } } /* Done: functions are sorted, resolved, etc etc */ } /* if (parse successful) */ /* 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_OTHER; s->shadow->u.global.variable = I_GLOBAL_VARIABLE_FUN; t = s->next; xfree(s); } all_efun_shadows = NULL; } all_globals = NULL; remove_unknown_identifier(); /* Now create the program structure */ switch (0) { default: #if 0 && defined(USE_STRUCTS) { int i, j; printf("DEBUG: --- structs in %s ---\n", current_loc.file->name); for (i = 0; i < STRUCT_COUNT; i++) { struct_type_t * ptype; ptype = STRUCT_DEF(i).type; printf("DEBUG: [%d] struct %s: (%s #%"PRId32") ref %"PRIdPINT ", %hd members, base %s, flags %"PRIx32"\n" , i, get_txt(ptype->name) , ptype->prog_name ? get_txt(ptype->prog_name) : "" , ptype->prog_id , ptype->ref , ptype->num_members , ptype->base ? get_txt(ptype->base->name) : "" , STRUCT_DEF(i).flags ); fflush(stdout); #if 1 for (j = 0; j < ptype->num_members; j++) { fulltype_t ftype; assign_var_to_fulltype(&ftype, ptype->member[j].type); printf("DEBUG: [%d] member %s: %s\n" , j, get_txt(ptype->member[j].name) , get_type_name(ftype) ); fflush(stdout); } #endif } printf("DEBUG: ------\n"); } #endif /* 0 && USE_STRUCTS */ /* On 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) { for (i = 0; (size_t)i < ARGTYPE_COUNT; i++) free_vartype_data(&ARGUMENT_TYPE(i)); 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 (%"PRIdPINT" bytes)", size); break; } prog = (program_t *)p; *prog = NULL_program; /* Set up the program structure */ if ( !(prog->name = new_mstring(current_loc.file->name)) ) { xfree(prog); yyerrorf("Out of memory: filename '%s'", current_loc.file->name); 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) | (pragma_share_variables ? P_SHARE_VARIABLES : 0) ; prog->load_time = current_time; total_prog_block_size += prog->total_size + mstrsize(prog->name); 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 = (string_t **)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->variables = (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); #ifdef USE_STRUCTS /* Add the struct information. */ prog->num_structs = STRUCT_COUNT; if (prog->num_structs) { memcpy(p, mem_block[A_STRUCT_DEFS].block, mem_block[A_STRUCT_DEFS].current_size); prog->struct_defs = (struct_def_t *)p; } else { prog->struct_defs = NULL; } p += align(mem_block[A_STRUCT_DEFS].current_size); #endif /* USE_STRUCTS */ /* 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 = (vartype_t *)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 + mstrsize(prog->name)+1; total_num_prog_blocks -= 1; xfree(prog); yyerrorf("Out of memory: linenumber structure (%zu bytes)" , 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; (size_t)i < LOCAL_TYPE_COUNT; i++) free_fulltype_data(&LOCAL_TYPE(i)); for (i = 0; (size_t)i < FUNCTION_COUNT; i++) free_fulltype_data(&FUNCTION(i)->type); for (i = 0; i < NUMAREAS; i++) { xfree(mem_block[i].block); } type_of_locals = NULL; #ifdef USE_NEW_INLINES type_of_context = NULL; #endif /* USE_NEW_INLINES */ /* 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; /* Free all function names and type data. */ 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_mstring(functions->name); } free_fulltype_data(&functions->type); } do_free_sub_strings( num_strings , (string_t **)mem_block[A_STRINGS].block , num_variables , (variable_t *)mem_block[A_VIRTUAL_VAR].block , INCLUDE_COUNT , (include_t *)mem_block[A_INCLUDES].block #ifdef USE_STRUCTS , STRUCT_COUNT , (struct_def_t *)mem_block[A_STRUCT_DEFS].block #endif /* USE_STRUCTS */ ); /* Free the type information */ for (i = 0; (size_t)i < ARGTYPE_COUNT; i++) { free_vartype_data(&ARGUMENT_TYPE(i)); } for (i = 0; (size_t)i < LOCAL_TYPE_COUNT; i++) { free_fulltype_data(&LOCAL_TYPE(i)); } compiled_prog = NULL; for (i = 0; i < NUMAREAS; i++) { xfree(mem_block[i].block); } type_of_locals = NULL; #ifdef USE_NEW_INLINES type_of_context = NULL; #endif /* USE_NEW_INLINES */ return; } /* NOTREACHED */ } /* epilog() */ /*-------------------------------------------------------------------------*/ void compile_file (int fd, const char * fname, Bool isMasterObj) /* Compile an LPC file. See the head comment for instructions. */ { prolog(fname, isMasterObj); start_new_file(fd, fname); yyparse(); /* If the parse failed, either num_parse_error != 0 * or inherit_file != NULL here. */ epilog(); end_new_file(); } /* compile_file() */ /*-------------------------------------------------------------------------*/ Bool is_undef_function (fun_hdr_p fun) /* Return TRUE if points to a referenced but undefined function. */ { return GET_CODE(FUNCTION_CODE(fun)) == F_UNDEF; } /* is_undef_function() */ /*-------------------------------------------------------------------------*/ #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 /*-------------------------------------------------------------------------*/ /***************************************************************************/ /* vim: filetype=c */