1561 lines
29 KiB
C
1561 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 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*/);
|
||
|
}
|