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/
/* 
 * code generator for runtime LPC code
 */
#include "std.h"
#include "lpc_incl.h"
#include "icode.h"
#include "trees.h"
#include "compiler.h"
#include "lex.h"
#include "generate.h"

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

/*
   this variable is used to properly adjust the 'break_sp' stack in
   the event a 'continue' statement is issued from inside a 'switch'.
*/
static int foreach_depth = 0;

static int current_forward_branch;
static int current_num_values;

static int last_size_generated;
static int line_being_generated;

static int push_state;
static int push_start;

static parse_node_t *branch_list[3];

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

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

	UPDATE_PROGRAM_SIZE;
	realloc_mem_block(mbp, mbp->current_size * 2);
	
	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 P1(short, l)
{
    if (prog_code + 2 > prog_code_max) {
	mem_block_t *mbp = &mem_block[current_block];
	UPDATE_PROGRAM_SIZE;
	realloc_mem_block(mbp, mbp->current_size * 2);
	
	prog_code = mbp->block + mbp->current_size;
	prog_code_max = mbp->block + mbp->max_size;
    }
    STORE_SHORT(prog_code, l);
}

static short read_short P1(int, offset)
{
    short l;

    COPY_SHORT(&l, mem_block[current_block].block + offset);
    return 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 P1(int, l)
{

    if (prog_code + 4 > prog_code_max) {
	mem_block_t *mbp = &mem_block[current_block];
	UPDATE_PROGRAM_SIZE;
	realloc_mem_block(mbp, mbp->current_size * 2);
	
	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 P1(long, l)
{
    if (prog_code + 8 > prog_code_max) {
	mem_block_t *mbp = &mem_block[current_block];
	UPDATE_PROGRAM_SIZE;
	realloc_mem_block(mbp, mbp->current_size * 2);
	
	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 P2(int, offset, short, l)
{
    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);
    COPY_SHORT(mem_block[current_block].block + offset, &l);
}

static void ins_byte P1(unsigned char, b)
{
    if (prog_code == prog_code_max) {
	mem_block_t *mbp = &mem_block[current_block];
	UPDATE_PROGRAM_SIZE;
	realloc_mem_block(mbp, mbp->current_size * 2);
	
	prog_code = mbp->block + mbp->current_size;
	prog_code_max = mbp->block + mbp->max_size;
    }
    *prog_code++ = b;
}

static void upd_byte P2(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[current_block].block[offset] = b;
}

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

static void initialize_push PROT((void)) {
    int what = mem_block[current_block].block[push_start];
    int arg = mem_block[current_block].block[push_start + 1];

    prog_code = mem_block[current_block].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 P1(int, val) {
    if (try_to_push(PUSH_NUMBER, val)) return;
    ins_byte(F_BYTE);
    ins_byte(val);
}

static void write_number P1(int, 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 {
	    ins_byte(F_NUMBER);
	    ins_int(val);
	}
    }
}

static void
generate_expr_list P1(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 P1(parse_node_t *, expr) {
    while ((expr = expr->r.expr)) {
      i_generate_node(expr->l.expr);
      end_pushes();
      ins_byte(F_VOID_ASSIGN);
    }
}

INLINE void
switch_to_line P1(int, line) {
    int sz = CURRENT_PROGRAM_SIZE - last_size_generated;
    short s;
    unsigned char *p;

    /* should be fixed later */
    if (current_block != A_PROGRAM)
	return;

    if (sz) {
	s = line_being_generated;

	last_size_generated += sz;
	while (sz > 255) {
	    p = (unsigned char *)allocate_in_mem_block(A_LINENUMBERS, 3);
	    *p++ = 255;
	    STORE_SHORT(p, s);
	    sz -= 255;
	}
	p = (unsigned char *)allocate_in_mem_block(A_LINENUMBERS, 3);
	*p++ = sz;
	STORE_SHORT(p, s);
    }
    line_being_generated = line;
}

static int
try_to_push P2(int, kind, int, value) {
    if (push_state) { 
	if (value <= PUSH_MASK) {
	    if (push_state == 1)
		initialize_push();
	    push_state++;
	    ins_byte(kind | value);
	    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 P1(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_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)
	    ins_int(expr->r.number);
	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();
	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();
	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();
	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 |= 1;
	    if (expr->r.expr) {
		tmp |= 4;
		if (expr->r.expr->v.number == F_GLOBAL_LVALUE) tmp |= 2;
	    }
	    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:
	{
	    int 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);
	    ins_byte(0xff); /* kind of table */
	    addr = CURRENT_PROGRAM_SIZE;
	    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 + 4, expr->v.expr->v.number);
		expr->v.expr = expr->v.expr->l.expr;
	    } else {
		upd_short(addr + 4, CURRENT_PROGRAM_SIZE);
	    }
	    /* 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, CURRENT_PROGRAM_SIZE);
#ifdef BINARIES
	    if (expr->kind == NODE_SWITCH_STRINGS) {
		short sw;
		sw = addr - 2;
		add_to_mem_block(A_PATCH, (char *)&sw, sizeof sw);
	    }
#endif
	    if (expr->kind == NODE_SWITCH_DIRECT) {
		parse_node_t *pn = expr->v.expr;
		while (pn) {
		    ins_short((short)pn->v.number);
		    pn = pn->l.expr;
		}
		ins_int(expr->v.expr->r.number);
		mem_block[current_block].block[addr-1] = (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);
		    ins_short((short)pn->v.number);
		    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[current_block].block[addr-1] = (char)(0xf0+i);
		else
		    mem_block[current_block].block[addr-1] = (char)(i*0x10+0x0f);
	    }
	    i_update_branch_list(branch_list[CJ_BREAK_SWITCH]);
	    branch_list[CJ_BREAK_SWITCH] = save_switch_breaks;
	    upd_short(last_break, CURRENT_PROGRAM_SIZE - last_break);
	    upd_short(addr+2, CURRENT_PROGRAM_SIZE);
	    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);
	    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:
	case FP_LOCAL:
	    ins_short(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);
		break;
	    }
	}
	break;
    case NODE_ANON_FUNC:
	{
	    int addr;
	    int save_fd = foreach_depth;

	    foreach_depth = 0;
	    end_pushes();
	    ins_byte(F_FUNCTION_CONSTRUCTOR);
	    ins_byte(FP_ANONYMOUS);
	    ins_byte(expr->v.number);
	    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);
	    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 P4(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]);
    if (inc) i_generate_node(inc);
    if (!forever && test_first) i_update_forward_branch();
    if (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]);
    branch_list[CJ_BREAK] = save_breaks;
    branch_list[CJ_CONTINUE] = save_continues;
    if (test_first == 2) foreach_depth--;
}

static void
i_generate_if_branch P2(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 P2(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___INIT() {
    add_to_mem_block(A_PROGRAM, (char *)mem_block[A_INITIALIZER].block,
		     mem_block[A_INITIALIZER].current_size);
    prog_code = mem_block[A_PROGRAM].block + mem_block[A_PROGRAM].current_size;
}

void i_generate_forward_branch P1(char, b) {
    end_pushes();
    ins_byte(b);
    ins_short(current_forward_branch);
    current_forward_branch = CURRENT_PROGRAM_SIZE - 2;
}

void
i_update_forward_branch() {
    int i = read_short(current_forward_branch);
    
    end_pushes();
    upd_short(current_forward_branch, CURRENT_PROGRAM_SIZE - current_forward_branch);
    current_forward_branch = i;
}

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

    end_pushes();
    i = read_short(current_forward_branch);
    upd_short(current_forward_branch, CURRENT_PROGRAM_SIZE - current_forward_branch);
    current_forward_branch = i;
    do {
	i = link_start->v.number;
	upd_byte(i-1, kind);
	upd_short(i, CURRENT_PROGRAM_SIZE - i);
	link_start = link_start->l.expr;
    } while (link_start->kind == NODE_BRANCH_LINK);
}

void
i_branch_backwards P2(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 P1(parse_node_t *, bl) {
    int current_size;
    
    end_pushes();
    current_size = CURRENT_PROGRAM_SIZE;

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

void
i_generate_else() {
    /* set up a new branch to after the end of the if */
    end_pushes();
    ins_byte(F_BRANCH);
    /* save the old saved value here */
    ins_short(read_short(current_forward_branch));
    /* update the old branch to point to this point */
    upd_short(current_forward_branch, CURRENT_PROGRAM_SIZE - current_forward_branch);
    /* point current_forward_branch at the new branch we made */
    current_forward_branch = CURRENT_PROGRAM_SIZE - 2;
}

void
i_initialize_parser() {
    foreach_depth = 0;
    branch_list[CJ_BREAK] = 0;
    branch_list[CJ_BREAK_SWITCH] = 0;
    branch_list[CJ_CONTINUE] = 0;

    current_forward_branch = 0;

    current_block = A_PROGRAM;
    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_generate_final_program P1(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 P3(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++)) {
	case F_NUMBER:
	case F_REAL:
	case F_CALL_INHERITED:
	    pc += 4;
	    break;
	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_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:
		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++;
	}
    }
}