From e78e9102be9ddec38efe0fbec3ea6a6a1ac5a218 Mon Sep 17 00:00:00 2001 From: "John J. Camilleri" Date: Mon, 13 Sep 2021 22:29:23 +0200 Subject: [PATCH 1/4] Add variable expressions --- src/runtime/python/expr.c | 96 +++++++++++++++++++++++++++++++- src/runtime/python/expr.h | 9 ++- src/runtime/python/marshaller.c | 22 +++++--- src/runtime/python/pypgf.c | 6 ++ src/runtime/python/test_suite.py | 19 +++++++ 5 files changed, 141 insertions(+), 11 deletions(-) diff --git a/src/runtime/python/expr.c b/src/runtime/python/expr.c index dcd5b16e7..82ac1f028 100644 --- a/src/runtime/python/expr.c +++ b/src/runtime/python/expr.c @@ -242,10 +242,10 @@ ExprMeta_init(ExprMetaObject *self, PyObject *args, PyObject *kwds) return -1; } if (lit == NULL) { - self->index = PyLong_FromLong(0); + self->id = PyLong_FromLong(0); return 0; } else if (PyLong_Check(lit)) { - self->index = lit; + self->id = lit; return 0; } else { PyErr_SetString(PyExc_TypeError, "invalid argument in ExprMeta_init"); @@ -257,7 +257,7 @@ static PyObject * ExprMeta_richcompare(ExprMetaObject *t1, ExprMetaObject *t2, int op) { bool same = false; - if (PyObject_RichCompareBool(t1->index, t2->index, Py_EQ) != 1) goto done; + if (PyObject_RichCompareBool(t1->id, t2->id, Py_EQ) != 1) goto done; same = true; done: @@ -317,6 +317,96 @@ PyTypeObject pgf_ExprMetaType = { // ---------------------------------------------------------------------------- +static ExprVarObject * +ExprVar_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) +{ + ExprVarObject* self = (ExprVarObject *)subtype->tp_alloc(subtype, 0); + return self; +} + +static int +ExprVar_init(ExprVarObject *self, PyObject *args, PyObject *kwds) +{ + PyObject* lit = NULL; + if (!PyArg_ParseTuple(args, "|O", &lit)) { + return -1; + } + if (lit == NULL) { + self->index = PyLong_FromLong(0); + return 0; + } else if (PyLong_Check(lit)) { + self->index = lit; + return 0; + } else { + PyErr_SetString(PyExc_TypeError, "invalid argument in ExprVar_init"); + return -1; + } +} + +static PyObject * +ExprVar_richcompare(ExprVarObject *t1, ExprVarObject *t2, int op) +{ + bool same = false; + if (PyObject_RichCompareBool(t1->index, t2->index, Py_EQ) != 1) goto done; + + same = true; +done: + + if (op == Py_EQ) { + if (same) Py_RETURN_TRUE; else Py_RETURN_FALSE; + } else if (op == Py_NE) { + if (same) Py_RETURN_FALSE; else Py_RETURN_TRUE; + } else { + PyErr_SetString(PyExc_TypeError, "comparison operation not supported"); + Py_RETURN_NOTIMPLEMENTED; + } +} + +/* static */ +PyTypeObject pgf_ExprVarType = { + PyVarObject_HEAD_INIT(NULL, 0) + //0, /*ob_size*/ + "pgf.ExprVar", /*tp_name*/ + sizeof(ExprVarObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, //(destructor)Expr_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, //(hashfunc) Expr_hash, /*tp_hash */ + 0, //(ternaryfunc) Expr_call, /*tp_call*/ + 0, //(reprfunc) Expr_str, /*tp_str*/ + 0, //(getattrofunc) Expr_getattro,/*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + "variable", /*tp_doc*/ + 0, /*tp_traverse */ + 0, /*tp_clear */ + (richcmpfunc) ExprVar_richcompare, /*tp_richcompare */ + 0, /*tp_weaklistoffset */ + 0, /*tp_iter */ + 0, /*tp_iternext */ + 0, //Expr_methods, /*tp_methods */ + 0, /*tp_members */ + 0, //Expr_getseters, /*tp_getset */ + &pgf_ExprType, /*tp_base */ + 0, /*tp_dict */ + 0, /*tp_descr_get */ + 0, /*tp_descr_set */ + 0, /*tp_dictoffset */ + (initproc) ExprVar_init, /*tp_init */ + 0, /*tp_alloc */ + (newfunc) ExprVar_new, /*tp_new */ +}; + +// ---------------------------------------------------------------------------- + static PyObject * Type_str(TypeObject *self) { diff --git a/src/runtime/python/expr.h b/src/runtime/python/expr.h index e1e2562de..19b3fa45d 100644 --- a/src/runtime/python/expr.h +++ b/src/runtime/python/expr.h @@ -28,13 +28,20 @@ typedef struct { typedef struct { PyObject_HEAD - PyObject *index; + PyObject *id; } ExprMetaObject; +typedef struct { + PyObject_HEAD + PyObject *index; +} ExprVarObject; + extern PyTypeObject pgf_ExprType; extern PyTypeObject pgf_ExprLitType; extern PyTypeObject pgf_ExprMetaType; +extern PyTypeObject pgf_ExprVarType; + #endif // PYPGF_EXPR_H_ diff --git a/src/runtime/python/marshaller.c b/src/runtime/python/marshaller.c index ee1702712..257fd8e9a 100644 --- a/src/runtime/python/marshaller.c +++ b/src/runtime/python/marshaller.c @@ -38,7 +38,7 @@ PgfExpr elit(PgfUnmarshaller *this, PgfLiteral lit) PgfExpr emeta(PgfUnmarshaller *this, PgfMetaId meta) { ExprMetaObject *pyexpr = (ExprMetaObject *)pgf_ExprMetaType.tp_alloc(&pgf_ExprMetaType, 0); - pyexpr->index = PyLong_FromLong(meta); + pyexpr->id = PyLong_FromLong(meta); return (PgfExpr) pyexpr; } @@ -50,8 +50,9 @@ PgfExpr efun(PgfUnmarshaller *this, PgfText *name) PgfExpr evar(PgfUnmarshaller *this, int index) { - PyErr_SetString(PyExc_NotImplementedError, "evar not implemented"); - return 0; + ExprVarObject *pyexpr = (ExprVarObject *)pgf_ExprVarType.tp_alloc(&pgf_ExprVarType, 0); + pyexpr->index = PyLong_FromLong(index); + return (PgfExpr) pyexpr; } PgfExpr etyped(PgfUnmarshaller *this, PgfExpr expr, PgfType typ) @@ -221,14 +222,21 @@ object match_lit(PgfMarshaller *this, PgfUnmarshaller *u, PgfLiteral lit) } } -object match_expr(PgfMarshaller *this, PgfUnmarshaller *u, PgfExpr ex) +object match_expr(PgfMarshaller *this, PgfUnmarshaller *u, PgfExpr expr) { - ExprObject *expr = (ExprObject *)ex; + PyObject *pyobj = (PyObject *)expr; - if (expr->ob_base.ob_type == &pgf_ExprLitType) { // use PyObject_IsInstance ? - ExprLitObject *elit= (ExprLitObject *)expr; + if (PyObject_TypeCheck(pyobj, &pgf_ExprLitType)) { + ExprLitObject *elit = (ExprLitObject *)expr; return this->vtbl->match_lit(this, u, (PgfLiteral) elit->value); + } else if (PyObject_TypeCheck(pyobj, &pgf_ExprMetaType)) { + ExprMetaObject *emeta = (ExprMetaObject *)expr; + return u->vtbl->emeta(u, (PgfMetaId) PyLong_AsLong(emeta->id)); + } else if (PyObject_TypeCheck(pyobj, &pgf_ExprVarType)) { + ExprVarObject *evar = (ExprVarObject *)expr; + return u->vtbl->evar(u, PyLong_AsLong(evar->index)); } else { + PyErr_SetString(PyExc_TypeError, "unable to match on expression"); return 0; } } diff --git a/src/runtime/python/pypgf.c b/src/runtime/python/pypgf.c index 438e51bfe..aaa4fd3e8 100644 --- a/src/runtime/python/pypgf.c +++ b/src/runtime/python/pypgf.c @@ -2636,6 +2636,9 @@ MOD_INIT(pgf) if (PyType_Ready(&pgf_ExprMetaType) < 0) return MOD_ERROR_VAL; + if (PyType_Ready(&pgf_ExprVarType) < 0) + return MOD_ERROR_VAL; + if (PyType_Ready(&pgf_TypeType) < 0) return MOD_ERROR_VAL; @@ -2670,6 +2673,9 @@ MOD_INIT(pgf) PyModule_AddObject(m, "ExprMeta", (PyObject *) &pgf_ExprMetaType); Py_INCREF(&pgf_ExprMetaType); + PyModule_AddObject(m, "ExprVar", (PyObject *) &pgf_ExprVarType); + Py_INCREF(&pgf_ExprVarType); + PyModule_AddObject(m, "Type", (PyObject *) &pgf_TypeType); Py_INCREF(&pgf_TypeType); diff --git a/src/runtime/python/test_suite.py b/src/runtime/python/test_suite.py index bbb170879..47e420327 100644 --- a/src/runtime/python/test_suite.py +++ b/src/runtime/python/test_suite.py @@ -227,6 +227,19 @@ def test_readExpr_lstr_str(): # ,TestCase (assertEqual "show expression 6" "z" (showExpr ["z","y","x"] (EVar 0))) # ,TestCase (assertEqual "show expression 7" "y" (showExpr ["z","y","x"] (EVar 1))) +# def test_readExpr_evar_equality_1(): +# assert pgf.readExpr("#0") == pgf.ExprVar() +# assert pgf.readExpr("#0") == pgf.ExprVar(0) + +# def test_readExpr_evar_equality_2(): +# assert pgf.readExpr("#42") == pgf.ExprVar(42) + +def test_readExpr_evar_str_1(): + assert str(pgf.ExprVar(0)) == "#0" + +def test_readExpr_evar_str_2(): + assert str(pgf.ExprVar(42)) == "#42" + # expressions: lambda abstractions # ,TestCase (assertEqual "show expression 8" "\\w->w" (showExpr ["z","y","x"] (EAbs Explicit "w" (EVar 0)))) @@ -247,6 +260,12 @@ def test_readExpr_emeta_1(): def test_readExpr_emeta_2(): assert pgf.readExpr("?42") == pgf.ExprMeta(42) +def test_readExpr_emeta_str_1(): + assert str(pgf.readExpr("?")) == "?" + +def test_readExpr_emeta_str_2(): + assert str(pgf.readExpr("?42")) == "?42" + # expressions: typed expressions # ,TestCase (assertEqual "show expression 18" "" (showExpr [] (ETyped (EFun "z") (DTyp [] "N" [])))) From a715d029f7f2a027a8ff8f65354947b207f40c61 Mon Sep 17 00:00:00 2001 From: "John J. Camilleri" Date: Mon, 13 Sep 2021 22:35:23 +0200 Subject: [PATCH 2/4] Fix Haskell tests after changes to categoryContext and functionProb --- src/runtime/haskell/tests/basic.hs | 8 ++++---- src/runtime/haskell/tests/transactions.hs | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/runtime/haskell/tests/basic.hs b/src/runtime/haskell/tests/basic.hs index a2a28aaba..e7e977520 100644 --- a/src/runtime/haskell/tests/basic.hs +++ b/src/runtime/haskell/tests/basic.hs @@ -21,10 +21,10 @@ main = do ,TestCase (assertBool "type of z" (eqJust (readType "N") (functionType gr "z"))) ,TestCase (assertBool "type of s" (eqJust (readType "N->N") (functionType gr "s"))) ,TestCase (assertBool "type of c" (eqJust (readType "N->S") (functionType gr "c"))) - ,TestCase (assertEqual "category context 1" [] (categoryContext gr "N")) - ,TestCase (assertEqual "category context 2" [] (categoryContext gr "S")) - ,TestCase (assertEqual "category context 3" [(Explicit,"_",DTyp [] "N" [])] (categoryContext gr "P")) - ,TestCase (assertEqual "category context 4" [] (categoryContext gr "X")) -- no such category + ,TestCase (assertEqual "category context 1" (Just []) (categoryContext gr "N")) + ,TestCase (assertEqual "category context 2" (Just []) (categoryContext gr "S")) + ,TestCase (assertEqual "category context 3" (Just [(Explicit,"_",DTyp [] "N" [])]) (categoryContext gr "P")) + ,TestCase (assertEqual "category context 4" Nothing (categoryContext gr "X")) -- no such category ,TestCase (assertEqual "function is constructor 1" True (functionIsConstructor gr "s")) ,TestCase (assertEqual "function is constructor 2" True (functionIsConstructor gr "z")) ,TestCase (assertEqual "function is constructor 3" True (functionIsConstructor gr "c")) diff --git a/src/runtime/haskell/tests/transactions.hs b/src/runtime/haskell/tests/transactions.hs index 37e75776e..fbd62f0d1 100644 --- a/src/runtime/haskell/tests/transactions.hs +++ b/src/runtime/haskell/tests/transactions.hs @@ -28,16 +28,16 @@ main = do ,TestCase (assertEqual "original categories" ["Float","Int","N","P","S","String"] (categories gr1)) ,TestCase (assertEqual "extended categories" ["Float","Int","N","P","Q","S","String"] (categories gr2)) ,TestCase (assertEqual "branched categories" ["Float","Int","N","P","R","S","String"] (categories gr3)) - ,TestCase (assertEqual "Q context" [(Explicit,"x",ty)] (categoryContext gr2 "Q")) - ,TestCase (assertEqual "R context" [(Explicit,"x",ty)] (categoryContext gr3 "R")) + ,TestCase (assertEqual "Q context" (Just [(Explicit,"x",ty)]) (categoryContext gr2 "Q")) + ,TestCase (assertEqual "R context" (Just [(Explicit,"x",ty)]) (categoryContext gr3 "R")) ,TestCase (assertEqual "reduced functions" ["c","s","z"] (functions gr6)) ,TestCase (assertEqual "reduced categories" ["Float","Int","N","P","String"] (categories gr6)) ,TestCase (assertEqual "old function type" Nothing (functionType gr1 "foo")) ,TestCase (assertEqual "new function type" (Just ty) (functionType gr2 "foo")) - ,TestCase (assertEqual "old function prob" (-log 0) (functionProb gr1 "foo")) - ,TestCase (assertEqual "new function prob" pi (functionProb gr2 "foo")) + ,TestCase (assertEqual "old function prob" (-log 0) (functionProbability gr1 "foo")) + ,TestCase (assertEqual "new function prob" pi (functionProbability gr2 "foo")) ] - + performMajorGC if (errors c == 0) && (failures c == 0) From 9e3d329528304e6476c37a9d348dd5871c43de80 Mon Sep 17 00:00:00 2001 From: "John J. Camilleri" Date: Mon, 13 Sep 2021 22:44:04 +0200 Subject: [PATCH 3/4] Update behaviour for bindings to categoryContext when cat is non-existant --- src/runtime/python/pypgf.c | 4 ++++ src/runtime/python/test_suite.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/runtime/python/pypgf.c b/src/runtime/python/pypgf.c index aaa4fd3e8..4f2e7765f 100644 --- a/src/runtime/python/pypgf.c +++ b/src/runtime/python/pypgf.c @@ -1917,6 +1917,10 @@ PGF_categoryContext(PGFObject *self, PyObject *args) return NULL; } + if (hypos == NULL) { + Py_RETURN_NONE; + } + PyObject *contexts = PyList_New(n_hypos); if (contexts == NULL) { return NULL; diff --git a/src/runtime/python/test_suite.py b/src/runtime/python/test_suite.py index 47e420327..d0699ecee 100644 --- a/src/runtime/python/test_suite.py +++ b/src/runtime/python/test_suite.py @@ -98,7 +98,7 @@ def test_categoryContext_3(PGF): assert tup[2] == pgf.readType("N") def test_categoryContext_4(PGF): - assert PGF.categoryContext("X") == [] + assert PGF.categoryContext("X") == None def test_functionIsConstructor_1(PGF): assert PGF.functionIsConstructor("s") == True From be5751060a57b8e0ee95c2f7d2f4ae3471cd450f Mon Sep 17 00:00:00 2001 From: "John J. Camilleri" Date: Tue, 14 Sep 2021 00:18:45 +0200 Subject: [PATCH 4/4] Add showExpr and tests for it using variable expressions --- src/runtime/python/marshaller.c | 4 ++-- src/runtime/python/marshaller.h | 2 ++ src/runtime/python/pypgf.c | 34 +++++++++++++++++++++++++++++++- src/runtime/python/test_suite.py | 17 +++++++++++----- 4 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/runtime/python/marshaller.c b/src/runtime/python/marshaller.c index 257fd8e9a..e2dab89fd 100644 --- a/src/runtime/python/marshaller.c +++ b/src/runtime/python/marshaller.c @@ -154,7 +154,7 @@ PgfUnmarshaller unmarshaller = { &unmarshallerVtbl }; // ---------------------------------------------------------------------------- -static PgfText * +PgfText * PyUnicode_AsPgfText(PyObject *pystr) { if (!PyUnicode_Check(pystr)) { @@ -166,7 +166,7 @@ PyUnicode_AsPgfText(PyObject *pystr) } Py_ssize_t size; - const char * enc = PyUnicode_AsUTF8AndSize(pystr, &size); + const char *enc = PyUnicode_AsUTF8AndSize(pystr, &size); PgfText *ptext = malloc(sizeof(PgfText)+size+1); memcpy(ptext->text, enc, size+1); ptext->size = size; diff --git a/src/runtime/python/marshaller.h b/src/runtime/python/marshaller.h index 71793ec2c..bec013fe8 100644 --- a/src/runtime/python/marshaller.h +++ b/src/runtime/python/marshaller.h @@ -6,6 +6,8 @@ #include +PgfText *PyUnicode_AsPgfText(PyObject *pystr); + extern PgfUnmarshaller unmarshaller; extern PgfMarshaller marshaller; diff --git a/src/runtime/python/pypgf.c b/src/runtime/python/pypgf.c index 4f2e7765f..6b4bff183 100644 --- a/src/runtime/python/pypgf.c +++ b/src/runtime/python/pypgf.c @@ -2541,7 +2541,7 @@ const char *fpath; return py_pgf; } -static ExprObject* +static ExprObject * pgf_readExpr(PyObject *self, PyObject *args) { const char *s; @@ -2563,6 +2563,36 @@ pgf_readExpr(PyObject *self, PyObject *args) return (ExprObject *)expr; } +static PyObject * +pgf_showExpr(PyObject *self, PyObject *args) +{ + PyObject *pylist; + PyObject *pyexpr; + if (!PyArg_ParseTuple(args, "O!O!", &PyList_Type, &pylist, &pgf_ExprType, &pyexpr)) + return NULL; + + PgfPrintContext *ctxt = NULL; + for (Py_ssize_t i = PyList_Size(pylist); i > 0 ; i--) { + PyObject *item = PyList_GetItem(pylist, i-1); + if (!PyUnicode_Check(item)) { + PyErr_SetString(PyExc_TypeError, "invalid variable argument in showExpr"); + return NULL; + } + PgfText *input = PyUnicode_AsPgfText(item); + + // TODO a better way to copy into this->name? + PgfPrintContext *this = (PgfPrintContext *)PyMem_Malloc(sizeof(PgfPrintContext *) + sizeof(PgfText) + input->size + 1); + this->next = ctxt; + memcpy(&this->name, input, sizeof(PgfText) + input->size + 1); + ctxt = this; + } + + PgfText *s = pgf_print_expr((PgfExpr) pyexpr, ctxt, 0, &marshaller); + PyObject *str = PyUnicode_FromStringAndSize(s->text, s->size); + free(s); + return str; +} + static TypeObject * pgf_readType(PyObject *self, PyObject *args) { @@ -2594,6 +2624,8 @@ static PyMethodDef module_methods[] = { "Reads an NGF file into memory"}, {"readExpr", (void*)pgf_readExpr, METH_VARARGS, "Parses a string as an abstract tree"}, + {"showExpr", (void*)pgf_showExpr, METH_VARARGS, + "Renders an expression as a string"}, {"readType", (void*)pgf_readType, METH_VARARGS, "Parses a string as an abstract type"}, {NULL, NULL, 0, NULL} /* Sentinel */ diff --git a/src/runtime/python/test_suite.py b/src/runtime/python/test_suite.py index d0699ecee..b3fb45a59 100644 --- a/src/runtime/python/test_suite.py +++ b/src/runtime/python/test_suite.py @@ -222,11 +222,6 @@ def test_readExpr_lstr_str(): # expressions: variables - # ,TestCase (assertEqual "show expression 4" "x" (showExpr ["x"] (EVar 0))) - # ,TestCase (assertEqual "show expression 5" "#1" (showExpr ["x"] (EVar 1))) - # ,TestCase (assertEqual "show expression 6" "z" (showExpr ["z","y","x"] (EVar 0))) - # ,TestCase (assertEqual "show expression 7" "y" (showExpr ["z","y","x"] (EVar 1))) - # def test_readExpr_evar_equality_1(): # assert pgf.readExpr("#0") == pgf.ExprVar() # assert pgf.readExpr("#0") == pgf.ExprVar(0) @@ -240,6 +235,18 @@ def test_readExpr_evar_str_1(): def test_readExpr_evar_str_2(): assert str(pgf.ExprVar(42)) == "#42" +def test_showExpr_evar_1(): + assert pgf.showExpr(["x"], pgf.ExprVar(0)) == "x" + +def test_showExpr_evar_2(): + assert pgf.showExpr(["x"], pgf.ExprVar(1)) == "#1" + +def test_showExpr_evar_3(): + assert pgf.showExpr(["z", "y", "x"], pgf.ExprVar(0)) == "z" + +def test_showExpr_evar_4(): + assert pgf.showExpr(["z", "y", "x"], pgf.ExprVar(1)) == "y" + # expressions: lambda abstractions # ,TestCase (assertEqual "show expression 8" "\\w->w" (showExpr ["z","y","x"] (EAbs Explicit "w" (EVar 0))))