You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5779 lines
133 KiB
5779 lines
133 KiB
/* Compile an expression node to intermediate code */ |
|
|
|
/* XXX TO DO: |
|
XXX add __doc__ attribute == co_doc to code object attributes? |
|
XXX (it's currently the first item of the co_const tuple) |
|
XXX Generate simple jump for break/return outside 'try...finally' |
|
XXX Allow 'continue' inside finally clause of try-finally |
|
XXX New opcode for loading the initial index for a for loop |
|
XXX other JAR tricks? |
|
*/ |
|
|
|
#include "Python.h" |
|
|
|
#include "node.h" |
|
#include "token.h" |
|
#include "graminit.h" |
|
#include "compile.h" |
|
#include "symtable.h" |
|
#include "opcode.h" |
|
#include "structmember.h" |
|
|
|
#include <ctype.h> |
|
|
|
/* Three symbols from graminit.h are also defined in Python.h, with |
|
Py_ prefixes to their names. Python.h can't include graminit.h |
|
(which defines too many confusing symbols), but we can check here |
|
that they haven't changed (which is very unlikely, but possible). */ |
|
#if Py_single_input != single_input |
|
#error "single_input has changed -- update Py_single_input in Python.h" |
|
#endif |
|
#if Py_file_input != file_input |
|
#error "file_input has changed -- update Py_file_input in Python.h" |
|
#endif |
|
#if Py_eval_input != eval_input |
|
#error "eval_input has changed -- update Py_eval_input in Python.h" |
|
#endif |
|
|
|
int Py_OptimizeFlag = 0; |
|
|
|
#define OP_DELETE 0 |
|
#define OP_ASSIGN 1 |
|
#define OP_APPLY 2 |
|
|
|
#define VAR_LOAD 0 |
|
#define VAR_STORE 1 |
|
#define VAR_DELETE 2 |
|
|
|
#define DEL_CLOSURE_ERROR \ |
|
"can not delete variable '%.400s' referenced in nested scope" |
|
|
|
#define DUPLICATE_ARGUMENT \ |
|
"duplicate argument '%s' in function definition" |
|
|
|
#define ILLEGAL_DYNAMIC_SCOPE \ |
|
"%.100s: exec or 'import *' makes names ambiguous in nested scope" |
|
|
|
#define GLOBAL_AFTER_ASSIGN \ |
|
"name '%.400s' is assigned to before global declaration" |
|
|
|
#define GLOBAL_AFTER_USE \ |
|
"name '%.400s' is used prior to global declaration" |
|
|
|
#define LOCAL_GLOBAL \ |
|
"name '%.400s' is a function parameter and declared global" |
|
|
|
#define LATE_FUTURE \ |
|
"from __future__ imports must occur at the beginning of the file" |
|
|
|
#define ASSIGN_DEBUG \ |
|
"can not assign to __debug__" |
|
|
|
#define MANGLE_LEN 256 |
|
|
|
#define OFF(x) offsetof(PyCodeObject, x) |
|
|
|
static PyMemberDef code_memberlist[] = { |
|
{"co_argcount", T_INT, OFF(co_argcount), READONLY}, |
|
{"co_nlocals", T_INT, OFF(co_nlocals), READONLY}, |
|
{"co_stacksize",T_INT, OFF(co_stacksize), READONLY}, |
|
{"co_flags", T_INT, OFF(co_flags), READONLY}, |
|
{"co_code", T_OBJECT, OFF(co_code), READONLY}, |
|
{"co_consts", T_OBJECT, OFF(co_consts), READONLY}, |
|
{"co_names", T_OBJECT, OFF(co_names), READONLY}, |
|
{"co_varnames", T_OBJECT, OFF(co_varnames), READONLY}, |
|
{"co_freevars", T_OBJECT, OFF(co_freevars), READONLY}, |
|
{"co_cellvars", T_OBJECT, OFF(co_cellvars), READONLY}, |
|
{"co_filename", T_OBJECT, OFF(co_filename), READONLY}, |
|
{"co_name", T_OBJECT, OFF(co_name), READONLY}, |
|
{"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY}, |
|
{"co_lnotab", T_OBJECT, OFF(co_lnotab), READONLY}, |
|
{NULL} /* Sentinel */ |
|
}; |
|
|
|
PyDoc_STRVAR(code_doc, |
|
"code(argcount, nlocals, stacksize, flags, codestring, constants, names,\n\ |
|
varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]])\n\ |
|
\n\ |
|
Create a code object. Not for the faint of heart."); |
|
|
|
static PyObject * |
|
code_new(PyTypeObject *type, PyObject *args, PyObject *kw) |
|
{ |
|
int argcount; |
|
int nlocals; |
|
int stacksize; |
|
int flags; |
|
PyObject *co; |
|
PyObject *empty = NULL; |
|
PyObject *code; |
|
PyObject *consts; |
|
PyObject *names; |
|
PyObject *varnames; |
|
PyObject *freevars = NULL; |
|
PyObject *cellvars = NULL; |
|
PyObject *filename; |
|
PyObject *name; |
|
int firstlineno; |
|
PyObject *lnotab; |
|
|
|
if (!PyArg_ParseTuple(args, "iiiiSO!O!O!SSiS|O!O!:code", |
|
&argcount, &nlocals, &stacksize, &flags, |
|
&code, |
|
&PyTuple_Type, &consts, |
|
&PyTuple_Type, &names, |
|
&PyTuple_Type, &varnames, |
|
&filename, &name, |
|
&firstlineno, &lnotab, |
|
&PyTuple_Type, &freevars, |
|
&PyTuple_Type, &cellvars)) |
|
return NULL; |
|
|
|
if (!PyObject_CheckReadBuffer(code)) { |
|
PyErr_SetString(PyExc_TypeError, |
|
"bytecode object must be a single-segment read-only buffer"); |
|
return NULL; |
|
} |
|
|
|
if (freevars == NULL || cellvars == NULL) { |
|
empty = PyTuple_New(0); |
|
if (empty == NULL) |
|
return NULL; |
|
if (freevars == NULL) |
|
freevars = empty; |
|
if (cellvars == NULL) |
|
cellvars = empty; |
|
} |
|
|
|
co = (PyObject *) PyCode_New(argcount, nlocals, stacksize, flags, |
|
code, consts, names, varnames, |
|
freevars, cellvars, filename, name, |
|
firstlineno, lnotab); |
|
Py_XDECREF(empty); |
|
return co; |
|
} |
|
|
|
static void |
|
code_dealloc(PyCodeObject *co) |
|
{ |
|
Py_XDECREF(co->co_code); |
|
Py_XDECREF(co->co_consts); |
|
Py_XDECREF(co->co_names); |
|
Py_XDECREF(co->co_varnames); |
|
Py_XDECREF(co->co_freevars); |
|
Py_XDECREF(co->co_cellvars); |
|
Py_XDECREF(co->co_filename); |
|
Py_XDECREF(co->co_name); |
|
Py_XDECREF(co->co_lnotab); |
|
PyObject_DEL(co); |
|
} |
|
|
|
static PyObject * |
|
code_repr(PyCodeObject *co) |
|
{ |
|
char buf[500]; |
|
int lineno = -1; |
|
char *filename = "???"; |
|
char *name = "???"; |
|
|
|
if (co->co_firstlineno != 0) |
|
lineno = co->co_firstlineno; |
|
if (co->co_filename && PyString_Check(co->co_filename)) |
|
filename = PyString_AS_STRING(co->co_filename); |
|
if (co->co_name && PyString_Check(co->co_name)) |
|
name = PyString_AS_STRING(co->co_name); |
|
PyOS_snprintf(buf, sizeof(buf), |
|
"<code object %.100s at %p, file \"%.300s\", line %d>", |
|
name, co, filename, lineno); |
|
return PyString_FromString(buf); |
|
} |
|
|
|
static int |
|
code_compare(PyCodeObject *co, PyCodeObject *cp) |
|
{ |
|
int cmp; |
|
cmp = PyObject_Compare(co->co_name, cp->co_name); |
|
if (cmp) return cmp; |
|
cmp = co->co_argcount - cp->co_argcount; |
|
if (cmp) return (cmp<0)?-1:1; |
|
cmp = co->co_nlocals - cp->co_nlocals; |
|
if (cmp) return (cmp<0)?-1:1; |
|
cmp = co->co_flags - cp->co_flags; |
|
if (cmp) return (cmp<0)?-1:1; |
|
cmp = PyObject_Compare(co->co_code, cp->co_code); |
|
if (cmp) return cmp; |
|
cmp = PyObject_Compare(co->co_consts, cp->co_consts); |
|
if (cmp) return cmp; |
|
cmp = PyObject_Compare(co->co_names, cp->co_names); |
|
if (cmp) return cmp; |
|
cmp = PyObject_Compare(co->co_varnames, cp->co_varnames); |
|
if (cmp) return cmp; |
|
cmp = PyObject_Compare(co->co_freevars, cp->co_freevars); |
|
if (cmp) return cmp; |
|
cmp = PyObject_Compare(co->co_cellvars, cp->co_cellvars); |
|
return cmp; |
|
} |
|
|
|
static long |
|
code_hash(PyCodeObject *co) |
|
{ |
|
long h, h0, h1, h2, h3, h4, h5, h6; |
|
h0 = PyObject_Hash(co->co_name); |
|
if (h0 == -1) return -1; |
|
h1 = PyObject_Hash(co->co_code); |
|
if (h1 == -1) return -1; |
|
h2 = PyObject_Hash(co->co_consts); |
|
if (h2 == -1) return -1; |
|
h3 = PyObject_Hash(co->co_names); |
|
if (h3 == -1) return -1; |
|
h4 = PyObject_Hash(co->co_varnames); |
|
if (h4 == -1) return -1; |
|
h5 = PyObject_Hash(co->co_freevars); |
|
if (h5 == -1) return -1; |
|
h6 = PyObject_Hash(co->co_cellvars); |
|
if (h6 == -1) return -1; |
|
h = h0 ^ h1 ^ h2 ^ h3 ^ h4 ^ h5 ^ h6 ^ |
|
co->co_argcount ^ co->co_nlocals ^ co->co_flags; |
|
if (h == -1) h = -2; |
|
return h; |
|
} |
|
|
|
/* XXX code objects need to participate in GC? */ |
|
|
|
PyTypeObject PyCode_Type = { |
|
PyObject_HEAD_INIT(&PyType_Type) |
|
0, |
|
"code", |
|
sizeof(PyCodeObject), |
|
0, |
|
(destructor)code_dealloc, /* tp_dealloc */ |
|
0, /* tp_print */ |
|
0, /* tp_getattr */ |
|
0, /* tp_setattr */ |
|
(cmpfunc)code_compare, /* tp_compare */ |
|
(reprfunc)code_repr, /* tp_repr */ |
|
0, /* tp_as_number */ |
|
0, /* tp_as_sequence */ |
|
0, /* tp_as_mapping */ |
|
(hashfunc)code_hash, /* tp_hash */ |
|
0, /* tp_call */ |
|
0, /* tp_str */ |
|
PyObject_GenericGetAttr, /* tp_getattro */ |
|
0, /* tp_setattro */ |
|
0, /* tp_as_buffer */ |
|
Py_TPFLAGS_DEFAULT, /* tp_flags */ |
|
code_doc, /* tp_doc */ |
|
0, /* tp_traverse */ |
|
0, /* tp_clear */ |
|
0, /* tp_richcompare */ |
|
0, /* tp_weaklistoffset */ |
|
0, /* tp_iter */ |
|
0, /* tp_iternext */ |
|
0, /* tp_methods */ |
|
code_memberlist, /* tp_members */ |
|
0, /* tp_getset */ |
|
0, /* tp_base */ |
|
0, /* tp_dict */ |
|
0, /* tp_descr_get */ |
|
0, /* tp_descr_set */ |
|
0, /* tp_dictoffset */ |
|
0, /* tp_init */ |
|
0, /* tp_alloc */ |
|
code_new, /* tp_new */ |
|
}; |
|
|
|
#define NAME_CHARS \ |
|
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz" |
|
|
|
/* all_name_chars(s): true iff all chars in s are valid NAME_CHARS */ |
|
|
|
static int |
|
all_name_chars(unsigned char *s) |
|
{ |
|
static char ok_name_char[256]; |
|
static unsigned char *name_chars = (unsigned char *)NAME_CHARS; |
|
|
|
if (ok_name_char[*name_chars] == 0) { |
|
unsigned char *p; |
|
for (p = name_chars; *p; p++) |
|
ok_name_char[*p] = 1; |
|
} |
|
while (*s) { |
|
if (ok_name_char[*s++] == 0) |
|
return 0; |
|
} |
|
return 1; |
|
} |
|
|
|
static int |
|
intern_strings(PyObject *tuple) |
|
{ |
|
int i; |
|
|
|
for (i = PyTuple_GET_SIZE(tuple); --i >= 0; ) { |
|
PyObject *v = PyTuple_GET_ITEM(tuple, i); |
|
if (v == NULL || !PyString_Check(v)) { |
|
Py_FatalError("non-string found in code slot"); |
|
PyErr_BadInternalCall(); |
|
return -1; |
|
} |
|
PyString_InternInPlace(&PyTuple_GET_ITEM(tuple, i)); |
|
} |
|
return 0; |
|
} |
|
|
|
#define GETARG(arr, i) ((int)((arr[i+2]<<8) + arr[i+1])) |
|
#define UNCONDITIONAL_JUMP(op) (op==JUMP_ABSOLUTE || op==JUMP_FORWARD) |
|
#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE || op==CONTINUE_LOOP) |
|
#define GETJUMPTGT(arr, i) (GETARG(arr,i) + (ABSOLUTE_JUMP(arr[i]) ? 0 : i+3)) |
|
#define SETARG(arr, i, val) arr[i+2] = val>>8; arr[i+1] = val & 255 |
|
|
|
static PyObject * |
|
optimize_code(PyObject *code, PyObject* consts) |
|
{ |
|
int i, j, codelen; |
|
int tgt, tgttgt, opcode; |
|
unsigned char *codestr; |
|
|
|
/* Make a modifiable copy of the code string */ |
|
if (!PyString_Check(code)) |
|
goto exitUnchanged; |
|
codelen = PyString_Size(code); |
|
codestr = PyMem_Malloc(codelen); |
|
if (codestr == NULL) |
|
goto exitUnchanged; |
|
codestr = memcpy(codestr, PyString_AS_STRING(code), codelen); |
|
assert(PyTuple_Check(consts)); |
|
|
|
for (i=0 ; i<codelen-7 ; i += HAS_ARG(codestr[i]) ? 3 : 1) { |
|
opcode = codestr[i]; |
|
switch (opcode) { |
|
|
|
/* Skip over LOAD_CONST trueconst JUMP_IF_FALSE xx POP_TOP. |
|
Note, only the first opcode is changed, the others still |
|
perform normally if they happen to be jump targets. */ |
|
case LOAD_CONST: |
|
j = GETARG(codestr, i); |
|
if (codestr[i+3] != JUMP_IF_FALSE || |
|
codestr[i+6] != POP_TOP || |
|
!PyObject_IsTrue(PyTuple_GET_ITEM(consts, j))) |
|
continue; |
|
codestr[i] = JUMP_FORWARD; |
|
SETARG(codestr, i, 4); |
|
break; |
|
|
|
/* Replace jumps to unconditional jumps */ |
|
case FOR_ITER: |
|
case JUMP_FORWARD: |
|
case JUMP_IF_FALSE: |
|
case JUMP_IF_TRUE: |
|
case JUMP_ABSOLUTE: |
|
case CONTINUE_LOOP: |
|
case SETUP_LOOP: |
|
case SETUP_EXCEPT: |
|
case SETUP_FINALLY: |
|
tgt = GETJUMPTGT(codestr, i); |
|
if (!UNCONDITIONAL_JUMP(codestr[tgt])) |
|
continue; |
|
tgttgt = GETJUMPTGT(codestr, tgt); |
|
if (opcode == JUMP_FORWARD) /* JMP_ABS can go backwards */ |
|
opcode = JUMP_ABSOLUTE; |
|
if (!ABSOLUTE_JUMP(opcode)) |
|
tgttgt -= i + 3; /* Calc relative jump addr */ |
|
if (tgttgt < 0) /* No backward relative jumps */ |
|
continue; |
|
codestr[i] = opcode; |
|
SETARG(codestr, i, tgttgt); |
|
break; |
|
|
|
case EXTENDED_ARG: |
|
PyMem_Free(codestr); |
|
goto exitUnchanged; |
|
} |
|
} |
|
code = PyString_FromStringAndSize((char *)codestr, codelen); |
|
PyMem_Free(codestr); |
|
return code; |
|
|
|
exitUnchanged: |
|
Py_INCREF(code); |
|
return code; |
|
} |
|
|
|
PyCodeObject * |
|
PyCode_New(int argcount, int nlocals, int stacksize, int flags, |
|
PyObject *code, PyObject *consts, PyObject *names, |
|
PyObject *varnames, PyObject *freevars, PyObject *cellvars, |
|
PyObject *filename, PyObject *name, int firstlineno, |
|
PyObject *lnotab) |
|
{ |
|
PyCodeObject *co; |
|
int i; |
|
/* Check argument types */ |
|
if (argcount < 0 || nlocals < 0 || |
|
code == NULL || |
|
consts == NULL || !PyTuple_Check(consts) || |
|
names == NULL || !PyTuple_Check(names) || |
|
varnames == NULL || !PyTuple_Check(varnames) || |
|
freevars == NULL || !PyTuple_Check(freevars) || |
|
cellvars == NULL || !PyTuple_Check(cellvars) || |
|
name == NULL || !PyString_Check(name) || |
|
filename == NULL || !PyString_Check(filename) || |
|
lnotab == NULL || !PyString_Check(lnotab) || |
|
!PyObject_CheckReadBuffer(code)) { |
|
PyErr_BadInternalCall(); |
|
return NULL; |
|
} |
|
intern_strings(names); |
|
intern_strings(varnames); |
|
intern_strings(freevars); |
|
intern_strings(cellvars); |
|
/* Intern selected string constants */ |
|
for (i = PyTuple_Size(consts); --i >= 0; ) { |
|
PyObject *v = PyTuple_GetItem(consts, i); |
|
if (!PyString_Check(v)) |
|
continue; |
|
if (!all_name_chars((unsigned char *)PyString_AS_STRING(v))) |
|
continue; |
|
PyString_InternInPlace(&PyTuple_GET_ITEM(consts, i)); |
|
} |
|
co = PyObject_NEW(PyCodeObject, &PyCode_Type); |
|
if (co != NULL) { |
|
co->co_argcount = argcount; |
|
co->co_nlocals = nlocals; |
|
co->co_stacksize = stacksize; |
|
co->co_flags = flags; |
|
co->co_code = optimize_code(code, consts); |
|
Py_INCREF(consts); |
|
co->co_consts = consts; |
|
Py_INCREF(names); |
|
co->co_names = names; |
|
Py_INCREF(varnames); |
|
co->co_varnames = varnames; |
|
Py_INCREF(freevars); |
|
co->co_freevars = freevars; |
|
Py_INCREF(cellvars); |
|
co->co_cellvars = cellvars; |
|
Py_INCREF(filename); |
|
co->co_filename = filename; |
|
Py_INCREF(name); |
|
co->co_name = name; |
|
co->co_firstlineno = firstlineno; |
|
Py_INCREF(lnotab); |
|
co->co_lnotab = lnotab; |
|
if (PyTuple_GET_SIZE(freevars) == 0 && |
|
PyTuple_GET_SIZE(cellvars) == 0) |
|
co->co_flags |= CO_NOFREE; |
|
} |
|
return co; |
|
} |
|
|
|
|
|
/* 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. |
|
*/ |
|
|
|
/* All about c_lnotab. |
|
|
|
c_lnotab is an array of unsigned bytes disguised as a Python string. Since |
|
version 2.3, SET_LINENO opcodes are never generated and bytecode offsets are |
|
mapped to source code line #s via c_lnotab instead. |
|
|
|
The array is conceptually a list of |
|
(bytecode offset increment, line number increment) |
|
pairs. The details are important and delicate, best illustrated by example: |
|
|
|
byte code offset source code line number |
|
0 1 |
|
6 2 |
|
50 7 |
|
350 307 |
|
361 308 |
|
|
|
The first trick is that these numbers aren't stored, only the increments |
|
from one row to the next (this doesn't really work, but it's a start): |
|
|
|
0, 1, 6, 1, 44, 5, 300, 300, 11, 1 |
|
|
|
The second trick is that an unsigned byte can't hold negative values, or |
|
values larger than 255, so (a) there's a deep assumption that byte code |
|
offsets and their corresponding line #s both increase monotonically, and (b) |
|
if at least one column jumps by more than 255 from one row to the next, more |
|
than one pair is written to the table. In case #b, there's no way to know |
|
from looking at the table later how many were written. That's the delicate |
|
part. A user of c_lnotab desiring to find the source line number |
|
corresponding to a bytecode address A should do something like this |
|
|
|
lineno = addr = 0 |
|
for addr_incr, line_incr in c_lnotab: |
|
addr += addr_incr |
|
if addr > A: |
|
return lineno |
|
lineno += line_incr |
|
|
|
In order for this to work, when the addr field increments by more than 255, |
|
the line # increment in each pair generated must be 0 until the remaining addr |
|
increment is < 256. So, in the example above, com_set_lineno should not (as |
|
was actually done until 2.2) expand 300, 300 to 255, 255, 45, 45, but to |
|
255, 0, 45, 255, 0, 45. |
|
*/ |
|
|
|
struct compiling { |
|
PyObject *c_code; /* string */ |
|
PyObject *c_consts; /* list of objects */ |
|
PyObject *c_const_dict; /* inverse of c_consts */ |
|
PyObject *c_names; /* list of strings (names) */ |
|
PyObject *c_name_dict; /* inverse of c_names */ |
|
PyObject *c_globals; /* dictionary (value=None or True) */ |
|
PyObject *c_locals; /* dictionary (value=localID) */ |
|
PyObject *c_varnames; /* list (inverse of c_locals) */ |
|
PyObject *c_freevars; /* dictionary (value=None) */ |
|
PyObject *c_cellvars; /* list */ |
|
int c_nlocals; /* index of next local */ |
|
int c_argcount; /* number of top-level arguments */ |
|
int c_flags; /* same as co_flags */ |
|
int c_nexti; /* index into c_code */ |
|
int c_errors; /* counts errors occurred */ |
|
int c_infunction; /* set when compiling a function */ |
|
int c_interactive; /* generating code for interactive command */ |
|
int c_loops; /* counts nested loops */ |
|
int c_begin; /* begin of current loop, for 'continue' */ |
|
int c_block[CO_MAXBLOCKS]; /* stack of block types */ |
|
int c_nblocks; /* current block stack level */ |
|
const char *c_filename; /* filename of current node */ |
|
char *c_name; /* name of object (e.g. function) */ |
|
int c_lineno; /* Current line number */ |
|
int c_stacklevel; /* Current stack level */ |
|
int c_maxstacklevel; /* Maximum stack level */ |
|
int c_firstlineno; |
|
PyObject *c_lnotab; /* Table mapping address to line number */ |
|
int c_last_addr, c_last_line, c_lnotab_next; |
|
char *c_private; /* for private name mangling */ |
|
int c_tmpname; /* temporary local name counter */ |
|
int c_nested; /* Is block nested funcdef or lamdef? */ |
|
int c_closure; /* Is nested w/freevars? */ |
|
struct symtable *c_symtable; /* pointer to module symbol table */ |
|
PyFutureFeatures *c_future; /* pointer to module's __future__ */ |
|
char *c_encoding; /* source encoding (a borrowed reference) */ |
|
}; |
|
|
|
static int |
|
is_free(int v) |
|
{ |
|
if ((v & (USE | DEF_FREE)) |
|
&& !(v & (DEF_LOCAL | DEF_PARAM | DEF_GLOBAL))) |
|
return 1; |
|
if (v & DEF_FREE_CLASS) |
|
return 1; |
|
return 0; |
|
} |
|
|
|
static void |
|
com_error(struct compiling *c, PyObject *exc, char *msg) |
|
{ |
|
PyObject *t = NULL, *v = NULL, *w = NULL, *line = NULL; |
|
|
|
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 || c->c_interactive) { |
|
/* Unknown line number or interactive input */ |
|
PyErr_SetString(exc, msg); |
|
return; |
|
} |
|
v = PyString_FromString(msg); |
|
if (v == NULL) |
|
return; /* MemoryError, too bad */ |
|
|
|
line = PyErr_ProgramText(c->c_filename, c->c_lineno); |
|
if (line == NULL) { |
|
Py_INCREF(Py_None); |
|
line = Py_None; |
|
} |
|
if (exc == PyExc_SyntaxError) { |
|
t = Py_BuildValue("(ziOO)", c->c_filename, c->c_lineno, |
|
Py_None, line); |
|
if (t == NULL) |
|
goto exit; |
|
w = Py_BuildValue("(OO)", v, t); |
|
if (w == NULL) |
|
goto exit; |
|
PyErr_SetObject(exc, w); |
|
} else { |
|
/* Make sure additional exceptions are printed with |
|
file and line, also. */ |
|
PyErr_SetObject(exc, v); |
|
PyErr_SyntaxLocation(c->c_filename, c->c_lineno); |
|
} |
|
exit: |
|
Py_XDECREF(t); |
|
Py_XDECREF(v); |
|
Py_XDECREF(w); |
|
Py_XDECREF(line); |
|
} |
|
|
|
/* Interface to the block stack */ |
|
|
|
static void |
|
block_push(struct compiling *c, int type) |
|
{ |
|
if (c->c_nblocks >= CO_MAXBLOCKS) { |
|
com_error(c, PyExc_SystemError, |
|
"too many statically nested blocks"); |
|
} |
|
else { |
|
c->c_block[c->c_nblocks++] = type; |
|
} |
|
} |
|
|
|
static void |
|
block_pop(struct compiling *c, int type) |
|
{ |
|
if (c->c_nblocks > 0) |
|
c->c_nblocks--; |
|
if (c->c_block[c->c_nblocks] != type && c->c_errors == 0) { |
|
com_error(c, PyExc_SystemError, "bad block pop"); |
|
} |
|
} |
|
|
|
/* Prototype forward declarations */ |
|
|
|
static int issue_warning(const char *, const char *, int); |
|
static int com_init(struct compiling *, const char *); |
|
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 *, 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); |
|
static void com_addfwref(struct compiling *, int, int *); |
|
static void com_backpatch(struct compiling *, int); |
|
static int com_add(struct compiling *, PyObject *, PyObject *, PyObject *); |
|
static int com_addconst(struct compiling *, PyObject *); |
|
static int com_addname(struct compiling *, PyObject *); |
|
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 void com_assign(struct compiling *, node *, int, node *); |
|
static void com_assign_name(struct compiling *, node *, int); |
|
static PyCodeObject *icompile(node *, struct compiling *); |
|
static PyCodeObject *jcompile(node *, const char *, struct compiling *, |
|
PyCompilerFlags *); |
|
static PyObject *parsestrplus(struct compiling*, node *); |
|
static PyObject *parsestr(struct compiling *, char *); |
|
static node *get_rawdocstring(node *); |
|
|
|
static int get_ref_type(struct compiling *, char *); |
|
|
|
/* symtable operations */ |
|
static struct symtable *symtable_build(node *, PyFutureFeatures *, |
|
const char *filename); |
|
static int symtable_load_symbols(struct compiling *); |
|
static struct symtable *symtable_init(void); |
|
static void symtable_enter_scope(struct symtable *, char *, int, int); |
|
static int symtable_exit_scope(struct symtable *); |
|
static int symtable_add_def(struct symtable *, char *, int); |
|
static int symtable_add_def_o(struct symtable *, PyObject *, PyObject *, int); |
|
|
|
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 *, int); |
|
static void symtable_list_comprehension(struct symtable *, node *); |
|
static void symtable_list_for(struct symtable *, node *); |
|
|
|
static int symtable_update_free_vars(struct symtable *); |
|
static int symtable_undo_free(struct symtable *, PyObject *, PyObject *); |
|
static int symtable_check_global(struct symtable *, PyObject *, PyObject *); |
|
|
|
/* 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, const char *filename) |
|
{ |
|
memset((void *)c, '\0', sizeof(struct compiling)); |
|
if ((c->c_code = PyString_FromStringAndSize((char *)NULL, |
|
1000)) == NULL) |
|
goto fail; |
|
if ((c->c_consts = PyList_New(0)) == NULL) |
|
goto fail; |
|
if ((c->c_const_dict = PyDict_New()) == NULL) |
|
goto fail; |
|
if ((c->c_names = PyList_New(0)) == NULL) |
|
goto fail; |
|
if ((c->c_name_dict = PyDict_New()) == NULL) |
|
goto fail; |
|
if ((c->c_locals = PyDict_New()) == NULL) |
|
goto fail; |
|
if ((c->c_lnotab = PyString_FromStringAndSize((char *)NULL, |
|
1000)) == NULL) |
|
goto fail; |
|
c->c_globals = NULL; |
|
c->c_varnames = NULL; |
|
c->c_freevars = NULL; |
|
c->c_cellvars = NULL; |
|
c->c_nlocals = 0; |
|
c->c_argcount = 0; |
|
c->c_flags = 0; |
|
c->c_nexti = 0; |
|
c->c_errors = 0; |
|
c->c_infunction = 0; |
|
c->c_interactive = 0; |
|
c->c_loops = 0; |
|
c->c_begin = 0; |
|
c->c_nblocks = 0; |
|
c->c_filename = filename; |
|
c->c_name = "?"; |
|
c->c_lineno = 0; |
|
c->c_stacklevel = 0; |
|
c->c_maxstacklevel = 0; |
|
c->c_firstlineno = 0; |
|
c->c_last_addr = 0; |
|
c->c_last_line = 0; |
|
c->c_lnotab_next = 0; |
|
c->c_tmpname = 0; |
|
c->c_nested = 0; |
|
c->c_closure = 0; |
|
c->c_symtable = NULL; |
|
return 1; |
|
|
|
fail: |
|
com_free(c); |
|
return 0; |
|
} |
|
|
|
static void |
|
com_free(struct compiling *c) |
|
{ |
|
Py_XDECREF(c->c_code); |
|
Py_XDECREF(c->c_consts); |
|
Py_XDECREF(c->c_const_dict); |
|
Py_XDECREF(c->c_names); |
|
Py_XDECREF(c->c_name_dict); |
|
Py_XDECREF(c->c_globals); |
|
Py_XDECREF(c->c_locals); |
|
Py_XDECREF(c->c_varnames); |
|
Py_XDECREF(c->c_freevars); |
|
Py_XDECREF(c->c_cellvars); |
|
Py_XDECREF(c->c_lnotab); |
|
if (c->c_future) |
|
PyObject_FREE((void *)c->c_future); |
|
} |
|
|
|
static void |
|
com_push(struct compiling *c, int n) |
|
{ |
|
c->c_stacklevel += n; |
|
if (c->c_stacklevel > c->c_maxstacklevel) { |
|
c->c_maxstacklevel = c->c_stacklevel; |
|
/* |
|
fprintf(stderr, "%s:%s:%d max stack nexti=%d level=%d n=%d\n", |
|
c->c_filename, c->c_name, c->c_lineno, |
|
c->c_nexti, c->c_stacklevel, n); |
|
*/ |
|
} |
|
} |
|
|
|
static void |
|
com_pop(struct compiling *c, int n) |
|
{ |
|
if (c->c_stacklevel < n) |
|
c->c_stacklevel = 0; |
|
else |
|
c->c_stacklevel -= n; |
|
} |
|
|
|
static void |
|
com_done(struct compiling *c) |
|
{ |
|
if (c->c_code != NULL) |
|
_PyString_Resize(&c->c_code, c->c_nexti); |
|
if (c->c_lnotab != NULL) |
|
_PyString_Resize(&c->c_lnotab, c->c_lnotab_next); |
|
} |
|
|
|
static int |
|
com_check_size(PyObject **s, int offset) |
|
{ |
|
int len = PyString_GET_SIZE(*s); |
|
if (offset >= len) |
|
return _PyString_Resize(s, len * 2); |
|
return 0; |
|
} |
|
|
|
static void |
|
com_addbyte(struct compiling *c, int byte) |
|
{ |
|
/*fprintf(stderr, "%3d: %3d\n", c->c_nexti, byte);*/ |
|
assert(byte >= 0 && byte <= 255); |
|
assert(c->c_code != 0); |
|
if (com_check_size(&c->c_code, c->c_nexti)) { |
|
c->c_errors++; |
|
return; |
|
} |
|
PyString_AS_STRING(c->c_code)[c->c_nexti++] = byte; |
|
} |
|
|
|
static void |
|
com_addint(struct compiling *c, int x) |
|
{ |
|
com_addbyte(c, x & 0xff); |
|
com_addbyte(c, x >> 8); /* XXX x should be positive */ |
|
} |
|
|
|
static void |
|
com_add_lnotab(struct compiling *c, int addr, int line) |
|
{ |
|
char *p; |
|
if (c->c_lnotab == NULL) |
|
return; |
|
if (com_check_size(&c->c_lnotab, c->c_lnotab_next + 2)) { |
|
c->c_errors++; |
|
return; |
|
} |
|
p = PyString_AS_STRING(c->c_lnotab) + c->c_lnotab_next; |
|
*p++ = addr; |
|
*p++ = line; |
|
c->c_lnotab_next += 2; |
|
} |
|
|
|
static void |
|
com_set_lineno(struct compiling *c, int lineno) |
|
{ |
|
c->c_lineno = lineno; |
|
if (c->c_firstlineno == 0) { |
|
c->c_firstlineno = c->c_last_line = lineno; |
|
} |
|
else { |
|
int incr_addr = c->c_nexti - c->c_last_addr; |
|
int incr_line = lineno - c->c_last_line; |
|
while (incr_addr > 255) { |
|
com_add_lnotab(c, 255, 0); |
|
incr_addr -= 255; |
|
} |
|
while (incr_line > 255) { |
|
com_add_lnotab(c, incr_addr, 255); |
|
incr_line -=255; |
|
incr_addr = 0; |
|
} |
|
if (incr_addr > 0 || incr_line > 0) |
|
com_add_lnotab(c, incr_addr, incr_line); |
|
c->c_last_addr = c->c_nexti; |
|
c->c_last_line = lineno; |
|
} |
|
} |
|
|
|
static void |
|
com_addoparg(struct compiling *c, int op, int arg) |
|
{ |
|
int extended_arg = arg >> 16; |
|
if (extended_arg){ |
|
com_addbyte(c, EXTENDED_ARG); |
|
com_addint(c, extended_arg); |
|
arg &= 0xffff; |
|
} |
|
com_addbyte(c, op); |
|
com_addint(c, arg); |
|
} |
|
|
|
static void |
|
com_addfwref(struct compiling *c, int op, int *p_anchor) |
|
{ |
|
/* Compile a forward reference for backpatching */ |
|
int here; |
|
int anchor; |
|
com_addbyte(c, op); |
|
here = c->c_nexti; |
|
anchor = *p_anchor; |
|
*p_anchor = here; |
|
com_addint(c, anchor == 0 ? 0 : here - anchor); |
|
} |
|
|
|
static void |
|
com_backpatch(struct compiling *c, int anchor) |
|
{ |
|
unsigned char *code = (unsigned char *) PyString_AS_STRING(c->c_code); |
|
int target = c->c_nexti; |
|
int dist; |
|
int prev; |
|
for (;;) { |
|
/* Make the JUMP instruction at anchor point to target */ |
|
prev = code[anchor] + (code[anchor+1] << 8); |
|
dist = target - (anchor+2); |
|
code[anchor] = dist & 0xff; |
|
dist >>= 8; |
|
code[anchor+1] = dist; |
|
dist >>= 8; |
|
if (dist) { |
|
com_error(c, PyExc_SystemError, |
|
"com_backpatch: offset too large"); |
|
break; |
|
} |
|
if (!prev) |
|
break; |
|
anchor -= prev; |
|
} |
|
} |
|
|
|
/* Handle literals and names uniformly */ |
|
|
|
static int |
|
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; |
|
w = PyDict_GetItem(dict, t); |
|
if (w != NULL) { |
|
n = PyInt_AsLong(w); |
|
} else { |
|
n = PyList_Size(list); |
|
np = PyInt_FromLong(n); |
|
if (np == NULL) |
|
goto fail; |
|
if (PyList_Append(list, v) != 0) |
|
goto fail; |
|
if (PyDict_SetItem(dict, t, np) != 0) |
|
goto fail; |
|
Py_DECREF(np); |
|
} |
|
Py_DECREF(t); |
|
return n; |
|
fail: |
|
Py_XDECREF(np); |
|
Py_XDECREF(t); |
|
c->c_errors++; |
|
return 0; |
|
} |
|
|
|
static int |
|
com_addconst(struct compiling *c, PyObject *v) |
|
{ |
|
return com_add(c, c->c_consts, c->c_const_dict, v); |
|
} |
|
|
|
static int |
|
com_addname(struct compiling *c, PyObject *v) |
|
{ |
|
return com_add(c, c->c_names, c->c_name_dict, v); |
|
} |
|
|
|
int |
|
_Py_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. */ |
|
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__ */ |
|
/* Strip leading underscores from class name */ |
|
while (*p == '_') |
|
p++; |
|
if (*p == '\0') |
|
return 0; /* Don't mangle if class is just underscores */ |
|
plen = strlen(p); |
|
if (plen + nlen >= maxlen) |
|
plen = maxlen-nlen-2; /* Truncate class name if too long */ |
|
/* buffer = "_" + p[:plen] + name # i.e. 1+plen+nlen bytes */ |
|
buffer[0] = '_'; |
|
strncpy(buffer+1, p, plen); |
|
strcpy(buffer+1+plen, name); |
|
return 1; |
|
} |
|
|
|
static void |
|
com_addop_name(struct compiling *c, int op, char *name) |
|
{ |
|
PyObject *v; |
|
int i; |
|
char buffer[MANGLE_LEN]; |
|
|
|
if (_Py_Mangle(c->c_private, name, buffer, sizeof(buffer))) |
|
name = buffer; |
|
if (name == NULL || (v = PyString_InternFromString(name)) == NULL) { |
|
c->c_errors++; |
|
i = 255; |
|
} |
|
else { |
|
i = com_addname(c, v); |
|
Py_DECREF(v); |
|
} |
|
com_addoparg(c, op, i); |
|
} |
|
|
|
#define NAME_LOCAL 0 |
|
#define NAME_GLOBAL 1 |
|
#define NAME_DEFAULT 2 |
|
#define NAME_CLOSURE 3 |
|
|
|
static int |
|
com_lookup_arg(PyObject *dict, PyObject *name) |
|
{ |
|
PyObject *v = PyDict_GetItem(dict, name); |
|
if (v == NULL) |
|
return -1; |
|
else |
|
return PyInt_AS_LONG(v); |
|
} |
|
|
|
static int |
|
none_assignment_check(struct compiling *c, char *name, int assigning) |
|
{ |
|
if (name[0] == 'N' && strcmp(name, "None") == 0) { |
|
char *msg; |
|
if (assigning) |
|
msg = "assignment to None"; |
|
else |
|
msg = "deleting None"; |
|
if (issue_warning(msg, c->c_filename, c->c_lineno) < 0) { |
|
c->c_errors++; |
|
return -1; |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
static void |
|
com_addop_varname(struct compiling *c, int kind, char *name) |
|
{ |
|
PyObject *v; |
|
int i, reftype; |
|
int scope = NAME_DEFAULT; |
|
int op = STOP_CODE; |
|
char buffer[MANGLE_LEN]; |
|
|
|
if (kind != VAR_LOAD && |
|
none_assignment_check(c, name, kind == VAR_STORE)) |
|
{ |
|
c->c_errors++; |
|
i = 255; |
|
goto done; |
|
} |
|
if (_Py_Mangle(c->c_private, name, buffer, sizeof(buffer))) |
|
name = buffer; |
|
if (name == NULL || (v = PyString_InternFromString(name)) == NULL) { |
|
c->c_errors++; |
|
i = 255; |
|
goto done; |
|
} |
|
|
|
reftype = get_ref_type(c, name); |
|
switch (reftype) { |
|
case LOCAL: |
|
if (c->c_symtable->st_cur->ste_type == TYPE_FUNCTION) |
|
scope = NAME_LOCAL; |
|
break; |
|
case GLOBAL_EXPLICIT: |
|
scope = NAME_GLOBAL; |
|
break; |
|
case GLOBAL_IMPLICIT: |
|
if (c->c_flags & CO_OPTIMIZED) |
|
scope = NAME_GLOBAL; |
|
break; |
|
case FREE: |
|
case CELL: |
|
scope = NAME_CLOSURE; |
|
break; |
|
} |
|
|
|
i = com_addname(c, v); |
|
if (scope == NAME_LOCAL) |
|
i = com_lookup_arg(c->c_locals, v); |
|
else if (reftype == FREE) |
|
i = com_lookup_arg(c->c_freevars, v); |
|
else if (reftype == CELL) |
|
i = com_lookup_arg(c->c_cellvars, v); |
|
if (i == -1) { |
|
c->c_errors++; /* XXX no exception set */ |
|
i = 255; |
|
goto done; |
|
} |
|
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 NAME_CLOSURE: |
|
op = LOAD_DEREF; |
|
break; |
|
} |
|
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 NAME_CLOSURE: |
|
op = STORE_DEREF; |
|
break; |
|
} |
|
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; |
|
case NAME_CLOSURE: { |
|
char buf[500]; |
|
PyOS_snprintf(buf, sizeof(buf), |
|
DEL_CLOSURE_ERROR, name); |
|
com_error(c, PyExc_SyntaxError, buf); |
|
i = 255; |
|
break; |
|
} |
|
} |
|
break; |
|
} |
|
done: |
|
com_addoparg(c, op, i); |
|
} |
|
|
|
static void |
|
com_addopname(struct compiling *c, int op, node *n) |
|
{ |
|
char *name; |
|
char buffer[1000]; |
|
/* XXX it is possible to write this code without the 1000 |
|
chars on the total length of dotted names, I just can't be |
|
bothered right now */ |
|
if (TYPE(n) == STAR) |
|
name = "*"; |
|
else if (TYPE(n) == dotted_name) { |
|
char *p = buffer; |
|
int i; |
|
name = buffer; |
|
for (i = 0; i < NCH(n); i += 2) { |
|
char *s = STR(CHILD(n, i)); |
|
if (p + strlen(s) > buffer + (sizeof buffer) - 2) { |
|
com_error(c, PyExc_MemoryError, |
|
"dotted_name too long"); |
|
name = NULL; |
|
break; |
|
} |
|
if (p != buffer) |
|
*p++ = '.'; |
|
strcpy(p, s); |
|
p = strchr(p, '\0'); |
|
} |
|
} |
|
else { |
|
REQ(n, NAME); |
|
name = STR(n); |
|
} |
|
com_addop_name(c, op, name); |
|
} |
|
|
|
static PyObject * |
|
parsenumber(struct compiling *c, char *s) |
|
{ |
|
char *end; |
|
long x; |
|
double dx; |
|
#ifndef WITHOUT_COMPLEX |
|
int imflag; |
|
#endif |
|
|
|
errno = 0; |
|
end = s + strlen(s) - 1; |
|
#ifndef WITHOUT_COMPLEX |
|
imflag = *end == 'j' || *end == 'J'; |
|
#endif |
|
if (*end == 'l' || *end == 'L') |
|
return PyLong_FromString(s, (char **)0, 0); |
|
if (s[0] == '0') { |
|
x = (long) PyOS_strtoul(s, &end, 0); |
|
if (x < 0 && errno == 0) { |
|
if (PyErr_WarnExplicit( |
|
PyExc_FutureWarning, |
|
"hex/oct constants > sys.maxint " |
|
"will return positive values " |
|
"in Python 2.4 and up", |
|
/* XXX: Give WarnExplicit |
|
a const char* argument. */ |
|
(char*)c->c_filename, |
|
c->c_lineno, |
|
NULL, |
|
NULL) < 0) |
|
return NULL; |
|
errno = 0; /* Might be changed by PyErr_Warn() */ |
|
} |
|
} |
|
else |
|
x = PyOS_strtol(s, &end, 0); |
|
if (*end == '\0') { |
|
if (errno != 0) |
|
return PyLong_FromString(s, (char **)0, 0); |
|
return PyInt_FromLong(x); |
|
} |
|
/* XXX Huge floats may silently fail */ |
|
#ifndef WITHOUT_COMPLEX |
|
if (imflag) { |
|
Py_complex z; |
|
z.real = 0.; |
|
PyFPE_START_PROTECT("atof", return 0) |
|
z.imag = atof(s); |
|
PyFPE_END_PROTECT(z) |
|
return PyComplex_FromCComplex(z); |
|
} |
|
else |
|
#endif |
|
{ |
|
PyFPE_START_PROTECT("atof", return 0) |
|
dx = atof(s); |
|
PyFPE_END_PROTECT(dx) |
|
return PyFloat_FromDouble(dx); |
|
} |
|
} |
|
|
|
static PyObject * |
|
decode_utf8(char **sPtr, char *end, char* encoding) |
|
{ |
|
#ifndef Py_USING_UNICODE |
|
Py_FatalError("decode_utf8 should not be called in this build."); |
|
return NULL; |
|
#else |
|
PyObject *u, *v; |
|
char *s, *t; |
|
t = s = *sPtr; |
|
/* while (s < end && *s != '\\') s++; */ /* inefficient for u".." */ |
|
while (s < end && (*s & 0x80)) s++; |
|
*sPtr = s; |
|
u = PyUnicode_DecodeUTF8(t, s - t, NULL); |
|
if (u == NULL) |
|
return NULL; |
|
v = PyUnicode_AsEncodedString(u, encoding, NULL); |
|
Py_DECREF(u); |
|
return v; |
|
#endif |
|
} |
|
|
|
/* compiler.transformer.Transformer.decode_literal depends on what |
|
might seem like minor details of this function -- changes here |
|
must be reflected there. */ |
|
static PyObject * |
|
parsestr(struct compiling *c, char *s) |
|
{ |
|
PyObject *v; |
|
size_t len; |
|
int quote = *s; |
|
int rawmode = 0; |
|
char* encoding = ((c == NULL) ? NULL : c->c_encoding); |
|
int need_encoding; |
|
int unicode = 0; |
|
|
|
if (isalpha(quote) || quote == '_') { |
|
if (quote == 'u' || quote == 'U') { |
|
quote = *++s; |
|
unicode = 1; |
|
} |
|
if (quote == 'r' || quote == 'R') { |
|
quote = *++s; |
|
rawmode = 1; |
|
} |
|
} |
|
if (quote != '\'' && quote != '\"') { |
|
PyErr_BadInternalCall(); |
|
return NULL; |
|
} |
|
s++; |
|
len = strlen(s); |
|
if (len > INT_MAX) { |
|
com_error(c, PyExc_OverflowError, |
|
"string to parse is too long"); |
|
return NULL; |
|
} |
|
if (s[--len] != quote) { |
|
PyErr_BadInternalCall(); |
|
return NULL; |
|
} |
|
if (len >= 4 && s[0] == quote && s[1] == quote) { |
|
s += 2; |
|
len -= 2; |
|
if (s[--len] != quote || s[--len] != quote) { |
|
PyErr_BadInternalCall(); |
|
return NULL; |
|
} |
|
} |
|
#ifdef Py_USING_UNICODE |
|
if (unicode || Py_UnicodeFlag) { |
|
PyObject *u, *w; |
|
char *buf; |
|
char *p; |
|
char *end; |
|
if (encoding == NULL) { |
|
buf = s; |
|
u = NULL; |
|
} else if (strcmp(encoding, "iso-8859-1") == 0) { |
|
buf = s; |
|
u = NULL; |
|
} else { |
|
/* "\XX" may become "\u005c\uHHLL" (12 bytes) */ |
|
u = PyString_FromStringAndSize((char *)NULL, len * 4); |
|
if (u == NULL) |
|
return NULL; |
|
p = buf = PyString_AsString(u); |
|
end = s + len; |
|
while (s < end) { |
|
if (*s == '\\') { |
|
*p++ = *s++; |
|
if (*s & 0x80) { |
|
strcpy(p, "u005c"); |
|
p += 5; |
|
} |
|
} |
|
if (*s & 0x80) { /* XXX inefficient */ |
|
char *r; |
|
int rn, i; |
|
w = decode_utf8(&s, end, "utf-16-be"); |
|
if (w == NULL) { |
|
Py_DECREF(u); |
|
return NULL; |
|
} |
|
r = PyString_AsString(w); |
|
rn = PyString_Size(w); |
|
assert(rn % 2 == 0); |
|
for (i = 0; i < rn; i += 2) { |
|
sprintf(p, "\\u%02x%02x", |
|
r[i + 0] & 0xFF, |
|
r[i + 1] & 0xFF); |
|
p += 6; |
|
} |
|
Py_DECREF(w); |
|
} else { |
|
*p++ = *s++; |
|
} |
|
} |
|
len = p - buf; |
|
} |
|
if (rawmode) |
|
v = PyUnicode_DecodeRawUnicodeEscape(buf, len, NULL); |
|
else |
|
v = PyUnicode_DecodeUnicodeEscape(buf, len, NULL); |
|
Py_XDECREF(u); |
|
if (v == NULL) |
|
PyErr_SyntaxLocation(c->c_filename, c->c_lineno); |
|
return v; |
|
|
|
} |
|
#endif |
|
need_encoding = (encoding != NULL && |
|
strcmp(encoding, "utf-8") != 0 && |
|
strcmp(encoding, "iso-8859-1") != 0); |
|
if (rawmode || strchr(s, '\\') == NULL) { |
|
if (need_encoding) { |
|
#ifndef Py_USING_UNICODE |
|
/* This should not happen - we never see any other |
|
encoding. */ |
|
Py_FatalError("cannot deal with encodings in this build."); |
|
#else |
|
PyObject* u = PyUnicode_DecodeUTF8(s, len, NULL); |
|
if (u == NULL) |
|
return NULL; |
|
v = PyUnicode_AsEncodedString(u, encoding, NULL); |
|
Py_DECREF(u); |
|
return v; |
|
#endif |
|
} else { |
|
return PyString_FromStringAndSize(s, len); |
|
} |
|
} |
|
|
|
v = PyString_DecodeEscape(s, len, NULL, unicode, |
|
need_encoding ? encoding : NULL); |
|
if (v == NULL) |
|
PyErr_SyntaxLocation(c->c_filename, c->c_lineno); |
|
return v; |
|
} |
|
|
|
static PyObject * |
|
parsestrplus(struct compiling* c, node *n) |
|
{ |
|
PyObject *v; |
|
int i; |
|
REQ(CHILD(n, 0), STRING); |
|
if ((v = parsestr(c, STR(CHILD(n, 0)))) != NULL) { |
|
/* String literal concatenation */ |
|
for (i = 1; i < NCH(n); i++) { |
|
PyObject *s; |
|
s = parsestr(c, STR(CHILD(n, i))); |
|
if (s == NULL) |
|
goto onError; |
|
if (PyString_Check(v) && PyString_Check(s)) { |
|
PyString_ConcatAndDel(&v, s); |
|
if (v == NULL) |
|
goto onError; |
|
} |
|
#ifdef Py_USING_UNICODE |
|
else { |
|
PyObject *temp; |
|
temp = PyUnicode_Concat(v, s); |
|
Py_DECREF(s); |
|
if (temp == NULL) |
|
goto onError; |
|
Py_DECREF(v); |
|
v = temp; |
|
} |
|
#endif |
|
} |
|
} |
|
return v; |
|
|
|
onError: |
|
Py_XDECREF(v); |
|
return NULL; |
|
} |
|
|
|
static void |
|
com_list_for(struct compiling *c, node *n, node *e, char *t) |
|
{ |
|
int anchor = 0; |
|
int save_begin = c->c_begin; |
|
|
|
/* list_iter: for v in expr [list_iter] */ |
|
com_node(c, CHILD(n, 3)); /* expr */ |
|
com_addbyte(c, GET_ITER); |
|
c->c_begin = c->c_nexti; |
|
com_addfwref(c, FOR_ITER, &anchor); |
|
com_push(c, 1); |
|
com_assign(c, CHILD(n, 1), OP_ASSIGN, NULL); |
|
c->c_loops++; |
|
com_list_iter(c, n, e, t); |
|
c->c_loops--; |
|
com_addoparg(c, JUMP_ABSOLUTE, c->c_begin); |
|
c->c_begin = save_begin; |
|
com_backpatch(c, anchor); |
|
com_pop(c, 1); /* FOR_ITER has popped this */ |
|
} |
|
|
|
static void |
|
com_list_if(struct compiling *c, node *n, node *e, char *t) |
|
{ |
|
int anchor = 0; |
|
int a = 0; |
|
/* list_iter: 'if' test [list_iter] */ |
|
com_node(c, CHILD(n, 1)); |
|
com_addfwref(c, JUMP_IF_FALSE, &a); |
|
com_addbyte(c, POP_TOP); |
|
com_pop(c, 1); |
|
com_list_iter(c, n, e, t); |
|
com_addfwref(c, JUMP_FORWARD, &anchor); |
|
com_backpatch(c, a); |
|
/* We jump here with an extra entry which we now pop */ |
|
com_addbyte(c, POP_TOP); |
|
com_backpatch(c, anchor); |
|
} |
|
|
|
static void |
|
com_list_iter(struct compiling *c, |
|
node *p, /* parent of list_iter node */ |
|
node *e, /* element expression node */ |
|
char *t /* name of result list temp local */) |
|
{ |
|
/* list_iter is the last child in a listmaker, list_for, or list_if */ |
|
node *n = CHILD(p, NCH(p)-1); |
|
if (TYPE(n) == list_iter) { |
|
n = CHILD(n, 0); |
|
switch (TYPE(n)) { |
|
case list_for: |
|
com_list_for(c, n, e, t); |
|
break; |
|
case list_if: |
|
com_list_if(c, n, e, t); |
|
break; |
|
default: |
|
com_error(c, PyExc_SystemError, |
|
"invalid list_iter node type"); |
|
} |
|
} |
|
else { |
|
com_addop_varname(c, VAR_LOAD, t); |
|
com_push(c, 1); |
|
com_node(c, e); |
|
com_addoparg(c, CALL_FUNCTION, 1); |
|
com_addbyte(c, POP_TOP); |
|
com_pop(c, 2); |
|
} |
|
} |
|
|
|
static void |
|
com_list_comprehension(struct compiling *c, node *n) |
|
{ |
|
/* listmaker: test list_for */ |
|
char tmpname[30]; |
|
|
|
REQ(n, listmaker); |
|
PyOS_snprintf(tmpname, sizeof(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_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_addop_varname(c, VAR_DELETE, tmpname); |
|
--c->c_tmpname; |
|
} |
|
|
|
static void |
|
com_listmaker(struct compiling *c, node *n) |
|
{ |
|
/* listmaker: test ( list_for | (',' test)* [','] ) */ |
|
if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == list_for) |
|
com_list_comprehension(c, n); |
|
else { |
|
int len = 0; |
|
int i; |
|
for (i = 0; i < NCH(n); i += 2, len++) |
|
com_node(c, CHILD(n, i)); |
|
com_addoparg(c, BUILD_LIST, len); |
|
com_pop(c, len-1); |
|
} |
|
} |
|
|
|
static void |
|
com_dictmaker(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
/* dictmaker: test ':' test (',' test ':' value)* [','] */ |
|
for (i = 0; i+2 < NCH(n); i += 4) { |
|
/* We must arrange things just right for STORE_SUBSCR. |
|
It wants the stack to look like (value) (dict) (key) */ |
|
com_addbyte(c, DUP_TOP); |
|
com_push(c, 1); |
|
com_node(c, CHILD(n, i)); /* key */ |
|
com_node(c, CHILD(n, i+2)); /* value */ |
|
com_addbyte(c, ROT_THREE); |
|
com_addbyte(c, STORE_SUBSCR); |
|
com_pop(c, 3); |
|
} |
|
} |
|
|
|
static void |
|
com_atom(struct compiling *c, node *n) |
|
{ |
|
node *ch; |
|
PyObject *v; |
|
int i; |
|
REQ(n, atom); |
|
ch = CHILD(n, 0); |
|
switch (TYPE(ch)) { |
|
case LPAR: |
|
if (TYPE(CHILD(n, 1)) == RPAR) { |
|
com_addoparg(c, BUILD_TUPLE, 0); |
|
com_push(c, 1); |
|
} |
|
else |
|
com_node(c, CHILD(n, 1)); |
|
break; |
|
case LSQB: /* '[' [listmaker] ']' */ |
|
if (TYPE(CHILD(n, 1)) == RSQB) { |
|
com_addoparg(c, BUILD_LIST, 0); |
|
com_push(c, 1); |
|
} |
|
else |
|
com_listmaker(c, CHILD(n, 1)); |
|
break; |
|
case LBRACE: /* '{' [dictmaker] '}' */ |
|
com_addoparg(c, BUILD_MAP, 0); |
|
com_push(c, 1); |
|
if (TYPE(CHILD(n, 1)) == dictmaker) |
|
com_dictmaker(c, CHILD(n, 1)); |
|
break; |
|
case BACKQUOTE: |
|
com_node(c, CHILD(n, 1)); |
|
com_addbyte(c, UNARY_CONVERT); |
|
break; |
|
case NUMBER: |
|
if ((v = parsenumber(c, STR(ch))) == NULL) { |
|
i = 255; |
|
} |
|
else { |
|
i = com_addconst(c, v); |
|
Py_DECREF(v); |
|
} |
|
com_addoparg(c, LOAD_CONST, i); |
|
com_push(c, 1); |
|
break; |
|
case STRING: |
|
v = parsestrplus(c, n); |
|
if (v == NULL) { |
|
c->c_errors++; |
|
i = 255; |
|
} |
|
else { |
|
i = com_addconst(c, v); |
|
Py_DECREF(v); |
|
} |
|
com_addoparg(c, LOAD_CONST, i); |
|
com_push(c, 1); |
|
break; |
|
case NAME: |
|
com_addop_varname(c, VAR_LOAD, STR(ch)); |
|
com_push(c, 1); |
|
break; |
|
default: |
|
com_error(c, PyExc_SystemError, |
|
"com_atom: unexpected node type"); |
|
} |
|
} |
|
|
|
static void |
|
com_slice(struct compiling *c, node *n, int op) |
|
{ |
|
if (NCH(n) == 1) { |
|
com_addbyte(c, op); |
|
} |
|
else if (NCH(n) == 2) { |
|
if (TYPE(CHILD(n, 0)) != COLON) { |
|
com_node(c, CHILD(n, 0)); |
|
com_addbyte(c, op+1); |
|
} |
|
else { |
|
com_node(c, CHILD(n, 1)); |
|
com_addbyte(c, op+2); |
|
} |
|
com_pop(c, 1); |
|
} |
|
else { |
|
com_node(c, CHILD(n, 0)); |
|
com_node(c, CHILD(n, 2)); |
|
com_addbyte(c, op+3); |
|
com_pop(c, 2); |
|
} |
|
} |
|
|
|
static void |
|
com_augassign_slice(struct compiling *c, node *n, int opcode, node *augn) |
|
{ |
|
if (NCH(n) == 1) { |
|
com_addbyte(c, DUP_TOP); |
|
com_push(c, 1); |
|
com_addbyte(c, SLICE); |
|
com_node(c, augn); |
|
com_addbyte(c, opcode); |
|
com_pop(c, 1); |
|
com_addbyte(c, ROT_TWO); |
|
com_addbyte(c, STORE_SLICE); |
|
com_pop(c, 2); |
|
} else if (NCH(n) == 2 && TYPE(CHILD(n, 0)) != COLON) { |
|
com_node(c, CHILD(n, 0)); |
|
com_addoparg(c, DUP_TOPX, 2); |
|
com_push(c, 2); |
|
com_addbyte(c, SLICE+1); |
|
com_pop(c, 1); |
|
com_node(c, augn); |
|
com_addbyte(c, opcode); |
|
com_pop(c, 1); |
|
com_addbyte(c, ROT_THREE); |
|
com_addbyte(c, STORE_SLICE+1); |
|
com_pop(c, 3); |
|
} else if (NCH(n) == 2) { |
|
com_node(c, CHILD(n, 1)); |
|
com_addoparg(c, DUP_TOPX, 2); |
|
com_push(c, 2); |
|
com_addbyte(c, SLICE+2); |
|
com_pop(c, 1); |
|
com_node(c, augn); |
|
com_addbyte(c, opcode); |
|
com_pop(c, 1); |
|
com_addbyte(c, ROT_THREE); |
|
com_addbyte(c, STORE_SLICE+2); |
|
com_pop(c, 3); |
|
} else { |
|
com_node(c, CHILD(n, 0)); |
|
com_node(c, CHILD(n, 2)); |
|
com_addoparg(c, DUP_TOPX, 3); |
|
com_push(c, 3); |
|
com_addbyte(c, SLICE+3); |
|
com_pop(c, 2); |
|
com_node(c, augn); |
|
com_addbyte(c, opcode); |
|
com_pop(c, 1); |
|
com_addbyte(c, ROT_FOUR); |
|
com_addbyte(c, STORE_SLICE+3); |
|
com_pop(c, 4); |
|
} |
|
} |
|
|
|
static void |
|
com_argument(struct compiling *c, node *n, PyObject **pkeywords) |
|
{ |
|
node *m; |
|
REQ(n, argument); /* [test '='] test; really [keyword '='] test */ |
|
if (NCH(n) == 1) { |
|
if (*pkeywords != NULL) { |
|
com_error(c, PyExc_SyntaxError, |
|
"non-keyword arg after keyword arg"); |
|
} |
|
else { |
|
com_node(c, CHILD(n, 0)); |
|
} |
|
return; |
|
} |
|
m = n; |
|
do { |
|
m = CHILD(m, 0); |
|
} while (NCH(m) == 1); |
|
if (TYPE(m) != NAME) { |
|
/* f(lambda x: x[0] = 3) ends up getting parsed with |
|
* LHS test = lambda x: x[0], and RHS test = 3. |
|
* SF bug 132313 points out that complaining about a keyword |
|
* then is very confusing. |
|
*/ |
|
com_error(c, PyExc_SyntaxError, |
|
TYPE(m) == lambdef ? |
|
"lambda cannot contain assignment" : |
|
"keyword can't be an expression"); |
|
} |
|
else { |
|
PyObject *v = PyString_InternFromString(STR(m)); |
|
(void) none_assignment_check(c, STR(m), 1); |
|
if (v != NULL && *pkeywords == NULL) |
|
*pkeywords = PyDict_New(); |
|
if (v == NULL) |
|
c->c_errors++; |
|
else if (*pkeywords == NULL) { |
|
c->c_errors++; |
|
Py_DECREF(v); |
|
} else { |
|
if (PyDict_GetItem(*pkeywords, v) != NULL) |
|
com_error(c, PyExc_SyntaxError, |
|
"duplicate keyword argument"); |
|
else |
|
if (PyDict_SetItem(*pkeywords, v, v) != 0) |
|
c->c_errors++; |
|
com_addoparg(c, LOAD_CONST, com_addconst(c, v)); |
|
com_push(c, 1); |
|
Py_DECREF(v); |
|
} |
|
} |
|
com_node(c, CHILD(n, 2)); |
|
} |
|
|
|
static void |
|
com_call_function(struct compiling *c, node *n) |
|
{ |
|
if (TYPE(n) == RPAR) { |
|
com_addoparg(c, CALL_FUNCTION, 0); |
|
} |
|
else { |
|
PyObject *keywords = NULL; |
|
int i, na, nk; |
|
int lineno = n->n_lineno; |
|
int star_flag = 0; |
|
int starstar_flag = 0; |
|
int opcode; |
|
REQ(n, arglist); |
|
na = 0; |
|
nk = 0; |
|
for (i = 0; i < NCH(n); i += 2) { |
|
node *ch = CHILD(n, i); |
|
if (TYPE(ch) == STAR || |
|
TYPE(ch) == DOUBLESTAR) |
|
break; |
|
if (ch->n_lineno != lineno) { |
|
lineno = ch->n_lineno; |
|
com_set_lineno(c, lineno); |
|
} |
|
com_argument(c, ch, &keywords); |
|
if (keywords == NULL) |
|
na++; |
|
else |
|
nk++; |
|
} |
|
Py_XDECREF(keywords); |
|
while (i < NCH(n)) { |
|
node *tok = CHILD(n, i); |
|
node *ch = CHILD(n, i+1); |
|
i += 3; |
|
switch (TYPE(tok)) { |
|
case STAR: star_flag = 1; break; |
|
case DOUBLESTAR: starstar_flag = 1; break; |
|
} |
|
com_node(c, ch); |
|
} |
|
if (na > 255 || nk > 255) { |
|
com_error(c, PyExc_SyntaxError, |
|
"more than 255 arguments"); |
|
} |
|
if (star_flag || starstar_flag) |
|
opcode = CALL_FUNCTION_VAR - 1 + |
|
star_flag + (starstar_flag << 1); |
|
else |
|
opcode = CALL_FUNCTION; |
|
com_addoparg(c, opcode, na | (nk << 8)); |
|
com_pop(c, na + 2*nk + star_flag + starstar_flag); |
|
} |
|
} |
|
|
|
static void |
|
com_select_member(struct compiling *c, node *n) |
|
{ |
|
com_addopname(c, LOAD_ATTR, n); |
|
} |
|
|
|
static void |
|
com_sliceobj(struct compiling *c, node *n) |
|
{ |
|
int i=0; |
|
int ns=2; /* number of slice arguments */ |
|
node *ch; |
|
|
|
/* first argument */ |
|
if (TYPE(CHILD(n,i)) == COLON) { |
|
com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); |
|
com_push(c, 1); |
|
i++; |
|
} |
|
else { |
|
com_node(c, CHILD(n,i)); |
|
i++; |
|
REQ(CHILD(n,i),COLON); |
|
i++; |
|
} |
|
/* second argument */ |
|
if (i < NCH(n) && TYPE(CHILD(n,i)) == test) { |
|
com_node(c, CHILD(n,i)); |
|
i++; |
|
} |
|
else { |
|
com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); |
|
com_push(c, 1); |
|
} |
|
/* remaining arguments */ |
|
for (; i < NCH(n); i++) { |
|
ns++; |
|
ch=CHILD(n,i); |
|
REQ(ch, sliceop); |
|
if (NCH(ch) == 1) { |
|
/* right argument of ':' missing */ |
|
com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); |
|
com_push(c, 1); |
|
} |
|
else |
|
com_node(c, CHILD(ch,1)); |
|
} |
|
com_addoparg(c, BUILD_SLICE, ns); |
|
com_pop(c, 1 + (ns == 3)); |
|
} |
|
|
|
static void |
|
com_subscript(struct compiling *c, node *n) |
|
{ |
|
node *ch; |
|
REQ(n, subscript); |
|
ch = CHILD(n,0); |
|
/* check for rubber index */ |
|
if (TYPE(ch) == DOT && TYPE(CHILD(n,1)) == DOT) { |
|
com_addoparg(c, LOAD_CONST, com_addconst(c, Py_Ellipsis)); |
|
com_push(c, 1); |
|
} |
|
else { |
|
/* check for slice */ |
|
if ((TYPE(ch) == COLON || NCH(n) > 1)) |
|
com_sliceobj(c, n); |
|
else { |
|
REQ(ch, test); |
|
com_node(c, ch); |
|
} |
|
} |
|
} |
|
|
|
static void |
|
com_subscriptlist(struct compiling *c, node *n, int assigning, node *augn) |
|
{ |
|
int i, op; |
|
REQ(n, subscriptlist); |
|
/* Check to make backward compatible slice behavior for '[i:j]' */ |
|
if (NCH(n) == 1) { |
|
node *sub = CHILD(n, 0); /* subscript */ |
|
/* 'Basic' slice, should have exactly one colon. */ |
|
if ((TYPE(CHILD(sub, 0)) == COLON |
|
|| (NCH(sub) > 1 && TYPE(CHILD(sub, 1)) == COLON)) |
|
&& (TYPE(CHILD(sub,NCH(sub)-1)) != sliceop)) |
|
{ |
|
switch (assigning) { |
|
case OP_DELETE: |
|
op = DELETE_SLICE; |
|
break; |
|
case OP_ASSIGN: |
|
op = STORE_SLICE; |
|
break; |
|
case OP_APPLY: |
|
op = SLICE; |
|
break; |
|
default: |
|
com_augassign_slice(c, sub, assigning, augn); |
|
return; |
|
} |
|
com_slice(c, sub, op); |
|
if (op == STORE_SLICE) |
|
com_pop(c, 2); |
|
else if (op == DELETE_SLICE) |
|
com_pop(c, 1); |
|
return; |
|
} |
|
} |
|
/* Else normal subscriptlist. Compile each subscript. */ |
|
for (i = 0; i < NCH(n); i += 2) |
|
com_subscript(c, CHILD(n, i)); |
|
/* Put multiple subscripts into a tuple */ |
|
if (NCH(n) > 1) { |
|
i = (NCH(n)+1) / 2; |
|
com_addoparg(c, BUILD_TUPLE, i); |
|
com_pop(c, i-1); |
|
} |
|
switch (assigning) { |
|
case OP_DELETE: |
|
op = DELETE_SUBSCR; |
|
i = 2; |
|
break; |
|
default: |
|
case OP_ASSIGN: |
|
op = STORE_SUBSCR; |
|
i = 3; |
|
break; |
|
case OP_APPLY: |
|
op = BINARY_SUBSCR; |
|
i = 1; |
|
break; |
|
} |
|
if (assigning > OP_APPLY) { |
|
com_addoparg(c, DUP_TOPX, 2); |
|
com_push(c, 2); |
|
com_addbyte(c, BINARY_SUBSCR); |
|
com_pop(c, 1); |
|
com_node(c, augn); |
|
com_addbyte(c, assigning); |
|
com_pop(c, 1); |
|
com_addbyte(c, ROT_THREE); |
|
} |
|
com_addbyte(c, op); |
|
com_pop(c, i); |
|
} |
|
|
|
static void |
|
com_apply_trailer(struct compiling *c, node *n) |
|
{ |
|
REQ(n, trailer); |
|
switch (TYPE(CHILD(n, 0))) { |
|
case LPAR: |
|
com_call_function(c, CHILD(n, 1)); |
|
break; |
|
case DOT: |
|
com_select_member(c, CHILD(n, 1)); |
|
break; |
|
case LSQB: |
|
com_subscriptlist(c, CHILD(n, 1), OP_APPLY, NULL); |
|
break; |
|
default: |
|
com_error(c, PyExc_SystemError, |
|
"com_apply_trailer: unknown trailer type"); |
|
} |
|
} |
|
|
|
static void |
|
com_power(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
REQ(n, power); |
|
com_atom(c, CHILD(n, 0)); |
|
for (i = 1; i < NCH(n); i++) { |
|
if (TYPE(CHILD(n, i)) == DOUBLESTAR) { |
|
com_factor(c, CHILD(n, i+1)); |
|
com_addbyte(c, BINARY_POWER); |
|
com_pop(c, 1); |
|
break; |
|
} |
|
else |
|
com_apply_trailer(c, CHILD(n, i)); |
|
} |
|
} |
|
|
|
static void |
|
com_invert_constant(struct compiling *c, node *n) |
|
{ |
|
/* Compute the inverse of int and longs and use them directly, |
|
but be prepared to generate code for all other |
|
possibilities (invalid numbers, floats, complex). |
|
*/ |
|
PyObject *num, *inv = NULL; |
|
int i; |
|
|
|
REQ(n, NUMBER); |
|
num = parsenumber(c, STR(n)); |
|
if (num == NULL) |
|
i = 255; |
|
else { |
|
inv = PyNumber_Invert(num); |
|
if (inv == NULL) { |
|
PyErr_Clear(); |
|
i = com_addconst(c, num); |
|
} else { |
|
i = com_addconst(c, inv); |
|
Py_DECREF(inv); |
|
} |
|
Py_DECREF(num); |
|
} |
|
com_addoparg(c, LOAD_CONST, i); |
|
com_push(c, 1); |
|
if (num != NULL && inv == NULL) |
|
com_addbyte(c, UNARY_INVERT); |
|
} |
|
|
|
static int |
|
is_float_zero(const char *p) |
|
{ |
|
int found_radix_point = 0; |
|
int ch; |
|
while ((ch = Py_CHARMASK(*p++)) != '\0') { |
|
switch (ch) { |
|
case '0': |
|
/* no reason to believe it's not 0 -- continue */ |
|
break; |
|
|
|
case 'e': case 'E': case 'j': case 'J': |
|
/* If this was a hex constant, we already would have |
|
returned 0 due to the 'x' or 'X', so 'e' or 'E' |
|
must be an exponent marker, and we haven't yet |
|
seen a non-zero digit, and it doesn't matter what |
|
the exponent is then. For 'j' or 'J' similarly, |
|
except that this is an imaginary 0 then. */ |
|
return 1; |
|
|
|
case '.': |
|
found_radix_point = 1; |
|
break; |
|
|
|
default: |
|
return 0; |
|
} |
|
} |
|
return found_radix_point; |
|
} |
|
|
|
static void |
|
com_factor(struct compiling *c, node *n) |
|
{ |
|
int childtype = TYPE(CHILD(n, 0)); |
|
node *pfactor, *ppower, *patom, *pnum; |
|
REQ(n, factor); |
|
/* If the unary +, -, or ~ operator is applied to a constant, |
|
don't generate a UNARY_xxx opcode. Just store the |
|
approriate value as a constant. If the value is negative, |
|
extend the string containing the constant and insert a |
|
negative in the 0th position -- unless we're doing unary minus |
|
of a floating zero! In that case the sign is significant, but |
|
the const dict can't distinguish +0.0 from -0.0. |
|
*/ |
|
if ((childtype == PLUS || childtype == MINUS || childtype == TILDE) |
|
&& NCH(n) == 2 |
|
&& TYPE((pfactor = CHILD(n, 1))) == factor |
|
&& NCH(pfactor) == 1 |
|
&& TYPE((ppower = CHILD(pfactor, 0))) == power |
|
&& NCH(ppower) == 1 |
|
&& TYPE((patom = CHILD(ppower, 0))) == atom |
|
&& TYPE((pnum = CHILD(patom, 0))) == NUMBER |
|
&& !(childtype == MINUS && |
|
(STR(pnum)[0] == '0' || is_float_zero(STR(pnum))))) { |
|
if (childtype == TILDE) { |
|
com_invert_constant(c, pnum); |
|
return; |
|
} |
|
if (childtype == MINUS) { |
|
char *s = PyObject_MALLOC(strlen(STR(pnum)) + 2); |
|
if (s == NULL) { |
|
com_error(c, PyExc_MemoryError, ""); |
|
com_addbyte(c, 255); |
|
return; |
|
} |
|
s[0] = '-'; |
|
strcpy(s + 1, STR(pnum)); |
|
PyObject_FREE(STR(pnum)); |
|
STR(pnum) = s; |
|
} |
|
com_atom(c, patom); |
|
} |
|
else if (childtype == PLUS) { |
|
com_factor(c, CHILD(n, 1)); |
|
com_addbyte(c, UNARY_POSITIVE); |
|
} |
|
else if (childtype == MINUS) { |
|
com_factor(c, CHILD(n, 1)); |
|
com_addbyte(c, UNARY_NEGATIVE); |
|
} |
|
else if (childtype == TILDE) { |
|
com_factor(c, CHILD(n, 1)); |
|
com_addbyte(c, UNARY_INVERT); |
|
} |
|
else { |
|
com_power(c, CHILD(n, 0)); |
|
} |
|
} |
|
|
|
static void |
|
com_term(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
int op; |
|
REQ(n, term); |
|
com_factor(c, CHILD(n, 0)); |
|
for (i = 2; i < NCH(n); i += 2) { |
|
com_factor(c, CHILD(n, i)); |
|
switch (TYPE(CHILD(n, i-1))) { |
|
case STAR: |
|
op = BINARY_MULTIPLY; |
|
break; |
|
case SLASH: |
|
if (c->c_flags & CO_FUTURE_DIVISION) |
|
op = BINARY_TRUE_DIVIDE; |
|
else |
|
op = BINARY_DIVIDE; |
|
break; |
|
case PERCENT: |
|
op = BINARY_MODULO; |
|
break; |
|
case DOUBLESLASH: |
|
op = BINARY_FLOOR_DIVIDE; |
|
break; |
|
default: |
|
com_error(c, PyExc_SystemError, |
|
"com_term: operator not *, /, // or %"); |
|
op = 255; |
|
} |
|
com_addbyte(c, op); |
|
com_pop(c, 1); |
|
} |
|
} |
|
|
|
static void |
|
com_arith_expr(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
int op; |
|
REQ(n, arith_expr); |
|
com_term(c, CHILD(n, 0)); |
|
for (i = 2; i < NCH(n); i += 2) { |
|
com_term(c, CHILD(n, i)); |
|
switch (TYPE(CHILD(n, i-1))) { |
|
case PLUS: |
|
op = BINARY_ADD; |
|
break; |
|
case MINUS: |
|
op = BINARY_SUBTRACT; |
|
break; |
|
default: |
|
com_error(c, PyExc_SystemError, |
|
"com_arith_expr: operator not + or -"); |
|
op = 255; |
|
} |
|
com_addbyte(c, op); |
|
com_pop(c, 1); |
|
} |
|
} |
|
|
|
static void |
|
com_shift_expr(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
int op; |
|
REQ(n, shift_expr); |
|
com_arith_expr(c, CHILD(n, 0)); |
|
for (i = 2; i < NCH(n); i += 2) { |
|
com_arith_expr(c, CHILD(n, i)); |
|
switch (TYPE(CHILD(n, i-1))) { |
|
case LEFTSHIFT: |
|
op = BINARY_LSHIFT; |
|
break; |
|
case RIGHTSHIFT: |
|
op = BINARY_RSHIFT; |
|
break; |
|
default: |
|
com_error(c, PyExc_SystemError, |
|
"com_shift_expr: operator not << or >>"); |
|
op = 255; |
|
} |
|
com_addbyte(c, op); |
|
com_pop(c, 1); |
|
} |
|
} |
|
|
|
static void |
|
com_and_expr(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
int op; |
|
REQ(n, and_expr); |
|
com_shift_expr(c, CHILD(n, 0)); |
|
for (i = 2; i < NCH(n); i += 2) { |
|
com_shift_expr(c, CHILD(n, i)); |
|
if (TYPE(CHILD(n, i-1)) == AMPER) { |
|
op = BINARY_AND; |
|
} |
|
else { |
|
com_error(c, PyExc_SystemError, |
|
"com_and_expr: operator not &"); |
|
op = 255; |
|
} |
|
com_addbyte(c, op); |
|
com_pop(c, 1); |
|
} |
|
} |
|
|
|
static void |
|
com_xor_expr(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
int op; |
|
REQ(n, xor_expr); |
|
com_and_expr(c, CHILD(n, 0)); |
|
for (i = 2; i < NCH(n); i += 2) { |
|
com_and_expr(c, CHILD(n, i)); |
|
if (TYPE(CHILD(n, i-1)) == CIRCUMFLEX) { |
|
op = BINARY_XOR; |
|
} |
|
else { |
|
com_error(c, PyExc_SystemError, |
|
"com_xor_expr: operator not ^"); |
|
op = 255; |
|
} |
|
com_addbyte(c, op); |
|
com_pop(c, 1); |
|
} |
|
} |
|
|
|
static void |
|
com_expr(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
int op; |
|
REQ(n, expr); |
|
com_xor_expr(c, CHILD(n, 0)); |
|
for (i = 2; i < NCH(n); i += 2) { |
|
com_xor_expr(c, CHILD(n, i)); |
|
if (TYPE(CHILD(n, i-1)) == VBAR) { |
|
op = BINARY_OR; |
|
} |
|
else { |
|
com_error(c, PyExc_SystemError, |
|
"com_expr: expr operator not |"); |
|
op = 255; |
|
} |
|
com_addbyte(c, op); |
|
com_pop(c, 1); |
|
} |
|
} |
|
|
|
static enum cmp_op |
|
cmp_type(node *n) |
|
{ |
|
REQ(n, comp_op); |
|
/* comp_op: '<' | '>' | '>=' | '<=' | '<>' | '!=' | '==' |
|
| 'in' | 'not' 'in' | 'is' | 'is' not' */ |
|
if (NCH(n) == 1) { |
|
n = CHILD(n, 0); |
|
switch (TYPE(n)) { |
|
case LESS: return PyCmp_LT; |
|
case GREATER: return PyCmp_GT; |
|
case EQEQUAL: return PyCmp_EQ; |
|
case LESSEQUAL: return PyCmp_LE; |
|
case GREATEREQUAL: return PyCmp_GE; |
|
case NOTEQUAL: return PyCmp_NE; /* <> or != */ |
|
case NAME: if (strcmp(STR(n), "in") == 0) return PyCmp_IN; |
|
if (strcmp(STR(n), "is") == 0) return PyCmp_IS; |
|
} |
|
} |
|
else if (NCH(n) == 2) { |
|
switch (TYPE(CHILD(n, 0))) { |
|
case NAME: if (strcmp(STR(CHILD(n, 1)), "in") == 0) |
|
return PyCmp_NOT_IN; |
|
if (strcmp(STR(CHILD(n, 0)), "is") == 0) |
|
return PyCmp_IS_NOT; |
|
} |
|
} |
|
return PyCmp_BAD; |
|
} |
|
|
|
static void |
|
com_comparison(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
enum cmp_op op; |
|
int anchor; |
|
REQ(n, comparison); /* comparison: expr (comp_op expr)* */ |
|
com_expr(c, CHILD(n, 0)); |
|
if (NCH(n) == 1) |
|
return; |
|
|
|
/**************************************************************** |
|
The following code is generated for all but the last |
|
comparison in a chain: |
|
|
|
label: on stack: opcode: jump to: |
|
|
|
a <code to load b> |
|
a, b DUP_TOP |
|
a, b, b ROT_THREE |
|
b, a, b COMPARE_OP |
|
b, 0-or-1 JUMP_IF_FALSE L1 |
|
b, 1 POP_TOP |
|
b |
|
|
|
We are now ready to repeat this sequence for the next |
|
comparison in the chain. |
|
|
|
For the last we generate: |
|
|
|
b <code to load c> |
|
b, c COMPARE_OP |
|
0-or-1 |
|
|
|
If there were any jumps to L1 (i.e., there was more than one |
|
comparison), we generate: |
|
|
|
0-or-1 JUMP_FORWARD L2 |
|
L1: b, 0 ROT_TWO |
|
0, b POP_TOP |
|
0 |
|
L2: 0-or-1 |
|
****************************************************************/ |
|
|
|
anchor = 0; |
|
|
|
for (i = 2; i < NCH(n); i += 2) { |
|
com_expr(c, CHILD(n, i)); |
|
if (i+2 < NCH(n)) { |
|
com_addbyte(c, DUP_TOP); |
|
com_push(c, 1); |
|
com_addbyte(c, ROT_THREE); |
|
} |
|
op = cmp_type(CHILD(n, i-1)); |
|
if (op == PyCmp_BAD) { |
|
com_error(c, PyExc_SystemError, |
|
"com_comparison: unknown comparison op"); |
|
} |
|
com_addoparg(c, COMPARE_OP, op); |
|
com_pop(c, 1); |
|
if (i+2 < NCH(n)) { |
|
com_addfwref(c, JUMP_IF_FALSE, &anchor); |
|
com_addbyte(c, POP_TOP); |
|
com_pop(c, 1); |
|
} |
|
} |
|
|
|
if (anchor) { |
|
int anchor2 = 0; |
|
com_addfwref(c, JUMP_FORWARD, &anchor2); |
|
com_backpatch(c, anchor); |
|
com_addbyte(c, ROT_TWO); |
|
com_addbyte(c, POP_TOP); |
|
com_backpatch(c, anchor2); |
|
} |
|
} |
|
|
|
static void |
|
com_not_test(struct compiling *c, node *n) |
|
{ |
|
REQ(n, not_test); /* 'not' not_test | comparison */ |
|
if (NCH(n) == 1) { |
|
com_comparison(c, CHILD(n, 0)); |
|
} |
|
else { |
|
com_not_test(c, CHILD(n, 1)); |
|
com_addbyte(c, UNARY_NOT); |
|
} |
|
} |
|
|
|
static void |
|
com_and_test(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
int anchor; |
|
REQ(n, and_test); /* not_test ('and' not_test)* */ |
|
anchor = 0; |
|
i = 0; |
|
for (;;) { |
|
com_not_test(c, CHILD(n, i)); |
|
if ((i += 2) >= NCH(n)) |
|
break; |
|
com_addfwref(c, JUMP_IF_FALSE, &anchor); |
|
com_addbyte(c, POP_TOP); |
|
com_pop(c, 1); |
|
} |
|
if (anchor) |
|
com_backpatch(c, anchor); |
|
} |
|
|
|
static int |
|
com_make_closure(struct compiling *c, PyCodeObject *co) |
|
{ |
|
int i, free = PyCode_GetNumFree(co); |
|
if (free == 0) |
|
return 0; |
|
for (i = 0; i < free; ++i) { |
|
/* Bypass com_addop_varname because it will generate |
|
LOAD_DEREF but LOAD_CLOSURE is needed. |
|
*/ |
|
PyObject *name = PyTuple_GET_ITEM(co->co_freevars, i); |
|
int arg, reftype; |
|
|
|
/* Special case: If a class contains a method with a |
|
free variable that has the same name as a method, |
|
the name will be considered free *and* local in the |
|
class. It should be handled by the closure, as |
|
well as by the normal name loookup logic. |
|
*/ |
|
reftype = get_ref_type(c, PyString_AS_STRING(name)); |
|
if (reftype == CELL) |
|
arg = com_lookup_arg(c->c_cellvars, name); |
|
else /* (reftype == FREE) */ |
|
arg = com_lookup_arg(c->c_freevars, name); |
|
if (arg == -1) { |
|
fprintf(stderr, "lookup %s in %s %d %d\n" |
|
"freevars of %s: %s\n", |
|
PyObject_REPR(name), |
|
c->c_name, |
|
reftype, arg, |
|
PyString_AS_STRING(co->co_name), |
|
PyObject_REPR(co->co_freevars)); |
|
Py_FatalError("com_make_closure()"); |
|
} |
|
com_addoparg(c, LOAD_CLOSURE, arg); |
|
|
|
} |
|
com_push(c, free); |
|
return 1; |
|
} |
|
|
|
static void |
|
com_test(struct compiling *c, node *n) |
|
{ |
|
REQ(n, test); /* and_test ('or' and_test)* | lambdef */ |
|
if (NCH(n) == 1 && TYPE(CHILD(n, 0)) == lambdef) { |
|
PyCodeObject *co; |
|
int i, closure; |
|
int ndefs = com_argdefs(c, CHILD(n, 0)); |
|
symtable_enter_scope(c->c_symtable, "lambda", lambdef, |
|
n->n_lineno); |
|
co = icompile(CHILD(n, 0), c); |
|
if (co == NULL) { |
|
c->c_errors++; |
|
return; |
|
} |
|
symtable_exit_scope(c->c_symtable); |
|
i = com_addconst(c, (PyObject *)co); |
|
closure = com_make_closure(c, co); |
|
com_addoparg(c, LOAD_CONST, i); |
|
com_push(c, 1); |
|
if (closure) { |
|
com_addoparg(c, MAKE_CLOSURE, ndefs); |
|
com_pop(c, PyCode_GetNumFree(co)); |
|
} else |
|
com_addoparg(c, MAKE_FUNCTION, ndefs); |
|
Py_DECREF(co); |
|
com_pop(c, ndefs); |
|
} |
|
else { |
|
int anchor = 0; |
|
int i = 0; |
|
for (;;) { |
|
com_and_test(c, CHILD(n, i)); |
|
if ((i += 2) >= NCH(n)) |
|
break; |
|
com_addfwref(c, JUMP_IF_TRUE, &anchor); |
|
com_addbyte(c, POP_TOP); |
|
com_pop(c, 1); |
|
} |
|
if (anchor) |
|
com_backpatch(c, anchor); |
|
} |
|
} |
|
|
|
static void |
|
com_list(struct compiling *c, node *n, int toplevel) |
|
{ |
|
/* exprlist: expr (',' expr)* [',']; likewise for testlist */ |
|
if (NCH(n) == 1 && !toplevel) { |
|
com_node(c, CHILD(n, 0)); |
|
} |
|
else { |
|
int i; |
|
int len; |
|
len = (NCH(n) + 1) / 2; |
|
for (i = 0; i < NCH(n); i += 2) |
|
com_node(c, CHILD(n, i)); |
|
com_addoparg(c, BUILD_TUPLE, len); |
|
com_pop(c, len-1); |
|
} |
|
} |
|
|
|
|
|
/* Begin of assignment compilation */ |
|
|
|
|
|
static void |
|
com_augassign_attr(struct compiling *c, node *n, int opcode, node *augn) |
|
{ |
|
com_addbyte(c, DUP_TOP); |
|
com_push(c, 1); |
|
com_addopname(c, LOAD_ATTR, n); |
|
com_node(c, augn); |
|
com_addbyte(c, opcode); |
|
com_pop(c, 1); |
|
com_addbyte(c, ROT_TWO); |
|
com_addopname(c, STORE_ATTR, n); |
|
com_pop(c, 2); |
|
} |
|
|
|
static void |
|
com_assign_attr(struct compiling *c, node *n, int assigning) |
|
{ |
|
if (none_assignment_check(c, STR(n), assigning)) |
|
return; |
|
com_addopname(c, assigning ? STORE_ATTR : DELETE_ATTR, n); |
|
com_pop(c, assigning ? 2 : 1); |
|
} |
|
|
|
static void |
|
com_assign_trailer(struct compiling *c, node *n, int assigning, node *augn) |
|
{ |
|
REQ(n, trailer); |
|
switch (TYPE(CHILD(n, 0))) { |
|
case LPAR: /* '(' [exprlist] ')' */ |
|
if (assigning == OP_DELETE) |
|
com_error(c, PyExc_SyntaxError, |
|
"can't delete function call"); |
|
else |
|
com_error(c, PyExc_SyntaxError, |
|
"can't assign to function call"); |
|
break; |
|
case DOT: /* '.' NAME */ |
|
if (assigning > OP_APPLY) |
|
com_augassign_attr(c, CHILD(n, 1), assigning, augn); |
|
else |
|
com_assign_attr(c, CHILD(n, 1), assigning); |
|
break; |
|
case LSQB: /* '[' subscriptlist ']' */ |
|
com_subscriptlist(c, CHILD(n, 1), assigning, augn); |
|
break; |
|
default: |
|
com_error(c, PyExc_SystemError, "unknown trailer type"); |
|
} |
|
} |
|
|
|
static void |
|
com_assign_sequence(struct compiling *c, node *n, int assigning) |
|
{ |
|
int i; |
|
if (TYPE(n) != testlist && TYPE(n) != listmaker) |
|
REQ(n, exprlist); |
|
if (assigning) { |
|
i = (NCH(n)+1)/2; |
|
com_addoparg(c, UNPACK_SEQUENCE, i); |
|
com_push(c, i-1); |
|
} |
|
for (i = 0; i < NCH(n); i += 2) |
|
com_assign(c, CHILD(n, i), assigning, NULL); |
|
} |
|
|
|
static void |
|
com_augassign_name(struct compiling *c, node *n, int opcode, node *augn) |
|
{ |
|
REQ(n, NAME); |
|
com_addop_varname(c, VAR_LOAD, STR(n)); |
|
com_push(c, 1); |
|
com_node(c, augn); |
|
com_addbyte(c, opcode); |
|
com_pop(c, 1); |
|
com_assign_name(c, n, OP_ASSIGN); |
|
} |
|
|
|
static void |
|
com_assign_name(struct compiling *c, node *n, int assigning) |
|
{ |
|
REQ(n, NAME); |
|
com_addop_varname(c, assigning ? VAR_STORE : VAR_DELETE, STR(n)); |
|
if (assigning) |
|
com_pop(c, 1); |
|
} |
|
|
|
static void |
|
com_assign(struct compiling *c, node *n, int assigning, node *augn) |
|
{ |
|
/* Loop to avoid trivial recursion */ |
|
for (;;) { |
|
switch (TYPE(n)) { |
|
|
|
case exprlist: |
|
case testlist: |
|
case testlist1: |
|
if (NCH(n) > 1) { |
|
if (assigning > OP_APPLY) { |
|
com_error(c, PyExc_SyntaxError, |
|
"augmented assign to tuple not possible"); |
|
return; |
|
} |
|
com_assign_sequence(c, n, assigning); |
|
return; |
|
} |
|
n = CHILD(n, 0); |
|
break; |
|
|
|
case test: |
|
case and_test: |
|
case not_test: |
|
case comparison: |
|
case expr: |
|
case xor_expr: |
|
case and_expr: |
|
case shift_expr: |
|
case arith_expr: |
|
case term: |
|
case factor: |
|
if (NCH(n) > 1) { |
|
com_error(c, PyExc_SyntaxError, |
|
"can't assign to operator"); |
|
return; |
|
} |
|
n = CHILD(n, 0); |
|
break; |
|
|
|
case power: /* atom trailer* ('**' power)* |
|
('+'|'-'|'~') factor | atom trailer* */ |
|
if (TYPE(CHILD(n, 0)) != atom) { |
|
com_error(c, PyExc_SyntaxError, |
|
"can't assign to operator"); |
|
return; |
|
} |
|
if (NCH(n) > 1) { /* trailer or exponent present */ |
|
int i; |
|
com_node(c, CHILD(n, 0)); |
|
for (i = 1; i+1 < NCH(n); i++) { |
|
if (TYPE(CHILD(n, i)) == DOUBLESTAR) { |
|
com_error(c, PyExc_SyntaxError, |
|
"can't assign to operator"); |
|
return; |
|
} |
|
com_apply_trailer(c, CHILD(n, i)); |
|
} /* NB i is still alive */ |
|
com_assign_trailer(c, |
|
CHILD(n, i), assigning, augn); |
|
return; |
|
} |
|
n = CHILD(n, 0); |
|
break; |
|
|
|
case atom: |
|
switch (TYPE(CHILD(n, 0))) { |
|
case LPAR: |
|
n = CHILD(n, 1); |
|
if (TYPE(n) == RPAR) { |
|
/* XXX Should allow () = () ??? */ |
|
com_error(c, PyExc_SyntaxError, |
|
"can't assign to ()"); |
|
return; |
|
} |
|
if (assigning > OP_APPLY) { |
|
com_error(c, PyExc_SyntaxError, |
|
"augmented assign to tuple literal not possible"); |
|
return; |
|
} |
|
break; |
|
case LSQB: |
|
n = CHILD(n, 1); |
|
if (TYPE(n) == RSQB) { |
|
com_error(c, PyExc_SyntaxError, |
|
"can't assign to []"); |
|
return; |
|
} |
|
if (assigning > OP_APPLY) { |
|
com_error(c, PyExc_SyntaxError, |
|
"augmented assign to list literal 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); |
|
return; |
|
case NAME: |
|
if (assigning > OP_APPLY) |
|
com_augassign_name(c, CHILD(n, 0), |
|
assigning, augn); |
|
else |
|
com_assign_name(c, CHILD(n, 0), |
|
assigning); |
|
return; |
|
default: |
|
com_error(c, PyExc_SyntaxError, |
|
"can't assign to literal"); |
|
return; |
|
} |
|
break; |
|
|
|
case lambdef: |
|
com_error(c, PyExc_SyntaxError, |
|
"can't assign to lambda"); |
|
return; |
|
|
|
default: |
|
com_error(c, PyExc_SystemError, |
|
"com_assign: bad node"); |
|
return; |
|
|
|
} |
|
} |
|
} |
|
|
|
static void |
|
com_augassign(struct compiling *c, node *n) |
|
{ |
|
int opcode; |
|
|
|
switch (STR(CHILD(CHILD(n, 1), 0))[0]) { |
|
case '+': opcode = INPLACE_ADD; break; |
|
case '-': opcode = INPLACE_SUBTRACT; break; |
|
case '/': |
|
if (STR(CHILD(CHILD(n, 1), 0))[1] == '/') |
|
opcode = INPLACE_FLOOR_DIVIDE; |
|
else if (c->c_flags & CO_FUTURE_DIVISION) |
|
opcode = INPLACE_TRUE_DIVIDE; |
|
else |
|
opcode = INPLACE_DIVIDE; |
|
break; |
|
case '%': opcode = INPLACE_MODULO; break; |
|
case '<': opcode = INPLACE_LSHIFT; break; |
|
case '>': opcode = INPLACE_RSHIFT; break; |
|
case '&': opcode = INPLACE_AND; break; |
|
case '^': opcode = INPLACE_XOR; break; |
|
case '|': opcode = INPLACE_OR; break; |
|
case '*': |
|
if (STR(CHILD(CHILD(n, 1), 0))[1] == '*') |
|
opcode = INPLACE_POWER; |
|
else |
|
opcode = INPLACE_MULTIPLY; |
|
break; |
|
default: |
|
com_error(c, PyExc_SystemError, "com_augassign: bad operator"); |
|
return; |
|
} |
|
com_assign(c, CHILD(n, 0), opcode, CHILD(n, 2)); |
|
} |
|
|
|
static void |
|
com_expr_stmt(struct compiling *c, node *n) |
|
{ |
|
REQ(n, expr_stmt); |
|
/* testlist (('=' testlist)* | augassign testlist) */ |
|
/* Forget it if we have just a doc string here */ |
|
if (!c->c_interactive && NCH(n) == 1 && get_rawdocstring(n) != NULL) |
|
return; |
|
if (NCH(n) == 1) { |
|
com_node(c, CHILD(n, NCH(n)-1)); |
|
if (c->c_interactive) |
|
com_addbyte(c, PRINT_EXPR); |
|
else |
|
com_addbyte(c, POP_TOP); |
|
com_pop(c, 1); |
|
} |
|
else if (TYPE(CHILD(n,1)) == augassign) |
|
com_augassign(c, n); |
|
else { |
|
int i; |
|
com_node(c, CHILD(n, NCH(n)-1)); |
|
for (i = 0; i < NCH(n)-2; i+=2) { |
|
if (i+2 < NCH(n)-2) { |
|
com_addbyte(c, DUP_TOP); |
|
com_push(c, 1); |
|
} |
|
com_assign(c, CHILD(n, i), OP_ASSIGN, NULL); |
|
} |
|
} |
|
} |
|
|
|
static void |
|
com_assert_stmt(struct compiling *c, node *n) |
|
{ |
|
int a = 0; |
|
int i; |
|
REQ(n, assert_stmt); /* 'assert' test [',' test] */ |
|
if (Py_OptimizeFlag) |
|
return; |
|
/* Generate code like |
|
|
|
if not <test>: |
|
raise AssertionError [, <message>] |
|
|
|
where <message> is the second test, if present. |
|
*/ |
|
com_node(c, CHILD(n, 1)); |
|
com_addfwref(c, JUMP_IF_TRUE, &a); |
|
com_addbyte(c, POP_TOP); |
|
com_pop(c, 1); |
|
/* Raise that exception! */ |
|
com_addop_name(c, LOAD_GLOBAL, "AssertionError"); |
|
com_push(c, 1); |
|
i = NCH(n)/2; /* Either 2 or 4 */ |
|
if (i > 1) |
|
com_node(c, CHILD(n, 3)); |
|
com_addoparg(c, RAISE_VARARGS, i); |
|
com_pop(c, i); |
|
/* The interpreter does not fall through */ |
|
/* Jump ends up here */ |
|
com_backpatch(c, a); |
|
com_addbyte(c, POP_TOP); |
|
} |
|
|
|
static void |
|
com_print_stmt(struct compiling *c, node *n) |
|
{ |
|
int i = 1; |
|
node* stream = NULL; |
|
|
|
REQ(n, print_stmt); /* 'print' (test ',')* [test] */ |
|
|
|
/* are we using the extended print form? */ |
|
if (NCH(n) >= 2 && TYPE(CHILD(n, 1)) == RIGHTSHIFT) { |
|
stream = CHILD(n, 2); |
|
com_node(c, stream); |
|
/* stack: [...] => [... stream] */ |
|
com_push(c, 1); |
|
if (NCH(n) > 3 && TYPE(CHILD(n, 3)) == COMMA) |
|
i = 4; |
|
else |
|
i = 3; |
|
} |
|
for (; i < NCH(n); i += 2) { |
|
if (stream != NULL) { |
|
com_addbyte(c, DUP_TOP); |
|
/* stack: [stream] => [stream stream] */ |
|
com_push(c, 1); |
|
com_node(c, CHILD(n, i)); |
|
/* stack: [stream stream] => [stream stream obj] */ |
|
com_addbyte(c, ROT_TWO); |
|
/* stack: [stream stream obj] => [stream obj stream] */ |
|
com_addbyte(c, PRINT_ITEM_TO); |
|
/* stack: [stream obj stream] => [stream] */ |
|
com_pop(c, 2); |
|
} |
|
else { |
|
com_node(c, CHILD(n, i)); |
|
/* stack: [...] => [... obj] */ |
|
com_addbyte(c, PRINT_ITEM); |
|
com_pop(c, 1); |
|
} |
|
} |
|
/* XXX Alternatively, LOAD_CONST '\n' and then PRINT_ITEM */ |
|
if (TYPE(CHILD(n, NCH(n)-1)) == COMMA) { |
|
if (stream != NULL) { |
|
/* must pop the extra stream object off the stack */ |
|
com_addbyte(c, POP_TOP); |
|
/* stack: [... stream] => [...] */ |
|
com_pop(c, 1); |
|
} |
|
} |
|
else { |
|
if (stream != NULL) { |
|
/* this consumes the last stream object on stack */ |
|
com_addbyte(c, PRINT_NEWLINE_TO); |
|
/* stack: [... stream] => [...] */ |
|
com_pop(c, 1); |
|
} |
|
else |
|
com_addbyte(c, PRINT_NEWLINE); |
|
} |
|
} |
|
|
|
static void |
|
com_return_stmt(struct compiling *c, node *n) |
|
{ |
|
REQ(n, return_stmt); /* 'return' [testlist] */ |
|
if (!c->c_infunction) { |
|
com_error(c, PyExc_SyntaxError, "'return' outside function"); |
|
} |
|
if (c->c_flags & CO_GENERATOR) { |
|
if (NCH(n) > 1) { |
|
com_error(c, PyExc_SyntaxError, |
|
"'return' with argument inside generator"); |
|
} |
|
} |
|
if (NCH(n) < 2) { |
|
com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); |
|
com_push(c, 1); |
|
} |
|
else |
|
com_node(c, CHILD(n, 1)); |
|
com_addbyte(c, RETURN_VALUE); |
|
com_pop(c, 1); |
|
} |
|
|
|
static void |
|
com_yield_stmt(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
REQ(n, yield_stmt); /* 'yield' testlist */ |
|
if (!c->c_infunction) { |
|
com_error(c, PyExc_SyntaxError, "'yield' outside function"); |
|
} |
|
|
|
for (i = 0; i < c->c_nblocks; ++i) { |
|
if (c->c_block[i] == SETUP_FINALLY) { |
|
com_error(c, PyExc_SyntaxError, |
|
"'yield' not allowed in a 'try' block " |
|
"with a 'finally' clause"); |
|
return; |
|
} |
|
} |
|
com_node(c, CHILD(n, 1)); |
|
com_addbyte(c, YIELD_VALUE); |
|
com_pop(c, 1); |
|
} |
|
|
|
static void |
|
com_raise_stmt(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
REQ(n, raise_stmt); /* 'raise' [test [',' test [',' test]]] */ |
|
if (NCH(n) > 1) { |
|
com_node(c, CHILD(n, 1)); |
|
if (NCH(n) > 3) { |
|
com_node(c, CHILD(n, 3)); |
|
if (NCH(n) > 5) |
|
com_node(c, CHILD(n, 5)); |
|
} |
|
} |
|
i = NCH(n)/2; |
|
com_addoparg(c, RAISE_VARARGS, i); |
|
com_pop(c, i); |
|
} |
|
|
|
static void |
|
com_from_import(struct compiling *c, node *n) |
|
{ |
|
com_addopname(c, IMPORT_FROM, CHILD(n, 0)); |
|
com_push(c, 1); |
|
if (NCH(n) > 1) { |
|
if (strcmp(STR(CHILD(n, 1)), "as") != 0) { |
|
com_error(c, PyExc_SyntaxError, "invalid syntax"); |
|
return; |
|
} |
|
com_addop_varname(c, VAR_STORE, STR(CHILD(n, 2))); |
|
} else |
|
com_addop_varname(c, VAR_STORE, STR(CHILD(n, 0))); |
|
com_pop(c, 1); |
|
} |
|
|
|
static void |
|
com_import_stmt(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
REQ(n, import_stmt); |
|
/* 'import' dotted_name (',' dotted_name)* | |
|
'from' dotted_name 'import' ('*' | NAME (',' NAME)*) */ |
|
if (STR(CHILD(n, 0))[0] == 'f') { |
|
PyObject *tup; |
|
/* 'from' dotted_name 'import' ... */ |
|
REQ(CHILD(n, 1), dotted_name); |
|
|
|
if (TYPE(CHILD(n, 3)) == STAR) { |
|
tup = Py_BuildValue("(s)", "*"); |
|
} else { |
|
tup = PyTuple_New((NCH(n) - 2)/2); |
|
for (i = 3; i < NCH(n); i += 2) { |
|
PyTuple_SET_ITEM(tup, (i-3)/2, |
|
PyString_FromString(STR( |
|
CHILD(CHILD(n, i), 0)))); |
|
} |
|
} |
|
com_addoparg(c, LOAD_CONST, com_addconst(c, tup)); |
|
Py_DECREF(tup); |
|
com_push(c, 1); |
|
com_addopname(c, IMPORT_NAME, CHILD(n, 1)); |
|
if (TYPE(CHILD(n, 3)) == STAR) |
|
com_addbyte(c, IMPORT_STAR); |
|
else { |
|
for (i = 3; i < NCH(n); i += 2) |
|
com_from_import(c, CHILD(n, i)); |
|
com_addbyte(c, POP_TOP); |
|
} |
|
com_pop(c, 1); |
|
} |
|
else { |
|
/* 'import' ... */ |
|
for (i = 1; i < NCH(n); i += 2) { |
|
node *subn = CHILD(n, i); |
|
REQ(subn, dotted_as_name); |
|
com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); |
|
com_push(c, 1); |
|
com_addopname(c, IMPORT_NAME, CHILD(subn, 0)); |
|
if (NCH(subn) > 1) { |
|
int j; |
|
if (strcmp(STR(CHILD(subn, 1)), "as") != 0) { |
|
com_error(c, PyExc_SyntaxError, |
|
"invalid syntax"); |
|
return; |
|
} |
|
for (j=2 ; j < NCH(CHILD(subn, 0)); j += 2) |
|
com_addopname(c, LOAD_ATTR, |
|
CHILD(CHILD(subn, 0), |
|
j)); |
|
com_addop_varname(c, VAR_STORE, |
|
STR(CHILD(subn, 2))); |
|
} else |
|
com_addop_varname(c, VAR_STORE, |
|
STR(CHILD(CHILD(subn, 0), |
|
0))); |
|
com_pop(c, 1); |
|
} |
|
} |
|
} |
|
|
|
static void |
|
com_exec_stmt(struct compiling *c, node *n) |
|
{ |
|
REQ(n, exec_stmt); |
|
/* exec_stmt: 'exec' expr ['in' expr [',' expr]] */ |
|
com_node(c, CHILD(n, 1)); |
|
if (NCH(n) >= 4) |
|
com_node(c, CHILD(n, 3)); |
|
else { |
|
com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); |
|
com_push(c, 1); |
|
} |
|
if (NCH(n) >= 6) |
|
com_node(c, CHILD(n, 5)); |
|
else { |
|
com_addbyte(c, DUP_TOP); |
|
com_push(c, 1); |
|
} |
|
com_addbyte(c, EXEC_STMT); |
|
com_pop(c, 3); |
|
} |
|
|
|
static int |
|
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: |
|
switch (TYPE(n)) { |
|
|
|
case suite: |
|
if (NCH(n) == 1) { |
|
n = CHILD(n, 0); |
|
goto next; |
|
} |
|
/* Fall through */ |
|
case file_input: |
|
for (i = 0; i < NCH(n); i++) { |
|
node *ch = CHILD(n, i); |
|
if (TYPE(ch) == stmt) { |
|
n = ch; |
|
goto next; |
|
} |
|
} |
|
break; |
|
|
|
case stmt: |
|
case simple_stmt: |
|
case small_stmt: |
|
n = CHILD(n, 0); |
|
goto next; |
|
|
|
case expr_stmt: |
|
case testlist: |
|
case testlist1: |
|
case test: |
|
case and_test: |
|
case not_test: |
|
case comparison: |
|
case expr: |
|
case xor_expr: |
|
case and_expr: |
|
case shift_expr: |
|
case arith_expr: |
|
case term: |
|
case factor: |
|
case power: |
|
case atom: |
|
if (NCH(n) == 1) { |
|
n = CHILD(n, 0); |
|
goto next; |
|
} |
|
break; |
|
|
|
case NAME: |
|
if (Py_OptimizeFlag && strcmp(STR(n), "__debug__") == 0) |
|
return 1; |
|
break; |
|
|
|
case NUMBER: |
|
v = parsenumber(c, STR(n)); |
|
if (v == NULL) { |
|
PyErr_Clear(); |
|
break; |
|
} |
|
i = PyObject_IsTrue(v); |
|
Py_DECREF(v); |
|
return i == 0; |
|
|
|
case STRING: |
|
v = parsestr(c, STR(n)); |
|
if (v == NULL) { |
|
PyErr_Clear(); |
|
break; |
|
} |
|
i = PyObject_IsTrue(v); |
|
Py_DECREF(v); |
|
return i == 0; |
|
|
|
} |
|
return 0; |
|
} |
|
|
|
|
|
/* Look under n for a return stmt with an expression. |
|
* This hack is used to find illegal returns under "if 0:" blocks in |
|
* functions already known to be generators (as determined by the symtable |
|
* pass). |
|
* Return the offending return node if found, else NULL. |
|
*/ |
|
static node * |
|
look_for_offending_return(node *n) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < NCH(n); ++i) { |
|
node *kid = CHILD(n, i); |
|
|
|
switch (TYPE(kid)) { |
|
case classdef: |
|
case funcdef: |
|
case lambdef: |
|
/* Stuff in nested functions & classes doesn't |
|
affect the code block we started in. */ |
|
return NULL; |
|
|
|
case return_stmt: |
|
if (NCH(kid) > 1) |
|
return kid; |
|
break; |
|
|
|
default: { |
|
node *bad = look_for_offending_return(kid); |
|
if (bad != NULL) |
|
return bad; |
|
} |
|
} |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
static void |
|
com_if_stmt(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
int anchor = 0; |
|
REQ(n, if_stmt); |
|
/*'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite] */ |
|
for (i = 0; i+3 < NCH(n); i+=4) { |
|
int a = 0; |
|
node *ch = CHILD(n, i+1); |
|
if (is_constant_false(c, ch)) { |
|
/* We're going to skip this block. However, if this |
|
is a generator, we have to check the dead code |
|
anyway to make sure there aren't any return stmts |
|
with expressions, in the same scope. */ |
|
if (c->c_flags & CO_GENERATOR) { |
|
node *p = look_for_offending_return(n); |
|
if (p != NULL) { |
|
int savelineno = c->c_lineno; |
|
c->c_lineno = p->n_lineno; |
|
com_error(c, PyExc_SyntaxError, |
|
"'return' with argument " |
|
"inside generator"); |
|
c->c_lineno = savelineno; |
|
} |
|
} |
|
continue; |
|
} |
|
if (i > 0) |
|
com_set_lineno(c, ch->n_lineno); |
|
com_node(c, ch); |
|
com_addfwref(c, JUMP_IF_FALSE, &a); |
|
com_addbyte(c, POP_TOP); |
|
com_pop(c, 1); |
|
com_node(c, CHILD(n, i+3)); |
|
com_addfwref(c, JUMP_FORWARD, &anchor); |
|
com_backpatch(c, a); |
|
/* We jump here with an extra entry which we now pop */ |
|
com_addbyte(c, POP_TOP); |
|
} |
|
if (i+2 < NCH(n)) |
|
com_node(c, CHILD(n, i+2)); |
|
if (anchor) |
|
com_backpatch(c, anchor); |
|
} |
|
|
|
static void |
|
com_while_stmt(struct compiling *c, node *n) |
|
{ |
|
int break_anchor = 0; |
|
int anchor = 0; |
|
int save_begin = c->c_begin; |
|
REQ(n, while_stmt); /* 'while' test ':' suite ['else' ':' suite] */ |
|
com_addfwref(c, SETUP_LOOP, &break_anchor); |
|
block_push(c, SETUP_LOOP); |
|
c->c_begin = c->c_nexti; |
|
com_set_lineno(c, n->n_lineno); |
|
com_node(c, CHILD(n, 1)); |
|
com_addfwref(c, JUMP_IF_FALSE, &anchor); |
|
com_addbyte(c, POP_TOP); |
|
com_pop(c, 1); |
|
c->c_loops++; |
|
com_node(c, CHILD(n, 3)); |
|
c->c_loops--; |
|
com_addoparg(c, JUMP_ABSOLUTE, c->c_begin); |
|
c->c_begin = save_begin; |
|
com_backpatch(c, anchor); |
|
/* We jump here with one entry more on the stack */ |
|
com_addbyte(c, POP_TOP); |
|
com_addbyte(c, POP_BLOCK); |
|
block_pop(c, SETUP_LOOP); |
|
if (NCH(n) > 4) |
|
com_node(c, CHILD(n, 6)); |
|
com_backpatch(c, break_anchor); |
|
} |
|
|
|
static void |
|
com_for_stmt(struct compiling *c, node *n) |
|
{ |
|
int break_anchor = 0; |
|
int anchor = 0; |
|
int save_begin = c->c_begin; |
|
REQ(n, for_stmt); |
|
/* 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite] */ |
|
com_addfwref(c, SETUP_LOOP, &break_anchor); |
|
block_push(c, SETUP_LOOP); |
|
com_node(c, CHILD(n, 3)); |
|
com_addbyte(c, GET_ITER); |
|
c->c_begin = c->c_nexti; |
|
com_set_lineno(c, c->c_last_line); |
|
com_addfwref(c, FOR_ITER, &anchor); |
|
com_push(c, 1); |
|
com_assign(c, CHILD(n, 1), OP_ASSIGN, NULL); |
|
c->c_loops++; |
|
com_node(c, CHILD(n, 5)); |
|
c->c_loops--; |
|
com_addoparg(c, JUMP_ABSOLUTE, c->c_begin); |
|
c->c_begin = save_begin; |
|
com_backpatch(c, anchor); |
|
com_pop(c, 1); /* FOR_ITER has popped this */ |
|
com_addbyte(c, POP_BLOCK); |
|
block_pop(c, SETUP_LOOP); |
|
if (NCH(n) > 8) |
|
com_node(c, CHILD(n, 8)); |
|
com_backpatch(c, break_anchor); |
|
} |
|
|
|
/* Code generated for "try: S finally: Sf" is as follows: |
|
|
|
SETUP_FINALLY L |
|
<code for S> |
|
POP_BLOCK |
|
LOAD_CONST <nil> |
|
L: <code for Sf> |
|
END_FINALLY |
|
|
|
The special instructions use the block stack. Each block |
|
stack entry contains the instruction that created it (here |
|
SETUP_FINALLY), the level of the value stack at the time the |
|
block stack entry was created, and a label (here L). |
|
|
|
SETUP_FINALLY: |
|
Pushes the current value stack level and the label |
|
onto the block stack. |
|
POP_BLOCK: |
|
Pops en entry from the block stack, and pops the value |
|
stack until its level is the same as indicated on the |
|
block stack. (The label is ignored.) |
|
END_FINALLY: |
|
Pops a variable number of entries from the *value* stack |
|
and re-raises the exception they specify. The number of |
|
entries popped depends on the (pseudo) exception type. |
|
|
|
The block stack is unwound when an exception is raised: |
|
when a SETUP_FINALLY entry is found, the exception is pushed |
|
onto the value stack (and the exception condition is cleared), |
|
and the interpreter jumps to the label gotten from the block |
|
stack. |
|
|
|
Code generated for "try: S except E1, V1: S1 except E2, V2: S2 ...": |
|
(The contents of the value stack is shown in [], with the top |
|
at the right; 'tb' is trace-back info, 'val' the exception's |
|
associated value, and 'exc' the exception.) |
|
|
|
Value stack Label Instruction Argument |
|
[] SETUP_EXCEPT L1 |
|
[] <code for S> |
|
[] POP_BLOCK |
|
[] JUMP_FORWARD L0 |
|
|
|
[tb, val, exc] L1: DUP ) |
|
[tb, val, exc, exc] <evaluate E1> ) |
|
[tb, val, exc, exc, E1] COMPARE_OP EXC_MATCH ) only if E1 |
|
[tb, val, exc, 1-or-0] JUMP_IF_FALSE L2 ) |
|
[tb, val, exc, 1] POP ) |
|
[tb, val, exc] POP |
|
[tb, val] <assign to V1> (or POP if no V1) |
|
[tb] POP |
|
[] <code for S1> |
|
JUMP_FORWARD L0 |
|
|
|
[tb, val, exc, 0] L2: POP |
|
[tb, val, exc] DUP |
|
.............................etc....................... |
|
|
|
[tb, val, exc, 0] Ln+1: POP |
|
[tb, val, exc] END_FINALLY # re-raise exception |
|
|
|
[] L0: <next statement> |
|
|
|
Of course, parts are not generated if Vi or Ei is not present. |
|
*/ |
|
|
|
static void |
|
com_try_except(struct compiling *c, node *n) |
|
{ |
|
int except_anchor = 0; |
|
int end_anchor = 0; |
|
int else_anchor = 0; |
|
int i; |
|
node *ch; |
|
|
|
com_addfwref(c, SETUP_EXCEPT, &except_anchor); |
|
block_push(c, SETUP_EXCEPT); |
|
com_node(c, CHILD(n, 2)); |
|
com_addbyte(c, POP_BLOCK); |
|
block_pop(c, SETUP_EXCEPT); |
|
com_addfwref(c, JUMP_FORWARD, &else_anchor); |
|
com_backpatch(c, except_anchor); |
|
for (i = 3; |
|
i < NCH(n) && TYPE(ch = CHILD(n, i)) == except_clause; |
|
i += 3) { |
|
/* except_clause: 'except' [expr [',' var]] */ |
|
if (except_anchor == 0) { |
|
com_error(c, PyExc_SyntaxError, |
|
"default 'except:' must be last"); |
|
break; |
|
} |
|
except_anchor = 0; |
|
com_push(c, 3); /* tb, val, exc pushed by exception */ |
|
com_set_lineno(c, ch->n_lineno); |
|
if (NCH(ch) > 1) { |
|
com_addbyte(c, DUP_TOP); |
|
com_push(c, 1); |
|
com_node(c, CHILD(ch, 1)); |
|
com_addoparg(c, COMPARE_OP, PyCmp_EXC_MATCH); |
|
com_pop(c, 1); |
|
com_addfwref(c, JUMP_IF_FALSE, &except_anchor); |
|
com_addbyte(c, POP_TOP); |
|
com_pop(c, 1); |
|
} |
|
com_addbyte(c, POP_TOP); |
|
com_pop(c, 1); |
|
if (NCH(ch) > 3) |
|
com_assign(c, CHILD(ch, 3), OP_ASSIGN, NULL); |
|
else { |
|
com_addbyte(c, POP_TOP); |
|
com_pop(c, 1); |
|
} |
|
com_addbyte(c, POP_TOP); |
|
com_pop(c, 1); |
|
com_node(c, CHILD(n, i+2)); |
|
com_addfwref(c, JUMP_FORWARD, &end_anchor); |
|
if (except_anchor) { |
|
com_backpatch(c, except_anchor); |
|
/* We come in with [tb, val, exc, 0] on the |
|
stack; one pop and it's the same as |
|
expected at the start of the loop */ |
|
com_addbyte(c, POP_TOP); |
|
} |
|
} |
|
/* We actually come in here with [tb, val, exc] but the |
|
END_FINALLY will zap those and jump around. |
|
The c_stacklevel does not reflect them so we need not pop |
|
anything. */ |
|
com_addbyte(c, END_FINALLY); |
|
com_backpatch(c, else_anchor); |
|
if (i < NCH(n)) |
|
com_node(c, CHILD(n, i+2)); |
|
com_backpatch(c, end_anchor); |
|
} |
|
|
|
static void |
|
com_try_finally(struct compiling *c, node *n) |
|
{ |
|
int finally_anchor = 0; |
|
node *ch; |
|
|
|
com_addfwref(c, SETUP_FINALLY, &finally_anchor); |
|
block_push(c, SETUP_FINALLY); |
|
com_node(c, CHILD(n, 2)); |
|
com_addbyte(c, POP_BLOCK); |
|
block_pop(c, SETUP_FINALLY); |
|
block_push(c, END_FINALLY); |
|
com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); |
|
/* While the generated code pushes only one item, |
|
the try-finally handling can enter here with |
|
up to three items. OK, here are the details: |
|
3 for an exception, 2 for RETURN, 1 for BREAK. */ |
|
com_push(c, 3); |
|
com_backpatch(c, finally_anchor); |
|
ch = CHILD(n, NCH(n)-1); |
|
com_set_lineno(c, ch->n_lineno); |
|
com_node(c, ch); |
|
com_addbyte(c, END_FINALLY); |
|
block_pop(c, END_FINALLY); |
|
com_pop(c, 3); /* Matches the com_push above */ |
|
} |
|
|
|
static void |
|
com_try_stmt(struct compiling *c, node *n) |
|
{ |
|
REQ(n, try_stmt); |
|
/* 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite] |
|
| 'try' ':' suite 'finally' ':' suite */ |
|
if (TYPE(CHILD(n, 3)) != except_clause) |
|
com_try_finally(c, n); |
|
else |
|
com_try_except(c, n); |
|
} |
|
|
|
static node * |
|
get_rawdocstring(node *n) |
|
{ |
|
int i; |
|
|
|
/* Label to avoid tail recursion */ |
|
next: |
|
switch (TYPE(n)) { |
|
|
|
case suite: |
|
if (NCH(n) == 1) { |
|
n = CHILD(n, 0); |
|
goto next; |
|
} |
|
/* Fall through */ |
|
case file_input: |
|
for (i = 0; i < NCH(n); i++) { |
|
node *ch = CHILD(n, i); |
|
if (TYPE(ch) == stmt) { |
|
n = ch; |
|
goto next; |
|
} |
|
} |
|
break; |
|
|
|
case stmt: |
|
case simple_stmt: |
|
case small_stmt: |
|
n = CHILD(n, 0); |
|
goto next; |
|
|
|
case expr_stmt: |
|
case testlist: |
|
case testlist1: |
|
case test: |
|
case and_test: |
|
case not_test: |
|
case comparison: |
|
case expr: |
|
case xor_expr: |
|
case and_expr: |
|
case shift_expr: |
|
case arith_expr: |
|
case term: |
|
case factor: |
|
case power: |
|
if (NCH(n) == 1) { |
|
n = CHILD(n, 0); |
|
goto next; |
|
} |
|
break; |
|
|
|
case atom: |
|
if (TYPE(CHILD(n, 0)) == STRING) |
|
return n; |
|
break; |
|
|
|
} |
|
return NULL; |
|
} |
|
|
|
static PyObject * |
|
get_docstring(struct compiling *c, node *n) |
|
{ |
|
/* Don't generate doc-strings if run with -OO */ |
|
if (Py_OptimizeFlag > 1) |
|
return NULL; |
|
n = get_rawdocstring(n); |
|
if (n == NULL) |
|
return NULL; |
|
return parsestrplus(c, n); |
|
} |
|
|
|
static void |
|
com_suite(struct compiling *c, node *n) |
|
{ |
|
REQ(n, suite); |
|
/* simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT */ |
|
if (NCH(n) == 1) { |
|
com_node(c, CHILD(n, 0)); |
|
} |
|
else { |
|
int i; |
|
for (i = 0; i < NCH(n) && c->c_errors == 0; i++) { |
|
node *ch = CHILD(n, i); |
|
if (TYPE(ch) == stmt) |
|
com_node(c, ch); |
|
} |
|
} |
|
} |
|
|
|
/* ARGSUSED */ |
|
static void |
|
com_continue_stmt(struct compiling *c, node *n) |
|
{ |
|
int i = c->c_nblocks; |
|
if (i-- > 0 && c->c_block[i] == SETUP_LOOP) { |
|
com_addoparg(c, JUMP_ABSOLUTE, c->c_begin); |
|
} |
|
else if (i <= 0) { |
|
/* at the outer level */ |
|
com_error(c, PyExc_SyntaxError, |
|
"'continue' not properly in loop"); |
|
} |
|
else { |
|
int j; |
|
for (j = i-1; j >= 0; --j) { |
|
if (c->c_block[j] == SETUP_LOOP) |
|
break; |
|
} |
|
if (j >= 0) { |
|
/* there is a loop, but something interferes */ |
|
for (; i > j; --i) { |
|
if (c->c_block[i] == SETUP_EXCEPT || |
|
c->c_block[i] == SETUP_FINALLY) { |
|
com_addoparg(c, CONTINUE_LOOP, |
|
c->c_begin); |
|
return; |
|
} |
|
if (c->c_block[i] == END_FINALLY) { |
|
com_error(c, PyExc_SyntaxError, |
|
"'continue' not supported inside 'finally' clause"); |
|
return; |
|
} |
|
} |
|
} |
|
com_error(c, PyExc_SyntaxError, |
|
"'continue' not properly in loop"); |
|
} |
|
/* XXX Could allow it inside a 'finally' clause |
|
XXX if we could pop the exception still on the stack */ |
|
} |
|
|
|
static int |
|
com_argdefs(struct compiling *c, node *n) |
|
{ |
|
int i, nch, nargs, ndefs; |
|
if (TYPE(n) == lambdef) { |
|
/* lambdef: 'lambda' [varargslist] ':' test */ |
|
n = CHILD(n, 1); |
|
} |
|
else { |
|
REQ(n, funcdef); /* funcdef: 'def' NAME parameters ... */ |
|
n = CHILD(n, 2); |
|
REQ(n, parameters); /* parameters: '(' [varargslist] ')' */ |
|
n = CHILD(n, 1); |
|
} |
|
if (TYPE(n) != varargslist) |
|
return 0; |
|
/* varargslist: |
|
(fpdef ['=' test] ',')* '*' ....... | |
|
fpdef ['=' test] (',' fpdef ['=' test])* [','] */ |
|
nch = NCH(n); |
|
nargs = 0; |
|
ndefs = 0; |
|
for (i = 0; i < nch; i++) { |
|
int t; |
|
if (TYPE(CHILD(n, i)) == STAR || |
|
TYPE(CHILD(n, i)) == DOUBLESTAR) |
|
break; |
|
nargs++; |
|
i++; |
|
if (i >= nch) |
|
t = RPAR; /* Anything except EQUAL or COMMA */ |
|
else |
|
t = TYPE(CHILD(n, i)); |
|
if (t == EQUAL) { |
|
i++; |
|
ndefs++; |
|
com_node(c, CHILD(n, i)); |
|
i++; |
|
if (i >= nch) |
|
break; |
|
t = TYPE(CHILD(n, i)); |
|
} |
|
else { |
|
/* Treat "(a=1, b)" as an error */ |
|
if (ndefs) |
|
com_error(c, PyExc_SyntaxError, |
|
"non-default argument follows default argument"); |
|
} |
|
if (t != COMMA) |
|
break; |
|
} |
|
return ndefs; |
|
} |
|
|
|
static void |
|
com_funcdef(struct compiling *c, node *n) |
|
{ |
|
PyObject *co; |
|
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), |
|
n->n_lineno); |
|
co = (PyObject *)icompile(n, c); |
|
symtable_exit_scope(c->c_symtable); |
|
if (co == NULL) |
|
c->c_errors++; |
|
else { |
|
int closure = com_make_closure(c, (PyCodeObject *)co); |
|
int i = com_addconst(c, co); |
|
com_addoparg(c, LOAD_CONST, i); |
|
com_push(c, 1); |
|
if (closure) |
|
com_addoparg(c, MAKE_CLOSURE, ndefs); |
|
else |
|
com_addoparg(c, MAKE_FUNCTION, ndefs); |
|
com_pop(c, ndefs); |
|
com_addop_varname(c, VAR_STORE, STR(CHILD(n, 1))); |
|
com_pop(c, 1); |
|
Py_DECREF(co); |
|
} |
|
} |
|
|
|
static void |
|
com_bases(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
REQ(n, testlist); |
|
/* testlist: test (',' test)* [','] */ |
|
for (i = 0; i < NCH(n); i += 2) |
|
com_node(c, CHILD(n, i)); |
|
i = (NCH(n)+1) / 2; |
|
com_addoparg(c, BUILD_TUPLE, i); |
|
com_pop(c, i-1); |
|
} |
|
|
|
static void |
|
com_classdef(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
PyObject *v; |
|
PyCodeObject *co; |
|
char *name; |
|
|
|
REQ(n, classdef); |
|
/* classdef: class NAME ['(' testlist ')'] ':' suite */ |
|
if ((v = PyString_InternFromString(STR(CHILD(n, 1)))) == NULL) { |
|
c->c_errors++; |
|
return; |
|
} |
|
/* Push the class name on the stack */ |
|
i = com_addconst(c, v); |
|
com_addoparg(c, LOAD_CONST, i); |
|
com_push(c, 1); |
|
Py_DECREF(v); |
|
/* Push the tuple of base classes on the stack */ |
|
if (TYPE(CHILD(n, 2)) != LPAR) { |
|
com_addoparg(c, BUILD_TUPLE, 0); |
|
com_push(c, 1); |
|
} |
|
else |
|
com_bases(c, CHILD(n, 3)); |
|
name = STR(CHILD(n, 1)); |
|
symtable_enter_scope(c->c_symtable, name, TYPE(n), n->n_lineno); |
|
co = icompile(n, c); |
|
symtable_exit_scope(c->c_symtable); |
|
if (co == NULL) |
|
c->c_errors++; |
|
else { |
|
int closure = com_make_closure(c, co); |
|
i = com_addconst(c, (PyObject *)co); |
|
com_addoparg(c, LOAD_CONST, i); |
|
com_push(c, 1); |
|
if (closure) { |
|
com_addoparg(c, MAKE_CLOSURE, 0); |
|
com_pop(c, PyCode_GetNumFree(co)); |
|
} else |
|
com_addoparg(c, MAKE_FUNCTION, 0); |
|
com_addoparg(c, CALL_FUNCTION, 0); |
|
com_addbyte(c, BUILD_CLASS); |
|
com_pop(c, 2); |
|
com_addop_varname(c, VAR_STORE, STR(CHILD(n, 1))); |
|
com_pop(c, 1); |
|
Py_DECREF(co); |
|
} |
|
} |
|
|
|
static void |
|
com_node(struct compiling *c, node *n) |
|
{ |
|
loop: |
|
if (c->c_errors) |
|
return; |
|
switch (TYPE(n)) { |
|
|
|
/* Definition nodes */ |
|
|
|
case funcdef: |
|
com_funcdef(c, n); |
|
break; |
|
case classdef: |
|
com_classdef(c, n); |
|
break; |
|
|
|
/* Trivial parse tree nodes */ |
|
|
|
case stmt: |
|
case small_stmt: |
|
case flow_stmt: |
|
n = CHILD(n, 0); |
|
goto loop; |
|
|
|
case simple_stmt: |
|
/* small_stmt (';' small_stmt)* [';'] NEWLINE */ |
|
com_set_lineno(c, n->n_lineno); |
|
{ |
|
int i; |
|
for (i = 0; i < NCH(n)-1; i += 2) |
|
com_node(c, CHILD(n, i)); |
|
} |
|
break; |
|
|
|
case compound_stmt: |
|
com_set_lineno(c, n->n_lineno); |
|
n = CHILD(n, 0); |
|
goto loop; |
|
|
|
/* Statement nodes */ |
|
|
|
case expr_stmt: |
|
com_expr_stmt(c, n); |
|
break; |
|
case print_stmt: |
|
com_print_stmt(c, n); |
|
break; |
|
case del_stmt: /* 'del' exprlist */ |
|
com_assign(c, CHILD(n, 1), OP_DELETE, NULL); |
|
break; |
|
case pass_stmt: |
|
break; |
|
case break_stmt: |
|
if (c->c_loops == 0) { |
|
com_error(c, PyExc_SyntaxError, |
|
"'break' outside loop"); |
|
} |
|
com_addbyte(c, BREAK_LOOP); |
|
break; |
|
case continue_stmt: |
|
com_continue_stmt(c, n); |
|
break; |
|
case return_stmt: |
|
com_return_stmt(c, n); |
|
break; |
|
case yield_stmt: |
|
com_yield_stmt(c, n); |
|
break; |
|
case raise_stmt: |
|
com_raise_stmt(c, n); |
|
break; |
|
case import_stmt: |
|
com_import_stmt(c, n); |
|
break; |
|
case global_stmt: |
|
break; |
|
case exec_stmt: |
|
com_exec_stmt(c, n); |
|
break; |
|
case assert_stmt: |
|
com_assert_stmt(c, n); |
|
break; |
|
case if_stmt: |
|
com_if_stmt(c, n); |
|
break; |
|
case while_stmt: |
|
com_while_stmt(c, n); |
|
break; |
|
case for_stmt: |
|
com_for_stmt(c, n); |
|
break; |
|
case try_stmt: |
|
com_try_stmt(c, n); |
|
break; |
|
case suite: |
|
com_suite(c, n); |
|
break; |
|
|
|
/* Expression nodes */ |
|
|
|
case testlist: |
|
case testlist1: |
|
case testlist_safe: |
|
com_list(c, n, 0); |
|
break; |
|
case test: |
|
com_test(c, n); |
|
break; |
|
case and_test: |
|
com_and_test(c, n); |
|
break; |
|
case not_test: |
|
com_not_test(c, n); |
|
break; |
|
case comparison: |
|
com_comparison(c, n); |
|
break; |
|
case exprlist: |
|
com_list(c, n, 0); |
|
break; |
|
case expr: |
|
com_expr(c, n); |
|
break; |
|
case xor_expr: |
|
com_xor_expr(c, n); |
|
break; |
|
case and_expr: |
|
com_and_expr(c, n); |
|
break; |
|
case shift_expr: |
|
com_shift_expr(c, n); |
|
break; |
|
case arith_expr: |
|
com_arith_expr(c, n); |
|
break; |
|
case term: |
|
com_term(c, n); |
|
break; |
|
case factor: |
|
com_factor(c, n); |
|
break; |
|
case power: |
|
com_power(c, n); |
|
break; |
|
case atom: |
|
com_atom(c, n); |
|
break; |
|
|
|
default: |
|
com_error(c, PyExc_SystemError, |
|
"com_node: unexpected node type"); |
|
} |
|
} |
|
|
|
static void com_fplist(struct compiling *, node *); |
|
|
|
static void |
|
com_fpdef(struct compiling *c, node *n) |
|
{ |
|
REQ(n, fpdef); /* fpdef: NAME | '(' fplist ')' */ |
|
if (TYPE(CHILD(n, 0)) == LPAR) |
|
com_fplist(c, CHILD(n, 1)); |
|
else { |
|
com_addop_varname(c, VAR_STORE, STR(CHILD(n, 0))); |
|
com_pop(c, 1); |
|
} |
|
} |
|
|
|
static void |
|
com_fplist(struct compiling *c, node *n) |
|
{ |
|
REQ(n, fplist); /* fplist: fpdef (',' fpdef)* [','] */ |
|
if (NCH(n) == 1) { |
|
com_fpdef(c, CHILD(n, 0)); |
|
} |
|
else { |
|
int i = (NCH(n)+1)/2; |
|
com_addoparg(c, UNPACK_SEQUENCE, i); |
|
com_push(c, i-1); |
|
for (i = 0; i < NCH(n); i += 2) |
|
com_fpdef(c, CHILD(n, i)); |
|
} |
|
} |
|
|
|
static void |
|
com_arglist(struct compiling *c, node *n) |
|
{ |
|
int nch, i, narg; |
|
int complex = 0; |
|
char nbuf[30]; |
|
REQ(n, varargslist); |
|
/* varargslist: |
|
(fpdef ['=' test] ',')* (fpdef ['=' test] | '*' .....) */ |
|
nch = NCH(n); |
|
/* Enter all arguments in table of locals */ |
|
for (i = 0, narg = 0; i < nch; i++) { |
|
node *ch = CHILD(n, i); |
|
node *fp; |
|
if (TYPE(ch) == STAR || TYPE(ch) == DOUBLESTAR) |
|
break; |
|
REQ(ch, fpdef); /* fpdef: NAME | '(' fplist ')' */ |
|
fp = CHILD(ch, 0); |
|
if (TYPE(fp) != NAME) { |
|
PyOS_snprintf(nbuf, sizeof(nbuf), ".%d", i); |
|
complex = 1; |
|
} |
|
narg++; |
|
/* all name updates handled by symtable */ |
|
if (++i >= nch) |
|
break; |
|
ch = CHILD(n, i); |
|
if (TYPE(ch) == EQUAL) |
|
i += 2; |
|
else |
|
REQ(ch, COMMA); |
|
} |
|
if (complex) { |
|
/* Generate code for complex arguments only after |
|
having counted the simple arguments */ |
|
int ilocal = 0; |
|
for (i = 0; i < nch; i++) { |
|
node *ch = CHILD(n, i); |
|
node *fp; |
|
if (TYPE(ch) == STAR || TYPE(ch) == DOUBLESTAR) |
|
break; |
|
REQ(ch, fpdef); /* fpdef: NAME | '(' fplist ')' */ |
|
fp = CHILD(ch, 0); |
|
if (TYPE(fp) != NAME) { |
|
com_addoparg(c, LOAD_FAST, ilocal); |
|
com_push(c, 1); |
|
com_fpdef(c, ch); |
|
} |
|
ilocal++; |
|
if (++i >= nch) |
|
break; |
|
ch = CHILD(n, i); |
|
if (TYPE(ch) == EQUAL) |
|
i += 2; |
|
else |
|
REQ(ch, COMMA); |
|
} |
|
} |
|
} |
|
|
|
static void |
|
com_file_input(struct compiling *c, node *n) |
|
{ |
|
int i; |
|
PyObject *doc; |
|
REQ(n, file_input); /* (NEWLINE | stmt)* ENDMARKER */ |
|
doc = get_docstring(c, n); |
|
if (doc != NULL) { |
|
int i = com_addconst(c, doc); |
|
Py_DECREF(doc); |
|
com_addoparg(c, LOAD_CONST, i); |
|
com_push(c, 1); |
|
com_addop_name(c, STORE_NAME, "__doc__"); |
|
com_pop(c, 1); |
|
} |
|
for (i = 0; i < NCH(n); i++) { |
|
node *ch = CHILD(n, i); |
|
if (TYPE(ch) != ENDMARKER && TYPE(ch) != NEWLINE) |
|
com_node(c, ch); |
|
} |
|
} |
|
|
|
/* Top-level compile-node interface */ |
|
|
|
static void |
|
compile_funcdef(struct compiling *c, node *n) |
|
{ |
|
PyObject *doc; |
|
node *ch; |
|
REQ(n, funcdef); /* funcdef: 'def' NAME parameters ':' suite */ |
|
c->c_name = STR(CHILD(n, 1)); |
|
doc = get_docstring(c, CHILD(n, 4)); |
|
if (doc != NULL) { |
|
(void) com_addconst(c, doc); |
|
Py_DECREF(doc); |
|
} |
|
else |
|
(void) com_addconst(c, Py_None); /* No docstring */ |
|
ch = CHILD(n, 2); /* parameters: '(' [varargslist] ')' */ |
|
ch = CHILD(ch, 1); /* ')' | varargslist */ |
|
if (TYPE(ch) == varargslist) |
|
com_arglist(c, ch); |
|
c->c_infunction = 1; |
|
com_node(c, CHILD(n, 4)); |
|
c->c_infunction = 0; |
|
com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); |
|
com_push(c, 1); |
|
com_addbyte(c, RETURN_VALUE); |
|
com_pop(c, 1); |
|
} |
|
|
|
static void |
|
compile_lambdef(struct compiling *c, node *n) |
|
{ |
|
node *ch; |
|
REQ(n, lambdef); /* lambdef: 'lambda' [varargslist] ':' test */ |
|
c->c_name = "<lambda>"; |
|
|
|
ch = CHILD(n, 1); |
|
(void) com_addconst(c, Py_None); /* No docstring */ |
|
if (TYPE(ch) == varargslist) { |
|
com_arglist(c, ch); |
|
ch = CHILD(n, 3); |
|
} |
|
else |
|
ch = CHILD(n, 2); |
|
com_node(c, ch); |
|
com_addbyte(c, RETURN_VALUE); |
|
com_pop(c, 1); |
|
} |
|
|
|
static void |
|
compile_classdef(struct compiling *c, node *n) |
|
{ |
|
node *ch; |
|
PyObject *doc; |
|
REQ(n, classdef); |
|
/* classdef: 'class' NAME ['(' testlist ')'] ':' suite */ |
|
c->c_name = STR(CHILD(n, 1)); |
|
c->c_private = c->c_name; |
|
/* Initialize local __module__ from global __name__ */ |
|
com_addop_name(c, LOAD_GLOBAL, "__name__"); |
|
com_addop_name(c, STORE_NAME, "__module__"); |
|
ch = CHILD(n, NCH(n)-1); /* The suite */ |
|
doc = get_docstring(c, ch); |
|
if (doc != NULL) { |
|
int i = com_addconst(c, doc); |
|
Py_DECREF(doc); |
|
com_addoparg(c, LOAD_CONST, i); |
|
com_push(c, 1); |
|
com_addop_name(c, STORE_NAME, "__doc__"); |
|
com_pop(c, 1); |
|
} |
|
else |
|
(void) com_addconst(c, Py_None); |
|
com_node(c, ch); |
|
com_addbyte(c, LOAD_LOCALS); |
|
com_push(c, 1); |
|
com_addbyte(c, RETURN_VALUE); |
|
com_pop(c, 1); |
|
} |
|
|
|
static void |
|
compile_node(struct compiling *c, node *n) |
|
{ |
|
com_set_lineno(c, n->n_lineno); |
|
|
|
switch (TYPE(n)) { |
|
|
|
case single_input: /* One interactive command */ |
|
/* NEWLINE | simple_stmt | compound_stmt NEWLINE */ |
|
c->c_interactive++; |
|
n = CHILD(n, 0); |
|
if (TYPE(n) != NEWLINE) |
|
com_node(c, n); |
|
com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); |
|
com_push(c, 1); |
|
com_addbyte(c, RETURN_VALUE); |
|
com_pop(c, 1); |
|
c->c_interactive--; |
|
break; |
|
|
|
case file_input: /* A whole file, or built-in function exec() */ |
|
com_file_input(c, n); |
|
com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); |
|
com_push(c, 1); |
|
com_addbyte(c, RETURN_VALUE); |
|
com_pop(c, 1); |
|
break; |
|
|
|
case eval_input: /* Built-in function input() */ |
|
com_node(c, CHILD(n, 0)); |
|
com_addbyte(c, RETURN_VALUE); |
|
com_pop(c, 1); |
|
break; |
|
|
|
case lambdef: /* anonymous function definition */ |
|
compile_lambdef(c, n); |
|
break; |
|
|
|
case funcdef: /* A function definition */ |
|
compile_funcdef(c, n); |
|
break; |
|
|
|
case classdef: /* A class definition */ |
|
compile_classdef(c, n); |
|
break; |
|
|
|
default: |
|
com_error(c, PyExc_SystemError, |
|
"compile_node: unexpected node type"); |
|
} |
|
} |
|
|
|
static PyObject * |
|
dict_keys_inorder(PyObject *dict, int offset) |
|
{ |
|
PyObject *tuple, *k, *v; |
|
int i, pos = 0, size = PyDict_Size(dict); |
|
|
|
tuple = PyTuple_New(size); |
|
if (tuple == NULL) |
|
return NULL; |
|
while (PyDict_Next(dict, &pos, &k, &v)) { |
|
i = PyInt_AS_LONG(v); |
|
Py_INCREF(k); |
|
assert((i - offset) < size); |
|
PyTuple_SET_ITEM(tuple, i - offset, k); |
|
} |
|
return tuple; |
|
} |
|
|
|
PyCodeObject * |
|
PyNode_Compile(node *n, const char *filename) |
|
{ |
|
return PyNode_CompileFlags(n, filename, NULL); |
|
} |
|
|
|
PyCodeObject * |
|
PyNode_CompileFlags(node *n, const char *filename, PyCompilerFlags *flags) |
|
{ |
|
return jcompile(n, filename, NULL, flags); |
|
} |
|
|
|
struct symtable * |
|
PyNode_CompileSymtable(node *n, const char *filename) |
|
{ |
|
struct symtable *st; |
|
PyFutureFeatures *ff; |
|
|
|
ff = PyNode_Future(n, filename); |
|
if (ff == NULL) |
|
return NULL; |
|
st = symtable_build(n, ff, filename); |
|
if (st == NULL) { |
|
PyObject_FREE((void *)ff); |
|
return NULL; |
|
} |
|
return st; |
|
} |
|
|
|
static PyCodeObject * |
|
icompile(node *n, struct compiling *base) |
|
{ |
|
return jcompile(n, base->c_filename, base, NULL); |
|
} |
|
|
|
static PyCodeObject * |
|
jcompile(node *n, const char *filename, struct compiling *base, |
|
PyCompilerFlags *flags) |
|
{ |
|
struct compiling sc; |
|
PyCodeObject *co; |
|
if (!com_init(&sc, filename)) |
|
return NULL; |
|
if (flags && flags->cf_flags & PyCF_SOURCE_IS_UTF8) { |
|
sc.c_encoding = "utf-8"; |
|
} else if (TYPE(n) == encoding_decl) { |
|
sc.c_encoding = STR(n); |
|
n = CHILD(n, 0); |
|
} else { |
|
sc.c_encoding = NULL; |
|
} |
|
if (base) { |
|
sc.c_private = base->c_private; |
|
sc.c_symtable = base->c_symtable; |
|
/* c_symtable still points to parent's symbols */ |
|
if (base->c_nested |
|
|| (sc.c_symtable->st_cur->ste_type == TYPE_FUNCTION)) |
|
sc.c_nested = 1; |
|
sc.c_flags |= base->c_flags & PyCF_MASK; |
|
if (base->c_encoding != NULL) { |
|
assert(sc.c_encoding == NULL); |
|
sc.c_encoding = base->c_encoding; |
|
} |
|
} else { |
|
sc.c_private = NULL; |
|
sc.c_future = PyNode_Future(n, filename); |
|
if (sc.c_future == NULL) { |
|
com_free(&sc); |
|
return NULL; |
|
} |
|
if (flags) { |
|
int merged = sc.c_future->ff_features | |
|
flags->cf_flags; |
|
sc.c_future->ff_features = merged; |
|
flags->cf_flags = merged; |
|
} |
|
sc.c_symtable = symtable_build(n, sc.c_future, sc.c_filename); |
|
if (sc.c_symtable == NULL) { |
|
com_free(&sc); |
|
return NULL; |
|
} |
|
/* reset symbol table for second pass */ |
|
sc.c_symtable->st_nscopes = 1; |
|
sc.c_symtable->st_pass = 2; |
|
} |
|
co = NULL; |
|
if (symtable_load_symbols(&sc) < 0) { |
|
sc.c_errors++; |
|
goto exit; |
|
} |
|
compile_node(&sc, n); |
|
com_done(&sc); |
|
if (sc.c_errors == 0) { |
|
PyObject *consts, *names, *varnames, *filename, *name, |
|
*freevars, *cellvars; |
|
consts = PyList_AsTuple(sc.c_consts); |
|
names = PyList_AsTuple(sc.c_names); |
|
varnames = PyList_AsTuple(sc.c_varnames); |
|
cellvars = dict_keys_inorder(sc.c_cellvars, 0); |
|
freevars = dict_keys_inorder(sc.c_freevars, |
|
PyTuple_GET_SIZE(cellvars)); |
|
filename = PyString_InternFromString(sc.c_filename); |
|
name = PyString_InternFromString(sc.c_name); |
|
if (!PyErr_Occurred()) |
|
co = PyCode_New(sc.c_argcount, |
|
sc.c_nlocals, |
|
sc.c_maxstacklevel, |
|
sc.c_flags, |
|
sc.c_code, |
|
consts, |
|
names, |
|
varnames, |
|
freevars, |
|
cellvars, |
|
filename, |
|
name, |
|
sc.c_firstlineno, |
|
sc.c_lnotab); |
|
Py_XDECREF(consts); |
|
Py_XDECREF(names); |
|
Py_XDECREF(varnames); |
|
Py_XDECREF(freevars); |
|
Py_XDECREF(cellvars); |
|
Py_XDECREF(filename); |
|
Py_XDECREF(name); |
|
} |
|
else if (!PyErr_Occurred()) { |
|
/* This could happen if someone called PyErr_Clear() after an |
|
error was reported above. That's not supposed to happen, |
|
but I just plugged one case and I'm not sure there can't be |
|
others. In that case, raise SystemError so that at least |
|
it gets reported instead dumping core. */ |
|
PyErr_SetString(PyExc_SystemError, "lost syntax error"); |
|
} |
|
exit: |
|
if (base == NULL) { |
|
PySymtable_Free(sc.c_symtable); |
|
sc.c_symtable = NULL; |
|
} |
|
com_free(&sc); |
|
return co; |
|
} |
|
|
|
int |
|
PyCode_Addr2Line(PyCodeObject *co, int addrq) |
|
{ |
|
int size = PyString_Size(co->co_lnotab) / 2; |
|
unsigned char *p = (unsigned char*)PyString_AsString(co->co_lnotab); |
|
int line = co->co_firstlineno; |
|
int addr = 0; |
|
while (--size >= 0) { |
|
addr += *p++; |
|
if (addr > addrq) |
|
break; |
|
line += *p++; |
|
} |
|
return line; |
|
} |
|
|
|
/* The test for LOCAL must come before the test for FREE in order to |
|
handle classes where name is both local and free. The local var is |
|
a method and the free var is a free var referenced within a method. |
|
*/ |
|
|
|
static int |
|
get_ref_type(struct compiling *c, char *name) |
|
{ |
|
char buf[350]; |
|
PyObject *v; |
|
|
|
if (PyDict_GetItemString(c->c_cellvars, name) != NULL) |
|
return CELL; |
|
if (PyDict_GetItemString(c->c_locals, name) != NULL) |
|
return LOCAL; |
|
if (PyDict_GetItemString(c->c_freevars, name) != NULL) |
|
return FREE; |
|
v = PyDict_GetItemString(c->c_globals, name); |
|
if (v) { |
|
if (v == Py_None) |
|
return GLOBAL_EXPLICIT; |
|
else { |
|
return GLOBAL_IMPLICIT; |
|
} |
|
} |
|
PyOS_snprintf(buf, sizeof(buf), |
|
"unknown scope for %.100s in %.100s(%s) " |
|
"in %s\nsymbols: %s\nlocals: %s\nglobals: %s\n", |
|
name, c->c_name, |
|
PyObject_REPR(c->c_symtable->st_cur->ste_id), |
|
c->c_filename, |
|
PyObject_REPR(c->c_symtable->st_cur->ste_symbols), |
|
PyObject_REPR(c->c_locals), |
|
PyObject_REPR(c->c_globals) |
|
); |
|
|
|
Py_FatalError(buf); |
|
return -1; |
|
} |
|
|
|
/* Helper functions to issue warnings */ |
|
|
|
static int |
|
issue_warning(const char *msg, const char *filename, int lineno) |
|
{ |
|
if (PyErr_Occurred()) { |
|
/* This can happen because symtable_node continues |
|
processing even after raising a SyntaxError. |
|
Calling PyErr_WarnExplicit now would clobber the |
|
pending exception; instead we fail and let that |
|
exception propagate. |
|
*/ |
|
return -1; |
|
} |
|
if (PyErr_WarnExplicit(PyExc_SyntaxWarning, msg, filename, |
|
lineno, NULL, NULL) < 0) { |
|
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) { |
|
PyErr_SetString(PyExc_SyntaxError, msg); |
|
PyErr_SyntaxLocation(filename, lineno); |
|
} |
|
return -1; |
|
} |
|
return 0; |
|
} |
|
|
|
static int |
|
symtable_warn(struct symtable *st, char *msg) |
|
{ |
|
if (issue_warning(msg, st->st_filename, st->st_cur->ste_lineno) < 0) { |
|
st->st_errors++; |
|
return -1; |
|
} |
|
return 0; |
|
} |
|
|
|
/* Helper function for setting lineno and filename */ |
|
|
|
static struct symtable * |
|
symtable_build(node *n, PyFutureFeatures *ff, const char *filename) |
|
{ |
|
struct symtable *st; |
|
|
|
st = symtable_init(); |
|
if (st == NULL) |
|
return NULL; |
|
st->st_future = ff; |
|
st->st_filename = filename; |
|
symtable_enter_scope(st, TOP, TYPE(n), n->n_lineno); |
|
if (st->st_errors > 0) |
|
goto fail; |
|
symtable_node(st, n); |
|
if (st->st_errors > 0) |
|
goto fail; |
|
return st; |
|
fail: |
|
if (!PyErr_Occurred()) { |
|
/* This could happen because after a syntax error is |
|
detected, the symbol-table-building continues for |
|
a while, and PyErr_Clear() might erroneously be |
|
called during that process. One such case has been |
|
fixed, but there might be more (now or later). |
|
*/ |
|
PyErr_SetString(PyExc_SystemError, "lost exception"); |
|
} |
|
st->st_future = NULL; |
|
st->st_filename = NULL; |
|
PySymtable_Free(st); |
|
return NULL; |
|
} |
|
|
|
static int |
|
symtable_init_compiling_symbols(struct compiling *c) |
|
{ |
|
PyObject *varnames; |
|
|
|
varnames = c->c_symtable->st_cur->ste_varnames; |
|
if (varnames == NULL) { |
|
varnames = PyList_New(0); |
|
if (varnames == NULL) |
|
return -1; |
|
c->c_symtable->st_cur->ste_varnames = varnames; |
|
Py_INCREF(varnames); |
|
} else |
|
Py_INCREF(varnames); |
|
c->c_varnames = varnames; |
|
|
|
c->c_globals = PyDict_New(); |
|
if (c->c_globals == NULL) |
|
return -1; |
|
c->c_freevars = PyDict_New(); |
|
if (c->c_freevars == NULL) |
|
return -1; |
|
c->c_cellvars = PyDict_New(); |
|
if (c->c_cellvars == NULL) |
|
return -1; |
|
return 0; |
|
} |
|
|
|
struct symbol_info { |
|
int si_nlocals; |
|
int si_ncells; |
|
int si_nfrees; |
|
int si_nimplicit; |
|
}; |
|
|
|
static void |
|
symtable_init_info(struct symbol_info *si) |
|
{ |
|
si->si_nlocals = 0; |
|
si->si_ncells = 0; |
|
si->si_nfrees = 0; |
|
si->si_nimplicit = 0; |
|
} |
|
|
|
static int |
|
symtable_resolve_free(struct compiling *c, PyObject *name, int flags, |
|
struct symbol_info *si) |
|
{ |
|
PyObject *dict, *v; |
|
|
|
/* Seperate logic for DEF_FREE. If it occurs in a function, |
|
it indicates a local that we must allocate storage for (a |
|
cell var). If it occurs in a class, then the class has a |
|
method and a free variable with the same name. |
|
*/ |
|
if (c->c_symtable->st_cur->ste_type == TYPE_FUNCTION) { |
|
/* If it isn't declared locally, it can't be a cell. */ |
|
if (!(flags & (DEF_LOCAL | DEF_PARAM))) |
|
return 0; |
|
v = PyInt_FromLong(si->si_ncells++); |
|
dict = c->c_cellvars; |
|
} else { |
|
/* If it is free anyway, then there is no need to do |
|
anything here. |
|
*/ |
|
if (is_free(flags ^ DEF_FREE_CLASS) |
|
|| (flags == DEF_FREE_CLASS)) |
|
return 0; |
|
v = PyInt_FromLong(si->si_nfrees++); |
|
dict = c->c_freevars; |
|
} |
|
if (v == NULL) |
|
return -1; |
|
if (PyDict_SetItem(dict, name, v) < 0) { |
|
Py_DECREF(v); |
|
return -1; |
|
} |
|
Py_DECREF(v); |
|
return 0; |
|
} |
|
|
|
/* If a variable is a cell and an argument, make sure that appears in |
|
co_cellvars before any variable to its right in varnames. |
|
*/ |
|
|
|
|
|
static int |
|
symtable_cellvar_offsets(PyObject **cellvars, int argcount, |
|
PyObject *varnames, int flags) |
|
{ |
|
PyObject *v = NULL; |
|
PyObject *w, *d, *list = NULL; |
|
int i, pos; |
|
|
|
if (flags & CO_VARARGS) |
|
argcount++; |
|
if (flags & CO_VARKEYWORDS) |
|
argcount++; |
|
for (i = argcount; --i >= 0; ) { |
|
v = PyList_GET_ITEM(varnames, i); |
|
if (PyDict_GetItem(*cellvars, v)) { |
|
if (list == NULL) { |
|
list = PyList_New(1); |
|
if (list == NULL) |
|
return -1; |
|
PyList_SET_ITEM(list, 0, v); |
|
Py_INCREF(v); |
|
} else { |
|
if (PyList_Insert(list, 0, v) < 0) { |
|
Py_DECREF(list); |
|
return -1; |
|
} |
|
} |
|
} |
|
} |
|
if (list == NULL) |
|
return 0; |
|
|
|
/* There are cellvars that are also arguments. Create a dict |
|
to replace cellvars and put the args at the front. |
|
*/ |
|
d = PyDict_New(); |
|
if (d == NULL) |
|
return -1; |
|
for (i = PyList_GET_SIZE(list); --i >= 0; ) { |
|
v = PyInt_FromLong(i); |
|
if (v == NULL) |
|
goto fail; |
|
if (PyDict_SetItem(d, PyList_GET_ITEM(list, i), v) < 0) |
|
goto fail; |
|
if (PyDict_DelItem(*cellvars, PyList_GET_ITEM(list, i)) < 0) |
|
goto fail; |
|
Py_DECREF(v); |
|
} |
|
pos = 0; |
|
i = PyList_GET_SIZE(list); |
|
Py_DECREF(list); |
|
while (PyDict_Next(*cellvars, &pos, &v, &w)) { |
|
w = PyInt_FromLong(i++); /* don't care about the old key */ |
|
if (w == NULL) |
|
goto fail; |
|
if (PyDict_SetItem(d, v, w) < 0) { |
|
Py_DECREF(w); |
|
v = NULL; |
|
goto fail; |
|
} |
|
Py_DECREF(w); |
|
} |
|
Py_DECREF(*cellvars); |
|
*cellvars = d; |
|
return 1; |
|
fail: |
|
Py_DECREF(d); |
|
Py_XDECREF(v); |
|
return -1; |
|
} |
|
|
|
static int |
|
symtable_freevar_offsets(PyObject *freevars, int offset) |
|
{ |
|
PyObject *name, *v; |
|
int pos; |
|
|
|
/* The cell vars are the first elements of the closure, |
|
followed by the free vars. Update the offsets in |
|
c_freevars to account for number of cellvars. */ |
|
pos = 0; |
|
while (PyDict_Next(freevars, &pos, &name, &v)) { |
|
int i = PyInt_AS_LONG(v) + offset; |
|
PyObject *o = PyInt_FromLong(i); |
|
if (o == NULL) |
|
return -1; |
|
if (PyDict_SetItem(freevars, name, o) < 0) { |
|
Py_DECREF(o); |
|
return -1; |
|
} |
|
Py_DECREF(o); |
|
} |
|
return 0; |
|
} |
|
|
|
static int |
|
symtable_check_unoptimized(struct compiling *c, |
|
PySymtableEntryObject *ste, |
|
struct symbol_info *si) |
|
{ |
|
char buf[300]; |
|
|
|
if (!(si->si_ncells || si->si_nfrees || ste->ste_child_free |
|
|| (ste->ste_nested && si->si_nimplicit))) |
|
return 0; |
|
|
|
#define ILLEGAL_CONTAINS "contains a nested function with free variables" |
|
|
|
#define ILLEGAL_IS "is a nested function" |
|
|
|
#define ILLEGAL_IMPORT_STAR \ |
|
"import * is not allowed in function '%.100s' because it %s" |
|
|
|
#define ILLEGAL_BARE_EXEC \ |
|
"unqualified exec is not allowed in function '%.100s' it %s" |
|
|
|
#define ILLEGAL_EXEC_AND_IMPORT_STAR \ |
|
"function '%.100s' uses import * and bare exec, which are illegal " \ |
|
"because it %s" |
|
|
|
/* XXX perhaps the linenos for these opt-breaking statements |
|
should be stored so the exception can point to them. */ |
|
|
|
if (ste->ste_child_free) { |
|
if (ste->ste_optimized == OPT_IMPORT_STAR) |
|
PyOS_snprintf(buf, sizeof(buf), |
|
ILLEGAL_IMPORT_STAR, |
|
PyString_AS_STRING(ste->ste_name), |
|
ILLEGAL_CONTAINS); |
|
else if (ste->ste_optimized == (OPT_BARE_EXEC | OPT_EXEC)) |
|
PyOS_snprintf(buf, sizeof(buf), |
|
ILLEGAL_BARE_EXEC, |
|
PyString_AS_STRING(ste->ste_name), |
|
ILLEGAL_CONTAINS); |
|
else { |
|
PyOS_snprintf(buf, sizeof(buf), |
|
ILLEGAL_EXEC_AND_IMPORT_STAR, |
|
PyString_AS_STRING(ste->ste_name), |
|
ILLEGAL_CONTAINS); |
|
} |
|
} else { |
|
if (ste->ste_optimized == OPT_IMPORT_STAR) |
|
PyOS_snprintf(buf, sizeof(buf), |
|
ILLEGAL_IMPORT_STAR, |
|
PyString_AS_STRING(ste->ste_name), |
|
ILLEGAL_IS); |
|
else if (ste->ste_optimized == (OPT_BARE_EXEC | OPT_EXEC)) |
|
PyOS_snprintf(buf, sizeof(buf), |
|
ILLEGAL_BARE_EXEC, |
|
PyString_AS_STRING(ste->ste_name), |
|
ILLEGAL_IS); |
|
else { |
|
PyOS_snprintf(buf, sizeof(buf), |
|
ILLEGAL_EXEC_AND_IMPORT_STAR, |
|
PyString_AS_STRING(ste->ste_name), |
|
ILLEGAL_IS); |
|
} |
|
} |
|
|
|
PyErr_SetString(PyExc_SyntaxError, buf); |
|
PyErr_SyntaxLocation(c->c_symtable->st_filename, |
|
ste->ste_opt_lineno); |
|
return -1; |
|
} |
|
|
|
static int |
|
symtable_update_flags(struct compiling *c, PySymtableEntryObject *ste, |
|
struct symbol_info *si) |
|
{ |
|
if (c->c_future) |
|
c->c_flags |= c->c_future->ff_features; |
|
if (ste->ste_generator) |
|
c->c_flags |= CO_GENERATOR; |
|
if (ste->ste_type != TYPE_MODULE) |
|
c->c_flags |= CO_NEWLOCALS; |
|
if (ste->ste_type == TYPE_FUNCTION) { |
|
c->c_nlocals = si->si_nlocals; |
|
if (ste->ste_optimized == 0) |
|
c->c_flags |= CO_OPTIMIZED; |
|
else if (ste->ste_optimized != OPT_EXEC) |
|
return symtable_check_unoptimized(c, ste, si); |
|
} |
|
return 0; |
|
} |
|
|
|
static int |
|
symtable_error(struct symtable *st, int lineno) |
|
{ |
|
if (lineno == 0) |
|
lineno = st->st_cur->ste_lineno; |
|
PyErr_SyntaxLocation(st->st_filename, lineno); |
|
st->st_errors++; |
|
return -1; |
|
} |
|
|
|
static int |
|
symtable_load_symbols(struct compiling *c) |
|
{ |
|
struct symtable *st = c->c_symtable; |
|
PySymtableEntryObject *ste = st->st_cur; |
|
PyObject *name, *varnames, *v; |
|
int i, flags, pos; |
|
struct symbol_info si; |
|
|
|
v = NULL; |
|
|
|
if (symtable_init_compiling_symbols(c) < 0) |
|
goto fail; |
|
symtable_init_info(&si); |
|
varnames = st->st_cur->ste_varnames; |
|
si.si_nlocals = PyList_GET_SIZE(varnames); |
|
c->c_argcount = si.si_nlocals; |
|
|
|
for (i = 0; i < si.si_nlocals; ++i) { |
|
v = PyInt_FromLong(i); |
|
if (v == NULL) |
|
goto fail; |
|
if (PyDict_SetItem(c->c_locals, |
|
PyList_GET_ITEM(varnames, i), v) < 0) |
|
goto fail; |
|
Py_DECREF(v); |
|
} |
|
|
|
/* 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(ste->ste_symbols, &pos, &name, &v)) { |
|
flags = PyInt_AS_LONG(v); |
|
|
|
if (flags & DEF_FREE_GLOBAL) |
|
/* undo the original DEF_FREE */ |
|
flags &= ~(DEF_FREE | DEF_FREE_CLASS); |
|
|
|
/* Deal with names that need two actions: |
|
1. Cell variables that are also locals. |
|
2. Free variables in methods that are also class |
|
variables or declared global. |
|
*/ |
|
if (flags & (DEF_FREE | DEF_FREE_CLASS)) |
|
symtable_resolve_free(c, name, flags, &si); |
|
|
|
if (flags & DEF_STAR) { |
|
c->c_argcount--; |
|
c->c_flags |= CO_VARARGS; |
|
} else if (flags & DEF_DOUBLESTAR) { |
|
c->c_argcount--; |
|
c->c_flags |= CO_VARKEYWORDS; |
|
} else if (flags & DEF_INTUPLE) |
|
c->c_argcount--; |
|
else if (flags & DEF_GLOBAL) { |
|
if (flags & DEF_PARAM) { |
|
PyErr_Format(PyExc_SyntaxError, LOCAL_GLOBAL, |
|
PyString_AS_STRING(name)); |
|
symtable_error(st, 0); |
|
goto fail; |
|
} |
|
if (PyDict_SetItem(c->c_globals, name, Py_None) < 0) |
|
goto fail; |
|
} else if (flags & DEF_FREE_GLOBAL) { |
|
si.si_nimplicit++; |
|
if (PyDict_SetItem(c->c_globals, name, Py_True) < 0) |
|
goto fail; |
|
} else if ((flags & DEF_LOCAL) && !(flags & DEF_PARAM)) { |
|
v = PyInt_FromLong(si.si_nlocals++); |
|
if (v == NULL) |
|
goto fail; |
|
if (PyDict_SetItem(c->c_locals, name, v) < 0) |
|
goto fail; |
|
Py_DECREF(v); |
|
if (ste->ste_type != TYPE_CLASS) |
|
if (PyList_Append(c->c_varnames, name) < 0) |
|
goto fail; |
|
} else if (is_free(flags)) { |
|
if (ste->ste_nested) { |
|
v = PyInt_FromLong(si.si_nfrees++); |
|
if (v == NULL) |
|
goto fail; |
|
if (PyDict_SetItem(c->c_freevars, name, v) < 0) |
|
goto fail; |
|
Py_DECREF(v); |
|
} else { |
|
si.si_nimplicit++; |
|
if (PyDict_SetItem(c->c_globals, name, |
|
Py_True) < 0) |
|
goto fail; |
|
if (st->st_nscopes != 1) { |
|
v = PyInt_FromLong(flags); |
|
if (v == NULL) |
|
goto fail; |
|
if (PyDict_SetItem(st->st_global, |
|
name, v)) |
|
goto fail; |
|
Py_DECREF(v); |
|
} |
|
} |
|
} |
|
} |
|
|
|
assert(PyDict_Size(c->c_freevars) == si.si_nfrees); |
|
|
|
if (si.si_ncells > 1) { /* one cell is always in order */ |
|
if (symtable_cellvar_offsets(&c->c_cellvars, c->c_argcount, |
|
c->c_varnames, c->c_flags) < 0) |
|
return -1; |
|
} |
|
if (symtable_freevar_offsets(c->c_freevars, si.si_ncells) < 0) |
|
return -1; |
|
return symtable_update_flags(c, ste, &si); |
|
fail: |
|
/* is this always the right thing to do? */ |
|
Py_XDECREF(v); |
|
return -1; |
|
} |
|
|
|
static struct symtable * |
|
symtable_init() |
|
{ |
|
struct symtable *st; |
|
|
|
st = (struct symtable *)PyObject_MALLOC(sizeof(struct symtable)); |
|
if (st == NULL) |
|
return NULL; |
|
st->st_pass = 1; |
|
|
|
st->st_filename = NULL; |
|
st->st_symbols = NULL; |
|
if ((st->st_stack = PyList_New(0)) == NULL) |
|
goto fail; |
|
if ((st->st_symbols = PyDict_New()) == NULL) |
|
goto fail; |
|
st->st_cur = NULL; |
|
st->st_nscopes = 0; |
|
st->st_errors = 0; |
|
st->st_private = NULL; |
|
return st; |
|
fail: |
|
PySymtable_Free(st); |
|
return NULL; |
|
} |
|
|
|
void |
|
PySymtable_Free(struct symtable *st) |
|
{ |
|
Py_XDECREF(st->st_symbols); |
|
Py_XDECREF(st->st_stack); |
|
Py_XDECREF(st->st_cur); |
|
PyObject_FREE((void *)st); |
|
} |
|
|
|
/* When the compiler exits a scope, it must should update the scope's |
|
free variable information with the list of free variables in its |
|
children. |
|
|
|
Variables that are free in children and defined in the current |
|
scope are cellvars. |
|
|
|
If the scope being exited is defined at the top-level (ste_nested is |
|
false), free variables in children that are not defined here are |
|
implicit globals. |
|
|
|
*/ |
|
|
|
static int |
|
symtable_update_free_vars(struct symtable *st) |
|
{ |
|
int i, j, def; |
|
PyObject *o, *name, *list = NULL; |
|
PySymtableEntryObject *child, *ste = st->st_cur; |
|
|
|
if (ste->ste_type == TYPE_CLASS) |
|
def = DEF_FREE_CLASS; |
|
else |
|
def = DEF_FREE; |
|
for (i = 0; i < PyList_GET_SIZE(ste->ste_children); ++i) { |
|
int pos = 0; |
|
|
|
if (list && PyList_SetSlice(list, 0, |
|
PyList_GET_SIZE(list), 0) < 0) |
|
return -1; |
|
child = (PySymtableEntryObject *) |
|
PyList_GET_ITEM(ste->ste_children, i); |
|
while (PyDict_Next(child->ste_symbols, &pos, &name, &o)) { |
|
int flags = PyInt_AS_LONG(o); |
|
if (!(is_free(flags))) |
|
continue; /* avoids indentation */ |
|
if (list == NULL) { |
|
list = PyList_New(0); |
|
if (list == NULL) |
|
return -1; |
|
} |
|
ste->ste_child_free = 1; |
|
if (PyList_Append(list, name) < 0) { |
|
Py_DECREF(list); |
|
return -1; |
|
} |
|
} |
|
for (j = 0; list && j < PyList_GET_SIZE(list); j++) { |
|
PyObject *v; |
|
name = PyList_GET_ITEM(list, j); |
|
v = PyDict_GetItem(ste->ste_symbols, name); |
|
/* If a name N is declared global in scope A and |
|
referenced in scope B contained (perhaps |
|
indirectly) in A and there are no scopes |
|
with bindings for N between B and A, then N |
|
is global in B. Unless A is a class scope, |
|
because class scopes are not considered for |
|
nested scopes. |
|
*/ |
|
if (v && (ste->ste_type != TYPE_CLASS)) { |
|
int flags = PyInt_AS_LONG(v); |
|
if (flags & DEF_GLOBAL) { |
|
symtable_undo_free(st, child->ste_id, |
|
name); |
|
continue; |
|
} |
|
} |
|
if (ste->ste_nested) { |
|
if (symtable_add_def_o(st, ste->ste_symbols, |
|
name, def) < 0) { |
|
Py_DECREF(list); |
|
return -1; |
|
} |
|
} else { |
|
if (symtable_check_global(st, child->ste_id, |
|
name) < 0) { |
|
Py_DECREF(list); |
|
return -1; |
|
} |
|
} |
|
} |
|
} |
|
|
|
Py_XDECREF(list); |
|
return 0; |
|
} |
|
|
|
/* If the current scope is a non-nested class or if name is not |
|
defined in the current, non-nested scope, then it is an implicit |
|
global in all nested scopes. |
|
*/ |
|
|
|
static int |
|
symtable_check_global(struct symtable *st, PyObject *child, PyObject *name) |
|
{ |
|
PyObject *o; |
|
int v; |
|
PySymtableEntryObject *ste = st->st_cur; |
|
|
|
if (ste->ste_type == TYPE_CLASS) |
|
return symtable_undo_free(st, child, name); |
|
o = PyDict_GetItem(ste->ste_symbols, name); |
|
if (o == NULL) |
|
return symtable_undo_free(st, child, name); |
|
v = PyInt_AS_LONG(o); |
|
|
|
if (is_free(v) || (v & DEF_GLOBAL)) |
|
return symtable_undo_free(st, child, name); |
|
else |
|
return symtable_add_def_o(st, ste->ste_symbols, |
|
name, DEF_FREE); |
|
} |
|
|
|
static int |
|
symtable_undo_free(struct symtable *st, PyObject *id, |
|
PyObject *name) |
|
{ |
|
int i, v, x; |
|
PyObject *info; |
|
PySymtableEntryObject *ste; |
|
|
|
ste = (PySymtableEntryObject *)PyDict_GetItem(st->st_symbols, id); |
|
if (ste == NULL) |
|
return -1; |
|
|
|
info = PyDict_GetItem(ste->ste_symbols, name); |
|
if (info == NULL) |
|
return 0; |
|
v = PyInt_AS_LONG(info); |
|
if (is_free(v)) { |
|
if (symtable_add_def_o(st, ste->ste_symbols, name, |
|
DEF_FREE_GLOBAL) < 0) |
|
return -1; |
|
} else |
|
/* If the name is defined here or declared global, |
|
then the recursion stops. */ |
|
return 0; |
|
|
|
for (i = 0; i < PyList_GET_SIZE(ste->ste_children); ++i) { |
|
PySymtableEntryObject *child; |
|
child = (PySymtableEntryObject *) |
|
PyList_GET_ITEM(ste->ste_children, i); |
|
x = symtable_undo_free(st, child->ste_id, name); |
|
if (x < 0) |
|
return x; |
|
} |
|
return 0; |
|
} |
|
|
|
/* symtable_enter_scope() gets a reference via PySymtableEntry_New(). |
|
This reference is released when the scope is exited, via the DECREF |
|
in symtable_exit_scope(). |
|
*/ |
|
|
|
static int |
|
symtable_exit_scope(struct symtable *st) |
|
{ |
|
int end; |
|
|
|
if (st->st_pass == 1) |
|
symtable_update_free_vars(st); |
|
Py_DECREF(st->st_cur); |
|
end = PyList_GET_SIZE(st->st_stack) - 1; |
|
st->st_cur = (PySymtableEntryObject *)PyList_GET_ITEM(st->st_stack, |
|
end); |
|
if (PySequence_DelItem(st->st_stack, end) < 0) |
|
return -1; |
|
return 0; |
|
} |
|
|
|
static void |
|
symtable_enter_scope(struct symtable *st, char *name, int type, |
|
int lineno) |
|
{ |
|
PySymtableEntryObject *prev = NULL; |
|
|
|
if (st->st_cur) { |
|
prev = st->st_cur; |
|
if (PyList_Append(st->st_stack, (PyObject *)st->st_cur) < 0) { |
|
st->st_errors++; |
|
return; |
|
} |
|
} |
|
st->st_cur = (PySymtableEntryObject *) |
|
PySymtableEntry_New(st, name, type, lineno); |
|
if (st->st_cur == NULL) { |
|
st->st_errors++; |
|
return; |
|
} |
|
if (strcmp(name, TOP) == 0) |
|
st->st_global = st->st_cur->ste_symbols; |
|
if (prev && st->st_pass == 1) { |
|
if (PyList_Append(prev->ste_children, |
|
(PyObject *)st->st_cur) < 0) |
|
st->st_errors++; |
|
} |
|
} |
|
|
|
static int |
|
symtable_lookup(struct symtable *st, char *name) |
|
{ |
|
char buffer[MANGLE_LEN]; |
|
PyObject *v; |
|
int flags; |
|
|
|
if (_Py_Mangle(st->st_private, name, buffer, sizeof(buffer))) |
|
name = buffer; |
|
v = PyDict_GetItemString(st->st_cur->ste_symbols, name); |
|
if (v == NULL) { |
|
if (PyErr_Occurred()) |
|
return -1; |
|
else |
|
return 0; |
|
} |
|
|
|
flags = PyInt_AS_LONG(v); |
|
return flags; |
|
} |
|
|
|
static int |
|
symtable_add_def(struct symtable *st, char *name, int flag) |
|
{ |
|
PyObject *s; |
|
char buffer[MANGLE_LEN]; |
|
int ret; |
|
|
|
/* Warn about None, except inside a tuple (where the assignment |
|
code already issues a warning). */ |
|
if ((flag & DEF_PARAM) && !(flag & DEF_INTUPLE) && |
|
*name == 'N' && strcmp(name, "None") == 0) |
|
{ |
|
if (symtable_warn(st, "argument named None")) |
|
return -1; |
|
} |
|
if (_Py_Mangle(st->st_private, name, buffer, sizeof(buffer))) |
|
name = buffer; |
|
if ((s = PyString_InternFromString(name)) == NULL) |
|
return -1; |
|
ret = symtable_add_def_o(st, st->st_cur->ste_symbols, s, flag); |
|
Py_DECREF(s); |
|
return ret; |
|
} |
|
|
|
/* Must only be called with mangled names */ |
|
|
|
static int |
|
symtable_add_def_o(struct symtable *st, PyObject *dict, |
|
PyObject *name, int flag) |
|
{ |
|
PyObject *o; |
|
int val; |
|
|
|
if ((o = PyDict_GetItem(dict, name))) { |
|
val = PyInt_AS_LONG(o); |
|
if ((flag & DEF_PARAM) && (val & DEF_PARAM)) { |
|
PyErr_Format(PyExc_SyntaxError, DUPLICATE_ARGUMENT, |
|
PyString_AsString(name)); |
|
return symtable_error(st, 0); |
|
} |
|
val |= flag; |
|
} else |
|
val = flag; |
|
o = PyInt_FromLong(val); |
|
if (o == NULL) |
|
return -1; |
|
if (PyDict_SetItem(dict, name, o) < 0) { |
|
Py_DECREF(o); |
|
return -1; |
|
} |
|
Py_DECREF(o); |
|
|
|
if (flag & DEF_PARAM) { |
|
if (PyList_Append(st->st_cur->ste_varnames, name) < 0) |
|
return -1; |
|
} else if (flag & DEF_GLOBAL) { |
|
/* XXX need to update DEF_GLOBAL for other flags too; |
|
perhaps only DEF_FREE_GLOBAL */ |
|
if ((o = PyDict_GetItem(st->st_global, name))) { |
|
val = PyInt_AS_LONG(o); |
|
val |= flag; |
|
} else |
|
val = flag; |
|
o = PyInt_FromLong(val); |
|
if (o == NULL) |
|
return -1; |
|
if (PyDict_SetItem(st->st_global, name, o) < 0) { |
|
Py_DECREF(o); |
|
return -1; |
|
} |
|
Py_DECREF(o); |
|
} |
|
return 0; |
|
} |
|
|
|
#define symtable_add_use(ST, NAME) symtable_add_def((ST), (NAME), USE) |
|
|
|
/* Look for a yield stmt under n. Return 1 if found, else 0. |
|
This hack is used to look inside "if 0:" blocks (which are normally |
|
ignored) in case those are the only places a yield occurs (so that this |
|
function is a generator). */ |
|
static int |
|
look_for_yield(node *n) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < NCH(n); ++i) { |
|
node *kid = CHILD(n, i); |
|
|
|
switch (TYPE(kid)) { |
|
|
|
case classdef: |
|
case funcdef: |
|
case lambdef: |
|
/* Stuff in nested functions and classes can't make |
|
the parent a generator. */ |
|
return 0; |
|
|
|
case yield_stmt: |
|
return 1; |
|
|
|
default: |
|
if (look_for_yield(kid)) |
|
return 1; |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
static void |
|
symtable_node(struct symtable *st, node *n) |
|
{ |
|
int i; |
|
|
|
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), n->n_lineno); |
|
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), n->n_lineno); |
|
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), n->n_lineno); |
|
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)))) { |
|
if (st->st_cur->ste_generator == 0) |
|
st->st_cur->ste_generator = |
|
look_for_yield(CHILD(n, i+3)); |
|
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: { |
|
st->st_cur->ste_optimized |= OPT_EXEC; |
|
symtable_node(st, CHILD(n, 1)); |
|
if (NCH(n) > 2) |
|
symtable_node(st, CHILD(n, 3)); |
|
else { |
|
st->st_cur->ste_optimized |= OPT_BARE_EXEC; |
|
st->st_cur->ste_opt_lineno = n->n_lineno; |
|
} |
|
if (NCH(n) > 4) |
|
symtable_node(st, CHILD(n, 5)); |
|
break; |
|
|
|
} |
|
case assert_stmt: |
|
if (Py_OptimizeFlag) |
|
return; |
|
if (NCH(n) == 2) { |
|
n = CHILD(n, 1); |
|
goto loop; |
|
} else { |
|
symtable_node(st, CHILD(n, 1)); |
|
n = CHILD(n, 3); |
|
goto loop; |
|
} |
|
case except_clause: |
|
if (NCH(n) == 4) |
|
symtable_assign(st, CHILD(n, 3), 0); |
|
if (NCH(n) > 1) { |
|
n = CHILD(n, 1); |
|
goto loop; |
|
} |
|
break; |
|
case del_stmt: |
|
symtable_assign(st, CHILD(n, 1), 0); |
|
break; |
|
case yield_stmt: |
|
st->st_cur->ste_generator = 1; |
|
n = CHILD(n, 1); |
|
goto loop; |
|
case expr_stmt: |
|
if (NCH(n) == 1) |
|
n = CHILD(n, 0); |
|
else { |
|
if (TYPE(CHILD(n, 1)) == augassign) { |
|
symtable_assign(st, CHILD(n, 0), 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), 0); |
|
n = CHILD(n, NCH(n) - 1); |
|
} |
|
} |
|
goto loop; |
|
case list_iter: |
|
/* only occurs when there are multiple for loops |
|
in a list comprehension */ |
|
n = CHILD(n, 0); |
|
if (TYPE(n) == list_for) |
|
symtable_list_for(st, n); |
|
else { |
|
REQ(n, list_if); |
|
symtable_node(st, CHILD(n, 1)); |
|
if (NCH(n) == 3) { |
|
n = CHILD(n, 2); |
|
goto loop; |
|
} |
|
} |
|
break; |
|
case for_stmt: |
|
symtable_assign(st, CHILD(n, 1), 0); |
|
for (i = 3; i < NCH(n); ++i) |
|
if (TYPE(CHILD(n, i)) >= single_input) |
|
symtable_node(st, CHILD(n, i)); |
|
break; |
|
/* The remaining cases fall through to default except in |
|
special circumstances. This requires the individual cases |
|
to be coded with great care, even though they look like |
|
rather innocuous. Each case must double-check TYPE(n). |
|
*/ |
|
case argument: |
|
if (TYPE(n) == argument && NCH(n) == 3) { |
|
n = CHILD(n, 2); |
|
goto loop; |
|
} |
|
/* fall through */ |
|
case listmaker: |
|
if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == list_for) { |
|
symtable_list_comprehension(st, n); |
|
break; |
|
} |
|
/* fall through */ |
|
case atom: |
|
if (TYPE(n) == atom && TYPE(CHILD(n, 0)) == NAME) { |
|
symtable_add_use(st, STR(CHILD(n, 0))); |
|
break; |
|
} |
|
/* fall through */ |
|
default: |
|
/* Walk over every non-token child with a special case |
|
for one child. |
|
*/ |
|
if (NCH(n) == 1) { |
|
n = CHILD(n, 0); |
|
goto loop; |
|
} |
|
for (i = 0; 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_args() 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 = -1, 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[30]; |
|
PyOS_snprintf(nbuf, sizeof(nbuf), ".%d", i); |
|
symtable_add_def(st, nbuf, DEF_PARAM); |
|
complex = i; |
|
} |
|
} |
|
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; |
|
if (i >= NCH(n)) |
|
c = NULL; |
|
else |
|
c = CHILD(n, i); |
|
} |
|
if (c && TYPE(c) == DOUBLESTAR) { |
|
i++; |
|
symtable_add_def(st, STR(CHILD(n, i)), |
|
DEF_PARAM | DEF_DOUBLESTAR); |
|
} |
|
} |
|
if (complex >= 0) { |
|
int j; |
|
for (j = 0; j <= complex; j++) { |
|
c = CHILD(n, j); |
|
if (TYPE(c) == COMMA) |
|
c = CHILD(n, ++j); |
|
else if (TYPE(c) == EQUAL) |
|
c = CHILD(n, j += 3); |
|
if (TYPE(CHILD(c, 0)) == LPAR) |
|
symtable_params_fplist(st, CHILD(c, 1)); |
|
} |
|
} |
|
} |
|
|
|
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; |
|
|
|
/* XXX It might be helpful to warn about module-level global |
|
statements, but it's hard to tell the difference between |
|
module-level and a string passed to exec. |
|
*/ |
|
|
|
for (i = 1; i < NCH(n); i += 2) { |
|
char *name = STR(CHILD(n, i)); |
|
int flags; |
|
|
|
flags = symtable_lookup(st, name); |
|
if (flags < 0) |
|
continue; |
|
if (flags && flags != DEF_GLOBAL) { |
|
char buf[500]; |
|
if (flags & DEF_PARAM) { |
|
PyErr_Format(PyExc_SyntaxError, |
|
"name '%.400s' is local and global", |
|
name); |
|
symtable_error(st, 0); |
|
return; |
|
} |
|
else { |
|
if (flags & DEF_LOCAL) |
|
PyOS_snprintf(buf, sizeof(buf), |
|
GLOBAL_AFTER_ASSIGN, |
|
name); |
|
else |
|
PyOS_snprintf(buf, sizeof(buf), |
|
GLOBAL_AFTER_USE, name); |
|
symtable_warn(st, buf); |
|
} |
|
} |
|
symtable_add_def(st, name, DEF_GLOBAL); |
|
} |
|
} |
|
|
|
static void |
|
symtable_list_comprehension(struct symtable *st, node *n) |
|
{ |
|
/* listmaker: test list_for */ |
|
char tmpname[30]; |
|
|
|
REQ(n, listmaker); |
|
PyOS_snprintf(tmpname, sizeof(tmpname), "_[%d]", |
|
++st->st_cur->ste_tmpname); |
|
symtable_add_def(st, tmpname, DEF_LOCAL); |
|
symtable_list_for(st, CHILD(n, 1)); |
|
symtable_node(st, CHILD(n, 0)); |
|
--st->st_cur->ste_tmpname; |
|
} |
|
|
|
static void |
|
symtable_list_for(struct symtable *st, node *n) |
|
{ |
|
REQ(n, list_for); |
|
/* list_for: for v in expr [list_iter] */ |
|
symtable_assign(st, CHILD(n, 1), 0); |
|
symtable_node(st, CHILD(n, 3)); |
|
if (NCH(n) == 5) |
|
symtable_node(st, CHILD(n, 4)); |
|
} |
|
|
|
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 */ |
|
node *dotname = CHILD(n, 1); |
|
if (strcmp(STR(CHILD(dotname, 0)), "__future__") == 0) { |
|
/* check for bogus imports */ |
|
if (n->n_lineno >= st->st_future->ff_last_lineno) { |
|
PyErr_SetString(PyExc_SyntaxError, |
|
LATE_FUTURE); |
|
symtable_error(st, n->n_lineno); |
|
return; |
|
} |
|
} |
|
if (TYPE(CHILD(n, 3)) == STAR) { |
|
if (st->st_cur->ste_type != TYPE_MODULE) { |
|
if (symtable_warn(st, |
|
"import * only allowed at module level") < 0) |
|
return; |
|
} |
|
st->st_cur->ste_optimized |= OPT_IMPORT_STAR; |
|
st->st_cur->ste_opt_lineno = n->n_lineno; |
|
} 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), |
|
DEF_IMPORT); |
|
else |
|
symtable_assign(st, CHILD(c, 0), |
|
DEF_IMPORT); |
|
} |
|
} |
|
} else { |
|
for (i = 1; i < NCH(n); i += 2) { |
|
symtable_assign(st, CHILD(n, i), DEF_IMPORT); |
|
} |
|
} |
|
} |
|
|
|
/* The third argument to symatble_assign() is a flag to be passed to |
|
symtable_add_def() if it is eventually called. The flag is useful |
|
to specify the particular type of assignment that should be |
|
recorded, e.g. an assignment caused by import. |
|
*/ |
|
|
|
static void |
|
symtable_assign(struct symtable *st, node *n, int def_flag) |
|
{ |
|
node *tmp; |
|
int i; |
|
|
|
loop: |
|
switch (TYPE(n)) { |
|
case lambdef: |
|
/* invalid assignment, e.g. lambda x:x=2. The next |
|
pass will catch this error. */ |
|
return; |
|
case power: |
|
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); |
|
goto loop; |
|
} |
|
return; |
|
case listmaker: |
|
if (NCH(n) > 1 && TYPE(CHILD(n, 1)) == list_for) { |
|
/* XXX This is an error, but the next pass |
|
will catch it. */ |
|
return; |
|
} else { |
|
for (i = 0; i < NCH(n); i += 2) |
|
symtable_assign(st, CHILD(n, i), def_flag); |
|
} |
|
return; |
|
case exprlist: |
|
case testlist: |
|
case testlist1: |
|
if (NCH(n) == 1) { |
|
n = CHILD(n, 0); |
|
goto loop; |
|
} |
|
else { |
|
int i; |
|
for (i = 0; i < NCH(n); i += 2) |
|
symtable_assign(st, CHILD(n, i), def_flag); |
|
return; |
|
} |
|
case atom: |
|
tmp = CHILD(n, 0); |
|
if (TYPE(tmp) == LPAR || TYPE(tmp) == LSQB) { |
|
n = CHILD(n, 1); |
|
goto loop; |
|
} else if (TYPE(tmp) == NAME) { |
|
if (strcmp(STR(tmp), "__debug__") == 0) { |
|
PyErr_SetString(PyExc_SyntaxError, |
|
ASSIGN_DEBUG); |
|
symtable_error(st, n->n_lineno); |
|
return; |
|
} |
|
symtable_add_def(st, STR(tmp), DEF_LOCAL | def_flag); |
|
} |
|
return; |
|
case dotted_as_name: |
|
if (NCH(n) == 3) |
|
symtable_add_def(st, STR(CHILD(n, 2)), |
|
DEF_LOCAL | def_flag); |
|
else |
|
symtable_add_def(st, |
|
STR(CHILD(CHILD(n, |
|
0), 0)), |
|
DEF_LOCAL | def_flag); |
|
return; |
|
case dotted_name: |
|
symtable_add_def(st, STR(CHILD(n, 0)), DEF_LOCAL | def_flag); |
|
return; |
|
case NAME: |
|
symtable_add_def(st, STR(n), DEF_LOCAL | def_flag); |
|
return; |
|
default: |
|
if (NCH(n) == 0) |
|
return; |
|
if (NCH(n) == 1) { |
|
n = CHILD(n, 0); |
|
goto loop; |
|
} |
|
/* Should only occur for errors like x + 1 = 1, |
|
which will be caught in the next pass. */ |
|
for (i = 0; i < NCH(n); ++i) |
|
if (TYPE(CHILD(n, i)) >= single_input) |
|
symtable_assign(st, CHILD(n, i), def_flag); |
|
} |
|
}
|
|
|