From 7dba3465d0d3b27b4105f0c96233fa7a655ab348 Mon Sep 17 00:00:00 2001 From: "John J. Camilleri" Date: Mon, 20 Sep 2021 23:42:50 +0200 Subject: [PATCH] Refactor modules in Python bindings. Start work on grammar-update functions, but without transactions. --- .gitignore | 4 +- src/runtime/python/expr.c | 2 +- src/runtime/python/{marshaller.c => ffi.c} | 20 +++- src/runtime/python/{marshaller.h => ffi.h} | 9 ++ src/runtime/python/pypgf.c | 55 +++++----- src/runtime/python/setup.py | 7 +- .../{test_suite.py => tests/test_basic.py} | 0 src/runtime/python/tests/test_transactions.py | 41 +++++++ src/runtime/python/transactions.c | 102 ++++++++++++++++++ src/runtime/python/transactions.h | 38 +++++++ 10 files changed, 243 insertions(+), 35 deletions(-) rename src/runtime/python/{marshaller.c => ffi.c} (95%) rename src/runtime/python/{marshaller.h => ffi.h} (67%) rename src/runtime/python/{test_suite.py => tests/test_basic.py} (100%) create mode 100644 src/runtime/python/tests/test_transactions.py create mode 100644 src/runtime/python/transactions.c create mode 100644 src/runtime/python/transactions.h diff --git a/.gitignore b/.gitignore index 8e9cac9a3..aedc1fd42 100644 --- a/.gitignore +++ b/.gitignore @@ -47,8 +47,8 @@ src/runtime/c/sg/.dirstamp src/runtime/c/stamp-h1 src/runtime/java/.libs/ src/runtime/python/build/ -src/runtime/python/__pycache__/ -src/runtime/python/.pytest_cache/ +src/runtime/python/**/__pycache__/ +src/runtime/python/**/.pytest_cache/ .cabal-sandbox cabal.sandbox.config .stack-work diff --git a/src/runtime/python/expr.c b/src/runtime/python/expr.c index 2a90b9e18..b36a8b80d 100644 --- a/src/runtime/python/expr.c +++ b/src/runtime/python/expr.c @@ -4,7 +4,7 @@ #include #include "./expr.h" -#include "./marshaller.h" +#include "./ffi.h" // ---------------------------------------------------------------------------- diff --git a/src/runtime/python/marshaller.c b/src/runtime/python/ffi.c similarity index 95% rename from src/runtime/python/marshaller.c rename to src/runtime/python/ffi.c index 562d7853f..74e95fdf6 100644 --- a/src/runtime/python/marshaller.c +++ b/src/runtime/python/ffi.c @@ -5,7 +5,25 @@ #include #include "./expr.h" -#include "./marshaller.h" +#include "./ffi.h" + +/* static */ +PyObject *PGFError; + +/* static */ +PgfExnType handleError(PgfExn err) +{ + if (err.type == PGF_EXN_SYSTEM_ERROR) { + errno = err.code; + PyErr_SetFromErrnoWithFilename(PyExc_IOError, err.msg); + } else if (err.type == PGF_EXN_PGF_ERROR) { + PyErr_SetString(PGFError, err.msg); + free((char*) err.msg); + } else if (err.type == PGF_EXN_OTHER_ERROR) { + PyErr_SetString(PGFError, "an unknown error occured"); + } + return err.type; +} // ---------------------------------------------------------------------------- diff --git a/src/runtime/python/marshaller.h b/src/runtime/python/ffi.h similarity index 67% rename from src/runtime/python/marshaller.h rename to src/runtime/python/ffi.h index 032b0f90b..c8b140c02 100644 --- a/src/runtime/python/marshaller.h +++ b/src/runtime/python/ffi.h @@ -6,6 +6,15 @@ #include +typedef struct { + PyObject_HEAD + PgfDB *db; + PgfRevision revision; +} PGFObject; + +extern PyObject *PGFError; +PgfExnType handleError(PgfExn err); + PgfText *PyUnicode_AsPgfText(PyObject *pystr); PyObject *PyUnicode_FromPgfText(PgfText *text); diff --git a/src/runtime/python/pypgf.c b/src/runtime/python/pypgf.c index 3fe02a86c..b53820a7d 100644 --- a/src/runtime/python/pypgf.c +++ b/src/runtime/python/pypgf.c @@ -4,35 +4,13 @@ #include #include "./expr.h" -#include "./marshaller.h" - -static PyObject *PGFError; - -static -PgfExnType handleError(PgfExn err) -{ - if (err.type == PGF_EXN_SYSTEM_ERROR) { - errno = err.code; - PyErr_SetFromErrnoWithFilename(PyExc_IOError, err.msg); - } else if (err.type == PGF_EXN_PGF_ERROR) { - PyErr_SetString(PGFError, err.msg); - free((char*) err.msg); - } else if (err.type == PGF_EXN_OTHER_ERROR) { - PyErr_SetString(PGFError, "an unknown error occured"); - } - return err.type; -} +#include "./ffi.h" +#include "./transactions.h" // static PyObject* ParseError; // static PyObject* TypeError; -typedef struct { - PyObject_HEAD - PgfDB *db; - PgfRevision revision; -} PGFObject; - // typedef struct IterObject { // PyObject_HEAD // PyObject* source; @@ -1753,9 +1731,9 @@ typedef struct { // gu_buf_push((GuBuf*) clo->collection, PgfConcr*, concr); // } -static PyObject* -PGF_str(PGFObject *self) -{ +// static PyObject* +// PGF_str(PGFObject *self) +// { // GuPool* tmp_pool = gu_local_pool(); // // GuExn* err = gu_exn(tmp_pool); @@ -1776,8 +1754,8 @@ PGF_str(PGFObject *self) // // gu_pool_free(tmp_pool); // return pystr; - return NULL; -} +// return NULL; +// } static PyObject* PGF_getAbstractName(PGFObject *self, void *closure) @@ -2410,6 +2388,21 @@ static PyMethodDef PGF_methods[] = { {"functionIsConstructor", (PyCFunction)PGF_functionIsConstructor, METH_VARARGS, "Checks whether a function is a constructor" }, + + + {"createFunction", (PyCFunction)PGF_createFunction, METH_VARARGS, + "Create function" + }, + {"dropFunction", (PyCFunction)PGF_dropFunction, METH_VARARGS, + "Drop function" + }, + {"createCategory", (PyCFunction)PGF_createCategory, METH_VARARGS, + "Create category" + }, + {"dropCategory", (PyCFunction)PGF_dropCategory, METH_VARARGS, + "Drop category" + }, + // {"generateAll", (PyCFunction)PGF_generateAll, METH_VARARGS | METH_KEYWORDS, // "Generates abstract syntax trees of given category in decreasing probability order" // }, @@ -2454,7 +2447,7 @@ static PyTypeObject pgf_PGFType = { 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ - (reprfunc) PGF_str, /*tp_str*/ + 0, // (reprfunc) PGF_str, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ @@ -2479,6 +2472,8 @@ static PyTypeObject pgf_PGFType = { 0, /*tp_new */ }; +// ---------------------------------------------------------------------------- + static PGFObject* pgf_readPGF(PyObject *self, PyObject *args) { diff --git a/src/runtime/python/setup.py b/src/runtime/python/setup.py index c41b11aeb..208857f70 100644 --- a/src/runtime/python/setup.py +++ b/src/runtime/python/setup.py @@ -10,7 +10,12 @@ if libraries==['']: pgf_module = Extension( 'pgf', - sources = ['pypgf.c', 'marshaller.c', 'expr.c'], + sources = [ + 'pypgf.c', + 'expr.c', + 'ffi.c', + 'transactions.c' + ], extra_compile_args = ['-std=c99', '-Werror', '-Wno-error=unused-variable', '-Wno-comment'], include_dirs = includes, library_dirs = libraries, diff --git a/src/runtime/python/test_suite.py b/src/runtime/python/tests/test_basic.py similarity index 100% rename from src/runtime/python/test_suite.py rename to src/runtime/python/tests/test_basic.py diff --git a/src/runtime/python/tests/test_transactions.py b/src/runtime/python/tests/test_transactions.py new file mode 100644 index 000000000..26ea56a0d --- /dev/null +++ b/src/runtime/python/tests/test_transactions.py @@ -0,0 +1,41 @@ +import pytest +from pgf import * + +ty = readType("(N -> N) -> P (s z)") +pi = 3.142 + +# @pytest.fixture(scope="module") +# def gr1(): +# return pgf.readPGF("../haskell/tests/basic.pgf") +# +# @pytest.fixture(scope="module") +# def gr2(gr1): +# transaction = [ +# createFunction("foo", ty, 0, pi), +# createCategory("Q", [(BIND_TYPE_EXPLICIT, "x", ty)], pi) +# ] +# return gr1.modifyPGF(transaction) +# +# @pytest.fixture(scope="module") +# def gr3(gr1): +# transaction = [ +# createFunction("bar", ty, 0, pi), +# createCategory("R", [(BIND_TYPE_EXPLICIT, "x", ty)], pi) +# ] +# return gr1.branchPGF("bar_branch", transaction) +# +# def original_functions(gr1): +# assert gr1.functions == ["c", "ind", "s", "z"] +# +# def extended_functions(gr2): +# assert gr2.functions == ["c", "foo", "ind", "s", "z"] +# +# def branched_functions(gr3): +# assert gr3.functions == ["bar", "c", "ind", "s", "z"] + +def test_non_transactional(): + gr1 = readPGF("../haskell/tests/basic.pgf") + assert gr1.functions == ["c", "ind", "s", "z"] + + gr1.createFunction("foo", ty, 0, pi) + assert gr1.functions == ["c", "foo", "ind", "s", "z"] diff --git a/src/runtime/python/transactions.c b/src/runtime/python/transactions.c new file mode 100644 index 000000000..8ff2e2e58 --- /dev/null +++ b/src/runtime/python/transactions.c @@ -0,0 +1,102 @@ +#define PY_SSIZE_T_CLEAN +#include +// #include + +#include +#include "./expr.h" +#include "./ffi.h" + +PyObject * +PGF_createFunction(PGFObject *self, PyObject *args) +{ + const char *s; + Py_ssize_t size; + TypeObject *type; + Py_ssize_t arity = 0; + float prob = 0.0; + if (!PyArg_ParseTuple(args, "s#O!nf", &s, &size, &pgf_TypeType, &type, &arity, &prob)) + return NULL; + + PgfText *fname = (PgfText *)PyMem_Malloc(sizeof(PgfText)+size+1); + memcpy(fname->text, s, size+1); + fname->size = size; + + PgfExn err; + pgf_create_function(self->db, self->revision, fname, (PgfType) type, arity, prob, &marshaller, &err); + PyMem_Free(fname); + if (handleError(err) != PGF_EXN_NONE) { + return NULL; + } + + Py_RETURN_NONE; +} + +PyObject * +PGF_dropFunction(PGFObject *self, PyObject *args) +{ + const char *s; + Py_ssize_t size; + if (!PyArg_ParseTuple(args, "s#", &s, &size)) + return NULL; + + PgfText *fname = (PgfText *)PyMem_Malloc(sizeof(PgfText)+size+1); + memcpy(fname->text, s, size+1); + fname->size = size; + + PgfExn err; + pgf_drop_category(self->db, self->revision, fname, &err); + PyMem_Free(fname); + if (handleError(err) != PGF_EXN_NONE) { + return NULL; + } + + Py_RETURN_NONE; +} + +PyObject * +PGF_createCategory(PGFObject *self, PyObject *args) +{ + const char *s; + Py_ssize_t size; + float prob = 0.0; + if (!PyArg_ParseTuple(args, "s#f", &s, &size, prob)) + return NULL; + + PgfText *catname = (PgfText *)PyMem_Malloc(sizeof(PgfText)+size+1); + memcpy(catname->text, s, size+1); + catname->size = size; + + Py_ssize_t n_hypos = 0; + PgfTypeHypo *context = NULL; + + PgfExn err; + pgf_create_category(self->db, self->revision, catname, n_hypos, context, prob, &marshaller, &err); + PyMem_Free(catname); + if (handleError(err) != PGF_EXN_NONE) { + return NULL; + } + + Py_RETURN_NONE; +} + +PyObject * +PGF_dropCategory(PGFObject *self, PyObject *args) +{ + const char *s; + Py_ssize_t size; + if (!PyArg_ParseTuple(args, "s#", &s, &size)) + return NULL; + + PgfText *catname = (PgfText *)PyMem_Malloc(sizeof(PgfText)+size+1); + memcpy(catname->text, s, size+1); + catname->size = size; + + PgfExn err; + pgf_drop_function(self->db, self->revision, catname, &err); + PyMem_Free(catname); + if (handleError(err) != PGF_EXN_NONE) { + return NULL; + } + + Py_RETURN_NONE; +} diff --git a/src/runtime/python/transactions.h b/src/runtime/python/transactions.h new file mode 100644 index 000000000..39b6a1e0e --- /dev/null +++ b/src/runtime/python/transactions.h @@ -0,0 +1,38 @@ +#ifndef PYPGF_TRANSACTIONS_H_ +#define PYPGF_TRANSACTIONS_H_ + +#define PY_SSIZE_T_CLEAN +#include + +#include + +// typedef struct { +// PyObject_HEAD +// PyObject *thing; +// } TransactionObject; + +// modifyPGF +// +// branchPGF +// +// checkoutPGF + +PyObject * +PGF_createFunction(PGFObject *self, PyObject *args); + +PyObject * +PGF_dropFunction(PGFObject *self, PyObject *args); + +PyObject * +PGF_createCategory(PGFObject *self, PyObject *args); + +PyObject * +PGF_dropCategory(PGFObject *self, PyObject *args); + +// setGlobalFlag +// +// setAbstractFlag + + + +#endif // PYPGF_TRANSACTIONS_H_