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 */
|
/* Compile an expression node to intermediate code */
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -174,7 +173,8 @@ com_addint(c, x)
|
||||||
struct compiling *c;
|
struct compiling *c;
|
||||||
int x;
|
int x;
|
||||||
{
|
{
|
||||||
com_addbyte(c, x);
|
com_addbyte(c, x & 0xff);
|
||||||
|
com_addbyte(c, x >> 8); /* XXX x should be positive */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -215,22 +215,10 @@ com_backpatch(c, anchor)
|
||||||
int prev;
|
int prev;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* Make the JUMP instruction at anchor point to target */
|
/* Make the JUMP instruction at anchor point to target */
|
||||||
prev = code[anchor];
|
prev = code[anchor] + (code[anchor+1] << 8);
|
||||||
dist = target - (anchor+1);
|
dist = target - (anchor+2);
|
||||||
if (dist > 255 && lastanchor &&
|
code[anchor] = dist & 0xff;
|
||||||
code[lastanchor-1] == code[anchor-1]) {
|
code[anchor+1] = dist >> 8;
|
||||||
/* Attempt to jump to a similar jump closer by */
|
|
||||||
/* XXX Show that it works */
|
|
||||||
fprintf(stderr, "com_backpatch: huge jump rescued (?)\n");
|
|
||||||
target = lastanchor-1;
|
|
||||||
dist = target - (anchor+1);
|
|
||||||
lastanchor = 0;
|
|
||||||
}
|
|
||||||
if (dist > 255) {
|
|
||||||
err_setstr(SystemError, "relative jump > 255 bytes");
|
|
||||||
c->c_errors++;
|
|
||||||
}
|
|
||||||
code[anchor] = dist;
|
|
||||||
if (!prev)
|
if (!prev)
|
||||||
break;
|
break;
|
||||||
lastanchor = anchor;
|
lastanchor = anchor;
|
||||||
|
@ -246,11 +234,16 @@ com_add(c, list, v)
|
||||||
object *list;
|
object *list;
|
||||||
object *v;
|
object *v;
|
||||||
{
|
{
|
||||||
/* XXX Should look through list for object with same value */
|
int n = getlistsize(list);
|
||||||
int i = 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)
|
if (addlistitem(list, v) != 0)
|
||||||
c->c_errors++;
|
c->c_errors++;
|
||||||
return i;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -926,6 +919,7 @@ com_assign(c, n, assigning)
|
||||||
/* Loop to avoid trivial recursion */
|
/* Loop to avoid trivial recursion */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
switch (TYPE(n)) {
|
switch (TYPE(n)) {
|
||||||
|
|
||||||
case exprlist:
|
case exprlist:
|
||||||
case testlist:
|
case testlist:
|
||||||
if (NCH(n) > 1) {
|
if (NCH(n) > 1) {
|
||||||
|
@ -934,6 +928,7 @@ com_assign(c, n, assigning)
|
||||||
}
|
}
|
||||||
n = CHILD(n, 0);
|
n = CHILD(n, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case test:
|
case test:
|
||||||
case and_test:
|
case and_test:
|
||||||
case not_test:
|
case not_test:
|
||||||
|
@ -945,6 +940,7 @@ com_assign(c, n, assigning)
|
||||||
}
|
}
|
||||||
n = CHILD(n, 0);
|
n = CHILD(n, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case comparison:
|
case comparison:
|
||||||
if (NCH(n) > 1) {
|
if (NCH(n) > 1) {
|
||||||
err_setstr(TypeError,
|
err_setstr(TypeError,
|
||||||
|
@ -954,6 +950,7 @@ com_assign(c, n, assigning)
|
||||||
}
|
}
|
||||||
n = CHILD(n, 0);
|
n = CHILD(n, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case expr:
|
case expr:
|
||||||
if (NCH(n) > 1) {
|
if (NCH(n) > 1) {
|
||||||
err_setstr(TypeError,
|
err_setstr(TypeError,
|
||||||
|
@ -963,6 +960,7 @@ com_assign(c, n, assigning)
|
||||||
}
|
}
|
||||||
n = CHILD(n, 0);
|
n = CHILD(n, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case term:
|
case term:
|
||||||
if (NCH(n) > 1) {
|
if (NCH(n) > 1) {
|
||||||
err_setstr(TypeError,
|
err_setstr(TypeError,
|
||||||
|
@ -972,6 +970,7 @@ com_assign(c, n, assigning)
|
||||||
}
|
}
|
||||||
n = CHILD(n, 0);
|
n = CHILD(n, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case factor: /* ('+'|'-') factor | atom trailer* */
|
case factor: /* ('+'|'-') factor | atom trailer* */
|
||||||
if (TYPE(CHILD(n, 0)) != atom) { /* '+' | '-' */
|
if (TYPE(CHILD(n, 0)) != atom) { /* '+' | '-' */
|
||||||
err_setstr(TypeError,
|
err_setstr(TypeError,
|
||||||
|
@ -991,6 +990,7 @@ com_assign(c, n, assigning)
|
||||||
}
|
}
|
||||||
n = CHILD(n, 0);
|
n = CHILD(n, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case atom:
|
case atom:
|
||||||
switch (TYPE(CHILD(n, 0))) {
|
switch (TYPE(CHILD(n, 0))) {
|
||||||
case LPAR:
|
case LPAR:
|
||||||
|
@ -1023,11 +1023,13 @@ com_assign(c, n, assigning)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "node type %d\n", TYPE(n));
|
fprintf(stderr, "node type %d\n", TYPE(n));
|
||||||
err_setstr(SystemError, "com_assign: bad node");
|
err_setstr(SystemError, "com_assign: bad node");
|
||||||
c->c_errors++;
|
c->c_errors++;
|
||||||
return;
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1065,6 +1067,7 @@ com_print_stmt(c, n)
|
||||||
}
|
}
|
||||||
if (TYPE(CHILD(n, NCH(n)-2)) != COMMA)
|
if (TYPE(CHILD(n, NCH(n)-2)) != COMMA)
|
||||||
com_addbyte(c, PRINT_NEWLINE);
|
com_addbyte(c, PRINT_NEWLINE);
|
||||||
|
/* XXX Alternatively, LOAD_CONST '\n' and then PRINT_ITEM */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1112,6 +1115,7 @@ com_import_stmt(c, n)
|
||||||
com_addbyte(c, POP_TOP);
|
com_addbyte(c, POP_TOP);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
/* 'import' ... */
|
||||||
for (i = 1; i < NCH(n); i += 2) {
|
for (i = 1; i < NCH(n); i += 2) {
|
||||||
com_addopname(c, IMPORT_NAME, CHILD(n, i));
|
com_addopname(c, IMPORT_NAME, CHILD(n, i));
|
||||||
com_addopname(c, STORE_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);
|
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
|
static void
|
||||||
com_try_stmt(c, n)
|
com_try_stmt(c, n)
|
||||||
struct compiling *c;
|
struct compiling *c;
|
||||||
|
@ -1206,6 +1295,7 @@ com_try_stmt(c, n)
|
||||||
int except_anchor = 0;
|
int except_anchor = 0;
|
||||||
REQ(n, try_stmt);
|
REQ(n, try_stmt);
|
||||||
/* 'try' ':' suite (except_clause ':' suite)* ['finally' ':' suite] */
|
/* 'try' ':' suite (except_clause ':' suite)* ['finally' ':' suite] */
|
||||||
|
|
||||||
if (NCH(n) > 3 && TYPE(CHILD(n, NCH(n)-3)) != except_clause) {
|
if (NCH(n) > 3 && TYPE(CHILD(n, NCH(n)-3)) != except_clause) {
|
||||||
/* Have a 'finally' clause */
|
/* Have a 'finally' clause */
|
||||||
com_addfwref(c, SETUP_FINALLY, &finally_anchor);
|
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 < NCH(n) && TYPE(ch = CHILD(n, i)) == except_clause;
|
||||||
i += 3) {
|
i += 3) {
|
||||||
/* except_clause: 'except' [expr [',' expr]] */
|
/* 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) {
|
if (NCH(ch) > 1) {
|
||||||
com_addbyte(c, DUP_TOP);
|
com_addbyte(c, DUP_TOP);
|
||||||
com_node(c, CHILD(ch, 1));
|
com_node(c, CHILD(ch, 1));
|
||||||
com_addoparg(c, COMPARE_OP, EXC_MATCH);
|
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);
|
||||||
}
|
}
|
||||||
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_addbyte(c, POP_TOP);
|
||||||
com_node(c, CHILD(n, i+2));
|
com_node(c, CHILD(n, i+2));
|
||||||
com_addfwref(c, JUMP_FORWARD, &end_anchor);
|
com_addfwref(c, JUMP_FORWARD, &end_anchor);
|
||||||
if (next_anchor)
|
if (except_anchor) {
|
||||||
com_backpatch(c, next_anchor);
|
com_backpatch(c, except_anchor);
|
||||||
|
com_addbyte(c, POP_TOP);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
com_addbyte(c, END_FINALLY);
|
com_addbyte(c, END_FINALLY);
|
||||||
com_backpatch(c, end_anchor);
|
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
|
static void
|
||||||
com_classdef(c, n)
|
com_classdef(c, n)
|
||||||
struct compiling *c;
|
struct compiling *c;
|
||||||
node *n;
|
node *n;
|
||||||
{
|
{
|
||||||
|
object *v;
|
||||||
REQ(n, classdef);
|
REQ(n, classdef);
|
||||||
/*
|
/*
|
||||||
classdef: 'class' NAME parameters ['=' baselist] ':' suite
|
classdef: 'class' NAME parameters ['=' baselist] ':' suite
|
||||||
baselist: atom arguments (',' atom arguments)*
|
baselist: atom arguments (',' atom arguments)*
|
||||||
arguments: '(' [testlist] ')'
|
arguments: '(' [testlist] ')'
|
||||||
*/
|
*/
|
||||||
err_setstr(SystemError, "can't compile 'class' yet");
|
if (NCH(n) == 7)
|
||||||
c->c_errors++;
|
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
|
static void
|
||||||
|
@ -1484,6 +1613,22 @@ compile_funcdef(c, n)
|
||||||
com_addbyte(c, RETURN_VALUE);
|
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
|
static void
|
||||||
compile_node(c, n)
|
compile_node(c, n)
|
||||||
struct compiling *c;
|
struct compiling *c;
|
||||||
|
@ -1511,6 +1656,10 @@ compile_node(c, n)
|
||||||
compile_funcdef(c, n);
|
compile_funcdef(c, n);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case classdef:
|
||||||
|
compile_classdef(c, n);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "node type %d\n", TYPE(n));
|
fprintf(stderr, "node type %d\n", TYPE(n));
|
||||||
err_setstr(SystemError, "compile_node: unexpected node type");
|
err_setstr(SystemError, "compile_node: unexpected node type");
|
||||||
|
|
Loading…
Reference in New Issue