mirror of
git://git.psyced.org/git/psyclpc
synced 2024-08-15 03:20:16 +00:00
16799 lines
513 KiB
Text
16799 lines
513 KiB
Text
|
%{
|
||
|
%line
|
||
|
/*---------------------------------------------------------------------------
|
||
|
* LPC compiler
|
||
|
*
|
||
|
*---------------------------------------------------------------------------
|
||
|
* TODO: Some code parts 'know' which instructions are xcodes and which normal.
|
||
|
* TODO:: Conditional compiles would be nice there.
|
||
|
* TODO: The handling of virtual inherits is a bit vague, too.
|
||
|
*
|
||
|
* This is the grammar definition and bytecode compiler of LPC. However, this
|
||
|
* file is not passed to byacc directly, but first preprocessed by make_func,
|
||
|
* among other things to synchronise the tokens with the other bytecodes
|
||
|
* (reason being that yacc doesn't know an include construct). The following
|
||
|
* keywords are recognized and replaced by make_func:
|
||
|
*
|
||
|
* %line: generates a #line statement to synchronize the C compiler.
|
||
|
*
|
||
|
* %typemap TYPE<name>:<value>,...,TYPE_<name>:<value>
|
||
|
* Generates a lookup table TYPE_<name> -> <value>. Unspecified
|
||
|
* TYPEs are given the value 0.
|
||
|
*
|
||
|
* %hookmap <hookname>:<value>,...,<hookname>:<value>
|
||
|
* Generates a lookup table <hookname> -> <value>. Unspecified
|
||
|
* driverhook entries are given the value 0.
|
||
|
*
|
||
|
* In addition, make_func implements a simple preprocessor using the
|
||
|
* keywords %if, %elif, %else and %endif 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, <filename>, <isMasterObj>);
|
||
|
*
|
||
|
* 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 <ctype.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdarg.h>
|
||
|
|
||
|
#include "prolang.h"
|
||
|
|
||
|
#include "array.h"
|
||
|
#include "backend.h"
|
||
|
#include "closure.h"
|
||
|
#include "exec.h"
|
||
|
#include "gcollect.h"
|
||
|
#include "interpret.h"
|
||
|
#include "instrs.h"
|
||
|
#include "lex.h"
|
||
|
#include "main.h"
|
||
|
#include "mapping.h"
|
||
|
#include "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 "../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 <s> 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 <t> to type(flags) <id> and the struct
|
||
|
* typeobject pointer to <ptr>.
|
||
|
*/
|
||
|
|
||
|
#define NEW_INHERITED_INDEX (0xfffff)
|
||
|
/* While inserting a new inherit, this marks the newly inherited
|
||
|
* things.
|
||
|
*/
|
||
|
|
||
|
/* Values for %type <number> 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 <n>.
|
||
|
* 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 <n>.
|
||
|
*/
|
||
|
|
||
|
#define FUNCTION_COUNT (mem_block[A_FUNCTIONS].current_size / sizeof (function_t))
|
||
|
/* Number of function_t stored so far in A_FUNCTIONS.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#define INHERIT_COUNT (mem_block[A_INHERITS].current_size / sizeof(inherit_t))
|
||
|
/* Number of inherit_t stored so far in A_INHERITS.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#define ARGUMENT_INDEX(n) ((unsigned short *)mem_block[A_ARGUMENT_INDEX].block)[n]
|
||
|
/* Lookup the start index of the types for function number <n>.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#define ARGTYPE_COUNT (mem_block[A_ARGUMENT_TYPES].current_size / sizeof(vartype_t))
|
||
|
/* Number of vartype_t stored so far in A_ARGUMENT_TYPES.
|
||
|
*/
|
||
|
|
||
|
#define ARGUMENT_TYPE(n) ((vartype_t *)mem_block[A_ARGUMENT_TYPES].block)[n]
|
||
|
/* Index the vartype_t <n>.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#define NV_VARIABLE(n) ((variable_t *)mem_block[A_VARIABLES].block + (n))
|
||
|
/* Return the variable_t* for the non-virtual variable <n>.
|
||
|
*/
|
||
|
|
||
|
#define NV_VARIABLE_COUNT (mem_block[A_VARIABLES].current_size / sizeof(variable_t))
|
||
|
#define V_VARIABLE_COUNT (mem_block[A_VIRTUAL_VAR].current_size / sizeof(variable_t))
|
||
|
/* Number of variables stored so var in A_VARIABLES resp. A_VIRTUAL_VAR.
|
||
|
*/
|
||
|
|
||
|
#define V_VARIABLE(n) ((variable_t *)mem_block[A_VIRTUAL_VAR].block + \
|
||
|
(n) - VIRTUAL_VAR_TAG)
|
||
|
/* Return the variable_t* for the virtual variable <n> (still including
|
||
|
* the offset).
|
||
|
*/
|
||
|
|
||
|
#define VARIABLE(n) ((n) & VIRTUAL_VAR_TAG ? V_VARIABLE(n) : NV_VARIABLE(n))
|
||
|
/* Return the variable_t* for the variable <n>, virtual or not.
|
||
|
*/
|
||
|
|
||
|
#define INHERIT(n) ((inherit_t *)mem_block[A_INHERITS].block)[n]
|
||
|
/* Index the inherit_t <n>.
|
||
|
*/
|
||
|
|
||
|
#define INHERIT_COUNT (mem_block[A_INHERITS].current_size / sizeof(inherit_t))
|
||
|
/* Return the number of inherits encountered so far.
|
||
|
*/
|
||
|
|
||
|
#ifdef USE_STRUCTS
|
||
|
#define STRUCT_DEF(n) ((struct_def_t *)mem_block[A_STRUCT_DEFS].block)[n]
|
||
|
/* Index the struct_def_t <n>.
|
||
|
*/
|
||
|
|
||
|
#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 <n>.
|
||
|
*/
|
||
|
|
||
|
#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 <n>.
|
||
|
*/
|
||
|
|
||
|
#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 <n>.
|
||
|
*/
|
||
|
|
||
|
#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 <n>.
|
||
|
*/
|
||
|
|
||
|
%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 <n>, 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 <n>, 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 */
|
||
|
mp_uint addr;
|
||
|
/* Address of CLEAR_LOCALS instruction, needed for backpatching */
|
||
|
};
|
||
|
|
||
|
static block_scope_t block_scope[COMPILER_STACK_SIZE];
|
||
|
/* A static stack of block scopes, indexed by <block_depth>-1.
|
||
|
* TODO: This should be dynamic.
|
||
|
*/
|
||
|
|
||
|
static int block_depth;
|
||
|
/* The nesting depth of blocks ( '{ ... }' ), used to distinguish
|
||
|
* local variable definitions.
|
||
|
* block_depth = 0: not used, would mean 'global'
|
||
|
* = 1: function arguments
|
||
|
* = 2: function local variables
|
||
|
* > 2: vars of nested blocks within the function
|
||
|
*/
|
||
|
|
||
|
static Bool use_local_scopes;
|
||
|
/* Copy of pragma_use_local_scopes, updated at every entry into
|
||
|
* a function. Reason is that the pragma must not change inside
|
||
|
* a function.
|
||
|
*/
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
#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 <comp_stack>.
|
||
|
*/
|
||
|
|
||
|
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 <argument_level>, 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 <str>: usually generate the error message and log it.
|
||
|
* If this is the first error in this file, account the wizard with an error.
|
||
|
* If too many errors occured already, do nothing.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
char *context;
|
||
|
|
||
|
if (num_parse_error > 5)
|
||
|
return;
|
||
|
context = lex_error_context();
|
||
|
fprintf(stderr, "%s %s line %d: %s%s.\n"
|
||
|
, time_stamp(), current_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 <str>: usually generate the warning message and
|
||
|
* log it.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
char *context;
|
||
|
|
||
|
context = lex_error_context();
|
||
|
fprintf(stderr, "%s %s line %d: Warning: %s%s.\n"
|
||
|
, time_stamp(), current_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 <size>, add it at the head of the last_yalloced
|
||
|
* list, and return the pointer.
|
||
|
*
|
||
|
* Together with yfree(), this allocator is able to free intermediate
|
||
|
* results in the epilog() which were thrown away due to an error.
|
||
|
* TODO: A stack'ish mempool could do this?
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
char **p;
|
||
|
|
||
|
p = xalloc(size+sizeof(char*));
|
||
|
if (!p)
|
||
|
{
|
||
|
fatal("Out of memory in compiler.\n");
|
||
|
return NULL;
|
||
|
}
|
||
|
*p++ = last_yalloced;
|
||
|
last_yalloced = (char *)p;
|
||
|
return p;
|
||
|
} /* yalloc() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static void
|
||
|
yfree (void *block)
|
||
|
|
||
|
/* Free the block last allocated by yalloc().
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
char **p;
|
||
|
|
||
|
p = (char **)block;
|
||
|
if (p != (char **)last_yalloced)
|
||
|
{
|
||
|
debug_message("%s Block mismatch", time_stamp());
|
||
|
return;
|
||
|
}
|
||
|
last_yalloced = *--p;
|
||
|
xfree(p);
|
||
|
} /* yfree() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static char *
|
||
|
ystring_copy (char *str)
|
||
|
|
||
|
/* Duplicate the string <str> using yalloc() and return the new one.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
char *p;
|
||
|
|
||
|
p = yalloc(strlen(str)+1);
|
||
|
strcpy(p, str);
|
||
|
return p;
|
||
|
} /* ystring_copy() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static void
|
||
|
add_string_constant (void)
|
||
|
|
||
|
/* Add the string <last_lex_string> to the string in <last_string_constant>.
|
||
|
* This is used to optimize "foo" + "bar" constructs.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
string_t *tmp;
|
||
|
|
||
|
tmp = mstr_add(last_string_constant, last_lex_string);
|
||
|
if (!tmp)
|
||
|
{
|
||
|
yyerrorf("Out of memory for string literal (%ld bytes)"
|
||
|
, (long)(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)
|
||
|
{
|
||
|
yyerror("Out of memory for string literal (%ld bytes)");
|
||
|
}
|
||
|
} /* add_string_constant() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static char *
|
||
|
realloc_mem_block (mem_block_t *mbp, mp_int size)
|
||
|
|
||
|
/* Resize memblock <mbp> to hold at least <size> bytes, but at least
|
||
|
* double its current size.
|
||
|
*
|
||
|
* Return NULL when out of memory, or a pointer to the newly allocated
|
||
|
* memory area (ie. mbp->block).
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
mp_uint max_size;
|
||
|
char *p;
|
||
|
|
||
|
max_size = mbp->max_size;
|
||
|
do {
|
||
|
max_size *= 2;
|
||
|
} while ((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 <size> bytes at the current position in memory area <n>.
|
||
|
* 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 <data> block of <size> bytes to the memory area <n>.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
mem_block_t *mbp = &mem_block[n];
|
||
|
|
||
|
if (size)
|
||
|
{
|
||
|
if (mbp->current_size + size > mbp->max_size)
|
||
|
{
|
||
|
if (!realloc_mem_block(mbp, mbp->current_size + size))
|
||
|
return;
|
||
|
}
|
||
|
memcpy(mbp->block + mbp->current_size, data, size);
|
||
|
mbp->current_size += size;
|
||
|
}
|
||
|
} /* add_to_mem_block() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
#define byte_to_mem_block(n, b) \
|
||
|
((void)((mem_block[n].current_size == mem_block[n].max_size \
|
||
|
? !!realloc_mem_block(&mem_block[n],0) : 1) \
|
||
|
? (mem_block[n].block[mem_block[n].current_size++] = (char)(b)) \
|
||
|
: 0)\
|
||
|
)
|
||
|
/* Add the byte <b> to the memory area <n>, which is resized
|
||
|
* if necessary.
|
||
|
*/
|
||
|
|
||
|
/* ============================== TYPES ============================== */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
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 <e> and <t> 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 <e> and <t> 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 <e> and <t> 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 <e> and <t> 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 <e> and <t> 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 <e> and <t> 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 <t> is a proper redefinition of <e>.
|
||
|
* This is the case if <e> and <t> are compatible base types,
|
||
|
* or if one of them is *ANY.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
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 <flags>.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
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 <type>.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
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 <type>.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
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 %ld: %s line %d\n"
|
||
|
, (long)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 "(<type1> vs. <type2>)".
|
||
|
*/
|
||
|
{
|
||
|
static char buff[100];
|
||
|
|
||
|
strcpy(buff, "(");
|
||
|
strcat(buff, get_type_name(type1));
|
||
|
strcat(buff, " vs ");
|
||
|
strcat(buff, get_type_name(type2));
|
||
|
strcat(buff, ")");
|
||
|
return buff;
|
||
|
} /* get_two_types() */
|
||
|
|
||
|
#ifdef USE_STRUCTS
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static char *
|
||
|
get_two_vtypes (vartype_t type1, vartype_t type2)
|
||
|
|
||
|
/* Return (in a static buffer) the text "(<type1> vs. <type2>)".
|
||
|
*/
|
||
|
{
|
||
|
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 "<str>: <type>".
|
||
|
*/
|
||
|
{
|
||
|
char *p;
|
||
|
|
||
|
p = get_type_name(type);
|
||
|
yyerrorf("%s: \"%s\"", str, p);
|
||
|
} /* type_error() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static void
|
||
|
argument_type_error (int instr, fulltype_t type)
|
||
|
|
||
|
/* Generate an yyerror with the message "Bad argument to <instr>: <type>".
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
char *p;
|
||
|
|
||
|
p = get_type_name(type);
|
||
|
yyerrorf("Bad argument to %s: \"%s\"", instrs[instr].name, p);
|
||
|
} /* argument_type_error() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static 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 <t1> and <t2> and return TRUE if they are compatible.
|
||
|
* Rules:
|
||
|
* - every type is compatible to itself
|
||
|
* - TYPE_UNKNOWN is incompatible to everything
|
||
|
* - TYPE_ANY is compatible to everything
|
||
|
* - two POINTER types are compatible if at least one is *TYPE_ANY.
|
||
|
*
|
||
|
* If <is_assign> is true, it is assumed that <t2> will be assigned
|
||
|
* to a var of <t1>, and the following rules have to match as well:
|
||
|
* - a struct <t1> is compatible to a derived struct <t2>.
|
||
|
* - if <t1> is a struct, <t2> 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 <n> 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 <n>th last argument from the stack.
|
||
|
* <n> must be >= 1.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
return
|
||
|
&((vartype_t *)
|
||
|
(type_of_arguments.block + type_of_arguments.current_size))[ - n];
|
||
|
} /* get_arguments_type_start() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static INLINE void
|
||
|
check_aggregate_types (int n)
|
||
|
|
||
|
/* The last <n> types on the argument stack are an aggregate type.
|
||
|
* Combine the single types and make sure that none is a reference type.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
vartype_t *argp;
|
||
|
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 <pubProg>::<pubFun>() shadows the
|
||
|
* private function <privProg>::<privFun>().
|
||
|
* Both <pubProg> and <privProg> 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 <size> more bytes can be stored in it.
|
||
|
*
|
||
|
* Return NULL when out of memory, or a pointer to the (possibly newly
|
||
|
* allocated) memory area (ie. mem_block[A_PROGRAM].block).
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
mem_block_t * mbp = &mem_block[A_PROGRAM];
|
||
|
mp_uint new_size = mbp->current_size + size;
|
||
|
|
||
|
if (new_size <= mbp->max_size)
|
||
|
return mbp->block;
|
||
|
return realloc_mem_block(mbp, new_size);
|
||
|
} /* realloc_a_program() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
|
||
|
#define ins_byte(b) byte_to_mem_block(A_PROGRAM, b)
|
||
|
|
||
|
#ifndef ins_byte
|
||
|
|
||
|
static INLINE void
|
||
|
ins_byte (unsigned char b)
|
||
|
|
||
|
/* Add the byte <b> to the A_PROGRAM area.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
if (mem_block[A_PROGRAM].current_size == mem_block[A_PROGRAM].max_size )
|
||
|
{
|
||
|
if (!realloc_a_program(1))
|
||
|
{
|
||
|
yyerrorf("Out of memory: program size %lu\n"
|
||
|
, mem_block[A_PROGRAM].current_size + 1);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
mem_block[A_PROGRAM].block[mem_block[A_PROGRAM].current_size++] = b;
|
||
|
} /* ins_byte() */
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static void
|
||
|
ins_f_code (unsigned int b)
|
||
|
|
||
|
/* Add the instruction <b> to the A_PROGRAM area, taking care of encoding
|
||
|
* multi-byte instructions properly.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
if (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 <l> 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 %lu\n"
|
||
|
, mem_block[A_PROGRAM].current_size + 2);
|
||
|
}
|
||
|
} /* ins_short() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static void
|
||
|
upd_short (mp_uint offset, long l)
|
||
|
|
||
|
/* Store the 2-byte number <l> at <offset> 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 <offset> in the A_PROGRAM area.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
short l;
|
||
|
char *dest;
|
||
|
|
||
|
dest = mem_block[A_PROGRAM].block + offset;
|
||
|
GET_SHORT(l, dest);
|
||
|
return l;
|
||
|
} /* read_short() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static void
|
||
|
upd_jump_offset (mp_uint offset, long l)
|
||
|
|
||
|
/* Store the 3-byte number <l> at <offset> in the A_PROGRAM are in
|
||
|
* a fixed byteorder.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
char *dest;
|
||
|
|
||
|
dest = mem_block[A_PROGRAM].block + offset;
|
||
|
PUT_3BYTE(dest, l);
|
||
|
} /* upd_jump_offset() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static void
|
||
|
ins_int32 (int32 l)
|
||
|
|
||
|
/* Add the 4-byte number <l> to the A_PROGRAM area in a fixed byteorder.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
if (realloc_a_program(4))
|
||
|
{
|
||
|
mp_uint current_size;
|
||
|
char *dest;
|
||
|
|
||
|
current_size = CURRENT_PROGRAM_SIZE;
|
||
|
CURRENT_PROGRAM_SIZE = current_size + 4;
|
||
|
dest = mem_block[A_PROGRAM].block + current_size;
|
||
|
PUT_INT32(dest, l);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
yyerrorf("Out of memory: program size %lu\n"
|
||
|
, mem_block[A_PROGRAM].current_size + 4);
|
||
|
}
|
||
|
} /* ins_int32() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static void
|
||
|
upd_int32 (mp_uint offset, int32 l)
|
||
|
|
||
|
/* Store the 4-byte number <l> at <offset> in the A_PROGRAM are in
|
||
|
* a fixed byteorder.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
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 <offset> in the A_PROGRAM area.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
int32 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 <num> 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 <n> 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 <i> to the program
|
||
|
* add_byte(b): to add byte <b> to the program
|
||
|
* add_short(s): to add short <s> 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 <address> onto the compiler stack.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
if (comp_stackp >= COMPILER_STACK_SIZE)
|
||
|
{
|
||
|
yyerror("Compiler stack overflow");
|
||
|
/* Don't store the address, but keep proper track of the depth.
|
||
|
*/
|
||
|
comp_stackp++;
|
||
|
return;
|
||
|
}
|
||
|
comp_stack[comp_stackp++] = address;
|
||
|
} /* push_explicit() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static p_int
|
||
|
pop_address (void)
|
||
|
|
||
|
/* Pop the most recent stored address from the compiler stack and return
|
||
|
* it.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
if (comp_stackp == 0)
|
||
|
fatal("Compiler stack underflow.\n");
|
||
|
if (comp_stackp > COMPILER_STACK_SIZE)
|
||
|
{
|
||
|
/* Nothing to retrieve, but keep track of the depth */
|
||
|
--comp_stackp;
|
||
|
return 0;
|
||
|
}
|
||
|
return comp_stack[--comp_stackp];
|
||
|
} /* pop_address() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static Bool
|
||
|
fix_branch (int ltoken, p_int dest, p_int loc)
|
||
|
|
||
|
/* Backpatch a branch instruction at <loc> to jump to <dest>.
|
||
|
* If the offset exceeds the 255 range, the branch instruction is changed
|
||
|
* into its long-branch variant <ltoken>.
|
||
|
*
|
||
|
* Return TRUE if the long branch had to be used, FALSE otherwise.
|
||
|
* TODO: This really confuses the line number detection code, as suddenly
|
||
|
* TODO:: the recorded offset are no longer accurate.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
p_int offset; /* The branch offset */
|
||
|
|
||
|
offset = dest - (loc +1);
|
||
|
|
||
|
if (offset > 0xff)
|
||
|
{
|
||
|
/* We need a long branch. That also means that we have to
|
||
|
* move the following code and adapt remembered addresses.
|
||
|
*/
|
||
|
p_int i, j;
|
||
|
bytecode_p p;
|
||
|
|
||
|
mem_block[A_PROGRAM].block[loc] = 0; /* Init it */
|
||
|
|
||
|
/* Update the break address */
|
||
|
if ( current_break_address > loc
|
||
|
&& !(current_break_address & (BREAK_ON_STACK|BREAK_DELIMITER) ) )
|
||
|
{
|
||
|
for (i = current_break_address & 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: %ld 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 <size> more bytes
|
||
|
* in the program area.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
if (realloc_a_program(size))
|
||
|
{
|
||
|
CURRENT_PROGRAM_SIZE += size;
|
||
|
return PROGRAM_BLOCK + CURRENT_PROGRAM_SIZE - size;
|
||
|
}
|
||
|
yyerrorf("Out of memory: program size %lu\n"
|
||
|
, mem_block[A_PROGRAM].current_size + size);
|
||
|
return NULL;
|
||
|
} /* yyget_space() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static void
|
||
|
yymove_switch_instructions (int len, p_int blocklen)
|
||
|
|
||
|
/* Callback function for switch: move the <blocklen> bytecodes at <switch_pc>
|
||
|
* back by <len> bytes to <switch_pc>+<len>. A continue address in the
|
||
|
* affected area is corrected.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
mp_int i, j;
|
||
|
|
||
|
if (realloc_a_program(len))
|
||
|
{
|
||
|
CURRENT_PROGRAM_SIZE += len;
|
||
|
|
||
|
/* Adjust the continue address, if any */
|
||
|
if ( (current_continue_address & CONTINUE_ADDRESS_MASK) > switch_pc
|
||
|
&& !(current_continue_address & CONTINUE_DELIMITER ) )
|
||
|
{
|
||
|
for(i = current_continue_address & CONTINUE_ADDRESS_MASK;
|
||
|
(j=read_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 %lu\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 <s1> in file <s2> at
|
||
|
* lines <line1> and <line2>.
|
||
|
* <s1> may contain one '%s' to insert s2, <s2> may contain one or
|
||
|
* or two '%d' to insert line1 and line2.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
char buff[100];
|
||
|
|
||
|
sprintf(buff, s2, line1, line2);
|
||
|
yyerrorf(s1, buff);
|
||
|
} /* yycerrorl() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static void
|
||
|
update_lop_branch ( p_uint address, int instruction )
|
||
|
|
||
|
/* <address> points to the branch offset value of an LAND/LOR operation,
|
||
|
* currently set to 0. Update that offset to branch to the current end
|
||
|
* of the program.
|
||
|
*
|
||
|
* If that branch is too long, the code is rewritten:
|
||
|
*
|
||
|
* Original: Rewritten:
|
||
|
*
|
||
|
* <expr1> <expr1>
|
||
|
* LOR/LAND l DUP
|
||
|
* <expr2> LBRANCH_<instruction>
|
||
|
* l: POP_VALUE
|
||
|
* <expr2>
|
||
|
* l:
|
||
|
*
|
||
|
* The extra DUP compensates the svalue the LBRANCH eats.
|
||
|
* The LBRANCH_<instruction> needs to be passed suiting the logical
|
||
|
* operator: LBRANCH_WHEN_ZERO for LAND, LBRANCH_WHEN_NON_ZERO for LOR.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
p_int offset;
|
||
|
|
||
|
last_expression = -1;
|
||
|
|
||
|
offset = mem_block[A_PROGRAM].current_size - ( address + 1);
|
||
|
if (offset > 0xff)
|
||
|
{
|
||
|
/* A long branch is needed */
|
||
|
|
||
|
int i;
|
||
|
bytecode_p p;
|
||
|
|
||
|
ins_short(0);
|
||
|
ins_byte(0);
|
||
|
p = PROGRAM_BLOCK + mem_block[A_PROGRAM].current_size-1;
|
||
|
for (i = offset; --i >= 0; --p )
|
||
|
*p = p[-3];
|
||
|
p[-4] = F_DUP;
|
||
|
p[-3] = instruction;
|
||
|
upd_short(address+1, offset+3);
|
||
|
if (offset > 0x7ffc)
|
||
|
yyerrorf("Compiler limit: Too much code to skip for ||/&&:"
|
||
|
" %ld 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 <depth>, and adjust the counters.
|
||
|
* A <depth> of 0 is equivalent to calling free_all_local_names().
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
ident_t *q;
|
||
|
|
||
|
if (!depth)
|
||
|
{
|
||
|
free_all_local_names();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Are the locals of the given depth? */
|
||
|
if (!all_locals || all_locals->u.local.depth < depth)
|
||
|
return;
|
||
|
|
||
|
if (all_locals->u.local.depth > depth)
|
||
|
fatal("List of locals clobbered: depth %d, block_depth %d\n"
|
||
|
, all_locals->u.local.depth, depth);
|
||
|
|
||
|
while (all_locals != NULL && all_locals->u.local.depth >= depth)
|
||
|
{
|
||
|
q = all_locals;
|
||
|
all_locals = q->next_all;
|
||
|
free_shared_identifier(q);
|
||
|
current_number_of_locals--;
|
||
|
free_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 <ident> with the type <type> on
|
||
|
* the scope depth <depth>. The references of <type> are NOT adopted.
|
||
|
#ifndef USE_NEW_INLINES
|
||
|
* If <may_shadow> 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 <ident>, to <type> at <depth>; the references
|
||
|
* of <type> 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 <ident> with the type <type> for the
|
||
|
* currently compiled inline closure. The references of <type> are NOT adopted.
|
||
|
* <num> 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 <ident>. 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 *<pType>.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
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 <depth>.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
block_scope[depth-1].num_locals = 0;
|
||
|
block_scope[depth-1].first_local = current_number_of_locals;
|
||
|
block_scope[depth-1].addr = 0;
|
||
|
} /* init_scope() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static void
|
||
|
enter_block_scope (void)
|
||
|
|
||
|
/* Enter a new scope and initialize it (if use_local_scopes requires it).
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
if (block_depth == COMPILER_STACK_SIZE)
|
||
|
yyerror("Too deep nesting of local blocks.\n");
|
||
|
|
||
|
if (use_local_scopes)
|
||
|
{
|
||
|
block_depth++;
|
||
|
init_scope(block_depth);
|
||
|
}
|
||
|
} /* enter_block_scope() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static void
|
||
|
leave_block_scope (void)
|
||
|
|
||
|
/* Leave the current scope (if use_local_scopes requires it), freeing
|
||
|
* all local names defined in that scope.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
if (use_local_scopes)
|
||
|
{
|
||
|
free_local_names(block_depth);
|
||
|
block_depth--;
|
||
|
}
|
||
|
} /* leave_block_scope() */
|
||
|
|
||
|
|
||
|
/* ====================== GLOBALS and FUNCTIONS ====================== */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static unsigned short
|
||
|
store_argument_types ( int num_arg )
|
||
|
|
||
|
/* Store the <num_arg> argument types from global type_of_locals[] into
|
||
|
* the proper memblock and return the new argument start index.
|
||
|
* It is task of the caller to store this start index where it belongs.
|
||
|
*
|
||
|
* If exact_types are not required, the function just returns
|
||
|
* INDEX_START_NONE.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
unsigned short argument_start_index;
|
||
|
|
||
|
/* Store the function arguments, if required.
|
||
|
*/
|
||
|
if (!exact_types.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 <p> with the characteristics <num_arg>, <num_local>,
|
||
|
* program <offset>, <flags> and <type>.
|
||
|
* The references of <type> 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 - <complete> is FALSE then. Additionally,
|
||
|
* the function is called as well after a functionbody has been parsed,
|
||
|
* <complete> is TRUE then.
|
||
|
*
|
||
|
* This function is called at least twice for all function definitions:
|
||
|
* first as prototype (flags & NAME_PROTOTYPE) when the function def is
|
||
|
* encountered, then a second time for real when the function has been
|
||
|
* completed. Explicite prototypes can cause additional calls.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
int num;
|
||
|
function_t fun;
|
||
|
unsigned short argument_start_index;
|
||
|
|
||
|
/* 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 <name> of type <type>.
|
||
|
* The references of <type> 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 <name> is inherited virtually with number <n>.
|
||
|
* Redeclare it from its original type to <type>.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
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 <p> is a global variable.
|
||
|
* If yes, return the index of that variable, -1 otherwise.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
int r;
|
||
|
|
||
|
if (p->type != I_TYPE_GLOBAL
|
||
|
|| (r = p->u.global.variable) < 0)
|
||
|
{
|
||
|
yyerrorf("Variable %s not declared !", 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: <type> [*] <name>
|
||
|
* 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: <type> [*] <name> = <expr>
|
||
|
* It will be called after the call to define_global_variable().
|
||
|
* It assigns the result of <expr> 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 <start>.
|
||
|
* The caller has to make sure that there is enough space.
|
||
|
* The references of <returntype> 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 <ix> in program <prog>
|
||
|
* (which may be inherited) and store it in *<fun_p>. It is the callers
|
||
|
* responsibility to set <fun_p>->flags _before_ calling this function.
|
||
|
*
|
||
|
* In particular, this function sets these <fun_p> 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 '<type> <functionname>' 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_inline> is TRUE, the function to be compiled is an inline closure,
|
||
|
* which requires a slightly different handling. This function is called
|
||
|
* after 'func <type>' 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 '<type> <name> ( <args> ) of a function definition,
|
||
|
* this function creates the function prototype entry.
|
||
|
*
|
||
|
* If <is_inline> is TRUE, the function to be compiled is an inline closure,
|
||
|
* which requires a slightly different handling. This function is called
|
||
|
* after 'func <type> <arguments> <context>' 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_inline> 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 <value>' 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 <p> with the visibility <flags>.
|
||
|
* If <proto> is TRUE, the function is called for a struct forward
|
||
|
* declaration; if <proto> 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 <name> and return its index. Return -1 if not found.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
ident_t * p;
|
||
|
|
||
|
p = find_shared_identifier(get_txt(name), I_TYPE_GLOBAL, 0);
|
||
|
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 <name> with type <type> to A_STRUCT_MEMBERS for the
|
||
|
* to the most recently defined struct <current_struct>.
|
||
|
* If <from_struct> 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 <current_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 <length> expressions in order
|
||
|
* to create a struct literal of struct <pdef>.
|
||
|
* Analyze the <list> 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 <pType> 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 <name>.
|
||
|
* Result:
|
||
|
* >= 0: Index of the struct in A_STRUCT_DEFS, *<pNum> 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 #%d: prev %d\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: %ld, 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: %d, context types: %d\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 <bAbort> 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 #%d: prev %d, end %ld, start %ld, length %ld, function %d pc %ld\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 %ld\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 %ld, length %ld, to %ld\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: %ld\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 %ld, from %ld length %ld\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 %ld, length %ld, to %ld\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: %d, context types: %d\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(): %d 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: #%d: start %ld, length %ld, function %d: new start %ld\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);
|
||
|
|
||
|
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 %ld, li_length %ld, new li_start %ld\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 <type>', 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);
|
||
|
|
||
|
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 <type> <arguments> <context>', 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: %ld align to %ld\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 %lu\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 <type> <arguments> <block>', 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(); /* Argument scope */
|
||
|
leave_block_scope(); /* 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(); /* Argument scope */
|
||
|
leave_block_scope(); /* 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 <str> to the strings used by the program.
|
||
|
* The function takes care that the same string is not stored twice.
|
||
|
* Result is the index of the string in the table, the function
|
||
|
* adopts the reference of <str>.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
mp_uint str_size, next_size;
|
||
|
long hash;
|
||
|
char mask, *tagp;
|
||
|
int i, *indexp;
|
||
|
|
||
|
/* Compute the hash and the tagmask for the hash table */
|
||
|
/* TODO: This assumes 32-Bit pointers */
|
||
|
hash = (long)str ^ (long)str >> 16;
|
||
|
hash = (hash ^ hash >> 8);
|
||
|
mask = 1 << (hash & 7);
|
||
|
hash = hash & 0xff;
|
||
|
|
||
|
indexp = &prog_string_indizes[hash];
|
||
|
tagp = &prog_string_tags[hash >> 3];
|
||
|
|
||
|
if (*tagp & mask)
|
||
|
{
|
||
|
/* There is a hash chain for this hash: search the
|
||
|
* string in there.
|
||
|
*/
|
||
|
i = *indexp;
|
||
|
for(;;)
|
||
|
{
|
||
|
if ( PROG_STRING(i) == str )
|
||
|
{
|
||
|
free_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 <str> is a new string.
|
||
|
*/
|
||
|
*tagp |= mask;
|
||
|
i = -1;
|
||
|
}
|
||
|
|
||
|
/* Add a totally new string */
|
||
|
|
||
|
str_size = mem_block[A_STRINGS].current_size;
|
||
|
next_size = mem_block[A_STRING_NEXT].current_size;
|
||
|
|
||
|
/* Make sure we have enough memory */
|
||
|
if (str_size + sizeof(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_AINDEX: [>x]
|
||
|
* F_RANGE: [ x.. y]
|
||
|
* F_RN_RANGE: [<x.. y]
|
||
|
* F_NR_RANGE: [ x..<y]
|
||
|
* F_RR_RANGE: [<x..<y]
|
||
|
* F_AN_RANGE: [>x.. y]
|
||
|
* F_AR_RANGE: [>x..<y]
|
||
|
* F_NA_RANGE: [ x..>y]
|
||
|
* F_RA_RANGE: [<x..>y]
|
||
|
* F_AA_RANGE: [>x..>y]
|
||
|
* F_NX_RANGE: [ x.. ]
|
||
|
* F_RX_RANGE: [<x.. ]
|
||
|
* F_AX_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: "<super>::<func>" */
|
||
|
|
||
|
struct {
|
||
|
int simul_efun; /* -1, or index of the simul_efun */
|
||
|
p_int start; /* Address of the function call */
|
||
|
} function_call_head;
|
||
|
/* Used to save address and possible sefun-index over
|
||
|
* the argument parsing in a function call.
|
||
|
*/
|
||
|
|
||
|
%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 <number> L_NUMBER constant
|
||
|
%type <float_number> L_FLOAT
|
||
|
%type <closure> L_CLOSURE
|
||
|
%type <symbol> L_SYMBOL
|
||
|
%type <number> L_QUOTED_AGGREGATE
|
||
|
%type <ident> L_IDENTIFIER L_INLINE_FUN L_LOCAL
|
||
|
%type <typeflags> optional_star type_modifier type_modifier_list
|
||
|
%type <fulltype> type
|
||
|
%type <fulltype> opt_basic_type basic_type
|
||
|
%type <fulltype> non_void_type opt_basic_non_void_type basic_non_void_type
|
||
|
%type <fulltype> name_list local_name_list
|
||
|
%type <inh_flags> inheritance_qualifier inheritance_qualifiers
|
||
|
%type <typeflags> inheritance_modifier_list inheritance_modifier
|
||
|
%ifdef USE_NEW_INLINES
|
||
|
%type <fulltype> inline_opt_type
|
||
|
%endif /* USE_NEW_INLINES */
|
||
|
%type <type> decl_cast cast
|
||
|
%type <lrvalue> note_start comma_expr expr0 expr4
|
||
|
%type <lrvalue> function_call
|
||
|
%ifdef USE_NEW_INLINES
|
||
|
%type <lrvalue> inline_func
|
||
|
%else /* USE_NEW_INLINES */
|
||
|
%type <lrvalue> inline_fun
|
||
|
%endif /* USE_NEW_INLINES */
|
||
|
%type <lrvalue> catch sscanf
|
||
|
%type <lrvalue> for_init_expr for_expr
|
||
|
%type <lrvalue> comma_expr_decl expr_decl
|
||
|
%ifdef USE_PARSE_COMMAND
|
||
|
%type <lrvalue> parse_command
|
||
|
%endif
|
||
|
%type <lvalue> lvalue name_lvalue local_name_lvalue foreach_var_lvalue
|
||
|
%type <index> index_range index_expr
|
||
|
%type <case_label> case_label
|
||
|
%type <address> optional_else
|
||
|
%type <string> anchestor
|
||
|
%type <sh_string> call_other_name identifier
|
||
|
%ifdef USE_STRUCTS
|
||
|
%type <fulltype> member_name_list
|
||
|
%type <struct_init_member> struct_init
|
||
|
%type <struct_init_list> opt_struct_init opt_struct_init2
|
||
|
%type <sh_string> struct_member_name
|
||
|
%endif /* USE_STRUCTS */
|
||
|
%type <function_name> function_name
|
||
|
|
||
|
|
||
|
/* Special uses of <number> */
|
||
|
|
||
|
%type <number> function_body
|
||
|
/* program address or -1 */
|
||
|
|
||
|
%type <number> argument argument_list lvalue_list
|
||
|
%ifdef USE_NEW_INLINES
|
||
|
%type <number> inline_opt_args
|
||
|
%endif /* USE_NEW_INLINES */
|
||
|
/* number of arguments */
|
||
|
|
||
|
%type <number> expr_list expr_list3 e_expr_list2 expr_list2
|
||
|
/* Number of expressions in an expression list */
|
||
|
|
||
|
%type <number> m_expr_values
|
||
|
/* Number of values for a mapping entry (ie the 'width') */
|
||
|
|
||
|
%type <number> L_ASSIGN
|
||
|
/* Instruction code of the assignment, e.g. F_ADD_EQ */
|
||
|
|
||
|
%type <number> 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 <number> foreach_vars
|
||
|
/* Number of variables given to foreach
|
||
|
*/
|
||
|
|
||
|
%type <number> opt_catch_mods opt_catch_mod_list opt_catch_modifier
|
||
|
/* Bitflags for catch() modes: CATCH_FLAG_xxx from simulate.h
|
||
|
*/
|
||
|
|
||
|
/* Special uses of <numbers> */
|
||
|
|
||
|
%type <numbers> condStart
|
||
|
/* [0]: current_break_address
|
||
|
* [1]: address of the branch-offset of the if
|
||
|
*/
|
||
|
|
||
|
%type <numbers> m_expr_list m_expr_list2
|
||
|
/* [0]: number of entries in a mapping literal
|
||
|
* [1]: width of the mapping literal
|
||
|
*/
|
||
|
|
||
|
/* Special uses of <lrvalue> */
|
||
|
|
||
|
%type <lrvalue> pre_inc_dec
|
||
|
/* .code: The instruction F_PRE_INC or F_PRE_DEC.
|
||
|
* .start: The CURRENT_PROGRAM_SIZE where this inst was encountered.
|
||
|
*/
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
|
||
|
%right L_ASSIGN
|
||
|
%right '?'
|
||
|
%left L_LOR
|
||
|
%left L_LAND
|
||
|
%left '|'
|
||
|
%left '^'
|
||
|
%left '&'
|
||
|
%left L_EQ L_NE
|
||
|
%left '<' L_LE '>' L_GE
|
||
|
%left L_LSH L_RSH L_RSHL
|
||
|
%left '+' '-'
|
||
|
%left '*' '/' '%'
|
||
|
%right '~' L_NOT
|
||
|
%nonassoc L_INC L_DEC
|
||
|
%left L_ARROW '['
|
||
|
%%
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
|
||
|
all: program;
|
||
|
|
||
|
program:
|
||
|
program def possible_semi_colon
|
||
|
| /* empty */ ;
|
||
|
|
||
|
possible_semi_colon:
|
||
|
/* empty */
|
||
|
| ';' { yywarn("Extra ';' ignored"); };
|
||
|
|
||
|
note_start: { $$.start = CURRENT_PROGRAM_SIZE; };
|
||
|
|
||
|
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
|
||
|
|
||
|
/* Function prototypes
|
||
|
* Function definitions
|
||
|
* Variable definitions
|
||
|
* Inheritance
|
||
|
* Default visibility
|
||
|
*/
|
||
|
|
||
|
def: type optional_star L_IDENTIFIER /* Function definition or prototype */
|
||
|
|
||
|
{
|
||
|
$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);
|
||
|
$<number>$ = CURRENT_PROGRAM_SIZE;
|
||
|
if (realloc_a_program(FUNCTION_HDR_SIZE))
|
||
|
{
|
||
|
CURRENT_PROGRAM_SIZE += FUNCTION_HDR_SIZE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
yyerrorf("Out of memory: program size %lu\n"
|
||
|
, mem_block[A_PROGRAM].current_size + FUNCTION_HDR_SIZE);
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
block
|
||
|
|
||
|
{ $$ = $<number>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 %ld\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 %ld\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 %ld\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 %ld\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 %ld\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;
|
||
|
#ifdef DEBUG_INLINES
|
||
|
printf("DEBUG: Before comma_expr: program size %ld\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 %ld\n", CURRENT_PROGRAM_SIZE);
|
||
|
#endif /* DEBUG_INLINES */
|
||
|
$$.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));
|
||
|
free_shared_identifier($2);
|
||
|
}
|
||
|
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);
|
||
|
if ($3->type == I_TYPE_UNKNOWN)
|
||
|
free_shared_identifier($3);
|
||
|
|
||
|
$$ = $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);
|
||
|
if ($4->type == I_TYPE_UNKNOWN)
|
||
|
free_shared_identifier($4);
|
||
|
|
||
|
$$ = $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;
|
||
|
}
|
||
|
|
||
|
/* Free the identifier again if this statement generated it */
|
||
|
if (last_identifier->type == I_TYPE_UNKNOWN)
|
||
|
free_shared_identifier(last_identifier);
|
||
|
}
|
||
|
; /* inheritance_qualifier */
|
||
|
|
||
|
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
|
||
|
/* Default visibility.
|
||
|
*
|
||
|
* We use the inheritance modifier notation to specify the default
|
||
|
* visibility of functions and variables.
|
||
|
*/
|
||
|
|
||
|
default_visibility:
|
||
|
L_DEFAULT inheritance_qualifiers ';'
|
||
|
{
|
||
|
if ($2[0] & ~( TYPE_MOD_PRIVATE | TYPE_MOD_PUBLIC
|
||
|
| TYPE_MOD_PROTECTED | TYPE_MOD_STATIC)
|
||
|
)
|
||
|
{
|
||
|
yyerror("Default visibility specification for functions "
|
||
|
"accepts only 'private', 'protected', 'public' or "
|
||
|
"'static'");
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
|
||
|
if ($2[1] & ~( TYPE_MOD_PRIVATE | TYPE_MOD_PUBLIC
|
||
|
| TYPE_MOD_PROTECTED)
|
||
|
)
|
||
|
{
|
||
|
yyerror("Default visibility specification for variables "
|
||
|
"accepts only 'private', 'protected' or 'public'"
|
||
|
);
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
|
||
|
default_funmod = $2[0];
|
||
|
default_varmod = $2[1];
|
||
|
}
|
||
|
; /* default_visibility */
|
||
|
|
||
|
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
|
||
|
/* Type specifications and casts
|
||
|
*
|
||
|
* The type rules are used to parse variable and function types, casts,
|
||
|
* or just visibility e.g for inheritance.
|
||
|
*/
|
||
|
|
||
|
optional_star:
|
||
|
/* empty */ { $$ = 0; }
|
||
|
| '*' { $$ = TYPE_MOD_POINTER; } ;
|
||
|
|
||
|
|
||
|
type: type_modifier_list opt_basic_type
|
||
|
{
|
||
|
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);
|
||
|
if ($1->type == I_TYPE_UNKNOWN)
|
||
|
free_shared_identifier($1);
|
||
|
$$ = 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");
|
||
|
|
||
|
$<number>$ = define_global_variable($3, $1, $2, MY_TRUE);
|
||
|
}
|
||
|
|
||
|
L_ASSIGN expr0
|
||
|
{
|
||
|
init_global_variable($<number>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
|
||
|
{
|
||
|
$<number>$ = define_global_variable($4, $1, $3, MY_TRUE);
|
||
|
}
|
||
|
|
||
|
L_ASSIGN expr0
|
||
|
{
|
||
|
init_global_variable($<number>5, $4, $1, $3, $6, $7.type);
|
||
|
$$ = $1;
|
||
|
}
|
||
|
|
||
|
; /* name_list */
|
||
|
|
||
|
|
||
|
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
|
||
|
/* Blocks and simple statements.
|
||
|
*/
|
||
|
|
||
|
block:
|
||
|
'{'
|
||
|
|
||
|
{ enter_block_scope(); }
|
||
|
|
||
|
statements
|
||
|
|
||
|
{
|
||
|
/* If this is a local block, the declarations inserted
|
||
|
* a code fragment to zero out the locals (previous blocks
|
||
|
* may have left values in them). Complete the fragment
|
||
|
* with the number of locals to clear, now that we
|
||
|
* know it.
|
||
|
*/
|
||
|
{
|
||
|
block_scope_t *scope = block_scope + block_depth - 1;
|
||
|
|
||
|
if (use_local_scopes && scope->num_locals)
|
||
|
{
|
||
|
mem_block[A_PROGRAM].block[scope->addr+2]
|
||
|
= (char)scope->num_locals;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
'}'
|
||
|
|
||
|
{ leave_block_scope(); }
|
||
|
; /* block */
|
||
|
|
||
|
|
||
|
statements:
|
||
|
/* empty */
|
||
|
| statements 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, &$<lvalue>$, MY_FALSE, MY_TRUE);
|
||
|
}
|
||
|
L_ASSIGN expr0
|
||
|
{
|
||
|
init_local_variable($3, &$<lvalue>4, $5, $6.type);
|
||
|
|
||
|
$$ = $1;
|
||
|
}
|
||
|
| basic_type optional_star L_LOCAL
|
||
|
{
|
||
|
define_local_variable($3, $1, $2, &$<lvalue>$, MY_TRUE, MY_TRUE);
|
||
|
}
|
||
|
L_ASSIGN expr0
|
||
|
{
|
||
|
init_local_variable($3, &$<lvalue>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, &$<lvalue>$, MY_FALSE, MY_TRUE);
|
||
|
}
|
||
|
L_ASSIGN expr0
|
||
|
{
|
||
|
init_local_variable($4, &$<lvalue>5, $6, $7.type);
|
||
|
|
||
|
$$ = $1;
|
||
|
}
|
||
|
| local_name_list ',' optional_star L_LOCAL
|
||
|
{
|
||
|
define_local_variable($4, $1, $3, &$<lvalue>$, MY_TRUE, MY_TRUE);
|
||
|
}
|
||
|
L_ASSIGN expr0
|
||
|
{
|
||
|
init_local_variable($4, &$<lvalue>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 | case | default
|
||
|
| return ';'
|
||
|
| block
|
||
|
| /* empty */ ';'
|
||
|
|
||
|
| L_BREAK ';'
|
||
|
{
|
||
|
/* Compile the break statement */
|
||
|
|
||
|
if (current_break_address == 0)
|
||
|
yyerror("break statement outside loop");
|
||
|
|
||
|
if (current_break_address & BREAK_ON_STACK)
|
||
|
{
|
||
|
/* We break from a switch() */
|
||
|
|
||
|
ins_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: %ld"
|
||
|
, 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: <body>
|
||
|
* c: <cond>
|
||
|
* BBRANCH_WHEN_NON_ZERO l
|
||
|
*/
|
||
|
|
||
|
while:
|
||
|
{
|
||
|
/* Save the previous environment */
|
||
|
|
||
|
$<numbers>$[0] = current_continue_address;
|
||
|
$<numbers>$[1] = current_break_address;
|
||
|
|
||
|
push_address(); /* Remember the starting address */
|
||
|
}
|
||
|
|
||
|
L_WHILE '(' comma_expr ')'
|
||
|
|
||
|
{
|
||
|
%line
|
||
|
p_int addr = pop_address();
|
||
|
p_int length = CURRENT_PROGRAM_SIZE - addr;
|
||
|
bytecode_p expression;
|
||
|
|
||
|
/* Take the <cond> code, add the BBRANCH instruction and
|
||
|
* store all of it outside the program. After the <body>
|
||
|
* has been compiled, the code will be put back in.
|
||
|
*/
|
||
|
expression = yalloc(length+2);
|
||
|
memcpy(expression, mem_block[A_PROGRAM].block+addr, length);
|
||
|
if (last_expression == CURRENT_PROGRAM_SIZE - 1
|
||
|
&& expression[length-1] == F_NOT
|
||
|
)
|
||
|
{
|
||
|
/* Optimizize
|
||
|
* NOT
|
||
|
* BBRANCH_WHEN_NON_ZERO
|
||
|
* into
|
||
|
* BBRANCH_WHEN_ZERO
|
||
|
*/
|
||
|
length--;
|
||
|
expression[length] = F_BBRANCH_WHEN_ZERO;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
expression[length] = F_BBRANCH_WHEN_NON_ZERO;
|
||
|
}
|
||
|
|
||
|
/* Save the code as 'expression' */
|
||
|
$<expression>$.p = expression;
|
||
|
$<expression>$.length = length;
|
||
|
$<expression>$.line = current_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 ($<expression>6.line != current_loc.line)
|
||
|
store_line_number_info();
|
||
|
add_to_mem_block(A_PROGRAM, $<expression>6.p, $<expression>6.length+2);
|
||
|
yfree($<expression>6.p);
|
||
|
|
||
|
/* Complete the branch at the end of the condition code */
|
||
|
offset += addr + 1 - ( CURRENT_PROGRAM_SIZE - 1 );
|
||
|
if (offset < -0xff)
|
||
|
{
|
||
|
/* We need a LBRANCH instead of the BBRANCH */
|
||
|
|
||
|
bytecode_p codep;
|
||
|
|
||
|
if (offset < -0x8000)
|
||
|
yyerror("offset overflow");
|
||
|
codep = PROGRAM_BLOCK + --CURRENT_PROGRAM_SIZE - 1;
|
||
|
*codep = *codep == F_BBRANCH_WHEN_NON_ZERO
|
||
|
? F_LBRANCH_WHEN_NON_ZERO
|
||
|
: F_LBRANCH_WHEN_ZERO
|
||
|
;
|
||
|
ins_short(offset);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Just add the short offset */
|
||
|
mem_block[A_PROGRAM].block[CURRENT_PROGRAM_SIZE-1] = -offset;
|
||
|
}
|
||
|
|
||
|
if ($<expression>6.line != current_loc.line)
|
||
|
store_line_number_relocation($<expression>6.line);
|
||
|
|
||
|
/* Now that we have the end of the while(), we can finish
|
||
|
* up the breaks.
|
||
|
*/
|
||
|
for( ; current_break_address > 0
|
||
|
; current_break_address = next_addr)
|
||
|
{
|
||
|
next_addr = read_int32(current_break_address);
|
||
|
upd_int32(current_break_address,
|
||
|
CURRENT_PROGRAM_SIZE - current_break_address);
|
||
|
}
|
||
|
|
||
|
/* Restore the previous environment */
|
||
|
current_continue_address = $<numbers>1[0];
|
||
|
current_break_address = $<numbers>1[1];
|
||
|
}
|
||
|
; /* while */
|
||
|
|
||
|
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
|
||
|
/* The do-while() statement
|
||
|
*
|
||
|
* It is compiled into:
|
||
|
*
|
||
|
* l: <body>
|
||
|
* <cond>
|
||
|
* BBRANCH_WHEN_NON_ZERO l
|
||
|
*/
|
||
|
|
||
|
do:
|
||
|
{
|
||
|
/* Save the previous environment */
|
||
|
$<numbers>$[0] = current_continue_address;
|
||
|
$<numbers>$[1] = current_break_address;
|
||
|
|
||
|
current_break_address = BREAK_DELIMITER;
|
||
|
current_continue_address = CONTINUE_DELIMITER;
|
||
|
|
||
|
push_address(); /* Address to branch back to */
|
||
|
}
|
||
|
|
||
|
L_DO statement L_WHILE
|
||
|
|
||
|
{
|
||
|
/* The body is complete - we can already patch up
|
||
|
* the continue statements.
|
||
|
*/
|
||
|
|
||
|
p_int next_addr;
|
||
|
p_int current;
|
||
|
%line
|
||
|
current = CURRENT_PROGRAM_SIZE;
|
||
|
for(; current_continue_address > 0
|
||
|
; current_continue_address = next_addr)
|
||
|
{
|
||
|
next_addr = read_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 %lu\n", current+3);
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
|
||
|
/* Add the branch statement */
|
||
|
dest = PROGRAM_BLOCK + current;
|
||
|
if (current == last_expression + 1 && dest[-1] == F_NOT)
|
||
|
{
|
||
|
/* Optimize 'NOT BBRANCH_WHEN_NON_ZERO' to 'BBRANCH_WHEN_ZERO'
|
||
|
*/
|
||
|
offset = addr - current;
|
||
|
if (offset < -0xff)
|
||
|
{
|
||
|
if (offset < -0x8000)
|
||
|
yyerror("offset overflow");
|
||
|
PUT_CODE(dest-1, F_LBRANCH_WHEN_ZERO);
|
||
|
PUT_SHORT(dest, offset);
|
||
|
current += 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
PUT_CODE(dest-1, F_BBRANCH_WHEN_ZERO);
|
||
|
PUT_UINT8(dest, -offset);
|
||
|
current++;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
offset = addr - ( current + 1 );
|
||
|
if (offset < -0xff) {
|
||
|
if (offset < -0x8000)
|
||
|
yyerror("offset overflow");
|
||
|
STORE_CODE(dest, F_LBRANCH_WHEN_NON_ZERO);
|
||
|
STORE_SHORT(dest, offset);
|
||
|
current += 3;
|
||
|
} else {
|
||
|
STORE_CODE(dest, F_BBRANCH_WHEN_NON_ZERO);
|
||
|
STORE_UINT8(dest, -offset);
|
||
|
current += 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CURRENT_PROGRAM_SIZE = current;
|
||
|
|
||
|
/* Now that we have the end of the do-while(), we can finish
|
||
|
* up the breaks.
|
||
|
*/
|
||
|
for (; current_break_address > 0
|
||
|
; current_break_address = next_addr)
|
||
|
{
|
||
|
next_addr = read_int32(current_break_address);
|
||
|
upd_int32(current_break_address,
|
||
|
current - current_break_address);
|
||
|
}
|
||
|
|
||
|
/* Restore the previous environment */
|
||
|
current_continue_address = $<numbers>1[0];
|
||
|
current_break_address = $<numbers>1[1];
|
||
|
}
|
||
|
; /* do */
|
||
|
|
||
|
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
|
||
|
/* The for() statement.
|
||
|
*
|
||
|
* It is compiled as:
|
||
|
*
|
||
|
* CLEAR_LOCALS
|
||
|
* <init>
|
||
|
* POP
|
||
|
* BRANCH c
|
||
|
* l: <body>
|
||
|
* <incr>
|
||
|
* POP
|
||
|
* c: <cond>
|
||
|
* BBRANCH_WHEN_NON_ZERO l
|
||
|
*/
|
||
|
|
||
|
for:
|
||
|
L_FOR '('
|
||
|
|
||
|
{
|
||
|
%line
|
||
|
/* Save the previous environment */
|
||
|
$<numbers>$[0] = current_continue_address;
|
||
|
$<numbers>$[1] = current_break_address;
|
||
|
|
||
|
/* Open a new scope to all variables local to the
|
||
|
* for-statement as a whole.
|
||
|
*/
|
||
|
enter_block_scope();
|
||
|
}
|
||
|
|
||
|
for_init_expr ';'
|
||
|
|
||
|
{
|
||
|
%line
|
||
|
/* Get rid of whatever init_expr computed */
|
||
|
insert_pop_value();
|
||
|
|
||
|
/* From here, the <body> will be placed eventually */
|
||
|
|
||
|
current_continue_address = CONTINUE_DELIMITER;
|
||
|
$<number>$ = CURRENT_PROGRAM_SIZE;
|
||
|
}
|
||
|
|
||
|
for_expr ';'
|
||
|
|
||
|
{
|
||
|
%line
|
||
|
/* Add the BBRANCH to the condition and save it all
|
||
|
* in an 'expression' on the compiler stack for later
|
||
|
* re-insertion.
|
||
|
*/
|
||
|
|
||
|
p_int start, length;
|
||
|
bytecode_p expression;
|
||
|
|
||
|
start = $<number>6;
|
||
|
length = CURRENT_PROGRAM_SIZE - start;
|
||
|
expression = yalloc(length+2);
|
||
|
memcpy(expression, mem_block[A_PROGRAM].block + start, length );
|
||
|
|
||
|
/* Add the branch instruction */
|
||
|
if (last_expression == CURRENT_PROGRAM_SIZE - 1
|
||
|
&& expression[length-1] == F_NOT
|
||
|
)
|
||
|
{
|
||
|
/* Optimize 'NOT BBRANCH_WHEN_NON_ZERO'
|
||
|
* to 'BBRANCH_WHEN_ZERO'
|
||
|
*/
|
||
|
length--;
|
||
|
expression[length] = F_BBRANCH_WHEN_ZERO;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
expression[length] = F_BBRANCH_WHEN_NON_ZERO;
|
||
|
}
|
||
|
|
||
|
/* Save the codeblock on the stack */
|
||
|
$<expression>$.p = expression;
|
||
|
$<expression>$.length = length;
|
||
|
$<expression>$.line = current_loc.line;
|
||
|
|
||
|
/* Restart codegeneration from here */
|
||
|
CURRENT_PROGRAM_SIZE = start;
|
||
|
last_expression = -1;
|
||
|
}
|
||
|
|
||
|
for_expr ')'
|
||
|
|
||
|
{
|
||
|
%line
|
||
|
/* Save the <incr> code block on the compiler stack
|
||
|
* for later re-insertion and start the compilation
|
||
|
* of the loop body.
|
||
|
*/
|
||
|
|
||
|
p_int length;
|
||
|
|
||
|
/* Save the code block */
|
||
|
insert_pop_value();
|
||
|
length = CURRENT_PROGRAM_SIZE - $<number>6;
|
||
|
$<expression>$.p = yalloc(length);
|
||
|
if (length)
|
||
|
memcpy( $<expression>$.p
|
||
|
, mem_block[A_PROGRAM].block + $<number>6
|
||
|
, length );
|
||
|
$<expression>$.length = length;
|
||
|
$<expression>$.line = current_loc.line;
|
||
|
|
||
|
/* Restart the codegeneration for the body */
|
||
|
CURRENT_PROGRAM_SIZE = $<number>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)
|
||
|
{
|
||
|
mem_block[A_PROGRAM].block[scope->addr+2]
|
||
|
= (char)scope->num_locals;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
statement
|
||
|
|
||
|
{
|
||
|
%line
|
||
|
/* The loop is complete, now add the <incr> and <cond>
|
||
|
* code 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 ( $<expression>9.line != current_loc.line
|
||
|
|| ( $<expression>12.line != current_loc.line
|
||
|
&& $<expression>12.length)
|
||
|
)
|
||
|
store_line_number_info();
|
||
|
|
||
|
/* Add the <incr> code block if needed */
|
||
|
if ($<expression>12.length)
|
||
|
{
|
||
|
add_to_mem_block(A_PROGRAM, $<expression>12.p
|
||
|
, $<expression>12.length);
|
||
|
if ($<expression>12.line != $<expression>9.line)
|
||
|
store_line_number_relocation($<expression>12.line);
|
||
|
}
|
||
|
yfree($<expression>12.p);
|
||
|
|
||
|
/* Fix the branch over the body */
|
||
|
offset =
|
||
|
fix_branch( F_LBRANCH, CURRENT_PROGRAM_SIZE, $<number>6 + 1);
|
||
|
|
||
|
/* Add the <cond> code block */
|
||
|
add_to_mem_block(A_PROGRAM, $<expression>9.p, $<expression>9.length+2);
|
||
|
yfree($<expression>9.p);
|
||
|
|
||
|
/* Create the branch back after the condition */
|
||
|
offset += $<number>6 + 2 - ( CURRENT_PROGRAM_SIZE - 1 );
|
||
|
if (offset < -0xff)
|
||
|
{
|
||
|
bytecode_p codep;
|
||
|
|
||
|
if (offset < -0x8000)
|
||
|
yyerror("offset overflow");
|
||
|
|
||
|
codep = PROGRAM_BLOCK + --CURRENT_PROGRAM_SIZE - 1;
|
||
|
*codep = *codep == F_BBRANCH_WHEN_NON_ZERO
|
||
|
? F_LBRANCH_WHEN_NON_ZERO
|
||
|
: F_LBRANCH_WHEN_ZERO
|
||
|
;
|
||
|
ins_short(offset);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mem_block[A_PROGRAM].block[CURRENT_PROGRAM_SIZE-1] = -offset;
|
||
|
}
|
||
|
|
||
|
if ($<expression>9.line != current_loc.line)
|
||
|
store_line_number_relocation($<expression>9.line);
|
||
|
|
||
|
/* Now complete the break instructions.
|
||
|
*/
|
||
|
for (; current_break_address > 0
|
||
|
; current_break_address = next_addr)
|
||
|
{
|
||
|
next_addr = read_int32(current_break_address);
|
||
|
upd_int32(current_break_address,
|
||
|
CURRENT_PROGRAM_SIZE - current_break_address);
|
||
|
}
|
||
|
|
||
|
/* Restore the previous environment */
|
||
|
current_continue_address = $<numbers>3[0];
|
||
|
current_break_address = $<numbers>3[1];
|
||
|
|
||
|
/* and leave the for scope */
|
||
|
leave_block_scope();
|
||
|
}
|
||
|
; /* for */
|
||
|
|
||
|
/* Special rules for 'int <name> = <expr>' declarations in the first
|
||
|
* for() expression.
|
||
|
*/
|
||
|
for_init_expr:
|
||
|
/* EMPTY */
|
||
|
{
|
||
|
last_expression = mem_block[A_PROGRAM].current_size;
|
||
|
ins_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 <name> = <expr>" 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 <name>" type expression.
|
||
|
* Compile it as if it was a "int <name> = 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 <statement> is empty:
|
||
|
*
|
||
|
* CLEAR_LOCALS CLEAR_LOCALS
|
||
|
* PUSH_(LOCAL_)LVALUE <var1> <expr>
|
||
|
* ... POP_VALUE
|
||
|
* PUSH_(LOCAL_)LVALUE <varn> [POP_VALUE for integer ranges]
|
||
|
* <expr>
|
||
|
* FOREACH(_REF) <numargs> c
|
||
|
* l: <body>
|
||
|
* c: FOREACH_NEXT l
|
||
|
* e: FOREACH_END
|
||
|
*
|
||
|
* continue's branch to c, break's to e.
|
||
|
*/
|
||
|
|
||
|
foreach:
|
||
|
|
||
|
L_FOREACH '('
|
||
|
|
||
|
{
|
||
|
/* Save the previous environment */
|
||
|
$<numbers>$[0] = current_continue_address;
|
||
|
$<numbers>$[1] = current_break_address;
|
||
|
|
||
|
current_break_address = BREAK_DELIMITER;
|
||
|
current_continue_address = CONTINUE_DELIMITER;
|
||
|
|
||
|
/* Open a new scope to all variables local to the
|
||
|
* foreach-statement as a whole.
|
||
|
*/
|
||
|
enter_block_scope();
|
||
|
}
|
||
|
|
||
|
foreach_vars foreach_in
|
||
|
|
||
|
{
|
||
|
%line
|
||
|
/* Remember the starting address of the expression */
|
||
|
$<address>$ = CURRENT_PROGRAM_SIZE;
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
mem_block[A_PROGRAM].block[scope->addr+2]
|
||
|
= (char)scope->num_locals;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* 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 = $<address>6;
|
||
|
start_addr = expr_addr - $4*2;
|
||
|
current = start_addr + (addr - 4 - expr_addr);
|
||
|
for ( src = PROGRAM_BLOCK + expr_addr,
|
||
|
dest = PROGRAM_BLOCK + start_addr
|
||
|
; expr_addr < addr-4
|
||
|
; src++, dest++, expr_addr++)
|
||
|
*dest = *src;
|
||
|
CURRENT_PROGRAM_SIZE = current;
|
||
|
ins_f_code(F_POP_VALUE);
|
||
|
current++;
|
||
|
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 = $<numbers>3[0];
|
||
|
current_break_address = $<numbers>3[1];
|
||
|
|
||
|
/* and leave the scope */
|
||
|
leave_block_scope();
|
||
|
}
|
||
|
; /* foreach */
|
||
|
|
||
|
|
||
|
foreach_vars : /* Parse and count the number of lvalues */
|
||
|
foreach_var_decl { $$ = 1; }
|
||
|
| foreach_vars ',' foreach_var_decl { $$ = $1 + 1; }
|
||
|
; /* foreach_vars */
|
||
|
|
||
|
|
||
|
foreach_var_decl: /* Generate the code for one lvalue */
|
||
|
|
||
|
/* TODO: It is tempting to add an alternative "| lvalue",
|
||
|
* TODO:: but then we get masses of reduce/reduce conflicts
|
||
|
* TODO:: between lvalue and expr4. Dunno why.
|
||
|
*/
|
||
|
foreach_var_lvalue
|
||
|
{
|
||
|
/* Add the bytecode to create the lvalue, and good is.
|
||
|
*/
|
||
|
|
||
|
%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 ) block
|
||
|
*
|
||
|
* and that case and default are both just special kinds of statement
|
||
|
* which mark addresses within the statement code to which the
|
||
|
* switch statement may jump.
|
||
|
*
|
||
|
* That also means that in contrast to C the code
|
||
|
*
|
||
|
* switch(x);
|
||
|
* or switch(x) write("Foo");
|
||
|
*
|
||
|
* is syntactically not ok.
|
||
|
*
|
||
|
* TODO: Since current_break_address is used to indicate an active switch(),
|
||
|
* TODO:: the compiler can't compile Duff's device: the inner while() hides
|
||
|
* TODO:: active switch(). If that is fixed, we can change the 'block'
|
||
|
* TODO:: back to 'statement' in the grammar rule.
|
||
|
*/
|
||
|
|
||
|
switch:
|
||
|
L_SWITCH '(' comma_expr ')'
|
||
|
|
||
|
{
|
||
|
/* We start a new switch(), which might be nested into
|
||
|
* an outer switch().
|
||
|
*/
|
||
|
|
||
|
case_state_t *statep;
|
||
|
%line
|
||
|
current_break_stack_need++;
|
||
|
if ( current_break_stack_need > max_break_stack_need )
|
||
|
max_break_stack_need = current_break_stack_need;
|
||
|
|
||
|
/* Save the previous switch state */
|
||
|
if ( !(statep = yalloc(sizeof(case_state_t))) )
|
||
|
{
|
||
|
yyerrorf("Out of memory: case state (%lu bytes)"
|
||
|
, (unsigned long) sizeof(case_state_t));
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
*statep = case_state;
|
||
|
case_state.previous = statep;
|
||
|
push_explicit(current_break_address);
|
||
|
push_explicit(switch_pc);
|
||
|
|
||
|
/* Create the SWITCH instruction plus two empty bytes */
|
||
|
ins_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;
|
||
|
}
|
||
|
|
||
|
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 */
|
||
|
|
||
|
case: L_CASE case_label ':'
|
||
|
{
|
||
|
%line
|
||
|
/* Mark the current program address as another
|
||
|
* case target for the current switch.
|
||
|
*/
|
||
|
case_list_entry_t *temp;
|
||
|
|
||
|
if ( !( current_break_address & CASE_LABELS_ENABLED ) )
|
||
|
{
|
||
|
yyerror("Case outside switch");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Get and fill in a new case entry structure */
|
||
|
if ( !(temp = new_case_entry()) )
|
||
|
{
|
||
|
yyerror("Out of memory: new case entry");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( !(temp->key = $2.key) )
|
||
|
{
|
||
|
case_state.zero = temp;
|
||
|
}
|
||
|
temp->addr = mem_block[A_PROGRAM].current_size - switch_pc;
|
||
|
temp->line = current_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");
|
||
|
|
||
|
if ( !( current_break_address & CASE_LABELS_ENABLED ) )
|
||
|
{
|
||
|
yyerror("Case range outside switch");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* A range like "case 4..2" is illegal,
|
||
|
* a range like "case 4..4" counts as simple "case 4".
|
||
|
*/
|
||
|
if ($2.key >= $4.key)
|
||
|
{
|
||
|
if ($2.key > $4.key)
|
||
|
{
|
||
|
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.
|
||
|
*/
|
||
|
|
||
|
if ( !( current_break_address & CASE_LABELS_ENABLED ) ) {
|
||
|
yyerror("Default outside switch");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (case_state.default_addr)
|
||
|
yyerror("Duplicate default");
|
||
|
|
||
|
case_state.default_addr = CURRENT_PROGRAM_SIZE - switch_pc;
|
||
|
}
|
||
|
; /* default */
|
||
|
|
||
|
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
|
||
|
/* The if()-statement.
|
||
|
*
|
||
|
* This is compiled as: resp. as:
|
||
|
*
|
||
|
* <cond> <cond>
|
||
|
* BRANCH_WHEN_ZERO e BRANCH_WHEN_ZERO e
|
||
|
* <if-part> <if-part>
|
||
|
* e: BRANCH f
|
||
|
* e: <else-part>
|
||
|
* f:
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
condStart:
|
||
|
L_IF '(' comma_expr ')'
|
||
|
{
|
||
|
/* When we enter a condition, we must not allow case labels
|
||
|
* anymore.
|
||
|
*/
|
||
|
|
||
|
mp_uint current;
|
||
|
bytecode_p current_code;
|
||
|
|
||
|
/* Turn off the case labels */
|
||
|
|
||
|
$$[0] = current_break_address;
|
||
|
current_break_address &= ~CASE_LABELS_ENABLED;
|
||
|
|
||
|
current = CURRENT_PROGRAM_SIZE;
|
||
|
if (!realloc_a_program(2))
|
||
|
{
|
||
|
yyerrorf("Out of memory: program size %lu\n", current+3);
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
current_code = PROGRAM_BLOCK + current;
|
||
|
|
||
|
/* Add the branch instruction, with the usual optimization */
|
||
|
if (last_expression == current - 1
|
||
|
&& current_code[-1] == F_NOT)
|
||
|
{
|
||
|
current_code[-1] = F_BRANCH_WHEN_NON_ZERO;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*current_code = F_BRANCH_WHEN_ZERO;
|
||
|
current++;
|
||
|
}
|
||
|
|
||
|
$$[1] = current;
|
||
|
CURRENT_PROGRAM_SIZE = current + 1;
|
||
|
}
|
||
|
; /* condStart */
|
||
|
|
||
|
|
||
|
cond:
|
||
|
condStart
|
||
|
statement
|
||
|
optional_else
|
||
|
{
|
||
|
p_int destination, location, offset;
|
||
|
|
||
|
/* Complete the branch over the if-part */
|
||
|
destination = (p_int)$3;
|
||
|
location = $1[1];
|
||
|
if ( (offset = destination - location) > 0x100)
|
||
|
{
|
||
|
fix_branch(
|
||
|
mem_block[A_PROGRAM].block[location-1] ==
|
||
|
F_BRANCH_WHEN_ZERO ?
|
||
|
F_LBRANCH_WHEN_ZERO :
|
||
|
F_LBRANCH_WHEN_NON_ZERO
|
||
|
,
|
||
|
destination, location
|
||
|
);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mem_block[A_PROGRAM].block[location] = offset - 1;
|
||
|
}
|
||
|
|
||
|
/* Restore the previous case-labels status without
|
||
|
* changing the actual break-address.
|
||
|
*/
|
||
|
current_break_address |= $1[0] & CASE_LABELS_ENABLED;
|
||
|
}
|
||
|
; /* cond */
|
||
|
|
||
|
|
||
|
optional_else:
|
||
|
/* empty */ %prec LOWER_THAN_ELSE
|
||
|
{
|
||
|
/* The if-part ends here */
|
||
|
$$ = CURRENT_PROGRAM_SIZE;
|
||
|
}
|
||
|
|
||
|
| L_ELSE
|
||
|
{
|
||
|
/* Add the branch over the else part */
|
||
|
ins_f_code(F_BRANCH);
|
||
|
$<address>$ = CURRENT_PROGRAM_SIZE;
|
||
|
ins_byte(0);
|
||
|
}
|
||
|
statement
|
||
|
{
|
||
|
/* Fix up the branch over the else part and return
|
||
|
* the start address of the else part.
|
||
|
*/
|
||
|
$$ = fix_branch( F_LBRANCH, CURRENT_PROGRAM_SIZE, $<address>2);
|
||
|
$$ += $<address>2 + 1;
|
||
|
}
|
||
|
; /* optional_else */
|
||
|
|
||
|
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
|
||
|
/* Constants
|
||
|
*
|
||
|
* The rules here implement constant folding for numeric and string constants.
|
||
|
*/
|
||
|
|
||
|
constant:
|
||
|
constant '|' constant { $$ = $1 | $3; }
|
||
|
| constant '^' constant { $$ = $1 ^ $3; }
|
||
|
| constant '&' constant { $$ = $1 & $3; }
|
||
|
| constant L_EQ constant { $$ = $1 == $3; }
|
||
|
| constant L_NE constant { $$ = $1 != $3; }
|
||
|
| constant '>' constant { $$ = $1 > $3; }
|
||
|
| constant L_GE constant { $$ = $1 >= $3; }
|
||
|
| constant '<' constant { $$ = $1 < $3; }
|
||
|
| constant L_LE constant { $$ = $1 <= $3; }
|
||
|
| constant L_LSH constant { $$ = (p_uint)$3 > MAX_SHIFT ? 0 : $1 << $3; }
|
||
|
| constant L_RSH constant { $$ = (p_uint)$3 > MAX_SHIFT ? ($1 >= 0 ? 0 : -1) : ($1 >> $3); }
|
||
|
| constant L_RSHL constant { $$ = (p_uint)$3 > MAX_SHIFT ? 0 : ((p_uint)$1 >> $3); }
|
||
|
| constant '+' constant { $$ = $1 + $3; }
|
||
|
| constant '-' constant { $$ = $1 - $3; }
|
||
|
| constant '*' constant { $$ = $1 * $3; }
|
||
|
| constant '%' constant
|
||
|
{
|
||
|
if ($3)
|
||
|
{
|
||
|
$$ = $1 % $3;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
yyerror("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):
|
||
|
*
|
||
|
* <expr0> <lvalue> <lvalue>
|
||
|
* <lvalue> LDUP LDUP
|
||
|
* ASSIGN-operator LOR l DUP
|
||
|
* <expr0> LBRANCH_WHEN_NON_ZERO l
|
||
|
* l: SWAP_VALUES POP_VALUE
|
||
|
* ASSIGN <expr0>
|
||
|
* l: SWAP_VALUES
|
||
|
* ASSIGN
|
||
|
*/
|
||
|
|
||
|
lvalue L_ASSIGN
|
||
|
|
||
|
{
|
||
|
if ($2 == F_LAND_EQ || $2 == F_LOR_EQ)
|
||
|
{
|
||
|
if (!add_lvalue_code(&$1, 0))
|
||
|
YYACCEPT;
|
||
|
|
||
|
/* Add the operator specific code */
|
||
|
|
||
|
if ($2 == F_LAND_EQ)
|
||
|
{
|
||
|
/* Insert the LDUP, LAND and remember the position */
|
||
|
|
||
|
ins_f_code(F_LDUP);
|
||
|
ins_f_code(F_LAND);
|
||
|
$<address>$ = CURRENT_PROGRAM_SIZE;
|
||
|
ins_byte(0);
|
||
|
}
|
||
|
else if ($2 == F_LOR_EQ)
|
||
|
{
|
||
|
/* Insert the LDUP, LOR and remember the position */
|
||
|
|
||
|
ins_f_code(F_LDUP);
|
||
|
ins_f_code(F_LOR);
|
||
|
$<address>$ = CURRENT_PROGRAM_SIZE;
|
||
|
ins_byte(0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
expr0 %prec L_ASSIGN
|
||
|
|
||
|
{
|
||
|
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($<address>3, F_LBRANCH_WHEN_ZERO);
|
||
|
}
|
||
|
else if ($2 == F_LOR_EQ)
|
||
|
{
|
||
|
update_lop_branch($<address>3, F_LBRANCH_WHEN_NON_ZERO);
|
||
|
}
|
||
|
|
||
|
/* Insert the SWAP and the ASSIGN */
|
||
|
|
||
|
ins_f_code(F_SWAP_VALUES);
|
||
|
ins_f_code(F_ASSIGN);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!add_lvalue_code(&$1, $2))
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
$$.end = CURRENT_PROGRAM_SIZE;
|
||
|
$$.type = restype;
|
||
|
}
|
||
|
|
||
|
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
|
||
|
| error L_ASSIGN expr0 %prec L_ASSIGN
|
||
|
{
|
||
|
yyerror("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);
|
||
|
$<address>$ = CURRENT_PROGRAM_SIZE;
|
||
|
ins_byte(0);
|
||
|
}
|
||
|
|
||
|
expr0
|
||
|
|
||
|
{
|
||
|
/* Insert the branch over the :-part, and update
|
||
|
* the earlier branch to the :-part.
|
||
|
*/
|
||
|
|
||
|
p_int address, offset;
|
||
|
|
||
|
address = (p_int)$<address>3;
|
||
|
|
||
|
/* The branch to the end */
|
||
|
ins_f_code(F_BRANCH);
|
||
|
$<address>$ = CURRENT_PROGRAM_SIZE;
|
||
|
ins_byte(0);
|
||
|
|
||
|
/* Update the earlier branch to point here */
|
||
|
offset = CURRENT_PROGRAM_SIZE - ( address + 1);
|
||
|
if (offset > 0xff - 1)
|
||
|
{
|
||
|
/* We have to make it a long branch and move the code
|
||
|
* generated so far.
|
||
|
*/
|
||
|
|
||
|
int i;
|
||
|
bytecode_p p;
|
||
|
|
||
|
$<address>$ = CURRENT_PROGRAM_SIZE;
|
||
|
ins_byte(0);
|
||
|
p = PROGRAM_BLOCK + mem_block[A_PROGRAM].current_size-1;
|
||
|
for (i = offset; --i >= 0; --p )
|
||
|
*p = p[-1];
|
||
|
p[-2] = F_LBRANCH_WHEN_ZERO;
|
||
|
upd_short(address, offset+2);
|
||
|
if (offset > 0x7ffd)
|
||
|
yyerror("offset overflow");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mem_block[A_PROGRAM].block[address] = offset;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
':' expr0 %prec '?'
|
||
|
|
||
|
{
|
||
|
/* Update the earlier branch skipping the :-part
|
||
|
* and check the types of the two parts.
|
||
|
*/
|
||
|
p_int address, old_address;
|
||
|
int offset;
|
||
|
fulltype_t type1, type2;
|
||
|
|
||
|
last_expression = -1;
|
||
|
|
||
|
old_address = $<address>3;
|
||
|
address = $<address>5;
|
||
|
offset = mem_block[A_PROGRAM].current_size - ( address + 1);
|
||
|
if (offset > 0xff)
|
||
|
{
|
||
|
/* We have to make the branch a long branch.
|
||
|
* This could also mean that the first branch now
|
||
|
* have to become a long branch, too.
|
||
|
*/
|
||
|
int i;
|
||
|
bytecode_p p;
|
||
|
|
||
|
ins_byte(0);
|
||
|
p = PROGRAM_BLOCK + mem_block[A_PROGRAM].current_size-1;
|
||
|
for( i = offset; --i >= 0; --p )
|
||
|
*p = p[-1];
|
||
|
p[-2] = F_LBRANCH;
|
||
|
upd_short(address, offset+2);
|
||
|
if (offset > 0x7ffd)
|
||
|
yyerror("offset overflow");
|
||
|
if ( mem_block[A_PROGRAM].block[old_address-1] ==
|
||
|
F_BRANCH_WHEN_ZERO )
|
||
|
mem_block[A_PROGRAM].block[old_address]++;
|
||
|
else
|
||
|
upd_short(old_address,read_short(old_address)+1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mem_block[A_PROGRAM].block[address] = offset;
|
||
|
}
|
||
|
|
||
|
/* 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);
|
||
|
$<address>$ = CURRENT_PROGRAM_SIZE;
|
||
|
ins_byte(0);
|
||
|
}
|
||
|
|
||
|
expr0
|
||
|
|
||
|
{
|
||
|
/* Update the offset the earlier LOR instruction */
|
||
|
|
||
|
update_lop_branch($<address>3, F_LBRANCH_WHEN_NON_ZERO);
|
||
|
|
||
|
$$ = $1;
|
||
|
$$.end = CURRENT_PROGRAM_SIZE;
|
||
|
|
||
|
/* Determine the result type */
|
||
|
if (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);
|
||
|
$<address>$ = CURRENT_PROGRAM_SIZE;
|
||
|
ins_byte(0);
|
||
|
}
|
||
|
|
||
|
expr0
|
||
|
|
||
|
{
|
||
|
/* Update the offset the earlier LAND instruction */
|
||
|
|
||
|
update_lop_branch($<address>3, F_LBRANCH_WHEN_ZERO);
|
||
|
|
||
|
$$ = $1;
|
||
|
$$.end = CURRENT_PROGRAM_SIZE;
|
||
|
|
||
|
/* Determine the return type */
|
||
|
if (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
|
||
|
$<numbers>$[0] = last_expression;
|
||
|
$<numbers>$[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
|
||
|
&& $<numbers>3[0] + 4 == (mp_int)current_size
|
||
|
&& ((p[-2]-(F_CSTRING0)) & ~3) == 0
|
||
|
&& ((p[-4]-(F_CSTRING0)) & ~3) == 0
|
||
|
)
|
||
|
{
|
||
|
/* Yup, we can combine the two strings.
|
||
|
*/
|
||
|
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 (%ld bytes)"
|
||
|
, (unsigned long)(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 ($<numbers>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 %lu\n"
|
||
|
, current+length);
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
p = PROGRAM_BLOCK;
|
||
|
memcpy(p + current, p + start, length);
|
||
|
p += start;
|
||
|
q = p + length;
|
||
|
length = current - start;
|
||
|
for( ; --length >= 0; )
|
||
|
*p++ = *q++;
|
||
|
if ($2.code == F_PUSH_IDENTIFIER16_LVALUE)
|
||
|
p[-3] = $2.code;
|
||
|
else
|
||
|
p[-1] = $2.code;
|
||
|
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 %lu\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 %lu\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 <expr4> and add our PUSH_INDEXED_MAP_LVALUE
|
||
|
*/
|
||
|
current = CURRENT_PROGRAM_SIZE;
|
||
|
if (!realloc_a_program(2))
|
||
|
{
|
||
|
yyerrorf("Out of memory: program size %lu\n", current+2);
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
p = PROGRAM_BLOCK + current;
|
||
|
*p++ = F_PUSH_INDEXED_MAP_LVALUE;
|
||
|
|
||
|
/* Finally store the actual instruction */
|
||
|
*p = $1.code;
|
||
|
last_expression = current + 1;
|
||
|
CURRENT_PROGRAM_SIZE = current + 2;
|
||
|
$$.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;
|
||
|
}
|
||
|
$<number>$ = num;
|
||
|
free_mstring($3);
|
||
|
}
|
||
|
|
||
|
note_start opt_struct_init ')'
|
||
|
|
||
|
{
|
||
|
/* Generate a literal struct */
|
||
|
|
||
|
int num = $<number>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 %lu\n", current+3);
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
p = PROGRAM_BLOCK + current;
|
||
|
|
||
|
if (i & VIRTUAL_VAR_TAG)
|
||
|
{
|
||
|
*p++ = F_PUSH_VIRTUAL_VARIABLE_LVALUE;
|
||
|
*p = i;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ((i + num_virtual_variables) & ~0xff)
|
||
|
{
|
||
|
*p = F_PUSH_IDENTIFIER16_LVALUE;
|
||
|
upd_short(++current, i + num_virtual_variables);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*p++ = F_PUSH_IDENTIFIER_LVALUE;
|
||
|
*p = i + num_virtual_variables;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CURRENT_PROGRAM_SIZE = current + 2;
|
||
|
if (i == -1)
|
||
|
$$.type = Type_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 %lu\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 %lu\n", current+3);
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
p = PROGRAM_BLOCK + current;
|
||
|
|
||
|
if (i & VIRTUAL_VAR_TAG)
|
||
|
{
|
||
|
/* Access a virtual variable */
|
||
|
|
||
|
$$.code = F_PUSH_VIRTUAL_VARIABLE_LVALUE;
|
||
|
*p++ = F_VIRTUAL_VARIABLE;
|
||
|
*p = i;
|
||
|
$$.type = V_VARIABLE(i)->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 %lu\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 %lu\n", current+1);
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
|
||
|
mark = PROGRAM_BLOCK + $3.start;
|
||
|
p = PROGRAM_BLOCK + current;
|
||
|
length = current - $3.start;
|
||
|
for( ; --length >= 0; p--) PUT_CODE(p, GET_CODE(p-1));
|
||
|
STORE_CODE(mark, F_CONST0);
|
||
|
CURRENT_PROGRAM_SIZE++;
|
||
|
$3.end++;
|
||
|
|
||
|
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 %lu\n", current+1);
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
|
||
|
mark = PROGRAM_BLOCK + $4.start;
|
||
|
p = PROGRAM_BLOCK + current;
|
||
|
length = current - $4.start;
|
||
|
for( ; --length >= 0; p--) PUT_CODE(p, GET_CODE(p-1));
|
||
|
STORE_CODE(mark, F_CONST0);
|
||
|
CURRENT_PROGRAM_SIZE++;
|
||
|
$4.end++;
|
||
|
|
||
|
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 %lu\n", current+1);
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
|
||
|
mark = PROGRAM_BLOCK + $4.start;
|
||
|
p = PROGRAM_BLOCK + current;
|
||
|
length = current - $4.start;
|
||
|
for( ; --length >= 0; p--) PUT_CODE(p, GET_CODE(p-1));
|
||
|
STORE_CODE(mark, F_CONST0);
|
||
|
CURRENT_PROGRAM_SIZE++;
|
||
|
$4.end++;
|
||
|
|
||
|
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.
|
||
|
*/
|
||
|
|
||
|
$<struct_init_list>$.list = NULL;
|
||
|
$<struct_init_list>$.last = NULL;
|
||
|
$<struct_init_list>$.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 */
|
||
|
$<function_call_head>$.start = CURRENT_PROGRAM_SIZE;
|
||
|
$<function_call_head>$.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 */
|
||
|
|
||
|
$<function_call_head>$.simul_efun = real_name->u.global.sim_efun;
|
||
|
|
||
|
if (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 = $<function_call_head>2.start;
|
||
|
$$.code = -1;
|
||
|
|
||
|
if ( $4 >= 0xff )
|
||
|
/* since num_arg is encoded in just one byte, and 0xff
|
||
|
* is taken for SIMUL_EFUN_VARARG */
|
||
|
yyerrorf("Too many arguments to function");
|
||
|
|
||
|
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 = $<function_call_head>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;
|
||
|
|
||
|
if (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 ($1.real->type == I_TYPE_UNKNOWN)
|
||
|
{
|
||
|
free_shared_identifier($1.real);
|
||
|
}
|
||
|
|
||
|
if (ix < 0)
|
||
|
{
|
||
|
switch(ix) {
|
||
|
case INHERITED_NOT_FOUND:
|
||
|
yyerror("function not defined by inheritance as specified");
|
||
|
break;
|
||
|
case INHERITED_WILDCARDED_ARGS:
|
||
|
yyerror("wildcarded call to inherited function can't pass arguments");
|
||
|
break;
|
||
|
case INHERITED_WILDCARDED_NOT_FOUND:
|
||
|
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 + $<function_call_head>2.start;
|
||
|
src = dest+1;
|
||
|
left = CURRENT_PROGRAM_SIZE - $<function_call_head>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 */
|
||
|
$<function_call_head>$.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 <expr4> 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 %lu\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 <expr4> already parsed.
|
||
|
* Putting this code block before the <expr4> in the rule
|
||
|
* however yields a faulty grammar.
|
||
|
*/
|
||
|
|
||
|
if (!disable_sefuns
|
||
|
&& call_other_sefun >= 0
|
||
|
&& 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 %lu\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));
|
||
|
|
||
|
if (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 %lu\n"
|
||
|
, mem_block[A_PROGRAM].current_size + i+2);
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
|
||
|
__PREPARE_INSERT__p = PROGRAM_BLOCK
|
||
|
+ CURRENT_PROGRAM_SIZE;
|
||
|
}
|
||
|
CURRENT_PROGRAM_SIZE += i;
|
||
|
while ( --i >= 0 )
|
||
|
{
|
||
|
add_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 + $<function_call_head>4.start;
|
||
|
src = dest+1;
|
||
|
left = CURRENT_PROGRAM_SIZE - $<function_call_head>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));
|
||
|
if ($1->type == I_TYPE_UNKNOWN)
|
||
|
free_shared_identifier($1);
|
||
|
}
|
||
|
|
||
|
| L_STRING L_STRING
|
||
|
{ fatal("presence of rule should prevent its reduction"); }
|
||
|
|
||
|
| L_STRING
|
||
|
{
|
||
|
$$ = ystring_copy(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();
|
||
|
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);
|
||
|
$<address>$ = 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 = $<address>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
|
||
|
* <expr>
|
||
|
* l: END_CATCH
|
||
|
*
|
||
|
* to
|
||
|
*
|
||
|
* CATCH l0
|
||
|
* BRANCH l1
|
||
|
* l0: LBRANCH l2
|
||
|
* l1: <expr>
|
||
|
* l2: END_CATCH
|
||
|
*/
|
||
|
|
||
|
int i;
|
||
|
bytecode_p p;
|
||
|
|
||
|
if (!realloc_a_program(5))
|
||
|
{
|
||
|
yyerrorf("Out of memory: program size %lu\n"
|
||
|
, CURRENT_PROGRAM_SIZE + 5);
|
||
|
YYACCEPT;
|
||
|
}
|
||
|
CURRENT_PROGRAM_SIZE += 5;
|
||
|
p = PROGRAM_BLOCK + CURRENT_PROGRAM_SIZE - 1;
|
||
|
for( i = offset; --i >= 0; --p ) *p = p[-5];
|
||
|
p[-5] = 2;
|
||
|
p[-4] = F_BRANCH ;
|
||
|
p[-3] = 3;
|
||
|
p[-2] = F_LBRANCH;
|
||
|
upd_short(start + 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 <expr>'"
|
||
|
, 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 <expr>'"
|
||
|
, 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: <type> [*] <name>
|
||
|
* 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->num_locals == 1)
|
||
|
{
|
||
|
/* First definition of a local, so insert the
|
||
|
* clear_locals bytecode and remember its position
|
||
|
*/
|
||
|
scope->addr = mem_block[A_PROGRAM].current_size;
|
||
|
ins_f_code(F_CLEAR_LOCALS);
|
||
|
ins_byte(scope->first_local);
|
||
|
ins_byte(0);
|
||
|
}
|
||
|
|
||
|
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->num_locals == 1)
|
||
|
{
|
||
|
/* First definition of a local, so insert the
|
||
|
* clear_locals bytecode and remember its position
|
||
|
*/
|
||
|
scope->addr = mem_block[A_PROGRAM].current_size;
|
||
|
ins_f_code(F_CLEAR_LOCALS);
|
||
|
ins_byte(scope->first_local);
|
||
|
ins_byte(0);
|
||
|
}
|
||
|
|
||
|
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: <type> [*] <name> = <expr>
|
||
|
* It will be called after the call to define_local_variable().
|
||
|
* It assigns the result of <expr> to the variable.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
/* We got a "<name> = <expr>" 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 %d\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 * <lv> to the end of the program.
|
||
|
* If <instruction> is not zero, it is the code for an instruction
|
||
|
* to be added after the lvalue code.
|
||
|
* Return TRUE on success, and FALSE on failure.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
p_int length;
|
||
|
|
||
|
/* Create the code to push the lvalue */
|
||
|
length = lv->length;
|
||
|
if (length)
|
||
|
{
|
||
|
add_to_mem_block(A_PROGRAM, lv->u.p, length);
|
||
|
yfree(lv->u.p);
|
||
|
last_expression = CURRENT_PROGRAM_SIZE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bytecode_p source, dest;
|
||
|
mp_uint current_size;
|
||
|
|
||
|
source = lv->u.simple;
|
||
|
current_size = CURRENT_PROGRAM_SIZE;
|
||
|
if (!realloc_a_program(2))
|
||
|
{
|
||
|
yyerrorf("Out of memory: program size %lu"
|
||
|
, current_size+2);
|
||
|
return MY_FALSE;
|
||
|
}
|
||
|
CURRENT_PROGRAM_SIZE = (last_expression = current_size + 2);
|
||
|
dest = PROGRAM_BLOCK + current_size;
|
||
|
*dest++ = *source++;
|
||
|
*dest++ = *source;
|
||
|
}
|
||
|
|
||
|
if (instruction != 0)
|
||
|
ins_f_code(instruction);
|
||
|
|
||
|
return MY_TRUE;
|
||
|
} /* add_lvalue_code() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static void
|
||
|
insert_pop_value (void)
|
||
|
|
||
|
/* Remove the last value computed from the stack. If possible, use
|
||
|
* last_expression to prohibit that value from being generated
|
||
|
* in the first place.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
if (last_expression == CURRENT_PROGRAM_SIZE-1)
|
||
|
{
|
||
|
/* We don't have to fear sideeffects and try to prevent
|
||
|
* the value from being generated.
|
||
|
*/
|
||
|
switch ( mem_block[A_PROGRAM].block[last_expression])
|
||
|
{
|
||
|
case F_ASSIGN:
|
||
|
mem_block[A_PROGRAM].block[last_expression] =
|
||
|
F_VOID_ASSIGN;
|
||
|
break;
|
||
|
case F_ADD_EQ:
|
||
|
mem_block[A_PROGRAM].block[last_expression] =
|
||
|
F_VOID_ADD_EQ;
|
||
|
break;
|
||
|
case F_PRE_INC:
|
||
|
case F_POST_INC:
|
||
|
mem_block[A_PROGRAM].block[last_expression] =
|
||
|
F_INC;
|
||
|
break;
|
||
|
case F_PRE_DEC:
|
||
|
case F_POST_DEC:
|
||
|
mem_block[A_PROGRAM].block[last_expression] =
|
||
|
F_DEC;
|
||
|
break;
|
||
|
case F_CONST0:
|
||
|
case F_CONST1:
|
||
|
case F_NCONST1:
|
||
|
mem_block[A_PROGRAM].current_size = last_expression;
|
||
|
break;
|
||
|
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 <start>
|
||
|
* newcode: additional instruction to insert.
|
||
|
*
|
||
|
* The following scenarios exist:
|
||
|
*
|
||
|
* code >= 0 && end != 0:
|
||
|
* The multi-byte instruction in [<start>..<end>[ (which might
|
||
|
* be a complete indexing operation, but always excludes the
|
||
|
* actual instruction bytes to change) is moved to the end of
|
||
|
* the current program, then its alternative <code> and <newcode>
|
||
|
* are appended.
|
||
|
*
|
||
|
* Cases are:
|
||
|
* global
|
||
|
* IDENTIFIER16 -> PUSH_IDENTIFIER16_LVALUE
|
||
|
#ifdef USE_STRUCTS
|
||
|
* expr4->x
|
||
|
* S_INDEX -> PUSH_PROTECTED_INDEXED_S_LVALUE
|
||
|
#endif
|
||
|
* expr4[x]
|
||
|
* INDEX -> PUSH_PROTECTED_INDEXED_LVALUE
|
||
|
* expr4[<x]
|
||
|
* RINDEX -> 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 <start> (1 byte code, 1 byte argument)
|
||
|
* removed (the argument byte is preserved), instead the instructions
|
||
|
* <code> plus the preserved argument byte and <newcode> 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 <newcode> is a protected-index-lvalue code, for which
|
||
|
* the appropriate push-protected-index-lvalue code has
|
||
|
* to be appended to the program.
|
||
|
*
|
||
|
* Cases where this code is generated:
|
||
|
* F_STRING, F_NUMBER, F_CLOSURE, F_FLOAT, F_SYMBOL,
|
||
|
* (expr0), ({ expr,... }), '({ expr,... }), ([...]),
|
||
|
* x[a..b], x[<a..b], x[a..<b], x[<a..<b], x[a..], x[<a..],
|
||
|
* &global, &local, &(expr4[x]), &(expr4[<x]), &(expr4[x,y]),
|
||
|
* &(expr4[x..y]), &(expr4[<x..y]), &(expr4[x..<y]),
|
||
|
* &(expr4[<x..<y]).
|
||
|
*
|
||
|
* Cases accepted by the function:
|
||
|
* &(expr4[x]): F_PROTECTED_INDEX_LVALUE
|
||
|
* -> F_PUSH_PROTECTED_INDEXED_LVALUE;
|
||
|
* &(expr4[<x]): F_PROTECTED_RINDEX_LVALUE
|
||
|
* -> 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 %lu\n"
|
||
|
, CURRENT_PROGRAM_SIZE + length);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* Cycle the indexing code to the end, where it belongs:
|
||
|
*
|
||
|
* <indexing-code> <instrs>
|
||
|
* is changed via
|
||
|
* <...> <instrs> <indexing-code>
|
||
|
* to
|
||
|
* <instrs> <indexing-code>
|
||
|
*/
|
||
|
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
|
||
|
* <old-code> <arg> <instrs...>
|
||
|
* to
|
||
|
* <instrs...> <code> <arg> <newcode>
|
||
|
*/
|
||
|
|
||
|
int instr_arg;
|
||
|
p_int length;
|
||
|
|
||
|
if (!realloc_a_program(2))
|
||
|
{
|
||
|
yyerrorf("Out of memory: program size %lu\n"
|
||
|
, CURRENT_PROGRAM_SIZE + 2);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
p = PROGRAM_BLOCK + start;
|
||
|
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 <newcode> */
|
||
|
|
||
|
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 %lu\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 <function>, 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 <super_name>::<real_name> and return
|
||
|
* it's function index, setting *pIP to the inherit_t pointer and
|
||
|
* *pFlags to the function flags.
|
||
|
* Return -1 if not found, *pIP set to NULL, and *pFlags set to 0.
|
||
|
*
|
||
|
* <super_name> can be an empty string or the (partial) name of one
|
||
|
* of the inherits. <real_name> must be shared string.
|
||
|
*/
|
||
|
{
|
||
|
inherit_t *ip, *foundp;
|
||
|
int num_inherits, super_length;
|
||
|
short found_ix;
|
||
|
|
||
|
found_ix = -1;
|
||
|
*pIP = NULL;
|
||
|
*pFlags = 0;
|
||
|
|
||
|
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 <super_name>::<real_name> and return
|
||
|
* it's function index as result, and the inheritance index in *<pInherit>.
|
||
|
* 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 #'<inherited_fun> closures,
|
||
|
* and by restore_value()/restore_object() to restore closure values.
|
||
|
*
|
||
|
* <super_name> 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 <super_name>::<real_name>() call with
|
||
|
* <num_arg> arguments; the codepointer is <__prepare_insert__p>.
|
||
|
*
|
||
|
* Look up the function information and set *<super_p> and *<fun_p>
|
||
|
* the program pointer and the function_t information. Also compile
|
||
|
* the function call(s).
|
||
|
*
|
||
|
* Result is the function index, or one of the negative error codes:
|
||
|
* INHERITED_NOT_FOUND (-1): the function wasn't found.
|
||
|
* INHERITED_WILDCARDED_ARGS (-2): it was a wildcarded supercall with
|
||
|
* arguments
|
||
|
* INHERITED_WILDCARDED_NOT_FOUND (-3): it was a wildcarded supercall,
|
||
|
* but not a single function was found.
|
||
|
|
||
|
* 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.
|
||
|
*
|
||
|
* <super_name> can be an empty string, the (partial) name of one
|
||
|
* of the inherits, or a wildcarded name (and no args). In the latter
|
||
|
* case, the function is called in all inherits matching the pattern.
|
||
|
* The results from such a wildcarded call are returned in an array,
|
||
|
* <super_p>, <fun_p> and the returned function index are those of
|
||
|
* the first function found.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
inherit_t *ip;
|
||
|
funflag_t flags;
|
||
|
short found_ix;
|
||
|
|
||
|
found_ix = lookup_inherited(super_name, real_name, &ip, &flags);
|
||
|
|
||
|
if (ip != NULL)
|
||
|
{
|
||
|
/* Found it! */
|
||
|
bytecode_p __PREPARE_INSERT__p = __prepare_insert__p;
|
||
|
|
||
|
/* Generate the function call */
|
||
|
add_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 <to> is a cross-definition from real function <from>,
|
||
|
* separated by <offset>.
|
||
|
* Set the flags and offset of <to> accordingly to point to <from>, and
|
||
|
* synchronize the NO_MASK flag of both.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
short nomask;
|
||
|
to->flags = (to->flags & ~NAME_UNDEFINED)
|
||
|
| (from->flags & (NAME_UNDEFINED|NAME_PROTOTYPE))
|
||
|
| NAME_CROSS_DEFINED | NAME_HIDDEN | NAME_INHERITED;
|
||
|
to->offset.func = MAKE_CROSSDEF_OFFSET(offset);
|
||
|
nomask = (from->flags|to->flags) & TYPE_MOD_NO_MASK;
|
||
|
from->flags |= nomask;
|
||
|
to ->flags |= nomask;
|
||
|
} /* cross_define() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static funflag_t *
|
||
|
get_virtual_function_id (program_t *progp, int fx)
|
||
|
|
||
|
/* Return a pointer to the flags of the first entry of function <fx> in <progp>
|
||
|
* 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 <from> which is inherited
|
||
|
* with visibility <flags>.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
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 <from> are inherited with visibility <type>.
|
||
|
* Copy all the function definitions into this program, but as UNDEFINED
|
||
|
* so that they can be redefined in the current program. The epilog()
|
||
|
* will later update the non-redefined inherited functions and also copy
|
||
|
* the types.
|
||
|
*
|
||
|
* An explicit call to an inherited function will not be
|
||
|
* done through this entry (because this entry can be replaced by a new
|
||
|
* definition). If an function defined by inheritance is called,
|
||
|
* this is done with F_CALL_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 (%lu bytes)"
|
||
|
, (unsigned long) sizeof(efun_shadow_t));
|
||
|
break;
|
||
|
}
|
||
|
q->shadow = p;
|
||
|
q->next = all_efun_shadows;
|
||
|
all_efun_shadows = q;
|
||
|
}
|
||
|
|
||
|
/* Update the symbol table entry to point
|
||
|
* to the newly read function, 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 <from> with visibility <type>.
|
||
|
* The variables are copied into our program, and it is important that
|
||
|
* they are stored in the same order with the same index.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
int i, j;
|
||
|
int new_bound, last_bound;
|
||
|
int variable_index_offset, fun_index_offset;
|
||
|
uint inheritc;
|
||
|
inherit_t *inheritp;
|
||
|
int previous_variable_index_offset;
|
||
|
int from_variable_index_offset;
|
||
|
|
||
|
type &= ~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 <from>. This way we can compare the variables
|
||
|
* original type with the type they got through inheritance.
|
||
|
*/
|
||
|
|
||
|
/* Has a new virtual variable been introduced in this program?
|
||
|
*/
|
||
|
if (progp->num_variables
|
||
|
&& from->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 <from>.
|
||
|
* After that, we will loop once more in here, but
|
||
|
* the if() below will notice that.
|
||
|
* As a side effect we terminate immediately if <from>
|
||
|
* had no variables on its own.
|
||
|
*/
|
||
|
previous_variable_index_offset = from_variable_index_offset;
|
||
|
new_bound = from->num_variables;
|
||
|
if (new_bound == last_bound)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Check the visibility of the newly inspected variables
|
||
|
* [last_bound..new_bound[.
|
||
|
*/
|
||
|
for (j = last_bound; j < new_bound; j++)
|
||
|
{
|
||
|
ident_t *p;
|
||
|
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 <from>, which haven't been resolved
|
||
|
* to belong to some other inherit, are now assigned to the current
|
||
|
* inherit.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
int i, inherit_index;
|
||
|
function_t *funp;
|
||
|
|
||
|
inherit_index = INHERIT_COUNT;
|
||
|
funp =
|
||
|
(function_t *)
|
||
|
(mem_block[A_FUNCTIONS].block+mem_block[A_FUNCTIONS].current_size) -
|
||
|
from->num_functions;
|
||
|
|
||
|
for (i = from->num_functions; --i >= 0; funp++)
|
||
|
{
|
||
|
if ( funp->offset.inherit == NEW_INHERITED_INDEX
|
||
|
&& !(funp->flags & NAME_CROSS_DEFINED) )
|
||
|
{
|
||
|
funp->offset.inherit = inherit_index;
|
||
|
}
|
||
|
}
|
||
|
} /* fix_function_inherit_indices() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
static void
|
||
|
fix_variable_index_offsets (program_t *new_prog)
|
||
|
|
||
|
/* Add num_virtual_variables to the index_offset of all variables
|
||
|
* in <new_prog> marked with NON_VIRTUAL_OFFSET_TAG. The tag is removed.
|
||
|
*
|
||
|
* Reason is that the non-virtual variables have to be put after
|
||
|
* the virtual variables, so the offsets of these variables are
|
||
|
* first counted from 0 up and then corrected in this function after
|
||
|
* the last virtual inherit.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
int i;
|
||
|
inherit_t *inheritp;
|
||
|
|
||
|
i = new_prog->num_inherited;
|
||
|
for (inheritp = new_prog->inherit; --i >= 0; inheritp++)
|
||
|
{
|
||
|
if (inheritp->variable_index_offset & NON_VIRTUAL_OFFSET_TAG)
|
||
|
{
|
||
|
inheritp->variable_index_offset += num_virtual_variables;
|
||
|
inheritp->variable_index_offset &= ~NON_VIRTUAL_OFFSET_TAG;
|
||
|
}
|
||
|
}
|
||
|
} /* fix_variable_index_offsets() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
void
|
||
|
store_line_number_info (void)
|
||
|
|
||
|
{
|
||
|
unsigned char c;
|
||
|
short offset;
|
||
|
|
||
|
/* Was code generated since the last call?
|
||
|
* If not, return.
|
||
|
*/
|
||
|
offset = mem_block[A_PROGRAM].current_size - stored_bytes;
|
||
|
if (offset <= 0)
|
||
|
return;
|
||
|
stored_bytes = mem_block[A_PROGRAM].current_size;
|
||
|
|
||
|
/* Less than 8 bytes code in 2..9 lines */
|
||
|
if (offset <= 8
|
||
|
&& current_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 <relocated_from>.
|
||
|
* Add the relocation marker with the offset to <relocated_from>, call
|
||
|
* store_line_number_info() for the modified linenumbers and the added
|
||
|
* codeblock, then restore the current line number.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
int save_current, offset;
|
||
|
|
||
|
save_current = current_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 <offset> lines.
|
||
|
* Adapted the stored_lines counter and add the LI_BACK linenumber entry.
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
if (offset > 0)
|
||
|
{
|
||
|
store_line_number_info();
|
||
|
stored_lines -= offset;
|
||
|
while (offset > 256)
|
||
|
{
|
||
|
byte_to_mem_block(A_LINENUMBERS, LI_BACK);
|
||
|
byte_to_mem_block(A_LINENUMBERS, 255);
|
||
|
offset -= 256;
|
||
|
}
|
||
|
byte_to_mem_block(A_LINENUMBERS, LI_BACK);
|
||
|
byte_to_mem_block(A_LINENUMBERS, offset-1);
|
||
|
}
|
||
|
} /* store_line_number_backward() */
|
||
|
|
||
|
/*-------------------------------------------------------------------------*/
|
||
|
mp_uint
|
||
|
store_include_info (char *name, char * filename, char delim, int depth)
|
||
|
|
||
|
/* The lexer is going to include <name>, which can be the filename given
|
||
|
* in an #include directive, or a descriptive name for a different source.
|
||
|
* The full (file)name of the source as seen by the lexer is <filename>.
|
||
|
* This will be include depth <depth>.
|
||
|
* <delim> is either '"' or '>' if this include is from a file, or ')'
|
||
|
* if it's a different source.
|
||
|
*
|
||
|
* Result is the offset of the include information in the mem_block.
|
||
|
* It is to be considered a handle and has to be passed to
|
||
|
* store_include_end().
|
||
|
*/
|
||
|
|
||
|
{
|
||
|
mp_uint rc;
|
||
|
|
||
|
/* Generate and store the plain include information */
|
||
|
{
|
||
|
include_t inc;
|
||
|
char * tmp;
|
||
|
size_t len;
|
||
|
|
||
|
/* Make sure that the filename starts with a leading slash,
|
||
|
* then make it a tabled string and store it.
|
||
|
*/
|
||
|
if (*filename != '/')
|
||
|
{
|
||
|
tmp = alloca(strlen(filename)+2);
|
||
|
if (tmp == NULL)
|
||
|
{
|
||
|
yyerror("Out of stack memory: copy of filename");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*tmp = '/';
|
||
|
strcpy(tmp+1, filename);
|
||
|
filename = tmp;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
inc.filename = new_tabled(filename);
|
||
|
if (inc.filename == NULL)
|
||
|
{
|
||
|
inc.filename = ref_mstring(STR_DEFAULT);
|
||
|
yyerror("Out of memory: sharing include filename");
|
||
|
}
|
||
|
|
||
|
/* Surround the <name> with the delimiters, then
|
||
|
* make it a tabled string and store it.
|
||
|
*/
|
||
|
len = strlen(name);
|
||
|
tmp = alloca(len+3);
|
||
|
if (tmp == NULL)
|
||
|
{
|
||
|
yyerror("Out of stack memory: copy of name");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*tmp = delim == '"' ? delim
|
||
|
: (delim == '>' ? '<' : '(');
|
||
|
strcpy(tmp+1, name);
|
||
|
tmp[len+1] = delim;
|
||
|
tmp[len+2] = '\0';
|
||
|
|
||
|
inc.name = 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. <inc_offset> has to be the offset returned by
|
||
|
* store_include_info() for this include file, <include_line> 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.
|
||
|
* <fname> is the name of the top LPC file to be compiled.
|
||
|
* <isMasterObj> 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)
|
||
|
{
|
||
|
/* No such identifier, therefor no such sefun */
|
||
|
free_shared_identifier(id);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* This shouldn't be necessary, but just in case... */
|
||
|
while (id && id->type > I_TYPE_GLOBAL)
|
||
|
id = id->inferior;
|
||
|
|
||
|
if ( id
|
||
|
&& id->u.global.function < 0
|
||
|
&& id->u.global.sim_efun >= 0)
|
||
|
{
|
||
|
/* There is a sefun for call_other() */
|
||
|
call_other_sefun = id->u.global.sim_efun;
|
||
|
}
|
||
|
}
|
||
|
} /* 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 size, i;
|
||
|
mp_int num_functions;
|
||
|
mp_int num_strings;
|
||
|
mp_int num_variables;
|
||
|
bytecode_p p;
|
||
|
ident_t *g, *q;
|
||
|
function_t *f;
|
||
|
function_t *funname_start1; /* The name chains (to sort) */
|
||
|
function_t *funname_start2;
|
||
|
mp_int num_function_names;
|
||
|
program_t *prog;
|
||
|
|
||
|
/* First, clean up */
|
||
|
#ifdef DEBUG
|
||
|
if (num_parse_error == 0 && type_of_arguments.current_size != 0)
|
||
|
fatal("Failed to deallocate argument type stack\n");
|
||
|
#endif
|
||
|
|
||
|
if (last_string_constant)
|
||
|
|
||
|
{
|
||
|
free_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 %lu\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;
|
||
|
|
||
|
/* 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 #%ld) ref %ld, %d members, base %s, flags %lx\n"
|
||
|
, i, get_txt(ptype->name)
|
||
|
, ptype->prog_name ? get_txt(ptype->prog_name) : "<none>"
|
||
|
, (long)ptype->prog_id
|
||
|
, (long)ptype->ref
|
||
|
, ptype->num_members
|
||
|
, ptype->base ? get_txt(ptype->base->name) : "<none>"
|
||
|
, (long)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 (%u 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 (%lu bytes)"
|
||
|
, (unsigned long)linenumber_size);
|
||
|
break;
|
||
|
}
|
||
|
total_prog_block_size += linenumber_size;
|
||
|
prog->line_numbers->size = linenumber_size;
|
||
|
if (mem_block[A_LINENUMBERS].current_size)
|
||
|
memcpy( prog->line_numbers->line_numbers
|
||
|
, mem_block[A_LINENUMBERS].block
|
||
|
, mem_block[A_LINENUMBERS].current_size);
|
||
|
}
|
||
|
|
||
|
/* Correct the variable index offsets */
|
||
|
fix_variable_index_offsets(prog);
|
||
|
|
||
|
prog->swap_num = -1;
|
||
|
|
||
|
/* Free the memareas */
|
||
|
|
||
|
for (i = 0; (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 <fun> 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
|
||
|
*/
|