diff options
author | Jeremy Hylton <jeremy@alum.mit.edu> | 2001-01-19 03:21:30 (GMT) |
---|---|---|
committer | Jeremy Hylton <jeremy@alum.mit.edu> | 2001-01-19 03:21:30 (GMT) |
commit | e36f77814e83bd2b3dc84a4e0bfb0b8dc0da9965 (patch) | |
tree | 1accc04b93292a6fbef2a2a5629d05521e67ec88 | |
parent | 19fe14e76ac3619e633b10c0e31effc2dad3c543 (diff) | |
download | cpython-e36f77814e83bd2b3dc84a4e0bfb0b8dc0da9965.zip cpython-e36f77814e83bd2b3dc84a4e0bfb0b8dc0da9965.tar.gz cpython-e36f77814e83bd2b3dc84a4e0bfb0b8dc0da9965.tar.bz2 |
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.
-rw-r--r-- | Python/compile.c | 1320 |
1 files changed, 1036 insertions, 284 deletions
diff --git a/Python/compile.c b/Python/compile.c index 734bfcf5..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 @@ int Py_OptimizeFlag = 0; #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 @@ PyCode_New(int argcount, int nlocals, int stacksize, int flags, /* 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 @@ struct compiling { 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 @@ static void 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_free(struct compiling *); 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_addopname(struct compiling *, int, node *); 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 @@ com_init(struct compiling *c, char *filename) 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 @@ com_init(struct compiling *c, char *filename) 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 @@ com_addbyte(struct compiling *c, int byte) { 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 @@ com_add(struct compiling *c, PyObject *list, PyObject *dict, PyObject *v) { PyObject *w, *t, *np=NULL; long n; - + t = Py_BuildValue("(OO)", v, v->ob_type); if (t == NULL) goto fail; @@ -679,20 +812,19 @@ com_addname(struct compiling *c, PyObject *v) 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 @@ com_mangle(struct compiling *c, char *name, char *buffer, size_t maxlen) /* 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 @@ com_addopnamestr(struct compiling *c, int op, char *name) 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 @@ com_addopname(struct compiling *c, int op, node *n) REQ(n, NAME); name = STR(n); } - com_addopnamestr(c, op, name); + com_addop_name(c, op, name); } static PyObject * @@ -1062,7 +1277,7 @@ com_list_iter(struct compiling *c, } } 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 @@ com_list_comprehension(struct compiling *c, node *n) { /* 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_atom(struct compiling *c, node *n) 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 @@ com_test(struct compiling *c, node *n) 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 @@ static void 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 @@ static void 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 @@ com_assign(struct compiling *c, node *n, int assigning, node *augn) } 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 @@ com_assign(struct compiling *c, node *n, int assigning, node *augn) } 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 @@ com_assert_stmt(struct compiling *c, node *n) 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_assert_stmt(struct compiling *c, node *n) 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_from_import(struct compiling *c, node *n) 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 @@ com_import_stmt(struct compiling *c, node *n) } 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 @@ is_constant_false(struct compiling *c, node *n) { PyObject *v; int i; + /* argument c will be NULL when called from symtable_node() */ /* Label to avoid tail recursion */ next: @@ -3031,18 +3192,21 @@ static void 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 @@ com_classdef(struct compiling *c, node *n) { 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 @@ com_classdef(struct compiling *c, node *n) } 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_classdef(struct compiling *c, node *n) 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 @@ com_classdef(struct compiling *c, node *n) static void com_node(struct compiling *c, node *n) { + loop: switch (TYPE(n)) { /* Definition nodes */ @@ -3119,8 +3289,8 @@ com_node(struct compiling *c, node *n) 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 @@ com_node(struct compiling *c, node *n) 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_node(struct compiling *c, node *n) 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 @@ com_fpdef(struct compiling *c, node *n) 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 @@ com_fplist(struct compiling *c, node *n) } } +/* 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 @@ com_arglist(struct compiling *c, node *n) 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 @@ com_arglist(struct compiling *c, node *n) 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 @@ com_arglist(struct compiling *c, node *n) 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 @@ com_arglist(struct compiling *c, node *n) 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 @@ com_file_input(struct compiling *c, node *n) 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 @@ compile_classdef(struct compiling *c, node *n) 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 @@ compile_classdef(struct compiling *c, node *n) 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 @@ compile_node(struct compiling *c, node *n) } } -/* 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 @@ jcompile(node *n, char *filename, struct compiling *base) 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 - 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; + if (symtable_build(&sc, n) < 0) { + com_free(&sc); + return NULL; + } } - else if (TYPE(n) == classdef) - sc.c_flags |= CO_NEWLOCALS; co = NULL; + if (symtable_load_symbols(&sc) < 0) + goto exit; + compile_node(&sc, n); + com_done(&sc); if (sc.c_errors == 0) { PyObject *consts, *names, *varnames, *filename, *name; consts = PyList_AsTuple(sc.c_consts); @@ -3721,6 +3761,9 @@ jcompile(node *n, char *filename, struct compiling *base) 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 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq) } 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; + } + } +} |