/
driver3.2@242/autoconf/
driver3.2@242/doc/LPC/
driver3.2@242/hosts/
driver3.2@242/hosts/amiga/NetIncl/
driver3.2@242/hosts/amiga/NetIncl/netinet/
driver3.2@242/hosts/amiga/NetIncl/sys/
driver3.2@242/hosts/atari/
driver3.2@242/hosts/fcrypt/
driver3.2@242/mudlib/
driver3.2@242/mudlib/sys/
driver3.2@242/util/
driver3.2@242/util/indent/hosts/next/
driver3.2@242/util/make_docs/
#if defined(AMIGA)
#include <stdarg.h>
#endif
#include "lint.h"
#include "lex.h"
#include "exec.h"
#include "interpret.h"
#include "object.h"
#include "lang.h"
#include "instrs.h"

/* maximum recursion depth for compile_value */
#define MAX_LAMBDA_LEVELS 0x10000;

#define SYMTAB_START_SIZE 16
#define CODE_BUFFER_START_SIZE 1024
#define VALUE_START_MAX 0x20

#define ZERO_ACCEPTED	0x01
#define VOID_ACCEPTED	0x02
#define VOID_GIVEN	0x04
#define NEGATE_ACCEPTED	0x08
#define NEGATE_GIVEN	0x10
#define REF_REJECTED	0x20

#define USE_INDEX_LVALUE	0x1
#define PROTECT_LVALUE		0x2

#define VOID_WANTED (ZERO_ACCEPTED | VOID_ACCEPTED | NEGATE_ACCEPTED)

static void insert_value_push PROT((struct svalue *));

static INLINE int function_cmp(name, prog, ix)
    char *name;
    struct program *prog;
    int ix;
{
    int32 flags;

    ix = prog->function_names[ix];
    flags = prog->functions[ix];
    while (flags & NAME_INHERITED) {
	struct inherit *inheritp;

	inheritp = &prog->inherit[flags & INHERIT_MASK];
	prog = inheritp->prog;
	ix -= inheritp->function_index_offset;
	flags = prog->functions[ix];
    }
    return memcmp(
      &name,
      &prog->program[flags & FUNSTART_MASK] - 1 - sizeof name,
      sizeof name
    );
}

int find_function(name, prog)
    char *name;
    struct program *prog;
{
    int i, o, d;
    int size;

    if ( !(size = prog->num_function_names) ) return -1;
    i = size >> 1;
    o = (i+2) >> 1;
    for (;;) {
	d = function_cmp(name, prog, i);
	if (d<0) {
	    i -= o;
	    if (i<0) {
		i = 0;
	    }
	} else if (d>0) {
	    i += o;
	    if (i >= size) {
		i = size-1;
	    }
	} else {
	    return prog->function_names[i];
	}
	if (o<=1) {
	    if (function_cmp(name, prog, i)) return -1;
	    return prog->function_names[i];
	}
	o = (o+1) >> 1;
    }
}

static struct work_area {
    struct symbol **symbols;
    mp_int symbol_max, symbol_mask, symbols_left;
    unsigned char *code, *codep;
    mp_int code_max, code_left;
    struct svalue *values, *valuep;
    mp_int value_max, values_left;
    mp_int num_locals;
    mp_int levels_left;
    struct work_area *last;
    struct object *lambda_origin;
} current = { 0, 0, 0, 0, 0, 0 };

struct lambda_replace_program_protector {
    struct svalue l;
    struct lambda_replace_program_protector *next;
    p_int size;
    struct vector *args;
    struct svalue block;
};

int lambda_ref_replace_program(l, type, size, args, block)
    struct lambda *l;
    int type;
    p_int size;
    struct vector *args;
    struct svalue *block;
{
    struct replace_ob *r_ob;

    for (r_ob = obj_list_replace; r_ob; r_ob = r_ob->next) {
	if (r_ob->ob == current_object) {
	    struct lambda_replace_program_protector *lrpp;

	    l->ref++;
	    lrpp = (struct lambda_replace_program_protector *)
		xalloc(sizeof *lrpp);
	    lrpp->l.u.lambda = l;
	    lrpp->l.x.closure_type = type;
	    lrpp->next = r_ob->lambda_rpp;
	    r_ob->lambda_rpp = lrpp;
	    if (size) {
		lrpp->size = size;
		args->ref++;
		lrpp->args = args;
		assign_svalue_no_free(&lrpp->block, block);
	    }
	    return 1;
	}
    }
    return 0;
}

void set_closure_user(svp, owner)
    struct svalue *svp;
    struct object *owner;
{
    int type;

    if ( !CLOSURE_MALLOCED(type = svp->x.closure_type) ) {
	free_object(svp->u.ob, "set_closure_user");
	svp->u.ob = owner;
    } else if (type == CLOSURE_PRELIMINARY) {
	int ix;
	struct lambda *l;
	int32 flags;
	struct program *prog;

	prog = owner->prog;
	l = svp->u.lambda;
	ix = l->function.index;
	if ( !(prog->flags & P_REPLACE_ACTIVE) ||
	     !lambda_ref_replace_program(
		l,
		ix >= CLOSURE_IDENTIFIER_OFFS ?
		  CLOSURE_IDENTIFIER :
		  CLOSURE_LFUN,
		0, 0, 0) )
	{
	    owner->flags |= O_LAMBDA_REFERENCED;
	}
	if (ix >= CLOSURE_IDENTIFIER_OFFS) {
	    ix -= CLOSURE_IDENTIFIER_OFFS;
	    svp->x.closure_type = CLOSURE_IDENTIFIER;
	} else {
	    flags = prog->functions[ix];
	    if (flags & NAME_CROSS_DEFINED) {
		ix += (flags & INHERIT_MASK) - (INHERIT_MASK + 1 >> 1);
	    }
	    svp->x.closure_type = CLOSURE_LFUN;
	}
	free_object(l->ob, "closure");
	l->function.index = ix;
	l->ob = owner;
    }
    add_ref(owner, "set_closure_user");
}

void replace_program_lambda_adjust(r_ob, old_prog)
    struct replace_ob *r_ob;
    struct program *old_prog;
{
    extern struct object *master_ob;

    static struct lambda_replace_program_protector *current_lrpp;

    struct lambda_replace_program_protector *lrpp, *next_lrpp;
    struct error_recovery_info error_recovery_info;

    lrpp = r_ob->lambda_rpp;
    /* Adjust lfuns first, because these are possible building blocks */
    do {
	if ( !CLOSURE_HAS_CODE(lrpp->l.x.closure_type) ) {
	    if (lrpp->l.x.closure_type == CLOSURE_LFUN) {
		struct lambda *l;
		int i;

		l = lrpp->l.u.lambda;
		i = l->function.index -= r_ob->fun_offset;
		if (i < 0 || i >= r_ob->new_prog->num_functions) {
		    assert_master_ob_loaded();
		    free_object(l->ob, "replace_program_lambda_adjust");
		    add_ref(
		      l->ob = master_ob, "replace_program_lambda_adjust");
		    i = find_function(
			findstring("dangling_lfun_closure"),
			master_ob->prog
		    );
		    l->function.index = i < 0 ? 0 : i;
		}
	    } else /* CLOSURE_IDENTIFIER */ {
		struct lambda *l;
		int i;

		l = lrpp->l.u.lambda;
		i = l->function.index -= r_ob->var_offset;
		if (i >= r_ob->new_prog->num_variables) {
		    l->function.index = -1;
		}
	    }
	}
    } while (lrpp = lrpp->next);
    lrpp = r_ob->lambda_rpp;
    error_recovery_info.last = error_recovery_pointer;
    error_recovery_info.type = ERROR_RECOVERY_BACKEND;
    error_recovery_pointer = &error_recovery_info;
    if (setjmp(error_recovery_info.context)) {
	unsigned char *p;

	lrpp = current_lrpp;
	p = lrpp->l.u.lambda->function.code;
	p[3] = F_ESCAPE - F_OFFSET;
	p[4] = F_UNDEF - F_OFFSET - 0x100;
	free_vector(lrpp->args);
	free_svalue(&lrpp->block);
	free_closure(&lrpp->l);
	next_lrpp = lrpp->next;
	xfree((char*)lrpp);
	lrpp = next_lrpp;
    }
    if (lrpp) do {
	if (lrpp->l.x.closure_type == CLOSURE_LAMBDA) {
	    struct lambda *l, *l2;
	    struct svalue *svp, *svp2;
	    mp_int num_values, num_values2, code_size2;

	    current_lrpp = lrpp;
	    l = lrpp->l.u.lambda;
	    l2 = lambda(lrpp->args, &lrpp->block, l->ob);
	    svp = (struct svalue *)l;
	    if ( (num_values = EXTRACT_UCHAR(l->function.code)) == 0xff)
		num_values = svp[-0xff].u.number;
	    svp2 = (struct svalue *)l2;
	    if ( (num_values2 = EXTRACT_UCHAR(l2->function.code)) == 0xff)
		num_values2 = svp2[-0xff].u.number;
	    code_size2 = current.code_max - current.code_left;
	    if (num_values != num_values2 || lrpp->size != code_size2) {
		free_svalue(&lrpp->block);
		/* lrpp->block will be freed after the error */
		lrpp->block.type = T_CLOSURE;
		lrpp->block.x.closure_type = CLOSURE_UNBOUND_LAMBDA;
		lrpp->block.u.lambda = l2;
		error("Cannot adjust lambda closure after replace_program()\n");
	    }
	    while (--num_values >= 0)
		transfer_svalue(--svp, --svp2);
	    memcpy(l->function.code, l2->function.code, code_size2);
	    xfree((char *)svp2);
	    free_vector(lrpp->args);
	    free_svalue(&lrpp->block);
	}
	free_closure(&lrpp->l);
	next_lrpp = lrpp->next;
	xfree((char*)lrpp);
    } while (lrpp = next_lrpp);
    error_recovery_pointer = error_recovery_info.last;
}

void closure_literal(dest, ix)
    struct svalue *dest;
    int ix;
{
    extern int function_index_offset;

    struct lambda *l;
    int32 flags;
    struct program *prog;

    l = (struct lambda *)
	xalloc(sizeof *l - sizeof l->function + sizeof l->function.index);
    l->ref = 1;
    prog = current_object->prog;
    if ( !(prog->flags & P_REPLACE_ACTIVE) ||
	 !lambda_ref_replace_program(
	    l,
	    ix >= CLOSURE_IDENTIFIER_OFFS ? CLOSURE_IDENTIFIER : CLOSURE_LFUN,
	    0, 0, 0) )
    {
	current_object->flags |= O_LAMBDA_REFERENCED;
    }
    if (ix >= CLOSURE_IDENTIFIER_OFFS) {
	extern struct svalue *current_variables;

	ix +=
	  -CLOSURE_IDENTIFIER_OFFS +
	  (current_variables - current_object->variables);
	dest->x.closure_type = CLOSURE_IDENTIFIER;
    } else {
	ix += function_index_offset;
	flags = prog->functions[ix];
	if (flags & NAME_CROSS_DEFINED) {
	    ix += (flags & INHERIT_MASK) - (INHERIT_MASK + 1 >> 1);
	}
	dest->x.closure_type = CLOSURE_LFUN;
    }
    l->ob = current_object;
    l->function.index = ix;
    add_ref(current_object, "closure");
    dest->type = T_CLOSURE;
    dest->u.lambda = l;
}

struct symbol {
    char *name;
    struct symbol *next;
    struct symbol *next_local;
    int index;
};

void realloc_values() {
    mp_int new_max;
    struct svalue *new_values;

    current.values_left += current.value_max;
    new_max = current.value_max << 1;
    new_values = xalloc(new_max * sizeof new_values[0]);
    memcpy(
	(current.valuep = new_values + current.value_max),
	current.values,
	current.value_max
    );
    current.values = new_values;
    current.value_max = new_max;
}

void realloc_code() {
    mp_int new_max;
    unsigned char *new_code;

    current.code_left += current.code_max;
    current.code_max = new_max = current.value_max << 1;
    new_code = rexalloc(current.code, new_max);
    current.codep += new_code - current.code;
    current.code = new_code;
}

static void free_symbols();

#if defined(AMIGA)
static VOLATILE void lambda_error(char *error_str, ...)
{
    va_list va;
#else
static VOLATILE void lambda_error(error_str, a1)
    char *error_str;
    char *a1;
{
#endif
    for (;;) {
	free_symbols();
	if (current.code)
	    xfree(current.code);
	if (current.values) {
	    mp_int num_values = current.value_max - current.values_left;
	    struct svalue *svp;

	    for (svp = current.valuep; --num_values >= 0; )
		free_svalue(svp++);
	    xfree((char *)current.values);
	}
	if (!current.last) break;
	current = *current.last;
    }
#if defined(AMIGA)
    va_start(va, error_str);
    error(error_str, va_arg(va, int32)); /* One arg or nothing :-) */
    va_end(va);
#else
    error(error_str, a1);
#endif
}

static void free_symbols()
{
    p_int i;
    struct symbol **symp, *sym, *next;

    i = current.symbol_max;
    symp = current.symbols;
    do {
	for (sym = *symp++; sym; sym = next) {
	    next = sym->next;
	    xfree((char *)sym);
	}
    } while (i -= sizeof sym);
    xfree((char *)current.symbols);
}

struct symbol *make_symbol(name)
    char *name;
{
    p_int h;
    struct symbol *sym, **symp;

    h = (p_int)name;
    h ^= h >> 16;
    h ^= h >> 8;
    h ^= h >> 4;
    h &= current.symbol_mask;
    symp = (struct symbol **)((char *)current.symbols + h);
    for (sym = *symp; sym; sym = sym->next) {
	if (sym->name == name)
	    return sym;
    }
    sym = (struct symbol *)xalloc(sizeof *sym);
    sym->name = name;
    sym->index = -1;
    sym->next = *symp;
    *symp = sym;
    if ( !(current.symbols_left -= sizeof sym) ) {
	struct symbol **newtab, *sym2;
	p_int i;

	sym2 = sym;
	current.symbols_left = current.symbol_max;
	current.symbol_max <<= 1;
	symp = newtab = (struct symbol **)xalloc(current.symbol_max);
	current.symbol_mask = i = current.symbol_max - sizeof sym;
	do {
	    *symp++ = 0;
	} while (--i >= 0);
	i = current.symbols_left - 1;
	do {
	    struct symbol *next;

	    for (sym = current.symbols[i]; sym; sym = next) {
		next = sym->next;
		h = (p_int)sym->name;
		h ^= h >> 16;
		h ^= h >> 8;
		h ^= h >> 4;
		h &= current.symbol_mask;
		symp = (struct symbol **)((char *)newtab + h);
		sym->next = *symp;
		*symp = sym;
	    }
	} while (--i >= 0);
	return sym2;
    }
    return sym;
}

/* compile_lvalue does not only supply an lvalue, but also 1 byte space to
 * store the assignment code
 */
void compile_lvalue PROT((struct svalue *, int));

int compile_value(value, opt_flags)
    struct svalue *value;
    int opt_flags;
{
    extern struct svalue const0, const1;

    if (!--current.levels_left)
	lambda_error("Too deep recursion inside lambda()\n");
    switch(value->type) {
      case T_POINTER:
      {
	struct vector *block;
	struct svalue *argp;
	ph_int type;
    
	block = value->u.vec;
	argp = block->item;
	if (!block->size || argp->type != T_CLOSURE) {
	    lambda_error("Missing function\n");
	}
	if ( (type = argp->x.closure_type) < CLOSURE_SIMUL_EFUN) {
	    if (type < CLOSURE_EFUN) {
		/* operator */
		switch(type - CLOSURE_OPERATOR) {
		  default:
		    lambda_error("Unimplemented operator %s for lambda()\n",
		      instrs[type - CLOSURE_OPERATOR].name);
		  case F_LOR-F_OFFSET:
		  case F_LAND-F_OFFSET:
		  {
		    mp_int *branchp;
		    mp_int i, start, end;
		    int code = type - CLOSURE_OPERATOR;
		    int void_given;

		    if (opt_flags & VOID_ACCEPTED) {
			code =
			  code == F_LAND-F_OFFSET ?
			    F_BRANCH_WHEN_ZERO - F_OFFSET :
			    F_BRANCH_WHEN_NON_ZERO - F_OFFSET;
			opt_flags |= VOID_GIVEN;
		    }
		    i = block->size - 1;
		    branchp = alloca(i * sizeof *branchp);
		    while (--i > 0) {
			compile_value(++argp, REF_REJECTED);
			if (current.code_left < 2)
			    realloc_code();
			*branchp++ = current.code_max - current.code_left;
			current.code_left -= 2;
			*current.codep = code;
			current.codep += 2;
		    }
		    void_given = compile_value(
		      i ?
			(code == F_LAND-F_OFFSET ? &const1 : &const0) :
			++argp,
		      opt_flags & (VOID_ACCEPTED|REF_REJECTED)
		    );
		    if (opt_flags & VOID_ACCEPTED && !(void_given & VOID_GIVEN))
		    {
			if (current.code_left < 1)
			    realloc_code();
			current.code_left--;
			*current.codep++ = F_POP_VALUE-F_OFFSET;
		    }
		    i = block->size - 1;
		    end = current.code_max - current.code_left;
		    while (--i > 0) {
			mp_int offset;

			start = *--branchp;
			offset = end - start - 2;
			if (offset <= 0xff) {
			    current.code[start+1] = offset;
			    continue;
			} else {
			    mp_int growth;
			    int growth_factor;
			    mp_int j;
			    char *p, *q;

			    if (opt_flags & VOID_ACCEPTED) {
				growth = i;
				growth_factor = 1;
				code +=
				  F_LBRANCH_WHEN_ZERO - F_BRANCH_WHEN_ZERO;
			    } else {
				growth = i * 3;
				growth_factor = 3;
				code += F_LBRANCH_WHEN_ZERO - F_LAND;
			    }
			    if (current.code_left < growth)
				realloc_code();
			    current.code_left -= growth;
			    current.codep += growth;
			    p = current.code + end;
			    q = p + growth;
			    /* - 1 is precompensation for leading branch code */
			    end += growth_factor - 1;
			    if ( !(opt_flags & VOID_ACCEPTED) )
				/* offset precompensation for leading F_DUP */
				end--;
			    branchp++;
			    do {
				char tmp_short[2];
				start = *--branchp;
				offset = end - start;
				end += growth_factor;
				if (offset > 0x7fff)
				    lambda_error("Unimplemented\n");
				*(short *)tmp_short = offset;
				j = offset;
				do {
				    *--q = *--p;
				} while (--j);
				if (opt_flags & VOID_ACCEPTED) {
				    *--q = tmp_short[1];
				    *--q = tmp_short[0];
				    *--q = code;
				} else {
				    *--q = F_POP_VALUE - F_OFFSET;
				    *--q = tmp_short[1];
				    *--q = tmp_short[0];
				    *--q = code;
				    *--q = F_DUP - F_OFFSET;
				}
				p -= 2;
			    } while (--i > 0);
			    break;
			}
		    }
		    break;
		  }
		  case F_BRANCH_WHEN_ZERO-F_OFFSET:
		  case F_BRANCH_WHEN_NON_ZERO-F_OFFSET:
		  {
		    mp_int *branchp;
		    mp_int i, start, end;
		    int code = type - CLOSURE_OPERATOR;
		    int opt_used, all_void;
		    mp_int last_branch;

		    i = block->size;
		    branchp = alloca(i * sizeof *branchp);
		    all_void = VOID_GIVEN;
		    while ( (i -= 2) > 0) {
			mp_int offset;

			opt_used = compile_value(++argp, NEGATE_ACCEPTED);
			if (current.code_left < 2)
			    realloc_code();
			last_branch = current.code_max - current.code_left;
			current.code_left -= 2;
			*current.codep = opt_used & NEGATE_GIVEN ?
			    (code == F_BRANCH_WHEN_NON_ZERO-F_OFFSET ?
				F_BRANCH_WHEN_ZERO :
				F_BRANCH_WHEN_NON_ZERO) :
			    code;
			current.codep += 2;
			opt_used =
			  compile_value(
			    ++argp,
			    opt_flags & (VOID_ACCEPTED|REF_REJECTED)
			  );
			all_void &= opt_used;
			if (current.code_left < 4)
			    realloc_code();
			offset =
			  current.code_max - current.code_left - last_branch;
			/* Allow the offset to be incremented
			 * by one afterwards.
			 */
			if (offset > 0xfe) {
			    char *p, tmp_short[2];
			    mp_int j;

			    p = current.codep++;
			    j = offset - 2;
			    if (offset > 0x7ffd)
				lambda_error("Unimplemented\n");
			    do {
				p--;
				p[1] = *p;
			    } while (--j);
			    current.code_left--;
			    *((short *)tmp_short) = offset + 2;
			    current.code[last_branch] +=
			      F_LBRANCH_WHEN_ZERO - F_BRANCH_WHEN_ZERO;
			    current.code[last_branch+1] = tmp_short[0];
			    current.code[last_branch+2] = tmp_short[1];
			} else {
			    current.code[last_branch+1] = offset;
			}
			*branchp++ = current.code_max - current.code_left;
			*branchp++ = last_branch;
			current.code_left -= 2;
			*current.codep++ = F_BRANCH-F_OFFSET;
			*current.codep++ = opt_used;
		    }
		    if ( opt_flags & VOID_ACCEPTED &&
			 (i || argp->type == T_NUMBER) && block->size > 2 )
		    {
			mp_int offset;

			opt_flags |= VOID_GIVEN;
			if (all_void) {
			    offset = -2;
			} else if ( !(current.codep[-1] & VOID_GIVEN) ) {
			    current.codep[-2] = F_POP_VALUE - F_OFFSET;
			    offset = -1;
			} else {
			    current.codep[-1] = 1;
			    *current.codep = F_POP_VALUE - F_OFFSET;
			    offset = 1;
			}
			start = *--branchp;
			code = current.code[start];
			if (code == F_LBRANCH_WHEN_ZERO-F_OFFSET ||
			    code == F_LBRANCH_WHEN_NON_ZERO-F_OFFSET)
			{
			    char tmp_short[2];

			    tmp_short[0] = current.code[start+1];
			    tmp_short[1] = current.code[start+2];
			    (*(short *)tmp_short) += offset;
			    current.code[start+1] = tmp_short[0];
			    current.code[start+2] = tmp_short[1];
			} else {
			    current.code[start+1] += offset;
			}
			current.codep += offset;
			current.code_left -= offset;
			branchp--;
			opt_used = VOID_GIVEN;
			i = block->size - 1;
		    } else {
			opt_used = compile_value(
			  i ? &const0 : ++argp,
			  opt_flags &
			    ( all_void ?
			      (VOID_ACCEPTED|REF_REJECTED) :
			      REF_REJECTED
			    )
			);
			if (opt_flags & VOID_ACCEPTED) {
			    opt_flags |= VOID_GIVEN;
			    if ( !(opt_used & VOID_GIVEN) ) {
				if (current.code_left < 1)
				    realloc_code();
				current.code_left--;
				*current.codep++ = F_POP_VALUE-F_OFFSET;
				opt_used = VOID_GIVEN;
			    } else {
				opt_used = VOID_ACCEPTED;
			    }
			} else {
			    opt_used = VOID_ACCEPTED;
			}
			i = block->size;
		    }
		    end = current.code_max - current.code_left;
		    while ( (i -= 2) > 0) {
			mp_int offset;

			start = *(branchp -= 2);
			offset = end - start - 2;
			if ( !( (current.code[start+1] | VOID_ACCEPTED) &
								    opt_used) )
			{
			    offset--;
			}
			if (offset <= 0xff) {
			    current.code[start+1] = offset;
			    continue;
			} else {
			    mp_int growth;
			    mp_int j;
			    char *p, *q;

			    growth = i;
			    if (current.code_left < growth)
				realloc_code();
			    current.code_left -= growth;
			    current.codep += growth;
			    p = current.code + end;
			    q = p + growth;
			    branchp++;
			    do {
				char tmp_short[2];

				start = *--branchp;
				code = current.code[start];
				if (code == F_LBRANCH_WHEN_ZERO-F_OFFSET ||
				    code == F_LBRANCH_WHEN_NON_ZERO-F_OFFSET)
				{
				    tmp_short[0] = current.code[start+1];
				    tmp_short[1] = current.code[start+2];
				    (*(short *)tmp_short)++;
				    current.code[start+1] = tmp_short[0];
				    current.code[start+2] = tmp_short[1];
				} else {
				    current.code[start+1]++;
				}
				start = *--branchp;
				offset = end - start;
				if ( !(
				  (current.code[start+1] | VOID_ACCEPTED) &
								    opt_used
				) )
				{
				    offset--;
				}
				end++;
				if (offset > 0x7fff)
				    lambda_error("Unimplemented\n");
				*(short *)tmp_short = offset;
				j = offset;
				do {
				    *--q = *--p;
				} while (--j);
				*--q = tmp_short[1];
				*--q = tmp_short[0];
				*--q = *(p-=2) +
				    (F_LBRANCH_WHEN_ZERO - F_BRANCH_WHEN_ZERO);
			    } while ( (i -= 2) > 0);
			    break;
			}
		    }
		    break;
		  }
		  case F_POP_VALUE-F_OFFSET:
		  {
		    mp_int i;
		    int void_given;

		    for (i = block->size - 1; --i > 0; ) {
			void_given = compile_value(++argp, VOID_WANTED);
			if ( !(void_given & VOID_GIVEN) ) {
			    if (current.code_left < 1)
				realloc_code();
			    current.code_left--;
			    *current.codep++ = F_POP_VALUE - F_OFFSET;
			}
		    }
		    opt_flags = compile_value(i ? &const0 : ++argp, opt_flags);
		    break;
		  }
		  case F_ASSIGN-F_OFFSET:
		  {
		    mp_int i;

		    if ( !(block->size & 1) )
			lambda_error("Missing value in assignment\n");
		    argp++;
		    for (i = block->size - 1; (i -= 2) >= 0; argp+=2) {
			compile_value(argp+1, REF_REJECTED);
			compile_lvalue(argp, USE_INDEX_LVALUE);
			if (!i) {
			    if (opt_flags & VOID_ACCEPTED) {
				opt_flags = VOID_GIVEN;
				*current.codep++ = F_VOID_ASSIGN - F_OFFSET;
			    } else {
				*current.codep++ = F_ASSIGN - F_OFFSET;
			    }
			} else {
			    *current.codep++ = F_VOID_ASSIGN - F_OFFSET;
			}
		    }
		    break;
		  }
		  case F_ADD_EQ-F_OFFSET:
		  {
		    if (block->size != 3)
			lambda_error("Bad number of arguments to #'+=\n");
		    compile_value(argp+2, REF_REJECTED);
		    compile_lvalue(argp+1, USE_INDEX_LVALUE);
		    if (opt_flags & VOID_ACCEPTED) {
			opt_flags = VOID_GIVEN;
			*current.codep++ = F_VOID_ADD_EQ - F_OFFSET;
		    } else {
			*current.codep++ = F_ADD_EQ - F_OFFSET;
		    }
		    break;
		  }
		  case F_SUB_EQ-F_OFFSET:
		  case F_MULT_EQ-F_OFFSET:
		  case F_AND_EQ-F_OFFSET:
		  case F_OR_EQ-F_OFFSET:
		  case F_XOR_EQ-F_OFFSET:
		  case F_LSH_EQ-F_OFFSET:
		  case F_RSH_EQ-F_OFFSET:
		  case F_DIV_EQ-F_OFFSET:
		  case F_MOD_EQ-F_OFFSET:
		  {
		    if (block->size != 3)
			lambda_error("Bad number of arguments to #'-=\n");
		    compile_value(argp+2, REF_REJECTED);
		    compile_lvalue(argp+1, USE_INDEX_LVALUE);
		    *current.codep++ = type;
		    break;
		  }
		  case F_BBRANCH_WHEN_NON_ZERO-F_OFFSET: /* #'do */
		  {
		    mp_int i;
		    int void_given;
		    mp_int offset;

		    i = block->size - 3;
		    if (i < 0)
			lambda_error("Missing argument(s) to #'do\n");
		    offset = current.code_left - current.code_max;
		    if (i) do {
			void_given = compile_value(++argp, VOID_WANTED);
			if ( !(void_given & VOID_GIVEN) ) {
			    if (current.code_left < 1)
				realloc_code();
			    current.code_left--;
			    *current.codep++ = F_POP_VALUE - F_OFFSET;
			}
		    } while(--i);
		    void_given =
		      compile_value(++argp, NEGATE_ACCEPTED);
		    offset += current.code_max - current.code_left + 1;
		    if (current.code_left < 3)
			realloc_code();
		    if (offset > 0xff) {
			char tmp_short[2];

			if (offset > 0x8000)
			    lambda_error("Unimplemented\n");
			current.code_left -= 3;
			*((short *)tmp_short) = -offset;
			*current.codep++ = void_given & NEGATE_GIVEN ?
			    F_LBRANCH_WHEN_ZERO - F_OFFSET		 :
			    F_LBRANCH_WHEN_NON_ZERO - F_OFFSET;
			*current.codep++ = tmp_short[0];
			*current.codep++ = tmp_short[1];
		    } else {
			current.code_left -= 2;
			*current.codep++ = void_given & NEGATE_GIVEN ?
			    F_BBRANCH_WHEN_ZERO - F_OFFSET		 :
			    F_BBRANCH_WHEN_NON_ZERO - F_OFFSET;
			*current.codep++ = offset;
		    }
		    opt_flags = compile_value(++argp, opt_flags);
		    break;
		  }
		  case F_BBRANCH_WHEN_ZERO-F_OFFSET: /* #'while */
		  {
		    mp_int i;
		    int void_given;
		    mp_int start_branch;
		    mp_int offset;

		    if (current.code_left < 2)
			realloc_code();
		    current.code_left -= 2;
		    start_branch = current.code_max - current.code_left;
		    *current.codep = F_BRANCH - F_OFFSET;
		    current.codep += 2;
		    i = block->size - 3;
		    if (i < 0)
			lambda_error("Missing argument(s) to #'while\n");
		    offset = current.code_left - current.code_max;
		    argp += 2;
		    if (i) do {
			void_given = compile_value(++argp, VOID_WANTED);
			if ( !(void_given & VOID_GIVEN) ) {
			    if (current.code_left < 2)
				realloc_code();
			    current.code_left--;
			    *current.codep++ = F_POP_VALUE - F_OFFSET;
			}
		    } while(--i);
		    offset =
		      current.code_max - current.code_left - start_branch;
		    if (offset > 0xff) {
			char *p, tmp_short[2];

			if (offset > 0x7ffd)
			    lambda_error("Unimplemented\n");
			p = current.codep++;
			i = offset;
			do {
			    p--;
			    p[1] = *p;
			} while (--i);
			*((short *)tmp_short) = offset + 2;
			current.code[start_branch-2] = F_LBRANCH - F_OFFSET;
			current.code[start_branch-1] = tmp_short[0];
			current.code[start_branch-0] = tmp_short[1];
			start_branch++;
		    } else {
			current.code[start_branch-1] = offset;
		    }
		    argp = block->item;
		    void_given =
		      compile_value(++argp, NEGATE_ACCEPTED);
		    if (current.code_left < 3)
			realloc_code();
		    offset =
		      current.code_max - current.code_left - start_branch + 1;
		    if (offset > 0xff) {
			char tmp_short[2];

			if (offset > 0x8000)
			    lambda_error("Unimplemented\n");
			current.code_left -= 3;
			*((short *)tmp_short) = -offset;
			*current.codep++ = void_given & NEGATE_GIVEN ?
			    F_LBRANCH_WHEN_ZERO - F_OFFSET		 :
			    F_LBRANCH_WHEN_NON_ZERO - F_OFFSET;
			*current.codep++ = tmp_short[0];
			*current.codep++ = tmp_short[1];
		    } else {
			current.code_left -= 2;
			*current.codep++ = void_given & NEGATE_GIVEN ?
			    F_BBRANCH_WHEN_ZERO - F_OFFSET		 :
			    F_BBRANCH_WHEN_NON_ZERO - F_OFFSET;
			*current.codep++ = offset;
		    }
		    opt_flags = compile_value(++argp, opt_flags);
		    break;
		  }
		  case F_NOT-F_OFFSET:
		  {
		    if (block->size != 2)
			lambda_error("Wrong number of arguments to #'!\n");
		    opt_flags |= compile_value(++argp, opt_flags);
		    if (opt_flags & NEGATE_ACCEPTED) {
			opt_flags ^= NEGATE_GIVEN;
		    } else {
			if (current.code_left < 1)
			    realloc_code();
			current.code_left--;
			*current.codep++ = F_NOT - F_OFFSET;
		    }
		    break;
		  }
		  case F_AND-F_OFFSET:
		  {
		    int i;

		    if ( (i = block->size - 2) > 0) {
			compile_value(++argp, 0);
			do {
			    compile_value(++argp, 0);
			    if (current.code_left < 1)
				realloc_code();
			    current.code_left--;
			    *current.codep++ = F_AND - F_OFFSET;
			} while (--i);

		    } else if (!i) {
			if (opt_flags & REF_REJECTED)
			    lambda_error("Reference value in bad position\n");
			compile_lvalue(++argp, PROTECT_LVALUE|USE_INDEX_LVALUE);
			current.code_left++;
		    } else {
			lambda_error("Missing argument(s) to #'&\n");
		    }
		    break;
		  }
		  case F_SSCANF:
		  {
		    int lvalues;

		    if ( (lvalues = block->size - 3) < 0)
			lambda_error("Missing argument(s) to #'sscanf\n");
		    if (lvalues > 0xff - 2)
			lambda_error("Too many arguments to #'sscanf\n");
		    compile_value(++argp, 0);
		    compile_value(++argp, 0);
		    while (--lvalues >= 0) {
			compile_lvalue(++argp, PROTECT_LVALUE|USE_INDEX_LVALUE);
			current.code_left++;
		    }
		    if (current.code_left < 2)
			realloc_code();
		    current.code_left -= 2;
		    *current.codep++ = F_SSCANF - F_OFFSET;
		    *current.codep++ = block->size - 1;
		  }
		  case F_AGGREGATE-F_OFFSET:
		  {
		    int i;
		    char size[2];

		    i = block->size - 1;
		    *(short *)size = i;
		    while (--i >= 0) {
			compile_value(++argp, REF_REJECTED);
		    }
		    if (current.code_left < 3)
			realloc_code();
		    current.code_left -= 3;
		    *current.codep++ = F_AGGREGATE-F_OFFSET;
		    *current.codep++ = size[0];
		    *current.codep++ = size[1];
		    break;
		  }
		  case F_M_CAGGREGATE-F_OFFSET:
		  {
		    int i, j, num_keys, num_values;

		    num_values = 1;
		    i = block->size;
		    num_keys = i - 1;
		    for (i = block->size; --i;) {
			struct svalue *element;

			if ( (++argp)->type != T_POINTER )
			    lambda_error("Bad argument to #'([\n");
			element = argp->u.vec->item;
			j = argp->u.vec->size;
			if (j != num_values) {
			    if (i != num_keys)
				lambda_error(
				  "#'([ : Inconsistent value count.\n");
			    num_values = j;
			}
			while (--j >= 0) {
			    compile_value(element++, REF_REJECTED);
			}
		    }
		    if (current.code_left < 5)
			realloc_code();
		    if ( (num_keys | num_values) & ~0xff) {
			char size[2];

			current.code_left -= 5;
			*current.codep++ = F_AGGREGATE-F_OFFSET;
			*(short *)size = num_keys;
			*current.codep++ = size[0];
			*current.codep++ = size[1];
			*(short *)size = num_values;
			*current.codep++ = size[0];
			*current.codep++ = size[1];
		    } else {
			current.code_left -= 3;
			*current.codep++ = F_M_CAGGREGATE-F_OFFSET;
			*current.codep++ = num_keys;
			*current.codep++ = num_values;
		    }
		    break;
		  }
		  case F_RETURN-F_OFFSET:
		  {
		    if (block->size != 2) {
			if (block->size > 1)
			    lambda_error("Too many arguments to #'return\n");
			opt_flags = VOID_GIVEN;
		    } else {
			opt_flags =
			  compile_value(++argp, ZERO_ACCEPTED|REF_REJECTED);
		    }
		    if (current.code_left < 1)
			realloc_code();
		    current.code_left--;
		    *current.codep++ =
		      opt_flags & VOID_GIVEN ?
			F_RETURN0 - F_OFFSET :
			F_RETURN - F_OFFSET;
		    break;
		  }
		  case F_EXTRACT_LVALUE-F_OFFSET:
		  {
		    if (block->size != 2)
			lambda_error("Bad number of arguments to #'[<..\n");
		    if ((++argp)->type == T_NUMBER) {
			static struct svalue tmp_svalue = { T_NUMBER };

			tmp_svalue.u.number = - argp->u.number;
			compile_value(&tmp_svalue, 0);
		    } else {
			compile_value(argp, 0);
			if (current.code_left < 1)
			    realloc_code();
			current.code_left--;
			*current.codep++ = F_NEGATE - F_OFFSET;
		    }
		    if (current.code_left < 1)
			realloc_code();
		    current.code_left--;
		    *current.codep++ = F_EXTRACT2 - F_OFFSET;
		    break;
		  }
		}
	    } else {
		/* efun */
		mp_int i;
		char *p;
		int f;
		int num_arg, min, max, def;
	
		for (i = block->size; --i; ) {
		    compile_value(++argp, 0);
		}
		argp = block->item;
		if (current.code_left < 5)
		    realloc_code();
		num_arg = block->size - 1;
		f = type - CLOSURE_EFUN;
		min = instrs[f].min_arg;
		max = instrs[f].max_arg;
		p = current.codep;
		if (num_arg < min) {
		    extern int proxy_efun PROT((int, int));
	
		    int g;
	
		    if (num_arg == min-1 && (def = instrs[f].Default)) {
			*p++ = def - F_OFFSET;
			current.code_left--;
			max--;
			min--;
		    } else if ( (g = proxy_efun(f, num_arg)) < 0 ||
							    (f = g,MY_FALSE) )
			lambda_error("Too few arguments to %s\n", instrs[f].name);
		} else if (num_arg > max && max != -1) {
		    lambda_error("Too many arguments to %s\n", instrs[f].name);
		}
		if (f > 0xff) {
		    *p++ = F_ESCAPE-F_OFFSET;
		    current.code_left--;
		}
		*p++ = f;
		current.code_left--;
		if (min != max) {
		    *p++ = num_arg;
		    if (num_arg > 0xff)
			lambda_error("Too many arguments to efun closure\n");
		    current.code_left--;
		}
		if ( instrs[f].ret_type == TYPE_VOID ) {
		    if (opt_flags & ZERO_ACCEPTED) {
			opt_flags = VOID_GIVEN;
		    } else {
			*p++ = F_CONST0-F_OFFSET;
			current.code_left--;
		    }
		}
		current.codep = p;
		break;
	    }
	} else switch (argp->x.closure_type) {
	  default:
	  {
	    /* simul_efun */
	    extern struct function *simul_efunp;
	    int simul_efun;
	    int num_arg;
	    int i;

	    simul_efun = type - CLOSURE_SIMUL_EFUN;
	    if (simul_efun > 0xff) {
		static struct svalue string_sv = { T_STRING };

		string_sv.x.string_type = STRING_SHARED;
		string_sv.u.string = query_simul_efun_file_name();
		compile_value(&string_sv, 0);
		string_sv.u.string =
		  simul_efunp[simul_efun].name;
		compile_value(&string_sv, 0);
	    }
	    for (i = block->size; --i; ) {
		compile_value(++argp, 0);
	    }
	    if (current.code_left < 3)
		realloc_code();
	    num_arg = block->size - 1;
	    if (simul_efun > 0xff) {
		current.code_left -= 2;
		*current.codep++ = F_CALL_OTHER - F_OFFSET;
		*current.codep++ = num_arg + 2;
		if (num_arg + 2 > 0xff)
		    lambda_error("Argument number overflow\n");
	    } else {
		struct function *funp;

		funp = &simul_efunp[simul_efun];
		if (num_arg > funp->num_arg)
		    lambda_error(
		      "Too many arguments to simul_efun %s\n", funp->name
		    );
		if (funp->num_arg != 0xff) {

		    i = funp->num_arg - num_arg;
		    if (i > 1 && current.code_left < i + 2)
			realloc_code();
		    current.code_left -= i;
		    while ( --i >= 0 ) {
			*current.codep++ = F_CONST0 - F_OFFSET;
		    }
		}
		*current.codep++ = F_SIMUL_EFUN - F_OFFSET;
		*current.codep++ = simul_efun;
		if (funp->num_arg == 0xff) {
		    *current.codep++ = num_arg;
		    current.code_left -= 3;
		} else
		    current.code_left -= 2;
		break;
	    }
	  }
	  case CLOSURE_UNBOUND_LAMBDA:
	  case CLOSURE_BOUND_LAMBDA:
	  case CLOSURE_LAMBDA:
	  case CLOSURE_PRELIMINARY:
	    lambda_error("Unimplemented closure type for lambda()\n");
	  case CLOSURE_LFUN:
	  {
	    mp_int i;
	    struct lambda *l;

	    l = argp->u.lambda;
	    if (l->ob != current.lambda_origin) {
		insert_value_push(argp);
		for (i = block->size; --i; ) {
		    compile_value(++argp, 0);
		}
		if (current.code_left < 2)
		    realloc_code();
		current.code_left -= 2;
		*current.codep++ = F_FUNCALL - F_OFFSET;
		*current.codep++ = block->size;
	    } else {
		for (i = block->size; --i; ) {
		    compile_value(++argp, 0);
		}
		if (current.code_left < 4)
		    realloc_code();
		current.code_left -= 4;
		*current.codep++ = F_CALL_FUNCTION_BY_ADDRESS - F_OFFSET;
		*current.codep++ = ((char *)&l->function.index)[0];
		*current.codep++ = ((char *)&l->function.index)[1];
		*current.codep++ = block->size - 1;
		if (block->size > 0x100)
		    lambda_error("Too many arguments to lfun closure\n");
	    }
	    break;
	  }
	  case CLOSURE_IDENTIFIER:
	  {
	    struct lambda *l;

	    if (block->size != 1)
		lambda_error("Argument to variable\n");
	    if (l->ob != current.lambda_origin) {
		insert_value_push(argp);
		if (current.code_left < 2)
		    realloc_code();
		current.code_left -= 2;
		*current.codep++ = F_FUNCALL - F_OFFSET;
		*current.codep++ = 1;
	    } else {
		if (current.code_left < 2)
		    realloc_code();
		current.code_left -= 2;
		if ((short)l->function.index < 0)
		    lambda_error("Variable not inherited\n");
		*current.codep++ = F_IDENTIFIER - F_OFFSET;
		*current.codep++ = l->function.index;
	    }
	    break;
	  }
	} /* end of switch on closure_type */
	break;
      } /* end of case T_POINTER (block compiling code) */
      case T_QUOTED_ARRAY:
	insert_value_push(value);
	if (!--current.valuep->x.quotes)
	    current.valuep->type = T_POINTER;
	break;
      case T_SYMBOL:
	if (value->x.quotes > 1) {
	    insert_value_push(value);
	    --current.valuep->x.quotes;
	} else {
	    struct symbol *sym;

	    sym = make_symbol(value->u.string);
	    if (sym->index < 0)
		lambda_error("Symbol '%s' not bound\n", sym->name);
	    if (current.code_left < 2)
		realloc_code();
	    *current.codep++ = F_LOCAL - F_OFFSET;
	    *current.codep++ = sym->index;
	    current.code_left -= 2;
	}
	break;
      case T_NUMBER:
      {
	mp_int i;

	if ( (i = value->u.number) >= 0) {
	    if (i < 0x100) {
		if (current.code_left < 2)
		    realloc_code();
		if (!i) {
		    if (opt_flags & ZERO_ACCEPTED) {
			opt_flags = VOID_GIVEN;
			break;
		    }
		    *current.codep++ = F_CONST0 - F_OFFSET;
		    current.code_left--;
		    break;
		} else if (i == 1) {
		    *current.codep++ = F_CONST1 - F_OFFSET;
		    current.code_left--;
		    break;
		}
		*current.codep++ = F_CLIT - F_OFFSET;
		*current.codep++ = i;
		current.code_left -= 2;
		break;
	    }
	} else if (i > -0x100) {
	    if (current.code_left < 2)
		realloc_code();
	    *current.codep++ = F_NCLIT - F_OFFSET;
	    *current.codep++ = -i;
	    current.code_left -= 2;
	    break;
	}
	/* else fall through */
      }
      default:
	insert_value_push(value);
	break;
    }
    current.levels_left++;
    return opt_flags;
}

int is_lvalue(argp, index_lvalue)
    struct svalue *argp;
    int index_lvalue;
{
    switch(argp->type) {
      case T_SYMBOL:
	return argp->x.quotes == 1;
      case T_POINTER:
	if (index_lvalue) {
	    struct vector *block;

	    block = argp->u.vec;
	    if (block->size != 3)
		break;
	    argp = block->item;
	    if (argp->type != T_CLOSURE)
	    {
		break;
	    }
	    switch (argp->x.closure_type) {
	      case F_INDEX -F_OFFSET+CLOSURE_EFUN:
	      case F_RINDEX-F_OFFSET+CLOSURE_EFUN:
		return 1;
	    }
	}
    }
    return 0;
}

void compile_lvalue(argp, flags)
    struct svalue *argp;
    int flags;
{
    switch(argp->type) {
      case T_SYMBOL:
      {
	struct symbol *sym;

	if (argp->x.quotes > 1)
	    break;
	sym = make_symbol(argp->u.string);
	if (sym->index < 0)
	    sym->index = current.num_locals++;
	if (current.code_left < 3)
	    realloc_code();
	current.code_left -= 3;
	*current.codep++ = F_PUSH_LOCAL_VARIABLE_LVALUE - F_OFFSET;
	*current.codep++ = sym->index;
	return;
      }
      case T_POINTER:
      {
	struct vector *block;

	block = argp->u.vec;
	if (block->size && (argp = block->item)->type == T_CLOSURE)
	{
	    switch (argp->x.closure_type) {
	      case F_INDEX -F_OFFSET+CLOSURE_EFUN:
	      case F_RINDEX-F_OFFSET+CLOSURE_EFUN:
		if (block->size != 3)
		    break;
		if (is_lvalue(argp+1, flags & USE_INDEX_LVALUE)) {
		    compile_value(argp+2, 0);
		    compile_lvalue(argp+1, flags & PROTECT_LVALUE);
		    if (current.code_left < 2)
			realloc_code();
		    if (flags & PROTECT_LVALUE) {
			current.code_left -= 2;
			*current.codep++ = F_ESCAPE - F_OFFSET;
			*current.codep++ =
			  argp->u.number == F_RINDEX-F_OFFSET ?
			    F_PROTECTED_RINDEX_LVALUE - F_OFFSET - 0x100 :
			    F_PROTECTED_INDEX_LVALUE  - F_OFFSET - 0x100;
		    } else {
			current.code_left--;
			*current.codep++ =
			  argp->u.number == F_RINDEX-F_OFFSET ?
			    F_RINDEX_LVALUE - F_OFFSET :
			    F_INDEX_LVALUE - F_OFFSET;
		    }
		    return;
		}
		compile_value(argp+1, 0);
		compile_value(argp+2, 0);
		if (current.code_left < 3)
		    realloc_code();
		if (flags & PROTECT_LVALUE) {
		    current.code_left -= 3;
		    *current.codep++ = F_ESCAPE - F_OFFSET;
		    *current.codep++ =
		      argp->u.number == F_RINDEX-F_OFFSET ?
			F_PUSH_PROTECTED_RINDEXED_LVALUE - F_OFFSET - 0x100 :
			F_PUSH_PROTECTED_INDEXED_LVALUE  - F_OFFSET - 0x100;
		} else {
		    current.code_left -= 2;
		    *current.codep++ =
		      argp->u.number == F_RINDEX-F_OFFSET ?
			F_PUSH_RINDEXED_LVALUE - F_OFFSET :
			F_PUSH_INDEXED_LVALUE - F_OFFSET;
		}
		return;
	      case F_RANGE -F_OFFSET+CLOSURE_EFUN:
		compile_lvalue(++argp, flags & PROTECT_LVALUE);
		compile_value(++argp, 0);
		compile_value(++argp, 0);
		if (current.code_left < 2)
		    realloc_code();
		if (flags & PROTECT_LVALUE) {
		    current.code_left -= 2;
		    *current.codep++ = F_ESCAPE - F_OFFSET;
		    *current.codep++ =
		      F_PROTECTED_RANGE_LVALUE - F_OFFSET - 0x100;
		} else {
		    current.code_left--;
		    *current.codep++ = F_RANGE_LVALUE - F_OFFSET;
		}
		return;
	      case F_MAP_INDEX -F_OFFSET+CLOSURE_EFUN:
		compile_value(++argp, 0);
		compile_value(++argp, 0);
		compile_value(++argp, 0);
		if (current.code_left < 2)
		    realloc_code();
		if (flags & PROTECT_LVALUE) {
		    current.code_left -= 2;
		    *current.codep++ = F_ESCAPE - F_OFFSET;
		    *current.codep++ =
		      F_PUSH_PROTECTED_INDEXED_MAP_LVALUE - F_OFFSET - 0x100;
		} else {
		    current.code_left--;
		    *current.codep++ = F_PUSH_INDEXED_MAP_LVALUE - F_OFFSET;
		}
		return;
	      case CLOSURE_IDENTIFIER:
	      {
		struct lambda *l;

		if (block->size != 1)
		    break;
		l = argp->u.lambda;
		if (l->ob != current.lambda_origin)
		    break;
		if (current.code_left < 3)
		    realloc_code();
		current.code_left -= 3;
		if ((short)l->function.index < 0)
		    lambda_error("Variable not inherited\n");
		*current.codep++ = F_PUSH_IDENTIFIER_LVALUE - F_OFFSET;
		*current.codep++ = l->function.index;
		return;
	      }
	    }
	}
	break;
      }
      case T_CLOSURE:
      {
	switch (argp->x.closure_type) {
	  case CLOSURE_IDENTIFIER:
	  {
	    struct lambda *l;

	    l = argp->u.lambda;
	    if (l->ob != current.lambda_origin)
		break;
	    if (current.code_left < 3)
		realloc_code();
	    current.code_left -= 3;
	    if ((short)l->function.index < 0)
		lambda_error("Variable not inherited\n");
	    *current.codep++ = F_PUSH_IDENTIFIER_LVALUE - F_OFFSET;
	    *current.codep++ = l->function.index;
	    return;
	  }
	}
	break;
      }
    }
    lambda_error("Illegal lvalue\n");
}

struct lambda *lambda(args, block, origin)
    struct vector *args;
    struct svalue *block;
    struct object *origin;
{
    mp_int i, j;
    struct svalue *argp;
    mp_int num_values, values_size, code_size;
    char *l0;
    struct lambda *l;
    int void_given;

    current.symbols_left = current.symbol_max =
	sizeof current.symbols[0] * SYMTAB_START_SIZE;
    current.symbol_mask = current.symbol_max- sizeof(struct symbol *);
    current.last = 0;
    current.code = 0;
    current.symbols = (struct symbol **)xalloc(current.symbol_max);
    i = SYMTAB_START_SIZE - 1;
    do {
	current.symbols[i] = 0;
    } while (--i >= 0);
    argp = args->item;
    j = args->size;
    for (i = 0; i < j; i++, argp++) {
	struct symbol *sym;

	if (argp->type != T_SYMBOL) {
	    lambda_error("Illegal argument type to lambda()\n");
	}
	sym = make_symbol(argp->u.string);
	if (sym->index >= 0)
	    lambda_error("Double symbol name in lambda arguments\n");
	sym->index = i;
    }
    current.num_locals = i;
    current.code_max = CODE_BUFFER_START_SIZE;
    current.code_left = CODE_BUFFER_START_SIZE-3;
    current.levels_left = MAX_LAMBDA_LEVELS;
    current.code = current.codep = xalloc(current.code_max);
    *current.codep++ = 0;		   /* dummy for num values */
    *current.codep++ = current.num_locals; /* num arguments */
    *current.codep++ = 0;		   /* dummy for num variables */
    current.value_max = current.values_left = VALUE_START_MAX;
    current.values = xalloc(current.value_max * sizeof current.values[0]);
    current.valuep = current.values + current.value_max;
    current.lambda_origin = origin;

    void_given = compile_value(block, ZERO_ACCEPTED);

    if (current.code_left < 1)
	realloc_code();
    current.code_left -= 1;
    *current.codep++ =
      void_given & VOID_GIVEN ? F_RETURN0 - F_OFFSET : F_RETURN - F_OFFSET;
    num_values = current.value_max - current.values_left;
    values_size = num_values * sizeof (struct svalue);
    code_size = current.code_max - current.code_left;
    l0 = xalloc(values_size + sizeof *l - sizeof l->function + code_size);
    memcpy(l0, (char *)current.valuep, values_size);
    l0 += values_size;
    l = (struct lambda *)l0;
    l->ref = 1;
    memcpy(l->function.code, current.code, code_size);
    /* fix number of constant values */
    if (num_values > 0xff) {
	((struct svalue *)l->function.code)[-0xff].u.number = num_values;
	l->function.code[0] = 0xff;
    } else {
	l->function.code[0] = num_values;
    }
    /* fix number of variables */
    l->function.code[2] = current.num_locals;
    free_symbols();
    xfree(current.code);
    xfree(current.values);
    if (origin && ( !(origin->prog->flags & P_REPLACE_ACTIVE) ||
	 !lambda_ref_replace_program(l, CLOSURE_LAMBDA, code_size, args, block)
    ) )
    {
	origin->flags |= O_LAMBDA_REFERENCED;
    }
    return l;
}

static void insert_value_push(value)
    struct svalue *value;
{
    mp_int offset;

    if (current.code_left < 3)
	realloc_code();
    offset = current.value_max - current.values_left;
    if (offset < 0xff) {
	current.code_left -= 2;
	*current.codep++ = F_LAMBDA_CCONSTANT - F_OFFSET;
	*current.codep++ = offset;
    } else {
	if (offset == 0xff) {
	    current.values_left--;
	    offset++;
	    (--current.valuep)->type = T_INVALID;
	}
	current.code_left -= 3;
	*current.codep++ = F_LAMBDA_CONSTANT - F_OFFSET;
	*current.codep++ = offset >> 8;
	*current.codep++ = offset;
    }
    if (!--current.values_left)
	realloc_values();
    assign_svalue_no_free(--current.valuep, value);
}

void free_closure(svp)
    struct svalue *svp;
{
    struct lambda *l;
    int type;

    if (!CLOSURE_MALLOCED(type = svp->x.closure_type)) {
	free_object(svp->u.ob, "free_closure");
	return;
    }
    l = svp->u.lambda;
    if (--l->ref) return;
    if (CLOSURE_HAS_CODE(type)) {
	mp_int num_values;

	if (type != CLOSURE_UNBOUND_LAMBDA)
	    free_object(l->ob, "free_closure");
	svp = (struct svalue *)l;
	if ( (num_values = EXTRACT_UCHAR(l->function.code)) == 0xff)
	    num_values = svp[-0xff].u.number;
	while (--num_values >= 0)
	    free_svalue(--svp);
	xfree((char *)svp);
	return;
    }
    free_object(l->ob, "free_closure");
    if (type == CLOSURE_BOUND_LAMBDA) {
	mp_int num_values;
	struct lambda *l2;

	l2 = l->function.lambda;
	xfree((char *)l);
	if (--l2->ref) return;
	svp = (struct svalue *)l2;
	if ( (num_values = EXTRACT_UCHAR(l2->function.code)) == 0xff)
	    num_values = svp[-0xff].u.number;
	while (--num_values >= 0)
	    free_svalue(--svp);
	xfree((char *)svp);
	return;
    }
    /* CLOSURE_LFUN || CLOSURE_IDENTIFIER || CLOSURE_PRELIMINARY */
    xfree((char *)l);
}

int symbol_operator(symbol, endp)
char *symbol, **endp;
{
    char c;
    int ret;

    switch(*symbol) {
      case '+':
	if (symbol[1] == '=') {
	    symbol++;
	    ret = F_ADD_EQ-F_OFFSET;
	    break;
	}
	ret = F_ADD-F_OFFSET;
	break;
      case '-':
	if (symbol[1] == '=') {
	    symbol++;
	    ret = F_SUB_EQ-F_OFFSET;
	    break;
	}
	ret = F_SUBTRACT-F_OFFSET;
	break;
      case '*':
	if (symbol[1] == '=') {
	    symbol++;
	    ret = F_MULT_EQ-F_OFFSET;
	    break;
	}
	ret = F_MULTIPLY-F_OFFSET;
	break;
      case '/':
	if (symbol[1] == '=') {
	    symbol++;
	    ret = F_DIV_EQ-F_OFFSET;
	    break;
	}
	ret = F_DIVIDE-F_OFFSET;
	break;
      case '%':
	if (symbol[1] == '=') {
	    symbol++;
	    ret = F_MOD_EQ-F_OFFSET;
	    break;
	}
	ret = F_MOD-F_OFFSET;
	break;
      case ',':
	ret = F_POP_VALUE-F_OFFSET;
	break;
      case '^':
	if (symbol[1] == '=') {
	    symbol++;
	    ret = F_XOR_EQ-F_OFFSET;
	    break;
	}
	ret = F_XOR-F_OFFSET;
	break;
      case '|':
	c = *++symbol;
	if (c == '|') {
	    ret = F_LOR-F_OFFSET;
	    break;
	} else if (c == '=') {
	    ret = F_OR_EQ-F_OFFSET;
	    break;
	}
	symbol--;
	ret = F_OR-F_OFFSET;
	break;
      case '&':
	c = *++symbol;
	if (c == '&') {
	    ret = F_LAND-F_OFFSET;
	    break;
	} else if (c == '=') {
	    ret = F_AND_EQ-F_OFFSET;
	    break;
	}
	symbol--;
	ret = F_AND-F_OFFSET;
	break;
      case '<':
	c = *++symbol;
	if (c == '=') {
	    ret = F_LE-F_OFFSET;
	    break;
	} else if (c == '<') {
	    if (symbol[1] == '=') {
		symbol++;
		ret = F_LSH_EQ-F_OFFSET;
		break;
	    }
	    ret = F_LSH-F_OFFSET;
	    break;
	}
	symbol--;
	ret = F_LT-F_OFFSET;
	break;
      case '>':
	c = *++symbol;
	if (c == '=') {
	    ret = F_GE-F_OFFSET;
	    break;
	} else if (c == '>') {
	    if (symbol[1] == '=') {
		symbol++;
		ret = F_RSH_EQ-F_OFFSET;
		break;
	    }
	    ret = F_RSH-F_OFFSET;
	    break;
	}
	symbol--;
	ret = F_GT-F_OFFSET;
	break;
      case '=':
	if (symbol[1] == '=') {
	    symbol++;
	    ret = F_EQ-F_OFFSET;
	    break;
	}
	ret = F_ASSIGN-F_OFFSET;
	break;
      case '!':
	if (symbol[1] == '=') {
	    symbol++;
	    ret = F_NE-F_OFFSET;
	    break;
	}
	ret = F_NOT-F_OFFSET;
	break;
      case '?':
	if (symbol[1] == '!') {
	    symbol++;
	    ret = F_BRANCH_WHEN_NON_ZERO-F_OFFSET;
	    break;
	}
	ret = F_BRANCH_WHEN_ZERO-F_OFFSET;
	break;
      case '[':
	c = *++symbol;
	if (c == '<') {
	    if (symbol[1] == '.' && symbol[2] == '.') {
		c = *(symbol+=3);
		if (c == ']') {
		    ret = F_RN_RANGE-F_OFFSET;
		    break;
		} else if (c == '<' && symbol[1] == ']') {
		    symbol++;
		    ret = F_RR_RANGE-F_OFFSET;
		    break;
		}
		symbol--;
		/* We will have to compile F_NEGATE , F_EXTRACT2 */
		ret = F_EXTRACT_LVALUE-F_OFFSET;
		break;
	    }
	    ret = F_RINDEX-F_OFFSET;
	    break;
	} else if (c == '.' && symbol[1] == '.') {
	    c = *(symbol+=2);
	    if (c == ']') {
		ret = F_RANGE-F_OFFSET;
		break;
	    } else if (c == '<' && symbol[1] == ']') {
		symbol++;
		ret = F_NR_RANGE-F_OFFSET;
		break;
	    }
	    symbol--;
	    ret = F_EXTRACT2-F_OFFSET;
	    break;
	} else if (c == ',' && symbol[1] == ']') {
	    symbol++;
	    ret = F_MAP_INDEX - F_OFFSET;
	    break;
	}
	symbol--;
	ret = F_INDEX-F_OFFSET;
	break;
      case '(':
	c = *++symbol;
	if (c == '{') {
	    ret = F_AGGREGATE-F_OFFSET;
	    break;
	} else if (c == '[') {
	    ret = F_M_CAGGREGATE-F_OFFSET;
	    break;
	}
	symbol--;
      /* fall through */
      default:
	ret = -1;
	symbol--;
    }
    *endp = symbol+1;
    return ret;
}

void symbol_efun(sp)
    struct svalue *sp;
{
    extern struct svalue *inter_sp;

    int efun_override = 0;
    char *str;

    str = sp->u.string;
    if (isalunum(*str)) {
	extern struct function *simul_efunp;

	struct ident *p;

	if ( !strncmp(str, "efun::", 6) ) {
	    str += 6;
	    efun_override = 1;
	}
	p = make_shared_identifier(str, I_TYPE_GLOBAL);
	while (p->type > I_TYPE_GLOBAL) {
	    if (p->type == I_TYPE_RESWORD) {
		int code;
	
		switch(code = p->u.code) {
		  default:
		    p = p->inferior;
		    continue;
		  case F_IF:
		    code = F_BRANCH_WHEN_ZERO-F_OFFSET;
		    break;
		  case F_DO:
		    code = F_BBRANCH_WHEN_NON_ZERO-F_OFFSET;
		    break;
		  case F_WHILE:
		    /* the politically correct code   /
		    /  was already taken, see above. */
		    code = F_BBRANCH_WHEN_ZERO-F_OFFSET;
		    break;
		  case F_CONTINUE:
		    code = F_BRANCH-F_OFFSET;
		  case F_BREAK:
		  case F_RETURN:
		  case F_SSCANF:
		    code -= F_OFFSET;
		    break;
		}
		free_string_svalue(sp);
		sp->type = T_CLOSURE;
		sp->x.closure_type = code + CLOSURE_OPERATOR;
		add_ref(sp->u.ob = current_object, "symbol_efun");
	        return;
	    }
	    p = p->inferior;
	}
	if (!p || p->type < I_TYPE_GLOBAL ||
	    ( efun_override || p->u.global.sim_efun < 0 ) &&
	      p->u.global.efun < 0 )
	{
	    if (p && p->type == I_TYPE_UNKNOWN)
		free_shared_identifier(p);
	    inter_sp = sp;
	    error("Undefined function: %s\n", str);
	}
	if (efun_override && p->u.global.sim_efun >= 0 &&
	      simul_efunp[p->u.global.sim_efun].type & TYPE_MOD_NO_MASK)
	{
	    struct svalue *res;
	
	    push_constant_string("nomask simul_efun");
	    push_object(current_object);
	    push_shared_string(p->name);
	    res = apply_master_ob("privilege_violation", 3);
	    if (!res || res->type != T_NUMBER || res->u.number < 0)
	    {
		inter_sp = sp;
		error(
		  "Privilege violation: nomask simul_efun %s\n",
		  p->name
		);
	    } else if (!res->u.number) {
		efun_override = 0;
	    }
	}
	free_string_svalue(sp);
	sp->type = T_CLOSURE;
	if (!efun_override && p->u.global.sim_efun >= 0) {
	    sp->x.closure_type = p->u.global.sim_efun + CLOSURE_SIMUL_EFUN;
	    add_ref(sp->u.ob = current_object, "symbol_efun");
	    return;
	}
	/* p->u.global.efun >= 0 */
	sp->x.closure_type = p->u.global.efun + CLOSURE_EFUN;
	if (sp->x.closure_type > LAST_INSTRUCTION_CODE + CLOSURE_EFUN)
	    sp->u.number =
	      efun_aliases[
		sp->x.closure_type - CLOSURE_EFUN - LAST_INSTRUCTION_CODE - 1];
	add_ref(sp->u.ob = current_object, "symbol_efun");
    } else {
	int i;
	char *end;

	i = symbol_operator(str, &end);
	if (*end) {
	    inter_sp = sp;
	    error("Unknown operator\n");
	}
	free_string_svalue(sp);
	sp->type = T_CLOSURE;
	if (instrs[i].Default == -1) {
	    sp->x.closure_type = i + CLOSURE_OPERATOR;
	} else {
	    sp->x.closure_type = i + CLOSURE_EFUN;
	}
	add_ref(sp->u.ob = current_object, "symbol_efun");
    }
}

struct svalue *unbound_lambda(sp)
    struct svalue *sp;
{
    extern struct vector null_vector;
    extern struct svalue *inter_sp;

    struct lambda *l;
    struct vector *args;

    if (sp[-1].type != T_POINTER) {
	if (sp[-1].type != T_NUMBER || sp[-1].u.number)
	    bad_efun_arg(1, F_UNBOUND_LAMBDA-F_OFFSET,sp);
	(args = &null_vector)->ref++;
    } else {
	args = sp[-1].u.vec;
    }
    inter_sp = sp;
    l = lambda(args, sp, 0);
    l->ob = 0;
    free_svalue(sp--);
    free_vector(args);
    sp->type = T_CLOSURE;
    sp->x.closure_type = CLOSURE_UNBOUND_LAMBDA;
    sp->u.lambda = l;
    return sp;
}