# include "comp.h"
# include "str.h"
# include "array.h"
# include "object.h"
# include "xfloat.h"
# include "interpret.h"
# include "data.h"
# include "table.h"
# include "node.h"
# include "control.h"
# include "codegen.h"
# include "compile.h"

# define LINE_CHUNK	128

typedef struct _linechunk_ {
    struct _linechunk_ *next;		/* next in list */
    char info[LINE_CHUNK];		/* chunk of line number info */
} linechunk;

static linechunk *lline, *tline;		/* line chunk list */
static linechunk *fline;			/* free line chunk list */
static unsigned int lchunksz = LINE_CHUNK;	/* line chunk size */
static unsigned short line;			/* current line number */
static unsigned int line_info_size;		/* size of all line info */

/*
 * NAME:	line->byte()
 * DESCRIPTION:	output a line description byte
 */
static void line_byte(byte)
int byte;
{
    if (lchunksz == LINE_CHUNK) {
	register linechunk *l;

	/* new chunk */
	if (fline != (linechunk *) NULL) {
	    /* from free list */
	    l = fline;
	    fline = l->next;
	} else {
	    l = ALLOC(linechunk, 1);
	}
	l->next = (linechunk *) NULL;
	if (tline != (linechunk *) NULL) {
	    tline->next = l;
	} else {
	    lline = l;
	}
	tline = l;
	lchunksz = 0;
    }

    tline->info[lchunksz++] = byte;
    line_info_size++;
}

/*
 * NAME:	line->fix()
 * DESCRIPTION:	Fix the new line number.  Return 0 .. 2 for simple offsets,
 *		3 for special ones.
 */
static int line_fix(newline)
register unsigned short newline;
{
    register short offset;

    if (newline == 0) {
	/* nothing changes */
	return 0;
    }
    offset = newline - line;
    if (line != 0 && offset >= 0 && offset <= 2) {
	/* simple offset */
	line = newline;
	return offset;
    }

    if (offset >= -64 && offset <= 63) {
	/* one byte offset */
	line_byte(offset + 128 + 64);
    } else {
	/* two byte offset */
	offset += 16384;
	line_byte((offset >> 8) & 127);
	line_byte(offset);
    }
    line = newline;
    return 3;
}

/*
 * NAME:	line->make()
 * DESCRIPTION:	put line number info after the function
 */
static void line_make(buf)
register char *buf;
{
    register linechunk *l;

    /* collect all line blocks in one large block */
    for (l = lline; l != tline; l = l->next) {
	memcpy(buf, l->info, LINE_CHUNK);
	buf += LINE_CHUNK;
    }
    memcpy(buf, l->info, lchunksz);

    /* add blocks to free list */
    tline->next = fline;
    fline = lline;
    /* clear blocks */
    lline = (linechunk *) NULL;
    tline = (linechunk *) NULL;
    lchunksz = LINE_CHUNK;
    line = 0;
    line_info_size = 0;
}

/*
 * NAME:	line->clear()
 * DESCRIPTION:	clean up line number info chunks
 */
static void line_clear()
{
    register linechunk *l, *f;

    for (l = fline; l != (linechunk *) NULL; ) {
	f = l;
	l = l->next;
	FREE(f);
    }
    fline = (linechunk *) NULL;
}


# define CODE_CHUNK	128

typedef struct _codechunk_ {
    struct _codechunk_ *next;		/* next in list */
    char code[CODE_CHUNK];		/* chunk of code */
} codechunk;

static codechunk *lcode, *tcode;		/* code chunk list */
static codechunk *fcode;			/* free code chunk list */
static unsigned int cchunksz = CODE_CHUNK;	/* code chunk size */
static Uint here;				/* current offset */
static char *last_instruction;			/* last instruction's address */

/*
 * NAME:	code->byte()
 * DESCRIPTION:	output a byte of code
 */
static void code_byte(byte)
char byte;
{
    if (cchunksz == CODE_CHUNK) {
	register codechunk *l;

	/* new chunk */
	if (fcode != (codechunk *) NULL) {
	    /* from free list */
	    l = fcode;
	    fcode = l->next;
	} else {
	    l = ALLOC(codechunk, 1);
	}
	l->next = (codechunk *) NULL;
	if (tcode != (codechunk *) NULL) {
	    tcode->next = l;
	} else {
	    lcode = l;
	}
	tcode = l;
	cchunksz = 0;
    }
    tcode->code[cchunksz++] = byte;
    here++;
}

/*
 * NAME:	code->word()
 * DESCRIPTION:	output a word of code
 */
static void code_word(word)
unsigned short word;
{
    code_byte(word >> 8);
    code_byte(word);
}

/*
 * NAME:	code->instr()
 * DESCRIPTION:	generate an instruction code
 */
static void code_instr(i, line)
register int i;
register unsigned short line;
{
    code_byte(i | (line_fix(line) << I_LINE_SHIFT));
    last_instruction = &tcode->code[cchunksz - 1];
}

/*
 * NAME:	code->kfun()
 * DESCRIPTION:	generate code for a builtin kfun
 */
static void code_kfun(kf, line)
int kf;
unsigned short line;
{
    code_instr(I_CALL_KFUNC, line);
    code_byte(kf);
}

/*
 * NAME:	code->make()
 * DESCRIPTION:	create function code block
 */
static char *code_make(depth, nlocals, size)
unsigned short depth, *size;
int nlocals;
{
    register codechunk *l;
    register char *code;
    Uint sz;

    *size = sz = 5 + here + line_info_size;
    if (sz > USHRT_MAX) {
	c_error("function too large");
    }
    code = ALLOC(char, sz);
    *code++ = depth >> 8;
    *code++ = depth;
    *code++ = nlocals;
    *code++ = here >> 8;
    *code++ = here;

    /* collect all code blocks in one large block */
    for (l = lcode; l != tcode; l = l->next) {
	memcpy(code, l->code, CODE_CHUNK);
	code += CODE_CHUNK;
    }
    memcpy(code, l->code, cchunksz);
    code += cchunksz;
    line_make(code);
    code -= 5 + here;

    /* add blocks to free list */
    tcode->next = fcode;
    fcode = lcode;
    /* clear blocks */
    lcode = (codechunk *) NULL;
    tcode = (codechunk *) NULL;
    cchunksz = CODE_CHUNK;

    here = 0;
    return code;
}

/*
 * NAME:	code->clear()
 * DESCRIPTION:	clean up the code chunks
 */
static void code_clear()
{
    register codechunk *l, *f;

    for (l = fcode; l != (codechunk *) NULL; ) {
	f = l;
	l = l->next;
	FREE(f);
    }
    fcode = (codechunk *) NULL;

    line_clear();
}


# define JUMP_CHUNK	128

typedef struct _jmplist_ {
    Uint where;				/* where to jump from */
    Uint to;				/* where to jump to */
    struct _jmplist_ *next;		/* next in list */
} jmplist;

typedef struct _jmpchunk_ {
    struct _jmpchunk_ *next;		/* next in list */
    jmplist jump[JUMP_CHUNK];		/* chunk of jumps */
} jmpchunk;

static jmpchunk *ljump;			/* list of jump chunks */
static jmpchunk *fjump;			/* list of free jump chunks */
static int jchunksz = JUMP_CHUNK;	/* size of jump chunk */
static jmplist *true_list;		/* list of true jumps */
static jmplist *false_list;		/* list of false jumps */
static jmplist *break_list;		/* list of break jumps */
static jmplist *continue_list;		/* list of continue jumps */

/*
 * NAME:	jump->addr()
 * DESCRIPTION:	generate a jump
 */
static jmplist *jump_addr(list)
jmplist *list;
{
    register jmplist *j;

    if (jchunksz == JUMP_CHUNK) {
	register jmpchunk *l;

	/* new chunk */
	if (fjump != (jmpchunk *) NULL) {
	    /* from free list */
	    l = fjump;
	    fjump = l->next;
	} else {
	    l = ALLOC(jmpchunk, 1);
	}
	l->next = ljump;
	ljump = l;
	jchunksz = 0;
    }
    j = &ljump->jump[jchunksz++];
    j->where = here;
    j->next = list;
    code_word(0);	/* empty space in code block filled in later */

    return j;
}

/*
 * NAME:	jump()
 * DESCRIPTION:	create a jump
 */
static jmplist *jump(i, list)
int i;
jmplist *list;
{
    code_instr(i, 0);
    return jump_addr(list);
}

/*
 * NAME:	jump->resolve()
 * DESCRIPTION:	resolve all jumps in a jump list
 */
static void jump_resolve(list, to)
register jmplist *list;
register Uint to;
{
    while (list != (jmplist *) NULL) {
	list->to = to;
	list = list->next;
    }
}

/*
 * NAME:	jump->make()
 * DESCRIPTION:	fill in all jumps in a code block
 */
static void jump_make(code)
register char *code;
{
    register jmpchunk *l;
    register jmplist *j;
    register int i;
    jmplist *jmpjmp;

    i = jchunksz;
    jmpjmp = (jmplist *) NULL;
    for (l = ljump; l != (jmpchunk *) NULL; ) {
	jmpchunk *f;

	/*
	 * fill in jump addresses in code block
	 */
	j = &l->jump[i];
	do {
	    --j;
	    code[j->where    ] = j->to >> 8;
	    code[j->where + 1] = j->to;
	    if ((code[j->to] & I_INSTR_MASK) == I_JUMP) {
		/*
		 * add to jump-to-jump list
		 */
		j->next = jmpjmp;
		jmpjmp = j;
	    }
	} while (--i > 0);
	i = JUMP_CHUNK;
	f = l;
	l = l->next;
	f->next = fjump;
	fjump = f;
    }

    for (j = jmpjmp; j != (jmplist *) NULL; j = j->next) {
	register Uint where, to;

	/*
	 * replace jump-to-jump by a direct jump to destination
	 */
	where = j->where;
	to = j->to;
	while ((code[to] & I_INSTR_MASK) == I_JUMP && to != where - 1) {
	    /*
	     * Change to jump across the next jump.  If there is a loop, it
	     * will eventually result in a jump to itself.
	     */
	    code[where    ] = code[to + 1];
	    code[where + 1] = code[to + 2];
	    where = to + 1;
	    to = (UCHAR(code[to + 1]) << 8) | UCHAR(code[to + 2]);
	}
	/*
	 * jump to final destination
	 */
	code[j->where    ] = to >> 8;
	code[j->where + 1] = to;
    }

    ljump = (jmpchunk *) NULL;
    jchunksz = JUMP_CHUNK;
}

/*
 * NAME:	jump->clear()
 * DESCRIPTION:	clean up the jump chunks
 */
static void jump_clear()
{
    register jmpchunk *l, *f;

    for (l = fjump; l != (jmpchunk *) NULL; ) {
	f = l;
	l = l->next;
	FREE(f);
    }
    fjump = (jmpchunk *) NULL;
}


static void cg_expr P((node*, int));
static void cg_cond P((node*, int));
static void cg_stmt P((node*));

static int nparams;		/* number of parameters */

/*
 * NAME:	codegen->cast()
 * DESCRIPTION:	generate code for a cast
 */
static void cg_cast(type)
unsigned short type;
{
    code_instr(I_CAST, 0);
    if ((type & T_REF) != 0) {
	type = T_ARRAY;
    }
    code_byte(type);
}

/*
 * NAME:	codegen->lvalue()
 * DESCRIPTION:	generate code for an lvalue
 */
static void cg_lvalue(n, type)
register node *n;
int type;
{
    register node *m;
    register int typeflag;

    typeflag = (type != 0) ? I_TYPE_BIT : 0;

    if (n->type == N_CAST) {
	n = n->l.left;
    }
    switch (n->type) {
    case N_LOCAL:
	code_instr(I_PUSH_LOCAL_LVAL | typeflag, n->line);
	code_byte(nparams - (int) n->r.number - 1);
	break;

    case N_GLOBAL:
	if ((n->r.number >> 8) == 1) {
	    code_instr(I_PUSH_GLOBAL_LVAL | typeflag, n->line);
	    code_byte((int) n->r.number);
	} else {
	    code_instr(I_PUSH_FAR_GLOBAL_LVAL | typeflag, n->line);
	    code_word((int) n->r.number);
	}
	break;

    case N_INDEX:
	m = n->l.left;
	if (m->type == N_CAST) {
	    m = m->l.left;
	}
	switch (m->type) {
	case N_LOCAL:
	    code_instr(I_PUSH_LOCAL_LVAL, m->line);
	    code_byte(nparams - (int) m->r.number - 1);
	    break;

	case N_GLOBAL:
	    if ((m->r.number >> 8) == 1) {
		code_instr(I_PUSH_GLOBAL_LVAL, m->line);
		code_byte((int) m->r.number);
	    } else {
		code_instr(I_PUSH_FAR_GLOBAL_LVAL, m->line);
		code_word((int) m->r.number);
	    }
	    break;

	case N_INDEX:
	    cg_expr(m->l.left, FALSE);
	    cg_expr(m->r.right, FALSE);
	    code_instr(I_INDEX_LVAL, m->line);
	    break;

	default:
	    cg_expr(m, FALSE);
	    break;
	}
	cg_expr(n->r.right, FALSE);
	code_instr(I_INDEX_LVAL | typeflag, n->line);
	break;
    }

    if (typeflag != 0) {
	code_byte((type & T_REF) ? T_ARRAY : type);
    }
}

/*
 * NAME:	codegen->fetch()
 * DESCRIPTION:	generate code for a fetched lvalue
 */
static void cg_fetch(n)
node *n;
{
    cg_lvalue(n, 0);
    code_instr(I_FETCH, 0);
    if (n->type == N_CAST) {
	cg_cast(n->mod);
    }
}

/*
 * NAME:	codegen->asgnop()
 * DESCRIPTION:	generate code for an assignment operator
 */
static void cg_asgnop(n, op)
register node *n;
int op;
{
    cg_fetch(n->l.left);
    cg_expr(n->r.right, FALSE);
    code_kfun(op, n->line);
    code_instr(I_STORE, 0);
}

/*
 * NAME:	codegen->aggr()
 * DESCRIPTION:	generate code for an aggregate
 */
static int cg_aggr(n)
register node *n;
{
    register int i;

    if (n == (node *) NULL) {
	return 0;
    }
    for (i = 1; n->type == N_PAIR; i++) {
	cg_expr(n->l.left, FALSE);
	n = n->r.right;
    }
    cg_expr(n, FALSE);
    return i;
}

/*
 * NAME:	codegen->map_aggr()
 * DESCRIPTION:	generate code for a mapping aggregate
 */
static int cg_map_aggr(n)
register node *n;
{
    register int i;

    if (n == (node *) NULL) {
	return 0;
    }
    for (i = 2; n->type == N_PAIR; i += 2) {
	cg_expr(n->l.left->l.left, FALSE);
	cg_expr(n->l.left->r.right, FALSE);
	n = n->r.right;
    }
    cg_expr(n->l.left, FALSE);
    cg_expr(n->r.right, FALSE);
    return i;
}

/*
 * NAME:	codegen->sumargs()
 * DESCRIPTION:	generate code for summand arguments
 */
static int cg_sumargs(n)
register node *n;
{
    int i;

    if (n->type == N_SUM) {
	i = cg_sumargs(n->l.left);
	n = n->r.right;
    } else {
	i = 0;
    }

    if (n->type == N_AGGR) {
	n->type = N_INT;
	n->l.number = -3 - cg_aggr(n->l.left);
	cg_expr(n, FALSE);
    } else if (n->type == N_RANGE) {
	cg_expr(n->l.left, FALSE);
	n = n->r.right;
	if (n->l.left != (node *) NULL) {
	    cg_expr(n->l.left, FALSE);
	    if (n->r.right != (node *) NULL) {
		cg_expr(n->r.right, FALSE);
		code_kfun(KF_CKRANGEFT, n->line);
	    } else {
		code_kfun(KF_CKRANGEF, n->line);
	    }
	} else if (n->r.right != (node *) NULL) {
	    cg_expr(n->r.right, FALSE);
	    code_kfun(KF_CKRANGET, n->line);
	} else {
	    code_kfun(KF_RANGE, n->line);
	    code_instr(I_PUSH_INT1, 0);
	    code_byte(-2);
	}
    } else {
	cg_expr(n, FALSE);
	code_instr(I_PUSH_INT1, 0);
	code_byte(-2);		/* no range */
    }

    return i + 1;
}

/*
 * NAME:	codegen->funargs()
 * DESCRIPTION:	generate code for function arguments
 */
static int cg_funargs(n, lv)
register node *n;
bool lv;
{
    register int i;

    if (n == (node *) NULL) {
	return 0;
    }
    for (i = 1; n->type == N_PAIR; i++) {
	cg_expr(n->l.left, FALSE);
	n = n->r.right;
    }
    if (n->type == N_SPREAD) {
	register int type;

	cg_expr(n->l.left, FALSE);
	type = n->l.left->mod & ~(1 << REFSHIFT);
	if (lv && type != T_MIXED) {
	    /* typechecked lvalues */
	    code_instr(I_SPREAD | I_TYPE_BIT, n->line);
	    code_byte(n->mod);
	    code_byte((type & T_REF) ? T_ARRAY : type);
	} else {
	    code_instr(I_SPREAD, n->line);
	    code_byte(n->mod);
	}
    } else {
	cg_expr(n, FALSE);
    }
    return i;
}

/*
 * NAME:	codegen->expr()
 * DESCRIPTION:	generate code for an expression
 */
static void cg_expr(n, pop)
register node *n;
register int pop;
{
    register jmplist *jlist, *j2list;
    register unsigned short i;
    long l;

    switch (n->type) {
    case N_ADD:
	cg_expr(n->l.left, FALSE);
	if (n->r.right->type == N_FLOAT) {
	    if (NFLT_ISONE(n->r.right)) {
		code_kfun(KF_ADD1, n->line);
		break;
	    }
	    if (NFLT_ISMONE(n->r.right)) {
		code_kfun(KF_SUB1, n->line);
		break;
	    }
	}
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_ADD, n->line);
	break;

    case N_ADD_INT:
	cg_expr(n->l.left, FALSE);
	if (n->r.right->type == N_INT) {
	    if (n->r.right->l.number == 1) {
		code_kfun(KF_ADD1_INT, n->line);
		break;
	    }
	    if (n->r.right->l.number == -1) {
		code_kfun(KF_SUB1_INT, n->line);
		break;
	    }
	}
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_ADD_INT, n->line);
	break;

    case N_ADD_EQ:
	cg_asgnop(n, KF_ADD);
	break;

    case N_ADD_EQ_INT:
	cg_asgnop(n, KF_ADD_INT);
	break;

    case N_ADD_EQ_1:
	cg_fetch(n->l.left);
	code_kfun(KF_ADD1, 0);
	code_instr(I_STORE, 0);
	break;

    case N_ADD_EQ_1_INT:
	cg_fetch(n->l.left);
	code_kfun(KF_ADD1_INT, 0);
	code_instr(I_STORE, 0);
	break;

    case N_AGGR:
	if (n->mod == T_MAPPING) {
	    i = cg_map_aggr(n->l.left);
	    code_instr(I_AGGREGATE, n->line);
	    code_byte(1);
	} else {
	    i = cg_aggr(n->l.left);
	    code_instr(I_AGGREGATE, n->line);
	    code_byte(0);
	}
	code_word(i);
	break;

    case N_AND:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_AND, n->line);
	break;

    case N_AND_INT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_AND_INT, n->line);
	break;

    case N_AND_EQ:
	cg_asgnop(n, KF_AND);
	break;

    case N_AND_EQ_INT:
	cg_asgnop(n, KF_AND_INT);
	break;

    case N_ASSIGN:
	if (n->r.right->type == N_CAST) {
	    cg_lvalue(n->l.left, n->r.right->mod);
	    cg_expr(n->r.right->l.left, FALSE);
	} else {
	    cg_lvalue(n->l.left, 0);
	    cg_expr(n->r.right, FALSE);
	}
	code_instr(I_STORE, n->line);
	break;

    case N_CAST:
	cg_expr(n->l.left, FALSE);
	cg_cast(n->mod);
	break;

    case N_CATCH:
	jlist = jump((pop) ? I_CATCH | I_POP_BIT : I_CATCH, (jmplist *) NULL);
	cg_expr(n->l.left, TRUE);
	code_instr(I_RETURN, 0);
	jump_resolve(jlist, here);
	return;

    case N_COMMA:
	cg_expr(n->l.left, TRUE);
	cg_expr(n->r.right, pop);
	return;

    case N_DIV:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_DIV, n->line);
	break;

    case N_DIV_INT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_DIV_INT, n->line);
	break;

    case N_DIV_EQ:
	cg_asgnop(n, KF_DIV);
	break;

    case N_DIV_EQ_INT:
	cg_asgnop(n, KF_DIV_INT);
	break;

    case N_EQ:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_EQ, n->line);
	break;

    case N_EQ_INT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_EQ_INT, n->line);
	break;

    case N_FLOAT:
	code_instr(I_PUSH_FLOAT, n->line);
	code_word(n->l.fhigh);
	code_word((int) (n->r.flow >> 16));
	code_word((int) n->r.flow);
	break;

    case N_FUNC:
	i = cg_funargs(n->l.left->r.right, (n->r.number >> 24) & KFCALL_LVAL);
	switch (n->r.number >> 24) {
	case KFCALL:
	case KFCALL_LVAL:
	    code_kfun((int) n->r.number, n->line);
	    if (PROTO_CLASS(KFUN((short) n->r.number).proto) & C_KFUN_VARARGS) {
		code_byte(i);
	    }
	    break;

	case DFCALL:
	    if ((n->r.number & 0xff00) == 0) {
		/* auto object */
		code_instr(I_CALL_AFUNC, n->line);
		code_byte((int) n->r.number);
	    } else {
		code_instr(I_CALL_DFUNC, n->line);
		code_word((int) n->r.number);
	    }
	    code_byte(i);
	    break;

	case FCALL:
	    code_instr(I_CALL_FUNC, n->line);
	    code_word(ctrl_gencall((long) n->r.number));
	    code_byte(i);
	    break;
	}
	break;

    case N_GE:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_GE, n->line);
	break;

    case N_GE_INT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_GE_INT, n->line);
	break;

    case N_GLOBAL:
	if ((n->r.number >> 8) == 1) {
	    code_instr(I_PUSH_GLOBAL, n->line);
	    code_byte((int) n->r.number);
	} else {
	    code_instr(I_PUSH_FAR_GLOBAL, n->line);
	    code_word((int) n->r.number);
	}
	break;

    case N_GT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_GT, n->line);
	break;

    case N_GT_INT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_GT_INT, n->line);
	break;

    case N_INDEX:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_instr(I_INDEX, n->line);
	break;

    case N_INT:
	if (n->l.number == 0) {
	    code_instr(I_PUSH_ZERO, n->line);
	} else if (n->l.number == 1) {
	    code_instr(I_PUSH_ONE, n->line);
	} else if (n->l.number >= -128 && n->l.number <= 127) {
	    code_instr(I_PUSH_INT1, n->line);
	    code_byte((int) n->l.number);
	} else {
	    code_instr(I_PUSH_INT4, n->line);
	    code_word((int) (n->l.number >> 16));
	    code_word((int) n->l.number);
	}
	break;

    case N_LAND:
	if (!pop) {
	    jlist = true_list;
	    true_list = (jmplist *) NULL;
	    cg_cond(n, TRUE);
	    code_instr(I_PUSH_ZERO, 0);
	    j2list = jump(I_JUMP, (jmplist *) NULL);
	    jump_resolve(true_list, here);
	    true_list = jlist;
	    code_instr(I_PUSH_ONE, 0);
	    jump_resolve(j2list, here);
	} else {
	    jlist = false_list;
	    false_list = (jmplist *) NULL;
	    cg_cond(n->l.left, FALSE);
	    cg_expr(n->r.right, TRUE);
	    jump_resolve(false_list, here);
	    false_list = jlist;
	}
	return;

    case N_LE:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_LE, n->line);
	break;

    case N_LE_INT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_LE_INT, n->line);
	break;

    case N_LOCAL:
	code_instr(I_PUSH_LOCAL, n->line);
	code_byte(nparams - (int) n->r.number - 1);
	break;

    case N_LOR:
	if (!pop) {
	    jlist = false_list;
	    false_list = (jmplist *) NULL;
	    cg_cond(n, FALSE);
	    code_instr(I_PUSH_ONE, 0);
	    j2list = jump(I_JUMP, (jmplist *) NULL);
	    jump_resolve(false_list, here);
	    false_list = jlist;
	    code_instr(I_PUSH_ZERO, 0);
	    jump_resolve(j2list, here);
	} else {
	    jlist = true_list;
	    true_list = (jmplist *) NULL;
	    cg_cond(n->l.left, TRUE);
	    cg_expr(n->r.right, TRUE);
	    jump_resolve(true_list, here);
	    true_list = jlist;
	}
	return;

    case N_LSHIFT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_LSHIFT, n->line);
	break;

    case N_LSHIFT_INT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_LSHIFT_INT, n->line);
	break;

    case N_LSHIFT_EQ:
	cg_asgnop(n, KF_LSHIFT);
	break;

    case N_LSHIFT_EQ_INT:
	cg_asgnop(n, KF_LSHIFT_INT);
	break;

    case N_LT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_LT, n->line);
	break;

    case N_LT_INT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_LT_INT, n->line);
	break;

    case N_LVALUE:
	cg_lvalue(n->l.left, (n->l.left->mod != T_MIXED) ? n->l.left->mod : 0);
	break;

    case N_MOD:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_MOD, n->line);
	break;

    case N_MOD_INT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_MOD_INT, n->line);
	break;

    case N_MOD_EQ:
	cg_asgnop(n, KF_MOD);
	break;

    case N_MOD_EQ_INT:
	cg_asgnop(n, KF_MOD_INT);
	break;

    case N_MULT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_MULT, n->line);
	break;

    case N_MULT_INT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_MULT_INT, n->line);
	break;

    case N_MULT_EQ:
	cg_asgnop(n, KF_MULT);
	break;

    case N_MULT_EQ_INT:
	cg_asgnop(n, KF_MULT_INT);
	break;

    case N_NE:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_NE, n->line);
	break;

    case N_NE_INT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_NE_INT, n->line);
	break;

    case N_NIL:
	code_kfun(KF_NIL, n->line);
	break;

    case N_NOT:
	cg_expr(n->l.left, FALSE);
	code_kfun((n->l.left->mod == T_INT) ? KF_NOT_INT : KF_NOT, n->line);
	break;

    case N_OR:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_OR, n->line);
	break;

    case N_OR_INT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_OR_INT, n->line);
	break;

    case N_OR_EQ:
	cg_asgnop(n, KF_OR);
	break;

    case N_OR_EQ_INT:
	cg_asgnop(n, KF_OR_INT);
	break;

    case N_QUEST:
	if (n->r.right->l.left != (node *) NULL) {
	    jlist = false_list;
	    false_list = (jmplist *) NULL;
	    cg_cond(n->l.left, FALSE);
	    cg_expr(n->r.right->l.left, pop);
	    if (n->r.right->r.right != (node *) NULL) {
		j2list = jump(I_JUMP, (jmplist *) NULL);
		jump_resolve(false_list, here);
		false_list = jlist;
		cg_expr(n->r.right->r.right, pop);
		jump_resolve(j2list, here);
	    } else {
		jump_resolve(false_list, here);
		false_list = jlist;
	    }
	} else {
	    jlist = true_list;
	    true_list = (jmplist *) NULL;
	    cg_cond(n->l.left, TRUE);
	    if (n->r.right->r.right != (node *) NULL) {
		cg_expr(n->r.right->r.right, pop);
	    }
	    jump_resolve(true_list, here);
	    true_list = jlist;
	}
	return;

    case N_RANGE:
	cg_expr(n->l.left, FALSE);
	n = n->r.right;
	if (n->l.left != (node *) NULL) {
	    cg_expr(n->l.left, FALSE);
	    if (n->r.right != (node *) NULL) {
		cg_expr(n->r.right, FALSE);
		code_kfun(KF_RANGEFT, n->line);
	    } else {
		code_kfun(KF_RANGEF, n->line);
	    }
	} else if (n->r.right != (node *) NULL) {
	    cg_expr(n->r.right, FALSE);
	    code_kfun(KF_RANGET, n->line);
	} else {
	    code_kfun(KF_RANGE, n->line);
	}
	break;

    case N_RSHIFT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_RSHIFT, n->line);
	break;

    case N_RSHIFT_INT:
	cg_expr(n->l.left, FALSE);
	cg_expr(n->r.right, FALSE);
	code_kfun(KF_RSHIFT_INT, n->line);
	break;

    case N_RSHIFT_EQ:
	cg_asgnop(n, KF_RSHIFT);
	break;

    case N_RSHIFT_EQ_INT:
	cg_asgnop(n, KF_RSHIFT_INT);
	break;

    case N_STR:
	l = ctrl_dstring(n->l.string);
	if ((l & 0x01000000L) && (unsigned short) l < 256) {
	    code_instr(I_PUSH_STRING, n->line);
	    code_byte((int) l);
	} else if ((unsigned short) l < 256) {
	    code_instr(I_PUSH_NEAR_STRING, n->line);
	    code_byte((int) (l >> 16));
	    code_byte((int) l);
	} else {
	    code_instr(I_PUSH_FAR_STRING, n->line);
	    code_byte((int) (l >> 16));
	    code_word((int) l);
	}
	break;

    case N_SUB:
	if ((n->l.left->type == N_INT && n->l.left->l.number == 0) ||
	    (n->l.left->type == N_FLOAT && NFLT_ISZERO(n->l.left))) {
	    cg_expr(n->r.right, FALSE);
	    code_kfun(KF_UMIN, n->line);
	} else {
	    cg_expr(n->l.left, FALSE);
	    if (n->r.right->type == N_FLOAT) {
		if (NFLT_ISONE(n->r.right)) {
		    code_kfun(KF_SUB1, n->line);
		    break;
		}
		if (NFLT_ISMONE(n->r.right)) {
		    code_kfun(KF_ADD1, n->line);
		    break;
		}
	    }
	    cg_expr(n->r.right, FALSE);
	    code_kfun(KF_SUB, n->line);
	}
	break;

    case N_SUB_INT:
	if (n->l.left->type == N_INT && n->l.left->l.number == 0) {
	    cg_expr(n->r.right, FALSE);
	    code_kfun(KF_UMIN_INT, n->line);
	} else {
	    cg_expr(n->l.left, FALSE);
	    if (n->r.right->type == N_INT) {
		if (n->r.right->l.number == 1) {
		    code_kfun(KF_SUB1_INT, n->line);
		    break;
		}
		if (n->r.right->l.number == -1) {
		    code_kfun(KF_ADD1_INT, n->line);
		    break;
		}
	    }
	    cg_expr(n->r.right, FALSE);
	    code_kfun(KF_SUB_INT, n->line);
	}
	break;

    case N_SUB_EQ:
	cg_asgnop(n, KF_SUB);
	break;

    case N_SUB_EQ_INT:
	cg_asgnop(n, KF_SUB_INT);
	break;

    case N_SUB_EQ_1:
	cg_fetch(n->l.left);
	code_kfun(KF_SUB1, 0);
	code_instr(I_STORE, 0);
	break;

    case N_SUB_EQ_1_INT:
	cg_fetch(n->l.left);
	code_kfun(KF_SUB1_INT, 0);
	code_instr(I_STORE, 0);
	break;

    case N_SUM:
	i = cg_sumargs(n);
	code_kfun(KF_SUM, 0);
	code_byte(i);
	break;

    case N_SUM_EQ:
	cg_fetch(n->l.left);
	code_instr(I_PUSH_INT1, 0);
	code_byte(-2);
	i = cg_sumargs(n->r.right) + 1;
	code_kfun(KF_SUM, 0);
	code_byte(i);
	code_instr(I_STORE, 0);
	break;

    case N_TOFLOAT:
	cg_expr(n->l.left, FALSE);
	code_kfun(KF_TOFLOAT, n->line);
	break;

    case N_TOINT:
	cg_expr(n->l.left, FALSE);
	code_kfun(KF_TOINT, n->line);
	break;

    case N_TOSTRING:
	cg_expr(n->l.left, FALSE);
	code_kfun(KF_TOSTRING, n->line);
	break;

    case N_TST:
	cg_expr(n->l.left, FALSE);
	code_kfun((n->l.left->mod == T_INT) ? KF_TST_INT : KF_TST, n->line);
	break;

    case N_UPLUS:
	cg_expr(n->l.left, pop);
	return;

    case N_XOR:
	if (n->r.right->type == N_INT && n->r.right->l.number == -1) {
	    cg_expr(n->l.left, FALSE);
	    code_kfun(KF_NEG, n->line);
	} else {
	    cg_expr(n->l.left, FALSE);
	    cg_expr(n->r.right, FALSE);
	    code_kfun(KF_XOR, n->line);
	}
	break;

    case N_XOR_INT:
	if (n->r.right->type == N_INT && n->r.right->l.number == -1) {
	    cg_expr(n->l.left, FALSE);
	    code_kfun(KF_NEG_INT, n->line);
	} else {
	    cg_expr(n->l.left, FALSE);
	    cg_expr(n->r.right, FALSE);
	    code_kfun(KF_XOR_INT, n->line);
	}
	break;

    case N_XOR_EQ:
	if (n->r.right->type == N_INT && n->r.right->l.number == -1) {
	    cg_fetch(n->l.left);
	    code_kfun(KF_NEG, 0);
	    code_instr(I_STORE, 0);
	} else {
	    cg_asgnop(n, KF_XOR);
	}
	break;

    case N_XOR_EQ_INT:
	if (n->r.right->type == N_INT && n->r.right->l.number == -1) {
	    cg_fetch(n->l.left);
	    code_kfun(KF_NEG_INT, 0);
	    code_instr(I_STORE, 0);
	} else {
	    cg_asgnop(n, KF_XOR_INT);
	}
	break;

    case N_MIN_MIN:
	cg_fetch(n->l.left);
	code_kfun(KF_SUB1, 0);
	code_instr(I_STORE, 0);
	code_kfun((n->mod == T_INT) ? KF_ADD1_INT : KF_ADD1, 0);
	break;

    case N_MIN_MIN_INT:
	cg_fetch(n->l.left);
	code_kfun(KF_SUB1_INT, 0);
	code_instr(I_STORE, 0);
	code_kfun(KF_ADD1_INT, 0);
	break;

    case N_PLUS_PLUS:
	cg_fetch(n->l.left);
	code_kfun(KF_ADD1, 0);
	code_instr(I_STORE, 0);
	code_kfun((n->mod == T_INT) ? KF_SUB1_INT : KF_SUB1, 0);
	break;

    case N_PLUS_PLUS_INT:
	cg_fetch(n->l.left);
	code_kfun(KF_ADD1_INT, 0);
	code_instr(I_STORE, 0);
	code_kfun(KF_SUB1_INT, 0);
	break;

# ifdef DEBUG
    default:
	fatal("unknown expression type %d", n->type);
# endif
    }

    if (pop) {
	*last_instruction |= I_POP_BIT;
    }
}

/*
 * NAME:	codegen->cond()
 * DESCRIPTION:	generate code for a condition
 */
static void cg_cond(n, jmptrue)
register node *n;
register int jmptrue;
{
    register jmplist *jlist;

    for (;;) {
	switch (n->type) {
	case N_INT:
	    if (jmptrue) {
		if (n->l.number != 0) {
		    true_list = jump(I_JUMP, true_list);
		}
	    } else if (n->l.number == 0) {
		false_list = jump(I_JUMP, false_list);
	    }
	    break;

	case N_LAND:
	    if (jmptrue) {
		jlist = false_list;
		false_list = (jmplist *) NULL;
		cg_cond(n->l.left, FALSE);
		cg_cond(n->r.right, TRUE);
		jump_resolve(false_list, here);
		false_list = jlist;
	    } else {
		cg_cond(n->l.left, FALSE);
		cg_cond(n->r.right, FALSE);
	    }
	    break;

	case N_LOR:
	    if (!jmptrue) {
		jlist = true_list;
		true_list = (jmplist *) NULL;
		cg_cond(n->l.left, TRUE);
		cg_cond(n->r.right, FALSE);
		jump_resolve(true_list, here);
		true_list = jlist;
	    } else {
		cg_cond(n->l.left, TRUE);
		cg_cond(n->r.right, TRUE);
	    }
	    break;

	case N_NOT:
	    jlist = true_list;
	    true_list = false_list;
	    false_list = jlist;
	    cg_cond(n->l.left, !jmptrue);
	    jlist = true_list;
	    true_list = false_list;
	    false_list = jlist;
	    break;

	case N_COMMA:
	    cg_expr(n->l.left, TRUE);
	    n = n->r.right;
	    continue;

	default:
	    cg_expr(n, FALSE);
	    if (jmptrue) {
		true_list = jump(I_JUMP_NONZERO | I_POP_BIT, true_list);
	    } else {
		false_list = jump(I_JUMP_ZERO | I_POP_BIT, false_list);
	    }
	    break;
	}
	break;
    }
}

typedef struct {
    Uint where;				/* where to jump to */
    jmplist *jump;			/* list of unresolved jumps */
} case_label;

static case_label *switch_table;	/* label table for current switch */

/*
 * NAME:	codegen->switch_int()
 * DESCRIPTION:	generate single label code for a switch statement
 */
static void cg_switch_int(n)
register node *n;
{
    register node *m;
    register int i, size, sz;
    case_label *table;

    /*
     * switch expression
     */
    cg_expr(n->r.right->l.left, FALSE);

    /*
     * switch table
     */
    code_instr(I_SWITCH | I_POP_BIT, 0);
    code_byte(SWITCH_INT);
    m = n->l.left;
    size = n->mod;
    sz = n->r.right->mod;
    if (m->l.left == (node *) NULL) {
	/* explicit default */
	m = m->r.right;
    } else {
	/* implicit default */
	size++;
    }
    code_word(size);
    code_byte(sz);

    table = switch_table;
    switch_table = ALLOCA(case_label, size);
    switch_table[0].jump = jump_addr((jmplist *) NULL);
    i = 1;
    do {
	register Int l;

	l = m->l.left->l.number;
	switch (sz) {
	case 4:
	    code_word((int) (l >> 16));
	case 2:
	    code_word((int) l);
	    break;

	case 3:
	    code_byte((int) (l >> 16));
	    code_word((int) l);
	    break;

	case 1:
	    code_byte((int) l);
	    break;
	}
	switch_table[i++].jump = jump_addr((jmplist *) NULL);
	m = m->r.right;
    } while (i < size);

    /*
     * generate code for body
     */
    cg_stmt(n->r.right->r.right);

    /*
     * resolve jumps
     */
    if (size > n->mod) {
	/* default: across switch */
	switch_table[0].where = here;
    }
    for (i = 0; i < size; i++) {
	jump_resolve(switch_table[i].jump, switch_table[i].where);
    }
    AFREE(switch_table);
    switch_table = table;
}

/*
 * NAME:	codegen->switch_range()
 * DESCRIPTION:	generate range label code for a switch statement
 */
static void cg_switch_range(n)
register node *n;
{
    register node *m;
    register int i, size, sz;
    case_label *table;

    /*
     * switch expression
     */
    cg_expr(n->r.right->l.left, FALSE);

    /*
     * switch table
     */
    code_instr(I_SWITCH | I_POP_BIT, 0);
    code_byte(SWITCH_RANGE);
    m = n->l.left;
    size = n->mod;
    sz = n->r.right->mod;
    if (m->l.left == (node *) NULL) {
	/* explicit default */
	m = m->r.right;
    } else {
	/* implicit default */
	size++;
    }
    code_word(size);
    code_byte(sz);

    table = switch_table;
    switch_table = ALLOCA(case_label, size);
    switch_table[0].jump = jump_addr((jmplist *) NULL);
    i = 1;
    do {
	register Int l;

	l = m->l.left->l.number;
	switch (sz) {
	case 4:
	    code_word((int) (l >> 16));
	case 2:
	    code_word((int) l);
	    break;

	case 3:
	    code_byte((int) (l >> 16));
	    code_word((int) l);
	    break;

	case 1:
	    code_byte((int) l);
	    break;
	}
	l = m->l.left->r.number;
	switch (sz) {
	case 4:
	    code_word((int) (l >> 16));
	case 2:
	    code_word((int) l);
	    break;

	case 3:
	    code_byte((int) (l >> 16));
	    code_word((int) l);
	    break;

	case 1:
	    code_byte((int) l);
	    break;
	}
	switch_table[i++].jump = jump_addr((jmplist *) NULL);
	m = m->r.right;
    } while (i < size);

    /*
     * generate code for body
     */
    cg_stmt(n->r.right->r.right);

    /*
     * resolve jumps
     */
    if (size > n->mod) {
	/* default: across switch */
	switch_table[0].where = here;
    }
    for (i = 0; i < size; i++) {
	jump_resolve(switch_table[i].jump, switch_table[i].where);
    }
    AFREE(switch_table);
    switch_table = table;
}

/*
 * NAME:	codegen->switch_str()
 * DESCRIPTION:	generate code for a string switch statement
 */
static void cg_switch_str(n)
register node *n;
{
    register node *m;
    register int i, size;
    case_label *table;

    /*
     * switch expression
     */
    cg_expr(n->r.right->l.left, FALSE);

    /*
     * switch table
     */
    code_instr(I_SWITCH | I_POP_BIT, 0);
    code_byte(SWITCH_STRING);
    m = n->l.left;
    size = n->mod;
    if (m->l.left == (node *) NULL) {
	/* explicit default */
	m = m->r.right;
    } else {
	/* implicit default */
	size++;
    }
    code_word(size);

    table = switch_table;
    switch_table = ALLOCA(case_label, size);
    switch_table[0].jump = jump_addr((jmplist *) NULL);
    i = 1;
    if (m->l.left->type == nil_node) {
	/*
	 * nil
	 */
	code_byte(0);
	switch_table[i++].jump = jump_addr((jmplist *) NULL);
	m = m->r.right;
    } else {
	/* no 0 case */
	code_byte(1);
    }
    while (i < size) {
	register Int l;

	l = ctrl_dstring(m->l.left->l.string);
	code_byte((int) (l >> 16));
	code_word((int) l);
	switch_table[i++].jump = jump_addr((jmplist *) NULL);
	m = m->r.right;
    }

    /*
     * generate code for body
     */
    cg_stmt(n->r.right->r.right);

    /*
     * resolve jumps
     */
    if (size > n->mod) {
	/* default: across switch */
	switch_table[0].where = here;
    }
    for (i = 0; i < size; i++) {
	jump_resolve(switch_table[i].jump, switch_table[i].where);
    }
    AFREE(switch_table);
    switch_table = table;
}

/*
 * NAME:	codegen->stmt()
 * DESCRIPTION:	generate code for a statement
 */
static void cg_stmt(n)
register node *n;
{
    register node *m;
    register jmplist *jlist, *j2list;
    register Uint where;

    while (n != (node *) NULL) {
	if (n->type == N_PAIR) {
	    m = n->l.left;
	    n = n->r.right;
	} else {
	    m = n;
	    n = (node *) NULL;
	}
	switch (m->type) {
	case N_BLOCK:
	    if (m->mod == N_BREAK) {
		jlist = break_list;
		break_list = (jmplist *) NULL;
		cg_stmt(m->l.left);
		if (break_list != (jmplist *) NULL) {
		    jump_resolve(break_list, here);
		}
		break_list = jlist;
	    } else {
		jlist = continue_list;
		continue_list = (jmplist *) NULL;
		cg_stmt(m->l.left);
		if (continue_list != (jmplist *) NULL) {
		    jump_resolve(continue_list, here);
		}
		continue_list = jlist;
	    }
	    break;

	case N_BREAK:
	    while (m->mod > 0) {
		code_instr(I_RETURN, 0);
		m->mod--;
	    }
	    break_list = jump(I_JUMP, break_list);
	    break;

	case N_CASE:
	    switch_table[m->mod].where = here;
	    cg_stmt(m->l.left);
	    break;

	case N_CONTINUE:
	    while (m->mod > 0) {
		code_instr(I_RETURN, 0);
		m->mod--;
	    }
	    continue_list = jump(I_JUMP, continue_list);
	    break;

	case N_DO:
	    where = here;
	    cg_stmt(m->r.right);
	    jlist = true_list;
	    true_list = (jmplist *) NULL;
	    cg_cond(m->l.left, TRUE);
	    jump_resolve(true_list, where);
	    true_list = jlist;
	    break;

	case N_FOR:
	    if (m->r.right != (node *) NULL) {
		jlist = jump(I_JUMP, (jmplist *) NULL);
		where = here;
		cg_stmt(m->r.right);
		jump_resolve(jlist, here);
	    } else {
		/* empty loop body */
		where = here;
	    }
	    jlist = true_list;
	    true_list = (jmplist *) NULL;
	    cg_cond(m->l.left, TRUE);
	    jump_resolve(true_list, where);
	    true_list = jlist;
	    break;

	case N_FOREVER:
	    where = here;
	    if (m->l.left != (node *) NULL) {
		cg_expr(m->l.left, TRUE);
	    }
	    cg_stmt(m->r.right);
	    jump_resolve(jump(I_JUMP, (jmplist *) NULL), where);
	    break;

	case N_RLIMITS:
	    cg_expr(m->l.left->l.left, FALSE);
	    cg_expr(m->l.left->r.right, FALSE);
	    code_instr(I_RLIMITS, 0);
	    code_byte(m->mod);
	    cg_stmt(m->r.right);
	    if (!(m->flags & F_END)) {
		code_instr(I_RETURN, 0);
	    }
	    break;

	case N_CATCH:
	    jlist = jump(I_CATCH | I_POP_BIT, (jmplist *) NULL);
	    cg_stmt(m->l.left);
	    if (m->l.left->flags & F_END) {
		jump_resolve(jlist, here);
		if (m->r.right != (node *) NULL) {
		    cg_stmt(m->r.right);
		}
	    } else {
		code_instr(I_RETURN, 0);
		if (m->r.right != (node *) NULL) {
		    j2list = jump(I_JUMP, (jmplist *) NULL);
		    jump_resolve(jlist, here);
		    cg_stmt(m->r.right);
		    jump_resolve(j2list, here);
		} else {
		    jump_resolve(jlist, here);
		}
	    }
	    break;

	case N_IF:
	    if (m->r.right->l.left != (node *) NULL &&
		m->r.right->l.left->mod == 0) {
		if (m->r.right->l.left->type == N_BREAK) {
		    jlist = true_list;
		    true_list = break_list;
		    cg_cond(m->l.left, TRUE);
		    break_list = true_list;
		    true_list = jlist;
		    if (m->r.right->r.right != (node *) NULL) {
			/* else */
			cg_stmt(m->r.right->r.right);
		    }
		    break;
		} else if (m->r.right->l.left->type == N_CONTINUE) {
		    jlist = true_list;
		    true_list = continue_list;
		    cg_cond(m->l.left, TRUE);
		    continue_list = true_list;
		    true_list = jlist;
		    if (m->r.right->r.right != (node *) NULL) {
			/* else */
			cg_stmt(m->r.right->r.right);
		    }
		    break;
		}
	    }
	    jlist = false_list;
	    false_list = (jmplist *) NULL;
	    cg_cond(m->l.left, FALSE);
	    cg_stmt(m->r.right->l.left);
	    if (m->r.right->r.right != (node *) NULL) {
		/* else */
		if (m->r.right->l.left != (node *) NULL &&
		    (m->r.right->l.left->flags & F_END)) {
		    jump_resolve(false_list, here);
		    false_list = jlist;
		    cg_stmt(m->r.right->r.right);
		} else {
		    j2list = jump(I_JUMP, (jmplist *) NULL);
		    jump_resolve(false_list, here);
		    false_list = jlist;
		    cg_stmt(m->r.right->r.right);
		    jump_resolve(j2list, here);
		}
	    } else {
		/* no else */
		jump_resolve(false_list, here);
		false_list = jlist;
	    }
	    break;

	case N_PAIR:
	    cg_stmt(m);
	    break;

	case N_POP:
	    cg_expr(m->l.left, TRUE);
	    break;

	case N_RETURN:
	    cg_expr(m->l.left, FALSE);
	    while (m->mod > 0) {
		code_instr(I_RETURN, 0);
		m->mod--;
	    }
	    code_instr(I_RETURN, m->line);
	    break;

	case N_SWITCH_INT:
	    cg_switch_int(m);
	    break;

	case N_SWITCH_RANGE:
	    cg_switch_range(m);
	    break;

	case N_SWITCH_STR:
	    cg_switch_str(m);
	    break;
	}
    }
}


static int nfuncs;		/* # functions generated */

/*
 * NAME:	codegen->init()
 * DESCRIPTION:	initialize the code generator
 */
void cg_init(inherited)
int inherited;
{
    nfuncs = 0;
}

/*
 * NAME:	codegen->compiled()
 * DESCRIPTION:	return FALSE to signal that the code is interpreted, and not
 *		compiled
 */
bool cg_compiled()
{
    return FALSE;
}

/*
 * NAME:	codegen->function()
 * DESCRIPTION:	generate code for a function
 */
char *cg_function(fname, n, nvar, npar, depth, size)
string *fname;
node *n;
int nvar, npar;
unsigned int depth;
unsigned short *size;
{
    char *prog;

    nparams = npar;
    cg_stmt(n);
    prog = code_make(depth + nvar - npar, nvar - npar, size);
    jump_make(prog + 5);
    nfuncs++;

    return prog;
}

/*
 * NAME:	codegen->nfuncs()
 * DESCRIPTION:	return the number of functions generated
 */
int cg_nfuncs()
{
    return nfuncs;
}

/*
 * NAME:	codegen->clear()
 * DESCRIPTION:	clean up code generator
 */
void cg_clear()
{
    jump_clear();
    code_clear();
}
