cpython/Python/ceval.c

1561 lines
29 KiB
C
Raw Normal View History

1990-11-18 13:27:39 -04:00
/* 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*/);
}