From 876e3c734a72472b8c8c02119ea3c6384a4f9d71 Mon Sep 17 00:00:00 2001 From: Krasimir Angelov Date: Mon, 15 Apr 2024 09:19:11 +0200 Subject: [PATCH] added Graphviz visualizations --- src/runtime/c/pgf/pgf.h | 6 +++ src/runtime/python/pgf.pyi | 32 ++++++++++++-- src/runtime/python/pypgf.c | 84 +++++++++++++++++++++++++++++++++++-- src/runtime/python/setup.py | 2 +- 4 files changed, 117 insertions(+), 7 deletions(-) diff --git a/src/runtime/c/pgf/pgf.h b/src/runtime/c/pgf/pgf.h index cb5852529..2022e00a3 100644 --- a/src/runtime/c/pgf/pgf.h +++ b/src/runtime/c/pgf/pgf.h @@ -878,6 +878,12 @@ typedef struct { const char *leafEdgeStyle; } PgfGraphvizOptions; +PGF_API_DECL PgfText * +pgf_graphviz_abstract_tree(PgfDB *db, PgfRevision revision, + PgfExpr expr, PgfMarshaller *m, + PgfGraphvizOptions* opts, + PgfExn *err); + PGF_API_DECL PgfText * pgf_graphviz_parse_tree(PgfDB *db, PgfConcrRevision revision, PgfExpr expr, PgfPrintContext *ctxt, diff --git a/src/runtime/python/pgf.pyi b/src/runtime/python/pgf.pyi index c1b9b0bc9..d98dd49a0 100644 --- a/src/runtime/python/pgf.pyi +++ b/src/runtime/python/pgf.pyi @@ -114,7 +114,7 @@ class PGF: """ ... - def generateRandom(cat : Type, depth : int = 5) -> tuple[Expr,float]: + def generateRandom(self, cat : Type, depth : int = 5) -> tuple[Expr,float]: """ Generates a random abstract syntax trees of the given type. The depth parameter specifies the maximal distance between @@ -123,6 +123,16 @@ class PGF: """ ... + def graphvizAbstractTree(self, e: Expr, + noFun:bool=False, noCat:bool=False, + nodeFont:str="", + nodeColor:str="", + nodeEdgeStyle:str="") -> str: + """ + Renders an abstract syntax tree in a Graphviz format + """ + ... + def newTransaction(self) -> Transaction: """ Starts a new transaction which makes it possible to update @@ -208,11 +218,11 @@ class Concr: """The name of the concrete syntax""" ... - def linearize(e: Expr) -> str: + def linearize(self, e: Expr) -> str: """Linearizes the abstract expression and returns as string""" ... - def bracketedLinearize(e: Expr) -> list[Any]: + def bracketedLinearize(self, e: Expr) -> list[Any]: """ Produces a bracketed linearization where syntactic phrases are represented as objects of type Bracket and terminal tokens @@ -221,6 +231,22 @@ class Concr: """ ... + def hasLinearization(self, fun: Str) -> bool: + """ + Returns true if the given function has linearization in the concrete syntax + """ + ... + + def graphvizParseTree(self, e: Expr, + noLeaves:bool=False, noFun:bool=False, noCat:bool=False, + nodeFont:str="", leafFont:str="", + nodeColor:str="", leafColor:str="", + nodeEdgeStyle:str="", leafEdgeStyle:str="") -> str: + """ + Renders an abstract syntax tree as a parse tree in Graphviz format + """ + ... + class PGFError: """ This is the exception that several functions throw diff --git a/src/runtime/python/pypgf.c b/src/runtime/python/pypgf.c index 8a89bb0c4..7e90c51ad 100644 --- a/src/runtime/python/pypgf.c +++ b/src/runtime/python/pypgf.c @@ -407,6 +407,47 @@ Concr_hasLinearization(ConcrObject* self, PyObject *args) Py_RETURN_FALSE; } +static PyObject* +Concr_graphvizParseTree(ConcrObject* self, PyObject *args, PyObject *kwargs) +{ + PgfGraphvizOptions opts; + memset(&opts, 0, sizeof(opts)); + + char *kwds[] = {"","noLeaves","noFun","noCat", + "nodeFont","leafFont", + "nodeColor","leafColor", + "nodeEdgeStyle","leafEdgeStyle", + NULL}; + + ExprObject* pyexpr; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|pppssssss", kwds, + &pgf_ExprType, &pyexpr, + &opts.noLeaves, + &opts.noFun, + &opts.noCat, + &opts.nodeFont, + &opts.leafFont, + &opts.nodeColor, + &opts.leafColor, + &opts.nodeEdgeStyle, + &opts.leafEdgeStyle)) + return NULL; + + PgfExn err; + PgfText *text = + pgf_graphviz_parse_tree(self->grammar->db, self->concr, + (PgfExpr) pyexpr, NULL, &marshaller, + &opts, &err); + if (handleError(err) != PGF_EXN_NONE) { + return NULL; + } + + PyObject* pystr = PyUnicode_FromStringAndSize(text->text, text->size); + free(text); + + return pystr; +} + typedef struct { PgfMorphoCallback fn; PyObject* analyses; @@ -519,10 +560,10 @@ static PyMethodDef Concr_methods[] = { },*/ {"hasLinearization", (PyCFunction)Concr_hasLinearization, METH_VARARGS, "hasLinearization(f) returns true if the function f has linearization in the concrete syntax" - },/* - {"graphvizParseTree", (PyCFunction)Concr_graphvizParseTree, METH_VARARGS, + }, + {"graphvizParseTree", (PyCFunction)Concr_graphvizParseTree, METH_VARARGS | METH_KEYWORDS, "Renders an abstract syntax tree as a parse tree in Graphviz format" - },*/ + }, {"lookupMorpho", (PyCFunction)Concr_lookupMorpho, METH_VARARGS, "Looks up a word in the lexicon of the grammar" },/* @@ -993,6 +1034,40 @@ PGF_generateRandom(PGFObject *self, PyObject *args, PyObject *keywds) return res; } +static PyObject* +PGF_graphvizAbstractTree(PGFObject* self, PyObject *args, PyObject *kwargs) { + PgfGraphvizOptions opts; + memset(&opts, 0, sizeof(opts)); + + char *kwds[] = {"","noFun","noCat", + "nodeFont", "nodeColor", "nodeEdgeStyle", + NULL}; + + ExprObject* pyexpr; + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O!|ppsss", kwds, + &pgf_ExprType, &pyexpr, + &opts.noFun, + &opts.noCat, + &opts.nodeFont, + &opts.nodeColor, + &opts.nodeEdgeStyle)) + return NULL; + + PgfExn err; + PgfText *text = + pgf_graphviz_abstract_tree(self->db, self->revision, + (PgfExpr) pyexpr, &marshaller, + &opts, &err); + if (handleError(err) != PGF_EXN_NONE) { + return NULL; + } + + PyObject* pystr = PyUnicode_FromStringAndSize(text->text, text->size); + free(text); + + return pystr; +} + static PyObject * PGF_categoryProbability(PGFObject *self, PyObject *args) { @@ -1537,6 +1612,9 @@ static PyMethodDef PGF_methods[] = { {"generateRandom", (PyCFunction)PGF_generateRandom, METH_VARARGS | METH_KEYWORDS, "Generates a random abstract syntax trees of the given type" }, + {"graphvizAbstractTree", (PyCFunction)PGF_graphvizAbstractTree, METH_VARARGS | METH_KEYWORDS, + "Renders an abstract syntax tree in a Graphviz format" + }, {"categoryProbability", (PyCFunction)PGF_categoryProbability, METH_VARARGS, "Returns the probability of a category" }, diff --git a/src/runtime/python/setup.py b/src/runtime/python/setup.py index 3196ef93c..36bbd099c 100644 --- a/src/runtime/python/setup.py +++ b/src/runtime/python/setup.py @@ -35,7 +35,7 @@ pgf_module = Extension( setup( name = 'pgf-majestic', - version = '2.8', + version = '2.9', description = 'Python bindings to the Grammatical Framework\'s PGF runtime', long_description="""\ Grammatical Framework (GF) is a programming language for multilingual grammar applications.