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;
+		}
+	}
+}