bpo-40334: PEP 617 implementation: New PEG parser for CPython (GH-19503)

Co-authored-by: Guido van Rossum <guido@python.org>
Co-authored-by: Lysandros Nikolaou <lisandrosnik@gmail.com>
diff --git a/Modules/Setup b/Modules/Setup
index 6f0374a..6bf1424 100644
--- a/Modules/Setup
+++ b/Modules/Setup
@@ -134,6 +134,9 @@
 # can call _PyTraceMalloc_NewReference().
 _tracemalloc _tracemalloc.c hashtable.c
 
+# PEG-based parser module -- slated to be *the* parser
+_peg_parser _peg_parser.c
+
 # The rest of the modules listed in this file are all commented out by
 # default.  Usually they can be detected and built as dynamically
 # loaded modules by the new setup.py script added in Python 2.1.  If
diff --git a/Modules/_peg_parser.c b/Modules/_peg_parser.c
new file mode 100644
index 0000000..0a84edc
--- /dev/null
+++ b/Modules/_peg_parser.c
@@ -0,0 +1,107 @@
+#include <Python.h>
+#include <pegen_interface.h>
+
+PyObject *
+_Py_parse_file(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    static char *keywords[] = {"file", "mode", NULL};
+    char *filename;
+    char *mode_str = "exec";
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s", keywords, &filename, &mode_str)) {
+        return NULL;
+    }
+
+    int mode;
+    if (strcmp(mode_str, "exec") == 0) {
+        mode = Py_file_input;
+    }
+    else if (strcmp(mode_str, "single") == 0) {
+        mode = Py_single_input;
+    }
+    else {
+        return PyErr_Format(PyExc_ValueError, "mode must be either 'exec' or 'single'");
+    }
+
+    PyArena *arena = PyArena_New();
+    if (arena == NULL) {
+        return NULL;
+    }
+
+    PyObject *result = NULL;
+
+    mod_ty res = PyPegen_ASTFromFile(filename, mode, arena);
+    if (res == NULL) {
+        goto error;
+    }
+    result = PyAST_mod2obj(res);
+
+error:
+    PyArena_Free(arena);
+    return result;
+}
+
+PyObject *
+_Py_parse_string(PyObject *self, PyObject *args, PyObject *kwds)
+{
+    static char *keywords[] = {"string", "mode", NULL};
+    char *the_string;
+    char *mode_str = "exec";
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s", keywords, &the_string, &mode_str)) {
+        return NULL;
+    }
+
+    int mode;
+    if (strcmp(mode_str, "exec") == 0) {
+        mode = Py_file_input;
+    }
+    else if (strcmp(mode_str, "eval") == 0) {
+        mode = Py_eval_input;
+    }
+    else if (strcmp(mode_str, "single") == 0) {
+        mode = Py_single_input;
+    }
+    else {
+        return PyErr_Format(PyExc_ValueError, "mode must be either 'exec' or 'eval' or 'single'");
+    }
+
+    PyArena *arena = PyArena_New();
+    if (arena == NULL) {
+        return NULL;
+    }
+
+    PyObject *result = NULL;
+
+    PyCompilerFlags flags = _PyCompilerFlags_INIT;
+    flags.cf_flags = PyCF_IGNORE_COOKIE;
+
+    mod_ty res = PyPegen_ASTFromString(the_string, mode, &flags, arena);
+    if (res == NULL) {
+        goto error;
+    }
+    result = PyAST_mod2obj(res);
+
+error:
+    PyArena_Free(arena);
+    return result;
+}
+
+static PyMethodDef ParseMethods[] = {
+    {"parse_file", (PyCFunction)(void (*)(void))_Py_parse_file, METH_VARARGS|METH_KEYWORDS, "Parse a file."},
+    {"parse_string", (PyCFunction)(void (*)(void))_Py_parse_string, METH_VARARGS|METH_KEYWORDS,"Parse a string."},
+    {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+static struct PyModuleDef parsemodule = {
+    PyModuleDef_HEAD_INIT,
+    .m_name = "peg_parser",
+    .m_doc = "A parser.",
+    .m_methods = ParseMethods,
+};
+
+PyMODINIT_FUNC
+PyInit__peg_parser(void)
+{
+    return PyModule_Create(&parsemodule);
+}