Compile class definitions.
Document and fix code generation for try statements. Use two bytes for all arguments. Avoid duplicate entries in lists of constants and names.
This commit is contained in:
parent
e9736fc8a1
commit
d6f3bc2aae
203
Python/compile.c
203
Python/compile.c
|
@ -1,4 +1,3 @@
|
|||
#define DEBUG
|
||||
/* Compile an expression node to intermediate code */
|
||||
|
||||
#include <stdio.h>
|
||||
|
@ -174,7 +173,8 @@ com_addint(c, x)
|
|||
struct compiling *c;
|
||||
int x;
|
||||
{
|
||||
com_addbyte(c, x);
|
||||
com_addbyte(c, x & 0xff);
|
||||
com_addbyte(c, x >> 8); /* XXX x should be positive */
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -215,22 +215,10 @@ com_backpatch(c, anchor)
|
|||
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;
|
||||
prev = code[anchor] + (code[anchor+1] << 8);
|
||||
dist = target - (anchor+2);
|
||||
code[anchor] = dist & 0xff;
|
||||
code[anchor+1] = dist >> 8;
|
||||
if (!prev)
|
||||
break;
|
||||
lastanchor = anchor;
|
||||
|
@ -246,11 +234,16 @@ com_add(c, list, v)
|
|||
object *list;
|
||||
object *v;
|
||||
{
|
||||
/* XXX Should look through list for object with same value */
|
||||
int i = getlistsize(list);
|
||||
int n = getlistsize(list);
|
||||
int i;
|
||||
for (i = n; --i >= 0; ) {
|
||||
object *w = getlistitem(list, i);
|
||||
if (cmpobject(v, w) == 0)
|
||||
return i;
|
||||
}
|
||||
if (addlistitem(list, v) != 0)
|
||||
c->c_errors++;
|
||||
return i;
|
||||
return n;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -926,6 +919,7 @@ com_assign(c, n, assigning)
|
|||
/* Loop to avoid trivial recursion */
|
||||
for (;;) {
|
||||
switch (TYPE(n)) {
|
||||
|
||||
case exprlist:
|
||||
case testlist:
|
||||
if (NCH(n) > 1) {
|
||||
|
@ -934,6 +928,7 @@ com_assign(c, n, assigning)
|
|||
}
|
||||
n = CHILD(n, 0);
|
||||
break;
|
||||
|
||||
case test:
|
||||
case and_test:
|
||||
case not_test:
|
||||
|
@ -945,6 +940,7 @@ com_assign(c, n, assigning)
|
|||
}
|
||||
n = CHILD(n, 0);
|
||||
break;
|
||||
|
||||
case comparison:
|
||||
if (NCH(n) > 1) {
|
||||
err_setstr(TypeError,
|
||||
|
@ -954,6 +950,7 @@ com_assign(c, n, assigning)
|
|||
}
|
||||
n = CHILD(n, 0);
|
||||
break;
|
||||
|
||||
case expr:
|
||||
if (NCH(n) > 1) {
|
||||
err_setstr(TypeError,
|
||||
|
@ -963,6 +960,7 @@ com_assign(c, n, assigning)
|
|||
}
|
||||
n = CHILD(n, 0);
|
||||
break;
|
||||
|
||||
case term:
|
||||
if (NCH(n) > 1) {
|
||||
err_setstr(TypeError,
|
||||
|
@ -972,6 +970,7 @@ com_assign(c, n, assigning)
|
|||
}
|
||||
n = CHILD(n, 0);
|
||||
break;
|
||||
|
||||
case factor: /* ('+'|'-') factor | atom trailer* */
|
||||
if (TYPE(CHILD(n, 0)) != atom) { /* '+' | '-' */
|
||||
err_setstr(TypeError,
|
||||
|
@ -991,6 +990,7 @@ com_assign(c, n, assigning)
|
|||
}
|
||||
n = CHILD(n, 0);
|
||||
break;
|
||||
|
||||
case atom:
|
||||
switch (TYPE(CHILD(n, 0))) {
|
||||
case LPAR:
|
||||
|
@ -1023,11 +1023,13 @@ com_assign(c, n, assigning)
|
|||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "node type %d\n", TYPE(n));
|
||||
err_setstr(SystemError, "com_assign: bad node");
|
||||
c->c_errors++;
|
||||
return;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1065,6 +1067,7 @@ com_print_stmt(c, n)
|
|||
}
|
||||
if (TYPE(CHILD(n, NCH(n)-2)) != COMMA)
|
||||
com_addbyte(c, PRINT_NEWLINE);
|
||||
/* XXX Alternatively, LOAD_CONST '\n' and then PRINT_ITEM */
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1112,6 +1115,7 @@ com_import_stmt(c, n)
|
|||
com_addbyte(c, POP_TOP);
|
||||
}
|
||||
else {
|
||||
/* 'import' ... */
|
||||
for (i = 1; i < NCH(n); i += 2) {
|
||||
com_addopname(c, IMPORT_NAME, CHILD(n, i));
|
||||
com_addopname(c, STORE_NAME, CHILD(n, i));
|
||||
|
@ -1197,6 +1201,91 @@ com_for_stmt(c, n)
|
|||
com_backpatch(c, break_anchor);
|
||||
}
|
||||
|
||||
/* Although 'execpt' and 'finally' clauses can be combined
|
||||
syntactically, they are compiled separately. In fact,
|
||||
try: S
|
||||
except E1: S1
|
||||
except E2: S2
|
||||
...
|
||||
finally: Sf
|
||||
is equivalent to
|
||||
try:
|
||||
try: S
|
||||
except E1: S1
|
||||
except E2: S2
|
||||
...
|
||||
finally: Sf
|
||||
meaning that the 'finally' clause is entered even if things
|
||||
go wrong again in an exception handler. Note that this is
|
||||
not the case for exception handlers: at most one is entered.
|
||||
|
||||
Code generated for "try: S finally: Sf" is as follows:
|
||||
|
||||
SETUP_FINALLY L
|
||||
<code for S>
|
||||
POP_BLOCK
|
||||
LOAD_CONST <nil>
|
||||
LOAD_CONST <nil>
|
||||
L: <code for Sf>
|
||||
END_FINALLY
|
||||
|
||||
The special instructions use the block stack. Each block
|
||||
stack entry contains the instruction that created it (here
|
||||
SETUP_FINALLY), the level of the value stack at the time the
|
||||
block stack entry was created, and a label (here L).
|
||||
|
||||
SETUP_FINALLY:
|
||||
Pushes the current value stack level and the label
|
||||
onto the block stack.
|
||||
POP_BLOCK:
|
||||
Pops en entry from the block stack, and pops the value
|
||||
stack until its level is the same as indicated on the
|
||||
block stack. (The label is ignored.)
|
||||
END_FINALLY:
|
||||
Pops two entries from the *value* stack and re-raises
|
||||
the exception they specify. If the top entry is nil,
|
||||
no exception is raised. If it is a number, a pseudo
|
||||
exception is raised ('return' or 'break'). Otherwise,
|
||||
a real exception (specified by a string) is raised.
|
||||
The second entry popped from the is the value that goes
|
||||
with the exception (or the return value).
|
||||
|
||||
The block stack is unwound when an exception is raised:
|
||||
when a SETUP_FINALLY entry is found, the exception is pushed
|
||||
onto the value stack (and the exception condition is cleared),
|
||||
and the interpreter jumps to the label gotten from the block
|
||||
stack.
|
||||
|
||||
Code generated for "try: S except E1, V1: S1 except E2, V2: S2 ...":
|
||||
|
||||
Value stack Label Instruction Argument
|
||||
[] SETUP_EXCEPT L1
|
||||
[] <code for S>
|
||||
[] POP_BLOCK
|
||||
[] JUMP_FORWARD L0
|
||||
|
||||
[val, exc] L1: DUP
|
||||
[val, exc, exc] <evaluate E1>
|
||||
[val, exc, exc, E1] COMPARE_OP EXC_MATCH
|
||||
[val, exc, 1-or-0] JUMP_IF_FALSE L2
|
||||
[val, exc, 1] POP
|
||||
[val, exc] POP
|
||||
[val] <assign to V1>
|
||||
[] <code for S1>
|
||||
JUMP_FORWARD L0
|
||||
|
||||
[val, exc, 0] L2: POP
|
||||
[val, exc] DUP
|
||||
.............................etc.......................
|
||||
|
||||
[val, exc, 0] Ln+1: POP
|
||||
[val, exc] END_FINALLY # re-raise exception
|
||||
|
||||
[] L0: <next statement>
|
||||
|
||||
Of course, parts are not generated if Vi or Ei is not present.
|
||||
*/
|
||||
|
||||
static void
|
||||
com_try_stmt(c, n)
|
||||
struct compiling *c;
|
||||
|
@ -1206,6 +1295,7 @@ com_try_stmt(c, n)
|
|||
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);
|
||||
|
@ -1226,12 +1316,18 @@ com_try_stmt(c, n)
|
|||
i < NCH(n) && TYPE(ch = CHILD(n, i)) == except_clause;
|
||||
i += 3) {
|
||||
/* except_clause: 'except' [expr [',' expr]] */
|
||||
int next_anchor = 0;
|
||||
if (except_anchor == 0) {
|
||||
err_setstr(TypeError,
|
||||
"default 'except:' must be last");
|
||||
c->c_errors++;
|
||||
break;
|
||||
}
|
||||
except_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_addfwref(c, JUMP_IF_FALSE, &except_anchor);
|
||||
com_addbyte(c, POP_TOP);
|
||||
}
|
||||
com_addbyte(c, POP_TOP);
|
||||
|
@ -1241,8 +1337,10 @@ com_try_stmt(c, n)
|
|||
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);
|
||||
if (except_anchor) {
|
||||
com_backpatch(c, except_anchor);
|
||||
com_addbyte(c, POP_TOP);
|
||||
}
|
||||
}
|
||||
com_addbyte(c, END_FINALLY);
|
||||
com_backpatch(c, end_anchor);
|
||||
|
@ -1296,19 +1394,50 @@ com_funcdef(c, n)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
com_bases(c, n)
|
||||
struct compiling *c;
|
||||
node *n;
|
||||
{
|
||||
int i, nbases;
|
||||
REQ(n, baselist);
|
||||
/*
|
||||
baselist: atom arguments (',' atom arguments)*
|
||||
arguments: '(' [testlist] ')'
|
||||
*/
|
||||
for (i = 0; i < NCH(n); i += 3)
|
||||
com_node(c, CHILD(n, i));
|
||||
com_addoparg(c, BUILD_TUPLE, (NCH(n)+1) / 3);
|
||||
}
|
||||
|
||||
static void
|
||||
com_classdef(c, n)
|
||||
struct compiling *c;
|
||||
node *n;
|
||||
{
|
||||
object *v;
|
||||
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++;
|
||||
if (NCH(n) == 7)
|
||||
com_bases(c, CHILD(n, 4));
|
||||
else
|
||||
com_addoparg(c, LOAD_CONST, com_addconst(c, None));
|
||||
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_addbyte(c, UNARY_CALL);
|
||||
com_addbyte(c, BUILD_CLASS);
|
||||
com_addopname(c, STORE_NAME, CHILD(n, 1));
|
||||
DECREF(v);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1484,6 +1613,22 @@ compile_funcdef(c, n)
|
|||
com_addbyte(c, RETURN_VALUE);
|
||||
}
|
||||
|
||||
static void
|
||||
compile_classdef(c, n)
|
||||
struct compiling *c;
|
||||
node *n;
|
||||
{
|
||||
node *ch;
|
||||
REQ(n, classdef);
|
||||
/*
|
||||
classdef: 'class' NAME parameters ['=' baselist] ':' suite
|
||||
*/
|
||||
com_addbyte(c, REFUSE_ARGS);
|
||||
com_node(c, CHILD(n, NCH(n)-1));
|
||||
com_addbyte(c, LOAD_LOCALS);
|
||||
com_addbyte(c, RETURN_VALUE);
|
||||
}
|
||||
|
||||
static void
|
||||
compile_node(c, n)
|
||||
struct compiling *c;
|
||||
|
@ -1511,6 +1656,10 @@ compile_node(c, n)
|
|||
compile_funcdef(c, n);
|
||||
break;
|
||||
|
||||
case classdef:
|
||||
compile_classdef(c, n);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "node type %d\n", TYPE(n));
|
||||
err_setstr(SystemError, "compile_node: unexpected node type");
|
||||
|
|
Loading…
Reference in New Issue