cpython/Python/ceval.c
Guido van Rossum e9736fc8a1 Free parse tree after compiling.
Added support for class definitions.
Reorganized main interpreter loop to fetch op and arg once at the head.
Use two bytes for arguments (see ceval.c).
1990-11-18 17:33:06 +00:00

1562 lines
29 KiB
C

/* 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 GETUSTRINGVALUE(s) ((unsigned char *)GETSTRINGVALUE(s))
#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) (GETUSTRINGVALUE((f)->f_code->co_code)[(f)->f_nexti++])
#define Peekbyte(f) (GETUSTRINGVALUE((f)->f_code->co_code)[(f)->f_nexti])
#define Peekint(f) \
(GETUSTRINGVALUE((f)->f_code->co_code)[(f)->f_nexti] + \
GETUSTRINGVALUE((f)->f_code->co_code)[(f)->f_nexti+1])
#define Prevbyte(f) (GETUSTRINGVALUE((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 GETUSTRINGVALUE(code)[f->f_nexti++];
}
static int
nextint(f)
frameobject *f;
{
int a, b;
#ifdef NDEBUG
a = Nextbyte(f);
b = Nextbyte(f);
#else
a = nextbyte(f);
b = nextbyte(f);
#endif
return a + (b << 8);
}
/* 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 = Peekint(f);
printf("op %d arg %d\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 void
import_from(ctx, v, name)
context *ctx;
object *v;
char *name;
{
object *w, *x;
w = getmoduledict(v);
if (name[0] == '*') {
int i;
int n = getdictsize(w);
for (i = 0; i < n; i++) {
name = getdictkey(w, i);
if (name == NULL || name[0] == '_')
continue;
x = dictlookup(w, name);
if (x == NULL) {
/* XXX can't happen? */
name_error(ctx, name);
break;
}
if (dictinsert(ctx->ctx_locals, name, x) != 0) {
puterrno(ctx);
break;
}
}
}
else {
x = dictlookup(w, name);
if (x == NULL)
name_error(ctx, name);
else if (dictinsert(ctx->ctx_locals, name, x) != 0)
puterrno(ctx);
}
}
static object *
build_class(ctx, v, w)
context *ctx;
object *v; /* None or tuple containing base classes */
object *w; /* dictionary */
{
if (is_tupleobject(v)) {
int i;
for (i = gettuplesize(v); --i >= 0; ) {
object *x = gettupleitem(v, i);
if (!is_classobject(x)) {
type_error(ctx, "base is not a class object");
return NULL;
}
}
}
else {
v = NULL;
}
if (!is_dictobject(w)) {
sys_error(ctx, "build_class with non-dictionary");
return NULL;
}
return checkerror(ctx, newclassobject(v, w));
}
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 i, 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)
#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))
#endif
if (arg != NULL) {
INCREF(arg);
PUSH(arg);
}
while (f->f_nexti < getstringsize((object *)f->f_code->co_code) &&
!ctx->ctx_exception) {
#ifdef NDEBUG
op = Nextbyte(f);
#else
op = trace ? trace_nextop(f) : nextbyte(f);
#endif
if (op >= HAVE_ARGUMENT)
i = nextint(f);
switch (op) {
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_classmethodobject(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_classmethodobject(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+0:
case SLICE+1:
case SLICE+2:
case SLICE+3:
op -= SLICE;
if (op & 2)
w = POP();
else
w = NULL;
if (op & 1)
v = POP();
else
v = NULL;
u = POP();
x = apply_slice(ctx, u, v, w);
DECREF(u);
XDECREF(v);
XDECREF(w);
PUSH(x);
break;
case STORE_SLICE+0:
case STORE_SLICE+1:
case STORE_SLICE+2:
case STORE_SLICE+3:
op -= SLICE;
if (op & 2)
w = POP();
else
w = NULL;
if (op & 1)
v = POP();
else
v = NULL;
u = POP();
x = POP();
assign_slice(ctx, u, v, w, x); /* u[v:w] = x */
DECREF(x);
DECREF(u);
XDECREF(v);
XDECREF(w);
break;
case DELETE_SLICE+0:
case DELETE_SLICE+1:
case DELETE_SLICE+2:
case DELETE_SLICE+3:
op -= SLICE;
if (op & 2)
w = POP();
else
w = NULL;
if (op & 1)
v = POP();
else
v = NULL;
u = POP();
x = NULL;
assign_slice(ctx, u, v, w, x); /* del u[v:w] */
DECREF(u);
XDECREF(v);
XDECREF(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 LOAD_LOCALS:
v = ctx->ctx_locals;
INCREF(v);
PUSH(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 BUILD_CLASS:
w = POP();
v = POP();
x = build_class(ctx, v, w);
PUSH(x);
DECREF(v);
DECREF(w);
break;
case STORE_NAME:
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:
name = GETNAME(i);
if (dictremove(ctx->ctx_locals, name) != 0)
name_error(ctx, name);
break;
case UNPACK_TUPLE:
v = POP();
if (!is_tupleobject(v)) {
type_error(ctx, "unpack non-tuple");
}
else if (gettuplesize(v) != i) {
error(ctx, "unpack tuple of wrong size");
}
else {
for (; --i >= 0; ) {
w = gettupleitem(v, i);
INCREF(w);
PUSH(w);
}
}
DECREF(v);
break;
case UNPACK_LIST:
v = POP();
if (!is_listobject(v)) {
type_error(ctx, "unpack non-list");
}
else if (getlistsize(v) != i) {
error(ctx, "unpack tuple of wrong size");
}
else {
for (; --i >= 0; ) {
w = getlistitem(v, i);
INCREF(w);
PUSH(w);
}
}
DECREF(v);
break;
case STORE_ATTR:
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:
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:
v = GETCONST(i);
INCREF(v);
PUSH(v);
break;
case LOAD_NAME:
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:
v = checkerror(ctx, newtupleobject(i));
if (v != NULL) {
for (; --i >= 0;) {
w = POP();
settupleitem(v, i, w);
}
}
PUSH(v);
break;
case BUILD_LIST:
v = checkerror(ctx, newlistobject(i));
if (v != NULL) {
for (; --i >= 0;) {
w = POP();
setlistitem(v, i, w);
}
}
PUSH(v);
break;
case BUILD_MAP:
v = checkerror(ctx, newdictobject());
PUSH(v);
break;
case LOAD_ATTR:
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:
w = POP();
v = POP();
u = cmp_outcome(ctx, (enum cmp_op)i, v, w);
DECREF(v);
DECREF(w);
PUSH(u);
break;
case IMPORT_NAME:
name = GETNAME(i);
u = import_module(ctx, name);
if (u != NULL) {
INCREF(u);
PUSH(u);
}
break;
case IMPORT_FROM:
name = GETNAME(i);
v = TOP();
import_from(ctx, v, name);
break;
case JUMP_FORWARD:
JUMPBY(i);
break;
case JUMP_IF_FALSE:
if (!testbool(ctx, TOP()))
JUMPBY(i);
break;
case JUMP_IF_TRUE:
if (testbool(ctx, TOP()))
JUMPBY(i);
break;
case JUMP_ABSOLUTE:
JUMPTO(i);
/* 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 */
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(i);
}
break;
case SETUP_LOOP:
setup_block(f, SETUP_LOOP, i);
break;
case SETUP_EXCEPT:
setup_block(f, SETUP_EXCEPT, i);
break;
case SETUP_FINALLY:
setup_block(f, SETUP_FINALLY, i);
break;
default:
printf("opcode %d\n", op);
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 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);
freenode(n); /* XXX A rather strange place to do this! */
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*/);
}