cpython/Modules/parsermodule.c
Guido van Rossum d59da4b432 Merged revisions 55407-55513 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/branches/p3yk

................
  r55413 | fred.drake | 2007-05-17 12:30:10 -0700 (Thu, 17 May 2007) | 1 line

  fix argument name in documentation; match the implementation
................
  r55430 | jack.diederich | 2007-05-18 06:39:59 -0700 (Fri, 18 May 2007) | 1 line

  Implements class decorators, PEP 3129.
................
  r55432 | guido.van.rossum | 2007-05-18 08:09:41 -0700 (Fri, 18 May 2007) | 2 lines

  obsubmit.
................
  r55434 | guido.van.rossum | 2007-05-18 09:39:10 -0700 (Fri, 18 May 2007) | 3 lines

  Fix bug in test_inspect.  (I presume this is how it should be fixed;
  Jack Diedrich, please verify.)
................
  r55460 | brett.cannon | 2007-05-20 00:31:57 -0700 (Sun, 20 May 2007) | 4 lines

  Remove the imageop module.  With imgfile already removed in Python 3.0 and
  rgbimg gone in Python 2.6 the unit tests themselves were made worthless.  Plus
  third-party libraries perform the same function much better.
................
  r55469 | neal.norwitz | 2007-05-20 11:28:20 -0700 (Sun, 20 May 2007) | 118 lines

  Merged revisions 55324-55467 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r55348 | georg.brandl | 2007-05-15 13:19:34 -0700 (Tue, 15 May 2007) | 4 lines

    HTML-escape the plain traceback in cgitb's HTML output, to prevent
    the traceback inadvertently or maliciously closing the comment and
    injecting HTML into the error page.
  ........
    r55372 | neal.norwitz | 2007-05-15 21:33:50 -0700 (Tue, 15 May 2007) | 6 lines

    Port rev 55353 from Guido:
    Add what looks like a necessary call to PyErr_NoMemory() when PyMem_MALLOC()
    fails.

    Will backport.
  ........
    r55377 | neal.norwitz | 2007-05-15 22:06:33 -0700 (Tue, 15 May 2007) | 1 line

    Mention removal of some directories for obsolete platforms
  ........
    r55380 | brett.cannon | 2007-05-15 22:50:03 -0700 (Tue, 15 May 2007) | 2 lines

    Change the maintainer of the BeOS port.
  ........
    r55383 | georg.brandl | 2007-05-16 06:44:18 -0700 (Wed, 16 May 2007) | 2 lines

    Bug #1719995: don't use deprecated method in sets example.
  ........
    r55386 | neal.norwitz | 2007-05-16 13:05:11 -0700 (Wed, 16 May 2007) | 5 lines

    Fix bug in marshal where bad data would cause a segfault due to
    lack of an infinite recursion check.

    Contributed by Damien Miller at Google.
  ........
    r55389 | brett.cannon | 2007-05-16 15:42:29 -0700 (Wed, 16 May 2007) | 6 lines

    Remove the gopherlib module.  It has been raising a DeprecationWarning since
    Python 2.5.

    Also remove gopher support from urllib/urllib2.  As both imported gopherlib the
    usage of the support would have raised a DeprecationWarning.
  ........
    r55394 | raymond.hettinger | 2007-05-16 18:08:04 -0700 (Wed, 16 May 2007) | 1 line

    calendar.py gets no benefit from xrange() instead of range()
  ........
    r55395 | brett.cannon | 2007-05-16 19:02:56 -0700 (Wed, 16 May 2007) | 3 lines

    Complete deprecation of BaseException.message.  Some subclasses were directly
    accessing the message attribute instead of using the descriptor.
  ........
    r55396 | neal.norwitz | 2007-05-16 23:11:36 -0700 (Wed, 16 May 2007) | 4 lines

    Reduce the max stack depth to see if this fixes the segfaults on
    Windows and some other boxes.  If this is successful, this rev should
    be backported.  I'm not sure how close to the limit we should push this.
  ........
    r55397 | neal.norwitz | 2007-05-16 23:23:50 -0700 (Wed, 16 May 2007) | 4 lines

    Set the depth to something very small to try to determine if the
    crashes on Windows are really due to the stack size or possibly
    some other problem.
  ........
    r55398 | neal.norwitz | 2007-05-17 00:04:46 -0700 (Thu, 17 May 2007) | 4 lines

    Last try for tweaking the max stack depth.  5000 was the original value,
    4000 didn't work either.  1000 does work on Windows.  If 2000 works,
    that will hopefully be a reasonable balance.
  ........
    r55412 | fred.drake | 2007-05-17 12:29:58 -0700 (Thu, 17 May 2007) | 1 line

    fix argument name in documentation; match the implementation
  ........
    r55427 | neal.norwitz | 2007-05-17 22:47:16 -0700 (Thu, 17 May 2007) | 1 line

    Verify neither dumps or loads overflow the stack and segfault.
  ........
    r55446 | collin.winter | 2007-05-18 16:11:24 -0700 (Fri, 18 May 2007) | 1 line

    Backport PEP 3110's new 'except' syntax to 2.6.
  ........
    r55448 | raymond.hettinger | 2007-05-18 18:11:16 -0700 (Fri, 18 May 2007) | 1 line

    Improvements to NamedTuple's implementation, tests, and documentation
  ........
    r55449 | raymond.hettinger | 2007-05-18 18:50:11 -0700 (Fri, 18 May 2007) | 1 line

    Fix beginner mistake -- don't mix spaces and tabs.
  ........
    r55450 | neal.norwitz | 2007-05-18 20:48:47 -0700 (Fri, 18 May 2007) | 1 line

    Clear data so random memory does not get freed.  Will backport.
  ........
    r55452 | neal.norwitz | 2007-05-18 21:34:55 -0700 (Fri, 18 May 2007) | 3 lines

    Whoops, need to pay attention to those test failures.
    Move the clear to *before* the first use, not after.
  ........
    r55453 | neal.norwitz | 2007-05-18 21:35:52 -0700 (Fri, 18 May 2007) | 1 line

    Give some clue as to what happened if the test fails.
  ........
    r55455 | georg.brandl | 2007-05-19 11:09:26 -0700 (Sat, 19 May 2007) | 2 lines

    Fix docstring for add_package in site.py.
  ........
    r55458 | brett.cannon | 2007-05-20 00:09:50 -0700 (Sun, 20 May 2007) | 2 lines

    Remove the rgbimg module.  It has been deprecated since Python 2.5.
  ........
    r55465 | nick.coghlan | 2007-05-20 04:12:49 -0700 (Sun, 20 May 2007) | 1 line

    Fix typo in example (should be backported, but my maintenance branch is woefully out of date)
  ........
................
  r55472 | brett.cannon | 2007-05-20 12:06:18 -0700 (Sun, 20 May 2007) | 2 lines

  Remove imageop from the Windows build process.
................
  r55486 | neal.norwitz | 2007-05-20 23:59:52 -0700 (Sun, 20 May 2007) | 1 line

  Remove callable() builtin
................
  r55506 | neal.norwitz | 2007-05-22 00:43:29 -0700 (Tue, 22 May 2007) | 78 lines

  Merged revisions 55468-55505 via svnmerge from
  svn+ssh://pythondev@svn.python.org/python/trunk

  ........
    r55468 | neal.norwitz | 2007-05-20 11:06:27 -0700 (Sun, 20 May 2007) | 1 line

    rotor is long gone.
  ........
    r55470 | neal.norwitz | 2007-05-20 11:43:00 -0700 (Sun, 20 May 2007) | 1 line

    Update directories/files at the top-level.
  ........
    r55471 | brett.cannon | 2007-05-20 12:05:06 -0700 (Sun, 20 May 2007) | 2 lines

    Try to remove rgbimg from Windows builds.
  ........
    r55474 | brett.cannon | 2007-05-20 16:17:38 -0700 (Sun, 20 May 2007) | 4 lines

    Remove the macfs module.  This led to the deprecation of macostools.touched();
    it completely relied on macfs and is a no-op on OS X according to code
    comments.
  ........
    r55476 | brett.cannon | 2007-05-20 16:56:18 -0700 (Sun, 20 May 2007) | 3 lines

    Move imgfile import to the global namespace to trigger an import error ASAP to
    prevent creation of a test file.
  ........
    r55477 | brett.cannon | 2007-05-20 16:57:38 -0700 (Sun, 20 May 2007) | 3 lines

    Cause posixfile to raise a DeprecationWarning.  Documented as deprecated since
    Ptyhon 1.5.
  ........
    r55479 | andrew.kuchling | 2007-05-20 17:03:15 -0700 (Sun, 20 May 2007) | 1 line

    Note removed modules
  ........
    r55481 | martin.v.loewis | 2007-05-20 21:35:47 -0700 (Sun, 20 May 2007) | 2 lines

    Add Alexandre Vassalotti.
  ........
    r55482 | george.yoshida | 2007-05-20 21:41:21 -0700 (Sun, 20 May 2007) | 4 lines

    fix against r55474 [Remove the macfs module]

    Remove "libmacfs.tex" from Makefile.deps and mac/mac.tex.
  ........
    r55487 | raymond.hettinger | 2007-05-21 01:13:35 -0700 (Mon, 21 May 2007) | 1 line

    Replace assertion with straight error-checking.
  ........
    r55489 | raymond.hettinger | 2007-05-21 09:40:10 -0700 (Mon, 21 May 2007) | 1 line

    Allow all alphanumeric and underscores in type and field names.
  ........
    r55490 | facundo.batista | 2007-05-21 10:32:32 -0700 (Mon, 21 May 2007) | 5 lines


    Added timeout support to HTTPSConnection, through the
    socket.create_connection function. Also added a small
    test for this, and updated NEWS file.
  ........
    r55495 | georg.brandl | 2007-05-21 13:34:16 -0700 (Mon, 21 May 2007) | 2 lines

    Patch #1686487: you can now pass any mapping after '**' in function calls.
  ........
    r55502 | neal.norwitz | 2007-05-21 23:03:36 -0700 (Mon, 21 May 2007) | 1 line

    Document new params to HTTPSConnection
  ........
    r55504 | neal.norwitz | 2007-05-22 00:16:10 -0700 (Tue, 22 May 2007) | 1 line

    Stop using METH_OLDARGS
  ........
    r55505 | neal.norwitz | 2007-05-22 00:16:44 -0700 (Tue, 22 May 2007) | 1 line

    Stop using METH_OLDARGS implicitly
  ........
................
2007-05-22 18:11:13 +00:00

3110 lines
90 KiB
C

/* parsermodule.c
*
* Copyright 1995-1996 by Fred L. Drake, Jr. and Virginia Polytechnic
* Institute and State University, Blacksburg, Virginia, USA.
* Portions copyright 1991-1995 by Stichting Mathematisch Centrum,
* Amsterdam, The Netherlands. Copying is permitted under the terms
* associated with the main Python distribution, with the additional
* restriction that this additional notice be included and maintained
* on all distributed copies.
*
* This module serves to replace the original parser module written
* by Guido. The functionality is not matched precisely, but the
* original may be implemented on top of this. This is desirable
* since the source of the text to be parsed is now divorced from
* this interface.
*
* Unlike the prior interface, the ability to give a parse tree
* produced by Python code as a tuple to the compiler is enabled by
* this module. See the documentation for more details.
*
* I've added some annotations that help with the lint code-checking
* program, but they're not complete by a long shot. The real errors
* that lint detects are gone, but there are still warnings with
* Py_[X]DECREF() and Py_[X]INCREF() macros. The lint annotations
* look like "NOTE(...)".
*/
#include "Python.h" /* general Python API */
#include "graminit.h" /* symbols defined in the grammar */
#include "node.h" /* internal parser structure */
#include "errcode.h" /* error codes for PyNode_*() */
#include "token.h" /* token definitions */
/* ISTERMINAL() / ISNONTERMINAL() */
#include "compile.h" /* PyNode_Compile() */
#ifdef lint
#include <note.h>
#else
#define NOTE(x)
#endif
/* String constants used to initialize module attributes.
*
*/
static char parser_copyright_string[] =
"Copyright 1995-1996 by Virginia Polytechnic Institute & State\n\
University, Blacksburg, Virginia, USA, and Fred L. Drake, Jr., Reston,\n\
Virginia, USA. Portions copyright 1991-1995 by Stichting Mathematisch\n\
Centrum, Amsterdam, The Netherlands.";
PyDoc_STRVAR(parser_doc_string,
"This is an interface to Python's internal parser.");
static char parser_version_string[] = "0.5";
typedef PyObject* (*SeqMaker) (Py_ssize_t length);
typedef int (*SeqInserter) (PyObject* sequence,
Py_ssize_t index,
PyObject* element);
/* The function below is copyrighted by Stichting Mathematisch Centrum. The
* original copyright statement is included below, and continues to apply
* in full to the function immediately following. All other material is
* original, copyrighted by Fred L. Drake, Jr. and Virginia Polytechnic
* Institute and State University. Changes were made to comply with the
* new naming conventions. Added arguments to provide support for creating
* lists as well as tuples, and optionally including the line numbers.
*/
static PyObject*
node2tuple(node *n, /* node to convert */
SeqMaker mkseq, /* create sequence */
SeqInserter addelem, /* func. to add elem. in seq. */
int lineno, /* include line numbers? */
int col_offset) /* include column offsets? */
{
if (n == NULL) {
Py_INCREF(Py_None);
return (Py_None);
}
if (ISNONTERMINAL(TYPE(n))) {
int i;
PyObject *v;
PyObject *w;
v = mkseq(1 + NCH(n) + (TYPE(n) == encoding_decl));
if (v == NULL)
return (v);
w = PyInt_FromLong(TYPE(n));
if (w == NULL) {
Py_DECREF(v);
return ((PyObject*) NULL);
}
(void) addelem(v, 0, w);
for (i = 0; i < NCH(n); i++) {
w = node2tuple(CHILD(n, i), mkseq, addelem, lineno, col_offset);
if (w == NULL) {
Py_DECREF(v);
return ((PyObject*) NULL);
}
(void) addelem(v, i+1, w);
}
if (TYPE(n) == encoding_decl)
(void) addelem(v, i+1, PyString_FromString(STR(n)));
return (v);
}
else if (ISTERMINAL(TYPE(n))) {
PyObject *result = mkseq(2 + lineno + col_offset);
if (result != NULL) {
(void) addelem(result, 0, PyInt_FromLong(TYPE(n)));
(void) addelem(result, 1, PyString_FromString(STR(n)));
if (lineno == 1)
(void) addelem(result, 2, PyInt_FromLong(n->n_lineno));
if (col_offset == 1)
(void) addelem(result, 3, PyInt_FromLong(n->n_col_offset));
}
return (result);
}
else {
PyErr_SetString(PyExc_SystemError,
"unrecognized parse tree node type");
return ((PyObject*) NULL);
}
}
/*
* End of material copyrighted by Stichting Mathematisch Centrum.
*/
/* There are two types of intermediate objects we're interested in:
* 'eval' and 'exec' types. These constants can be used in the st_type
* field of the object type to identify which any given object represents.
* These should probably go in an external header to allow other extensions
* to use them, but then, we really should be using C++ too. ;-)
*/
#define PyST_EXPR 1
#define PyST_SUITE 2
/* These are the internal objects and definitions required to implement the
* ST type. Most of the internal names are more reminiscent of the 'old'
* naming style, but the code uses the new naming convention.
*/
static PyObject*
parser_error = 0;
typedef struct {
PyObject_HEAD /* standard object header */
node* st_node; /* the node* returned by the parser */
int st_type; /* EXPR or SUITE ? */
} PyST_Object;
static void parser_free(PyST_Object *st);
static int parser_compare(PyST_Object *left, PyST_Object *right);
static PyObject *parser_getattr(PyObject *self, char *name);
static
PyTypeObject PyST_Type = {
PyObject_HEAD_INIT(NULL)
0,
"parser.st", /* tp_name */
(int) sizeof(PyST_Object), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)parser_free, /* tp_dealloc */
0, /* tp_print */
parser_getattr, /* tp_getattr */
0, /* tp_setattr */
(cmpfunc)parser_compare, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
/* Functions to access object as input/output buffer */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
/* __doc__ */
"Intermediate representation of a Python parse tree."
}; /* PyST_Type */
static int
parser_compare_nodes(node *left, node *right)
{
int j;
if (TYPE(left) < TYPE(right))
return (-1);
if (TYPE(right) < TYPE(left))
return (1);
if (ISTERMINAL(TYPE(left)))
return (strcmp(STR(left), STR(right)));
if (NCH(left) < NCH(right))
return (-1);
if (NCH(right) < NCH(left))
return (1);
for (j = 0; j < NCH(left); ++j) {
int v = parser_compare_nodes(CHILD(left, j), CHILD(right, j));
if (v != 0)
return (v);
}
return (0);
}
/* int parser_compare(PyST_Object* left, PyST_Object* right)
*
* Comparison function used by the Python operators ==, !=, <, >, <=, >=
* This really just wraps a call to parser_compare_nodes() with some easy
* checks and protection code.
*
*/
static int
parser_compare(PyST_Object *left, PyST_Object *right)
{
if (left == right)
return (0);
if ((left == 0) || (right == 0))
return (-1);
return (parser_compare_nodes(left->st_node, right->st_node));
}
/* parser_newstobject(node* st)
*
* Allocates a new Python object representing an ST. This is simply the
* 'wrapper' object that holds a node* and allows it to be passed around in
* Python code.
*
*/
static PyObject*
parser_newstobject(node *st, int type)
{
PyST_Object* o = PyObject_New(PyST_Object, &PyST_Type);
if (o != 0) {
o->st_node = st;
o->st_type = type;
}
else {
PyNode_Free(st);
}
return ((PyObject*)o);
}
/* void parser_free(PyST_Object* st)
*
* This is called by a del statement that reduces the reference count to 0.
*
*/
static void
parser_free(PyST_Object *st)
{
PyNode_Free(st->st_node);
PyObject_Del(st);
}
/* parser_st2tuple(PyObject* self, PyObject* args, PyObject* kw)
*
* This provides conversion from a node* to a tuple object that can be
* returned to the Python-level caller. The ST object is not modified.
*
*/
static PyObject*
parser_st2tuple(PyST_Object *self, PyObject *args, PyObject *kw)
{
PyObject *line_option = 0;
PyObject *col_option = 0;
PyObject *res = 0;
int ok;
static char *keywords[] = {"ast", "line_info", "col_info", NULL};
if (self == NULL) {
ok = PyArg_ParseTupleAndKeywords(args, kw, "O!|OO:st2tuple", keywords,
&PyST_Type, &self, &line_option,
&col_option);
}
else
ok = PyArg_ParseTupleAndKeywords(args, kw, "|OO:totuple", &keywords[1],
&line_option, &col_option);
if (ok != 0) {
int lineno = 0;
int col_offset = 0;
if (line_option != NULL) {
lineno = (PyObject_IsTrue(line_option) != 0) ? 1 : 0;
}
if (col_option != NULL) {
col_offset = (PyObject_IsTrue(col_option) != 0) ? 1 : 0;
}
/*
* Convert ST into a tuple representation. Use Guido's function,
* since it's known to work already.
*/
res = node2tuple(((PyST_Object*)self)->st_node,
PyTuple_New, PyTuple_SetItem, lineno, col_offset);
}
return (res);
}
/* parser_st2list(PyObject* self, PyObject* args, PyObject* kw)
*
* This provides conversion from a node* to a list object that can be
* returned to the Python-level caller. The ST object is not modified.
*
*/
static PyObject*
parser_st2list(PyST_Object *self, PyObject *args, PyObject *kw)
{
PyObject *line_option = 0;
PyObject *col_option = 0;
PyObject *res = 0;
int ok;
static char *keywords[] = {"ast", "line_info", "col_info", NULL};
if (self == NULL)
ok = PyArg_ParseTupleAndKeywords(args, kw, "O!|OO:st2list", keywords,
&PyST_Type, &self, &line_option,
&col_option);
else
ok = PyArg_ParseTupleAndKeywords(args, kw, "|OO:tolist", &keywords[1],
&line_option, &col_option);
if (ok) {
int lineno = 0;
int col_offset = 0;
if (line_option != 0) {
lineno = PyObject_IsTrue(line_option) ? 1 : 0;
}
if (col_option != NULL) {
col_offset = (PyObject_IsTrue(col_option) != 0) ? 1 : 0;
}
/*
* Convert ST into a tuple representation. Use Guido's function,
* since it's known to work already.
*/
res = node2tuple(self->st_node,
PyList_New, PyList_SetItem, lineno, col_offset);
}
return (res);
}
/* parser_compilest(PyObject* self, PyObject* args)
*
* This function creates code objects from the parse tree represented by
* the passed-in data object. An optional file name is passed in as well.
*
*/
static PyObject*
parser_compilest(PyST_Object *self, PyObject *args, PyObject *kw)
{
PyObject* res = 0;
char* str = "<syntax-tree>";
int ok;
static char *keywords[] = {"ast", "filename", NULL};
if (self == NULL)
ok = PyArg_ParseTupleAndKeywords(args, kw, "O!|s:compilest", keywords,
&PyST_Type, &self, &str);
else
ok = PyArg_ParseTupleAndKeywords(args, kw, "|s:compile", &keywords[1],
&str);
if (ok)
res = (PyObject *)PyNode_Compile(self->st_node, str);
return (res);
}
/* PyObject* parser_isexpr(PyObject* self, PyObject* args)
* PyObject* parser_issuite(PyObject* self, PyObject* args)
*
* Checks the passed-in ST object to determine if it is an expression or
* a statement suite, respectively. The return is a Python truth value.
*
*/
static PyObject*
parser_isexpr(PyST_Object *self, PyObject *args, PyObject *kw)
{
PyObject* res = 0;
int ok;
static char *keywords[] = {"ast", NULL};
if (self == NULL)
ok = PyArg_ParseTupleAndKeywords(args, kw, "O!:isexpr", keywords,
&PyST_Type, &self);
else
ok = PyArg_ParseTupleAndKeywords(args, kw, ":isexpr", &keywords[1]);
if (ok) {
/* Check to see if the ST represents an expression or not. */
res = (self->st_type == PyST_EXPR) ? Py_True : Py_False;
Py_INCREF(res);
}
return (res);
}
static PyObject*
parser_issuite(PyST_Object *self, PyObject *args, PyObject *kw)
{
PyObject* res = 0;
int ok;
static char *keywords[] = {"ast", NULL};
if (self == NULL)
ok = PyArg_ParseTupleAndKeywords(args, kw, "O!:issuite", keywords,
&PyST_Type, &self);
else
ok = PyArg_ParseTupleAndKeywords(args, kw, ":issuite", &keywords[1]);
if (ok) {
/* Check to see if the ST represents an expression or not. */
res = (self->st_type == PyST_EXPR) ? Py_False : Py_True;
Py_INCREF(res);
}
return (res);
}
#define PUBLIC_METHOD_TYPE (METH_VARARGS|METH_KEYWORDS)
static PyMethodDef
parser_methods[] = {
{"compile", (PyCFunction)parser_compilest, PUBLIC_METHOD_TYPE,
PyDoc_STR("Compile this ST object into a code object.")},
{"isexpr", (PyCFunction)parser_isexpr, PUBLIC_METHOD_TYPE,
PyDoc_STR("Determines if this ST object was created from an expression.")},
{"issuite", (PyCFunction)parser_issuite, PUBLIC_METHOD_TYPE,
PyDoc_STR("Determines if this ST object was created from a suite.")},
{"tolist", (PyCFunction)parser_st2list, PUBLIC_METHOD_TYPE,
PyDoc_STR("Creates a list-tree representation of this ST.")},
{"totuple", (PyCFunction)parser_st2tuple, PUBLIC_METHOD_TYPE,
PyDoc_STR("Creates a tuple-tree representation of this ST.")},
{NULL, NULL, 0, NULL}
};
static PyObject*
parser_getattr(PyObject *self, char *name)
{
return (Py_FindMethod(parser_methods, self, name));
}
/* err_string(char* message)
*
* Sets the error string for an exception of type ParserError.
*
*/
static void
err_string(char *message)
{
PyErr_SetString(parser_error, message);
}
/* PyObject* parser_do_parse(PyObject* args, int type)
*
* Internal function to actually execute the parse and return the result if
* successful or set an exception if not.
*
*/
static PyObject*
parser_do_parse(PyObject *args, PyObject *kw, char *argspec, int type)
{
char* string = 0;
PyObject* res = 0;
static char *keywords[] = {"source", NULL};
if (PyArg_ParseTupleAndKeywords(args, kw, argspec, keywords, &string)) {
node* n = PyParser_SimpleParseString(string,
(type == PyST_EXPR)
? eval_input : file_input);
if (n)
res = parser_newstobject(n, type);
}
return (res);
}
/* PyObject* parser_expr(PyObject* self, PyObject* args)
* PyObject* parser_suite(PyObject* self, PyObject* args)
*
* External interfaces to the parser itself. Which is called determines if
* the parser attempts to recognize an expression ('eval' form) or statement
* suite ('exec' form). The real work is done by parser_do_parse() above.
*
*/
static PyObject*
parser_expr(PyST_Object *self, PyObject *args, PyObject *kw)
{
NOTE(ARGUNUSED(self))
return (parser_do_parse(args, kw, "s:expr", PyST_EXPR));
}
static PyObject*
parser_suite(PyST_Object *self, PyObject *args, PyObject *kw)
{
NOTE(ARGUNUSED(self))
return (parser_do_parse(args, kw, "s:suite", PyST_SUITE));
}
/* This is the messy part of the code. Conversion from a tuple to an ST
* object requires that the input tuple be valid without having to rely on
* catching an exception from the compiler. This is done to allow the
* compiler itself to remain fast, since most of its input will come from
* the parser directly, and therefore be known to be syntactically correct.
* This validation is done to ensure that we don't core dump the compile
* phase, returning an exception instead.
*
* Two aspects can be broken out in this code: creating a node tree from
* the tuple passed in, and verifying that it is indeed valid. It may be
* advantageous to expand the number of ST types to include funcdefs and
* lambdadefs to take advantage of the optimizer, recognizing those STs
* here. They are not necessary, and not quite as useful in a raw form.
* For now, let's get expressions and suites working reliably.
*/
static node* build_node_tree(PyObject *tuple);
static int validate_expr_tree(node *tree);
static int validate_file_input(node *tree);
static int validate_encoding_decl(node *tree);
/* PyObject* parser_tuple2st(PyObject* self, PyObject* args)
*
* This is the public function, called from the Python code. It receives a
* single tuple object from the caller, and creates an ST object if the
* tuple can be validated. It does this by checking the first code of the
* tuple, and, if acceptable, builds the internal representation. If this
* step succeeds, the internal representation is validated as fully as
* possible with the various validate_*() routines defined below.
*
* This function must be changed if support is to be added for PyST_FRAGMENT
* ST objects.
*
*/
static PyObject*
parser_tuple2st(PyST_Object *self, PyObject *args, PyObject *kw)
{
NOTE(ARGUNUSED(self))
PyObject *st = 0;
PyObject *tuple;
node *tree;
static char *keywords[] = {"sequence", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kw, "O:sequence2st", keywords,
&tuple))
return (0);
if (!PySequence_Check(tuple)) {
PyErr_SetString(PyExc_ValueError,
"sequence2st() requires a single sequence argument");
return (0);
}
/*
* Convert the tree to the internal form before checking it.
*/
tree = build_node_tree(tuple);
if (tree != 0) {
int start_sym = TYPE(tree);
if (start_sym == eval_input) {
/* Might be an eval form. */
if (validate_expr_tree(tree))
st = parser_newstobject(tree, PyST_EXPR);
else
PyNode_Free(tree);
}
else if (start_sym == file_input) {
/* This looks like an exec form so far. */
if (validate_file_input(tree))
st = parser_newstobject(tree, PyST_SUITE);
else
PyNode_Free(tree);
}
else if (start_sym == encoding_decl) {
/* This looks like an encoding_decl so far. */
if (validate_encoding_decl(tree))
st = parser_newstobject(tree, PyST_SUITE);
else
PyNode_Free(tree);
}
else {
/* This is a fragment, at best. */
PyNode_Free(tree);
err_string("parse tree does not use a valid start symbol");
}
}
/* Make sure we throw an exception on all errors. We should never
* get this, but we'd do well to be sure something is done.
*/
if (st == NULL && !PyErr_Occurred())
err_string("unspecified ST error occurred");
return st;
}
/* node* build_node_children()
*
* Iterate across the children of the current non-terminal node and build
* their structures. If successful, return the root of this portion of
* the tree, otherwise, 0. Any required exception will be specified already,
* and no memory will have been deallocated.
*
*/
static node*
build_node_children(PyObject *tuple, node *root, int *line_num)
{
Py_ssize_t len = PyObject_Size(tuple);
Py_ssize_t i;
int err;
for (i = 1; i < len; ++i) {
/* elem must always be a sequence, however simple */
PyObject* elem = PySequence_GetItem(tuple, i);
int ok = elem != NULL;
long type = 0;
char *strn = 0;
if (ok)
ok = PySequence_Check(elem);
if (ok) {
PyObject *temp = PySequence_GetItem(elem, 0);
if (temp == NULL)
ok = 0;
else {
ok = PyInt_Check(temp);
if (ok)
type = PyInt_AS_LONG(temp);
Py_DECREF(temp);
}
}
if (!ok) {
PyObject *err = Py_BuildValue("os", elem,
"Illegal node construct.");
PyErr_SetObject(parser_error, err);
Py_XDECREF(err);
Py_XDECREF(elem);
return (0);
}
if (ISTERMINAL(type)) {
Py_ssize_t len = PyObject_Size(elem);
PyObject *temp;
if ((len != 2) && (len != 3)) {
err_string("terminal nodes must have 2 or 3 entries");
return 0;
}
temp = PySequence_GetItem(elem, 1);
if (temp == NULL)
return 0;
if (!PyString_Check(temp)) {
PyErr_Format(parser_error,
"second item in terminal node must be a string,"
" found %s",
temp->ob_type->tp_name);
Py_DECREF(temp);
return 0;
}
if (len == 3) {
PyObject *o = PySequence_GetItem(elem, 2);
if (o != NULL) {
if (PyInt_Check(o))
*line_num = PyInt_AS_LONG(o);
else {
PyErr_Format(parser_error,
"third item in terminal node must be an"
" integer, found %s",
temp->ob_type->tp_name);
Py_DECREF(o);
Py_DECREF(temp);
return 0;
}
Py_DECREF(o);
}
}
len = PyString_GET_SIZE(temp) + 1;
strn = (char *)PyObject_MALLOC(len);
if (strn != NULL)
(void) memcpy(strn, PyString_AS_STRING(temp), len);
Py_DECREF(temp);
}
else if (!ISNONTERMINAL(type)) {
/*
* It has to be one or the other; this is an error.
* Throw an exception.
*/
PyObject *err = Py_BuildValue("os", elem, "unknown node type.");
PyErr_SetObject(parser_error, err);
Py_XDECREF(err);
Py_XDECREF(elem);
return (0);
}
err = PyNode_AddChild(root, type, strn, *line_num, 0);
if (err == E_NOMEM) {
PyObject_FREE(strn);
return (node *) PyErr_NoMemory();
}
if (err == E_OVERFLOW) {
PyObject_FREE(strn);
PyErr_SetString(PyExc_ValueError,
"unsupported number of child nodes");
return NULL;
}
if (ISNONTERMINAL(type)) {
node* new_child = CHILD(root, i - 1);
if (new_child != build_node_children(elem, new_child, line_num)) {
Py_XDECREF(elem);
return (0);
}
}
else if (type == NEWLINE) { /* It's true: we increment the */
++(*line_num); /* line number *after* the newline! */
}
Py_XDECREF(elem);
}
return root;
}
static node*
build_node_tree(PyObject *tuple)
{
node* res = 0;
PyObject *temp = PySequence_GetItem(tuple, 0);
long num = -1;
if (temp != NULL)
num = PyInt_AsLong(temp);
Py_XDECREF(temp);
if (ISTERMINAL(num)) {
/*
* The tuple is simple, but it doesn't start with a start symbol.
* Throw an exception now and be done with it.
*/
tuple = Py_BuildValue("os", tuple,
"Illegal syntax-tree; cannot start with terminal symbol.");
PyErr_SetObject(parser_error, tuple);
Py_XDECREF(tuple);
}
else if (ISNONTERMINAL(num)) {
/*
* Not efficient, but that can be handled later.
*/
int line_num = 0;
PyObject *encoding = NULL;
if (num == encoding_decl) {
encoding = PySequence_GetItem(tuple, 2);
/* tuple isn't borrowed anymore here, need to DECREF */
tuple = PySequence_GetSlice(tuple, 0, 2);
}
res = PyNode_New(num);
if (res != NULL) {
if (res != build_node_children(tuple, res, &line_num)) {
PyNode_Free(res);
res = NULL;
}
if (res && encoding) {
Py_ssize_t len;
len = PyString_GET_SIZE(encoding) + 1;
res->n_str = (char *)PyObject_MALLOC(len);
if (res->n_str != NULL)
(void) memcpy(res->n_str, PyString_AS_STRING(encoding), len);
Py_DECREF(encoding);
Py_DECREF(tuple);
}
}
}
else {
/* The tuple is illegal -- if the number is neither TERMINAL nor
* NONTERMINAL, we can't use it. Not sure the implementation
* allows this condition, but the API doesn't preclude it.
*/
PyObject *err = Py_BuildValue("os", tuple,
"Illegal component tuple.");
PyErr_SetObject(parser_error, err);
Py_XDECREF(err);
}
return (res);
}
/*
* Validation routines used within the validation section:
*/
static int validate_terminal(node *terminal, int type, char *string);
#define validate_ampersand(ch) validate_terminal(ch, AMPER, "&")
#define validate_circumflex(ch) validate_terminal(ch, CIRCUMFLEX, "^")
#define validate_colon(ch) validate_terminal(ch, COLON, ":")
#define validate_comma(ch) validate_terminal(ch, COMMA, ",")
#define validate_dedent(ch) validate_terminal(ch, DEDENT, "")
#define validate_equal(ch) validate_terminal(ch, EQUAL, "=")
#define validate_indent(ch) validate_terminal(ch, INDENT, (char*)NULL)
#define validate_lparen(ch) validate_terminal(ch, LPAR, "(")
#define validate_newline(ch) validate_terminal(ch, NEWLINE, (char*)NULL)
#define validate_rparen(ch) validate_terminal(ch, RPAR, ")")
#define validate_semi(ch) validate_terminal(ch, SEMI, ";")
#define validate_star(ch) validate_terminal(ch, STAR, "*")
#define validate_vbar(ch) validate_terminal(ch, VBAR, "|")
#define validate_doublestar(ch) validate_terminal(ch, DOUBLESTAR, "**")
#define validate_dot(ch) validate_terminal(ch, DOT, ".")
#define validate_at(ch) validate_terminal(ch, AT, "@")
#define validate_name(ch, str) validate_terminal(ch, NAME, str)
#define VALIDATER(n) static int validate_##n(node *tree)
VALIDATER(node); VALIDATER(small_stmt);
VALIDATER(class); VALIDATER(node);
VALIDATER(parameters); VALIDATER(suite);
VALIDATER(testlist); VALIDATER(varargslist);
VALIDATER(vfpdef);
VALIDATER(stmt); VALIDATER(simple_stmt);
VALIDATER(expr_stmt); VALIDATER(power);
VALIDATER(del_stmt);
VALIDATER(return_stmt); VALIDATER(raise_stmt);
VALIDATER(import_stmt); VALIDATER(import_stmt);
VALIDATER(import_name); VALIDATER(yield_stmt);
VALIDATER(global_stmt); VALIDATER(assert_stmt);
VALIDATER(compound_stmt);
VALIDATER(while); VALIDATER(for);
VALIDATER(try); VALIDATER(except_clause);
VALIDATER(test); VALIDATER(and_test);
VALIDATER(not_test); VALIDATER(comparison);
VALIDATER(comp_op);
VALIDATER(star_expr); VALIDATER(expr);
VALIDATER(xor_expr); VALIDATER(and_expr);
VALIDATER(shift_expr); VALIDATER(arith_expr);
VALIDATER(term); VALIDATER(factor);
VALIDATER(atom); VALIDATER(lambdef);
VALIDATER(trailer); VALIDATER(subscript);
VALIDATER(subscriptlist); VALIDATER(sliceop);
VALIDATER(exprlist); VALIDATER(dictorsetmaker);
VALIDATER(arglist); VALIDATER(argument);
VALIDATER(testlist1); VALIDATER(comp_for);
VALIDATER(comp_iter); VALIDATER(comp_if);
VALIDATER(testlist_comp); VALIDATER(yield_expr);
VALIDATER(yield_or_testlist); VALIDATER(or_test);
VALIDATER(test_nocond); VALIDATER(lambdef_nocond);
#undef VALIDATER
#define is_even(n) (((n) & 1) == 0)
#define is_odd(n) (((n) & 1) == 1)
static int
validate_ntype(node *n, int t)
{
if (TYPE(n) != t) {
PyErr_Format(parser_error, "Expected node type %d, got %d.",
t, TYPE(n));
return 0;
}
return 1;
}
/* Verifies that the number of child nodes is exactly 'num', raising
* an exception if it isn't. The exception message does not indicate
* the exact number of nodes, allowing this to be used to raise the
* "right" exception when the wrong number of nodes is present in a
* specific variant of a statement's syntax. This is commonly used
* in that fashion.
*/
static int
validate_numnodes(node *n, int num, const char *const name)
{
if (NCH(n) != num) {
PyErr_Format(parser_error,
"Illegal number of children for %s node.", name);
return 0;
}
return 1;
}
static int
validate_terminal(node *terminal, int type, char *string)
{
int res = (validate_ntype(terminal, type)
&& ((string == 0) || (strcmp(string, STR(terminal)) == 0)));
if (!res && !PyErr_Occurred()) {
PyErr_Format(parser_error,
"Illegal terminal: expected \"%s\"", string);
}
return (res);
}
/* X (',' X) [',']
*/
static int
validate_repeating_list(node *tree, int ntype, int (*vfunc)(node *),
const char *const name)
{
int nch = NCH(tree);
int res = (nch && validate_ntype(tree, ntype)
&& vfunc(CHILD(tree, 0)));
if (!res && !PyErr_Occurred())
(void) validate_numnodes(tree, 1, name);
else {
if (is_even(nch))
res = validate_comma(CHILD(tree, --nch));
if (res && nch > 1) {
int pos = 1;
for ( ; res && pos < nch; pos += 2)
res = (validate_comma(CHILD(tree, pos))
&& vfunc(CHILD(tree, pos + 1)));
}
}
return (res);
}
/* validate_class()
*
* classdef:
* 'class' NAME ['(' testlist ')'] ':' suite
*/
static int
validate_class(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, classdef) &&
((nch == 4) || (nch == 6) || (nch == 7)));
if (res) {
res = (validate_name(CHILD(tree, 0), "class")
&& validate_ntype(CHILD(tree, 1), NAME)
&& validate_colon(CHILD(tree, nch - 2))
&& validate_suite(CHILD(tree, nch - 1)));
}
else {
(void) validate_numnodes(tree, 4, "class");
}
if (res) {
if (nch == 7) {
res = ((validate_lparen(CHILD(tree, 2)) &&
validate_testlist(CHILD(tree, 3)) &&
validate_rparen(CHILD(tree, 4))));
}
else if (nch == 6) {
res = (validate_lparen(CHILD(tree,2)) &&
validate_rparen(CHILD(tree,3)));
}
}
return (res);
}
/* if_stmt:
* 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
*/
static int
validate_if(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, if_stmt)
&& (nch >= 4)
&& validate_name(CHILD(tree, 0), "if")
&& validate_test(CHILD(tree, 1))
&& validate_colon(CHILD(tree, 2))
&& validate_suite(CHILD(tree, 3)));
if (res && ((nch % 4) == 3)) {
/* ... 'else' ':' suite */
res = (validate_name(CHILD(tree, nch - 3), "else")
&& validate_colon(CHILD(tree, nch - 2))
&& validate_suite(CHILD(tree, nch - 1)));
nch -= 3;
}
else if (!res && !PyErr_Occurred())
(void) validate_numnodes(tree, 4, "if");
if ((nch % 4) != 0)
/* Will catch the case for nch < 4 */
res = validate_numnodes(tree, 0, "if");
else if (res && (nch > 4)) {
/* ... ('elif' test ':' suite)+ ... */
int j = 4;
while ((j < nch) && res) {
res = (validate_name(CHILD(tree, j), "elif")
&& validate_colon(CHILD(tree, j + 2))
&& validate_test(CHILD(tree, j + 1))
&& validate_suite(CHILD(tree, j + 3)));
j += 4;
}
}
return (res);
}
/* parameters:
* '(' [varargslist] ')'
*
*/
static int
validate_parameters(node *tree)
{
int nch = NCH(tree);
int res = validate_ntype(tree, parameters) && ((nch == 2) || (nch == 3));
if (res) {
res = (validate_lparen(CHILD(tree, 0))
&& validate_rparen(CHILD(tree, nch - 1)));
if (res && (nch == 3))
res = validate_varargslist(CHILD(tree, 1));
}
else {
(void) validate_numnodes(tree, 2, "parameters");
}
return (res);
}
/* validate_suite()
*
* suite:
* simple_stmt
* | NEWLINE INDENT stmt+ DEDENT
*/
static int
validate_suite(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, suite) && ((nch == 1) || (nch >= 4)));
if (res && (nch == 1))
res = validate_simple_stmt(CHILD(tree, 0));
else if (res) {
/* NEWLINE INDENT stmt+ DEDENT */
res = (validate_newline(CHILD(tree, 0))
&& validate_indent(CHILD(tree, 1))
&& validate_stmt(CHILD(tree, 2))
&& validate_dedent(CHILD(tree, nch - 1)));
if (res && (nch > 4)) {
int i = 3;
--nch; /* forget the DEDENT */
for ( ; res && (i < nch); ++i)
res = validate_stmt(CHILD(tree, i));
}
else if (nch < 4)
res = validate_numnodes(tree, 4, "suite");
}
return (res);
}
static int
validate_testlist(node *tree)
{
return (validate_repeating_list(tree, testlist,
validate_test, "testlist"));
}
static int
validate_testlist1(node *tree)
{
return (validate_repeating_list(tree, testlist1,
validate_test, "testlist1"));
}
/* validate either vfpdef or tfpdef.
* vfpdef: NAME
* tfpdef: NAME [':' test]
*/
static int
validate_vfpdef(node *tree)
{
int nch = NCH(tree);
if (TYPE(tree) == vfpdef) {
return nch == 1 && validate_name(CHILD(tree, 0), NULL);
}
else if (TYPE(tree) == tfpdef) {
if (nch == 1) {
return validate_name(CHILD(tree, 0), NULL);
}
else if (nch == 3) {
return validate_name(CHILD(tree, 0), NULL) &&
validate_colon(CHILD(tree, 1)) &&
validate_test(CHILD(tree, 2));
}
}
return 0;
}
/* '*' vfpdef (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef
* ..or tfpdef in place of vfpdef. vfpdef: NAME; tfpdef: NAME [':' test]
*/
static int
validate_varargslist_trailer(node *tree, int start)
{
int nch = NCH(tree);
int res = 0, i;
int sym;
if (nch <= start) {
err_string("expected variable argument trailer for varargslist");
return 0;
}
sym = TYPE(CHILD(tree, start));
if (sym == STAR) {
/*
* '*' vfpdef (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef
*/
if (nch-start == 2)
res = validate_vfpdef(CHILD(tree, start+1));
else if (nch-start == 5 && TYPE(CHILD(tree, start+2)) == COMMA)
res = (validate_vfpdef(CHILD(tree, start+1))
&& validate_comma(CHILD(tree, start+2))
&& validate_doublestar(CHILD(tree, start+3))
&& validate_vfpdef(CHILD(tree, start+4)));
else {
/* skip over vfpdef (',' vfpdef ['=' test])* */
i = start + 1;
if (TYPE(CHILD(tree, i)) == vfpdef ||
TYPE(CHILD(tree, i)) == tfpdef) { /* skip over vfpdef or tfpdef */
i += 1;
}
while (res && i+1 < nch) { /* validate (',' vfpdef ['=' test])* */
res = validate_comma(CHILD(tree, i));
if (TYPE(CHILD(tree, i+1)) == DOUBLESTAR)
break;
res = res && validate_vfpdef(CHILD(tree, i+1));
if (res && i+2 < nch && TYPE(CHILD(tree, i+2)) == EQUAL) {
res = res && (i+3 < nch)
&& validate_test(CHILD(tree, i+3));
i += 4;
}
else {
i += 2;
}
}
/* [',' '**' vfpdef] */
if (res && i+1 < nch && TYPE(CHILD(tree, i+1)) == DOUBLESTAR) {
res = validate_vfpdef(CHILD(tree, i+2));
}
}
}
else if (sym == DOUBLESTAR) {
/*
* '**' NAME
*/
if (nch-start == 2)
res = validate_vfpdef(CHILD(tree, start+1));
}
if (!res)
err_string("illegal variable argument trailer for varargslist");
return res;
}
/* validate_varargslist()
*
* Validate typedargslist or varargslist.
*
* typedargslist: ((tfpdef ['=' test] ',')*
* ('*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] |
* '**' tfpdef)
* | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
* tfpdef: NAME [':' test]
* varargslist: ((vfpdef ['=' test] ',')*
* ('*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] |
* '**' vfpdef)
* | vfpdef ['=' test] (',' vfpdef ['=' test])* [','])
* vfpdef: NAME
*
*/
static int
validate_varargslist(node *tree)
{
int nch = NCH(tree);
int res = (TYPE(tree) == varargslist ||
TYPE(tree) == typedargslist) &&
(nch != 0);
int sym;
node *ch;
int i = 0;
if (!res)
return 0;
if (nch < 1) {
err_string("varargslist missing child nodes");
return 0;
}
while (i < nch) {
ch = CHILD(tree, i);
sym = TYPE(ch);
if (sym == vfpdef || sym == tfpdef) {
/* validate (vfpdef ['=' test] ',')+ */
res = validate_vfpdef(ch);
++i;
if (res && (i+2 <= nch) && TYPE(CHILD(tree, i)) == EQUAL) {
res = (validate_equal(CHILD(tree, i))
&& validate_test(CHILD(tree, i+1)));
if (res)
i += 2;
}
if (res && i < nch) {
res = validate_comma(CHILD(tree, i));
++i;
}
} else if (sym == DOUBLESTAR || sym == STAR) {
res = validate_varargslist_trailer(tree, i);
break;
} else {
res = 0;
err_string("illegal formation for varargslist");
}
}
return res;
}
/* comp_iter: comp_for | comp_if
*/
static int
validate_comp_iter(node *tree)
{
int res = (validate_ntype(tree, comp_iter)
&& validate_numnodes(tree, 1, "comp_iter"));
if (res && TYPE(CHILD(tree, 0)) == comp_for)
res = validate_comp_for(CHILD(tree, 0));
else
res = validate_comp_if(CHILD(tree, 0));
return res;
}
/* comp_for: 'for' exprlist 'in' test [comp_iter]
*/
static int
validate_comp_for(node *tree)
{
int nch = NCH(tree);
int res;
if (nch == 5)
res = validate_comp_iter(CHILD(tree, 4));
else
res = validate_numnodes(tree, 4, "comp_for");
if (res)
res = (validate_name(CHILD(tree, 0), "for")
&& validate_exprlist(CHILD(tree, 1))
&& validate_name(CHILD(tree, 2), "in")
&& validate_or_test(CHILD(tree, 3)));
return res;
}
/* comp_if: 'if' test_nocond [comp_iter]
*/
static int
validate_comp_if(node *tree)
{
int nch = NCH(tree);
int res;
if (nch == 3)
res = validate_comp_iter(CHILD(tree, 2));
else
res = validate_numnodes(tree, 2, "comp_if");
if (res)
res = (validate_name(CHILD(tree, 0), "if")
&& validate_test_nocond(CHILD(tree, 1)));
return res;
}
/* simple_stmt | compound_stmt
*
*/
static int
validate_stmt(node *tree)
{
int res = (validate_ntype(tree, stmt)
&& validate_numnodes(tree, 1, "stmt"));
if (res) {
tree = CHILD(tree, 0);
if (TYPE(tree) == simple_stmt)
res = validate_simple_stmt(tree);
else
res = validate_compound_stmt(tree);
}
return (res);
}
/* small_stmt (';' small_stmt)* [';'] NEWLINE
*
*/
static int
validate_simple_stmt(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, simple_stmt)
&& (nch >= 2)
&& validate_small_stmt(CHILD(tree, 0))
&& validate_newline(CHILD(tree, nch - 1)));
if (nch < 2)
res = validate_numnodes(tree, 2, "simple_stmt");
--nch; /* forget the NEWLINE */
if (res && is_even(nch))
res = validate_semi(CHILD(tree, --nch));
if (res && (nch > 2)) {
int i;
for (i = 1; res && (i < nch); i += 2)
res = (validate_semi(CHILD(tree, i))
&& validate_small_stmt(CHILD(tree, i + 1)));
}
return (res);
}
static int
validate_small_stmt(node *tree)
{
int nch = NCH(tree);
int res = validate_numnodes(tree, 1, "small_stmt");
if (res) {
int ntype = TYPE(CHILD(tree, 0));
if ( (ntype == expr_stmt)
|| (ntype == del_stmt)
|| (ntype == pass_stmt)
|| (ntype == flow_stmt)
|| (ntype == import_stmt)
|| (ntype == global_stmt)
|| (ntype == assert_stmt))
res = validate_node(CHILD(tree, 0));
else {
res = 0;
err_string("illegal small_stmt child type");
}
}
else if (nch == 1) {
res = 0;
PyErr_Format(parser_error,
"Unrecognized child node of small_stmt: %d.",
TYPE(CHILD(tree, 0)));
}
return (res);
}
/* compound_stmt:
* if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef | decorated
*/
static int
validate_compound_stmt(node *tree)
{
int res = (validate_ntype(tree, compound_stmt)
&& validate_numnodes(tree, 1, "compound_stmt"));
int ntype;
if (!res)
return (0);
tree = CHILD(tree, 0);
ntype = TYPE(tree);
if ( (ntype == if_stmt)
|| (ntype == while_stmt)
|| (ntype == for_stmt)
|| (ntype == try_stmt)
|| (ntype == funcdef)
|| (ntype == classdef)
|| (ntype == decorated))
res = validate_node(tree);
else {
res = 0;
PyErr_Format(parser_error,
"Illegal compound statement type: %d.", TYPE(tree));
}
return (res);
}
static int
validate_yield_or_testlist(node *tree)
{
if (TYPE(tree) == yield_expr)
return validate_yield_expr(tree);
else
return validate_testlist(tree);
}
static int
validate_expr_stmt(node *tree)
{
int j;
int nch = NCH(tree);
int res = (validate_ntype(tree, expr_stmt)
&& is_odd(nch)
&& validate_testlist(CHILD(tree, 0)));
if (res && nch == 3
&& TYPE(CHILD(tree, 1)) == augassign) {
res = validate_numnodes(CHILD(tree, 1), 1, "augassign")
&& validate_yield_or_testlist(CHILD(tree, 2));
if (res) {
char *s = STR(CHILD(CHILD(tree, 1), 0));
res = (strcmp(s, "+=") == 0
|| strcmp(s, "-=") == 0
|| strcmp(s, "*=") == 0
|| strcmp(s, "/=") == 0
|| strcmp(s, "//=") == 0
|| strcmp(s, "%=") == 0
|| strcmp(s, "&=") == 0
|| strcmp(s, "|=") == 0
|| strcmp(s, "^=") == 0
|| strcmp(s, "<<=") == 0
|| strcmp(s, ">>=") == 0
|| strcmp(s, "**=") == 0);
if (!res)
err_string("illegal augmmented assignment operator");
}
}
else {
for (j = 1; res && (j < nch); j += 2)
res = validate_equal(CHILD(tree, j))
&& validate_yield_or_testlist(CHILD(tree, j + 1));
}
return (res);
}
static int
validate_del_stmt(node *tree)
{
return (validate_numnodes(tree, 2, "del_stmt")
&& validate_name(CHILD(tree, 0), "del")
&& validate_exprlist(CHILD(tree, 1)));
}
static int
validate_return_stmt(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, return_stmt)
&& ((nch == 1) || (nch == 2))
&& validate_name(CHILD(tree, 0), "return"));
if (res && (nch == 2))
res = validate_testlist(CHILD(tree, 1));
return (res);
}
static int
validate_raise_stmt(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, raise_stmt)
&& ((nch == 1) || (nch == 2) || (nch == 4) || (nch == 6)));
if (res) {
res = validate_name(CHILD(tree, 0), "raise");
if (res && (nch >= 2))
res = validate_test(CHILD(tree, 1));
if (res && nch > 2) {
res = (validate_comma(CHILD(tree, 2))
&& validate_test(CHILD(tree, 3)));
if (res && (nch > 4))
res = (validate_comma(CHILD(tree, 4))
&& validate_test(CHILD(tree, 5)));
}
}
else
(void) validate_numnodes(tree, 2, "raise");
if (res && (nch == 4))
res = (validate_comma(CHILD(tree, 2))
&& validate_test(CHILD(tree, 3)));
return (res);
}
/* yield_expr: 'yield' [testlist]
*/
static int
validate_yield_expr(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, yield_expr)
&& ((nch == 1) || (nch == 2))
&& validate_name(CHILD(tree, 0), "yield"));
if (res && (nch == 2))
res = validate_testlist(CHILD(tree, 1));
return (res);
}
/* yield_stmt: yield_expr
*/
static int
validate_yield_stmt(node *tree)
{
return (validate_ntype(tree, yield_stmt)
&& validate_numnodes(tree, 1, "yield_stmt")
&& validate_yield_expr(CHILD(tree, 0)));
}
static int
validate_import_as_name(node *tree)
{
int nch = NCH(tree);
int ok = validate_ntype(tree, import_as_name);
if (ok) {
if (nch == 1)
ok = validate_name(CHILD(tree, 0), NULL);
else if (nch == 3)
ok = (validate_name(CHILD(tree, 0), NULL)
&& validate_name(CHILD(tree, 1), "as")
&& validate_name(CHILD(tree, 2), NULL));
else
ok = validate_numnodes(tree, 3, "import_as_name");
}
return ok;
}
/* dotted_name: NAME ("." NAME)*
*/
static int
validate_dotted_name(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, dotted_name)
&& is_odd(nch)
&& validate_name(CHILD(tree, 0), NULL));
int i;
for (i = 1; res && (i < nch); i += 2) {
res = (validate_dot(CHILD(tree, i))
&& validate_name(CHILD(tree, i+1), NULL));
}
return res;
}
/* dotted_as_name: dotted_name [NAME NAME]
*/
static int
validate_dotted_as_name(node *tree)
{
int nch = NCH(tree);
int res = validate_ntype(tree, dotted_as_name);
if (res) {
if (nch == 1)
res = validate_dotted_name(CHILD(tree, 0));
else if (nch == 3)
res = (validate_dotted_name(CHILD(tree, 0))
&& validate_name(CHILD(tree, 1), "as")
&& validate_name(CHILD(tree, 2), NULL));
else {
res = 0;
err_string("illegal number of children for dotted_as_name");
}
}
return res;
}
/* dotted_as_name (',' dotted_as_name)* */
static int
validate_dotted_as_names(node *tree)
{
int nch = NCH(tree);
int res = is_odd(nch) && validate_dotted_as_name(CHILD(tree, 0));
int i;
for (i = 1; res && (i < nch); i += 2)
res = (validate_comma(CHILD(tree, i))
&& validate_dotted_as_name(CHILD(tree, i + 1)));
return (res);
}
/* import_as_name (',' import_as_name)* [','] */
static int
validate_import_as_names(node *tree)
{
int nch = NCH(tree);
int res = validate_import_as_name(CHILD(tree, 0));
int i;
for (i = 1; res && (i + 1 < nch); i += 2)
res = (validate_comma(CHILD(tree, i))
&& validate_import_as_name(CHILD(tree, i + 1)));
return (res);
}
/* 'import' dotted_as_names */
static int
validate_import_name(node *tree)
{
return (validate_ntype(tree, import_name)
&& validate_numnodes(tree, 2, "import_name")
&& validate_name(CHILD(tree, 0), "import")
&& validate_dotted_as_names(CHILD(tree, 1)));
}
/* Helper function to count the number of leading dots in
* 'from ...module import name'
*/
static int
count_from_dots(node *tree)
{
int i;
for (i = 0; i < NCH(tree); i++)
if (TYPE(CHILD(tree, i)) != DOT)
break;
return i;
}
/* 'from' ('.'* dotted_name | '.') 'import' ('*' | '(' import_as_names ')' |
* import_as_names
*/
static int
validate_import_from(node *tree)
{
int nch = NCH(tree);
int ndots = count_from_dots(tree);
int havename = (TYPE(CHILD(tree, ndots + 1)) == dotted_name);
int offset = ndots + havename;
int res = validate_ntype(tree, import_from)
&& (nch >= 4 + ndots)
&& validate_name(CHILD(tree, 0), "from")
&& (!havename || validate_dotted_name(CHILD(tree, ndots + 1)))
&& validate_name(CHILD(tree, offset + 1), "import");
if (res && TYPE(CHILD(tree, offset + 2)) == LPAR)
res = ((nch == offset + 5)
&& validate_lparen(CHILD(tree, offset + 2))
&& validate_import_as_names(CHILD(tree, offset + 3))
&& validate_rparen(CHILD(tree, offset + 4)));
else if (res && TYPE(CHILD(tree, offset + 2)) != STAR)
res = validate_import_as_names(CHILD(tree, offset + 2));
return (res);
}
/* import_stmt: import_name | import_from */
static int
validate_import_stmt(node *tree)
{
int nch = NCH(tree);
int res = validate_numnodes(tree, 1, "import_stmt");
if (res) {
int ntype = TYPE(CHILD(tree, 0));
if (ntype == import_name || ntype == import_from)
res = validate_node(CHILD(tree, 0));
else {
res = 0;
err_string("illegal import_stmt child type");
}
}
else if (nch == 1) {
res = 0;
PyErr_Format(parser_error,
"Unrecognized child node of import_stmt: %d.",
TYPE(CHILD(tree, 0)));
}
return (res);
}
static int
validate_global_stmt(node *tree)
{
int j;
int nch = NCH(tree);
int res = (validate_ntype(tree, global_stmt)
&& is_even(nch) && (nch >= 2));
if (!res && !PyErr_Occurred())
err_string("illegal global statement");
if (res)
res = (validate_name(CHILD(tree, 0), "global")
&& validate_ntype(CHILD(tree, 1), NAME));
for (j = 2; res && (j < nch); j += 2)
res = (validate_comma(CHILD(tree, j))
&& validate_ntype(CHILD(tree, j + 1), NAME));
return (res);
}
/* assert_stmt:
*
* 'assert' test [',' test]
*/
static int
validate_assert_stmt(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, assert_stmt)
&& ((nch == 2) || (nch == 4))
&& (validate_name(CHILD(tree, 0), "assert"))
&& validate_test(CHILD(tree, 1)));
if (!res && !PyErr_Occurred())
err_string("illegal assert statement");
if (res && (nch > 2))
res = (validate_comma(CHILD(tree, 2))
&& validate_test(CHILD(tree, 3)));
return (res);
}
static int
validate_while(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, while_stmt)
&& ((nch == 4) || (nch == 7))
&& validate_name(CHILD(tree, 0), "while")
&& validate_test(CHILD(tree, 1))
&& validate_colon(CHILD(tree, 2))
&& validate_suite(CHILD(tree, 3)));
if (res && (nch == 7))
res = (validate_name(CHILD(tree, 4), "else")
&& validate_colon(CHILD(tree, 5))
&& validate_suite(CHILD(tree, 6)));
return (res);
}
static int
validate_for(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, for_stmt)
&& ((nch == 6) || (nch == 9))
&& validate_name(CHILD(tree, 0), "for")
&& validate_exprlist(CHILD(tree, 1))
&& validate_name(CHILD(tree, 2), "in")
&& validate_testlist(CHILD(tree, 3))
&& validate_colon(CHILD(tree, 4))
&& validate_suite(CHILD(tree, 5)));
if (res && (nch == 9))
res = (validate_name(CHILD(tree, 6), "else")
&& validate_colon(CHILD(tree, 7))
&& validate_suite(CHILD(tree, 8)));
return (res);
}
/* try_stmt:
* 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite]
* | 'try' ':' suite 'finally' ':' suite
*
*/
static int
validate_try(node *tree)
{
int nch = NCH(tree);
int pos = 3;
int res = (validate_ntype(tree, try_stmt)
&& (nch >= 6) && ((nch % 3) == 0));
if (res)
res = (validate_name(CHILD(tree, 0), "try")
&& validate_colon(CHILD(tree, 1))
&& validate_suite(CHILD(tree, 2))
&& validate_colon(CHILD(tree, nch - 2))
&& validate_suite(CHILD(tree, nch - 1)));
else if (!PyErr_Occurred()) {
const char* name = "except";
if (TYPE(CHILD(tree, nch - 3)) != except_clause)
name = STR(CHILD(tree, nch - 3));
PyErr_Format(parser_error,
"Illegal number of children for try/%s node.", name);
}
/* Skip past except_clause sections: */
while (res && (TYPE(CHILD(tree, pos)) == except_clause)) {
res = (validate_except_clause(CHILD(tree, pos))
&& validate_colon(CHILD(tree, pos + 1))
&& validate_suite(CHILD(tree, pos + 2)));
pos += 3;
}
if (res && (pos < nch)) {
res = validate_ntype(CHILD(tree, pos), NAME);
if (res && (strcmp(STR(CHILD(tree, pos)), "finally") == 0))
res = (validate_numnodes(tree, 6, "try/finally")
&& validate_colon(CHILD(tree, 4))
&& validate_suite(CHILD(tree, 5)));
else if (res) {
if (nch == (pos + 3)) {
res = ((strcmp(STR(CHILD(tree, pos)), "except") == 0)
|| (strcmp(STR(CHILD(tree, pos)), "else") == 0));
if (!res)
err_string("illegal trailing triple in try statement");
}
else if (nch == (pos + 6)) {
res = (validate_name(CHILD(tree, pos), "except")
&& validate_colon(CHILD(tree, pos + 1))
&& validate_suite(CHILD(tree, pos + 2))
&& validate_name(CHILD(tree, pos + 3), "else"));
}
else
res = validate_numnodes(tree, pos + 3, "try/except");
}
}
return (res);
}
static int
validate_except_clause(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, except_clause)
&& ((nch == 1) || (nch == 2) || (nch == 4))
&& validate_name(CHILD(tree, 0), "except"));
if (res && (nch > 1))
res = validate_test(CHILD(tree, 1));
if (res && (nch == 4))
res = (validate_name(CHILD(tree, 2), "as")
&& validate_ntype(CHILD(tree, 3), NAME));
return (res);
}
static int
validate_test(node *tree)
{
int nch = NCH(tree);
int res = validate_ntype(tree, test) && is_odd(nch);
if (res && (TYPE(CHILD(tree, 0)) == lambdef))
res = ((nch == 1)
&& validate_lambdef(CHILD(tree, 0)));
else if (res) {
res = validate_or_test(CHILD(tree, 0));
res = (res && (nch == 1 || (nch == 5 &&
validate_name(CHILD(tree, 1), "if") &&
validate_or_test(CHILD(tree, 2)) &&
validate_name(CHILD(tree, 3), "else") &&
validate_test(CHILD(tree, 4)))));
}
return (res);
}
static int
validate_test_nocond(node *tree)
{
int nch = NCH(tree);
int res = validate_ntype(tree, test_nocond) && (nch == 1);
if (res && (TYPE(CHILD(tree, 0)) == lambdef_nocond))
res = (validate_lambdef_nocond(CHILD(tree, 0)));
else if (res) {
res = (validate_or_test(CHILD(tree, 0)));
}
return (res);
}
static int
validate_or_test(node *tree)
{
int nch = NCH(tree);
int res = validate_ntype(tree, or_test) && is_odd(nch);
if (res) {
int pos;
res = validate_and_test(CHILD(tree, 0));
for (pos = 1; res && (pos < nch); pos += 2)
res = (validate_name(CHILD(tree, pos), "or")
&& validate_and_test(CHILD(tree, pos + 1)));
}
return (res);
}
static int
validate_and_test(node *tree)
{
int pos;
int nch = NCH(tree);
int res = (validate_ntype(tree, and_test)
&& is_odd(nch)
&& validate_not_test(CHILD(tree, 0)));
for (pos = 1; res && (pos < nch); pos += 2)
res = (validate_name(CHILD(tree, pos), "and")
&& validate_not_test(CHILD(tree, 0)));
return (res);
}
static int
validate_not_test(node *tree)
{
int nch = NCH(tree);
int res = validate_ntype(tree, not_test) && ((nch == 1) || (nch == 2));
if (res) {
if (nch == 2)
res = (validate_name(CHILD(tree, 0), "not")
&& validate_not_test(CHILD(tree, 1)));
else if (nch == 1)
res = validate_comparison(CHILD(tree, 0));
}
return (res);
}
static int
validate_comparison(node *tree)
{
int pos;
int nch = NCH(tree);
int res = (validate_ntype(tree, comparison)
&& is_odd(nch)
&& validate_star_expr(CHILD(tree, 0)));
for (pos = 1; res && (pos < nch); pos += 2)
res = (validate_comp_op(CHILD(tree, pos))
&& validate_star_expr(CHILD(tree, pos + 1)));
return (res);
}
static int
validate_comp_op(node *tree)
{
int res = 0;
int nch = NCH(tree);
if (!validate_ntype(tree, comp_op))
return (0);
if (nch == 1) {
/*
* Only child will be a terminal with a well-defined symbolic name
* or a NAME with a string of either 'is' or 'in'
*/
tree = CHILD(tree, 0);
switch (TYPE(tree)) {
case LESS:
case GREATER:
case EQEQUAL:
case EQUAL:
case LESSEQUAL:
case GREATEREQUAL:
case NOTEQUAL:
res = 1;
break;
case NAME:
res = ((strcmp(STR(tree), "in") == 0)
|| (strcmp(STR(tree), "is") == 0));
if (!res) {
PyErr_Format(parser_error,
"illegal operator '%s'", STR(tree));
}
break;
default:
err_string("illegal comparison operator type");
break;
}
}
else if ((res = validate_numnodes(tree, 2, "comp_op")) != 0) {
res = (validate_ntype(CHILD(tree, 0), NAME)
&& validate_ntype(CHILD(tree, 1), NAME)
&& (((strcmp(STR(CHILD(tree, 0)), "is") == 0)
&& (strcmp(STR(CHILD(tree, 1)), "not") == 0))
|| ((strcmp(STR(CHILD(tree, 0)), "not") == 0)
&& (strcmp(STR(CHILD(tree, 1)), "in") == 0))));
if (!res && !PyErr_Occurred())
err_string("unknown comparison operator");
}
return (res);
}
static int
validate_star_expr(node *tree)
{
int res = validate_ntype(tree, star_expr);
if (!res) return res;
if (NCH(tree) == 2) {
return validate_ntype(CHILD(tree, 0), STAR) && \
validate_expr(CHILD(tree, 1));
} else {
return validate_expr(CHILD(tree, 0));
}
}
static int
validate_expr(node *tree)
{
int j;
int nch = NCH(tree);
int res = (validate_ntype(tree, expr)
&& is_odd(nch)
&& validate_xor_expr(CHILD(tree, 0)));
for (j = 2; res && (j < nch); j += 2)
res = (validate_xor_expr(CHILD(tree, j))
&& validate_vbar(CHILD(tree, j - 1)));
return (res);
}
static int
validate_xor_expr(node *tree)
{
int j;
int nch = NCH(tree);
int res = (validate_ntype(tree, xor_expr)
&& is_odd(nch)
&& validate_and_expr(CHILD(tree, 0)));
for (j = 2; res && (j < nch); j += 2)
res = (validate_circumflex(CHILD(tree, j - 1))
&& validate_and_expr(CHILD(tree, j)));
return (res);
}
static int
validate_and_expr(node *tree)
{
int pos;
int nch = NCH(tree);
int res = (validate_ntype(tree, and_expr)
&& is_odd(nch)
&& validate_shift_expr(CHILD(tree, 0)));
for (pos = 1; res && (pos < nch); pos += 2)
res = (validate_ampersand(CHILD(tree, pos))
&& validate_shift_expr(CHILD(tree, pos + 1)));
return (res);
}
static int
validate_chain_two_ops(node *tree, int (*termvalid)(node *), int op1, int op2)
{
int pos = 1;
int nch = NCH(tree);
int res = (is_odd(nch)
&& (*termvalid)(CHILD(tree, 0)));
for ( ; res && (pos < nch); pos += 2) {
if (TYPE(CHILD(tree, pos)) != op1)
res = validate_ntype(CHILD(tree, pos), op2);
if (res)
res = (*termvalid)(CHILD(tree, pos + 1));
}
return (res);
}
static int
validate_shift_expr(node *tree)
{
return (validate_ntype(tree, shift_expr)
&& validate_chain_two_ops(tree, validate_arith_expr,
LEFTSHIFT, RIGHTSHIFT));
}
static int
validate_arith_expr(node *tree)
{
return (validate_ntype(tree, arith_expr)
&& validate_chain_two_ops(tree, validate_term, PLUS, MINUS));
}
static int
validate_term(node *tree)
{
int pos = 1;
int nch = NCH(tree);
int res = (validate_ntype(tree, term)
&& is_odd(nch)
&& validate_factor(CHILD(tree, 0)));
for ( ; res && (pos < nch); pos += 2)
res = (((TYPE(CHILD(tree, pos)) == STAR)
|| (TYPE(CHILD(tree, pos)) == SLASH)
|| (TYPE(CHILD(tree, pos)) == DOUBLESLASH)
|| (TYPE(CHILD(tree, pos)) == PERCENT))
&& validate_factor(CHILD(tree, pos + 1)));
return (res);
}
/* factor:
*
* factor: ('+'|'-'|'~') factor | power
*/
static int
validate_factor(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, factor)
&& (((nch == 2)
&& ((TYPE(CHILD(tree, 0)) == PLUS)
|| (TYPE(CHILD(tree, 0)) == MINUS)
|| (TYPE(CHILD(tree, 0)) == TILDE))
&& validate_factor(CHILD(tree, 1)))
|| ((nch == 1)
&& validate_power(CHILD(tree, 0)))));
return (res);
}
/* power:
*
* power: atom trailer* ('**' factor)*
*/
static int
validate_power(node *tree)
{
int pos = 1;
int nch = NCH(tree);
int res = (validate_ntype(tree, power) && (nch >= 1)
&& validate_atom(CHILD(tree, 0)));
while (res && (pos < nch) && (TYPE(CHILD(tree, pos)) == trailer))
res = validate_trailer(CHILD(tree, pos++));
if (res && (pos < nch)) {
if (!is_even(nch - pos)) {
err_string("illegal number of nodes for 'power'");
return (0);
}
for ( ; res && (pos < (nch - 1)); pos += 2)
res = (validate_doublestar(CHILD(tree, pos))
&& validate_factor(CHILD(tree, pos + 1)));
}
return (res);
}
static int
validate_atom(node *tree)
{
int pos;
int nch = NCH(tree);
int res = validate_ntype(tree, atom);
if (res && nch < 1)
res = validate_numnodes(tree, nch+1, "atom");
if (res) {
switch (TYPE(CHILD(tree, 0))) {
case LPAR:
res = ((nch <= 3)
&& (validate_rparen(CHILD(tree, nch - 1))));
if (res && (nch == 3)) {
if (TYPE(CHILD(tree, 1))==yield_expr)
res = validate_yield_expr(CHILD(tree, 1));
else
res = validate_testlist_comp(CHILD(tree, 1));
}
break;
case LSQB:
if (nch == 2)
res = validate_ntype(CHILD(tree, 1), RSQB);
else if (nch == 3)
res = (validate_testlist_comp(CHILD(tree, 1))
&& validate_ntype(CHILD(tree, 2), RSQB));
else {
res = 0;
err_string("illegal list display atom");
}
break;
case LBRACE:
res = ((nch <= 3)
&& validate_ntype(CHILD(tree, nch - 1), RBRACE));
if (res && (nch == 3))
res = validate_dictorsetmaker(CHILD(tree, 1));
break;
case NAME:
case NUMBER:
res = (nch == 1);
break;
case STRING:
for (pos = 1; res && (pos < nch); ++pos)
res = validate_ntype(CHILD(tree, pos), STRING);
break;
case DOT:
res = (nch == 3 &&
validate_ntype(CHILD(tree, 1), DOT) &&
validate_ntype(CHILD(tree, 2), DOT));
break;
default:
res = 0;
break;
}
}
return (res);
}
/* testlist_comp:
* test ( comp_for | (',' test)* [','] )
*/
static int
validate_testlist_comp(node *tree)
{
int nch = NCH(tree);
int ok = nch;
if (nch == 0)
err_string("missing child nodes of testlist_comp");
else {
ok = validate_test(CHILD(tree, 0));
}
/*
* comp_for | (',' test)* [',']
*/
if (nch == 2 && TYPE(CHILD(tree, 1)) == comp_for)
ok = validate_comp_for(CHILD(tree, 1));
else {
/* (',' test)* [','] */
int i = 1;
while (ok && nch - i >= 2) {
ok = (validate_comma(CHILD(tree, i))
&& validate_test(CHILD(tree, i+1)));
i += 2;
}
if (ok && i == nch-1)
ok = validate_comma(CHILD(tree, i));
else if (i != nch) {
ok = 0;
err_string("illegal trailing nodes for testlist_comp");
}
}
return ok;
}
/* decorator:
* '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
*/
static int
validate_decorator(node *tree)
{
int ok;
int nch = NCH(tree);
ok = (validate_ntype(tree, decorator) &&
(nch == 3 || nch == 5 || nch == 6) &&
validate_at(CHILD(tree, 0)) &&
validate_dotted_name(CHILD(tree, 1)) &&
validate_newline(RCHILD(tree, -1)));
if (ok && nch != 3) {
ok = (validate_lparen(CHILD(tree, 2)) &&
validate_rparen(RCHILD(tree, -2)));
if (ok && nch == 6)
ok = validate_arglist(CHILD(tree, 3));
}
return ok;
}
/* decorators:
* decorator+
*/
static int
validate_decorators(node *tree)
{
int i, nch, ok;
nch = NCH(tree);
ok = validate_ntype(tree, decorators) && nch >= 1;
for (i = 0; ok && i < nch; ++i)
ok = validate_decorator(CHILD(tree, i));
return ok;
}
/* funcdef:
*
* -5 -4 -3 -2 -1
* 'def' NAME parameters ':' suite
*/
static int
validate_funcdef(node *tree)
{
int nch = NCH(tree);
int ok = (validate_ntype(tree, funcdef)
&& (nch == 5)
&& validate_name(RCHILD(tree, -5), "def")
&& validate_ntype(RCHILD(tree, -4), NAME)
&& validate_colon(RCHILD(tree, -2))
&& validate_parameters(RCHILD(tree, -3))
&& validate_suite(RCHILD(tree, -1)));
return ok;
}
/* decorated
* decorators (classdef | funcdef)
*/
static int
validate_decorated(node *tree)
{
int nch = NCH(tree);
int ok = (validate_ntype(tree, decorated)
&& (nch == 2)
&& validate_decorators(RCHILD(tree, -2))
&& (validate_funcdef(RCHILD(tree, -1))
|| validate_class(RCHILD(tree, -1)))
);
return ok;
}
static int
validate_lambdef(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, lambdef)
&& ((nch == 3) || (nch == 4))
&& validate_name(CHILD(tree, 0), "lambda")
&& validate_colon(CHILD(tree, nch - 2))
&& validate_test(CHILD(tree, nch - 1)));
if (res && (nch == 4))
res = validate_varargslist(CHILD(tree, 1));
else if (!res && !PyErr_Occurred())
(void) validate_numnodes(tree, 3, "lambdef");
return (res);
}
static int
validate_lambdef_nocond(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, lambdef_nocond)
&& ((nch == 3) || (nch == 4))
&& validate_name(CHILD(tree, 0), "lambda")
&& validate_colon(CHILD(tree, nch - 2))
&& validate_test(CHILD(tree, nch - 1)));
if (res && (nch == 4))
res = validate_varargslist(CHILD(tree, 1));
else if (!res && !PyErr_Occurred())
(void) validate_numnodes(tree, 3, "lambdef_nocond");
return (res);
}
/* arglist:
*
* (argument ',')* (argument [','] | '*' test [',' '**' test] | '**' test)
*/
static int
validate_arglist(node *tree)
{
int nch = NCH(tree);
int i = 0;
int ok = 1;
if (nch <= 0)
/* raise the right error from having an invalid number of children */
return validate_numnodes(tree, nch + 1, "arglist");
if (nch > 1) {
for (i=0; i<nch; i++) {
if (TYPE(CHILD(tree, i)) == argument) {
node *ch = CHILD(tree, i);
if (NCH(ch) == 2 && TYPE(CHILD(ch, 1)) == comp_for) {
err_string("need '(', ')' for generator expression");
return 0;
}
}
}
}
while (ok && nch-i >= 2) {
/* skip leading (argument ',') */
ok = (validate_argument(CHILD(tree, i))
&& validate_comma(CHILD(tree, i+1)));
if (ok)
i += 2;
else
PyErr_Clear();
}
ok = 1;
if (nch-i > 0) {
/*
* argument | '*' test [',' '**' test] | '**' test
*/
int sym = TYPE(CHILD(tree, i));
if (sym == argument) {
ok = validate_argument(CHILD(tree, i));
if (ok && i+1 != nch) {
err_string("illegal arglist specification"
" (extra stuff on end)");
ok = 0;
}
}
else if (sym == STAR) {
ok = validate_star(CHILD(tree, i));
if (ok && (nch-i == 2))
ok = validate_test(CHILD(tree, i+1));
else if (ok && (nch-i == 5))
ok = (validate_test(CHILD(tree, i+1))
&& validate_comma(CHILD(tree, i+2))
&& validate_doublestar(CHILD(tree, i+3))
&& validate_test(CHILD(tree, i+4)));
else {
err_string("illegal use of '*' in arglist");
ok = 0;
}
}
else if (sym == DOUBLESTAR) {
if (nch-i == 2)
ok = (validate_doublestar(CHILD(tree, i))
&& validate_test(CHILD(tree, i+1)));
else {
err_string("illegal use of '**' in arglist");
ok = 0;
}
}
else {
err_string("illegal arglist specification");
ok = 0;
}
}
return (ok);
}
/* argument:
*
* [test '='] test [comp_for]
*/
static int
validate_argument(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, argument)
&& ((nch == 1) || (nch == 2) || (nch == 3))
&& validate_test(CHILD(tree, 0)));
if (res && (nch == 2))
res = validate_comp_for(CHILD(tree, 1));
else if (res && (nch == 3))
res = (validate_equal(CHILD(tree, 1))
&& validate_test(CHILD(tree, 2)));
return (res);
}
/* trailer:
*
* '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
*/
static int
validate_trailer(node *tree)
{
int nch = NCH(tree);
int res = validate_ntype(tree, trailer) && ((nch == 2) || (nch == 3));
if (res) {
switch (TYPE(CHILD(tree, 0))) {
case LPAR:
res = validate_rparen(CHILD(tree, nch - 1));
if (res && (nch == 3))
res = validate_arglist(CHILD(tree, 1));
break;
case LSQB:
res = (validate_numnodes(tree, 3, "trailer")
&& validate_subscriptlist(CHILD(tree, 1))
&& validate_ntype(CHILD(tree, 2), RSQB));
break;
case DOT:
res = (validate_numnodes(tree, 2, "trailer")
&& validate_ntype(CHILD(tree, 1), NAME));
break;
default:
res = 0;
break;
}
}
else {
(void) validate_numnodes(tree, 2, "trailer");
}
return (res);
}
/* subscriptlist:
*
* subscript (',' subscript)* [',']
*/
static int
validate_subscriptlist(node *tree)
{
return (validate_repeating_list(tree, subscriptlist,
validate_subscript, "subscriptlist"));
}
/* subscript:
*
* '.' '.' '.' | test | [test] ':' [test] [sliceop]
*/
static int
validate_subscript(node *tree)
{
int offset = 0;
int nch = NCH(tree);
int res = validate_ntype(tree, subscript) && (nch >= 1) && (nch <= 4);
if (!res) {
if (!PyErr_Occurred())
err_string("invalid number of arguments for subscript node");
return (0);
}
if (TYPE(CHILD(tree, 0)) == DOT)
/* take care of ('.' '.' '.') possibility */
return (validate_numnodes(tree, 3, "subscript")
&& validate_dot(CHILD(tree, 0))
&& validate_dot(CHILD(tree, 1))
&& validate_dot(CHILD(tree, 2)));
if (nch == 1) {
if (TYPE(CHILD(tree, 0)) == test)
res = validate_test(CHILD(tree, 0));
else
res = validate_colon(CHILD(tree, 0));
return (res);
}
/* Must be [test] ':' [test] [sliceop],
* but at least one of the optional components will
* be present, but we don't know which yet.
*/
if ((TYPE(CHILD(tree, 0)) != COLON) || (nch == 4)) {
res = validate_test(CHILD(tree, 0));
offset = 1;
}
if (res)
res = validate_colon(CHILD(tree, offset));
if (res) {
int rem = nch - ++offset;
if (rem) {
if (TYPE(CHILD(tree, offset)) == test) {
res = validate_test(CHILD(tree, offset));
++offset;
--rem;
}
if (res && rem)
res = validate_sliceop(CHILD(tree, offset));
}
}
return (res);
}
static int
validate_sliceop(node *tree)
{
int nch = NCH(tree);
int res = ((nch == 1) || validate_numnodes(tree, 2, "sliceop"))
&& validate_ntype(tree, sliceop);
if (!res && !PyErr_Occurred()) {
res = validate_numnodes(tree, 1, "sliceop");
}
if (res)
res = validate_colon(CHILD(tree, 0));
if (res && (nch == 2))
res = validate_test(CHILD(tree, 1));
return (res);
}
static int
validate_exprlist(node *tree)
{
return (validate_repeating_list(tree, exprlist,
validate_star_expr, "exprlist"));
}
static int
validate_dictorsetmaker(node *tree)
{
int nch = NCH(tree);
int res = (validate_ntype(tree, dictorsetmaker)
&& (nch >= 3)
&& validate_test(CHILD(tree, 0))
&& validate_colon(CHILD(tree, 1))
&& validate_test(CHILD(tree, 2)));
if (res && ((nch % 4) == 0))
res = validate_comma(CHILD(tree, --nch));
else if (res)
res = ((nch % 4) == 3);
if (res && (nch > 3)) {
int pos = 3;
/* ( ',' test ':' test )* */
while (res && (pos < nch)) {
res = (validate_comma(CHILD(tree, pos))
&& validate_test(CHILD(tree, pos + 1))
&& validate_colon(CHILD(tree, pos + 2))
&& validate_test(CHILD(tree, pos + 3)));
pos += 4;
}
}
return (res);
}
static int
validate_eval_input(node *tree)
{
int pos;
int nch = NCH(tree);
int res = (validate_ntype(tree, eval_input)
&& (nch >= 2)
&& validate_testlist(CHILD(tree, 0))
&& validate_ntype(CHILD(tree, nch - 1), ENDMARKER));
for (pos = 1; res && (pos < (nch - 1)); ++pos)
res = validate_ntype(CHILD(tree, pos), NEWLINE);
return (res);
}
static int
validate_node(node *tree)
{
int nch = 0; /* num. children on current node */
int res = 1; /* result value */
node* next = 0; /* node to process after this one */
while (res && (tree != 0)) {
nch = NCH(tree);
next = 0;
switch (TYPE(tree)) {
/*
* Definition nodes.
*/
case funcdef:
res = validate_funcdef(tree);
break;
case classdef:
res = validate_class(tree);
break;
case decorated:
res = validate_decorated(tree);
break;
/*
* "Trivial" parse tree nodes.
* (Why did I call these trivial?)
*/
case stmt:
res = validate_stmt(tree);
break;
case small_stmt:
/*
* expr_stmt | del_stmt | pass_stmt | flow_stmt
* | import_stmt | global_stmt | assert_stmt
*/
res = validate_small_stmt(tree);
break;
case flow_stmt:
res = (validate_numnodes(tree, 1, "flow_stmt")
&& ((TYPE(CHILD(tree, 0)) == break_stmt)
|| (TYPE(CHILD(tree, 0)) == continue_stmt)
|| (TYPE(CHILD(tree, 0)) == yield_stmt)
|| (TYPE(CHILD(tree, 0)) == return_stmt)
|| (TYPE(CHILD(tree, 0)) == raise_stmt)));
if (res)
next = CHILD(tree, 0);
else if (nch == 1)
err_string("illegal flow_stmt type");
break;
case yield_stmt:
res = validate_yield_stmt(tree);
break;
/*
* Compound statements.
*/
case simple_stmt:
res = validate_simple_stmt(tree);
break;
case compound_stmt:
res = validate_compound_stmt(tree);
break;
/*
* Fundamental statements.
*/
case expr_stmt:
res = validate_expr_stmt(tree);
break;
case del_stmt:
res = validate_del_stmt(tree);
break;
case pass_stmt:
res = (validate_numnodes(tree, 1, "pass")
&& validate_name(CHILD(tree, 0), "pass"));
break;
case break_stmt:
res = (validate_numnodes(tree, 1, "break")
&& validate_name(CHILD(tree, 0), "break"));
break;
case continue_stmt:
res = (validate_numnodes(tree, 1, "continue")
&& validate_name(CHILD(tree, 0), "continue"));
break;
case return_stmt:
res = validate_return_stmt(tree);
break;
case raise_stmt:
res = validate_raise_stmt(tree);
break;
case import_stmt:
res = validate_import_stmt(tree);
break;
case import_name:
res = validate_import_name(tree);
break;
case import_from:
res = validate_import_from(tree);
break;
case global_stmt:
res = validate_global_stmt(tree);
break;
case assert_stmt:
res = validate_assert_stmt(tree);
break;
case if_stmt:
res = validate_if(tree);
break;
case while_stmt:
res = validate_while(tree);
break;
case for_stmt:
res = validate_for(tree);
break;
case try_stmt:
res = validate_try(tree);
break;
case suite:
res = validate_suite(tree);
break;
/*
* Expression nodes.
*/
case testlist:
res = validate_testlist(tree);
break;
case yield_expr:
res = validate_yield_expr(tree);
break;
case testlist1:
res = validate_testlist1(tree);
break;
case test:
res = validate_test(tree);
break;
case and_test:
res = validate_and_test(tree);
break;
case not_test:
res = validate_not_test(tree);
break;
case comparison:
res = validate_comparison(tree);
break;
case exprlist:
res = validate_exprlist(tree);
break;
case comp_op:
res = validate_comp_op(tree);
break;
case expr:
res = validate_expr(tree);
break;
case xor_expr:
res = validate_xor_expr(tree);
break;
case and_expr:
res = validate_and_expr(tree);
break;
case shift_expr:
res = validate_shift_expr(tree);
break;
case arith_expr:
res = validate_arith_expr(tree);
break;
case term:
res = validate_term(tree);
break;
case factor:
res = validate_factor(tree);
break;
case power:
res = validate_power(tree);
break;
case atom:
res = validate_atom(tree);
break;
default:
/* Hopefully never reached! */
err_string("unrecognized node type");
res = 0;
break;
}
tree = next;
}
return (res);
}
static int
validate_expr_tree(node *tree)
{
int res = validate_eval_input(tree);
if (!res && !PyErr_Occurred())
err_string("could not validate expression tuple");
return (res);
}
/* file_input:
* (NEWLINE | stmt)* ENDMARKER
*/
static int
validate_file_input(node *tree)
{
int j;
int nch = NCH(tree) - 1;
int res = ((nch >= 0)
&& validate_ntype(CHILD(tree, nch), ENDMARKER));
for (j = 0; res && (j < nch); ++j) {
if (TYPE(CHILD(tree, j)) == stmt)
res = validate_stmt(CHILD(tree, j));
else
res = validate_newline(CHILD(tree, j));
}
/* This stays in to prevent any internal failures from getting to the
* user. Hopefully, this won't be needed. If a user reports getting
* this, we have some debugging to do.
*/
if (!res && !PyErr_Occurred())
err_string("VALIDATION FAILURE: report this to the maintainer!");
return (res);
}
static int
validate_encoding_decl(node *tree)
{
int nch = NCH(tree);
int res = ((nch == 1)
&& validate_file_input(CHILD(tree, 0)));
if (!res && !PyErr_Occurred())
err_string("Error Parsing encoding_decl");
return res;
}
static PyObject*
pickle_constructor = NULL;
static PyObject*
parser__pickler(PyObject *self, PyObject *args)
{
NOTE(ARGUNUSED(self))
PyObject *result = NULL;
PyObject *st = NULL;
PyObject *empty_dict = NULL;
if (PyArg_ParseTuple(args, "O!:_pickler", &PyST_Type, &st)) {
PyObject *newargs;
PyObject *tuple;
if ((empty_dict = PyDict_New()) == NULL)
goto finally;
if ((newargs = Py_BuildValue("Oi", st, 1)) == NULL)
goto finally;
tuple = parser_st2tuple((PyST_Object*)NULL, newargs, empty_dict);
if (tuple != NULL) {
result = Py_BuildValue("O(O)", pickle_constructor, tuple);
Py_DECREF(tuple);
}
Py_DECREF(empty_dict);
Py_DECREF(newargs);
}
finally:
Py_XDECREF(empty_dict);
return (result);
}
/* Functions exported by this module. Most of this should probably
* be converted into an ST object with methods, but that is better
* done directly in Python, allowing subclasses to be created directly.
* We'd really have to write a wrapper around it all anyway to allow
* inheritance.
*/
static PyMethodDef parser_functions[] = {
{"ast2tuple", (PyCFunction)parser_st2tuple, PUBLIC_METHOD_TYPE,
PyDoc_STR("Creates a tuple-tree representation of an ST.")},
{"ast2list", (PyCFunction)parser_st2list, PUBLIC_METHOD_TYPE,
PyDoc_STR("Creates a list-tree representation of an ST.")},
{"compileast", (PyCFunction)parser_compilest, PUBLIC_METHOD_TYPE,
PyDoc_STR("Compiles an ST object into a code object.")},
{"compilest", (PyCFunction)parser_compilest, PUBLIC_METHOD_TYPE,
PyDoc_STR("Compiles an ST object into a code object.")},
{"expr", (PyCFunction)parser_expr, PUBLIC_METHOD_TYPE,
PyDoc_STR("Creates an ST object from an expression.")},
{"isexpr", (PyCFunction)parser_isexpr, PUBLIC_METHOD_TYPE,
PyDoc_STR("Determines if an ST object was created from an expression.")},
{"issuite", (PyCFunction)parser_issuite, PUBLIC_METHOD_TYPE,
PyDoc_STR("Determines if an ST object was created from a suite.")},
{"suite", (PyCFunction)parser_suite, PUBLIC_METHOD_TYPE,
PyDoc_STR("Creates an ST object from a suite.")},
{"sequence2ast", (PyCFunction)parser_tuple2st, PUBLIC_METHOD_TYPE,
PyDoc_STR("Creates an ST object from a tree representation.")},
{"sequence2st", (PyCFunction)parser_tuple2st, PUBLIC_METHOD_TYPE,
PyDoc_STR("Creates an ST object from a tree representation.")},
{"st2tuple", (PyCFunction)parser_st2tuple, PUBLIC_METHOD_TYPE,
PyDoc_STR("Creates a tuple-tree representation of an ST.")},
{"st2list", (PyCFunction)parser_st2list, PUBLIC_METHOD_TYPE,
PyDoc_STR("Creates a list-tree representation of an ST.")},
{"tuple2ast", (PyCFunction)parser_tuple2st, PUBLIC_METHOD_TYPE,
PyDoc_STR("Creates an ST object from a tree representation.")},
{"tuple2st", (PyCFunction)parser_tuple2st, PUBLIC_METHOD_TYPE,
PyDoc_STR("Creates an ST object from a tree representation.")},
/* private stuff: support pickle module */
{"_pickler", (PyCFunction)parser__pickler, METH_VARARGS,
PyDoc_STR("Returns the pickle magic to allow ST objects to be pickled.")},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC initparser(void); /* supply a prototype */
PyMODINIT_FUNC
initparser(void)
{
PyObject *module, *copyreg;
PyST_Type.ob_type = &PyType_Type;
module = Py_InitModule("parser", parser_functions);
if (module == NULL)
return;
if (parser_error == 0)
parser_error = PyErr_NewException("parser.ParserError", NULL, NULL);
if (parser_error == 0)
/* caller will check PyErr_Occurred() */
return;
/* CAUTION: The code next used to skip bumping the refcount on
* parser_error. That's a disaster if initparser() gets called more
* than once. By incref'ing, we ensure that each module dict that
* gets created owns its reference to the shared parser_error object,
* and the file static parser_error vrbl owns a reference too.
*/
Py_INCREF(parser_error);
if (PyModule_AddObject(module, "ParserError", parser_error) != 0)
return;
Py_INCREF(&PyST_Type);
PyModule_AddObject(module, "ASTType", (PyObject*)&PyST_Type);
Py_INCREF(&PyST_Type);
PyModule_AddObject(module, "STType", (PyObject*)&PyST_Type);
PyModule_AddStringConstant(module, "__copyright__",
parser_copyright_string);
PyModule_AddStringConstant(module, "__doc__",
parser_doc_string);
PyModule_AddStringConstant(module, "__version__",
parser_version_string);
/* Register to support pickling.
* If this fails, the import of this module will fail because an
* exception will be raised here; should we clear the exception?
*/
copyreg = PyImport_ImportModule("copy_reg");
if (copyreg != NULL) {
PyObject *func, *pickler;
func = PyObject_GetAttrString(copyreg, "pickle");
pickle_constructor = PyObject_GetAttrString(module, "sequence2st");
pickler = PyObject_GetAttrString(module, "_pickler");
Py_XINCREF(pickle_constructor);
if ((func != NULL) && (pickle_constructor != NULL)
&& (pickler != NULL)) {
PyObject *res;
res = PyObject_CallFunctionObjArgs(func, &PyST_Type, pickler,
pickle_constructor, NULL);
Py_XDECREF(res);
}
Py_XDECREF(func);
Py_XDECREF(pickle_constructor);
Py_XDECREF(pickler);
Py_DECREF(copyreg);
}
}