tmi2/
tmi2/bin/
tmi2/etc/
tmi2/lib/
tmi2/lib/adm/
tmi2/lib/adm/daemons/languages/
tmi2/lib/adm/daemons/network/I3/
tmi2/lib/adm/daemons/virtual/template/
tmi2/lib/adm/obj/master/
tmi2/lib/adm/priv/
tmi2/lib/adm/shell/
tmi2/lib/adm/tmp/
tmi2/lib/cmds/
tmi2/lib/d/
tmi2/lib/d/Conf/
tmi2/lib/d/Conf/adm/
tmi2/lib/d/Conf/boards/
tmi2/lib/d/Conf/cmds/
tmi2/lib/d/Conf/data/
tmi2/lib/d/Conf/logs/
tmi2/lib/d/Conf/obj/
tmi2/lib/d/Conf/text/help/
tmi2/lib/d/Fooland/adm/
tmi2/lib/d/Fooland/data/
tmi2/lib/d/Fooland/data/attic/
tmi2/lib/d/Fooland/items/
tmi2/lib/d/TMI/
tmi2/lib/d/TMI/adm/
tmi2/lib/d/TMI/boards/
tmi2/lib/d/TMI/data/
tmi2/lib/d/TMI/rooms/
tmi2/lib/d/grid/
tmi2/lib/d/grid/adm/
tmi2/lib/d/grid/data/
tmi2/lib/d/std/
tmi2/lib/d/std/adm/
tmi2/lib/data/adm/
tmi2/lib/data/adm/daemons/
tmi2/lib/data/adm/daemons/doc_d/
tmi2/lib/data/adm/daemons/emoted/
tmi2/lib/data/adm/daemons/network/http/
tmi2/lib/data/adm/daemons/network/services/mail_q/
tmi2/lib/data/adm/daemons/network/smtp/
tmi2/lib/data/adm/daemons/news/archives/
tmi2/lib/data/attic/connection/
tmi2/lib/data/attic/user/
tmi2/lib/data/std/connection/b/
tmi2/lib/data/std/connection/l/
tmi2/lib/data/std/user/a/
tmi2/lib/data/std/user/b/
tmi2/lib/data/std/user/d/
tmi2/lib/data/std/user/f/
tmi2/lib/data/std/user/l/
tmi2/lib/data/std/user/x/
tmi2/lib/data/u/d/dm/working/doc_d/
tmi2/lib/data/u/l/leto/doc_d/
tmi2/lib/data/u/l/leto/smtp/
tmi2/lib/doc/
tmi2/lib/doc/driverdoc/applies/
tmi2/lib/doc/driverdoc/concepts/
tmi2/lib/doc/driverdoc/driver/
tmi2/lib/doc/driverdoc/efuns/arrays/
tmi2/lib/doc/driverdoc/efuns/buffers/
tmi2/lib/doc/driverdoc/efuns/compile/
tmi2/lib/doc/driverdoc/efuns/ed/
tmi2/lib/doc/driverdoc/efuns/floats/
tmi2/lib/doc/driverdoc/efuns/functions/
tmi2/lib/doc/driverdoc/efuns/general/
tmi2/lib/doc/driverdoc/efuns/numbers/
tmi2/lib/doc/driverdoc/efuns/parsing/
tmi2/lib/doc/driverdoc/lpc/constructs/
tmi2/lib/doc/driverdoc/lpc/preprocessor/
tmi2/lib/doc/driverdoc/lpc/types/
tmi2/lib/doc/driverdoc/platforms/
tmi2/lib/doc/mudlib/
tmi2/lib/ftp/
tmi2/lib/log/
tmi2/lib/obj/net/
tmi2/lib/obj/shells/
tmi2/lib/std/board/
tmi2/lib/std/body/
tmi2/lib/std/fun/
tmi2/lib/std/living/
tmi2/lib/std/object/
tmi2/lib/std/shop/
tmi2/lib/std/socket/
tmi2/lib/std/virtual/
tmi2/lib/student/
tmi2/lib/student/kalypso/
tmi2/lib/student/kalypso/armor/
tmi2/lib/student/kalypso/rooms/
tmi2/lib/student/kalypso/weapons/
tmi2/lib/u/l/leto/
tmi2/lib/u/l/leto/cmds/
tmi2/lib/www/errors/
tmi2/lib/www/gateways/
tmi2/lib/www/images/
tmi2/old/
tmi2/v21.7a10/
tmi2/v21.7a10/ChangeLog.old/
tmi2/v21.7a10/compat/simuls/
tmi2/v21.7a10/include/
tmi2/v21.7a10/testsuite/
tmi2/v21.7a10/testsuite/clone/
tmi2/v21.7a10/testsuite/command/
tmi2/v21.7a10/testsuite/data/
tmi2/v21.7a10/testsuite/etc/
tmi2/v21.7a10/testsuite/include/
tmi2/v21.7a10/testsuite/inherit/
tmi2/v21.7a10/testsuite/inherit/master/
tmi2/v21.7a10/testsuite/log/
tmi2/v21.7a10/testsuite/u/
tmi2/v21.7a10/tmp/
#include "std.h"
#include "lpc_incl.h"
#include "compiler.h"
#include "trees.h"
#include "lex.h"
#include "functab_tree.h"
#include "generate.h"
#include "swap.h"
#include "scratchpad.h"
#include "qsort.h"
#include "file.h"
#include "binaries.h"
/* This should be moved with initializers move out of here */
#include "icode.h"

static void clean_parser PROT((void));
static void prolog PROT((int, char *));
static void epilog PROT((void));
static void show_overload_warnings PROT((void));

#define CT(x) (1 << (x))
#define CT_SIMPLE(x) (CT(TYPE_ANY) | CT(x))

short compatible[11] = { 
    /* UNKNOWN */  0,
    /* ANY */      0xfff,
    /* NOVALUE */  CT_SIMPLE(TYPE_NOVALUE) | CT(TYPE_VOID) | CT(TYPE_NUMBER),
    /* VOID */     CT_SIMPLE(TYPE_VOID) | CT(TYPE_NOVALUE) | CT(TYPE_NUMBER),
    /* NUMBER */   CT_SIMPLE(TYPE_NUMBER) | CT(TYPE_NOVALUE) | CT(TYPE_REAL) | CT(TYPE_VOID),
    /* STRING */   CT_SIMPLE(TYPE_STRING),
    /* OBJECT */   CT_SIMPLE(TYPE_OBJECT),
    /* MAPPING */  CT_SIMPLE(TYPE_MAPPING),
    /* FUNCTION */ CT_SIMPLE(TYPE_FUNCTION),
    /* REAL */     CT_SIMPLE(TYPE_REAL) | CT(TYPE_NUMBER),
    /* BUFFER */   CT_SIMPLE(TYPE_BUFFER),
	       };

short is_type[11] = { 
    /* UNKNOWN */ 0,
    /* ANY */     0xfff,
    /* NOVALUE */ CT_SIMPLE(TYPE_NOVALUE) | CT(TYPE_VOID),
    /* VOID */    CT_SIMPLE(TYPE_VOID) | CT(TYPE_NOVALUE),
    /* NUMBER */  CT_SIMPLE(TYPE_NUMBER),
    /* STRING */  CT_SIMPLE(TYPE_STRING),
    /* OBJECT */  CT_SIMPLE(TYPE_OBJECT),
    /* MAPPING */ CT_SIMPLE(TYPE_MAPPING),
    /* FUNCTION */CT_SIMPLE(TYPE_FUNCTION),
    /* REAL */    CT_SIMPLE(TYPE_REAL),
    /* BUFFER */  CT_SIMPLE(TYPE_BUFFER),
	      };

mem_block_t mem_block[NUMAREAS];

function_context_t function_context;

int exact_types, global_modifiers;

int current_type;

int current_block;
char *prog_code;
char *prog_code_max;

program_t NULL_program;

program_t *prog;

static short string_idx[0x100];
unsigned char string_tags[0x20];
short freed_string;

unsigned short *type_of_locals;
ident_hash_elem_t **locals;
char *runtime_locals;

unsigned short *type_of_locals_ptr;
ident_hash_elem_t **locals_ptr;
char *runtime_locals_ptr;

int locals_size = 0;
int type_of_locals_size = 0; 
int current_number_of_locals = 0;
int max_num_locals = 0;

#ifdef OPTIMIZE_FUNCTION_TABLE_SEARCH
unsigned short a_functions_root = (unsigned short) 0xffff;
#endif

char *get_two_types P2(int, type1, int, 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;
}

void init_locals()
{
    type_of_locals = CALLOCATE(MAX_LOCAL,unsigned short, 
			       TAG_LOCALS, "init_locals:1");
    locals = CALLOCATE(MAX_LOCAL, ident_hash_elem_t *, 
		       TAG_LOCALS, "init_locals:2");
    runtime_locals = CALLOCATE(MAX_LOCAL, char, 
			       TAG_LOCALS, "init_locals:3");
    type_of_locals_ptr = type_of_locals;
    locals_ptr = locals;
    runtime_locals_ptr = runtime_locals;
    locals_size = type_of_locals_size = MAX_LOCAL;
    current_number_of_locals = max_num_locals = 0;
}

void free_all_local_names()
{
    int i;

    for (i=0; i < current_number_of_locals; i++) {
	locals_ptr[i]->sem_value--;
	locals_ptr[i]->dn.local_num = -1;
    }
    current_number_of_locals = 0;
    max_num_locals = 0;
}

void deactivate_current_locals(){
    int i;

    for (i = 0; i < current_number_of_locals; i++){
	runtime_locals_ptr[i] = locals_ptr[i]->dn.local_num;
	locals_ptr[i]->dn.local_num = -1;
    }
}

void reactivate_current_locals(){
    int i;

    for (i = 0; i < current_number_of_locals; i++){
	locals_ptr[i]->dn.local_num = runtime_locals_ptr[i];
	locals_ptr[i]->sem_value++;
    }
}

void clean_up_locals()
{
    int offset;

    offset = locals_ptr + current_number_of_locals - locals;
    while (offset--){
	locals[offset]->sem_value--;
	locals[offset]->dn.local_num = -1;
    }
    current_number_of_locals = 0;
    max_num_locals = 0;
    locals_ptr = locals;
    type_of_locals_ptr = type_of_locals;
    runtime_locals_ptr = runtime_locals;
}

void pop_n_locals P1(int, num) {
    while (num--) {
	locals_ptr[--current_number_of_locals]->sem_value--;
	locals_ptr[current_number_of_locals]->dn.local_num = -1;
    }
}

int add_local_name P2(char *, str, int, type)
{
    if (max_num_locals == MAX_LOCAL) {
	yyerror("Too many local variables");
	return 0;
    } else {
	ident_hash_elem_t *ihe;

	ihe = find_or_add_ident(str,FOA_NEEDS_MALLOC);
	type_of_locals_ptr[max_num_locals] = type;
	locals_ptr[current_number_of_locals++] = ihe;
	if (ihe->dn.local_num == -1)
	    ihe->sem_value++;
	return ihe->dn.local_num = max_num_locals++;
    }
}

void reallocate_locals(){
    int offset;
    offset = type_of_locals_ptr - type_of_locals;
    type_of_locals = RESIZE(type_of_locals, type_of_locals_size += MAX_LOCAL,
			    unsigned short, TAG_LOCALS, "reallocate_locals:1");
    type_of_locals_ptr = type_of_locals + offset;
    offset = locals_ptr - locals;
    locals = RESIZE(locals, locals_size, 
		    ident_hash_elem_t *, TAG_LOCALS, "reallocate_locals:2");
    locals_ptr = locals + offset;
    runtime_locals = RESIZE(runtime_locals, locals_size, char,
			    TAG_LOCALS, "reallocate_locals:3");
    runtime_locals_ptr = runtime_locals + offset;
}

/*
 * Copy all variable names from the object that is inherited from.
 * It is very important that they are stored in the same order with the
 * same index.
 */
void copy_variables P2(program_t *, from, int, type)
{
    int i, numvars = from->num_variables, n;
    variable_t *from_vars = from->variable_names;
    ident_hash_elem_t *ihe;
    int new_type;

    for (i = 0; (unsigned) i < numvars; i++) {
        new_type = type;

        /*
         * 'public' variables should not become private when inherited
         * 'private'.
         */
        if (from_vars[i].type & TYPE_MOD_PUBLIC)
            new_type &= ~TYPE_MOD_PRIVATE;

        if ((ihe = lookup_ident(from->variable_names[i].name)) &&
	    ((n = ihe->dn.global_num) != -1)) {
            char tmp[2048];

            if (VARIABLE(n)->type & TYPE_MOD_NO_MASK) {
                sprintf(tmp, "Illegal to redefine 'nomask' variable \"%s\"",
                        VARIABLE(n)->name);
                yyerror(tmp);
	    }
	}
        define_variable(from->variable_names[i].name,
                        from->variable_names[i].type | new_type,
                        from->variable_names[i].type & TYPE_MOD_PRIVATE);
     } 
}

static void copy_function_details P2(function_t *, to, function_t *, from) {
    to->offset = from->offset;
    to->function_index_offset = from->function_index_offset;
    to->type = from->type;
    if (to->type & TYPE_MOD_PRIVATE)
	to->type |= TYPE_MOD_HIDDEN;
    
    to->num_local = from->num_local;
    to->num_arg = from->num_arg;
    to->flags = (from->flags & NAME_MASK) | NAME_DEF_BY_INHERIT | NAME_UNDEFINED;
}

/* copy a function verbatim into this object, and possibly add it to the
   list of functions in this object, as well
 */
static function_t *copy_function P2(function_t *, new, int, add) {
    function_t *ret;
    int num = mem_block[A_FUNCTIONS].current_size/sizeof(function_t);
    ident_hash_elem_t *ihe;

    ret = (function_t *)allocate_in_mem_block(A_FUNCTIONS, sizeof(function_t));
    *ret = *new;
    if (ret->type & TYPE_MOD_PRIVATE)
	ret->type |= TYPE_MOD_HIDDEN;
    
    ref_string(new->name); /* increment ref count */
    /* don't propagate certain flags forward */
    ret->flags &= NAME_MASK;
    if (!(new->flags & NAME_UNDEFINED))
	ret->flags |= NAME_DEF_BY_INHERIT;

    if (add) {
#ifdef OPTIMIZE_FUNCTION_TABLE_SEARCH
	add_function((function_t *) mem_block[A_FUNCTIONS].block,
		     &a_functions_root, num);
#endif
	/* add the identifier */
	ihe = find_or_add_ident(new->name, FOA_GLOBAL_SCOPE);
	if (ihe->dn.function_num == -1)
	    ihe->sem_value++;
	ihe->dn.function_num = num;
    }
    return ret;
}

void copy_structures P1(program_t *, prog) {
    class_def_t *sd;
    class_member_entry_t *sme;
    ident_hash_elem_t *ihe;
    char *str;
    int sm_off = mem_block[A_CLASS_MEMBER].current_size / sizeof(class_member_entry_t);
    int sd_off = mem_block[A_CLASS_DEF].current_size / sizeof(class_def_t);
    int i, num = 0;

    sd = (class_def_t *)allocate_in_mem_block(A_CLASS_DEF, prog->num_classes * sizeof(class_def_t));
    for (i = 0; i < prog->num_classes; i++) {
	sd[i].size = prog->classes[i].size;
	num += sd[i].size;
	sd[i].index = prog->classes[i].index + sm_off;
	str = prog->strings[prog->classes[i].name];
	sd[i].name = store_prog_string(str);
	ihe = find_or_add_ident(str, FOA_GLOBAL_SCOPE);
	if (ihe->dn.class_num == -1)
	    ihe->sem_value++;
	ihe->dn.class_num = i + sd_off;
    }

    sme = (class_member_entry_t *)allocate_in_mem_block(A_CLASS_MEMBER, sizeof(class_member_entry_t) * num);
    while (num--) {
	sme[num].type = prog->class_members[num].type;
	sme[num].name = store_prog_string(prog->strings[prog->class_members[num].name]);
    }
}

typedef struct ovlwarn_s {
    struct ovlwarn_s *next;
    char *func;
    char *warn;
} ovlwarn_t;

ovlwarn_t *overload_warnings = 0;

static void remove_overload_warnings P1(char *, func) {
    ovlwarn_t **p;
    ovlwarn_t *tmp;

    p = &overload_warnings;
    while (*p) {
	if (!func || (*p)->func == func) {
	    FREE((*p)->warn);
	    tmp = *p;
	    *p = (*p)->next;
	    FREE(tmp);
	} else
	    p = &(*p)->next;
    }
}

static void show_overload_warnings() {
    ovlwarn_t *p, *next;
    p = overload_warnings;
    while (p) {
	yywarn(p->warn);
	FREE(p->warn);
	next = p->next;
	FREE(p);
	p = next;
    }
    overload_warnings = 0;
}

/* Overload the function index with the new definition */
static function_t *overload_function P3(int, index, program_t *, prog, int, newindex) {
    function_t *funp, *alias;
    function_t *new;

    new = &prog->functions[newindex];
    funp = (function_t *)mem_block[A_FUNCTIONS].block + index;
    /* Be careful with nomask; if both functions exists and either is nomask,
       we error.  */
    if (!(new->flags & NAME_NO_CODE) && REAL_FUNCTION(funp)
        && ((new->type & TYPE_MOD_NO_MASK) || (funp->type & TYPE_MOD_NO_MASK))
       ) {
	char buf[2048];
	sprintf(buf, "Illegal to redefine 'nomask' function \"%s\"",
		funp->name);
	yyerror(buf);
    }
    /* Try to prevent some confusion re: overloading.
     * Warn them about the behavior of inheriting the same function
     * from two branches.
     * 
     * Note that we don't want to scream now, b/c if the function is 
     * overloaded later this becomes irrelevant.
     *
     * Note also that this is real spammy if you inherit the same object
     * twice.  Something should be done about that.
     */
    if ((pragmas & PRAGMA_WARNINGS) 
	&& REAL_FUNCTION(funp) && !(new->flags & NAME_NO_CODE)) {
	/* don't scream if one is private.  Why not?  Because I said so.
	 * private is pretty screwed up anyway.  In the future there
	 * won't be such a clash b/c private won't come up the tree.
	 * This also give the coder a way to shut the compiler up when
	 * you do inherit the same object twice in different branches :)
	 */
	if (!(funp->type & TYPE_MOD_PRIVATE) && !(new->type & TYPE_MOD_PRIVATE)) {
	    char buf[1024];
	    char *from1 ;
	    ovlwarn_t *ow;
	    
	    from1 = ((inherit_t *) mem_block[A_INHERITS].block + funp->offset)->prog->name;
	    sprintf(buf, "%s() inherited from both %s and %s; using the definition in %s.", funp->name, from1, prog->name, prog->name);
	
	    ow = ALLOCATE(ovlwarn_t, TAG_COMPILER, "overload warning");
	    ow->next = overload_warnings;
	    ow->func = funp->name;
	    ow->warn = alloc_cstring(buf, "overload warning");
	    overload_warnings = ow;
	}
    }

    /* A new function also has to be inserted, since this spot will be
       used when this function is called in an object beneath us.  Point
       it at the overloaded function. */
    alias = copy_function(new, 0);
    /* Ick! copy_functions calls allocate_in_mem_block(), so funp might
       be dangling now.  Be safe and find it again. */
    funp = (function_t *)mem_block[A_FUNCTIONS].block + index;
    alias->flags = NAME_ALIAS;
    /* offset to the real def */
    alias->offset = alias - funp;

    /* The rule here is that the latest function wins, so if it's not
       defined at this level and defined in the new object, we copy it in */
    if ((funp->flags & NAME_UNDEFINED) && (!(new->flags & NAME_NO_CODE)))
	copy_function_details(funp, new);
    else
	funp = 0; /* we aren't overloading */

    return funp;
}

/*
 * Copy all function definitions from an inherited object. They are added
 * as undefined, so that they can be redefined by a local definition.
 * If they are not redefined, then they will be updated, so that they
 * point to the inherited definition. See epilog(). Types will be copied
 * at that moment (if available).
 *
 * A 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, then one
 * special definition will be made at first call.
 */
int copy_functions P2(program_t *, from, int, type)
{
    int i, initializer = -1, num_functions = from->num_functions;
    unsigned short tmp_short;
    function_t *from_funcs = from->functions;
    function_t *funp;
    int new_type;
    ident_hash_elem_t *ihe;
    int num;


    if (num_functions && (*from_funcs[num_functions-1].name == APPLY___INIT_SPECIAL_CHAR)){
        initializer = --num_functions;
    }
	
    for (i = 0; i < num_functions; i++) {
	    
	ihe = lookup_ident(from_funcs[i].name);
	if (ihe && ((num = ihe->dn.function_num)!=-1)) {
	  /* The function has already been defined in this object */
	  funp = overload_function(num, from, i);
	} else {
	  funp = copy_function(&from_funcs[i], 1);
	  /* the function hasn't been defined at this level yet */
	  funp->flags |= NAME_UNDEFINED;
	}

	if (funp) {
	    /* point the new function entry at the one in the inherited file,
	       in case it's not overloaded and this becomes a real function */
	    funp->offset = mem_block[A_INHERITS].current_size / sizeof(inherit_t) - 1;
	    funp->function_index_offset = i;

#ifdef PROFILE_FUNCTIONS
	    funp->calls = 0L;
	    funp->self = 0L;
	    funp->children = 0L;
#endif
	    
	    /*
	     * public functions should not become private when inherited
	     * 'private'
	     */
	    new_type = type;
	    if (funp->type & TYPE_MOD_PUBLIC)
		new_type &= ~TYPE_MOD_PRIVATE;
	    funp->type |= new_type;
	}

	/* Beek - some of this stuff below belongs above where we decide
	 * whether to copy the function or not.  But this code doesn't
	 * do anything yet anyway.
	 * shouldn't this depend on save_types anyway? Anyway, future
	 * work.
	 */
	    
	/*
	 * Copy information about the types of the arguments, if it is
	 * available.
	 */
	tmp_short = INDEX_START_NONE;	/* Presume not available. */
	if (exact_types && from->type_start != 0 && 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 = mem_block[A_ARGUMENT_TYPES].current_size /
		sizeof from->argument_types[0];
	    add_to_mem_block(A_ARGUMENT_TYPES, 
			     (char *) &from->argument_types[from->type_start[i]],
			     sizeof(unsigned short) * from->functions[i].num_arg);
	}
	/*
	 * 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, (char *) &tmp_short, sizeof tmp_short);
    }
    return initializer;
}

void type_error P2(char *, str, int, type)
{
    static char buff[100];
    char *p;

    p = get_type_name(type);
    if (strlen(str) + strlen(p) + 5 >= sizeof buff) {
	yyerror(str);
    } else {
	strcpy(buff, str);
	strcat(buff, ": \"");
	strcat(buff, p);
	strcat(buff, "\"");
	yyerror(buff);
    }
}

/*
 * Compare two types, and return true if they are compatible.
 */

int compatible_types P2(int, t1, int, t2)
{
#ifdef OLD_TYPE_BEHAVIOR
    /* The old version effectively was almost always was true */
    return 1;
#else
    t1 &= TYPE_MOD_MASK;
    t2 &= TYPE_MOD_MASK;
    if (t1 == TYPE_ANY || t2 == TYPE_ANY) return 1;
    if ((t1 == (TYPE_ANY | TYPE_MOD_ARRAY) && (t2 & TYPE_MOD_ARRAY)))
	return 1;
    if ((t2 == (TYPE_ANY | TYPE_MOD_ARRAY) && (t1 & TYPE_MOD_ARRAY)))
	return 1;
    if (t1 & TYPE_MOD_CLASS)
	return t1 == t2;
    if (t1 & TYPE_MOD_ARRAY) {
	if (!(t2 & TYPE_MOD_ARRAY)) return 0;
	return t1 == (TYPE_MOD_ARRAY | TYPE_ANY) ||
	       t2 == (TYPE_MOD_ARRAY | TYPE_ANY) || (t1 == t2);
    } else if (t2 & TYPE_MOD_ARRAY)
	return 0;
    return compatible[t1] & (1 << t2);
#endif
}

/*
 * Patch a function definition of an inherited function, to what it really
 * should be.
 * The name of the function can be one of:
 *    object::name
 *    ::name
 * Where 'object' is the name of the superclass.
 *
 * Note: this function is now only used for resolving :: references
 */
void arrange_call_inherited P2(char *, name, parse_node_t *, node)
{
    int i;
    inherit_t *ip;
    int num_inherits, super_length = 0;
    char *super_name = 0, *p, *real_name = name;
    char *shared_string;

    if (real_name[0] == ':')
	real_name += 2; 	/* There will be exactly two ':' */
    else if ((p = strchr(real_name, ':'))) {
        super_name = name;
        real_name = p+2;
        super_length = real_name - super_name - 2;
    }
    num_inherits = mem_block[A_INHERITS].current_size /
	sizeof(inherit_t);
    /* no need to look for it unless its in the shared string table */
    if ((shared_string = findstring(real_name))) {
	ip = (inherit_t *) mem_block[A_INHERITS].block;
	for (; num_inherits > 0; ip++, num_inherits--) {
	    if (super_name) {
		int l = strlen(ip->prog->name);	/* Including .c */

		if (l - 2 < super_length)
		    continue;
		if (strncmp(super_name, ip->prog->name + l - 2 -super_length,
			    super_length) != 0 ||
                    !((l - 2 == super_length) ||
                     ((ip->prog->name + l - 3 - super_length)[0] == '/')))
                    continue;
	    }
#ifdef OPTIMIZE_FUNCTION_TABLE_SEARCH
	    i = lookup_function(ip->prog->functions,
				ip->prog->tree_r, shared_string);
	    if (i != -1 && !(ip->prog->functions[i].flags
			     & NAME_UNDEFINED)) {
#else
	    for (i = 0; (unsigned) i < ip->prog->num_functions; i++) {
		if (ip->prog->functions[i].flags & NAME_UNDEFINED)
		    continue;
		    /* can use pointer compare because both are shared */
		if (ip->prog->functions[i].name != shared_string)
		    continue;
#endif
		node->kind = NODE_CALL_2;
		node->v.number = F_CALL_INHERITED;
		node->l.number = i + ((ip - (inherit_t *) mem_block[A_INHERITS].block) << 16);
		node->type = ip->prog->functions[i].type;
		return;
	    }
	}
    }				/* if in shared string table */
    {
	char buff[MAXLINE + 30];
	sprintf(buff, "No such inherited function %.50s", name);
	yyerror(buff);
	node->kind = NODE_CALL_2;
	node->v.number = F_CALL_INHERITED;
	node->l.number = 0;
	node->type = TYPE_ANY;
    }
}

/*
 * Define a new function. Note that this function is called at least twice
 * for all function definitions. First as a prototype, then as the real
 * function. Thus, there are tests to avoid generating error messages more
 * than once by looking at (flags & NAME_PROTOTYPE).
 */
int define_new_function P5(char *, name, int, num_arg, int, num_local,
                           int, flags, int, type)
{
    int num;
    function_t fun;
    unsigned short argument_start_index;
    ident_hash_elem_t *ihe;

    num = (ihe = lookup_ident(name)) ? ihe->dn.function_num : -1;
    if (num >= 0) {
	function_t *funp;

	/*
	 * 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 doubly defined.
	 * 5.	A "late" prototype has been encountered.
	 */
	funp = (function_t *) (mem_block[A_FUNCTIONS].block) + num;
	if (!(funp->flags & NAME_UNDEFINED) &&
	    !(flags & NAME_PROTOTYPE)) {
	    char buff[500];

	    sprintf(buff, "Redeclaration of function %s.", name);
	    yyerror(buff);
	    return num;
	}
	/*
	 * It was either an undefined but used funtion, 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 ((funp->type & TYPE_MOD_NO_MASK) &&
	    !(funp->flags & NAME_PROTOTYPE) &&
	    !(flags & NAME_PROTOTYPE)) {
	    char p[2048];

	    sprintf(p, "Illegal to redefine 'nomask' function \"%s\"", name);
	    yyerror(p);
	}
	/* only check prototypes for matching.  It shouldn't be required that
	   overloading a function must have the same signature */
	if (exact_types && (funp->flags & NAME_PROTOTYPE) && funp->type != TYPE_UNKNOWN) {
	    int i;

	    /* This should be changed to catch two prototypes which disagree */
	    if (funp->num_arg != num_arg && !(funp->type & TYPE_MOD_VARARGS)
		&& !(flags & NAME_PROTOTYPE))
		yyerror("Number of arguments disagrees with previous definition.");
	    else if (!(funp->flags & NAME_STRICT_TYPES) && !(flags & NAME_PROTOTYPE))
		yyerror("Called function not compiled with type testing.");
	    else {
		/* Now check that argument types wasn't changed. */
		if ((type & TYPE_MOD_MASK) != (funp->type & TYPE_MOD_MASK)) {
		    char buff[200];
		    sprintf(buff, "Return type doesn't match prototype %s",
			    get_two_types(type, funp->type));
		    yywarn(buff);
		}
		
		for (i = 0; i < num_arg; i++) {
		    /* FIXME: check arg types here */
		}
	    }
	}
	/* If it was yet another prototype, then simply return. */
	if (flags & NAME_PROTOTYPE) {
	    return num;
	}

	if (pragmas & PRAGMA_WARNINGS)
	    remove_overload_warnings(funp->name);
	
	funp->num_arg = num_arg;
	funp->num_local = num_local;
	funp->flags = flags;
	funp->offset = 0;
	funp->function_index_offset = 0;
	funp->type = type;
	if (exact_types)
	    funp->flags |= NAME_STRICT_TYPES;
	return num;
    }
    fun.name = make_shared_string(name);
    fun.offset = 0;
    fun.flags = flags;
    fun.num_arg = num_arg;
    fun.num_local = num_local;
    fun.function_index_offset = 0;
    fun.type = type;
#ifdef PROFILE_FUNCTIONS
    fun.calls = 0L;
    fun.self = 0L;
    fun.children = 0L;
#endif

    if (exact_types)
	fun.flags |= NAME_STRICT_TYPES;
    num = mem_block[A_FUNCTIONS].current_size / sizeof fun;
    /* Number of local variables will be updated later */
    add_to_mem_block(A_FUNCTIONS, (char *) &fun, sizeof fun);
#ifdef OPTIMIZE_FUNCTION_TABLE_SEARCH
    add_function((function_t *) mem_block[A_FUNCTIONS].block,
		 &a_functions_root, num);
#endif

    if (exact_types == 0 || num_arg == 0) {
	argument_start_index = INDEX_START_NONE;
    } else {
	int i;

	/*
	 * Save the start of argument types.
	 */
	argument_start_index =
	    mem_block[A_ARGUMENT_TYPES].current_size /
	    sizeof(unsigned short);
	for (i = 0; i < num_arg; i++) {
	    add_to_mem_block(A_ARGUMENT_TYPES, 
			     (char *) &type_of_locals_ptr[i],
			     sizeof type_of_locals_ptr[i]);
	}
    }
    add_to_mem_block(A_ARGUMENT_INDEX, (char *) &argument_start_index,
		     sizeof argument_start_index);
    ihe = find_or_add_ident(fun.name, FOA_GLOBAL_SCOPE);
    if (ihe->dn.function_num == -1)
	ihe->sem_value++;
    ihe->dn.function_num = num;
    return num;
}

int define_variable P3(char *, name, int, type, int, hide)
{
    variable_t *dummy;
    int n;
    char *str;
    ident_hash_elem_t *ihe;

    str = make_shared_string(name);

    n = (mem_block[A_VARIABLES].current_size / sizeof(variable_t));

    ihe = find_or_add_ident(str, FOA_GLOBAL_SCOPE);
    if (ihe->dn.global_num == -1) {
	ihe->sem_value++;
	ihe->dn.global_num = n;
    } else {
	if (VARIABLE(ihe->dn.global_num)->type & TYPE_MOD_NO_MASK) {
	    char p[2048];
	    
	    sprintf(p, "Illegal to redefine 'nomask' variable \"%s\"", name);
	    yyerror(p);
	}
	/* Okay, the nasty idiots have two variables of the same name in
	   the same object.  This causes headaches for save_object().
	   To keep save_object sane, we need to make one static */
	if (!(type & TYPE_MOD_STATIC)) {
	    /* this one isn't static, make the other one static */
	    VARIABLE(ihe->dn.global_num)->type |= TYPE_MOD_STATIC;
	}
	/* hidden variables don't cause variables that are visible to become
	   invisible; we only add them above (in the !hide case) for better
	   error messages */
	if (!hide)
	    ihe->dn.global_num = n;
    }

    dummy = (variable_t *)allocate_in_mem_block(A_VARIABLES,sizeof(variable_t));
    dummy->name = str;
    dummy->type = type;

    if (hide) dummy->type |= TYPE_MOD_HIDDEN;

    return n;
}

char *compiler_type_names[] = {"unknown", "mixed", "void", "void", 
		               "int", "string", "object", "mapping",
		               "function", "float", "buffer" };

char *get_type_name P1(int, type)
{
    static char buff[100];
    int pointer = 0;

    buff[0] = 0;
    if (type & TYPE_MOD_STATIC)
	strcat(buff, "static ");
    if (type & TYPE_MOD_NO_MASK)
	strcat(buff, "nomask ");
    if (type & TYPE_MOD_PRIVATE)
	strcat(buff, "private ");
    if (type & TYPE_MOD_PROTECTED)
	strcat(buff, "protected ");
    if (type & TYPE_MOD_PUBLIC)
	strcat(buff, "public ");
    if (type & TYPE_MOD_VARARGS)
	strcat(buff, "varargs ");
    type &= TYPE_MOD_MASK;
    if (type & TYPE_MOD_ARRAY) {
	pointer = 1;
	type &= ~TYPE_MOD_ARRAY;
    }
    if (type & TYPE_MOD_CLASS) {
	strcat(buff, "class ");
	strcat(buff, PROG_STRING(CLASS(type & ~TYPE_MOD_CLASS)->name));
    } else {
	DEBUG_CHECK(type >= sizeof compiler_type_names / sizeof compiler_type_names[0], "Bad type\n");
	strcat(buff, compiler_type_names[type]);
    }
    strcat(buff, " ");
    if (pointer)
	strcat(buff, "* ");
    return buff;
}

#define STRING_HASH(var,str) \
    var = (long)str ^ (long)str >> 16; \
    var = (var ^ var >> 8) & 0xff;

short store_prog_string P1(char *, str)
{
    short i, next, *next_tab, *idxp;
    char **p;
    unsigned char hash, mask, *tagp;

    str = make_shared_string(str);
    STRING_HASH(hash, str);
    idxp = &string_idx[hash];

    /* string_tags is a big bit-array, so find correct bit */
    mask = 1 << (hash & 7);
    tagp = &string_tags[hash >> 3];

    p = (char **)&PROG_STRING(0);
    next_tab = (short *) mem_block[A_STRING_NEXT].block;

    if (*tagp & mask) {
	/* search hash chain to see if it's there */
	for (i = *idxp; i >= 0; i = next_tab[i]) {
	    if (p[i] == str) {
		free_string(str);	/* needed as string is only free'ed
					 * once. */
		((short *) mem_block[A_STRING_REFS].block)[i]++;
		return i;
	    }
	}
	next = *idxp;
    } else {
	*tagp |= mask;
	next = -1;
    }

    /*
     * New string, add to table
     */

    if (freed_string >= 0) {
	/* reuse freed string */
	int top;

	i = freed_string;

	top = mem_block[A_STRINGS].current_size / sizeof str;
	for (freed_string++; freed_string < top; freed_string++) {
	    if (p[freed_string] == 0)
		break;
	}
	if (freed_string >= top)
	    freed_string = -1;
    } else {
	/* grow by one element. */
	add_to_mem_block(A_STRINGS, 0, sizeof str);
	add_to_mem_block(A_STRING_NEXT, 0, sizeof(short));
	add_to_mem_block(A_STRING_REFS, 0, sizeof(short));
	/* test if number of strings isn't too large ? */
	i = mem_block[A_STRINGS].current_size / sizeof str - 1;
    }
    PROG_STRING(i) = str;
    ((short *) mem_block[A_STRING_NEXT].block)[i] = next;
    ((short *) mem_block[A_STRING_REFS].block)[i] = 1;
    *idxp = i;
    return i;
}

void free_prog_string P1(short, num)
{
    short i, prv, *next_tab, top, *idxp;
    char **p, *str;
    unsigned char hash, mask;

    top = mem_block[A_STRINGS].current_size / sizeof(char *) - 1;
    if (num < 0 || num > top) {
	yyerror("free_prog_string: index out of range.\n");
	return;
    }
    if (--((short *) mem_block[A_STRING_REFS].block)[num] >= 1)
	return;

    p = (char **) mem_block[A_STRINGS].block;
    next_tab = (short *) mem_block[A_STRING_NEXT].block;

    str = p[num];
    STRING_HASH(hash, str);
    idxp = &string_idx[hash];

    for (prv = -1, i = *idxp; i != num; prv = i, i = next_tab[i]) {
	if (i == -1) {
	    yyerror("free_prog_string: string not in prog table.\n");
	    return;
	}
    }

    if (prv == -1) {		/* string is head of list */
	*idxp = next_tab[i];
	if (*idxp == -1) {
	    /* clear tag bit since hash chain now empty */
	    mask = 1 << (hash & 7);
	    string_tags[hash >> 3] &= ~mask;
	}
    } else {			/* easy unlink */
	next_tab[prv] = next_tab[i];
    }

    free_string(str);		/* important */
    p[i] = 0;
    if (i != top) {
	if (i < freed_string || freed_string == -1)
	    freed_string = i;
    } else {
	/* shrink table */
	mem_block[A_STRINGS].current_size -= sizeof str;
	mem_block[A_STRING_REFS].current_size -= sizeof(short);
	mem_block[A_STRING_NEXT].current_size -= sizeof(short);
    }
}

int validate_function_call P3(function_t *, funp, int, f, parse_node_t *, args)
{
    int num_arg = ( args ? args->kind : 0 );
    int num_var = 0;
    parse_node_t *pn = args;
    
    while (pn) {
	if (pn->type & 1) num_var++;
	pn = pn->r.expr;
    }
	
    /*
     * Verify that the function has been defined already.
     */
    if ((funp->flags & NAME_UNDEFINED) &&
	!(funp->flags & (NAME_PROTOTYPE | NAME_DEF_BY_INHERIT)) && exact_types) {
	char buff[256];

	sprintf(buff, "Function %.50s undefined", funp->name);
	yyerror(buff);
    }
    /*
     * Check number of arguments.
     */
    if (!(funp->type & TYPE_MOD_VARARGS) &&
	(funp->flags & NAME_STRICT_TYPES) &&
	exact_types) {
	char buff[100];

	if (num_var) {
	    sprintf(buff, "Illegal to pass a variable number of arguments to non-varargs function %.60s\n", funp->name);
	    yyerror(buff);
	} else
	if (funp->num_arg != num_arg) {
	    sprintf(buff, "Wrong number of arguments to %.60s\n    Expected: %d  Got: %d", funp->name, funp->num_arg, num_arg);
	    yyerror(buff);
	}
    }
    /*
     * Check the argument types.
     */
    if (exact_types && *(unsigned short *)
	&mem_block[A_ARGUMENT_INDEX].block[f * sizeof(unsigned short)]
	!= INDEX_START_NONE) {
	int i, first, tmp;
	unsigned short *arg_types;
	parse_node_t *enode = args;

	arg_types = (unsigned short *) mem_block[A_ARGUMENT_TYPES].block;
	first = *(unsigned short *) &mem_block[A_ARGUMENT_INDEX].block[f * sizeof(unsigned short)];
	for (i = 0; (unsigned) i < funp->num_arg && i < num_arg; i++) {
	    if (enode->type & 1) break;
	    tmp = enode->v.expr->type;

	    if (!compatible_types(tmp, arg_types[first + i])) {
		char buff[100];

		sprintf(buff, "Bad type for argument %d %s", i + 1,
			get_two_types(arg_types[first + i], tmp));
		yyerror(buff);
	    }
	    enode = enode->r.expr;
	}
    }
    return funp->type & TYPE_MOD_MASK;
}

parse_node_t *
promote_to_float P1(parse_node_t *, node) {
    parse_node_t *expr;
    if (node->kind == NODE_NUMBER) {
	node->kind = NODE_REAL;
	node->v.real = node->v.number;
	return node;
    }
    expr = new_node();
    expr->kind = NODE_EFUN;
    expr->v.number = F_TO_FLOAT;
    expr->type = TYPE_REAL;
    expr->l.number = 1;
    expr->r.expr = new_node_no_line();
    expr->r.expr->kind = 1;
    expr->r.expr->l.expr = expr->r.expr;
    expr->r.expr->type = 0;
    expr->r.expr->v.expr = node;
    expr->r.expr->r.expr = 0;
    return expr;
}

parse_node_t *
promote_to_int P1(parse_node_t *, node) {
    parse_node_t *expr;
    if (node->kind == NODE_REAL) {
	node->kind = NODE_NUMBER;
	node->v.number = node->v.real;
	return node;
    }
    expr = new_node();
    expr->kind = NODE_EFUN;
    expr->v.number = F_TO_INT;
    expr->type = TYPE_NUMBER;
    expr->l.number = 1;
    expr->r.expr = new_node_no_line();
    expr->r.expr->kind = 1;
    expr->r.expr->l.expr = expr->r.expr;
    expr->r.expr->type = 0;
    expr->r.expr->v.expr = node;
    expr->r.expr->r.expr = 0;
    return expr;
}

parse_node_t *do_promotions P2(parse_node_t *, node, int, type) {
    if (type == TYPE_REAL) {
	if (node->type == TYPE_NUMBER || node->kind == NODE_NUMBER)
	    return promote_to_float(node);
    }
    if (type == TYPE_NUMBER && node->type == TYPE_REAL)
	return promote_to_int(node);
    return node;
}

/* Take a NODE_CALL, and discard the call, preserving only the args with
   side effects */
parse_node_t *
throw_away_call P1(parse_node_t *, pn) {
    parse_node_t *enode;
    parse_node_t *ret = 0;
    parse_node_t *arg;
    
    enode = pn->r.expr;
    while (enode) {
	arg = insert_pop_value(enode->v.expr);
	if (arg) {
	    /* woops.  Don't lose the side effect. */
	    if (ret) {
		parse_node_t *tmp;
		CREATE_STATEMENTS(tmp, ret, arg);
		ret = tmp;
	    } else {
		ret = arg;
	    }
	}
	enode = enode->r.expr;
    }
    return ret;
}

parse_node_t *
throw_away_mapping P1(parse_node_t *, pn) {
    parse_node_t *enode;
    parse_node_t *ret = 0;
    parse_node_t *arg;
    
    enode = pn->r.expr;
    while (enode) {
	arg = insert_pop_value(enode->v.expr->l.expr);
	if (arg) {
	    /* woops.  Don't lose the side effect. */
	    if (ret) {
		parse_node_t *tmp;
		CREATE_STATEMENTS(tmp, ret, arg);
		ret = tmp;
	    } else {
		ret = arg;
	    }
	}
	arg = insert_pop_value(enode->v.expr->r.expr);
	if (arg) {
	    /* woops.  Don't lose the side effect. */
	    if (ret) {
		parse_node_t *tmp;
		CREATE_STATEMENTS(tmp, ret, arg);
		ret = tmp;
	    } else {
		ret = arg;
	    }
	}
	enode = enode->r.expr;
    }
    return ret;
}

parse_node_t *
validate_efun_call P2(int, f, parse_node_t *, args) {
    int num = args->v.number;
    int min_arg, max_arg, def, *argp;
    int num_var = 0;
    parse_node_t *pn = args->r.expr;
    
    while (pn) {
	if (pn->type & 1) num_var++;
	pn = pn->r.expr;
    }
    
    if (f != -1) {
	/* should this move out of here? */
	switch (predefs[f].token) {
	case F_SIZEOF:
	    /* Obscene crap like: sizeof( ({ 1, i++, x + 1, foo() }) )
	     *                    -> i++, foo(), 4
	     */
	    if (!pn && num == 1 && 
		IS_NODE(args->r.expr->v.expr, NODE_CALL, F_AGGREGATE)) {
		parse_node_t *repl, *ret, *node;

		CREATE_NUMBER(node, args->r.expr->v.expr->l.number);
		ret = throw_away_call(args->r.expr->v.expr);
		CREATE_TWO_VALUES(repl, TYPE_NUMBER, ret, node);
		
		return repl;
	    }
	}

	min_arg = predefs[f].min_args;
	max_arg = predefs[f].max_args;

	def = predefs[f].Default;
	if (!num_var && def != DEFAULT_NONE && num == min_arg -1) {
	    parse_node_t *tmp;
	    tmp = new_node_no_line();
	    tmp->r.expr = 0;
	    tmp->type = 0;
	    args->l.expr->r.expr = tmp;
	    if (def == DEFAULT_THIS_OBJECT) {
		tmp->v.expr = new_node_no_line();
		tmp->v.expr->kind = NODE_EFUN;
		tmp->v.expr->v.number = F_THIS_OBJECT;
		tmp->v.expr->l.number = 0;
		tmp->v.expr->type = TYPE_ANY;
		tmp->v.expr->r.expr = 0;
	    } else {
		CREATE_NUMBER(tmp->v.expr, def);
	    }
	    args->v.number++;
	    num++;
	} else if (num_var && max_arg != -1) {
	    char bff[100];
	    sprintf(bff, "Illegal to pass variable number of arguments to non-varargs efun %s", predefs[f].word);
	    yyerror(bff);
	    return args;
	} else if ((num - num_var) < min_arg) {
	    char bff[100];
	    sprintf(bff, "Too few arguments to %s", predefs[f].word);
	    yyerror(bff);
	    return args;
	} else if (num > max_arg && max_arg != -1) {
	    char bff[100];
	    sprintf(bff, "Too many arguments to %s", predefs[f].word);
	    yyerror(bff);
	    return args;
	}
	if (max_arg != -1 && exact_types) {
	    /*
	     * Now check all types of arguments to efuns.
	     */
	    int i, argn, tmp;
	    char buff[100];
	    parse_node_t *enode = args;
	    argp = &efun_arg_types[predefs[f].arg_index];
	    
	    for (argn = 0; argn < num; argn++) {
		enode = enode->r.expr;
		if (enode->type & 1) break;
		/* this can happen for default args */
		if (!enode->v.expr) break;
		tmp = enode->v.expr->type;
		for (i=0; !compatible_types(argp[i], tmp) && argp[i] != 0; i++)
		    ;

		if (argp[i] == 0) {
		    sprintf(buff, "Bad argument %d to efun %s()",
			    argn+1, predefs[f].word);
		    yyerror(buff);
		} else {
		    /* check for (int) -> (float) promotion */
		    if (tmp == TYPE_NUMBER && argp[i] == TYPE_REAL) {
			for (i++; argp[i] && argp[i] != TYPE_NUMBER; i++)
			    ;
			if (!argp[i])
			    enode->v.expr = promote_to_float(enode->v.expr);
		    }
		    else if (tmp == TYPE_REAL && argp[i] == TYPE_NUMBER) {
			for (i++; argp[i] && argp[i] != TYPE_REAL; i++)
			    ;
			if (!argp[i])
			    enode->v.expr = promote_to_int(enode->v.expr);
		    }
		}
		while (argp[i] != 0)
		    i++;
		argp += i + 1;
	    }
	}
	args->l.number = num;
	args->v.number = predefs[f].token;
	args->type = predefs[f].ret_type;
	if (args->type == TYPE_NOVALUE) {
	    args->v.number += NOVALUE_USED_FLAG;
	    args->type = TYPE_VOID;
	}
	args->kind = NODE_EFUN;
    } else {
	CREATE_ERROR(args);
    }
    return args;
}

/*
 * Initialization code is now more compact.  It's collected in
 * A_INITIALIZER and put at the end of the program.  For compatibility,
 * there is a jump to it at address 0.
 */
void switch_to_block P1(int, block) {
    UPDATE_PROGRAM_SIZE;

    prog_code = mem_block[block].block + mem_block[block].current_size;
    prog_code_max = mem_block[block].block + mem_block[block].max_size;
    current_block = block;
}

void yyerror P1(char *, str)
{
    extern int num_parse_error;

    function_context.num_parameters = -1;
    if (num_parse_error > 5)
	return;
    smart_log(current_file, current_line, str, 0);
#ifdef PACKAGE_MUDLIB_STATS
    add_errors_for_file (current_file, 1);
#endif
    num_parse_error++;
}

void yywarn P1(char *, str) {
    if (!(pragmas & PRAGMA_WARNINGS)) return;
    
    smart_log(current_file, current_line, str, 1);
}

/*
 * Compile an LPC file.
 */
void compile_file P2(int, f, char *, name) {
    int yyparse PROT((void));

    prolog(f, name);
    yyparse();
    epilog();
}

int get_id_number() {
    static int current_id_number = 1;
    return current_id_number++;
}

INLINE void copy_in P2(int, which, char **, start) {
    char *block;
    int size;

    size = mem_block[which].current_size;
    if (!size) return;

    block = mem_block[which].block;
    memcpy(*start, block, size);

    *start += align(size);
}

/*
 * The program has been compiled. Prepare a 'program_t' to be returned.
 */
static void epilog() {
    int size, i, lnsz, lnoff;
    char *p;
    function_t *funp;
    ident_hash_elem_t *ihe;

    /* don't need the parse trees any more */
    release_tree();
    
    if (num_parse_error > 0 || inherit_file) {
	/* don't print these; they can be wrong, since we didn't parse the
	   entire file */
	if (pragmas & PRAGMA_WARNINGS)
	    remove_overload_warnings(0);
	clean_parser();
	end_new_file();
	free_string(current_file);
	current_file = 0;
	return;
    }

    if (pragmas & PRAGMA_WARNINGS)
	show_overload_warnings();
    
    /*
     * Define the __INIT function, but only if there was any code
     * to initialize.
     */
    UPDATE_PROGRAM_SIZE;

    if (mem_block[A_INITIALIZER].current_size) {
	parse_node_t *pn;
	int fun;
	/* end the __INIT function */
	switch_to_block(A_INITIALIZER);
	CREATE_RETURN(pn, 0);
	generate(pn);
	switch_to_block(A_PROGRAM);
 	fun = define_new_function(APPLY___INIT, 0, 0, 
			    NAME_STRICT_TYPES, TYPE_VOID | TYPE_MOD_PRIVATE);
	FUNCTION(fun)->offset = CURRENT_PROGRAM_SIZE;
	generate___INIT();
    }

    generate_final_program(0);
    UPDATE_PROGRAM_SIZE;

    /*
     * If functions are undefined, replace them by definitions done
     * by inheritance. All explicit "name::func" are already resolved.
     * Also replace aliases with the updated info.  We use aliases
     * so that only one function definition has to be fooled with for
     * overloading during compile time, but here we want to make sure
     * they're all the same again.
     */
    for (i = 0; i < mem_block[A_FUNCTIONS].current_size; i += sizeof *funp) {
	funp = (function_t *)(mem_block[A_FUNCTIONS].block + i);
	if ((funp->flags & NAME_UNDEFINED) && (funp->flags & NAME_DEF_BY_INHERIT))
	    funp->flags = (funp->flags & ~NAME_UNDEFINED) | NAME_INHERITED;
	if (funp->flags & NAME_ALIAS) {
	    *funp = *(funp - funp->offset);
	    funp->flags |= NAME_ALIAS;
	}
    }
    generate_final_program(1);

    size = align(sizeof (program_t));

    /* delete argument information if we're not saving it */
    if (!(pragmas & PRAGMA_SAVE_TYPES))
	mem_block[A_ARGUMENT_TYPES].current_size = 0;
    if (!(mem_block[A_ARGUMENT_TYPES].current_size))
	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);

    p = (char *)DXALLOC(size, TAG_PROGRAM, "epilog: 1");
    prog = (program_t *)p;
    *prog = NULL_program;
    prog->total_size = size;
    prog->ref = 0;
    prog->func_ref = 0;
    ihe = lookup_ident("heart_beat");
    prog->heart_beat = (ihe ? ihe->dn.function_num : -1);
    prog->name = current_file;
    current_file = 0;

    prog->id_number = get_id_number();
    total_prog_block_size += prog->total_size;
    total_num_prog_blocks += 1;

#ifdef OPTIMIZE_FUNCTION_TABLE_SEARCH
    prog->tree_r = a_functions_root;
#endif

    prog->line_swap_index = -1;
    /* Format is now:
     * <short total size> <short line_info_offset> <file info> <line info>
     */
    lnoff = 2 + (mem_block[A_FILE_INFO].current_size / sizeof(short));
    lnsz = lnoff * sizeof(short) + mem_block[A_LINENUMBERS].current_size;

    prog->file_info = (unsigned short *)DXALLOC(lnsz, TAG_LINENUMBERS
						    , "epilog");
    prog->file_info[0] = (unsigned short)lnsz;
    prog->file_info[1] = (unsigned short)lnoff;

    memcpy(((char*)&prog->file_info[2]),
	  mem_block[A_FILE_INFO].block,
	  mem_block[A_FILE_INFO].current_size);

    prog->line_info = (unsigned char *)(&prog->file_info[lnoff]);
    memcpy(((char*)&prog->file_info[lnoff]),
	   mem_block[A_LINENUMBERS].block,
	   mem_block[A_LINENUMBERS].current_size);

    p += align(sizeof (program_t));

    prog->program = p;
    prog->program_size = mem_block[A_PROGRAM].current_size;
    copy_in(A_PROGRAM, &p);

    prog->functions = (function_t *)p;
    prog->num_functions = mem_block[A_FUNCTIONS].current_size /
	sizeof (function_t);
    copy_in(A_FUNCTIONS, &p);

    prog->classes = (class_def_t *)p;
    prog->num_classes = mem_block[A_CLASS_DEF].current_size /
	  sizeof (class_def_t);
    copy_in(A_CLASS_DEF, &p);

    prog->class_members = (class_member_entry_t *)p;
    copy_in(A_CLASS_MEMBER, &p);

    prog->strings = (char **)p;
    prog->num_strings = mem_block[A_STRINGS].current_size /
	  sizeof (char *);
    copy_in(A_STRINGS, &p);

    prog->variable_names = (variable_t *)p;
    prog->num_variables = mem_block[A_VARIABLES].current_size /
	  sizeof (variable_t);
    copy_in(A_VARIABLES, &p);

    prog->num_inherited = mem_block[A_INHERITS].current_size /
	  sizeof (inherit_t);
    if (prog->num_inherited) {
	prog->inherit = (inherit_t *)p;
	copy_in(A_INHERITS, &p);
    } else
	prog->inherit = 0;

    if (mem_block[A_ARGUMENT_TYPES].current_size) {
	prog->argument_types = (unsigned short *) p;
	copy_in(A_ARGUMENT_TYPES, &p);

	prog->type_start = (unsigned short *) p;
	copy_in(A_ARGUMENT_INDEX, &p);
    } else {
	prog->argument_types = 0;
	prog->type_start = 0;
    }
#ifdef BINARIES
    if ((pragmas & PRAGMA_SAVE_BINARY)
#ifdef LPC_TO_C
    || compile_to_c
#endif
    ) {
	save_binary(prog, &mem_block[A_INCLUDES], &mem_block[A_PATCH]);
    }
#endif

    swap_line_numbers(prog); /* do this after saving binary */

    for (i=0; i<NUMAREAS; i++)
	FREE((char *)mem_block[i].block);

    /*  marion
	Do referencing here - 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; (unsigned)i < prog->num_inherited; i++) {
	reference_prog (prog->inherit[i].prog, "inheritance");
    }
    scratch_destroy();
    clean_up_locals();
    free_unused_identifiers();
    end_new_file();
}

/*
 * Initialize the environment that the compiler needs.
 */
static void prolog P2(int, f, char *, name) {
    int i;

    function_context.num_parameters = -1;
    prog = 0;   /* 0 means fail to load. */
    num_parse_error = 0;
    global_modifiers = 0;
#ifdef OPTIMIZE_FUNCTION_TABLE_SEARCH
    a_functions_root = (unsigned short)0xffff;
#endif
    /* Initialize memory blocks where the result of the compilation
     * will be stored.
     */
    for (i=0; i < NUMAREAS; i++) {
	mem_block[i].block = DXALLOC(START_BLOCK_SIZE, TAG_COMPILER, "prolog: 2");
	mem_block[i].current_size = 0;
	mem_block[i].max_size = START_BLOCK_SIZE;
    }
    memset(string_tags, 0, sizeof(string_tags));
    freed_string = -1;
    initialize_parser();

    current_file = make_shared_string(name);
    current_file_id = add_program_file(name, 1);
    start_new_file(f);
}

/*
 * The program has errors, clean things up.
 */
static void clean_parser() {
    int i;
    function_t *funp;
    variable_t dummy;
    char *s;

    /*
     * Free function stuff.
     */
    for (i = 0; i < mem_block[A_FUNCTIONS].current_size; i += sizeof *funp) {
	funp = (function_t *)(mem_block[A_FUNCTIONS].block + i);
	if (funp->name)
	    free_string(funp->name);
    }
    for (i = 0; i < mem_block[A_STRINGS].current_size; i += sizeof(char *)) {
	COPY_PTR(&s, mem_block[A_STRINGS].block + i);
	free_string(s);
    }
    for (i = 0; i < mem_block[A_VARIABLES].current_size; i += sizeof dummy) {
	memcpy(&dummy, mem_block[A_VARIABLES].block + i, sizeof dummy);
	free_string(dummy.name);
    }

    prog = 0;
    for (i=0; i<NUMAREAS; i++)
	FREE(mem_block[i].block);
    clean_up_locals();
    scratch_destroy();
    free_unused_identifiers();
}

char *
the_file_name P1(char *, name)
{
    char *tmp;
    int len;

    len = strlen(name);
    if (len < 3) {
	return string_copy(name, "the_file_name");
    }
    tmp = new_string(len - 1, "the_file_name");
    if (!tmp) {
	return string_copy(name, "the_file_name");
    }
    tmp[0] = '/';
    strncpy(tmp + 1, name, len - 2);
    tmp[len - 1] = '\0';
    return tmp;
}

int case_compare P2(parse_node_t **, c1, parse_node_t **, c2) {
    if ((*c1)->kind == NODE_DEFAULT)
	return -1;
    if ((*c2)->kind == NODE_DEFAULT)
	return 1;

    return ((*c1)->r.number - (*c2)->r.number);
}

int string_case_compare P2(parse_node_t **, c1, parse_node_t **, c2) {
    int i1, i2;
    char *p1, *p2;
    
    if ((*c1)->kind == NODE_DEFAULT)
	return -1;
    if ((*c2)->kind == NODE_DEFAULT)
	return 1;

    i1 = (*c1)->r.number;
    i2 = (*c2)->r.number;
    p1 = (i1 ? PROG_STRING(i1) : 0);
    p2 = (i2 ? PROG_STRING(i2) : 0);
    
    return (p1 - p2);
}

void prepare_cases P2(parse_node_t *, pn, int, start) {
    parse_node_t **ce_start, **ce_end, **ce;
    int end, last_key, this_key;
    int direct = 1;
    
    ce_start = (parse_node_t **)&mem_block[A_CASES].block[start];
    end = mem_block[A_CASES].current_size;
    ce_end = (parse_node_t **)&mem_block[A_CASES].block[end];

    if (ce_start == ce_end) {
	/* no cases */
	pn->v.expr = 0;
	mem_block[A_CASES].current_size = start;
	return;
    }

    if (pn->kind == NODE_SWITCH_STRINGS)
	quickSort((char *)ce_start, ce_end - ce_start, sizeof(parse_node_t *),
		  string_case_compare);
    else
	quickSort((char *)ce_start, ce_end - ce_start, sizeof(parse_node_t *),
		  case_compare);

    ce = ce_start;
    if ((*ce)->kind == NODE_DEFAULT) {
	if (ce + 1 == ce_end) {
	    /* only a default */
	    pn->v.expr = *ce;
	    (*ce)->l.expr = 0;
	    mem_block[A_CASES].current_size = start;
	    return;
	}
	ce++;
	(*(ce-1))->l.expr = *ce;
    }
    if ((*ce)->v.expr) {
	last_key = (*ce)->v.expr->r.number;
	direct = 0;
    } else
	last_key = (*ce)->r.number;
    ce++;
    while (ce < ce_end) {
	this_key = (*ce)->r.number;
	if (pn->kind == NODE_SWITCH_RANGES && this_key <= last_key) {
	    char buf[1024];
	    char *f1, *f2;
	    int fi1, fi2;
	    int l1, l2;

	    /* make sure line numbers exist for the cases */
	    save_file_info(current_file_id, current_line - current_line_saved);
	    current_line_saved = current_line;

	    translate_absolute_line((*ce)->line, 
				    (unsigned short *)mem_block[A_FILE_INFO].block,
				    &fi1, &l1);
	    translate_absolute_line((*(ce-1))->line, 
				    (unsigned short *)mem_block[A_FILE_INFO].block,
				    &fi2, &l2);
	    f1 = PROG_STRING(fi1);
	    f2 = PROG_STRING(fi2);

	    sprintf(buf, "Overlapping cases: %s%s%d and %s%s%d.",
		    f1 ? f1 : "", f1 ? ":" : "line ", l1,
		    f2 ? f2 : "", f2 ? ":" : "line ", l2);
	    yyerror(buf);
	}
	(*(ce-1))->l.expr = *ce;
	if ((*ce)->v.expr) {
	    last_key = (*ce)->v.expr->r.number;
	    direct = 0;
	} else {
	    if (last_key + 1 != this_key) direct = 0;
	    last_key = this_key;
	}
	ce++;
    }
    (*(ce_end-1))->l.expr = 0;
    if (direct && pn->kind == NODE_SWITCH_NUMBERS)
	pn->kind = NODE_SWITCH_DIRECT;
    pn->v.expr = *(ce_start);
    mem_block[A_CASES].current_size = start;
}

void
save_file_info P2(int, file_id, int, lines) {
    short fi[2];

    fi[0] = lines;
    fi[1] = file_id;
    add_to_mem_block(A_FILE_INFO, (char *)&fi[0], sizeof(fi));
}

int
add_program_file P2(char *, name, int, top) {
    if (!top)
	add_to_mem_block(A_INCLUDES, name, strlen(name)+1);
    return store_prog_string(name) + 1;
}