This patch introduces an extra pass to the compiler that generates a
symbol table for each top-level compilation unit. The information in
the symbol table allows the elimination of the later optimize() pass;
the bytecode generation emits the correct opcodes.
The current version passes the complete regression test, but may still
contain some bugs. It's a fairly substantial revision. The current
code adds an assert() and a test that may lead to a Py_FatalError().
I expect to remove these before 2.1 beta 1.
The symbol table (struct symtable) is described in comments in the
code.
The changes affects the several com_XXX() functions that were used to
emit LOAD_NAME and its ilk. The primary interface for this bytecode
is now com_addop_varname() which takes a kind and a name, where kind
is one of VAR_LOAD, VAR_STORE, or VAR_DELETE.
There are many other smaller changes:
- The name mangling code is no longer contained in ifdefs. There are
two functions that expose the mangling logical: com_mangle() and
symtable_mangle().
- The com_error() function can accept NULL for its first argument;
this is useful with is_constant_false() is called during symbol
table generation.
- The loop index names used by list comprehensions have been changed
from __1__ to [1], so that they can not be accessed by Python code.
- in com_funcdef(), com_argdefs() is now called before the body of the
function is compiled. This provides consistency with com_lambdef()
and symtable_funcdef().
- Helpers do_pad(), dump(), and DUMP() are added to aid in debugging
the compiler.
diff --git a/Python/compile.c b/Python/compile.c
index 734bfcf..8e1cfd8 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -10,10 +10,6 @@
XXX other JAR tricks?
*/
-#ifndef NO_PRIVATE_NAME_MANGLING
-#define PRIVATE_NAME_MANGLING
-#endif
-
#include "Python.h"
#include "node.h"
@@ -45,6 +41,23 @@
#define OP_ASSIGN 1
#define OP_APPLY 2
+#define VAR_LOAD 0
+#define VAR_STORE 1
+#define VAR_DELETE 2
+
+#define NAME_LOCAL 0
+#define NAME_GLOBAL 1
+#define NAME_DEFAULT 2
+
+#define TYPE_FUNCTION 0
+#define TYPE_CLASS 1
+#define TYPE_MODULE 2
+
+#define IMPLICIT_GLOBAL 1
+#define EXPLICIT_GLOBAL 2
+
+#define MANGLE_LEN 256
+
#define OFF(x) offsetof(PyCodeObject, x)
static struct memberlist code_memberlist[] = {
@@ -268,6 +281,19 @@
/* Data structure used internally */
+/* The compiler uses two passes to generate bytecodes. The first pass
+ builds the symbol table. The second pass generates the bytecode.
+
+ The first pass uses a single symtable struct. The second pass uses
+ a compiling struct for each code block. The compiling structs
+ share a reference to the symtable.
+
+ The two passes communicate via symtable_load_symbols() and via
+ is_local() and is_global(). The former initializes several slots
+ in the compiling struct: c_varnames, c_locals, c_nlocals,
+ c_argcount, c_globals, and c_flags.
+*/
+
struct compiling {
PyObject *c_code; /* string */
PyObject *c_consts; /* list of objects */
@@ -296,12 +322,65 @@
int c_firstlineno;
PyObject *c_lnotab; /* Table mapping address to line number */
int c_last_addr, c_last_line, c_lnotab_next;
-#ifdef PRIVATE_NAME_MANGLING
char *c_private; /* for private name mangling */
-#endif
int c_tmpname; /* temporary local name counter */
+ struct symtable *c_symtable; /* pointer to module symbol table */
};
+/* The symbol table is constructed each time PyNode_Compile() is
+ called. The table walks the entire parse tree and identifies each
+ use or definition of a variable.
+
+ The symbol table contains a dictionary for each code block in a
+ module: The symbol dictionary for the block. They keys of these
+ dictionaries are the name of all variables used or defined in the
+ block; the integer values are used to store several flags,
+ e.g. DEF_PARAM indicates that a variable is a parameter to a
+ function.
+
+ The slots st_cur, st_cur_id, and st_cur_type always refer to the
+ current code block. The st_cur slot is the symbol dictionary. The
+ st_cur_id slot is the id is the key in st_symbols. The st_cur_type
+ slot is one of TYPE_FUNCTION, TYPE_CLASS, or TYPE_MODULE.
+
+ The st_symbols slot is a dictionary that maps code block ids to
+ symbol dictionaries. The keys are generated by a counter that is
+ incremented each time a new code block is found. The counter is
+ identifies a specific scope, because both passes walk the parse
+ tree in the same order.
+
+ The st_varnames slot is a dictionary that maps code block ids to
+ parameter lists. The st_global slot always refers to the symbol
+ dictionary for the module.
+*/
+
+struct symtable {
+ PyObject *st_symbols; /* dictionary of symbol tables */
+ PyObject *st_varnames; /* dictionary of parameter lists */
+ PyObject *st_namespaces; /* pointer to list of namespaces */
+ PyObject *st_types; /* pointer to list of namespace types */
+ PyObject *st_cur; /* borrowed ref to dict in st_symbols */
+ PyObject *st_cur_id; /* int id of current code block */
+ int st_cur_type; /* type of current scope */
+ PyObject *st_global; /* borrowed ref to MODULE in st_symbols */
+ int st_scopes; /* number of scopes */
+ int st_errors; /* number of errors */
+ char *st_private; /* name of current class or NULL */
+ int st_tmpname; /* temporary name counter */
+};
+
+#define GLOBAL "global"
+#define NOOPT ".noopt"
+
+/* Flags for def-use information */
+
+#define DEF_GLOBAL 1
+#define DEF_LOCAL 2
+#define DEF_PARAM 2<<1
+#define USE 2<<2
+#define DEF_STAR 2<<3
+#define DEF_DOUBLESTAR 2<<4
+#define DEF_INTUPLE 2<<5
/* Error message including line number */
@@ -309,6 +388,12 @@
com_error(struct compiling *c, PyObject *exc, char *msg)
{
PyObject *v, *tb, *tmp;
+ if (c == NULL) {
+ /* Error occurred via symtable call to
+ is_constant_false */
+ PyErr_SetString(exc, msg);
+ return;
+ }
c->c_errors++;
if (c->c_lineno <= 1) {
/* Unknown line number or single interactive command */
@@ -378,8 +463,8 @@
static void com_push(struct compiling *, int);
static void com_pop(struct compiling *, int);
static void com_done(struct compiling *);
-static void com_node(struct compiling *, struct _node *);
-static void com_factor(struct compiling *, struct _node *);
+static void com_node(struct compiling *, node *);
+static void com_factor(struct compiling *, node *);
static void com_addbyte(struct compiling *, int);
static void com_addint(struct compiling *, int);
static void com_addoparg(struct compiling *, int, int);
@@ -392,16 +477,63 @@
static void com_list(struct compiling *, node *, int);
static void com_list_iter(struct compiling *, node *, node *, char *);
static int com_argdefs(struct compiling *, node *);
-static int com_newlocal(struct compiling *, char *);
static void com_assign(struct compiling *, node *, int, node *);
static void com_assign_name(struct compiling *, node *, int);
-static PyCodeObject *icompile(struct _node *, struct compiling *);
-static PyCodeObject *jcompile(struct _node *, char *,
- struct compiling *);
+static PyCodeObject *icompile(node *, struct compiling *);
+static PyCodeObject *jcompile(node *, char *, struct compiling *);
static PyObject *parsestrplus(node *);
static PyObject *parsestr(char *);
static node *get_rawdocstring(node *);
+static int is_local(struct compiling *, char *);
+static int is_global(struct compiling *, char *);
+
+/* symtable operations */
+static int symtable_build(struct compiling *, node *);
+static int symtable_load_symbols(struct compiling *);
+static struct symtable *symtable_init(void);
+static void symtable_free(struct symtable *);
+static int symtable_enter_scope(struct symtable *, char *, int);
+static int symtable_exit_scope(struct symtable *);
+static int symtable_update_cur(struct symtable *);
+static int symtable_add_def(struct symtable *, char *, int);
+static int symtable_add_use(struct symtable *, char *);
+
+static void symtable_node(struct symtable *, node *);
+static void symtable_funcdef(struct symtable *, node *);
+static void symtable_default_args(struct symtable *, node *);
+static void symtable_params(struct symtable *, node *);
+static void symtable_params_fplist(struct symtable *, node *n);
+static void symtable_global(struct symtable *, node *);
+static void symtable_import(struct symtable *, node *);
+static void symtable_assign(struct symtable *, node *);
+static void symtable_list_comprehension(struct symtable *, node *);
+
+/* helper */
+static void
+do_pad(int pad)
+{
+ int i;
+ for (i = 0; i < pad; ++i)
+ fprintf(stderr, " ");
+}
+
+static void
+dump(node *n, int pad, int depth)
+{
+ int i;
+ if (depth == 0)
+ return;
+ do_pad(pad);
+ fprintf(stderr, "%d: %s\n", TYPE(n), STR(n));
+ if (depth > 0)
+ depth--;
+ for (i = 0; i < NCH(n); ++i)
+ dump(CHILD(n, i), pad + 1, depth);
+}
+
+#define DUMP(N) dump(N, 0, -1)
+
static int
com_init(struct compiling *c, char *filename)
{
@@ -421,11 +553,10 @@
goto fail;
if ((c->c_locals = PyDict_New()) == NULL)
goto fail;
- if ((c->c_varnames = PyList_New(0)) == NULL)
- goto fail;
if ((c->c_lnotab = PyString_FromStringAndSize((char *)NULL,
1000)) == NULL)
goto fail;
+ c->c_varnames = NULL;
c->c_nlocals = 0;
c->c_argcount = 0;
c->c_flags = 0;
@@ -446,6 +577,7 @@
c->c_last_line = 0;
c-> c_lnotab_next = 0;
c->c_tmpname = 0;
+ c->c_symtable = NULL;
return 1;
fail:
@@ -503,6 +635,7 @@
{
int len;
/*fprintf(stderr, "%3d: %3d\n", c->c_nexti, byte);*/
+ assert(byte >= 0 && byte <= 255);
if (byte < 0 || byte > 255) {
/*
fprintf(stderr, "XXX compiling bad byte: %d\n", byte);
@@ -640,7 +773,7 @@
{
PyObject *w, *t, *np=NULL;
long n;
-
+
t = Py_BuildValue("(OO)", v, v->ob_type);
if (t == NULL)
goto fail;
@@ -679,20 +812,19 @@
return com_add(c, c->c_names, c->c_name_dict, v);
}
-#ifdef PRIVATE_NAME_MANGLING
static int
-com_mangle(struct compiling *c, char *name, char *buffer, size_t maxlen)
+mangle(char *p, char *name, char *buffer, size_t maxlen)
{
/* Name mangling: __private becomes _classname__private.
This is independent from how the name is used. */
- char *p;
size_t nlen, plen;
+ if (p == NULL || name == NULL || name[0] != '_' || name[1] != '_')
+ return 0;
nlen = strlen(name);
if (nlen+2 >= maxlen)
return 0; /* Don't mangle __extremely_long_names */
if (name[nlen-1] == '_' && name[nlen-2] == '_')
return 0; /* Don't mangle __whatever__ */
- p = c->c_private;
/* Strip leading underscores from class name */
while (*p == '_')
p++;
@@ -708,20 +840,24 @@
/* fprintf(stderr, "mangle %s -> %s\n", name, buffer); */
return 1;
}
-#endif
+
+static int
+com_mangle(struct compiling *c, char *name, char *buffer, size_t maxlen)
+{
+ return mangle(c->c_private, name, buffer, maxlen);
+}
static void
-com_addopnamestr(struct compiling *c, int op, char *name)
+com_addop_name(struct compiling *c, int op, char *name)
{
PyObject *v;
int i;
-#ifdef PRIVATE_NAME_MANGLING
- char buffer[256];
- if (name != NULL && name[0] == '_' && name[1] == '_' &&
- c->c_private != NULL &&
- com_mangle(c, name, buffer, sizeof(buffer)))
+ char buffer[MANGLE_LEN];
+/* fprintf(stderr, "com_addop_name(%s, %d, %s)\n",
+ c->c_name, op, name);
+*/
+ if (com_mangle(c, name, buffer, sizeof(buffer)))
name = buffer;
-#endif
if (name == NULL || (v = PyString_InternFromString(name)) == NULL) {
c->c_errors++;
i = 255;
@@ -730,19 +866,98 @@
i = com_addname(c, v);
Py_DECREF(v);
}
- /* Hack to replace *_NAME opcodes by *_GLOBAL if necessary */
- switch (op) {
- case LOAD_NAME:
- case STORE_NAME:
- case DELETE_NAME:
- if (PyDict_GetItemString(c->c_globals, name) != NULL) {
- switch (op) {
- case LOAD_NAME: op = LOAD_GLOBAL; break;
- case STORE_NAME: op = STORE_GLOBAL; break;
- case DELETE_NAME: op = DELETE_GLOBAL; break;
- }
- }
+ com_addoparg(c, op, i);
+}
+
+static void
+com_addop_varname(struct compiling *c, int kind, char *name)
+{
+ PyObject *v;
+ int i;
+ int scope;
+ int op = STOP_CODE;
+ char buffer[MANGLE_LEN];
+
+ if (com_mangle(c, name, buffer, sizeof(buffer)))
+ name = buffer;
+ if (name == NULL || (v = PyString_InternFromString(name)) == NULL) {
+ c->c_errors++;
+ i = 255;
+ goto done;
}
+ scope = NAME_DEFAULT;
+ if (c->c_symtable->st_cur_type == TYPE_FUNCTION && is_local(c, name)) {
+ scope = NAME_LOCAL;
+ } else {
+ int g = is_global(c, name);
+ if ((g & EXPLICIT_GLOBAL)
+ || ((c->c_flags & CO_OPTIMIZED) && g))
+ scope = NAME_GLOBAL;
+ }
+
+ /* XXX Test can probably be eliminated once we reach 2.1 beta 1 */
+ if (!(is_local(c, name) || is_global(c, name))) {
+ fprintf(stderr, "name: %s", name);
+ fprintf(stderr, ", in %s, file '%s', line %d\n",
+ c->c_name, c->c_filename, c->c_lineno);
+ fprintf(stderr, "locals: %s\n",
+ PyString_AS_STRING(PyObject_Repr(c->c_locals)));
+ fprintf(stderr, "globals: %s\n",
+ PyString_AS_STRING(PyObject_Repr(c->c_globals)));
+ Py_FatalError("compiler did not label name as local or global");
+ }
+
+ i = com_addname(c, v);
+ if (scope == NAME_LOCAL) {
+ PyObject *w = PyDict_GetItem(c->c_locals, v);
+ if (w == NULL) {
+ c->c_errors++;
+ i = 255;
+ goto done;
+ } else
+ i = PyInt_AsLong(w);
+ }
+ Py_DECREF(v);
+
+ switch (kind) {
+ case VAR_LOAD:
+ switch (scope) {
+ case NAME_LOCAL:
+ op = LOAD_FAST;
+ break;
+ case NAME_GLOBAL:
+ op = LOAD_GLOBAL;
+ break;
+ case NAME_DEFAULT:
+ op = LOAD_NAME;
+ }
+ break;
+ case VAR_STORE:
+ switch (scope) {
+ case NAME_LOCAL:
+ op = STORE_FAST;
+ break;
+ case NAME_GLOBAL:
+ op = STORE_GLOBAL;
+ break;
+ case NAME_DEFAULT:
+ op = STORE_NAME;
+ }
+ break;
+ case VAR_DELETE:
+ switch (scope) {
+ case NAME_LOCAL:
+ op = DELETE_FAST;
+ break;
+ case NAME_GLOBAL:
+ op = DELETE_GLOBAL;
+ break;
+ case NAME_DEFAULT:
+ op = DELETE_NAME;
+ }
+ break;
+ }
+done:
com_addoparg(c, op, i);
}
@@ -778,7 +993,7 @@
REQ(n, NAME);
name = STR(n);
}
- com_addopnamestr(c, op, name);
+ com_addop_name(c, op, name);
}
static PyObject *
@@ -1062,7 +1277,7 @@
}
}
else {
- com_addopnamestr(c, LOAD_NAME, t);
+ com_addop_varname(c, VAR_LOAD, t);
com_push(c, 1);
com_node(c, e);
com_addoparg(c, CALL_FUNCTION, 1);
@@ -1076,15 +1291,15 @@
{
/* listmaker: test list_for */
char tmpname[12];
- sprintf(tmpname, "__%d__", ++c->c_tmpname);
+ sprintf(tmpname, "[%d]", ++c->c_tmpname);
com_addoparg(c, BUILD_LIST, 0);
com_addbyte(c, DUP_TOP); /* leave the result on the stack */
com_push(c, 2);
- com_addopnamestr(c, LOAD_ATTR, "append");
- com_addopnamestr(c, STORE_NAME, tmpname);
+ com_addop_name(c, LOAD_ATTR, "append");
+ com_addop_varname(c, VAR_STORE, tmpname);
com_pop(c, 1);
com_list_for(c, CHILD(n, 1), CHILD(n, 0), tmpname);
- com_addopnamestr(c, DELETE_NAME, tmpname);
+ com_addop_varname(c, VAR_DELETE, tmpname);
--c->c_tmpname;
}
@@ -1182,7 +1397,7 @@
com_push(c, 1);
break;
case NAME:
- com_addopname(c, LOAD_NAME, ch);
+ com_addop_varname(c, VAR_LOAD, STR(ch));
com_push(c, 1);
break;
default:
@@ -1871,7 +2086,9 @@
PyObject *v;
int i;
int ndefs = com_argdefs(c, CHILD(n, 0));
+ symtable_enter_scope(c->c_symtable, "lambda", lambdef);
v = (PyObject *) icompile(CHILD(n, 0), c);
+ symtable_exit_scope(c->c_symtable);
if (v == NULL) {
c->c_errors++;
i = 255;
@@ -1986,7 +2203,7 @@
com_augassign_name(struct compiling *c, node *n, int opcode, node *augn)
{
REQ(n, NAME);
- com_addopname(c, LOAD_NAME, n);
+ com_addop_varname(c, VAR_LOAD, STR(n));
com_push(c, 1);
com_node(c, augn);
com_addbyte(c, opcode);
@@ -1998,7 +2215,7 @@
com_assign_name(struct compiling *c, node *n, int assigning)
{
REQ(n, NAME);
- com_addopname(c, assigning ? STORE_NAME : DELETE_NAME, n);
+ com_addop_varname(c, assigning ? VAR_STORE : VAR_DELETE, STR(n));
if (assigning)
com_pop(c, 1);
}
@@ -2080,7 +2297,7 @@
}
if (assigning > OP_APPLY) {
com_error(c, PyExc_SyntaxError,
- "augmented assign to tuple not possible");
+ "augmented assign to tuple not possible");
return;
}
break;
@@ -2093,7 +2310,13 @@
}
if (assigning > OP_APPLY) {
com_error(c, PyExc_SyntaxError,
- "augmented assign to list not possible");
+ "augmented assign to list not possible");
+ return;
+ }
+ if (NCH(n) > 1
+ && TYPE(CHILD(n, 1)) == list_for) {
+ com_error(c, PyExc_SyntaxError,
+ "can't assign to list comprehension");
return;
}
com_assign_sequence(c, n, assigning);
@@ -2201,9 +2424,14 @@
where <message> is the second test, if present.
*/
+
+ /* XXX should __debug__ and AssertionError get inserted into
+ the symbol table? they don't follow the normal rules
+ because they are always loaded as globals */
+
if (Py_OptimizeFlag)
return;
- com_addopnamestr(c, LOAD_GLOBAL, "__debug__");
+ com_addop_name(c, LOAD_GLOBAL, "__debug__");
com_push(c, 1);
com_addfwref(c, JUMP_IF_FALSE, &a);
com_addbyte(c, POP_TOP);
@@ -2213,7 +2441,7 @@
com_addbyte(c, POP_TOP);
com_pop(c, 1);
/* Raise that exception! */
- com_addopnamestr(c, LOAD_GLOBAL, "AssertionError");
+ com_addop_name(c, LOAD_GLOBAL, "AssertionError");
com_push(c, 1);
i = NCH(n)/2; /* Either 2 or 4 */
if (i > 1)
@@ -2332,9 +2560,9 @@
com_error(c, PyExc_SyntaxError, "invalid syntax");
return;
}
- com_addopname(c, STORE_NAME, CHILD(n, 2));
+ com_addop_varname(c, VAR_STORE, STR(CHILD(n, 2)));
} else
- com_addopname(c, STORE_NAME, CHILD(n, 0));
+ com_addop_varname(c, VAR_STORE, STR(CHILD(n, 0)));
com_pop(c, 1);
}
@@ -2390,88 +2618,20 @@
}
for (j=2 ; j < NCH(CHILD(subn, 0)); j += 2)
com_addopname(c, LOAD_ATTR,
- CHILD(CHILD(subn, 0), j));
- com_addopname(c, STORE_NAME, CHILD(subn, 2));
+ CHILD(CHILD(subn, 0),
+ j));
+ com_addop_varname(c, VAR_STORE,
+ STR(CHILD(subn, 2)));
} else
- com_addopname(c, STORE_NAME,
- CHILD(CHILD(subn, 0),0));
+ com_addop_varname(c, VAR_STORE,
+ STR(CHILD(CHILD(subn, 0),
+ 0)));
com_pop(c, 1);
}
}
}
static void
-com_global_stmt(struct compiling *c, node *n)
-{
- int i;
- REQ(n, global_stmt);
- /* 'global' NAME (',' NAME)* */
- for (i = 1; i < NCH(n); i += 2) {
- char *s = STR(CHILD(n, i));
-#ifdef PRIVATE_NAME_MANGLING
- char buffer[256];
- if (s != NULL && s[0] == '_' && s[1] == '_' &&
- c->c_private != NULL &&
- com_mangle(c, s, buffer, sizeof(buffer)))
- s = buffer;
-#endif
- if (PyDict_GetItemString(c->c_locals, s) != NULL) {
- com_error(c, PyExc_SyntaxError,
- "name is local and global");
- }
- else if (PyDict_SetItemString(c->c_globals, s, Py_None) != 0)
- c->c_errors++;
- }
-}
-
-static int
-com_newlocal_o(struct compiling *c, PyObject *nameval)
-{
- int i;
- PyObject *ival;
- if (PyList_Size(c->c_varnames) != c->c_nlocals) {
- /* This is usually caused by an error on a previous call */
- if (c->c_errors == 0) {
- com_error(c, PyExc_SystemError,
- "mixed up var name/index");
- }
- return 0;
- }
- ival = PyInt_FromLong(i = c->c_nlocals++);
- if (ival == NULL)
- c->c_errors++;
- else if (PyDict_SetItem(c->c_locals, nameval, ival) != 0)
- c->c_errors++;
- else if (PyList_Append(c->c_varnames, nameval) != 0)
- c->c_errors++;
- Py_XDECREF(ival);
- return i;
-}
-
-static int
-com_addlocal_o(struct compiling *c, PyObject *nameval)
-{
- PyObject *ival = PyDict_GetItem(c->c_locals, nameval);
- if (ival != NULL)
- return PyInt_AsLong(ival);
- return com_newlocal_o(c, nameval);
-}
-
-static int
-com_newlocal(struct compiling *c, char *name)
-{
- PyObject *nameval = PyString_InternFromString(name);
- int i;
- if (nameval == NULL) {
- c->c_errors++;
- return 0;
- }
- i = com_newlocal_o(c, nameval);
- Py_DECREF(nameval);
- return i;
-}
-
-static void
com_exec_stmt(struct compiling *c, node *n)
{
REQ(n, exec_stmt);
@@ -2498,6 +2658,7 @@
{
PyObject *v;
int i;
+ /* argument c will be NULL when called from symtable_node() */
/* Label to avoid tail recursion */
next:
@@ -3031,18 +3192,21 @@
com_funcdef(struct compiling *c, node *n)
{
PyObject *v;
+ int ndefs;
REQ(n, funcdef); /* funcdef: 'def' NAME parameters ':' suite */
+ ndefs = com_argdefs(c, n);
+ symtable_enter_scope(c->c_symtable, STR(CHILD(n, 1)), TYPE(n));
v = (PyObject *)icompile(n, c);
+ symtable_exit_scope(c->c_symtable);
if (v == NULL)
c->c_errors++;
else {
int i = com_addconst(c, v);
- int ndefs = com_argdefs(c, n);
com_addoparg(c, LOAD_CONST, i);
com_push(c, 1);
com_addoparg(c, MAKE_FUNCTION, ndefs);
com_pop(c, ndefs);
- com_addopname(c, STORE_NAME, CHILD(n, 1));
+ com_addop_varname(c, VAR_STORE, STR(CHILD(n, 1)));
com_pop(c, 1);
Py_DECREF(v);
}
@@ -3066,6 +3230,8 @@
{
int i;
PyObject *v;
+ char *name;
+
REQ(n, classdef);
/* classdef: class NAME ['(' testlist ')'] ':' suite */
if ((v = PyString_InternFromString(STR(CHILD(n, 1)))) == NULL) {
@@ -3084,7 +3250,10 @@
}
else
com_bases(c, CHILD(n, 3));
+ name = STR(CHILD(n, 1));
+ symtable_enter_scope(c->c_symtable, name, TYPE(n));
v = (PyObject *)icompile(n, c);
+ symtable_exit_scope(c->c_symtable);
if (v == NULL)
c->c_errors++;
else {
@@ -3095,7 +3264,7 @@
com_addoparg(c, CALL_FUNCTION, 0);
com_addbyte(c, BUILD_CLASS);
com_pop(c, 2);
- com_addopname(c, STORE_NAME, CHILD(n, 1));
+ com_addop_varname(c, VAR_STORE, STR(CHILD(n, 1)));
Py_DECREF(v);
}
}
@@ -3103,6 +3272,7 @@
static void
com_node(struct compiling *c, node *n)
{
+ loop:
switch (TYPE(n)) {
/* Definition nodes */
@@ -3119,8 +3289,8 @@
case stmt:
case small_stmt:
case flow_stmt:
- com_node(c, CHILD(n, 0));
- break;
+ n = CHILD(n, 0);
+ goto loop;
case simple_stmt:
/* small_stmt (';' small_stmt)* [';'] NEWLINE */
@@ -3134,8 +3304,8 @@
case compound_stmt:
com_addoparg(c, SET_LINENO, n->n_lineno);
- com_node(c, CHILD(n, 0));
- break;
+ n = CHILD(n, 0);
+ goto loop;
/* Statement nodes */
@@ -3170,7 +3340,6 @@
com_import_stmt(c, n);
break;
case global_stmt:
- com_global_stmt(c, n);
break;
case exec_stmt:
com_exec_stmt(c, n);
@@ -3258,7 +3427,7 @@
if (TYPE(CHILD(n, 0)) == LPAR)
com_fplist(c, CHILD(n, 1));
else {
- com_addoparg(c, STORE_FAST, com_newlocal(c, STR(CHILD(n, 0))));
+ com_addop_varname(c, VAR_STORE, STR(CHILD(n, 0)));
com_pop(c, 1);
}
}
@@ -3279,6 +3448,10 @@
}
}
+/* XXX This function could probably be made simpler, because it
+ doesn't do anything except generate code for complex arguments.
+*/
+
static void
com_arglist(struct compiling *c, node *n)
{
@@ -3294,7 +3467,6 @@
node *ch = CHILD(n, i);
node *fp;
char *name;
- PyObject *nameval;
if (TYPE(ch) == STAR || TYPE(ch) == DOUBLESTAR)
break;
REQ(ch, fpdef); /* fpdef: NAME | '(' fplist ')' */
@@ -3306,17 +3478,7 @@
sprintf(nbuf, ".%d", i);
complex = 1;
}
- nameval = PyString_InternFromString(name);
- if (nameval == NULL) {
- c->c_errors++;
- }
- if (PyDict_GetItem(c->c_locals, nameval)) {
- com_error(c, PyExc_SyntaxError,
- "duplicate argument in function definition");
- }
- com_newlocal_o(c, nameval);
- Py_DECREF(nameval);
- c->c_argcount++;
+ /* all name updates handled by symtable */
if (++i >= nch)
break;
ch = CHILD(n, i);
@@ -3333,9 +3495,7 @@
REQ(ch, STAR);
ch = CHILD(n, i+1);
if (TYPE(ch) == NAME) {
- c->c_flags |= CO_VARARGS;
i += 3;
- com_newlocal(c, STR(ch));
}
}
}
@@ -3352,8 +3512,6 @@
else
ch = CHILD(n, i+1);
REQ(ch, NAME);
- c->c_flags |= CO_VARKEYWORDS;
- com_newlocal(c, STR(ch));
}
if (complex) {
/* Generate code for complex arguments only after
@@ -3395,7 +3553,7 @@
Py_DECREF(doc);
com_addoparg(c, LOAD_CONST, i);
com_push(c, 1);
- com_addopnamestr(c, STORE_NAME, "__doc__");
+ com_addop_name(c, STORE_NAME, "__doc__");
com_pop(c, 1);
}
for (i = 0; i < NCH(n); i++) {
@@ -3462,9 +3620,7 @@
REQ(n, classdef);
/* classdef: 'class' NAME ['(' testlist ')'] ':' suite */
c->c_name = STR(CHILD(n, 1));
-#ifdef PRIVATE_NAME_MANGLING
c->c_private = c->c_name;
-#endif
ch = CHILD(n, NCH(n)-1); /* The suite */
doc = get_docstring(ch);
if (doc != NULL) {
@@ -3472,7 +3628,7 @@
Py_DECREF(doc);
com_addoparg(c, LOAD_CONST, i);
com_push(c, 1);
- com_addopnamestr(c, STORE_NAME, "__doc__");
+ com_addop_name(c, STORE_NAME, "__doc__");
com_pop(c, 1);
}
else
@@ -3537,122 +3693,6 @@
}
}
-/* Optimization for local variables in functions (and *only* functions).
-
- This replaces all LOAD_NAME, STORE_NAME and DELETE_NAME
- instructions that refer to local variables with LOAD_FAST etc.
- The latter instructions are much faster because they don't need to
- look up the variable name in a dictionary.
-
- To find all local variables, we check all STORE_NAME, IMPORT_FROM
- and DELETE_NAME instructions. This yields all local variables,
- function definitions, class definitions and import statements.
- Argument names have already been entered into the list by the
- special processing for the argument list.
-
- All remaining LOAD_NAME instructions must refer to non-local (global
- or builtin) variables, so are replaced by LOAD_GLOBAL.
-
- There are two problems: 'from foo import *' and 'exec' may introduce
- local variables that we can't know while compiling. If this is the
- case, we can still optimize bona fide locals (since those
- statements will be surrounded by fast_2_locals() and
- locals_2_fast()), but we can't change LOAD_NAME to LOAD_GLOBAL.
-
- NB: this modifies the string object c->c_code! */
-
-static void
-optimize(struct compiling *c)
-{
- unsigned char *next_instr, *cur_instr;
- int opcode;
- int oparg = 0;
- PyObject *name;
- PyObject *error_type, *error_value, *error_traceback;
-
-#define NEXTOP() (*next_instr++)
-#define NEXTARG() (next_instr += 2, (next_instr[-1]<<8) + next_instr[-2])
-#define GETITEM(v, i) (PyList_GetItem((v), (i)))
-#define GETNAMEOBJ(i) (GETITEM(c->c_names, (i)))
-
- PyErr_Fetch(&error_type, &error_value, &error_traceback);
-
- c->c_flags |= CO_OPTIMIZED;
-
- next_instr = (unsigned char *) PyString_AsString(c->c_code);
- for (;;) {
- opcode = NEXTOP();
- if (opcode == STOP_CODE)
- break;
- if (HAS_ARG(opcode))
- oparg = NEXTARG();
- dispatch_opcode1:
- switch (opcode) {
- case STORE_NAME:
- case DELETE_NAME:
- case IMPORT_FROM:
- com_addlocal_o(c, GETNAMEOBJ(oparg));
- break;
- case IMPORT_STAR:
- case EXEC_STMT:
- c->c_flags &= ~CO_OPTIMIZED;
- break;
- case EXTENDED_ARG:
- opcode = NEXTOP();
- oparg = oparg<<16 | NEXTARG();
- goto dispatch_opcode1;
- break;
- }
- }
-
- /* TBD: Is this still necessary ? */
- if (PyDict_GetItemString(c->c_locals, "*") != NULL)
- c->c_flags &= ~CO_OPTIMIZED;
-
- next_instr = (unsigned char *) PyString_AsString(c->c_code);
- for (;;) {
- cur_instr = next_instr;
- opcode = NEXTOP();
- if (opcode == STOP_CODE)
- break;
- if (HAS_ARG(opcode))
- oparg = NEXTARG();
- dispatch_opcode2:
- if (opcode == LOAD_NAME ||
- opcode == STORE_NAME ||
- opcode == DELETE_NAME) {
- PyObject *v;
- int i;
- name = GETNAMEOBJ(oparg);
- v = PyDict_GetItem(c->c_locals, name);
- if (v == NULL) {
- if (opcode == LOAD_NAME &&
- (c->c_flags&CO_OPTIMIZED))
- cur_instr[0] = LOAD_GLOBAL;
- continue;
- }
- i = PyInt_AsLong(v);
- if (i >> 16) /* too big for 2 bytes */
- continue;
- switch (opcode) {
- case LOAD_NAME: cur_instr[0] = LOAD_FAST; break;
- case STORE_NAME: cur_instr[0] = STORE_FAST; break;
- case DELETE_NAME: cur_instr[0] = DELETE_FAST; break;
- }
- cur_instr[1] = i & 0xff;
- cur_instr[2] = i >> 8;
- }
- if (opcode == EXTENDED_ARG) {
- opcode = NEXTOP();
- oparg = oparg<<16 | NEXTARG();
- goto dispatch_opcode2;
- }
- }
-
- if (c->c_errors == 0)
- PyErr_Restore(error_type, error_value, error_traceback);
-}
-
PyCodeObject *
PyNode_Compile(node *n, char *filename)
{
@@ -3672,21 +3712,21 @@
PyCodeObject *co;
if (!com_init(&sc, filename))
return NULL;
-#ifdef PRIVATE_NAME_MANGLING
- if (base)
+ if (base) {
sc.c_private = base->c_private;
- else
+ sc.c_symtable = base->c_symtable;
+ } else {
sc.c_private = NULL;
-#endif
+ if (symtable_build(&sc, n) < 0) {
+ com_free(&sc);
+ return NULL;
+ }
+ }
+ co = NULL;
+ if (symtable_load_symbols(&sc) < 0)
+ goto exit;
compile_node(&sc, n);
com_done(&sc);
- if ((TYPE(n) == funcdef || TYPE(n) == lambdef) && sc.c_errors == 0) {
- optimize(&sc);
- sc.c_flags |= CO_NEWLOCALS;
- }
- else if (TYPE(n) == classdef)
- sc.c_flags |= CO_NEWLOCALS;
- co = NULL;
if (sc.c_errors == 0) {
PyObject *consts, *names, *varnames, *filename, *name;
consts = PyList_AsTuple(sc.c_consts);
@@ -3721,6 +3761,9 @@
it gets reported instead dumping core. */
PyErr_SetString(PyExc_SystemError, "lost syntax error");
}
+ exit:
+ if (base == NULL)
+ symtable_free(sc.c_symtable);
com_free(&sc);
return co;
}
@@ -3740,3 +3783,712 @@
}
return line;
}
+
+static int
+is_local(struct compiling *c, char *name)
+{
+ if (PyDict_GetItemString(c->c_locals, name) != NULL)
+ return 1;
+ else
+ return 0;
+}
+
+static int
+is_global(struct compiling *c, char *name)
+{
+ PyObject *v;
+ v = PyDict_GetItemString(c->c_globals, name);
+ if (v == NULL)
+ return 0;
+ else if (v == Py_None)
+ return IMPLICIT_GLOBAL;
+ else
+ return EXPLICIT_GLOBAL;
+}
+
+static int
+symtable_build(struct compiling *c, node *n)
+{
+ if ((c->c_symtable = symtable_init()) == NULL)
+ return -1;
+ if (symtable_enter_scope(c->c_symtable, GLOBAL, TYPE(n)) < 0)
+ return -1;
+ symtable_node(c->c_symtable, n);
+ /* reset for second pass */
+ c->c_symtable->st_scopes = 1;
+ return 0;
+}
+
+static int
+symtable_load_symbols(struct compiling *c)
+{
+ static PyObject *global = NULL;
+ PyObject *name, *v, *varnames;
+ int i, info, count, pos;
+ struct symtable *st = c->c_symtable;
+
+ varnames = PyDict_GetItem(st->st_varnames, st->st_cur_id);
+ if (varnames == NULL) {
+ varnames = PyList_New(0);
+ if (varnames == NULL)
+ return -1;
+ } else
+ Py_INCREF(varnames);
+
+ c->c_varnames = varnames;
+ count = PyList_GET_SIZE(varnames);
+ c->c_argcount = count;
+ for (i = 0; i < count; ++i) {
+ v = PyInt_FromLong(i);
+ if (PyDict_SetItem(c->c_locals,
+ PyList_GET_ITEM(varnames, i), v) < 0)
+ return -1;
+ }
+
+ /* XXX The cases below define the rules for whether a name is
+ local or global. The logic could probably be clearer. */
+ pos = 0;
+ while (PyDict_Next(st->st_cur, &pos, &name, &v)) {
+ info = PyInt_AS_LONG(v);
+ if (info & DEF_STAR) {
+ c->c_argcount--;
+ c->c_flags |= CO_VARARGS;
+ } else if (info & DEF_DOUBLESTAR) {
+ c->c_argcount--;
+ c->c_flags |= CO_VARKEYWORDS;
+ } else if (info & DEF_INTUPLE)
+ c->c_argcount--;
+ else if (info & DEF_GLOBAL) {
+ if ((info & DEF_PARAM)
+ && (PyString_AS_STRING(name)[0] != '.')){
+ char buf[500];
+ sprintf(buf,
+ "name '%.400s' is local and global",
+ PyString_AS_STRING(name));
+ com_error(c, PyExc_SyntaxError, buf);
+ return -1;
+ }
+ if (global == NULL) {
+ global = PyInt_FromLong(1);
+ if (global == NULL) {
+ return -1;
+ }
+ }
+ if (PyDict_SetItem(c->c_globals, name, global) < 0)
+ return -1;
+ } else if ((info & USE) && !(info & (DEF_LOCAL | DEF_PARAM))) {
+ if (PyDict_SetItem(c->c_globals, name,
+ Py_None) < 0)
+ return -1;
+ } else if ((info & DEF_LOCAL) && !(info & DEF_PARAM)) {
+ v = PyInt_FromLong(count++);
+ if (PyDict_SetItem(c->c_locals, name, v) < 0)
+ return -1;
+ if (st->st_cur_type != TYPE_CLASS)
+ if (PyList_Append(c->c_varnames, name) < 0)
+ return -1;
+ }
+ }
+
+ if (st->st_cur_type == TYPE_FUNCTION)
+ c->c_nlocals = count;
+
+ if (st->st_cur_type != TYPE_MODULE)
+ c->c_flags |= CO_NEWLOCALS;
+ if ((st->st_cur_type == TYPE_FUNCTION)
+ && (PyDict_GetItemString(st->st_cur, NOOPT) == NULL))
+ c->c_flags |= CO_OPTIMIZED;
+
+ return 0;
+}
+
+static struct symtable *
+symtable_init()
+{
+ struct symtable *st;
+ PyObject *d;
+
+ st = (struct symtable *)PyMem_Malloc(sizeof(struct symtable));
+ if (st == NULL)
+ return NULL;
+ if ((st->st_namespaces = PyList_New(0)) == NULL)
+ goto fail;
+ if ((st->st_types = PyList_New(0)) == NULL)
+ goto fail;
+ if ((st->st_symbols = PyDict_New()) == NULL)
+ goto fail;
+ if ((st->st_varnames = PyDict_New()) == NULL)
+ goto fail;
+ if ((d = PyDict_New()) == NULL)
+ goto fail;
+ if (PyDict_SetItemString(st->st_symbols, GLOBAL, d) < 0)
+ goto fail;
+ st->st_global = d;
+ st->st_cur = NULL;
+ st->st_cur_id = NULL;
+ st->st_cur_type = 0;
+ st->st_scopes = 0;
+ st->st_errors = 0;
+ st->st_tmpname = 0;
+ st->st_private = NULL;
+ return st;
+ fail:
+ symtable_free(st);
+ return NULL;
+}
+
+static void
+symtable_free(struct symtable *st)
+{
+ Py_XDECREF(st->st_symbols);
+ Py_XDECREF(st->st_varnames);
+ Py_XDECREF(st->st_namespaces);
+ Py_XDECREF(st->st_types);
+ Py_XDECREF(st->st_cur_id);
+ PyMem_Free((void *)st);
+}
+
+/* XXX name isn't used ... */
+
+static int
+symtable_enter_scope(struct symtable *st, char *name, int type)
+{
+ PyObject *o;
+
+ o = PyInt_FromLong(st->st_scopes++);
+ if (o == NULL)
+ return -1;
+ if (PyList_Append(st->st_namespaces, o) < 0)
+ return -1;
+ switch (type) {
+ case funcdef:
+ case lambdef:
+ st->st_cur_type = TYPE_FUNCTION;
+ break;
+ case classdef:
+ st->st_cur_type = TYPE_CLASS;
+ break;
+ case single_input:
+ case eval_input:
+ case file_input:
+ st->st_cur_type = TYPE_MODULE;
+ break;
+ default:
+ fprintf(stderr, "invalid symtable scope: %d\n", type);
+ }
+ o = PyInt_FromLong(st->st_cur_type);
+ if (o == NULL)
+ return -1;
+ if (PyList_Append(st->st_types, o) < 0)
+ return -1;
+ return symtable_update_cur(st);
+}
+
+static int
+symtable_exit_scope(struct symtable *st)
+{
+ PyObject *o;
+ int end;
+
+ end = PyList_GET_SIZE(st->st_namespaces) - 1;
+ if (PySequence_DelItem(st->st_namespaces, end) < 0)
+ return -1;
+ if (PySequence_DelItem(st->st_types, end) < 0)
+ return -1;
+ o = PyList_GET_ITEM(st->st_types, end - 1);
+ st->st_cur_type = PyInt_AS_LONG(o);
+ return symtable_update_cur(st);
+}
+
+static int
+symtable_update_cur(struct symtable *st)
+{
+ PyObject *s, *d, *l;
+ int end;
+
+ end = PyList_GET_SIZE(st->st_namespaces) - 1;
+ s = PyList_GET_ITEM(st->st_namespaces, end);
+ st->st_cur_id = s;
+ d = PyDict_GetItem(st->st_symbols, s);
+ if (d == NULL) {
+ if ((d = PyDict_New()) == NULL)
+ return -1;
+ if (PyObject_SetItem(st->st_symbols, s, d) < 0) {
+ Py_DECREF(d);
+ return -1;
+ }
+ if (st->st_cur_type == TYPE_FUNCTION) {
+ if ((l = PyList_New(0)) == NULL)
+ return -1;
+ if (PyDict_SetItem(st->st_varnames, s, l) < 0)
+ return -1;
+ }
+ }
+
+ st->st_cur = d;
+ return 0;
+}
+
+static int
+symtable_mangle(struct symtable *st, char *name, char *buffer, size_t maxlen)
+{
+ return mangle(st->st_private, name, buffer, maxlen);
+}
+
+static int
+symtable_add_def(struct symtable *st, char *name, int scope)
+{
+ PyObject *s, *o;
+ int val;
+ char buffer[MANGLE_LEN];
+
+ if (symtable_mangle(st, name, buffer, sizeof(buffer)))
+ name = buffer;
+ if ((s = PyString_InternFromString(name)) == NULL)
+ return -1;
+ if ((o = PyDict_GetItem(st->st_cur, s))) {
+ val = PyInt_AS_LONG(o);
+ if ((scope & DEF_PARAM) && (val & DEF_PARAM)) {
+ PyErr_Format(PyExc_SyntaxError,
+ "duplicate argument '%s' in function definition",
+ name);
+ return -1;
+ }
+ val |= scope;
+ } else
+ val = scope;
+ o = PyInt_FromLong(val);
+ if (PyDict_SetItem(st->st_cur, s, o) < 0) {
+ Py_DECREF(o);
+ return -1;
+ }
+ Py_DECREF(o);
+
+ if (scope & DEF_PARAM) {
+ PyObject *l = PyDict_GetItem(st->st_varnames,
+ st->st_cur_id);
+ if (l == NULL)
+ return -1;
+ if (PyList_Append(l, s) < 0)
+ return -1;
+ } else if (scope & DEF_GLOBAL) {
+ if ((o = PyDict_GetItem(st->st_global, s))) {
+ val = PyInt_AS_LONG(o);
+ val |= scope;
+ } else
+ val = scope;
+ o = PyInt_FromLong(val);
+ if (PyDict_SetItem(st->st_global, s, o) < 0) {
+ Py_DECREF(o);
+ return -1;
+ }
+ Py_DECREF(o);
+ }
+ return 0;
+}
+
+static int
+symtable_add_use(struct symtable *st, char *name)
+{
+ PyObject *s, *o;
+ int val;
+ char buffer[MANGLE_LEN];
+
+ if (symtable_mangle(st, name, buffer, sizeof(buffer)))
+ name = buffer;
+/* fprintf(stderr, "add_use(%s)\n", name); */
+ if ((s = PyString_InternFromString(name)) == NULL)
+ return -1;
+ if ((o = PyDict_GetItem(st->st_cur, s))) {
+ val = PyInt_AS_LONG(o);
+ val |= USE;
+ } else
+ val = USE;
+ o = PyInt_FromLong(val);
+ if (PyDict_SetItem(st->st_cur, s, o) < 0) {
+ Py_DECREF(o);
+ return -1;
+ }
+ Py_DECREF(o);
+ return 0;
+}
+
+static void
+symtable_node(struct symtable *st, node *n)
+{
+ int i, start = 0;
+
+ loop:
+ switch (TYPE(n)) {
+ case funcdef: {
+ char *func_name = STR(CHILD(n, 1));
+ symtable_add_def(st, func_name, DEF_LOCAL);
+ symtable_default_args(st, CHILD(n, 2));
+ symtable_enter_scope(st, func_name, TYPE(n));
+ symtable_funcdef(st, n);
+ symtable_exit_scope(st);
+ break;
+ }
+ case lambdef:
+ if (NCH(n) == 4)
+ symtable_default_args(st, CHILD(n, 1));
+ symtable_enter_scope(st, "lambda", TYPE(n));
+ symtable_funcdef(st, n);
+ symtable_exit_scope(st);
+ break;
+ case classdef: {
+ char *tmp, *class_name = STR(CHILD(n, 1));
+ symtable_add_def(st, class_name, DEF_LOCAL);
+ if (TYPE(CHILD(n, 2)) == LPAR) {
+ node *bases = CHILD(n, 3);
+ int i;
+ for (i = 0; i < NCH(bases); i += 2) {
+ symtable_node(st, CHILD(bases, i));
+ }
+ }
+ symtable_enter_scope(st, class_name, TYPE(n));
+ tmp = st->st_private;
+ st->st_private = class_name;
+ symtable_node(st, CHILD(n, NCH(n) - 1));
+ st->st_private = tmp;
+ symtable_exit_scope(st);
+ break;
+ }
+ case if_stmt:
+ for (i = 0; i + 3 < NCH(n); i += 4) {
+ if (is_constant_false(NULL, (CHILD(n, i + 1))))
+ continue;
+ symtable_node(st, CHILD(n, i + 1));
+ symtable_node(st, CHILD(n, i + 3));
+ }
+ if (i + 2 < NCH(n))
+ symtable_node(st, CHILD(n, i + 2));
+ break;
+ case global_stmt:
+ symtable_global(st, n);
+ break;
+ case import_stmt:
+ symtable_import(st, n);
+ break;
+ case exec_stmt:
+ if (PyDict_SetItemString(st->st_cur, NOOPT, Py_None) < 0)
+ st->st_errors++;
+ symtable_node(st, CHILD(n, 1));
+ if (NCH(n) > 2)
+ symtable_node(st, CHILD(n, 3));
+ if (NCH(n) > 4)
+ symtable_node(st, CHILD(n, 5));
+ break;
+ case except_clause:
+ if (NCH(n) == 4)
+ symtable_assign(st, CHILD(n, 3));
+ if (NCH(n) > 1) {
+ n = CHILD(n, 1);
+ goto loop;
+ }
+ break;
+ case del_stmt:
+ symtable_assign(st, CHILD(n, 1));
+ break;
+ case expr_stmt:
+ if (NCH(n) == 1)
+ n = CHILD(n, 0);
+ else {
+ if (TYPE(CHILD(n, 1)) == augassign) {
+ symtable_assign(st, CHILD(n, 0));
+ symtable_node(st, CHILD(n, 2));
+ break;
+ } else {
+ int i;
+ for (i = 0; i < NCH(n) - 2; i += 2)
+ symtable_assign(st, CHILD(n, i));
+ n = CHILD(n, NCH(n) - 1);
+ }
+ }
+ goto loop;
+ /* watchout for fall-through logic below */
+ case listmaker:
+ if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == list_for) {
+ symtable_list_comprehension(st, CHILD(n, 1));
+ break;
+ }
+ case atom:
+ if (TYPE(n) == atom && TYPE(CHILD(n, 0)) == NAME) {
+ symtable_add_use(st, STR(CHILD(n, 0)));
+ break;
+ }
+ case for_stmt:
+ if (TYPE(n) == for_stmt) {
+ symtable_assign(st, CHILD(n, 1));
+ start = 3;
+ }
+ default:
+ if (NCH(n) == 1) {
+ n = CHILD(n, 0);
+ goto loop;
+ }
+ for (i = start; i < NCH(n); ++i)
+ if (TYPE(CHILD(n, i)) >= single_input)
+ symtable_node(st, CHILD(n, i));
+ }
+}
+
+static void
+symtable_funcdef(struct symtable *st, node *n)
+{
+ node *body;
+
+ if (TYPE(n) == lambdef) {
+ if (NCH(n) == 4)
+ symtable_params(st, CHILD(n, 1));
+ } else
+ symtable_params(st, CHILD(n, 2));
+ body = CHILD(n, NCH(n) - 1);
+ symtable_node(st, body);
+}
+
+/* The next two functions parse the argument tuple.
+ symtable_default_arg() checks for names in the default arguments,
+ which are references in the defining scope. symtable_params()
+ parses the parameter names, which are defined in the function's
+ body.
+
+ varargslist:
+ (fpdef ['=' test] ',')* ('*' NAME [',' '**' NAME] | '**' NAME)
+ | fpdef ['=' test] (',' fpdef ['=' test])* [',']
+*/
+
+static void
+symtable_default_args(struct symtable *st, node *n)
+{
+ node *c;
+ int i;
+
+ if (TYPE(n) == parameters) {
+ n = CHILD(n, 1);
+ if (TYPE(n) == RPAR)
+ return;
+ }
+ REQ(n, varargslist);
+ for (i = 0; i < NCH(n); i += 2) {
+ c = CHILD(n, i);
+ if (TYPE(c) == STAR || TYPE(c) == DOUBLESTAR) {
+ break;
+ }
+ if (i > 0 && (TYPE(CHILD(n, i - 1)) == EQUAL))
+ symtable_node(st, CHILD(n, i));
+ }
+}
+
+static void
+symtable_params(struct symtable *st, node *n)
+{
+ int i, complex = 0, ext = 0;
+ node *c = NULL;
+
+ if (TYPE(n) == parameters) {
+ n = CHILD(n, 1);
+ if (TYPE(n) == RPAR)
+ return;
+ }
+ REQ(n, varargslist);
+ for (i = 0; i < NCH(n); i += 2) {
+ c = CHILD(n, i);
+ if (TYPE(c) == STAR || TYPE(c) == DOUBLESTAR) {
+ ext = 1;
+ break;
+ }
+ if (TYPE(c) == test) {
+ continue;
+ }
+ if (TYPE(CHILD(c, 0)) == NAME)
+ symtable_add_def(st, STR(CHILD(c, 0)), DEF_PARAM);
+ else {
+ char nbuf[10];
+ sprintf(nbuf, ".%d", i);
+ symtable_add_def(st, nbuf, DEF_PARAM);
+ complex = 1;
+ }
+ }
+ if (complex) {
+ int j;
+ for (j = 0; j < i; j += 2) {
+ c = CHILD(n, j);
+ if (TYPE(CHILD(c, 0)) == LPAR)
+ symtable_params_fplist(st, CHILD(c, 1));
+ }
+ }
+ if (ext) {
+ c = CHILD(n, i);
+ if (TYPE(c) == STAR) {
+ i++;
+ symtable_add_def(st, STR(CHILD(n, i)),
+ DEF_PARAM | DEF_STAR);
+ i += 2;
+ c = CHILD(n, i);
+ }
+ if (TYPE(c) == DOUBLESTAR) {
+ i++;
+ symtable_add_def(st, STR(CHILD(n, i)),
+ DEF_PARAM | DEF_DOUBLESTAR);
+ }
+ }
+}
+
+static void
+symtable_params_fplist(struct symtable *st, node *n)
+{
+ int i;
+ node *c;
+
+ REQ(n, fplist);
+ for (i = 0; i < NCH(n); i += 2) {
+ c = CHILD(n, i);
+ REQ(c, fpdef);
+ if (NCH(c) == 1)
+ symtable_add_def(st, STR(CHILD(c, 0)),
+ DEF_PARAM | DEF_INTUPLE);
+ else
+ symtable_params_fplist(st, CHILD(c, 1));
+ }
+
+}
+
+static void
+symtable_global(struct symtable *st, node *n)
+{
+ int i;
+
+ for (i = 1; i < NCH(n); i += 2)
+ symtable_add_def(st, STR(CHILD(n, i)), DEF_GLOBAL);
+}
+
+static void
+symtable_list_comprehension(struct symtable *st, node *n)
+{
+ char tmpname[12];
+
+ sprintf(tmpname, "[%d]", ++st->st_tmpname);
+ symtable_add_def(st, tmpname, DEF_LOCAL);
+ symtable_assign(st, CHILD(n, 1));
+ symtable_node(st, CHILD(n, 3));
+ if (NCH(n) == 5)
+ symtable_node(st, CHILD(n, 4));
+ --st->st_tmpname;
+}
+
+static void
+symtable_import(struct symtable *st, node *n)
+{
+ int i;
+ /*
+ import_stmt: 'import' dotted_as_name (',' dotted_as_name)*
+ | 'from' dotted_name 'import'
+ ('*' | import_as_name (',' import_as_name)*)
+ import_as_name: NAME [NAME NAME]
+ */
+
+ if (STR(CHILD(n, 0))[0] == 'f') { /* from */
+ if (TYPE(CHILD(n, 3)) == STAR) {
+ if (PyDict_SetItemString(st->st_cur, NOOPT,
+ Py_None) < 0)
+ st->st_errors++;
+ } else {
+ for (i = 3; i < NCH(n); i += 2) {
+ node *c = CHILD(n, i);
+ if (NCH(c) > 1) /* import as */
+ symtable_assign(st, CHILD(c, 2));
+ else
+ symtable_assign(st, CHILD(c, 0));
+ }
+ }
+ } else {
+ for (i = 1; i < NCH(n); i += 2) {
+ symtable_assign(st, CHILD(n, i));
+ }
+ }
+}
+
+static void
+symtable_assign(struct symtable *st, node *n)
+{
+ node *tmp;
+ int i;
+
+ for (;;) {
+/* fprintf(stderr, "symtable_assign(%d, %d)\n",
+ TYPE(n), NCH(n));
+*/
+
+ switch (TYPE(n)) {
+ case power:
+ /* not sure that NCH(n) > 1 always means that
+ none of the left-hand side names are
+ targets of assignments */
+ if (NCH(n) > 2) {
+ for (i = 2; i < NCH(n); ++i)
+ if (TYPE(CHILD(n, i)) != DOUBLESTAR)
+ symtable_node(st, CHILD(n, i));
+ }
+ if (NCH(n) > 1) {
+ symtable_node(st, CHILD(n, 0));
+ symtable_node(st, CHILD(n, 1));
+ } else {
+ n = CHILD(n, 0);
+ continue;
+ }
+ return;
+ case listmaker:
+ if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == list_for)
+ symtable_list_comprehension(st, CHILD(n, 1));
+ else {
+ for (i = 0; i < NCH(n); i += 2)
+ symtable_assign(st, CHILD(n, i));
+ }
+ return;
+ case exprlist:
+ case testlist:
+ if (NCH(n) == 1) {
+ n = CHILD(n, 0);
+ break;
+ }
+ else {
+ int i;
+ for (i = 0; i < NCH(n); i += 2)
+ symtable_assign(st, CHILD(n, i));
+ return;
+ }
+ break;
+ case atom:
+ tmp = CHILD(n, 0);
+ if (TYPE(tmp) == LPAR || TYPE(tmp) == LSQB) {
+ n = CHILD(n, 1);
+ continue;
+ } else if (TYPE(tmp) == NAME)
+ symtable_add_def(st, STR(tmp), DEF_LOCAL);
+ return;
+ case dotted_as_name:
+ if (NCH(n) == 3)
+ symtable_add_def(st, STR(CHILD(n, 2)),
+ DEF_LOCAL);
+ else
+ symtable_add_def(st,
+ STR(CHILD(CHILD(n,
+ 0), 0)),
+ DEF_LOCAL);
+ return;
+ case dotted_name:
+ symtable_add_def(st, STR(CHILD(n, 0)), DEF_LOCAL);
+ return;
+ case NAME:
+ symtable_add_def(st, STR(n), DEF_LOCAL);
+ return;
+ default:
+ if (NCH(n) == 0)
+ return;
+ assert(NCH(n) == 1);
+ n = CHILD(n, 0);
+ break;
+ }
+ }
+}