diff options
author | Pablo Galindo <Pablogsal@gmail.com> | 2020-06-11 16:30:46 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-06-11 16:30:46 (GMT) |
commit | 1ed83adb0e95305af858bd41af531e487f54fee7 (patch) | |
tree | 5b05876e1800975fd2f0b8021544423f9fd9822a /Modules/parsermodule.c | |
parent | 311110abcd8ab648dbf1803e36a8ba5d93fa019b (diff) | |
download | cpython-1ed83adb0e95305af858bd41af531e487f54fee7.zip cpython-1ed83adb0e95305af858bd41af531e487f54fee7.tar.gz cpython-1ed83adb0e95305af858bd41af531e487f54fee7.tar.bz2 |
bpo-40939: Remove the old parser (GH-20768)
This commit removes the old parser, the deprecated parser module, the old parser compatibility flags and environment variables and all associated support code and documentation.
Diffstat (limited to 'Modules/parsermodule.c')
-rw-r--r-- | Modules/parsermodule.c | 1222 |
1 files changed, 0 insertions, 1222 deletions
diff --git a/Modules/parsermodule.c b/Modules/parsermodule.c deleted file mode 100644 index 24b0ffb..0000000 --- a/Modules/parsermodule.c +++ /dev/null @@ -1,1222 +0,0 @@ -/* 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 "Python-ast.h" /* mod_ty */ -#undef Yield /* undefine macro conflicting with <winbase.h> */ -#include "ast.h" -#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 "grammar.h" -#include "parsetok.h" - -extern grammar _PyParser_Grammar; /* From graminit.c */ - -#ifdef lint -#include <note.h> -#else -#define NOTE(x) -#endif - -/* String constants used to initialize module attributes. - * - */ -static const 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 const 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? */ -{ - PyObject *result = NULL, *w; - - if (n == NULL) { - Py_RETURN_NONE; - } - - if (ISNONTERMINAL(TYPE(n))) { - int i; - - result = mkseq(1 + NCH(n) + (TYPE(n) == encoding_decl)); - if (result == NULL) - goto error; - - w = PyLong_FromLong(TYPE(n)); - if (w == NULL) - goto error; - (void) addelem(result, 0, w); - - for (i = 0; i < NCH(n); i++) { - w = node2tuple(CHILD(n, i), mkseq, addelem, lineno, col_offset); - if (w == NULL) - goto error; - (void) addelem(result, i+1, w); - } - - if (TYPE(n) == encoding_decl) { - w = PyUnicode_FromString(STR(n)); - if (w == NULL) - goto error; - (void) addelem(result, i+1, w); - } - } - else if (ISTERMINAL(TYPE(n))) { - result = mkseq(2 + lineno + col_offset); - if (result == NULL) - goto error; - - w = PyLong_FromLong(TYPE(n)); - if (w == NULL) - goto error; - (void) addelem(result, 0, w); - - w = PyUnicode_FromString(STR(n)); - if (w == NULL) - goto error; - (void) addelem(result, 1, w); - - if (lineno) { - w = PyLong_FromLong(n->n_lineno); - if (w == NULL) - goto error; - (void) addelem(result, 2, w); - } - - if (col_offset) { - w = PyLong_FromLong(n->n_col_offset); - if (w == NULL) - goto error; - (void) addelem(result, 2 + lineno, w); - } - } - else { - PyErr_SetString(PyExc_SystemError, - "unrecognized parse tree node type"); - return ((PyObject*) NULL); - } - return result; - -error: - Py_XDECREF(result); - return 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 ? */ - PyCompilerFlags st_flags; /* Parser and compiler flags */ -} PyST_Object; - - -static void parser_free(PyST_Object *st); -static PyObject* parser_sizeof(PyST_Object *, void *); -static PyObject* parser_richcompare(PyObject *left, PyObject *right, int op); -static PyObject* parser_compilest(PyST_Object *, PyObject *, PyObject *); -static PyObject* parser_isexpr(PyST_Object *, PyObject *, PyObject *); -static PyObject* parser_issuite(PyST_Object *, PyObject *, PyObject *); -static PyObject* parser_st2list(PyST_Object *, PyObject *, PyObject *); -static PyObject* parser_st2tuple(PyST_Object *, PyObject *, PyObject *); - -#define PUBLIC_METHOD_TYPE (METH_VARARGS|METH_KEYWORDS) - -static PyMethodDef parser_methods[] = { - {"compile", (PyCFunction)(void(*)(void))parser_compilest, PUBLIC_METHOD_TYPE, - PyDoc_STR("Compile this ST object into a code object.")}, - {"isexpr", (PyCFunction)(void(*)(void))parser_isexpr, PUBLIC_METHOD_TYPE, - PyDoc_STR("Determines if this ST object was created from an expression.")}, - {"issuite", (PyCFunction)(void(*)(void))parser_issuite, PUBLIC_METHOD_TYPE, - PyDoc_STR("Determines if this ST object was created from a suite.")}, - {"tolist", (PyCFunction)(void(*)(void))parser_st2list, PUBLIC_METHOD_TYPE, - PyDoc_STR("Creates a list-tree representation of this ST.")}, - {"totuple", (PyCFunction)(void(*)(void))parser_st2tuple, PUBLIC_METHOD_TYPE, - PyDoc_STR("Creates a tuple-tree representation of this ST.")}, - {"__sizeof__", (PyCFunction)parser_sizeof, METH_NOARGS, - PyDoc_STR("Returns size in memory, in bytes.")}, - {NULL, NULL, 0, NULL} -}; - -static -PyTypeObject PyST_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - "parser.st", /* tp_name */ - (int) sizeof(PyST_Object), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)parser_free, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 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.", - 0, /* tp_traverse */ - 0, /* tp_clear */ - parser_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - parser_methods, /* tp_methods */ -}; /* PyST_Type */ - - -/* PyST_Type isn't subclassable, so just check ob_type */ -#define PyST_Object_Check(v) Py_IS_TYPE(v, &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); -} - -/* parser_richcompare(PyObject* left, PyObject* right, int op) - * - * 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 PyObject * -parser_richcompare(PyObject *left, PyObject *right, int op) -{ - int result; - - /* neither argument should be NULL, unless something's gone wrong */ - if (left == NULL || right == NULL) { - PyErr_BadInternalCall(); - return NULL; - } - - /* both arguments should be instances of PyST_Object */ - if (!PyST_Object_Check(left) || !PyST_Object_Check(right)) { - Py_RETURN_NOTIMPLEMENTED; - } - - if (left == right) - /* if arguments are identical, they're equal */ - result = 0; - else - result = parser_compare_nodes(((PyST_Object *)left)->st_node, - ((PyST_Object *)right)->st_node); - - Py_RETURN_RICHCOMPARE(result, 0, op); -} - -/* 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; - o->st_flags = _PyCompilerFlags_INIT; - } - 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); -} - -static PyObject * -parser_sizeof(PyST_Object *st, void *unused) -{ - Py_ssize_t res; - - res = _PyObject_SIZE(Py_TYPE(st)) + _PyNode_SizeOf(st->st_node); - return PyLong_FromSsize_t(res); -} - - -/* 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) -{ - int line_info = 0; - int col_info = 0; - PyObject *res = 0; - int ok; - - static char *keywords[] = {"st", "line_info", "col_info", NULL}; - - if (self == NULL || PyModule_Check(self)) { - ok = PyArg_ParseTupleAndKeywords(args, kw, "O!|pp:st2tuple", keywords, - &PyST_Type, &self, &line_info, - &col_info); - } - else - ok = PyArg_ParseTupleAndKeywords(args, kw, "|pp:totuple", &keywords[1], - &line_info, &col_info); - if (ok != 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, line_info, col_info); - } - 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) -{ - int line_info = 0; - int col_info = 0; - PyObject *res = 0; - int ok; - - static char *keywords[] = {"st", "line_info", "col_info", NULL}; - - if (self == NULL || PyModule_Check(self)) - ok = PyArg_ParseTupleAndKeywords(args, kw, "O!|pp:st2list", keywords, - &PyST_Type, &self, &line_info, - &col_info); - else - ok = PyArg_ParseTupleAndKeywords(args, kw, "|pp:tolist", &keywords[1], - &line_info, &col_info); - if (ok) { - /* - * 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, line_info, col_info); - } - 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 = NULL; - PyArena* arena = NULL; - mod_ty mod; - PyObject* filename = NULL; - int ok; - - static char *keywords[] = {"st", "filename", NULL}; - - if (self == NULL || PyModule_Check(self)) - ok = PyArg_ParseTupleAndKeywords(args, kw, "O!|O&:compilest", keywords, - &PyST_Type, &self, - PyUnicode_FSDecoder, &filename); - else - ok = PyArg_ParseTupleAndKeywords(args, kw, "|O&:compile", &keywords[1], - PyUnicode_FSDecoder, &filename); - if (!ok) - goto error; - - if (filename == NULL) { - filename = PyUnicode_FromString("<syntax-tree>"); - if (filename == NULL) - goto error; - } - - arena = PyArena_New(); - if (!arena) - goto error; - - mod = PyAST_FromNodeObject(self->st_node, &self->st_flags, - filename, arena); - if (!mod) - goto error; - - res = (PyObject *)PyAST_CompileObject(mod, filename, - &self->st_flags, -1, arena); -error: - Py_XDECREF(filename); - if (arena != NULL) - PyArena_Free(arena); - 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[] = {"st", NULL}; - - if (self == NULL || PyModule_Check(self)) - 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[] = {"st", NULL}; - - if (self == NULL || PyModule_Check(self)) - 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); -} - - -/* err_string(const char* message) - * - * Sets the error string for an exception of type ParserError. - * - */ -static void -err_string(const 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, const char *argspec, int type) -{ - char* string = 0; - PyObject* res = 0; - int flags = 0; - perrdetail err; - - static char *keywords[] = {"source", NULL}; - - if (PyArg_ParseTupleAndKeywords(args, kw, argspec, keywords, &string)) { - node* n = PyParser_ParseStringFlagsFilenameEx(string, NULL, - &_PyParser_Grammar, - (type == PyST_EXPR) - ? eval_input : file_input, - &err, &flags); - - if (n) { - res = parser_newstobject(n, type); - if (res) { - ((PyST_Object *)res)->st_flags.cf_flags = flags & PyCF_MASK; - ((PyST_Object *)res)->st_flags.cf_feature_version = PY_MINOR_VERSION; - } - } - else { - PyParser_SetError(&err); - } - PyParser_ClearError(&err); - } - 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_node(node *tree) -{ - int type = TYPE(tree); - int nch = NCH(tree); - state *dfa_state; - int pos, arc; - - assert(ISNONTERMINAL(type)); - type -= NT_OFFSET; - if (type >= _PyParser_Grammar.g_ndfas) { - PyErr_Format(parser_error, "Unrecognized node type %d.", TYPE(tree)); - return 0; - } - const dfa *nt_dfa = &_PyParser_Grammar.g_dfa[type]; - REQ(tree, nt_dfa->d_type); - - /* Run the DFA for this nonterminal. */ - dfa_state = nt_dfa->d_state; - for (pos = 0; pos < nch; ++pos) { - node *ch = CHILD(tree, pos); - int ch_type = TYPE(ch); - if ((ch_type >= NT_OFFSET + _PyParser_Grammar.g_ndfas) - || (ISTERMINAL(ch_type) && (ch_type >= N_TOKENS)) - || (ch_type < 0) - ) { - PyErr_Format(parser_error, "Unrecognized node type %d.", ch_type); - return 0; - } - if (ch_type == suite && TYPE(tree) == funcdef) { - /* This is the opposite hack of what we do in parser.c - (search for func_body_suite), except we don't ever - support type comments here. */ - ch_type = func_body_suite; - } - for (arc = 0; arc < dfa_state->s_narcs; ++arc) { - short a_label = dfa_state->s_arc[arc].a_lbl; - assert(a_label < _PyParser_Grammar.g_ll.ll_nlabels); - - const char *label_str = _PyParser_Grammar.g_ll.ll_label[a_label].lb_str; - if ((_PyParser_Grammar.g_ll.ll_label[a_label].lb_type == ch_type) - && ((ch->n_str == NULL) || (label_str == NULL) - || (strcmp(ch->n_str, label_str) == 0)) - ) { - /* The child is acceptable; if non-terminal, validate it recursively. */ - if (ISNONTERMINAL(ch_type) && !validate_node(ch)) - return 0; - - /* Update the state, and move on to the next child. */ - dfa_state = &nt_dfa->d_state[dfa_state->s_arc[arc].a_arrow]; - goto arc_found; - } - } - /* What would this state have accepted? */ - { - short a_label = dfa_state->s_arc->a_lbl; - if (!a_label) /* Wouldn't accept any more children */ - goto illegal_num_children; - - int next_type = _PyParser_Grammar.g_ll.ll_label[a_label].lb_type; - const char *expected_str = _PyParser_Grammar.g_ll.ll_label[a_label].lb_str; - - if (ISNONTERMINAL(next_type)) { - PyErr_Format(parser_error, "Expected %s, got %s.", - _PyParser_Grammar.g_dfa[next_type - NT_OFFSET].d_name, - ISTERMINAL(ch_type) ? _PyParser_TokenNames[ch_type] : - _PyParser_Grammar.g_dfa[ch_type - NT_OFFSET].d_name); - } - else if (expected_str != NULL) { - PyErr_Format(parser_error, "Illegal terminal: expected '%s'.", - expected_str); - } - else { - PyErr_Format(parser_error, "Illegal terminal: expected %s.", - _PyParser_TokenNames[next_type]); - } - return 0; - } - -arc_found: - continue; - } - /* Are we in a final state? If so, return 1 for successful validation. */ - for (arc = 0; arc < dfa_state->s_narcs; ++arc) { - if (!dfa_state->s_arc[arc].a_lbl) { - return 1; - } - } - -illegal_num_children: - PyErr_Format(parser_error, - "Illegal number of children for %s node.", nt_dfa->d_name); - return 0; -} - -/* 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 recursive validate_node() routine defined above. - * - * 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) { - node *validation_root = NULL; - int tree_type = 0; - switch (TYPE(tree)) { - case eval_input: - /* Might be an eval form. */ - tree_type = PyST_EXPR; - validation_root = tree; - break; - case encoding_decl: - /* This looks like an encoding_decl so far. */ - if (NCH(tree) == 1) { - tree_type = PyST_SUITE; - validation_root = CHILD(tree, 0); - } - else { - err_string("Error Parsing encoding_decl"); - } - break; - case file_input: - /* This looks like an exec form so far. */ - tree_type = PyST_SUITE; - validation_root = tree; - break; - default: - /* This is a fragment, at best. */ - err_string("parse tree does not use a valid start symbol"); - } - - if (validation_root != NULL && validate_node(validation_root)) - st = parser_newstobject(tree, tree_type); - else - PyNode_Free(tree); - } - /* Make sure we raise 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; - - if (len < 0) { - return NULL; - } - for (i = 1; i < len; ++i) { - /* elem must always be a sequence, however simple */ - PyObject* elem = PySequence_GetItem(tuple, i); - int ok = elem != NULL; - int 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 = PyLong_Check(temp); - if (ok) { - type = _PyLong_AsInt(temp); - if (type == -1 && PyErr_Occurred()) { - Py_DECREF(temp); - Py_DECREF(elem); - return NULL; - } - } - 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 NULL; - } - if (ISTERMINAL(type)) { - Py_ssize_t len = PyObject_Size(elem); - PyObject *temp; - const char *temp_str; - - if ((len != 2) && (len != 3)) { - err_string("terminal nodes must have 2 or 3 entries"); - Py_DECREF(elem); - return NULL; - } - temp = PySequence_GetItem(elem, 1); - if (temp == NULL) { - Py_DECREF(elem); - return NULL; - } - if (!PyUnicode_Check(temp)) { - PyErr_Format(parser_error, - "second item in terminal node must be a string," - " found %s", - Py_TYPE(temp)->tp_name); - Py_DECREF(temp); - Py_DECREF(elem); - return NULL; - } - if (len == 3) { - PyObject *o = PySequence_GetItem(elem, 2); - if (o == NULL) { - Py_DECREF(temp); - Py_DECREF(elem); - return NULL; - } - if (PyLong_Check(o)) { - int num = _PyLong_AsInt(o); - if (num == -1 && PyErr_Occurred()) { - Py_DECREF(o); - Py_DECREF(temp); - Py_DECREF(elem); - return NULL; - } - *line_num = num; - } - else { - PyErr_Format(parser_error, - "third item in terminal node must be an" - " integer, found %s", - Py_TYPE(temp)->tp_name); - Py_DECREF(o); - Py_DECREF(temp); - Py_DECREF(elem); - return NULL; - } - Py_DECREF(o); - } - temp_str = PyUnicode_AsUTF8AndSize(temp, &len); - if (temp_str == NULL) { - Py_DECREF(temp); - Py_DECREF(elem); - return NULL; - } - strn = (char *)PyObject_MALLOC(len + 1); - if (strn == NULL) { - Py_DECREF(temp); - Py_DECREF(elem); - PyErr_NoMemory(); - return NULL; - } - (void) memcpy(strn, temp_str, len + 1); - Py_DECREF(temp); - } - else if (!ISNONTERMINAL(type)) { - /* - * It has to be one or the other; this is an error. - * Raise an exception. - */ - PyObject *err = Py_BuildValue("Os", elem, "unknown node type."); - PyErr_SetObject(parser_error, err); - Py_XDECREF(err); - Py_DECREF(elem); - return NULL; - } - err = PyNode_AddChild(root, type, strn, *line_num, 0, *line_num, 0); - if (err == E_NOMEM) { - Py_DECREF(elem); - PyObject_FREE(strn); - PyErr_NoMemory(); - return NULL; - } - if (err == E_OVERFLOW) { - Py_DECREF(elem); - 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_DECREF(elem); - return NULL; - } - } - else if (type == NEWLINE) { /* It's true: we increment the */ - ++(*line_num); /* line number *after* the newline! */ - } - Py_DECREF(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 = PyLong_AsLong(temp); - Py_XDECREF(temp); - if (ISTERMINAL(num)) { - /* - * The tuple is simple, but it doesn't start with a start symbol. - * Raise 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); - if (encoding == NULL) { - PyErr_SetString(parser_error, "missed encoding"); - return NULL; - } - if (!PyUnicode_Check(encoding)) { - PyErr_Format(parser_error, - "encoding must be a string, found %.200s", - Py_TYPE(encoding)->tp_name); - Py_DECREF(encoding); - return NULL; - } - /* tuple isn't borrowed anymore here, need to DECREF */ - tuple = PySequence_GetSlice(tuple, 0, 2); - if (tuple == NULL) { - Py_DECREF(encoding); - return NULL; - } - } - 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; - const char *temp; - temp = PyUnicode_AsUTF8AndSize(encoding, &len); - if (temp == NULL) { - PyNode_Free(res); - Py_DECREF(encoding); - Py_DECREF(tuple); - return NULL; - } - res->n_str = (char *)PyObject_MALLOC(len + 1); - if (res->n_str == NULL) { - PyNode_Free(res); - Py_DECREF(encoding); - Py_DECREF(tuple); - PyErr_NoMemory(); - return NULL; - } - (void) memcpy(res->n_str, temp, len + 1); - } - } - if (encoding != NULL) { - 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); -} - - -static PyObject* -pickle_constructor = NULL; - - -static PyObject* -parser__pickler(PyObject *self, PyObject *args) -{ - NOTE(ARGUNUSED(self)) - PyObject *result = NULL; - PyObject *st = NULL; - - if (PyArg_ParseTuple(args, "O!:_pickler", &PyST_Type, &st)) { - PyObject *newargs; - PyObject *tuple; - - if ((newargs = PyTuple_Pack(2, st, Py_True)) == NULL) - return NULL; - tuple = parser_st2tuple((PyST_Object*)NULL, newargs, NULL); - if (tuple != NULL) { - result = Py_BuildValue("O(O)", pickle_constructor, tuple); - Py_DECREF(tuple); - } - Py_DECREF(newargs); - } - - 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[] = { - {"compilest", (PyCFunction)(void(*)(void))parser_compilest, PUBLIC_METHOD_TYPE, - PyDoc_STR("Compiles an ST object into a code object.")}, - {"expr", (PyCFunction)(void(*)(void))parser_expr, PUBLIC_METHOD_TYPE, - PyDoc_STR("Creates an ST object from an expression.")}, - {"isexpr", (PyCFunction)(void(*)(void))parser_isexpr, PUBLIC_METHOD_TYPE, - PyDoc_STR("Determines if an ST object was created from an expression.")}, - {"issuite", (PyCFunction)(void(*)(void))parser_issuite, PUBLIC_METHOD_TYPE, - PyDoc_STR("Determines if an ST object was created from a suite.")}, - {"suite", (PyCFunction)(void(*)(void))parser_suite, PUBLIC_METHOD_TYPE, - PyDoc_STR("Creates an ST object from a suite.")}, - {"sequence2st", (PyCFunction)(void(*)(void))parser_tuple2st, PUBLIC_METHOD_TYPE, - PyDoc_STR("Creates an ST object from a tree representation.")}, - {"st2tuple", (PyCFunction)(void(*)(void))parser_st2tuple, PUBLIC_METHOD_TYPE, - PyDoc_STR("Creates a tuple-tree representation of an ST.")}, - {"st2list", (PyCFunction)(void(*)(void))parser_st2list, PUBLIC_METHOD_TYPE, - PyDoc_STR("Creates a list-tree representation of an ST.")}, - {"tuple2st", (PyCFunction)(void(*)(void))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} - }; - - - -static struct PyModuleDef parsermodule = { - PyModuleDef_HEAD_INIT, - "parser", - NULL, - -1, - parser_functions, - NULL, - NULL, - NULL, - NULL -}; - -PyMODINIT_FUNC PyInit_parser(void); /* supply a prototype */ - -PyMODINIT_FUNC -PyInit_parser(void) -{ - PyObject *module, *copyreg; - - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "The parser module is deprecated and will be removed " - "in future versions of Python", 7) != 0) { - return NULL; - } - - if (PyType_Ready(&PyST_Type) < 0) - return NULL; - module = PyModule_Create(&parsermodule); - if (module == NULL) - return NULL; - - if (parser_error == 0) - parser_error = PyErr_NewException("parser.ParserError", NULL, NULL); - - if (parser_error == 0) - return NULL; - /* CAUTION: The code next used to skip bumping the refcount on - * parser_error. That's a disaster if PyInit_parser() 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 NULL; - - 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_ImportModuleNoBlock("copyreg"); - if (copyreg != NULL) { - PyObject *func, *pickler; - _Py_IDENTIFIER(pickle); - _Py_IDENTIFIER(sequence2st); - _Py_IDENTIFIER(_pickler); - - func = _PyObject_GetAttrId(copyreg, &PyId_pickle); - pickle_constructor = _PyObject_GetAttrId(module, &PyId_sequence2st); - pickler = _PyObject_GetAttrId(module, &PyId__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); - } - return module; -} |