]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Initial revision
authorGuido van Rossum <guido@python.org>
Sun, 18 Nov 1990 17:27:39 +0000 (17:27 +0000)
committerGuido van Rossum <guido@python.org>
Sun, 18 Nov 1990 17:27:39 +0000 (17:27 +0000)
Include/opcode.h [new file with mode: 0644]
Python/ceval.c [new file with mode: 0644]
Python/compile.c [new file with mode: 0644]

diff --git a/Include/opcode.h b/Include/opcode.h
new file mode 100644 (file)
index 0000000..43ce54e
--- /dev/null
@@ -0,0 +1,78 @@
+/* Instruction opcodes for compiled code */
+
+#define DUP_TOP                0
+#define POP_TOP                1
+#define ROT_TWO                2
+#define ROT_THREE      3
+
+#define UNARY_POSITIVE 10
+#define UNARY_NEGATIVE 11
+#define UNARY_NOT      12
+#define UNARY_CONVERT  13
+#define UNARY_CALL     14
+
+#define BINARY_MULTIPLY        20
+#define BINARY_DIVIDE  21
+#define BINARY_MODULO  22
+#define BINARY_ADD     23
+#define BINARY_SUBTRACT        24
+#define BINARY_SUBSCR  25
+#define BINARY_CALL    26
+
+#define SLICE          30
+/* Also uses 31-33 */
+
+#define STORE_SLICE    40
+/* Also uses 41-43 */
+
+#define DELETE_SLICE   50
+/* Also uses 51-53 */
+
+#define STORE_SUBSCR   60
+#define DELETE_SUBSCR  61
+
+#define PRINT_EXPR     70
+#define PRINT_ITEM     71
+#define PRINT_NEWLINE  72
+
+#define BREAK_LOOP     80
+#define RAISE_EXCEPTION        81
+#define RETURN_VALUE   83
+#define REQUIRE_ARGS   84
+#define REFUSE_ARGS    85
+#define BUILD_FUNCTION 86
+#define POP_BLOCK      87
+#define END_FINALLY    88
+
+#define HAVE_ARGUMENT  90      /* Opcodes from here have an argument: */
+
+#define STORE_NAME     90      /* Index in name list */
+#define DELETE_NAME    91      /* "" */
+#define UNPACK_TUPLE   92      /* Number of tuple items */
+#define UNPACK_LIST    93      /* Number of list items */
+/* unused:             94 */
+#define STORE_ATTR     95      /* Index in name list */
+#define DELETE_ATTR    96      /* "" */
+
+#define LOAD_CONST     100     /* Index in const list */
+#define LOAD_NAME      101     /* Index in name list */
+#define BUILD_TUPLE    102     /* Number of tuple items */
+#define BUILD_LIST     103     /* Number of list items */
+#define BUILD_MAP      104     /* Always zero for now */
+#define LOAD_ATTR      105     /* Index in name list */
+#define COMPARE_OP     106     /* Comparison operator */
+#define IMPORT_NAME    107     /* Index in name list */
+#define IMPORT_FROM    108     /* Index in name list */
+
+#define JUMP_FORWARD   110     /* Number of bytes to skip */
+#define JUMP_IF_FALSE  111     /* "" */
+#define JUMP_IF_TRUE   112     /* "" */
+#define JUMP_ABSOLUTE  113     /* Target byte offset from beginning of code */
+#define FOR_LOOP       114     /* Number of bytes to skip */
+
+#define SETUP_LOOP     120     /* Target address (absolute) */
+#define SETUP_EXCEPT   121     /* "" */
+#define SETUP_FINALLY  122     /* "" */
+
+/* Comparison operator codes (argument to COMPARE_OP) */
+enum cmp_op {LT, LE, EQ, NE, GT, GE, IN, NOT_IN, IS, IS_NOT, EXC_MATCH, BAD};
diff --git a/Python/ceval.c b/Python/ceval.c
new file mode 100644 (file)
index 0000000..6a303ca
--- /dev/null
@@ -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*/);
+}
diff --git a/Python/compile.c b/Python/compile.c
new file mode 100644 (file)
index 0000000..03a1e32
--- /dev/null
@@ -0,0 +1,1537 @@
+#define DEBUG
+/* Compile an expression node to intermediate code */
+
+#include <stdio.h>
+#include <ctype.h>
+#include "string.h"
+
+#include "PROTO.h"
+#include "object.h"
+#include "objimpl.h"
+#include "intobject.h"
+#include "floatobject.h"
+#include "stringobject.h"
+#include "listobject.h"
+#include "node.h"
+#include "token.h"
+#include "graminit.h"
+#include "errors.h"
+#include "compile.h"
+#include "opcode.h"
+
+static void
+code_dealloc(c)
+       codeobject *c;
+{
+       XDECREF(c->co_code);
+       XDECREF(c->co_consts);
+       XDECREF(c->co_names);
+       DEL(c);
+}
+
+typeobject Codetype = {
+       OB_HEAD_INIT(&Typetype)
+       0,
+       "code",
+       sizeof(codeobject),
+       0,
+       code_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 codeobject *newcodeobject PROTO((object *, object *, object *));
+
+static codeobject *
+newcodeobject(code, consts, names)
+       object *code;
+       object *consts;
+       object *names;
+{
+       codeobject *co;
+       int i;
+       /* Check argument types */
+       if (code == NULL || !is_stringobject(code) ||
+               consts == NULL || !is_listobject(consts) ||
+               names == NULL || !is_listobject(names)) {
+               err_badcall();
+               return NULL;
+       }
+       /* Make sure the list of names contains only strings */
+       for (i = getlistsize(names); --i >= 0; ) {
+               object *v = getlistitem(names, i);
+               if (v == NULL || !is_stringobject(v)) {
+                       err_badcall();
+                       return NULL;
+               }
+       }
+       co = NEWOBJ(codeobject, &Codetype);
+       if (co != NULL) {
+               INCREF(code);
+               co->co_code = (stringobject *)code;
+               INCREF(consts);
+               co->co_consts = consts;
+               INCREF(names);
+               co->co_names = names;
+       }
+       return co;
+}
+
+
+/* Data structure used internally */
+struct compiling {
+       object *c_code;         /* string */
+       object *c_consts;       /* list of objects */
+       object *c_names;        /* list of strings (names) */
+       int c_nexti;            /* index into c_code */
+       int c_errors;           /* counts errors occurred */
+};
+
+/* Prototypes */
+static int com_init PROTO((struct compiling *));
+static void com_free PROTO((struct compiling *));
+static void com_done PROTO((struct compiling *));
+static void com_node PROTO((struct compiling *, struct _node *));
+static void com_addbyte PROTO((struct compiling *, int));
+static void com_addint PROTO((struct compiling *, int));
+static void com_addoparg PROTO((struct compiling *, int, int));
+static void com_addfwref PROTO((struct compiling *, int, int *));
+static void com_backpatch PROTO((struct compiling *, int));
+static int com_add PROTO((struct compiling *, object *, object *));
+static int com_addconst PROTO((struct compiling *, object *));
+static int com_addname PROTO((struct compiling *, object *));
+static void com_addopname PROTO((struct compiling *, int, node *));
+
+static int
+com_init(c)
+       struct compiling *c;
+{
+       if ((c->c_code = newsizedstringobject((char *)NULL, 0)) == NULL)
+               goto fail_3;
+       if ((c->c_consts = newlistobject(0)) == NULL)
+               goto fail_2;
+       if ((c->c_names = newlistobject(0)) == NULL)
+               goto fail_1;
+       c->c_nexti = 0;
+       c->c_errors = 0;
+       return 1;
+       
+  fail_1:
+       DECREF(c->c_consts);
+  fail_2:
+       DECREF(c->c_code);
+  fail_3:
+       return 0;
+}
+
+static void
+com_free(c)
+       struct compiling *c;
+{
+       XDECREF(c->c_code);
+       XDECREF(c->c_consts);
+       XDECREF(c->c_names);
+}
+
+static void
+com_done(c)
+       struct compiling *c;
+{
+       if (c->c_code != NULL)
+               resizestring(&c->c_code, c->c_nexti);
+}
+
+static void
+com_addbyte(c, byte)
+       struct compiling *c;
+       int byte;
+{
+       int len;
+       if (byte < 0 || byte > 255) {
+               err_setstr(SystemError, "com_addbyte: byte out of range");
+               c->c_errors++;
+       }
+       if (c->c_code == NULL)
+               return;
+       len = getstringsize(c->c_code);
+       if (c->c_nexti >= len) {
+               if (resizestring(&c->c_code, len+1000) != 0) {
+                       c->c_errors++;
+                       return;
+               }
+       }
+       getstringvalue(c->c_code)[c->c_nexti++] = byte;
+}
+
+static void
+com_addint(c, x)
+       struct compiling *c;
+       int x;
+{
+       com_addbyte(c, x);
+}
+
+static void
+com_addoparg(c, op, arg)
+       struct compiling *c;
+       int op;
+       int arg;
+{
+       com_addbyte(c, op);
+       com_addint(c, arg);
+}
+
+static void
+com_addfwref(c, op, p_anchor)
+       struct compiling *c;
+       int op;
+       int *p_anchor;
+{
+       /* Compile a forward reference for backpatching */
+       int here;
+       int anchor;
+       com_addbyte(c, op);
+       here = c->c_nexti;
+       anchor = *p_anchor;
+       *p_anchor = here;
+       com_addint(c, anchor == 0 ? 0 : here - anchor);
+}
+
+static void
+com_backpatch(c, anchor)
+       struct compiling *c;
+       int anchor; /* Must be nonzero */
+{
+       unsigned char *code = (unsigned char *) getstringvalue(c->c_code);
+       int target = c->c_nexti;
+       int lastanchor = 0;
+       int dist;
+       int prev;
+       for (;;) {
+               /* Make the JUMP instruction at anchor point to target */
+               prev = code[anchor];
+               dist = target - (anchor+1);
+               if (dist > 255 && lastanchor &&
+                               code[lastanchor-1] == code[anchor-1]) {
+                       /* Attempt to jump to a similar jump closer by */
+/* XXX Show that it works */
+fprintf(stderr, "com_backpatch: huge jump rescued (?)\n");
+                       target = lastanchor-1;
+                       dist = target - (anchor+1);
+                       lastanchor = 0;
+               }
+               if (dist > 255) {
+                       err_setstr(SystemError, "relative jump > 255 bytes");
+                       c->c_errors++;
+               }
+               code[anchor] = dist;
+               if (!prev)
+                       break;
+               lastanchor = anchor;
+               anchor -= prev;
+       }
+}
+
+/* Handle constants and names uniformly */
+
+static int
+com_add(c, list, v)
+       struct compiling *c;
+       object *list;
+       object *v;
+{
+       /* XXX Should look through list for object with same value */
+       int i = getlistsize(list);
+       if (addlistitem(list, v) != 0)
+               c->c_errors++;
+       return i;
+}
+
+static int
+com_addconst(c, v)
+       struct compiling *c;
+       object *v;
+{
+       return com_add(c, c->c_consts, v);
+}
+
+static int
+com_addname(c, v)
+       struct compiling *c;
+       object *v;
+{
+       return com_add(c, c->c_names, v);
+}
+
+static void
+com_addopname(c, op, n)
+       struct compiling *c;
+       int op;
+       node *n;
+{
+       object *v;
+       int i;
+       char *name;
+       if (TYPE(n) == STAR)
+               name = "*";
+       else {
+               REQ(n, NAME);
+               name = STR(n);
+       }
+       if ((v = newstringobject(name)) == NULL) {
+               c->c_errors++;
+               i = 255;
+       }
+       else {
+               i = com_addname(c, v);
+               DECREF(v);
+       }
+       com_addoparg(c, op, i);
+}
+
+static object *
+parsenumber(s)
+       char *s;
+{
+       extern long strtol();
+       extern double atof();
+       char *end = s;
+       long x;
+       x = strtol(s, &end, 0);
+       if (*end == '\0')
+               return newintobject(x);
+       if (*end == '.' || *end == 'e' || *end == 'E')
+               return newfloatobject(atof(s));
+       err_setstr(RuntimeError, "bad number syntax");
+       return NULL;
+}
+
+static object *
+parsestr(s)
+       char *s;
+{
+       object *v;
+       int len;
+       char *buf;
+       char *p;
+       int c;
+       if (*s != '\'') {
+               err_badcall();
+               return NULL;
+       }
+       s++;
+       len = strlen(s);
+       if (s[--len] != '\'') {
+               err_badcall();
+               return NULL;
+       }
+       if (strchr(s, '\\') == NULL)
+               return newsizedstringobject(s, len);
+       v = newsizedstringobject((char *)NULL, len);
+       p = buf = getstringvalue(v);
+       while (*s != '\0' && *s != '\'') {
+               if (*s != '\\') {
+                       *p++ = *s++;
+                       continue;
+               }
+               s++;
+               switch (*s++) {
+               /* XXX This assumes ASCII! */
+               case '\\': *p++ = '\\'; break;
+               case '\'': *p++ = '\''; break;
+               case 'b': *p++ = '\b'; break;
+               case 'f': *p++ = '\014'; break; /* FF */
+               case 't': *p++ = '\t'; break;
+               case 'n': *p++ = '\n'; break;
+               case 'r': *p++ = '\r'; break;
+               case 'v': *p++ = '\013'; break; /* VT */
+               case 'E': *p++ = '\033'; break; /* ESC, not C */
+               case 'a': *p++ = '\007'; break; /* BEL, not classic C */
+               case '0': case '1': case '2': case '3':
+               case '4': case '5': case '6': case '7':
+                       c = s[-1] - '0';
+                       if ('0' <= *s && *s <= '7') {
+                               c = (c<<3) + *s++ - '0';
+                               if ('0' <= *s && *s <= '7')
+                                       c = (c<<3) + *s++ - '0';
+                       }
+                       *p++ = c;
+                       break;
+               case 'x':
+                       if (isxdigit(*s)) {
+                               sscanf(s, "%x", &c);
+                               *p++ = c;
+                               do {
+                                       s++;
+                               } while (isxdigit(*s));
+                               break;
+                       }
+               /* FALLTHROUGH */
+               default: *p++ = '\\'; *p++ = s[-1]; break;
+               }
+       }
+       resizestring(&v, (int)(p - buf));
+       return v;
+}
+
+static void
+com_list_constructor(c, n)
+       struct compiling *c;
+       node *n;
+{
+       int len;
+       int i;
+       object *v, *w;
+       if (TYPE(n) != testlist)
+               REQ(n, exprlist);
+       /* exprlist: expr (',' expr)* [',']; likewise for testlist */
+       len = (NCH(n) + 1) / 2;
+       for (i = 0; i < NCH(n); i += 2)
+               com_node(c, CHILD(n, i));
+       com_addoparg(c, BUILD_LIST, len);
+}
+
+static void
+com_atom(c, n)
+       struct compiling *c;
+       node *n;
+{
+       node *ch;
+       object *v;
+       int i;
+       REQ(n, atom);
+       ch = CHILD(n, 0);
+       switch (TYPE(ch)) {
+       case LPAR:
+               if (TYPE(CHILD(n, 1)) == RPAR)
+                       com_addoparg(c, BUILD_TUPLE, 0);
+               else
+                       com_node(c, CHILD(n, 1));
+               break;
+       case LSQB:
+               if (TYPE(CHILD(n, 1)) == RSQB)
+                       com_addoparg(c, BUILD_LIST, 0);
+               else
+                       com_list_constructor(c, CHILD(n, 1));
+               break;
+       case LBRACE:
+               com_addoparg(c, BUILD_MAP, 0);
+               break;
+       case BACKQUOTE:
+               com_node(c, CHILD(n, 1));
+               com_addbyte(c, UNARY_CONVERT);
+               break;
+       case NUMBER:
+               if ((v = parsenumber(STR(ch))) == NULL) {
+                       c->c_errors++;
+                       i = 255;
+               }
+               else {
+                       i = com_addconst(c, v);
+                       DECREF(v);
+               }
+               com_addoparg(c, LOAD_CONST, i);
+               break;
+       case STRING:
+               if ((v = parsestr(STR(ch))) == NULL) {
+                       c->c_errors++;
+                       i = 255;
+               }
+               else {
+                       i = com_addconst(c, v);
+                       DECREF(v);
+               }
+               com_addoparg(c, LOAD_CONST, i);
+               break;
+       case NAME:
+               com_addopname(c, LOAD_NAME, ch);
+               break;
+       default:
+               fprintf(stderr, "node type %d\n", TYPE(ch));
+               err_setstr(SystemError, "com_atom: unexpected node type");
+               c->c_errors++;
+       }
+}
+
+static void
+com_slice(c, n, op)
+       struct compiling *c;
+       node *n;
+       int op;
+{
+       if (NCH(n) == 1) {
+               com_addbyte(c, op);
+       }
+       else if (NCH(n) == 2) {
+               if (TYPE(CHILD(n, 0)) != COLON) {
+                       com_node(c, CHILD(n, 0));
+                       com_addbyte(c, op+1);
+               }
+               else {
+                       com_node(c, CHILD(n, 1));
+                       com_addbyte(c, op+2);
+               }
+       }
+       else {
+               com_node(c, CHILD(n, 0));
+               com_node(c, CHILD(n, 2));
+               com_addbyte(c, op+3);
+       }
+}
+
+static void
+com_apply_subscript(c, n)
+       struct compiling *c;
+       node *n;
+{
+       REQ(n, subscript);
+       if (NCH(n) == 1 && TYPE(CHILD(n, 0)) != COLON) {
+               /* It's a single subscript */
+               com_node(c, CHILD(n, 0));
+               com_addbyte(c, BINARY_SUBSCR);
+       }
+       else {
+               /* It's a slice: [expr] ':' [expr] */
+               com_slice(c, n, SLICE);
+       }
+}
+
+static void
+com_call_function(c, n)
+       struct compiling *c;
+       node *n; /* EITHER testlist OR ')' */
+{
+       if (TYPE(n) == RPAR) {
+               com_addbyte(c, UNARY_CALL);
+       }
+       else {
+               com_node(c, n);
+               com_addbyte(c, BINARY_CALL);
+       }
+}
+
+static void
+com_select_member(c, n)
+       struct compiling *c;
+       node *n;
+{
+       com_addopname(c, LOAD_ATTR, n);
+}
+
+static void
+com_apply_trailer(c, n)
+       struct compiling *c;
+       node *n;
+{
+       REQ(n, trailer);
+       switch (TYPE(CHILD(n, 0))) {
+       case LPAR:
+               com_call_function(c, CHILD(n, 1));
+               break;
+       case DOT:
+               com_select_member(c, CHILD(n, 1));
+               break;
+       case LSQB:
+               com_apply_subscript(c, CHILD(n, 1));
+               break;
+       default:
+               err_setstr(SystemError,
+                       "com_apply_trailer: unknown trailer type");
+               c->c_errors++;
+       }
+}
+
+static void
+com_factor(c, n)
+       struct compiling *c;
+       node *n;
+{
+       int i;
+       REQ(n, factor);
+       if (TYPE(CHILD(n, 0)) == PLUS) {
+               com_factor(c, CHILD(n, 1));
+               com_addbyte(c, UNARY_POSITIVE);
+       }
+       else if (TYPE(CHILD(n, 0)) == MINUS) {
+               com_factor(c, CHILD(n, 1));
+               com_addbyte(c, UNARY_NEGATIVE);
+       }
+       else {
+               com_atom(c, CHILD(n, 0));
+               for (i = 1; i < NCH(n); i++)
+                       com_apply_trailer(c, CHILD(n, i));
+       }
+}
+
+static void
+com_term(c, n)
+       struct compiling *c;
+       node *n;
+{
+       int i;
+       int op;
+       REQ(n, term);
+       com_factor(c, CHILD(n, 0));
+       for (i = 2; i < NCH(n); i += 2) {
+               com_factor(c, CHILD(n, i));
+               switch (TYPE(CHILD(n, i-1))) {
+               case STAR:
+                       op = BINARY_MULTIPLY;
+                       break;
+               case SLASH:
+                       op = BINARY_DIVIDE;
+                       break;
+               case PERCENT:
+                       op = BINARY_MODULO;
+                       break;
+               default:
+                       err_setstr(SystemError,
+                               "com_term: term operator not *, / or %");
+                       c->c_errors++;
+                       op = 255;
+               }
+               com_addbyte(c, op);
+       }
+}
+
+static void
+com_expr(c, n)
+       struct compiling *c;
+       node *n;
+{
+       int i;
+       int op;
+       REQ(n, expr);
+       com_term(c, CHILD(n, 0));
+       for (i = 2; i < NCH(n); i += 2) {
+               com_term(c, CHILD(n, i));
+               switch (TYPE(CHILD(n, i-1))) {
+               case PLUS:
+                       op = BINARY_ADD;
+                       break;
+               case MINUS:
+                       op = BINARY_SUBTRACT;
+                       break;
+               default:
+                       err_setstr(SystemError,
+                               "com_expr: expr operator not + or -");
+                       c->c_errors++;
+                       op = 255;
+               }
+               com_addbyte(c, op);
+       }
+}
+
+static enum cmp_op
+cmp_type(n)
+       node *n;
+{
+       REQ(n, comp_op);
+       /* comp_op: '<' | '>' | '=' | '>' '=' | '<' '=' | '<' '>'
+                 | 'in' | 'not' 'in' | 'is' | 'is' not' */
+       if (NCH(n) == 1) {
+               n = CHILD(n, 0);
+               switch (TYPE(n)) {
+               case LESS:      return LT;
+               case GREATER:   return GT;
+               case EQUAL:     return EQ;
+               case NAME:      if (strcmp(STR(n), "in") == 0) return IN;
+                               if (strcmp(STR(n), "is") == 0) return IS;
+               }
+       }
+       else if (NCH(n) == 2) {
+               int t2 = TYPE(CHILD(n, 1));
+               switch (TYPE(CHILD(n, 0))) {
+               case LESS:      if (t2 == EQUAL)        return LE;
+                               if (t2 == GREATER)      return NE;
+                               break;
+               case GREATER:   if (t2 == EQUAL)        return GE;
+                               break;
+               case NAME:      if (strcmp(STR(CHILD(n, 1)), "in") == 0)
+                                       return NOT_IN;
+                               if (strcmp(STR(CHILD(n, 0)), "is") == 0)
+                                       return IS_NOT;
+               }
+       }
+       return BAD;
+}
+
+static void
+com_comparison(c, n)
+       struct compiling *c;
+       node *n;
+{
+       int i;
+       enum cmp_op op;
+       int anchor;
+       REQ(n, comparison); /* comparison: expr (comp_op expr)* */
+       com_expr(c, CHILD(n, 0));
+       if (NCH(n) == 1)
+               return;
+       
+       /****************************************************************
+          The following code is generated for all but the last
+          comparison in a chain:
+          
+          label:       on stack:       opcode:         jump to:
+          
+                       a               <code to load b>
+                       a, b            DUP_TOP
+                       a, b, b         ROT_THREE
+                       b, a, b         COMPARE_OP
+                       b, 0-or-1       JUMP_IF_FALSE   L1
+                       b, 1            POP_TOP
+                       b               
+       
+          We are now ready to repeat this sequence for the next
+          comparison in the chain.
+          
+          For the last we generate:
+          
+                       b               <code to load c>
+                       b, c            COMPARE_OP
+                       0-or-1          
+          
+          If there were any jumps to L1 (i.e., there was more than one
+          comparison), we generate:
+          
+                       0-or-1          JUMP_FORWARD    L2
+          L1:          b, 0            ROT_TWO
+                       0, b            POP_TOP
+                       0
+          L2:
+       ****************************************************************/
+       
+       anchor = 0;
+       
+       for (i = 2; i < NCH(n); i += 2) {
+               com_expr(c, CHILD(n, i));
+               if (i+2 < NCH(n)) {
+                       com_addbyte(c, DUP_TOP);
+                       com_addbyte(c, ROT_THREE);
+               }
+               op = cmp_type(CHILD(n, i-1));
+               if (op == BAD) {
+                       err_setstr(SystemError,
+                               "com_comparison: unknown comparison op");
+                       c->c_errors++;
+               }
+               com_addoparg(c, COMPARE_OP, op);
+               if (i+2 < NCH(n)) {
+                       com_addfwref(c, JUMP_IF_FALSE, &anchor);
+                       com_addbyte(c, POP_TOP);
+               }
+       }
+       
+       if (anchor) {
+               int anchor2 = 0;
+               com_addfwref(c, JUMP_FORWARD, &anchor2);
+               com_backpatch(c, anchor);
+               com_addbyte(c, ROT_TWO);
+               com_addbyte(c, POP_TOP);
+               com_backpatch(c, anchor2);
+       }
+}
+
+static void
+com_not_test(c, n)
+       struct compiling *c;
+       node *n;
+{
+       REQ(n, not_test); /* 'not' not_test | comparison */
+       if (NCH(n) == 1) {
+               com_comparison(c, CHILD(n, 0));
+       }
+       else {
+               com_not_test(c, CHILD(n, 1));
+               com_addbyte(c, UNARY_NOT);
+       }
+}
+
+static void
+com_and_test(c, n)
+       struct compiling *c;
+       node *n;
+{
+       int i;
+       int anchor;
+       REQ(n, and_test); /* not_test ('and' not_test)* */
+       anchor = 0;
+       i = 0;
+       for (;;) {
+               com_not_test(c, CHILD(n, i));
+               if ((i += 2) >= NCH(n))
+                       break;
+               com_addfwref(c, JUMP_IF_FALSE, &anchor);
+               com_addbyte(c, POP_TOP);
+       }
+       if (anchor)
+               com_backpatch(c, anchor);
+}
+
+static void
+com_test(c, n)
+       struct compiling *c;
+       node *n;
+{
+       int i;
+       int anchor;
+       REQ(n, test); /* and_test ('and' and_test)* */
+       anchor = 0;
+       i = 0;
+       for (;;) {
+               com_and_test(c, CHILD(n, i));
+               if ((i += 2) >= NCH(n))
+                       break;
+               com_addfwref(c, JUMP_IF_TRUE, &anchor);
+               com_addbyte(c, POP_TOP);
+       }
+       if (anchor)
+               com_backpatch(c, anchor);
+}
+
+static void
+com_list(c, n)
+       struct compiling *c;
+       node *n;
+{
+       /* exprlist: expr (',' expr)* [',']; likewise for testlist */
+       if (NCH(n) == 1) {
+               com_node(c, CHILD(n, 0));
+       }
+       else {
+               int i;
+               int len;
+               len = (NCH(n) + 1) / 2;
+               for (i = 0; i < NCH(n); i += 2)
+                       com_node(c, CHILD(n, i));
+               com_addoparg(c, BUILD_TUPLE, len);
+       }
+}
+
+
+/* Begin of assignment compilation */
+
+static void com_assign_name PROTO((struct compiling *, node *, int));
+static void com_assign PROTO((struct compiling *, node *, int));
+
+static void
+com_assign_attr(c, n, assigning)
+       struct compiling *c;
+       node *n;
+       int assigning;
+{
+       com_addopname(c, assigning ? STORE_ATTR : DELETE_ATTR, n);
+}
+
+static void
+com_assign_slice(c, n, assigning)
+       struct compiling *c;
+       node *n;
+       int assigning;
+{
+       com_slice(c, n, assigning ? STORE_SLICE : DELETE_SLICE);
+}
+
+static void
+com_assign_subscript(c, n, assigning)
+       struct compiling *c;
+       node *n;
+       int assigning;
+{
+       com_node(c, n);
+       com_addbyte(c, assigning ? STORE_SUBSCR : DELETE_SUBSCR);
+}
+
+static void
+com_assign_trailer(c, n, assigning)
+       struct compiling *c;
+       node *n;
+       int assigning;
+{
+       char *name;
+       REQ(n, trailer);
+       switch (TYPE(CHILD(n, 0))) {
+       case LPAR: /* '(' [exprlist] ')' */
+               err_setstr(TypeError, "can't assign to function call");
+               c->c_errors++;
+               break;
+       case DOT: /* '.' NAME */
+               com_assign_attr(c, CHILD(n, 1), assigning);
+               break;
+       case LSQB: /* '[' subscript ']' */
+               n = CHILD(n, 1);
+               REQ(n, subscript); /* subscript: expr | [expr] ':' [expr] */
+               if (NCH(n) > 1 || TYPE(CHILD(n, 0)) == COLON)
+                       com_assign_slice(c, n, assigning);
+               else
+                       com_assign_subscript(c, CHILD(n, 0), assigning);
+               break;
+       default:
+               err_setstr(TypeError, "unknown trailer type");
+               c->c_errors++;
+       }
+}
+
+static void
+com_assign_tuple(c, n, assigning)
+       struct compiling *c;
+       node *n;
+       int assigning;
+{
+       int i;
+       if (TYPE(n) != testlist)
+               REQ(n, exprlist);
+       if (assigning)
+               com_addoparg(c, UNPACK_TUPLE, (NCH(n)+1)/2);
+       for (i = 0; i < NCH(n); i += 2)
+               com_assign(c, CHILD(n, i), assigning);
+}
+
+static void
+com_assign_list(c, n, assigning)
+       struct compiling *c;
+       node *n;
+       int assigning;
+{
+       int i;
+       if (assigning)
+               com_addoparg(c, UNPACK_LIST, (NCH(n)+1)/2);
+       for (i = 0; i < NCH(n); i += 2)
+               com_assign(c, CHILD(n, i), assigning);
+}
+
+static void
+com_assign_name(c, n, assigning)
+       struct compiling *c;
+       node *n;
+       int assigning;
+{
+       REQ(n, NAME);
+       com_addopname(c, assigning ? STORE_NAME : DELETE_NAME, n);
+}
+
+static void
+com_assign(c, n, assigning)
+       struct compiling *c;
+       node *n;
+       int assigning;
+{
+       /* Loop to avoid trivial recursion */
+       for (;;) {
+               switch (TYPE(n)) {
+               case exprlist:
+               case testlist:
+                       if (NCH(n) > 1) {
+                               com_assign_tuple(c, n, assigning);
+                               return;
+                       }
+                       n = CHILD(n, 0);
+                       break;
+               case test:
+               case and_test:
+               case not_test:
+                       if (NCH(n) > 1) {
+                               err_setstr(TypeError,
+                                       "can't assign to operator");
+                               c->c_errors++;
+                               return;
+                       }
+                       n = CHILD(n, 0);
+                       break;
+               case comparison:
+                       if (NCH(n) > 1) {
+                               err_setstr(TypeError,
+                                       "can't assign to operator");
+                               c->c_errors++;
+                               return;
+                       }
+                       n = CHILD(n, 0);
+                       break;
+               case expr:
+                       if (NCH(n) > 1) {
+                               err_setstr(TypeError,
+                                       "can't assign to operator");
+                               c->c_errors++;
+                               return;
+                       }
+                       n = CHILD(n, 0);
+                       break;
+               case term:
+                       if (NCH(n) > 1) {
+                               err_setstr(TypeError,
+                                       "can't assign to operator");
+                               c->c_errors++;
+                               return;
+                       }
+                       n = CHILD(n, 0);
+                       break;
+               case factor: /* ('+'|'-') factor | atom trailer* */
+                       if (TYPE(CHILD(n, 0)) != atom) { /* '+' | '-' */
+                               err_setstr(TypeError,
+                                       "can't assign to operator");
+                               c->c_errors++;
+                               return;
+                       }
+                       if (NCH(n) > 1) { /* trailer present */
+                               int i;
+                               com_node(c, CHILD(n, 0));
+                               for (i = 1; i+1 < NCH(n); i++) {
+                                       com_apply_trailer(c, CHILD(n, i));
+                               } /* NB i is still alive */
+                               com_assign_trailer(c,
+                                               CHILD(n, i), assigning);
+                               return;
+                       }
+                       n = CHILD(n, 0);
+                       break;
+               case atom:
+                       switch (TYPE(CHILD(n, 0))) {
+                       case LPAR:
+                               n = CHILD(n, 1);
+                               if (TYPE(n) == RPAR) {
+                                       /* XXX Should allow () = () ??? */
+                                       err_setstr(TypeError,
+                                               "can't assign to ()");
+                                       c->c_errors++;
+                                       return;
+                               }
+                               break;
+                       case LSQB:
+                               n = CHILD(n, 1);
+                               if (TYPE(n) == RSQB) {
+                                       err_setstr(TypeError,
+                                               "can't assign to []");
+                                       c->c_errors++;
+                                       return;
+                               }
+                               com_assign_list(c, n, assigning);
+                               return;
+                       case NAME:
+                               com_assign_name(c, CHILD(n, 0), assigning);
+                               return;
+                       default:
+                               err_setstr(TypeError,
+                                               "can't assign to constant");
+                               c->c_errors++;
+                               return;
+                       }
+                       break;
+               default:
+                       fprintf(stderr, "node type %d\n", TYPE(n));
+                       err_setstr(SystemError, "com_assign: bad node");
+                       c->c_errors++;
+                       return;
+               }
+       }
+}
+
+static void
+com_expr_stmt(c, n)
+       struct compiling *c;
+       node *n;
+{
+       REQ(n, expr_stmt); /* exprlist ('=' exprlist)* NEWLINE */
+       com_node(c, CHILD(n, NCH(n)-2));
+       if (NCH(n) == 2) {
+               com_addbyte(c, PRINT_EXPR);
+       }
+       else {
+               int i;
+               for (i = 0; i < NCH(n)-3; i+=2) {
+                       if (i+2 < NCH(n)-3)
+                               com_addbyte(c, DUP_TOP);
+                       com_assign(c, CHILD(n, i), 1/*assign*/);
+               }
+       }
+}
+
+static void
+com_print_stmt(c, n)
+       struct compiling *c;
+       node *n;
+{
+       int i;
+       REQ(n, print_stmt); /* 'print' (test ',')* [test] NEWLINE */
+       for (i = 1; i+1 < NCH(n); i += 2) {
+               com_node(c, CHILD(n, i));
+               com_addbyte(c, PRINT_ITEM);
+       }
+       if (TYPE(CHILD(n, NCH(n)-2)) != COMMA)
+               com_addbyte(c, PRINT_NEWLINE);
+}
+
+static void
+com_return_stmt(c, n)
+       struct compiling *c;
+       node *n;
+{
+       REQ(n, return_stmt); /* 'return' [testlist] NEWLINE */
+       if (NCH(n) == 2)
+               com_addoparg(c, LOAD_CONST, com_addconst(c, None));
+       else
+               com_node(c, CHILD(n, 1));
+       com_addbyte(c, RETURN_VALUE);
+}
+
+static void
+com_raise_stmt(c, n)
+       struct compiling *c;
+       node *n;
+{
+       REQ(n, raise_stmt); /* 'raise' expr [',' expr] NEWLINE */
+       com_node(c, CHILD(n, 1));
+       if (NCH(n) > 3)
+               com_node(c, CHILD(n, 3));
+       else
+               com_addoparg(c, LOAD_CONST, com_addconst(c, None));
+       com_addbyte(c, RAISE_EXCEPTION);
+}
+
+static void
+com_import_stmt(c, n)
+       struct compiling *c;
+       node *n;
+{
+       int i;
+       REQ(n, import_stmt);
+       /* 'import' NAME (',' NAME)* NEWLINE |
+          'from' NAME 'import' ('*' | NAME (',' NAME)*) NEWLINE */
+       if (STR(CHILD(n, 0))[0] == 'f') {
+               /* 'from' NAME 'import' ... */
+               REQ(CHILD(n, 1), NAME);
+               com_addopname(c, IMPORT_NAME, CHILD(n, 1));
+               for (i = 3; i < NCH(n); i += 2)
+                       com_addopname(c, IMPORT_FROM, CHILD(n, i));
+               com_addbyte(c, POP_TOP);
+       }
+       else {
+               for (i = 1; i < NCH(n); i += 2) {
+                       com_addopname(c, IMPORT_NAME, CHILD(n, i));
+                       com_addopname(c, STORE_NAME, CHILD(n, i));
+               }
+       }
+}
+
+static void
+com_if_stmt(c, n)
+       struct compiling *c;
+       node *n;
+{
+       int i;
+       int anchor = 0;
+       REQ(n, if_stmt);
+       /*'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] */
+       for (i = 0; i+3 < NCH(n); i+=4) {
+               int a = 0;
+               com_node(c, CHILD(n, i+1));
+               com_addfwref(c, JUMP_IF_FALSE, &a);
+               com_addbyte(c, POP_TOP);
+               com_node(c, CHILD(n, i+3));
+               com_addfwref(c, JUMP_FORWARD, &anchor);
+               com_backpatch(c, a);
+               com_addbyte(c, POP_TOP);
+       }
+       if (i+2 < NCH(n))
+               com_node(c, CHILD(n, i+2));
+       com_backpatch(c, anchor);
+}
+
+static void
+com_while_stmt(c, n)
+       struct compiling *c;
+       node *n;
+{
+       int break_anchor = 0;
+       int anchor = 0;
+       int begin;
+       REQ(n, while_stmt); /* 'while' test ':' suite ['else' ':' suite] */
+       com_addfwref(c, SETUP_LOOP, &break_anchor);
+       begin = c->c_nexti;
+       com_node(c, CHILD(n, 1));
+       com_addfwref(c, JUMP_IF_FALSE, &anchor);
+       com_addbyte(c, POP_TOP);
+       com_node(c, CHILD(n, 3));
+       com_addoparg(c, JUMP_ABSOLUTE, begin);
+       com_backpatch(c, anchor);
+       com_addbyte(c, POP_TOP);
+       com_addbyte(c, POP_BLOCK);
+       if (NCH(n) > 4)
+               com_node(c, CHILD(n, 6));
+       com_backpatch(c, break_anchor);
+}
+
+static void
+com_for_stmt(c, n)
+       struct compiling *c;
+       node *n;
+{
+       object *v;
+       int break_anchor = 0;
+       int anchor = 0;
+       int begin;
+       REQ(n, for_stmt);
+       /* 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite] */
+       com_addfwref(c, SETUP_LOOP, &break_anchor);
+       com_node(c, CHILD(n, 3));
+       v = newintobject(0L);
+       if (v == NULL)
+               c->c_errors++;
+       com_addoparg(c, LOAD_CONST, com_addconst(c, v));
+       XDECREF(v);
+       begin = c->c_nexti;
+       com_addfwref(c, FOR_LOOP, &anchor);
+       com_assign(c, CHILD(n, 1), 1/*assigning*/);
+       com_node(c, CHILD(n, 5));
+       com_addoparg(c, JUMP_ABSOLUTE, begin);
+       com_backpatch(c, anchor);
+       com_addbyte(c, POP_BLOCK);
+       if (NCH(n) > 8)
+               com_node(c, CHILD(n, 8));
+       com_backpatch(c, break_anchor);
+}
+
+static void
+com_try_stmt(c, n)
+       struct compiling *c;
+       node *n;
+{
+       int finally_anchor = 0;
+       int except_anchor = 0;
+       REQ(n, try_stmt);
+       /* 'try' ':' suite (except_clause ':' suite)* ['finally' ':' suite] */
+       if (NCH(n) > 3 && TYPE(CHILD(n, NCH(n)-3)) != except_clause) {
+               /* Have a 'finally' clause */
+               com_addfwref(c, SETUP_FINALLY, &finally_anchor);
+       }
+       if (NCH(n) > 3 && TYPE(CHILD(n, 3)) == except_clause) {
+               /* Have an 'except' clause */
+               com_addfwref(c, SETUP_EXCEPT, &except_anchor);
+       }
+       com_node(c, CHILD(n, 2));
+       if (except_anchor) {
+               int end_anchor = 0;
+               int i;
+               node *ch;
+               com_addbyte(c, POP_BLOCK);
+               com_addfwref(c, JUMP_FORWARD, &end_anchor);
+               com_backpatch(c, except_anchor);
+               for (i = 3;
+                       i < NCH(n) && TYPE(ch = CHILD(n, i)) == except_clause;
+                                                               i += 3) {
+                       /* except_clause: 'except' [expr [',' expr]] */
+                       int next_anchor = 0;
+                       if (NCH(ch) > 1) {
+                               com_addbyte(c, DUP_TOP);
+                               com_node(c, CHILD(ch, 1));
+                               com_addoparg(c, COMPARE_OP, EXC_MATCH);
+                               com_addfwref(c, JUMP_IF_FALSE, &next_anchor);
+                               com_addbyte(c, POP_TOP);
+                       }
+                       com_addbyte(c, POP_TOP);
+                       if (NCH(ch) > 3)
+                               com_assign(c, CHILD(ch, 3), 1/*assigning*/);
+                       else
+                               com_addbyte(c, POP_TOP);
+                       com_node(c, CHILD(n, i+2));
+                       com_addfwref(c, JUMP_FORWARD, &end_anchor);
+                       if (next_anchor)
+                               com_backpatch(c, next_anchor);
+               }
+               com_addbyte(c, END_FINALLY);
+               com_backpatch(c, end_anchor);
+       }
+       if (finally_anchor) {
+               com_addbyte(c, POP_BLOCK);
+               com_addoparg(c, LOAD_CONST, com_addconst(c, None));
+               com_addoparg(c, LOAD_CONST, com_addconst(c, None));
+               com_backpatch(c, finally_anchor);
+               com_node(c, CHILD(n, NCH(n)-1));
+               com_addbyte(c, END_FINALLY);
+       }
+}
+
+static void
+com_suite(c, n)
+       struct compiling *c;
+       node *n;
+{
+       REQ(n, suite);
+       /* simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT */
+       if (NCH(n) == 1) {
+               com_node(c, CHILD(n, 0));
+       }
+       else {
+               int i;
+               for (i = 0; i < NCH(n); i++) {
+                       node *ch = CHILD(n, i);
+                       if (TYPE(ch) == stmt)
+                               com_node(c, ch);
+               }
+       }
+}
+
+static void
+com_funcdef(c, n)
+       struct compiling *c;
+       node *n;
+{
+       object *v;
+       REQ(n, funcdef); /* funcdef: 'def' NAME parameters ':' suite */
+       v = (object *)compile(n);
+       if (v == NULL)
+               c->c_errors++;
+       else {
+               int i = com_addconst(c, v);
+               com_addoparg(c, LOAD_CONST, i);
+               com_addbyte(c, BUILD_FUNCTION);
+               com_addopname(c, STORE_NAME, CHILD(n, 1));
+               DECREF(v);
+       }
+}
+
+static void
+com_classdef(c, n)
+       struct compiling *c;
+       node *n;
+{
+       REQ(n, classdef);
+       /*
+       classdef: 'class' NAME parameters ['=' baselist] ':' suite
+       baselist: atom arguments (',' atom arguments)*
+       arguments: '(' [testlist] ')'
+       */
+       err_setstr(SystemError, "can't compile 'class' yet");
+       c->c_errors++;
+}
+
+static void
+com_node(c, n)
+       struct compiling *c;
+       node *n;
+{
+       switch (TYPE(n)) {
+       
+       /* Definition nodes */
+       
+       case funcdef:
+               com_funcdef(c, n);
+               break;
+       case classdef:
+               com_classdef(c, n);
+               break;
+       
+       /* Trivial parse tree nodes */
+       
+       case stmt:
+       case simple_stmt:
+       case flow_stmt:
+       case compound_stmt:
+               com_node(c, CHILD(n, 0));
+               break;
+
+       /* Statement nodes */
+       
+       case expr_stmt:
+               com_expr_stmt(c, n);
+               break;
+       case print_stmt:
+               com_print_stmt(c, n);
+               break;
+       case del_stmt: /* 'del' exprlist NEWLINE */
+               com_assign(c, CHILD(n, 1), 0/*delete*/);
+               break;
+       case pass_stmt:
+               break;
+       case break_stmt:
+               com_addbyte(c, BREAK_LOOP);
+               break;
+       case return_stmt:
+               com_return_stmt(c, n);
+               break;
+       case raise_stmt:
+               com_raise_stmt(c, n);
+               break;
+       case import_stmt:
+               com_import_stmt(c, n);
+               break;
+       case if_stmt:
+               com_if_stmt(c, n);
+               break;
+       case while_stmt:
+               com_while_stmt(c, n);
+               break;
+       case for_stmt:
+               com_for_stmt(c, n);
+               break;
+       case try_stmt:
+               com_try_stmt(c, n);
+               break;
+       case suite:
+               com_suite(c, n);
+               break;
+       
+       /* Expression nodes */
+       
+       case testlist:
+               com_list(c, n);
+               break;
+       case test:
+               com_test(c, n);
+               break;
+       case and_test:
+               com_and_test(c, n);
+               break;
+       case not_test:
+               com_not_test(c, n);
+               break;
+       case comparison:
+               com_comparison(c, n);
+               break;
+       case exprlist:
+               com_list(c, n);
+               break;
+       case expr:
+               com_expr(c, n);
+               break;
+       case term:
+               com_term(c, n);
+               break;
+       case factor:
+               com_factor(c, n);
+               break;
+       case atom:
+               com_atom(c, n);
+               break;
+       
+       default:
+               fprintf(stderr, "node type %d\n", TYPE(n));
+               err_setstr(SystemError, "com_node: unexpected node type");
+               c->c_errors++;
+       }
+}
+
+static void com_fplist PROTO((struct compiling *, node *));
+
+static void
+com_fpdef(c, n)
+       struct compiling *c;
+       node *n;
+{
+       REQ(n, fpdef); /* fpdef: NAME | '(' fplist ')' */
+       if (TYPE(CHILD(n, 0)) == LPAR)
+               com_fplist(c, CHILD(n, 1));
+       else
+               com_addopname(c, STORE_NAME, CHILD(n, 0));
+}
+
+static void
+com_fplist(c, n)
+       struct compiling *c;
+       node *n;
+{
+       REQ(n, fplist); /* fplist: fpdef (',' fpdef)* */
+       if (NCH(n) == 1) {
+               com_fpdef(c, CHILD(n, 0));
+       }
+       else {
+               int i;
+               com_addoparg(c, UNPACK_TUPLE, (NCH(n)+1)/2);
+               for (i = 0; i < NCH(n); i += 2)
+                       com_fpdef(c, CHILD(n, i));
+       }
+}
+
+static void
+com_file_input(c, n)
+       struct compiling *c;
+       node *n;
+{
+       int i;
+       REQ(n, file_input); /* (NEWLINE | stmt)* ENDMARKER */
+       for (i = 0; i < NCH(n); i++) {
+               node *ch = CHILD(n, i);
+               if (TYPE(ch) != ENDMARKER && TYPE(ch) != NEWLINE)
+                       com_node(c, ch);
+       }
+}
+
+/* Top-level compile-node interface */
+
+static void
+compile_funcdef(c, n)
+       struct compiling *c;
+       node *n;
+{
+       node *ch;
+       REQ(n, funcdef); /* funcdef: 'def' NAME parameters ':' suite */
+       ch = CHILD(n, 2); /* parameters: '(' [fplist] ')' */
+       ch = CHILD(ch, 1); /* ')' | fplist */
+       if (TYPE(ch) == RPAR)
+               com_addbyte(c, REFUSE_ARGS);
+       else {
+               com_addbyte(c, REQUIRE_ARGS);
+               com_fplist(c, ch);
+       }
+       com_node(c, CHILD(n, 4));
+       com_addoparg(c, LOAD_CONST, com_addconst(c, None));
+       com_addbyte(c, RETURN_VALUE);
+}
+
+static void
+compile_node(c, n)
+       struct compiling *c;
+       node *n;
+{
+       switch (TYPE(n)) {
+       
+       case single_input:
+               /* NEWLINE | simple_stmt | compound_stmt NEWLINE */
+               n = CHILD(n, 0);
+               if (TYPE(n) != NEWLINE)
+                       com_node(c, n);
+               break;
+       
+       case file_input:
+               com_file_input(c, n);
+               break;
+       
+       case expr_input:
+       case eval_input:
+               com_node(c, CHILD(n, 0));
+               break;
+       
+       case funcdef:
+               compile_funcdef(c, n);
+               break;
+       
+       default:
+               fprintf(stderr, "node type %d\n", TYPE(n));
+               err_setstr(SystemError, "compile_node: unexpected node type");
+               c->c_errors++;
+       }
+}
+
+codeobject *
+compile(n)
+       node *n;
+{
+       struct compiling sc;
+       codeobject *co;
+       if (!com_init(&sc))
+               return NULL;
+       compile_node(&sc, n);
+       com_done(&sc);
+       if (sc.c_errors == 0)
+               co = newcodeobject(sc.c_code, sc.c_consts, sc.c_names);
+       else
+               co = NULL;
+       com_free(&sc);
+       return co;
+}