Initial revision
diff --git a/Python/ceval.c b/Python/ceval.c
new file mode 100644
index 0000000..6a303ca
--- /dev/null
+++ b/Python/ceval.c
@@ -0,0 +1,1560 @@
+/* Evaluate compiled expression nodes */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "string.h"
+
+#include "PROTO.h"
+#include "object.h"
+#include "objimpl.h"
+#include "intobject.h"
+#include "stringobject.h"
+#include "tupleobject.h"
+#include "listobject.h"
+#include "dictobject.h"
+#include "builtinobject.h"
+#include "methodobject.h"
+#include "moduleobject.h"
+#include "context.h"
+#include "funcobject.h"
+#include "classobject.h"
+#include "token.h"
+#include "graminit.h"
+#include "run.h"
+#include "import.h"
+#include "support.h"
+#include "sysmodule.h"
+#include "compile.h"
+#include "opcode.h"
+
+/* List access macros */
+#ifdef NDEBUG
+#define GETITEM(v, i) GETLISTITEM((listobject *)(v), (i))
+#define GETITEMNAME(v, i) GETSTRINGVALUE((stringobject *)GETITEM((v), (i)))
+#else
+#define GETITEM(v, i) getlistitem((v), (i))
+#define GETITEMNAME(v, i) getstringvalue(getlistitem((v), (i)))
+#endif
+
+typedef struct {
+	int b_type;		/* what kind of block this is */
+	int b_handler;		/* where to jump to find handler */
+	int b_level;		/* value stack level to pop to */
+} block;
+
+typedef struct _frame {
+	OB_HEAD
+	struct _frame *f_back;	/* previous frame, or NULL */
+	codeobject *f_code;	/* code segment */
+	object *f_locals;	/* local symbol table (dictobject) */
+	object *f_globals;	/* global symbol table (dictobject) */
+	object **f_valuestack;	/* malloc'ed array */
+	block *f_blockstack;	/* malloc'ed array */
+	int f_nvalues;		/* size of f_valuestack */
+	int f_nblocks;		/* size of f_blockstack */
+	int f_ivalue;		/* index in f_valuestack */
+	int f_iblock;		/* index in f_blockstack */
+	int f_nexti;		/* index in f_code (next instruction) */
+} frameobject;
+
+#define is_frameobject(op) ((op)->ob_type == &Frametype)
+
+static void
+frame_dealloc(f)
+	frameobject *f;
+{
+	XDECREF(f->f_back);
+	XDECREF(f->f_code);
+	XDECREF(f->f_locals);
+	XDECREF(f->f_globals);
+	XDEL(f->f_valuestack);
+	XDEL(f->f_blockstack);
+	DEL(f);
+}
+typeobject Frametype = {
+	OB_HEAD_INIT(&Typetype)
+	0,
+	"frame",
+	sizeof(frameobject),
+	0,
+	frame_dealloc,	/*tp_dealloc*/
+	0,		/*tp_print*/
+	0,		/*tp_getattr*/
+	0,		/*tp_setattr*/
+	0,		/*tp_compare*/
+	0,		/*tp_repr*/
+	0,		/*tp_as_number*/
+	0,		/*tp_as_sequence*/
+	0,		/*tp_as_mapping*/
+};
+
+static frameobject * newframeobject PROTO(
+	(frameobject *, codeobject *, object *, object *, int, int));
+
+static frameobject *
+newframeobject(back, code, locals, globals, nvalues, nblocks)
+	frameobject *back;
+	codeobject *code;
+	object *locals;
+	object *globals;
+	int nvalues;
+	int nblocks;
+{
+	frameobject *f;
+	if ((back != NULL && !is_frameobject(back)) ||
+		code == NULL || !is_codeobject(code) ||
+		locals == NULL || !is_dictobject(locals) ||
+		globals == NULL || !is_dictobject(globals) ||
+		nvalues < 0 || nblocks < 0) {
+		err_badcall();
+		return NULL;
+	}
+	f = NEWOBJ(frameobject, &Frametype);
+	if (f != NULL) {
+		if (back)
+			INCREF(back);
+		f->f_back = back;
+		INCREF(code);
+		f->f_code = code;
+		INCREF(locals);
+		f->f_locals = locals;
+		INCREF(globals);
+		f->f_globals = globals;
+		f->f_valuestack = NEW(object *, nvalues+1);
+		f->f_blockstack = NEW(block, nblocks+1);
+		f->f_nvalues = nvalues;
+		f->f_nblocks = nblocks;
+		f->f_ivalue = f->f_iblock = f->f_nexti = 0;
+		if (f->f_valuestack == NULL || f->f_blockstack == NULL) {
+			err_nomem();
+			DECREF(f);
+			f = NULL;
+		}
+	}
+	return f;
+}
+
+#define Push(f, v)	((f)->f_valuestack[(f)->f_ivalue++] = (v))
+#define Pop(f)		((f)->f_valuestack[--(f)->f_ivalue])
+#define Top(f)		((f)->f_valuestack[(f)->f_ivalue-1])
+#define Empty(f)	((f)->f_ivalue == 0)
+#define Full(f)		((f)->f_ivalue == (f)->f_nvalues)
+#define Nextbyte(f)	(GETSTRINGVALUE((f)->f_code->co_code)[(f)->f_nexti++])
+#define Peekbyte(f)	(GETSTRINGVALUE((f)->f_code->co_code)[(f)->f_nexti])
+#define Prevbyte(f)	(GETSTRINGVALUE((f)->f_code->co_code)[(f)->f_nexti-1])
+#define Jumpto(f, x)	((f)->f_nexti = (x))
+#define Jumpby(f, x)	((f)->f_nexti += (x))
+#define Getconst(f, i)	(GETITEM((f)->f_code->co_consts, (i)))
+#define Getname(f, i)	(GETITEMNAME((f)->f_code->co_names, (i)))
+
+/* Corresponding functions, for debugging */
+
+static void
+push(f, v)
+	frameobject *f;
+	object *v;
+{
+	if (Full(f)) {
+		printf("stack overflow\n");
+		abort();
+	}
+	Push(f, v);
+}
+
+static object *
+pop(f)
+	frameobject *f;
+{
+	if (Empty(f)) {
+		printf("stack underflow\n");
+		abort();
+	}
+	return Pop(f);
+}
+
+static object *
+top(f)
+	frameobject *f;
+{
+	if (Empty(f)) {
+		printf("stack underflow\n");
+		abort();
+	}
+	return Top(f);
+}
+
+static int
+nextbyte(f)
+	frameobject *f;
+{
+	stringobject *code = (f)->f_code->co_code;
+	if (f->f_nexti >= getstringsize((object *)code)) {
+		printf("ran off end of instructions\n");
+		abort();
+	}
+	return GETSTRINGVALUE(code)[f->f_nexti++];
+}
+
+/* Tracing versions */
+
+static void
+trace_push(f, v)
+	frameobject *f;
+	object *v;
+{
+	printf("\tpush ");
+	printobject(v, stdout, 0);
+	printf("\n");
+	push(f, v);
+}
+
+static object *
+trace_pop(f)
+	frameobject *f;
+{
+	object *v;
+	v = pop(f);
+	printf("\tpop ");
+	printobject(v, stdout, 0);
+	printf("\n");
+	return v;
+}
+
+static object *
+trace_top(f)
+	frameobject *f;
+{
+	object *v;
+	v = top(f);
+	printf("\ttop ");
+	printobject(v, stdout, 0);
+	printf("\n");
+	return v;
+}
+
+static int
+trace_nextop(f)
+	frameobject *f;
+{
+	int op;
+	int arg;
+	
+	printf("%d: ", f->f_nexti);
+	op = nextbyte(f);
+	if (op < HAVE_ARGUMENT)
+		printf("op %3d\n", op);
+	else {
+		arg = Peekbyte(f);
+		printf("op %3d arg %3d\n", op, arg);
+	}
+	return op;
+}
+
+/* Block management */
+
+static void
+setup_block(f, type, handler)
+	frameobject *f;
+	int type;
+	int handler;
+{
+	block *b;
+	if (f->f_iblock >= f->f_nblocks) {
+		printf("block stack overflow\n");
+		abort();
+	}
+	b = &f->f_blockstack[f->f_iblock++];
+	b->b_type = type;
+	b->b_level = f->f_ivalue;
+	b->b_handler = handler + f->f_nexti;
+}
+
+static block *
+pop_block(f)
+	frameobject *f;
+{
+	block *b;
+	if (f->f_iblock <= 0) {
+		printf("block stack underflow\n");
+		abort();
+	}
+	b = &f->f_blockstack[--f->f_iblock];
+	while (f->f_ivalue > b->b_level) {
+		object *v = Pop(f);
+		XDECREF(v);
+	}
+	return b;
+}
+
+
+/* XXX Mixing "print ...," and direct file I/O on stdin/stdout
+   XXX has some bad consequences.  The needspace flag should
+   XXX really be part of the file object. */
+
+static int needspace;
+
+void
+flushline()
+{
+	FILE *fp = sysgetfile("stdout", stdout);
+	if (needspace) {
+		fprintf(fp, "\n");
+		needspace = 0;
+	}
+}
+
+static object *
+checkerror(ctx, v)
+	context *ctx;
+	object *v;
+{
+	if (v == NULL)
+		puterrno(ctx);
+	return v;
+}
+
+static object *
+add(ctx, v, w)
+	context *ctx;
+	object *v, *w;
+{
+	if (v->ob_type->tp_as_number != NULL)
+		v = (*v->ob_type->tp_as_number->nb_add)(v, w);
+	else if (v->ob_type->tp_as_sequence != NULL)
+		v = (*v->ob_type->tp_as_sequence->sq_concat)(v, w);
+	else {
+		type_error(ctx, "+ not supported by operands");
+		return NULL;
+	}
+	return checkerror(ctx, v);
+}
+
+static object *
+sub(ctx, v, w)
+	context *ctx;
+	object *v, *w;
+{
+	if (v->ob_type->tp_as_number != NULL)
+		return checkerror(ctx,
+			(*v->ob_type->tp_as_number->nb_subtract)(v, w));
+	type_error(ctx, "bad operand type(s) for -");
+	return NULL;
+}
+
+static object *
+mul(ctx, v, w)
+	context *ctx;
+	object *v, *w;
+{
+	typeobject *tp;
+	if (is_intobject(v) && w->ob_type->tp_as_sequence != NULL) {
+		/* int*sequence -- swap v and w */
+		object *tmp = v;
+		v = w;
+		w = tmp;
+	}
+	tp = v->ob_type;
+	if (tp->tp_as_number != NULL)
+		return checkerror(ctx, (*tp->tp_as_number->nb_multiply)(v, w));
+	if (tp->tp_as_sequence != NULL) {
+		if (!is_intobject(w)) {
+			type_error(ctx, "can't multiply sequence with non-int");
+			return NULL;
+		}
+		if (tp->tp_as_sequence->sq_repeat == NULL) {
+			type_error(ctx, "sequence does not support *");
+			return NULL;
+		}
+		return checkerror(ctx, (*tp->tp_as_sequence->sq_repeat)
+						(v, (int)getintvalue(w)));
+	}
+	type_error(ctx, "bad operand type(s) for *");
+	return NULL;
+}
+
+static object *
+div(ctx, v, w)
+	context *ctx;
+	object *v, *w;
+{
+	if (v->ob_type->tp_as_number != NULL)
+		return checkerror(ctx,
+			(*v->ob_type->tp_as_number->nb_divide)(v, w));
+	type_error(ctx, "bad operand type(s) for /");
+	return NULL;
+}
+
+static object *
+rem(ctx, v, w)
+	context *ctx;
+	object *v, *w;
+{
+	if (v->ob_type->tp_as_number != NULL)
+		return checkerror(ctx,
+			(*v->ob_type->tp_as_number->nb_remainder)(v, w));
+	type_error(ctx, "bad operand type(s) for %");
+	return NULL;
+}
+
+static object *
+neg(ctx, v)
+	context *ctx;
+	object *v;
+{
+	if (v->ob_type->tp_as_number != NULL)
+		return checkerror(ctx,
+			(*v->ob_type->tp_as_number->nb_negative)(v));
+	type_error(ctx, "bad operand type(s) for unary -");
+	return NULL;
+}
+
+static object *
+pos(ctx, v)
+	context *ctx;
+	object *v;
+{
+	if (v->ob_type->tp_as_number != NULL)
+		return checkerror(ctx,
+			(*v->ob_type->tp_as_number->nb_positive)(v));
+	type_error(ctx, "bad operand type(s) for unary +");
+	return NULL;
+}
+
+static object *
+not(ctx, v)
+	context *ctx;
+	object *v;
+{
+	int outcome = testbool(ctx, v);
+	if (ctx->ctx_exception)
+		return NULL;
+	return checkerror(ctx, newintobject((long) !outcome));
+}
+
+static object *
+call_builtin(ctx, func, args)
+	context *ctx;
+	object *func;
+	object *args;
+{
+	if (is_builtinobject(func)) {
+		function funcptr = getbuiltinfunction(func);
+		return (*funcptr)(ctx, args);
+	}
+	if (is_methodobject(func)) {
+		method meth = getmethod(func);
+		object *self = getself(func);
+		return checkerror(ctx, (*meth)(self, args));
+	}
+	if (is_classobject(func)) {
+		if (args != NULL) {
+			type_error(ctx, "classobject() allows no arguments");
+			return NULL;
+		}
+		return checkerror(ctx, newclassmemberobject(func));
+	}
+	type_error(ctx, "call of non-function");
+	return NULL;
+}
+
+static object *eval_compiled PROTO((context *, codeobject *, object *, int));
+
+/* XXX Eventually, this should not call eval_compiled recursively
+   but create a new frame */
+
+static object *
+call_function(ctx, func, args)
+	context *ctx;
+	object *func;
+	object *args;
+{
+	object *newargs = NULL;
+	object *savelocals, *newlocals, *saveglobals;
+	object *c, *v;
+	
+	if (is_classmethodobject(func)) {
+		object *self = classmethodgetself(func);
+		func = classmethodgetfunc(func);
+		if (args == NULL) {
+			args = self;
+		}
+		else {
+			newargs = checkerror(ctx, newtupleobject(2));
+			if (newargs == NULL)
+				return NULL;
+			INCREF(self);
+			INCREF(args);
+			settupleitem(newargs, 0, self);
+			settupleitem(newargs, 1, args);
+			args = newargs;
+		}
+	}
+	else {
+		if (!is_funcobject(func)) {
+			type_error(ctx, "call of non-function");
+			return NULL;
+		}
+	}
+	
+	c = checkerror(ctx, getfunccode(func));
+	if (c == NULL) {
+		XDECREF(newargs);
+		return NULL;
+	}
+	if (!is_codeobject(c)) {
+		printf("Bad code\n");
+		abort();
+	}
+	newlocals = checkerror(ctx, newdictobject());
+	if (newlocals == NULL) {
+		XDECREF(newargs);
+		return NULL;
+	}
+	
+	savelocals = ctx->ctx_locals;
+	ctx->ctx_locals = newlocals;
+	saveglobals = ctx->ctx_globals;
+	ctx->ctx_globals = getfuncglobals(func);
+	
+	v = eval_compiled(ctx, (codeobject *)c, args, 1);
+	
+	DECREF(ctx->ctx_locals);
+	ctx->ctx_locals = savelocals;
+	ctx->ctx_globals = saveglobals;
+	
+	XDECREF(newargs);
+	
+	return v;
+}
+
+static object *
+apply_subscript(ctx, v, w)
+	context *ctx;
+	object *v, *w;
+{
+	typeobject *tp = v->ob_type;
+	if (tp->tp_as_sequence == NULL && tp->tp_as_mapping == NULL) {
+		type_error(ctx, "unsubscriptable object");
+		return NULL;
+	}
+	if (tp->tp_as_sequence != NULL) {
+		int i;
+		if (!is_intobject(w)) {
+			type_error(ctx, "sequence subscript not int");
+			return NULL;
+		}
+		i = getintvalue(w);
+		return checkerror(ctx, (*tp->tp_as_sequence->sq_item)(v, i));
+	}
+	return checkerror(ctx, (*tp->tp_as_mapping->mp_subscript)(v, w));
+}
+
+static object *
+loop_subscript(ctx, v, w)
+	context *ctx;
+	object *v, *w;
+{
+	sequence_methods *sq = v->ob_type->tp_as_sequence;
+	int i, n;
+	if (sq == NULL) {
+		type_error(ctx, "loop over non-sequence");
+		return NULL;
+	}
+	i = getintvalue(w);
+	n = (*sq->sq_length)(v);
+	if (i >= n)
+		return NULL; /* End of loop */
+	return checkerror(ctx, (*sq->sq_item)(v, i));
+}
+
+static int
+slice_index(ctx, v, isize, pi)
+	context *ctx;
+	object *v;
+	int isize;
+	int *pi;
+{
+	if (v != NULL) {
+		if (!is_intobject(v)) {
+			type_error(ctx, "slice index must be int");
+			return 0;
+		}
+		*pi = getintvalue(v);
+		if (*pi < 0)
+			*pi += isize;
+	}
+	return 1;
+}
+
+static object *
+apply_slice(ctx, u, v, w) /* u[v:w] */
+	context *ctx;
+	object *u, *v, *w;
+{
+	typeobject *tp = u->ob_type;
+	int ilow, ihigh, isize;
+	if (tp->tp_as_sequence == NULL) {
+		type_error(ctx, "only sequences can be sliced");
+		return NULL;
+	}
+	ilow = 0;
+	isize = ihigh = (*tp->tp_as_sequence->sq_length)(u);
+	if (!slice_index(ctx, v, isize, &ilow))
+		return NULL;
+	if (!slice_index(ctx, w, isize, &ihigh))
+		return NULL;
+	return checkerror(ctx, (*tp->tp_as_sequence->sq_slice)(u, ilow, ihigh));
+}
+static void
+assign_subscript(ctx, w, key, v)
+	context *ctx;
+	object *w;
+	object *key;
+	object *v;
+{
+	typeobject *tp = w->ob_type;
+	sequence_methods *sq;
+	mapping_methods *mp;
+	int (*func)();
+	int err;
+	if ((sq = tp->tp_as_sequence) != NULL &&
+			(func = sq->sq_ass_item) != NULL) {
+		if (!is_intobject(key)) {
+			type_error(ctx, "sequence subscript must be integer");
+			return;
+		}
+		err = (*func)(w, (int)getintvalue(key), v);
+	}
+	else if ((mp = tp->tp_as_mapping) != NULL &&
+			(func = mp->mp_ass_subscript) != NULL) {
+		err = (*func)(w, key, v);
+	}
+	else {
+		type_error(ctx, "can't assign to this subscripted object");
+		return;
+	}
+	if (err != 0)
+		puterrno(ctx);
+}
+
+static void
+assign_slice(ctx, u, v, w, x) /* u[v:w] = x */
+	context *ctx;
+	object *u, *v, *w, *x;
+{
+	typeobject *tp = u->ob_type;
+	int ilow, ihigh, isize;
+	if (tp->tp_as_sequence == NULL ||
+			tp->tp_as_sequence->sq_ass_slice == NULL) {
+		type_error(ctx, "unassignable slice");
+		return;
+	}
+	ilow = 0;
+	isize = ihigh = (*tp->tp_as_sequence->sq_length)(u);
+	if (!slice_index(ctx, v, isize, &ilow))
+		return;
+	if (!slice_index(ctx, w, isize, &ihigh))
+		return;
+	if ((*tp->tp_as_sequence->sq_ass_slice)(u, ilow, ihigh, x) != 0)
+		puterrno(ctx);
+}
+
+static int
+cmp_exception(err, v)
+	object *err, *v;
+{
+	if (is_tupleobject(v)) {
+		int i, n;
+		n = gettuplesize(v);
+		for (i = 0; i < n; i++) {
+			if (err == gettupleitem(v, i))
+				return 1;
+		}
+		return 0;
+	}
+	return err == v;
+}
+
+static object *
+cmp_outcome(ctx, op, v, w)
+	register context *ctx;
+	enum cmp_op op;
+	register object *v;
+	register object *w;
+{
+	register int cmp;
+	register int res = 0;
+	switch (op) {
+	case IN:
+	case NOT_IN:
+		cmp = cmp_member(ctx, v, w);
+		break;
+	case IS:
+	case IS_NOT:
+		cmp = (v == w);
+		break;
+	case EXC_MATCH:
+		cmp = cmp_exception(v, w);
+		break;
+	default:
+		cmp = cmp_values(ctx, v, w);
+	}
+	if (ctx->ctx_exception)
+		return NULL;
+	switch (op) {
+	case EXC_MATCH:
+	case IS:
+	case IN:     res =  cmp; break;
+	case IS_NOT:
+	case NOT_IN: res = !cmp; break;
+	case LT: res = cmp <  0; break;
+	case LE: res = cmp <= 0; break;
+	case EQ: res = cmp == 0; break;
+	case NE: res = cmp != 0; break;
+	case GT: res = cmp >  0; break;
+	case GE: res = cmp >= 0; break;
+	/* XXX no default? */
+	}
+	v = res ? True : False;
+	INCREF(v);
+	return v;
+}
+
+static object *
+eval_compiled(ctx, co, arg, needvalue)
+	context *ctx;
+	codeobject *co;
+	object *arg;
+	int needvalue;
+{
+	frameobject *f;
+	register object *v;
+	register object *w;
+	register object *u;
+	register object *x;
+	char *name;
+	int n, i;
+	enum cmp_op op;
+	FILE *fp;
+#ifndef NDEBUG
+	int trace = dictlookup(ctx->ctx_globals, "__trace") != NULL;
+#endif
+
+	f = newframeobject(
+			(frameobject *)NULL,	/*back*/
+			co,			/*code*/
+			ctx->ctx_locals,	/*locals*/
+			ctx->ctx_globals,	/*globals*/
+			50,			/*nvalues*/
+			20);			/*nblocks*/
+	if (f == NULL) {
+		puterrno(ctx);
+		return NULL;
+	}
+
+#define EMPTY()		Empty(f)
+#define FULL()		Full(f)
+#define GETCONST(i)	Getconst(f, i)
+#define GETNAME(i)	Getname(f, i)
+#define JUMPTO(x)	Jumpto(f, x)
+#define JUMPBY(x)	Jumpby(f, x)
+
+#ifdef NDEBUG
+
+#define PUSH(v) 	Push(f, v)
+#define TOP()		Top(f)
+#define POP()		Pop(f)
+#define NEXTOP()	Nextbyte(f)
+#define NEXTI()		Nextbyte(f)
+
+#else
+
+#define PUSH(v) if(trace) trace_push(f, v); else push(f, v)
+#define TOP() (trace ? trace_top(f) : top(f))
+#define POP() (trace ? trace_pop(f) : pop(f))
+#define NEXTOP() (trace ? trace_nextop(f) : nextbyte(f))
+#define NEXTI()  (nextbyte(f))
+
+#endif
+
+	if (arg != NULL) {
+		INCREF(arg);
+		PUSH(arg);
+	}
+	
+	while (f->f_nexti < getstringsize((object *)f->f_code->co_code) &&
+				!ctx->ctx_exception) {
+		
+		switch (NEXTOP()) {
+		
+		case DUP_TOP:
+			v = TOP();
+			INCREF(v);
+			PUSH(v);
+			break;
+		
+		case POP_TOP:
+			v = POP();
+			DECREF(v);
+			break;
+		
+		case ROT_TWO:
+			v = POP();
+			w = POP();
+			PUSH(v);
+			PUSH(w);
+			break;
+		
+		case ROT_THREE:
+			v = POP();
+			w = POP();
+			x = POP();
+			PUSH(v);
+			PUSH(x);
+			PUSH(w);
+			break;
+		
+		case UNARY_POSITIVE:
+			v = POP();
+			u = pos(ctx, v);
+			DECREF(v);
+			PUSH(u);
+			break;
+		
+		case UNARY_NEGATIVE:
+			v = POP();
+			u = neg(ctx, v);
+			DECREF(v);
+			PUSH(u);
+			break;
+		
+		case UNARY_NOT:
+			v = POP();
+			u = not(ctx, v);
+			DECREF(v);
+			PUSH(u);
+			break;
+		
+		case UNARY_CONVERT:
+			v = POP();
+			u = checkerror(ctx, reprobject(v));
+			DECREF(v);
+			PUSH(u);
+			break;
+		
+		case UNARY_CALL:
+			v = POP();
+			if (is_classmemberobject(v) || is_funcobject(v))
+				u = call_function(ctx, v, (object *)NULL);
+			else
+				u = call_builtin(ctx, v, (object *)NULL);
+			DECREF(v);
+			PUSH(u);
+			break;
+		
+		case BINARY_MULTIPLY:
+			w = POP();
+			v = POP();
+			u = mul(ctx, v, w);
+			DECREF(v);
+			DECREF(w);
+			PUSH(u);
+			break;
+		
+		case BINARY_DIVIDE:
+			w = POP();
+			v = POP();
+			u = div(ctx, v, w);
+			DECREF(v);
+			DECREF(w);
+			PUSH(u);
+			break;
+		
+		case BINARY_MODULO:
+			w = POP();
+			v = POP();
+			u = rem(ctx, v, w);
+			DECREF(v);
+			DECREF(w);
+			PUSH(u);
+			break;
+		
+		case BINARY_ADD:
+			w = POP();
+			v = POP();
+			u = add(ctx, v, w);
+			DECREF(v);
+			DECREF(w);
+			PUSH(u);
+			break;
+		
+		case BINARY_SUBTRACT:
+			w = POP();
+			v = POP();
+			u = sub(ctx, v, w);
+			DECREF(v);
+			DECREF(w);
+			PUSH(u);
+			break;
+		
+		case BINARY_SUBSCR:
+			w = POP();
+			v = POP();
+			u = apply_subscript(ctx, v, w);
+			DECREF(v);
+			DECREF(w);
+			PUSH(u);
+			break;
+		
+		case BINARY_CALL:
+			w = POP();
+			v = POP();
+			if (is_classmemberobject(v) || is_funcobject(v))
+				u = call_function(ctx, v, w);
+			else
+				u = call_builtin(ctx, v, w);
+			DECREF(v);
+			DECREF(w);
+			PUSH(u);
+			break;
+		
+		case SLICE:
+			w = NULL;
+			v = NULL;
+			u = POP();
+			x = apply_slice(ctx, u, v, w);
+			DECREF(u);
+			PUSH(x);
+			break;
+		
+		case SLICE+1:
+			w = NULL;
+			v = POP();
+			u = POP();
+			x = apply_slice(ctx, u, v, w);
+			DECREF(u);
+			DECREF(v);
+			PUSH(x);
+			break;
+		
+		case SLICE+2:
+			w = POP();
+			v = NULL;
+			u = POP();
+			x = apply_slice(ctx, u, v, w);
+			DECREF(u);
+			DECREF(w);
+			PUSH(x);
+			break;
+		
+		case SLICE+3:
+			w = POP();
+			v = POP();
+			u = POP();
+			x = apply_slice(ctx, u, v, w);
+			DECREF(u);
+			DECREF(v);
+			DECREF(w);
+			PUSH(x);
+			break;
+		
+		case STORE_SLICE:
+			w = NULL;
+			v = NULL;
+			u = POP();
+			x = POP();
+			assign_slice(ctx, u, v, w, x); /* u[:] = x */
+			DECREF(x);
+			DECREF(u);
+			break;
+		
+		case STORE_SLICE+1:
+			w = NULL;
+			v = POP();
+			u = POP();
+			x = POP();
+			assign_slice(ctx, u, v, w, x); /* u[v:] = x */
+			DECREF(x);
+			DECREF(u);
+			DECREF(v);
+			break;
+		
+		case STORE_SLICE+2:
+			w = POP();
+			v = NULL;
+			u = POP();
+			x = POP();
+			assign_slice(ctx, u, v, w, x); /* u[:w] = x */
+			DECREF(x);
+			DECREF(u);
+			DECREF(w);
+			break;
+		
+		case STORE_SLICE+3:
+			w = POP();
+			v = POP();
+			u = POP();
+			x = POP();
+			assign_slice(ctx, u, v, w, x); /* u[v:w] = x */
+			DECREF(x);
+			DECREF(u);
+			DECREF(v);
+			DECREF(w);
+			break;
+		
+		case DELETE_SLICE:
+			w = NULL;
+			v = NULL;
+			u = POP();
+			x = NULL;
+			assign_slice(ctx, u, v, w, x); /* del u[:] */
+			DECREF(u);
+			break;
+		
+		case DELETE_SLICE+1:
+			w = NULL;
+			v = POP();
+			u = POP();
+			x = NULL;
+			assign_slice(ctx, u, v, w, x); /* del u[v:] */
+			DECREF(u);
+			DECREF(v);
+			break;
+		
+		case DELETE_SLICE+2:
+			w = POP();
+			v = NULL;
+			u = POP();
+			x = NULL;
+			assign_slice(ctx, u, v, w, x); /* del u[:w] */
+			DECREF(u);
+			DECREF(w);
+			break;
+		
+		case DELETE_SLICE+3:
+			w = POP();
+			v = POP();
+			u = POP();
+			x = NULL;
+			assign_slice(ctx, u, v, w, x); /* del u[v:w] */
+			DECREF(u);
+			DECREF(v);
+			DECREF(w);
+			break;
+		
+		case STORE_SUBSCR:
+			w = POP();
+			v = POP();
+			u = POP();
+			/* v[w] = u */
+			assign_subscript(ctx, v, w, u);
+			DECREF(u);
+			DECREF(v);
+			DECREF(w);
+			break;
+		
+		case DELETE_SUBSCR:
+			w = POP();
+			v = POP();
+			/* del v[w] */
+			assign_subscript(ctx, v, w, (object *)NULL);
+			DECREF(v);
+			DECREF(w);
+			break;
+		
+		case PRINT_EXPR:
+			v = POP();
+			fp = sysgetfile("stdout", stdout);
+			/* Print value except if procedure result */
+			if (v != None) {
+				flushline();
+				printobject(v, fp, 0);
+				fprintf(fp, "\n");
+			}
+			DECREF(v);
+			break;
+		
+		case PRINT_ITEM:
+			v = POP();
+			fp = sysgetfile("stdout", stdout);
+			if (needspace)
+				fprintf(fp, " ");
+			if (is_stringobject(v)) {
+				char *s = getstringvalue(v);
+				int len = getstringsize(v);
+				fwrite(s, 1, len, fp);
+				if (len > 0 && s[len-1] == '\n')
+					needspace = 0;
+				else
+					needspace = 1;
+			}
+			else {
+				printobject(v, fp, 0);
+				needspace = 1;
+			}
+			DECREF(v);
+			break;
+		
+		case PRINT_NEWLINE:
+			fp = sysgetfile("stdout", stdout);
+			fprintf(fp, "\n");
+			needspace = 0;
+			break;
+		
+		case BREAK_LOOP:
+			raise_pseudo(ctx, BREAK_PSEUDO);
+			break;
+		
+		case RAISE_EXCEPTION:
+			v = POP();
+			w = POP();
+			if (!is_stringobject(w)) {
+				DECREF(v);
+				DECREF(v);
+				type_error(ctx, "exceptions must be strings");
+			}
+			else {
+				raise_exception(ctx, w, v);
+			}
+			break;
+		
+		case RETURN_VALUE:
+			v = POP();
+			raise_pseudo(ctx, RETURN_PSEUDO);
+			ctx->ctx_errval = v;
+			break;
+		
+		case REQUIRE_ARGS:
+			if (EMPTY())
+				type_error(ctx,
+					"function expects argument(s)");
+			break;
+		
+		case REFUSE_ARGS:
+			if (!EMPTY())
+				type_error(ctx,
+					"function expects no argument(s)");
+			break;
+		
+		case BUILD_FUNCTION:
+			v = POP();
+			x = checkerror(ctx, newfuncobject(v, ctx->ctx_globals));
+			DECREF(v);
+			PUSH(x);
+			break;
+		
+		case POP_BLOCK:
+			(void) pop_block(f);
+			break;
+		
+		case END_FINALLY:
+			v = POP();
+			w = POP();
+			if (is_intobject(v)) {
+				raise_pseudo(ctx, (int)getintvalue(v));
+				DECREF(v);
+				if (w == None)
+					DECREF(w);
+				else
+					ctx->ctx_errval = w;
+			}
+			else if (is_stringobject(v))
+				raise_exception(ctx, v, w);
+			else if (v == None) {
+				DECREF(v);
+				DECREF(w);
+			}
+			else {
+				sys_error(ctx, "'finally' pops bad exception");
+			}
+			break;
+		
+		case STORE_NAME:
+			i = NEXTI();
+			name = GETNAME(i);
+			v = POP();
+			if (dictinsert(ctx->ctx_locals, name, v) != 0)
+				mem_error(ctx, "insert in symbol table");
+			DECREF(v);
+			break;
+		
+		case DELETE_NAME:
+			i = NEXTI();
+			name = GETNAME(i);
+			if (dictremove(ctx->ctx_locals, name) != 0)
+				name_error(ctx, name);
+			break;
+		
+		case UNPACK_TUPLE:
+			n = NEXTI();
+			v = POP();
+			if (!is_tupleobject(v)) {
+				type_error(ctx, "unpack non-tuple");
+			}
+			else if (gettuplesize(v) != n) {
+				error(ctx, "unpack tuple of wrong size");
+			}
+			else {
+				for (i = n; --i >= 0; ) {
+					w = gettupleitem(v, i);
+					INCREF(w);
+					PUSH(w);
+				}
+			}
+			DECREF(v);
+			break;
+		
+		case UNPACK_LIST:
+			n = NEXTI();
+			v = POP();
+			if (!is_listobject(v)) {
+				type_error(ctx, "unpack non-list");
+			}
+			else if (getlistsize(v) != n) {
+				error(ctx, "unpack tuple of wrong size");
+			}
+			else {
+				for (i = n; --i >= 0; ) {
+					w = getlistitem(v, i);
+					INCREF(w);
+					PUSH(w);
+				}
+			}
+			DECREF(v);
+			break;
+		
+		case STORE_ATTR:
+			i = NEXTI();
+			name = GETNAME(i);
+			v = POP();
+			u = POP();
+			/* v.name = u */
+			if (v->ob_type->tp_setattr == NULL) {
+				type_error(ctx, "object without writable attributes");
+			}
+			else {
+				if ((*v->ob_type->tp_setattr)(v, name, u) != 0)
+					puterrno(ctx);
+			}
+			DECREF(v);
+			DECREF(u);
+			break;
+		
+		case DELETE_ATTR:
+			i = NEXTI();
+			name = GETNAME(i);
+			v = POP();
+			/* del v.name */
+			if (v->ob_type->tp_setattr == NULL) {
+				type_error(ctx,
+					"object without writable attributes");
+			}
+			else {
+				if ((*v->ob_type->tp_setattr)
+						(v, name, (object *)NULL) != 0)
+					puterrno(ctx);
+			}
+			DECREF(v);
+			break;
+		
+		case LOAD_CONST:
+			i = NEXTI();
+			v = GETCONST(i);
+			INCREF(v);
+			PUSH(v);
+			break;
+		
+		case LOAD_NAME:
+			i = NEXTI();
+			name = GETNAME(i);
+			v = dictlookup(ctx->ctx_locals, name);
+			if (v == NULL) {
+				v = dictlookup(ctx->ctx_globals, name);
+				if (v == NULL)
+					v = dictlookup(ctx->ctx_builtins, name);
+			}
+			if (v == NULL)
+				name_error(ctx, name);
+				/* XXX could optimize */
+			else
+				INCREF(v);
+			PUSH(v);
+			break;
+		
+		case BUILD_TUPLE:
+			n = NEXTI();
+			v = checkerror(ctx, newtupleobject(n));
+			if (v != NULL) {
+				for (i = n; --i >= 0;) {
+					w = POP();
+					settupleitem(v, i, w);
+				}
+			}
+			PUSH(v);
+			break;
+		
+		case BUILD_LIST:
+			n = NEXTI();
+			v = checkerror(ctx, newlistobject(n));
+			if (v != NULL) {
+				for (i = n; --i >= 0;) {
+					w = POP();
+					setlistitem(v, i, w);
+				}
+			}
+			PUSH(v);
+			break;
+		
+		case BUILD_MAP:
+			(void) NEXTI();
+			v = checkerror(ctx, newdictobject());
+			PUSH(v);
+			break;
+		
+		case LOAD_ATTR:
+			i = NEXTI();
+			name = GETNAME(i);
+			v = POP();
+			if (v->ob_type->tp_getattr == NULL) {
+				type_error(ctx, "attribute-less object");
+				u = NULL;
+			}
+			else {
+				u = checkerror(ctx,
+					(*v->ob_type->tp_getattr)(v, name));
+			}
+			DECREF(v);
+			PUSH(u);
+			break;
+		
+		case COMPARE_OP:
+			op = NEXTI();
+			w = POP();
+			v = POP();
+			u = cmp_outcome(ctx, op, v, w);
+			DECREF(v);
+			DECREF(w);
+			PUSH(u);
+			break;
+		
+		case IMPORT_NAME:
+			i = NEXTI();
+			name = GETNAME(i);
+			u = import_module(ctx, name);
+			if (u != NULL) {
+				INCREF(u);
+				PUSH(u);
+			}
+			break;
+		
+		case IMPORT_FROM:
+			i = NEXTI();
+			name = GETNAME(i);
+			v = TOP();
+			w = getmoduledict(v);
+			x = dictlookup(w, name);
+			if (x == NULL)
+				name_error(ctx, name);
+			else if (dictinsert(ctx->ctx_locals, name, x) != 0)
+				puterrno(ctx);
+			break;
+		
+		/* WARNING!
+		   Don't assign an expression containing NEXTI() directly
+		   to nexti.  This expands to "nexti = ... + *nexti++"
+		   which has undefined evaluation order.  On some machines
+		   (e.g., mips!) the nexti++ is done after the assignment
+		   to nexti. */
+		
+		case JUMP_FORWARD:
+			n = NEXTI();
+			JUMPBY(n);
+			break;
+		
+		case JUMP_IF_FALSE:
+			n = NEXTI();
+			if (!testbool(ctx, TOP()))
+				JUMPBY(n);
+			break;
+		
+		case JUMP_IF_TRUE:
+			n = NEXTI();
+			if (testbool(ctx, TOP()))
+				JUMPBY(n);
+			break;
+		
+		case JUMP_ABSOLUTE:
+			n = NEXTI();
+			JUMPTO(n);
+			/* XXX Should check for interrupts more often? */
+			if (intrcheck())
+				intr_error(ctx);
+			break;
+		
+		case FOR_LOOP:
+			/* for v in s: ...
+			   On entry: stack contains s, i.
+			   On exit: stack contains s, i+1, s[i];
+			   but if loop exhausted:
+			   	s, i are popped, and we jump n bytes */
+			n = NEXTI();
+			w = POP(); /* Loop index */
+			v = POP(); /* Sequence object */
+			x = loop_subscript(ctx, v, w);
+			if (x != NULL) {
+				PUSH(v);
+				u = checkerror(ctx,
+					newintobject(getintvalue(w)+1));
+				PUSH(u);
+				DECREF(w);
+				PUSH(x);
+			}
+			else {
+				DECREF(v);
+				DECREF(w);
+				JUMPBY(n);
+			}
+			break;
+		
+		case SETUP_LOOP:
+			n = NEXTI();
+			setup_block(f, SETUP_LOOP, n);
+			break;
+		
+		case SETUP_EXCEPT:
+			n = NEXTI();
+			setup_block(f, SETUP_EXCEPT, n);
+			break;
+		
+		case SETUP_FINALLY:
+			n = NEXTI();
+			setup_block(f, SETUP_FINALLY, n);
+			break;
+		
+		default:
+			printf("opcode %d\n", Prevbyte(f));
+			sys_error(ctx, "eval_compiled: unknown opcode");
+			break;
+		
+		}
+		
+		/* Unwind block stack if an exception occurred */
+		
+		while (ctx->ctx_exception && f->f_iblock > 0) {
+			block *b = pop_block(f);
+			if (b->b_type == SETUP_LOOP &&
+					ctx->ctx_exception == BREAK_PSEUDO) {
+				clear_exception(ctx);
+				JUMPTO(b->b_handler);
+				break;
+			}
+			else if (b->b_type == SETUP_FINALLY ||
+				b->b_type == SETUP_EXCEPT &&
+				ctx->ctx_exception == CATCHABLE_EXCEPTION) {
+				v = ctx->ctx_errval;
+				if (v == NULL)
+					v = None;
+				INCREF(v);
+				PUSH(v);
+				v = ctx->ctx_error;
+				if (v == NULL)
+					v = newintobject(ctx->ctx_exception);
+				else
+					INCREF(v);
+				PUSH(v);
+				clear_exception(ctx);
+				JUMPTO(b->b_handler);
+				break;
+			}
+		}
+	}
+	
+	if (ctx->ctx_exception) {
+		while (!EMPTY()) {
+			v = POP();
+			XDECREF(v);
+		}
+		v = NULL;
+		if (ctx->ctx_exception == RETURN_PSEUDO) {
+			if (needvalue) {
+				v = ctx->ctx_errval;
+				INCREF(v);
+				clear_exception(ctx);
+			}
+			else {
+				/* XXX Can detect this statically! */
+				type_error(ctx, "unexpected return statement");
+			}
+		}
+	}
+	else {
+		if (needvalue)
+			v = POP();
+		else
+			v = NULL;
+		if (!EMPTY()) {
+			sys_error(ctx, "stack not cleaned up");
+			XDECREF(v);
+			while (!EMPTY()) {
+				v = POP();
+				XDECREF(v);
+			}
+			v = NULL;
+		}
+	}
+
+#undef EMPTY
+#undef FULL
+#undef GETCONST
+#undef GETNAME
+#undef JUMPTO
+#undef JUMPBY
+
+#undef NEXTOP
+#undef NEXTI
+#undef POP
+#undef TOP
+#undef PUSH
+	
+	DECREF(f);
+	return v;
+
+}
+
+/* Provisional interface until everything is compilable */
+
+#include "node.h"
+
+static object *
+eval_or_exec(ctx, n, arg, needvalue)
+	context *ctx;
+	node *n;
+	object *arg;
+	int needvalue;
+{
+	object *v;
+	codeobject *co = compile(n);
+	if (co == NULL) {
+		puterrno(ctx);
+		return NULL;
+	}
+	v = eval_compiled(ctx, co, arg, needvalue);
+	DECREF(co);
+	return v;
+}
+
+object *
+eval_node(ctx, n)
+	context *ctx;
+	node *n;
+{
+	return eval_or_exec(ctx, n, (object *)NULL, 1/*needvalue*/);
+}
+
+void
+exec_node(ctx, n)
+	context *ctx;
+	node *n;
+{
+	(void) eval_or_exec(ctx, n, (object *)NULL, 0/*needvalue*/);
+}