ds2.10/bin/
ds2.10/extra/
ds2.10/extra/crat/
ds2.10/extra/creremote/
ds2.10/extra/mingw/
ds2.10/extra/wolfpaw/
ds2.10/fluffos-2.16-ds05/
ds2.10/fluffos-2.16-ds05/Win32/
ds2.10/fluffos-2.16-ds05/compat/
ds2.10/fluffos-2.16-ds05/compat/simuls/
ds2.10/fluffos-2.16-ds05/include/
ds2.10/fluffos-2.16-ds05/testsuite/
ds2.10/fluffos-2.16-ds05/testsuite/clone/
ds2.10/fluffos-2.16-ds05/testsuite/command/
ds2.10/fluffos-2.16-ds05/testsuite/data/
ds2.10/fluffos-2.16-ds05/testsuite/etc/
ds2.10/fluffos-2.16-ds05/testsuite/include/
ds2.10/fluffos-2.16-ds05/testsuite/inherit/
ds2.10/fluffos-2.16-ds05/testsuite/inherit/master/
ds2.10/fluffos-2.16-ds05/testsuite/log/
ds2.10/fluffos-2.16-ds05/testsuite/single/
ds2.10/fluffos-2.16-ds05/testsuite/single/tests/compiler/
ds2.10/fluffos-2.16-ds05/testsuite/single/tests/efuns/
ds2.10/fluffos-2.16-ds05/testsuite/single/tests/operators/
ds2.10/fluffos-2.16-ds05/testsuite/u/
ds2.10/lib/cmds/admins/
ds2.10/lib/cmds/common/
ds2.10/lib/cmds/creators/include/
ds2.10/lib/daemon/services/
ds2.10/lib/daemon/tmp/
ds2.10/lib/doc/
ds2.10/lib/doc/bguide/
ds2.10/lib/doc/efun/all/
ds2.10/lib/doc/efun/arrays/
ds2.10/lib/doc/efun/buffers/
ds2.10/lib/doc/efun/compile/
ds2.10/lib/doc/efun/floats/
ds2.10/lib/doc/efun/functions/
ds2.10/lib/doc/efun/general/
ds2.10/lib/doc/efun/mixed/
ds2.10/lib/doc/efun/numbers/
ds2.10/lib/doc/efun/parsing/
ds2.10/lib/doc/help/classes/
ds2.10/lib/doc/help/races/
ds2.10/lib/doc/lfun/
ds2.10/lib/doc/lfun/all/
ds2.10/lib/doc/lfun/lib/abilities/
ds2.10/lib/doc/lfun/lib/armor/
ds2.10/lib/doc/lfun/lib/bank/
ds2.10/lib/doc/lfun/lib/bot/
ds2.10/lib/doc/lfun/lib/clay/
ds2.10/lib/doc/lfun/lib/clean/
ds2.10/lib/doc/lfun/lib/clerk/
ds2.10/lib/doc/lfun/lib/client/
ds2.10/lib/doc/lfun/lib/combat/
ds2.10/lib/doc/lfun/lib/connect/
ds2.10/lib/doc/lfun/lib/container/
ds2.10/lib/doc/lfun/lib/corpse/
ds2.10/lib/doc/lfun/lib/creator/
ds2.10/lib/doc/lfun/lib/daemon/
ds2.10/lib/doc/lfun/lib/damage/
ds2.10/lib/doc/lfun/lib/deterioration/
ds2.10/lib/doc/lfun/lib/donate/
ds2.10/lib/doc/lfun/lib/door/
ds2.10/lib/doc/lfun/lib/equip/
ds2.10/lib/doc/lfun/lib/file/
ds2.10/lib/doc/lfun/lib/fish/
ds2.10/lib/doc/lfun/lib/fishing/
ds2.10/lib/doc/lfun/lib/flashlight/
ds2.10/lib/doc/lfun/lib/follow/
ds2.10/lib/doc/lfun/lib/ftp_client/
ds2.10/lib/doc/lfun/lib/ftp_data_connection/
ds2.10/lib/doc/lfun/lib/fuel/
ds2.10/lib/doc/lfun/lib/furnace/
ds2.10/lib/doc/lfun/lib/genetics/
ds2.10/lib/doc/lfun/lib/holder/
ds2.10/lib/doc/lfun/lib/id/
ds2.10/lib/doc/lfun/lib/interactive/
ds2.10/lib/doc/lfun/lib/lamp/
ds2.10/lib/doc/lfun/lib/leader/
ds2.10/lib/doc/lfun/lib/light/
ds2.10/lib/doc/lfun/lib/limb/
ds2.10/lib/doc/lfun/lib/living/
ds2.10/lib/doc/lfun/lib/load/
ds2.10/lib/doc/lfun/lib/look/
ds2.10/lib/doc/lfun/lib/manipulate/
ds2.10/lib/doc/lfun/lib/meal/
ds2.10/lib/doc/lfun/lib/messages/
ds2.10/lib/doc/lfun/lib/player/
ds2.10/lib/doc/lfun/lib/poison/
ds2.10/lib/doc/lfun/lib/position/
ds2.10/lib/doc/lfun/lib/post_office/
ds2.10/lib/doc/lfun/lib/potion/
ds2.10/lib/doc/lfun/lib/room/
ds2.10/lib/doc/lfun/lib/server/
ds2.10/lib/doc/lfun/lib/spell/
ds2.10/lib/doc/lfun/lib/torch/
ds2.10/lib/doc/lfun/lib/vendor/
ds2.10/lib/doc/lfun/lib/virt_sky/
ds2.10/lib/doc/lfun/lib/weapon/
ds2.10/lib/doc/lfun/lib/worn_storage/
ds2.10/lib/doc/lpc/constructs/
ds2.10/lib/doc/lpc/etc/
ds2.10/lib/doc/lpc/intermediate/
ds2.10/lib/doc/lpc/types/
ds2.10/lib/doc/misc/
ds2.10/lib/doc/old/
ds2.10/lib/doc/phints/
ds2.10/lib/domains/
ds2.10/lib/domains/Praxis/adm/
ds2.10/lib/domains/Praxis/attic/
ds2.10/lib/domains/Praxis/cemetery/mon/
ds2.10/lib/domains/Praxis/data/
ds2.10/lib/domains/Praxis/death/
ds2.10/lib/domains/Praxis/mountains/
ds2.10/lib/domains/Praxis/obj/armour/
ds2.10/lib/domains/Praxis/obj/magic/
ds2.10/lib/domains/Praxis/obj/weapon/
ds2.10/lib/domains/Praxis/orc_valley/
ds2.10/lib/domains/Ylsrim/
ds2.10/lib/domains/Ylsrim/adm/
ds2.10/lib/domains/Ylsrim/armor/
ds2.10/lib/domains/Ylsrim/broken/
ds2.10/lib/domains/Ylsrim/fish/
ds2.10/lib/domains/Ylsrim/meal/
ds2.10/lib/domains/Ylsrim/npc/
ds2.10/lib/domains/Ylsrim/obj/
ds2.10/lib/domains/Ylsrim/virtual/
ds2.10/lib/domains/Ylsrim/weapon/
ds2.10/lib/domains/alpha/room/
ds2.10/lib/domains/alpha/virtual/
ds2.10/lib/domains/campus/adm/
ds2.10/lib/domains/campus/etc/
ds2.10/lib/domains/campus/meals/
ds2.10/lib/domains/campus/txt/ai/charles/
ds2.10/lib/domains/campus/txt/ai/charles/bak2/
ds2.10/lib/domains/campus/txt/ai/charles/bak2/bak1/
ds2.10/lib/domains/campus/txt/ai/charly/
ds2.10/lib/domains/campus/txt/ai/charly/bak/
ds2.10/lib/domains/campus/txt/jenny/
ds2.10/lib/domains/cave/doors/
ds2.10/lib/domains/cave/etc/
ds2.10/lib/domains/cave/meals/
ds2.10/lib/domains/cave/weap/
ds2.10/lib/domains/default/chamber/
ds2.10/lib/domains/default/creator/
ds2.10/lib/domains/default/doors/
ds2.10/lib/domains/default/etc/
ds2.10/lib/domains/default/vehicle/
ds2.10/lib/domains/default/virtual/
ds2.10/lib/domains/town/save/
ds2.10/lib/domains/town/txt/shame/
ds2.10/lib/domains/town/virtual/
ds2.10/lib/domains/town/virtual/bottom/
ds2.10/lib/domains/town/virtual/space/
ds2.10/lib/estates/
ds2.10/lib/ftp/
ds2.10/lib/lib/comp/
ds2.10/lib/lib/daemons/
ds2.10/lib/lib/daemons/include/
ds2.10/lib/lib/lvs/
ds2.10/lib/lib/user/
ds2.10/lib/lib/virtual/
ds2.10/lib/log/
ds2.10/lib/log/adm/
ds2.10/lib/log/archive/
ds2.10/lib/log/chan/
ds2.10/lib/log/errors/
ds2.10/lib/log/law/adm/
ds2.10/lib/log/law/email/
ds2.10/lib/log/law/names/
ds2.10/lib/log/law/sites-misc/
ds2.10/lib/log/law/sites-register/
ds2.10/lib/log/law/sites-tempban/
ds2.10/lib/log/law/sites-watch/
ds2.10/lib/log/open/
ds2.10/lib/log/reports/
ds2.10/lib/log/router/
ds2.10/lib/log/secure/
ds2.10/lib/log/watch/
ds2.10/lib/obj/book_source/
ds2.10/lib/obj/include/
ds2.10/lib/powers/prayers/
ds2.10/lib/powers/spells/
ds2.10/lib/realms/template/
ds2.10/lib/realms/template/adm/
ds2.10/lib/realms/template/area/
ds2.10/lib/realms/template/area/armor/
ds2.10/lib/realms/template/area/npc/
ds2.10/lib/realms/template/area/obj/
ds2.10/lib/realms/template/area/room/
ds2.10/lib/realms/template/area/weap/
ds2.10/lib/realms/template/bak/
ds2.10/lib/realms/template/cmds/
ds2.10/lib/save/kills/o/
ds2.10/lib/secure/cfg/classes/
ds2.10/lib/secure/cmds/builders/
ds2.10/lib/secure/cmds/creators/include/
ds2.10/lib/secure/cmds/players/include/
ds2.10/lib/secure/daemon/imc2server/
ds2.10/lib/secure/daemon/include/
ds2.10/lib/secure/lib/
ds2.10/lib/secure/lib/include/
ds2.10/lib/secure/lib/net/include/
ds2.10/lib/secure/lib/std/
ds2.10/lib/secure/log/adm/
ds2.10/lib/secure/log/bak/
ds2.10/lib/secure/log/intermud/
ds2.10/lib/secure/log/network/
ds2.10/lib/secure/modules/
ds2.10/lib/secure/npc/
ds2.10/lib/secure/obj/include/
ds2.10/lib/secure/room/
ds2.10/lib/secure/save/
ds2.10/lib/secure/save/backup/
ds2.10/lib/secure/save/boards/
ds2.10/lib/secure/save/players/g/
ds2.10/lib/secure/tmp/
ds2.10/lib/secure/upgrades/files/
ds2.10/lib/secure/verbs/creators/
ds2.10/lib/std/board/
ds2.10/lib/std/lib/
ds2.10/lib/verbs/admins/include/
ds2.10/lib/verbs/builders/
ds2.10/lib/verbs/common/
ds2.10/lib/verbs/common/include/
ds2.10/lib/verbs/creators/
ds2.10/lib/verbs/creators/include/
ds2.10/lib/verbs/rooms/
ds2.10/lib/verbs/rooms/include/
ds2.10/lib/www/client/
ds2.10/lib/www/errors/
ds2.10/lib/www/images/
ds2.10/win32/
/*
 * code generator for runtime LPC code
 */
#include "std.h"
#include "lpc_incl.h"
#include "icode.h"
#include "compiler.h"
#include "generate.h"

static void ins_real (double);
static void ins_short (short);
static void upd_short (int, int, const char *);
static void ins_byte (unsigned char);
static void upd_byte (int, unsigned char);
static void write_number (long);
static void ins_int (long);
#if SIZEOF_PTR == 8
static void ins_long (long);
#endif
void i_generate_node (parse_node_t *);
static void i_generate_if_branch (parse_node_t *, int);
static void i_generate_loop (int, parse_node_t *, parse_node_t *,
        parse_node_t *);
static void i_update_branch_list (parse_node_t *, const char *);
static int try_to_push (int, int);

static int foreach_depth = 0;

static int current_num_values;

static unsigned int last_size_generated;
static unsigned int line_being_generated;

static int push_state;
static int push_start;

static parse_node_t *branch_list[3];
static int nforward_branches, nforward_branches_max;
static int *forward_branches = 0;

static void ins_real (double l)
{
    float f = (float)l;

    if (prog_code + 4 > prog_code_max) {
        mem_block_t *mbp = &mem_block[A_PROGRAM];

        UPDATE_PROGRAM_SIZE;
        realloc_mem_block(mbp);

        prog_code = mbp->block + mbp->current_size;
        prog_code_max = mbp->block + mbp->max_size;
    }
    STORE_FLOAT(prog_code, f);
}

/*
 * Store a 2 byte number. It is stored in such a way as to be sure
 * that correct byte order is used, regardless of machine architecture.
 * Also beware that some machines can't write a word to odd addresses.
 */
static void ins_short (short l)
{
    if (prog_code + 2 > prog_code_max) {
        mem_block_t *mbp = &mem_block[A_PROGRAM];
        UPDATE_PROGRAM_SIZE;
        realloc_mem_block(mbp);

        prog_code = mbp->block + mbp->current_size;
        prog_code_max = mbp->block + mbp->max_size;
    }
    STORE_SHORT(prog_code, l);
}

/*
 * Store a 4 byte number. It is stored in such a way as to be sure
 * that correct byte order is used, regardless of machine architecture.
 */
static void ins_int (long l)
{

    if (prog_code + SIZEOF_LONG > prog_code_max) {
        mem_block_t *mbp = &mem_block[A_PROGRAM];
        UPDATE_PROGRAM_SIZE;
        realloc_mem_block(mbp);

        prog_code = mbp->block + mbp->current_size;
        prog_code_max = mbp->block + mbp->max_size;
    }
    STORE_INT(prog_code, l);
}

/*
 * Store a 8 byte number. It is stored in such a way as to be sure
 * that correct byte order is used, regardless of machine architecture.
 */
#if SIZEOF_PTR == 8
static void ins_long (long l)
{
    if (prog_code + 8 > prog_code_max) {
        mem_block_t *mbp = &mem_block[A_PROGRAM];
        UPDATE_PROGRAM_SIZE;
        realloc_mem_block(mbp);

        prog_code = mbp->block + mbp->current_size;
        prog_code_max = mbp->block + mbp->max_size;
    }
    STORE_PTR(prog_code, l);
}
#endif

static void upd_short (int offset, int l, const char * where)
{
    unsigned short s;

    IF_DEBUG(UPDATE_PROGRAM_SIZE);
    DEBUG_CHECK2(offset > CURRENT_PROGRAM_SIZE,
            "patch offset %x larger than current program size %x.\n",
            offset, CURRENT_PROGRAM_SIZE);
    if (l > USHRT_MAX) {
        char buf[256];

        sprintf(buf, "branch limit exceeded in %s, near line %i",
                where, line_being_generated);
        yyerror(buf);
    }
    s = l;

    COPY_SHORT(mem_block[A_PROGRAM].block + offset, &s);
}

static void ins_rel_short (int l)
{
    if (l > USHRT_MAX) {
        char buf[256];

        sprintf(buf, "branch limit exceeded in switch table, near line %i",
                line_being_generated);
        yyerror(buf);
    }
    ins_short(l);
}

static void ins_byte (unsigned char b)
{
    if (prog_code == prog_code_max) {
        mem_block_t *mbp = &mem_block[A_PROGRAM];
        UPDATE_PROGRAM_SIZE;
        realloc_mem_block(mbp);

        prog_code = mbp->block + mbp->current_size;
        prog_code_max = mbp->block + mbp->max_size;
    }
    *prog_code++ = b;
}

static void upd_byte (int offset, unsigned char b)
{
    IF_DEBUG(UPDATE_PROGRAM_SIZE);
    DEBUG_CHECK2(offset > CURRENT_PROGRAM_SIZE,
            "patch offset %x larger than current program size %x.\n",
            offset, CURRENT_PROGRAM_SIZE);
    mem_block[A_PROGRAM].block[offset] = b;
}

static void end_pushes (void) {
    if (push_state) {
        if (push_state > 1)
            upd_byte(push_start, push_state);
        push_state = 0;
    }
}

//
// F_PUSH pushes multiple data items onto the stack.  This function
// sets up the multi push by reading the single push off the top of
// the stack and rewriting the mult-push into the stack.
//

static void initialize_push (void) {
    int what = mem_block[A_PROGRAM].block[push_start];
    int arg = 0;

    // This is only valid if it is not a one byte const target.
    if (what != F_CONST0 && what != F_CONST1) {
        arg = mem_block[A_PROGRAM].block[push_start + 1];
    }
    prog_code = mem_block[A_PROGRAM].block + push_start;
    ins_byte(F_PUSH);
    push_start++; /* now points to the zero here */
    ins_byte(0);

    switch (what) {
        case F_CONST0:
            ins_byte(PUSH_NUMBER | 0);
            break;
        case F_CONST1:
            ins_byte(PUSH_NUMBER | 1);
            break;
        case F_BYTE:
            ins_byte(PUSH_NUMBER | arg);
            break;
        case F_SHORT_STRING:
            ins_byte(PUSH_STRING | arg);
            break;
        case F_LOCAL:
            ins_byte(PUSH_LOCAL | arg);
            break;
        case F_GLOBAL:
            ins_byte(PUSH_GLOBAL | arg);
            break;
    }
}


/*
 * Generate the code to push a number on the stack.
 * This varies since there are several opcodes (for
 * optimizing speed and/or size).
 */
static void write_small_number (int val) {
    if (try_to_push(PUSH_NUMBER, val)) return;
    ins_byte(F_BYTE);
    ins_byte(val);
}

static void write_number (long val)
{
    if ((val & ~0xff) == 0)
        write_small_number(val);
    else {
        end_pushes();
        if (val < 0 && val > -256) {
            ins_byte(F_NBYTE);
            ins_byte(-val);
        } else if (val >= -32768 && val <= 32767) {
            ins_byte(F_SHORT_INT);
            ins_short(val);
        } else {
            ins_byte(F_NUMBER);
#if SIZEOF_LONG == 4
            ins_int(val);
#else
            ins_long(val);
#endif
        }
    }
}

static void
generate_expr_list (parse_node_t * expr) {
    parse_node_t *pn;
    int n, flag;

    if (!expr) return;
    pn = expr;
    flag = n = 0;
    do {
        if (pn->type & 1) flag = 1;
        i_generate_node(pn->v.expr);
        n++;
    } while ((pn = pn->r.expr));

    if (flag) {
        pn = expr;
        do {
            n--;
            if (pn->type & 1) {
                end_pushes();
                ins_byte(F_EXPAND_VARARGS);
                ins_byte(n);
            }
        } while ((pn = pn->r.expr));
    }
}

static void
generate_lvalue_list (parse_node_t * expr) {
    while ((expr = expr->r.expr)) {
        i_generate_node(expr->l.expr);
        end_pushes();
        ins_byte(F_VOID_ASSIGN);
    }
}

INLINE_STATIC void
switch_to_line (unsigned int line) {
    unsigned int sz = CURRENT_PROGRAM_SIZE - last_size_generated;
    ADDRESS_TYPE s;
    unsigned char *p;

    if (sz) {
        s = line_being_generated;

        last_size_generated += sz;
        while (sz > 255) {
            p = (unsigned char *)allocate_in_mem_block(A_LINENUMBERS, sizeof(ADDRESS_TYPE) + 1);
            *p++ = 255;
#if !defined(USE_32BIT_ADDRESSES)
            STORE_SHORT(p, s);
#else
            STORE4(p, s);
#endif
            sz -= 255;
        }
        p = (unsigned char *)allocate_in_mem_block(A_LINENUMBERS, sizeof(ADDRESS_TYPE) + 1);
        *p++ = sz;
#if !defined(USE_32BIT_ADDRESSES)
        STORE_SHORT(p, s);
#else
        STORE4(p, s);
#endif
    }
    line_being_generated = line;
}

static int
try_to_push (int kind, int value) {
    if (push_state) {
        if (value <= PUSH_MASK) {
            if (push_state == 1)
                initialize_push();
            push_state++;
            ins_byte(kind | value);
            if (push_state == 255)
                end_pushes();
            return 1;
        } else end_pushes();
    } else if (value <= PUSH_MASK) {
        push_start = CURRENT_PROGRAM_SIZE;
        push_state = 1;
        switch (kind) {
            case PUSH_STRING: ins_byte(F_SHORT_STRING); break;
            case PUSH_LOCAL: ins_byte(F_LOCAL); break;
            case PUSH_GLOBAL: ins_byte(F_GLOBAL); break;
            case PUSH_NUMBER:
                              if (value == 0) {
                                  ins_byte(F_CONST0);
                                  return 1;
                              } else if (value == 1) {
                                  ins_byte(F_CONST1);
                                  return 1;
                              }
                              ins_byte(F_BYTE);
        }
        ins_byte(value);
        return 1;
    }
    return 0;
}

void
i_generate_node (parse_node_t * expr) {
    if (!expr) return;

    if (expr->line && expr->line != line_being_generated)
        switch_to_line(expr->line);
    switch (expr->kind) {
        case NODE_FUNCTION:
            {
                unsigned short num;

                /* In the following we know that this function wins all the */
                /* rest  - Sym                                              */

                num = FUNCTION_TEMP(expr->v.number)->u.index;
                FUNC(num)->address = generate_function(FUNC(num),
                        expr->r.expr, expr->l.number);
                break;
            }
        case NODE_TERNARY_OP:
            i_generate_node(expr->l.expr);
            expr = expr->r.expr;
        case NODE_BINARY_OP:
            i_generate_node(expr->l.expr);
            /* fall through */
        case NODE_UNARY_OP:
            i_generate_node(expr->r.expr);
            /* fall through */
        case NODE_OPCODE:
            end_pushes();
            ins_byte(expr->v.number);
            break;
        case NODE_TERNARY_OP_1:
            i_generate_node(expr->l.expr);
            expr = expr->r.expr;
            /* fall through */
        case NODE_BINARY_OP_1:
            i_generate_node(expr->l.expr);
            i_generate_node(expr->r.expr);
            end_pushes();
            ins_byte(expr->v.number);
            ins_byte(expr->type);
            break;
        case NODE_UNARY_OP_1:
            i_generate_node(expr->r.expr);
            /* fall through */
        case NODE_OPCODE_1:
            if (expr->v.number == F_LOCAL) {
                if (try_to_push(PUSH_LOCAL, expr->l.number)) break;
            } else if (expr->v.number == F_GLOBAL) {
                if (try_to_push(PUSH_GLOBAL, expr->l.number)) break;
            }
            end_pushes();
            ins_byte(expr->v.number);
            ins_byte(expr->l.number);
            break;
        case NODE_OPCODE_2:
            end_pushes();
            ins_byte(expr->v.number);
            ins_byte(expr->l.number);
            if (expr->v.number == F_LOOP_COND_NUMBER)
#if SIZEOF_LONG == 8
                ins_long(expr->r.number);
#else
            ins_int(expr->r.number);
#endif
            else
                ins_byte(expr->r.number);
            break;
        case NODE_RETURN:
            {
                int n;
                n = foreach_depth;
                end_pushes();
                while (n--)
                    ins_byte(F_EXIT_FOREACH);

                if (expr->r.expr) {
                    i_generate_node(expr->r.expr);
                    end_pushes();
                    ins_byte(F_RETURN);
                } else ins_byte(F_RETURN_ZERO);
                break;
            }
        case NODE_STRING:
            if (try_to_push(PUSH_STRING, expr->v.number)) break;
            if (expr->v.number <= 0xff) {
                ins_byte(F_SHORT_STRING);
                ins_byte(expr->v.number);
            } else {
                ins_byte(F_STRING);
                ins_short(expr->v.number);
            }
            break;
        case NODE_REAL:
            end_pushes();
            ins_byte(F_REAL);
            ins_real(expr->v.real);
            break;
        case NODE_NUMBER:
            write_number(expr->v.number);
            break;
        case NODE_LAND_LOR:
            i_generate_node(expr->l.expr);
            i_generate_forward_branch(expr->v.number);
            i_generate_node(expr->r.expr);
            if (expr->l.expr->kind == NODE_BRANCH_LINK)
                i_update_forward_branch_links(expr->v.number,expr->l.expr);
            else
                i_update_forward_branch("&& or ||");
            break;
        case NODE_BRANCH_LINK:
            i_generate_node(expr->l.expr);
            end_pushes();
            ins_byte(0);
            expr->v.number = CURRENT_PROGRAM_SIZE;
            ins_short(0);
            i_generate_node(expr->r.expr);
            break;
        case NODE_CALL_2:
            generate_expr_list(expr->r.expr);
            end_pushes();
            ins_byte(expr->v.number);
            ins_byte(expr->l.number >> 16);
            ins_short(expr->l.number & 0xffff);
            ins_byte((expr->r.expr ? expr->r.expr->kind : 0));
            break;
        case NODE_CALL_1:
            generate_expr_list(expr->r.expr);
            end_pushes();
            if (expr->v.number == F_CALL_FUNCTION_BY_ADDRESS) {
                expr->l.number = comp_def_index_map[expr->l.number];
            }
            ins_byte(expr->v.number);
            ins_short(expr->l.number);
            ins_byte((expr->r.expr ? expr->r.expr->kind : 0));
            break;
        case NODE_CALL:
            generate_expr_list(expr->r.expr);
            end_pushes();
            ins_byte(expr->v.number);
            ins_short(expr->l.number);
            break;
        case NODE_TWO_VALUES:
            i_generate_node(expr->l.expr);
            i_generate_node(expr->r.expr);
            break;
        case NODE_CONTROL_JUMP:
            {
                int kind = expr->v.number;
                end_pushes();
                ins_byte(F_BRANCH);
                expr->v.expr = branch_list[kind];
                expr->l.number = CURRENT_PROGRAM_SIZE;
                ins_short(0);
                branch_list[kind] = expr;
                break;
            }
        case NODE_PARAMETER:
            {
                int which = expr->v.number + current_num_values;
                if (try_to_push(PUSH_LOCAL, which)) break;
                ins_byte(F_LOCAL);
                ins_byte(which);
                break;
            }
        case NODE_PARAMETER_LVALUE:
            end_pushes();
            ins_byte(F_LOCAL_LVALUE);
            ins_byte(expr->v.number + current_num_values);
            break;
        case NODE_IF:
            i_generate_if_branch(expr->v.expr, 0);
            i_generate_node(expr->l.expr);
            if (expr->r.expr) {
                i_generate_else();
                i_generate_node(expr->r.expr);
            }
            i_update_forward_branch("if");
            break;
        case NODE_LOOP:
            i_generate_loop(expr->type, expr->v.expr, expr->l.expr, expr->r.expr);
            break;
        case NODE_FOREACH:
            {
                int tmp = 0;

                i_generate_node(expr->v.expr);
                end_pushes();
                ins_byte(F_FOREACH);
                if (expr->l.expr->v.number == F_GLOBAL_LVALUE)
                    tmp |= FOREACH_RIGHT_GLOBAL;
                else if (expr->l.expr->v.number == F_REF_LVALUE)
                    tmp |= FOREACH_REF;
                if (expr->r.expr) {
                    if (tmp & FOREACH_RIGHT_GLOBAL)
                        tmp = (tmp & ~FOREACH_RIGHT_GLOBAL) | FOREACH_LEFT_GLOBAL;

                    tmp |= FOREACH_MAPPING;
                    if (expr->r.expr->v.number == F_GLOBAL_LVALUE)
                        tmp |= FOREACH_RIGHT_GLOBAL;
                    else if (expr->r.expr->v.number == F_REF_LVALUE)
                        tmp |= FOREACH_REF;
                }
                ins_byte(tmp);
                ins_byte(expr->l.expr->l.number);
                if (expr->r.expr)
                    ins_byte(expr->r.expr->l.number);
            }
            break;
        case NODE_CASE_NUMBER:
        case NODE_CASE_STRING:
            if (expr->v.expr) {
                parse_node_t *other = expr->v.expr;
                expr->v.number = 1;
                other->l.expr = expr->l.expr;
                other->v.number = CURRENT_PROGRAM_SIZE;
                expr->l.expr = other;
            } else {
                expr->v.number = CURRENT_PROGRAM_SIZE;
            }
            end_pushes();
            break;
        case NODE_DEFAULT:
            expr->v.number = CURRENT_PROGRAM_SIZE;
            end_pushes();
            break;
        case NODE_SWITCH_STRINGS:
        case NODE_SWITCH_NUMBERS:
        case NODE_SWITCH_DIRECT:
        case NODE_SWITCH_RANGES:
            {
                long addr, last_break;
                parse_node_t *sub = expr->l.expr;
                parse_node_t *save_switch_breaks = branch_list[CJ_BREAK_SWITCH];

                i_generate_node(sub);
                branch_list[CJ_BREAK_SWITCH] = 0;
                end_pushes();
                ins_byte(F_SWITCH);
                addr = CURRENT_PROGRAM_SIZE;
                /* all these are addresses in here are relative to addr now,
                 * which is also the value of pc in f_switch().  Note that
                 * addr is one less than it was in older drivers.
                 */
                ins_byte(0xff); /* kind of table */
                ins_short(0); /* address of table */
                ins_short(0); /* end of table */
                ins_short(0); /* default address */
                i_generate_node(expr->r.expr);
                if (expr->v.expr && expr->v.expr->kind == NODE_DEFAULT) {
                    upd_short(addr + 5, expr->v.expr->v.number - addr,
                            "switch");
                    expr->v.expr = expr->v.expr->l.expr;
                } else {
                    upd_short(addr + 5, CURRENT_PROGRAM_SIZE - addr, "switch");
                }
                /* just in case the last case doesn't have a break */
                end_pushes();
                ins_byte(F_BRANCH);
                last_break = CURRENT_PROGRAM_SIZE;
                ins_short(0);
                /* build table */
                upd_short(addr + 1, CURRENT_PROGRAM_SIZE - addr, "switch");

                if (expr->kind == NODE_SWITCH_DIRECT) {
                    parse_node_t *pn = expr->v.expr;
                    while (pn) {
                        ins_rel_short(pn->v.number - addr);
                        pn = pn->l.expr;
                    }
                    ins_int(expr->v.expr->r.number);
                    mem_block[A_PROGRAM].block[addr] = (char)0xfe;
                } else {
                    int table_size = 0;
                    int power_of_two = 1;
                    int i = 0;
                    parse_node_t *pn = expr->v.expr;

                    while (pn) {
                        if (expr->kind == NODE_SWITCH_STRINGS) {
                            if (pn->r.number) {
                                INS_POINTER((POINTER_INT)
                                        PROG_STRING(pn->r.number));
                            } else
                                INS_POINTER((POINTER_INT)0);
                        } else
                            INS_POINTER((POINTER_INT)pn->r.expr);
                        if (pn->v.number == 1)
                            ins_short(1);
                        else
                            ins_rel_short(pn->v.number - addr);
                        pn = pn->l.expr;
                        table_size += 1;
                    }
                    while ((power_of_two<<1) <= table_size) {
                        power_of_two <<= 1;
                        i++;
                    }
                    if (expr->kind != NODE_SWITCH_STRINGS)
                        mem_block[A_PROGRAM].block[addr] = (char)(0xf0+i);
                    else
                        mem_block[A_PROGRAM].block[addr] = (char)(i*0x10+0x0f);
                }
                i_update_branch_list(branch_list[CJ_BREAK_SWITCH], "switch break");
                branch_list[CJ_BREAK_SWITCH] = save_switch_breaks;
                upd_short(last_break, CURRENT_PROGRAM_SIZE - last_break, "switch break");
                upd_short(addr+3, CURRENT_PROGRAM_SIZE - addr, "switch");
                break;
            }
        case NODE_CATCH:
            {
                int addr;

                end_pushes();
                ins_byte(F_CATCH);
                addr = CURRENT_PROGRAM_SIZE;
                ins_short(0);
                i_generate_node(expr->r.expr);
                ins_byte(F_END_CATCH);
                upd_short(addr, CURRENT_PROGRAM_SIZE - addr, "catch");
                break;
            }
        case NODE_TIME_EXPRESSION:
            {
                end_pushes();
                ins_byte(F_TIME_EXPRESSION);
                i_generate_node(expr->r.expr);
                ins_byte(F_END_TIME_EXPRESSION);
                break;
            }
        case NODE_LVALUE_EFUN:
            i_generate_node(expr->l.expr);
            generate_lvalue_list(expr->r.expr);
            break;
        case NODE_FUNCTION_CONSTRUCTOR:
            if (expr->r.expr) {
                generate_expr_list(expr->r.expr);
                end_pushes();
                ins_byte(F_AGGREGATE);
                ins_short(expr->r.expr->kind);
            } else {
                end_pushes();
                ins_byte(F_CONST0);
            }
            end_pushes();

            ins_byte(F_FUNCTION_CONSTRUCTOR);
            ins_byte(expr->v.number & 0xff);

            switch (expr->v.number & 0xff) {
                case FP_SIMUL:
                    ins_short(expr->v.number >> 8);
                    break;
                case FP_LOCAL:
                    ins_short(comp_def_index_map[expr->v.number >> 8]);
                    break;
                case FP_EFUN:
                    ins_short(predefs[expr->v.number >> 8].token);
                    break;
                case FP_FUNCTIONAL:
                case FP_FUNCTIONAL | FP_NOT_BINDABLE:
                    {
                        int addr, save_current_num_values = current_num_values;
                        ins_byte(expr->v.number >> 8);
                        addr = CURRENT_PROGRAM_SIZE;
                        ins_short(0);
                        current_num_values = expr->r.expr ? expr->r.expr->kind : 0;
                        i_generate_node(expr->l.expr);
                        current_num_values = save_current_num_values;
                        end_pushes();
                        ins_byte(F_RETURN);
                        upd_short(addr, CURRENT_PROGRAM_SIZE - addr - 2,
                                "function pointer");
                        break;
                    }
            }
            break;
        case NODE_ANON_FUNC:
            {
                int addr;
                int save_fd = foreach_depth;

                foreach_depth = 0;
                end_pushes();
                ins_byte(F_FUNCTION_CONSTRUCTOR);
                if (expr->v.number & 0x10000)
                    ins_byte(FP_ANONYMOUS | FP_NOT_BINDABLE);
                else
                    ins_byte(FP_ANONYMOUS);
                ins_byte(expr->v.number & 0xff);
                ins_byte(expr->l.number);
                addr = CURRENT_PROGRAM_SIZE;
                ins_short(0);
                i_generate_node(expr->r.expr);
                upd_short(addr, CURRENT_PROGRAM_SIZE - addr - 2,
                        "function pointer");
                foreach_depth = save_fd;
                break;
            }
        case NODE_EFUN:
            {
                int novalue_used = expr->v.number & NOVALUE_USED_FLAG;
                int f = expr->v.number & ~NOVALUE_USED_FLAG;

                generate_expr_list(expr->r.expr);
                end_pushes();
                if (f < ONEARG_MAX) {
                    ins_byte(f);
                } else {
                    /* max_arg == -1 must use F_EFUNV so that varargs expansion works*/
                    if (expr->l.number < 4 && instrs[f].max_arg != -1)
                        ins_byte(F_EFUN0 + expr->l.number);
                    else {
                        ins_byte(F_EFUNV);
                        ins_byte(expr->l.number);
                    }
                    ins_byte(f - ONEARG_MAX);
                }
                if (novalue_used) {
                    /* the value of a void efun was used.  Put in a zero. */
                    ins_byte(F_CONST0);
                }
                break;
            }
        default:
            fatal("Unknown node %i in i_generate_node.\n", expr->kind);
    }
}

static void i_generate_loop (int test_first, parse_node_t * block,
        parse_node_t * inc, parse_node_t * test) {
    parse_node_t *save_breaks = branch_list[CJ_BREAK];
    parse_node_t *save_continues = branch_list[CJ_CONTINUE];
    int forever = node_always_true(test);
    int pos;

    if (test_first == 2) foreach_depth++;
    branch_list[CJ_BREAK] = branch_list[CJ_CONTINUE] = 0;
    end_pushes();
    if (!forever && test_first)
        i_generate_forward_branch(F_BRANCH);
    pos = CURRENT_PROGRAM_SIZE;
    i_generate_node(block);
    i_update_branch_list(branch_list[CJ_CONTINUE], "continue");
    if (inc) i_generate_node(inc);
    if (!forever && test_first) i_update_forward_branch("loop");
    if (test && (test->v.number == F_LOOP_COND_LOCAL ||
                test->v.number == F_LOOP_COND_NUMBER ||
                test->v.number == F_NEXT_FOREACH)) {
        i_generate_node(test);
        ins_short(CURRENT_PROGRAM_SIZE - pos);
    } else i_branch_backwards(generate_conditional_branch(test), pos);
    i_update_branch_list(branch_list[CJ_BREAK], "break");
    branch_list[CJ_BREAK] = save_breaks;
    branch_list[CJ_CONTINUE] = save_continues;
    if (test_first == 2) foreach_depth--;
}

static void
i_generate_if_branch (parse_node_t * node, int invert) {
    int generate_both = 0;
    int branch = (invert ? F_BRANCH_WHEN_NON_ZERO : F_BRANCH_WHEN_ZERO);

    switch (node->kind) {
        case NODE_UNARY_OP:
            if (node->v.number == F_NOT) {
                i_generate_if_branch(node->r.expr, !invert);
                return;
            }
            break;
        case NODE_BINARY_OP:
            switch (node->v.number) {
                case F_EQ:
                    generate_both = 1;
                    branch = (invert ? F_BRANCH_EQ : F_BRANCH_NE);
                    break;
                case F_GE:
                    if (invert) {
                        generate_both = 1;
                        branch = F_BRANCH_GE;
                    }
                    break;
                case F_LE:
                    if (invert) {
                        generate_both = 1;
                        branch = F_BRANCH_LE;
                    }
                    break;
                case F_LT:
                    if (!invert) {
                        generate_both = 1;
                        branch = F_BRANCH_GE;
                    }
                    break;
                case F_GT:
                    if (!invert) {
                        generate_both = 1;
                        branch = F_BRANCH_LE;
                    }
                    break;
                case F_NE:
                    generate_both = 1;
                    branch = (invert ? F_BRANCH_NE : F_BRANCH_EQ);
                    break;
            }
    }
    if (generate_both) {
        i_generate_node(node->l.expr);
        i_generate_node(node->r.expr);
    } else {
        i_generate_node(node);
    }
    i_generate_forward_branch(branch);
}

void
i_generate_inherited_init_call (int index, short f) {
    end_pushes();
    ins_byte(F_CALL_INHERITED);
    ins_byte(index);
    ins_short(f);
    ins_byte(0);
    ins_byte(F_POP_VALUE);
}

void i_generate_forward_branch (char b) {
    end_pushes();
    ins_byte(b);
    if (nforward_branches == nforward_branches_max) {
        nforward_branches_max += 10;
        forward_branches = RESIZE(forward_branches, nforward_branches_max, int,
                TAG_COMPILER, "forward_branches");
    }

    forward_branches[nforward_branches++] = CURRENT_PROGRAM_SIZE;
    ins_short(0);
}

void
i_update_forward_branch (const char * what) {
    end_pushes();
    nforward_branches--;
    upd_short(forward_branches[nforward_branches],
            CURRENT_PROGRAM_SIZE - forward_branches[nforward_branches],
            what);
}

void i_update_forward_branch_links (char kind, parse_node_t * link_start) {
    int i;

    end_pushes();
    nforward_branches--;
    upd_short(forward_branches[nforward_branches],
            CURRENT_PROGRAM_SIZE - forward_branches[nforward_branches],
            "&& or ||");
    do {
        i = link_start->v.number;
        upd_byte(i-1, kind);
        upd_short(i, CURRENT_PROGRAM_SIZE - i, "&& or ||");
        link_start = link_start->l.expr;
    } while (link_start->kind == NODE_BRANCH_LINK);
}

void
i_branch_backwards (char b, int addr) {
    end_pushes();
    if (b) {
        if (b != F_WHILE_DEC)
            ins_byte(b);
        ins_short(CURRENT_PROGRAM_SIZE - addr);
    }
}

static void
i_update_branch_list (parse_node_t * bl, const char * what) {
    int current_size;

    end_pushes();
    current_size = CURRENT_PROGRAM_SIZE;

    while (bl) {
        upd_short(bl->l.number, current_size - bl->l.number, what);
        bl = bl->v.expr;
    }
}

void
i_generate_else() {
    int old;

    old = forward_branches[nforward_branches - 1];

    /* set up a new branch to after the end of the if */
    end_pushes();
    ins_byte(F_BRANCH);
    forward_branches[nforward_branches-1] = CURRENT_PROGRAM_SIZE;
    ins_short(0);

    /* update the old branch to point to this point */
    upd_short(old, CURRENT_PROGRAM_SIZE - old, "if");
}

void
i_initialize_parser() {
    foreach_depth = 0;

    if (!forward_branches) {
        forward_branches = CALLOCATE(10, int, TAG_COMPILER, "forward_branches");
        nforward_branches_max = 10;
    }
    nforward_branches = 0;

    branch_list[CJ_BREAK] = 0;
    branch_list[CJ_BREAK_SWITCH] = 0;
    branch_list[CJ_CONTINUE] = 0;

    prog_code = mem_block[A_PROGRAM].block;
    prog_code_max = mem_block[A_PROGRAM].block + mem_block[A_PROGRAM].max_size;

    line_being_generated = 0;
    last_size_generated = 0;
}

void
i_uninitialize_parser() {
#ifdef DEBUGMALLOC_EXTENSIONS
    /*
     * The intention is to keep the allocated space for forward branches
     * hanging around between compiles so we don't have to keep allocating it.
     * The problem is that with DEBUGMALLOC_EXENTIONS compiled in, it'll be
     * reported as a memory leak, so clean it up here.  Yes, it's a performance
     * hit, but we're trying to debug not run a race, so deal with it.
     * -- Marius, 11-Aug-2000
     */
    if (forward_branches) {
        FREE(forward_branches);
        forward_branches = 0;
    }
#endif
}

void
i_generate_final_program (int x) {
    if (!x) {
        UPDATE_PROGRAM_SIZE;
        /* This needs work
         * if (pragmas & PRAGMA_OPTIMIZE)
         *     optimize_icode(0, 0, 0);
         */
        save_file_info(current_file_id, current_line - current_line_saved);
        switch_to_line(-1); /* generate line numbers for the end */
    }
}

/* Currently, this procedure handles:
 * - jump threading
 */
void
optimize_icode (char * start, char * pc, char * end) {
    int instr;
    if (start == 0) {
        /* we don't optimize the initializer block right now b/c all the
         * stuff we do (jump threading, etc) can't occur there.
         */
        start = mem_block[A_PROGRAM].block;
        pc = start;
        end = pc + mem_block[A_PROGRAM].current_size;
        if (*pc == 0) {
            /* no initializer jump */
            pc += 3;
        }
    }
    while (pc < end) {
        switch (instr = EXTRACT_UCHAR(pc++)) {
#if SIZEOF_LONG == 4
            case F_NUMBER:
#endif
            case F_REAL:
            case F_CALL_INHERITED:
                pc += 4;
                break;
#if SIZEOF_LONG == 8
            case F_NUMBER:
                pc += 8;
                break;
#endif
            case F_SIMUL_EFUN:
            case F_CALL_FUNCTION_BY_ADDRESS:
                pc += 3;
                break;
            case F_BRANCH:
            case F_BRANCH_WHEN_ZERO:
            case F_BRANCH_WHEN_NON_ZERO:
            case F_BBRANCH:
            case F_BBRANCH_WHEN_ZERO:
            case F_BBRANCH_WHEN_NON_ZERO:
                {
                    char *tmp;
                    short sarg;
                    /* thread jumps */
                    COPY_SHORT(&sarg, pc);
                    if (instr > F_BRANCH)
                        tmp = pc - sarg;
                    else
                        tmp = pc + sarg;
                    sarg = 0;
                    while (1) {
                        if (EXTRACT_UCHAR(tmp) == F_BRANCH) {
                            COPY_SHORT(&sarg, tmp + 1);
                            tmp += sarg + 1;
                        } else if (EXTRACT_UCHAR(tmp) == F_BBRANCH) {
                            COPY_SHORT(&sarg, tmp + 1);
                            tmp -= sarg - 1;
                        } else break;
                    }
                    if (!sarg) {
                        pc += 2;
                        break;
                    }
                    /* be careful; in the process of threading a forward jump
                     * may have changed to a reverse one or vice versa
                     */
                    if (tmp > pc) {
                        if (instr > F_BRANCH) {
                            pc[-1] -= 3;   /* change to forward branch */
                        }
                        sarg = tmp - pc;
                    } else {
                        if (instr <= F_BRANCH) {
                            pc[-1] += 3;   /* change to backwards branch */
                        }
                        sarg = pc - tmp;
                    }
                    STORE_SHORT(pc, sarg);
                    break;
                }
#ifdef F_LOR
            case F_LOR:
            case F_LAND:
                {
                    char *tmp;
                    short sarg;
                    /* thread jumps */
                    COPY_SHORT(&sarg, pc);
                    tmp = pc + sarg;
                    sarg = 0;
                    while (1) {
                        if (EXTRACT_UCHAR(tmp) == F_BRANCH) {
                            COPY_SHORT(&sarg, tmp + 1);
                            tmp += sarg + 1;
                        } else if (EXTRACT_UCHAR(tmp) == F_BBRANCH) {
                            COPY_SHORT(&sarg, tmp + 1);
                            tmp -= sarg - 1;
                        } else break;
                    }
                    if (!sarg) {
                        pc += 2;
                        break;
                    }
                    /* be careful; in the process of threading a forward jump
                     * may have changed to a reverse one or vice versa
                     */
                    if (tmp > pc) {
                        sarg = tmp - pc;
                    } else {
#ifdef DEBUG
                        fprintf(stderr,"Optimization failed; can't || or && backwards.\n");
#endif
                        pc += 2;
                        break;
                    }
                    STORE_SHORT(pc, sarg);
                    break;
                }
#endif
            case F_CATCH:
            case F_AGGREGATE:
            case F_AGGREGATE_ASSOC:
            case F_STRING:
#ifdef F_JUMP_WHEN_ZERO
            case F_JUMP_WHEN_ZERO:
            case F_JUMP_WHEN_NON_ZERO:
#endif
#ifdef F_JUMP
            case F_JUMP:
#endif
                pc += 2;
                break;
            case F_GLOBAL_LVALUE:
            case F_GLOBAL:
            case F_SHORT_STRING:
            case F_LOOP_INCR:
            case F_WHILE_DEC:
            case F_LOCAL:
            case F_LOCAL_LVALUE:
            case F_REF:
            case F_REF_LVALUE:
            case F_SSCANF:
            case F_PARSE_COMMAND:
            case F_BYTE:
            case F_NBYTE:
                pc++;
                break;
            case F_FUNCTION_CONSTRUCTOR:
                switch (EXTRACT_UCHAR(pc++)) {
                    case FP_SIMUL:
                    case FP_LOCAL:
                        pc += 2;
                        break;
                    case FP_FUNCTIONAL:
                    case FP_FUNCTIONAL | FP_NOT_BINDABLE:
                        pc += 3;
                        break;
                    case FP_ANONYMOUS:
                    case FP_ANONYMOUS | FP_NOT_BINDABLE:
                        pc += 4;
                        break;
                    case FP_EFUN:
                        pc += 2;
                        break;
                }
                break;
            case F_SWITCH:
                {
                    unsigned short stable, etable;
                    pc++; /* table type */
                    LOAD_SHORT(stable, pc);
                    LOAD_SHORT(etable, pc);
                    pc += 2; /* def */
                    DEBUG_CHECK(stable < pc - start || etable < pc - start
                            || etable < stable,
                            "Error in switch table found while optimizing\n");
                    /* recursively optimize the inside of the switch */
                    optimize_icode(start, pc, start + stable);
                    pc = start + etable;
                    break;
                }
            case F_EFUN0:
            case F_EFUN1:
            case F_EFUN2:
            case F_EFUN3:
            case F_EFUNV:
                instr = EXTRACT_UCHAR(pc++) + ONEARG_MAX;
            default:
                if ((instr >= BASE) &&
                        (instrs[instr].min_arg != instrs[instr].max_arg))
                    pc++;
        }
    }
}