summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjordi.saludes <jordi.saludes@upc.edu>2011-12-07 18:09:33 +0000
committerjordi.saludes <jordi.saludes@upc.edu>2011-12-07 18:09:33 +0000
commit75c6aa83cd16d1afd0e883833354877c1502166d (patch)
tree6cf59ffe4c71b6de003517ba61ddba1841cb1815
parentbb6905e36ff87d65cb8732ce5325a6a1bbbf6e31 (diff)
Added parser completions to py-bindings.
-rw-r--r--contrib/py-bindings/PyGF.hsc43
-rw-r--r--contrib/py-bindings/gfmodule.c43
-rw-r--r--contrib/py-bindings/test.py32
3 files changed, 102 insertions, 16 deletions
diff --git a/contrib/py-bindings/PyGF.hsc b/contrib/py-bindings/PyGF.hsc
index d9f249eab..ddd3920b6 100644
--- a/contrib/py-bindings/PyGF.hsc
+++ b/contrib/py-bindings/PyGF.hsc
@@ -1,7 +1,9 @@
{-# LANGUAGE ForeignFunctionInterface #-}
--- GF Python bindings -- Jordi Saludes, upc.edu 2010
+--
+-- GF Python bindings
-- Jordi Saludes, upc.edu 2010
--
+
module PyGF where
import PGF
@@ -9,6 +11,8 @@ import Foreign
import CString
import Foreign.C.Types
import Control.Monad
+import Data.Map (keys, (!))
+import Data.Char (isSpace)
#include "pygf.h"
@@ -73,6 +77,7 @@ instance Storable Tree where
deRefStablePtr sp
-}
+
foreign export ccall gf_freePGF :: Ptr PGF -> IO ()
foreign export ccall gf_freeType :: Ptr Type -> IO ()
foreign export ccall gf_freeLanguage :: Ptr Language -> IO ()
@@ -141,6 +146,18 @@ listToPy mk ls = do
poke pl l
pyl << pl
+
+listToPyStrings :: [String] -> IO (Ptr ())
+listToPyStrings ss = do
+ pyls <- pyList
+ mapM_ (mpoke pyls) ss
+ return pyls
+ where mpoke pyl s = do
+ cs <- newCString s
+ pcs <- pyString cs
+ pyl << pcs
+
+
-- foreign export ccall "gf_freeArray" free :: Ptr a -> IO ()
@@ -256,6 +273,29 @@ gf_functiontype ppgf pcid = do
_ -> return nullPtr
+foreign export ccall gf_completions :: Ptr PGF -> Ptr Language -> Ptr Type -> CString -> IO (Ptr ())
+gf_completions ppgf plang pcat ctoks = do
+ pgf <- peek ppgf
+ lang <- peek plang
+ cat <- peek pcat
+ toks <- peekCString ctoks
+ let (rpre,rs) = break isSpace (reverse toks)
+ pre = reverse rpre
+ ws = words (reverse rs)
+ state0 = initState pgf lang cat
+ completions =
+ case loop state0 ws of
+ Nothing -> []
+ Just state -> keys $ getCompletions state pre
+ listToPyStrings completions
+ where
+ loop ps [] = Just ps
+ loop ps (w:ws) =
+ case nextState ps (simpleParseInput w) of
+ Left _ -> Nothing
+ Right ps -> loop ps ws
+
+
foreign import ccall "newLang" pyLang :: IO (Ptr Language)
foreign import ccall "newPGF" pyPGF :: IO (Ptr PGF)
foreign import ccall "newTree" pyTree :: IO (Ptr Tree)
@@ -263,4 +303,5 @@ foreign import ccall "newgfType" pyType :: IO (Ptr Type)
foreign import ccall "newCId" pyCId :: IO (Ptr CId)
foreign import ccall "newExpr" pyExpr :: IO (Ptr Expr)
foreign import ccall "newList" pyList :: IO (Ptr ())
+foreign import ccall "newString" pyString :: CString -> IO (Ptr ())
foreign import ccall "append" (<<) :: Ptr () -> Ptr a -> IO ()
diff --git a/contrib/py-bindings/gfmodule.c b/contrib/py-bindings/gfmodule.c
index bd4693d2e..dc8f583a7 100644
--- a/contrib/py-bindings/gfmodule.c
+++ b/contrib/py-bindings/gfmodule.c
@@ -1,5 +1,5 @@
// GF Python bindings
-// Jordi Saludes, upc.edu 2010
+// Jordi Saludes, upc.edu 2010, 2011
//
#include <Python.h>
@@ -33,6 +33,7 @@ NEWGF(Tree,GF_Tree,TreeType,"gf.tree","gf tree")
DEALLOCFN(CId_dealloc, CId, gf_freeCId, "freeCId")
+
/* PGF methods, constructor and destructor */
DEALLOCFN(PGF_dealloc, PGF, gf_freePGF, "freePGF")
@@ -123,17 +124,16 @@ parse(PGF *self, PyObject *args, PyObject *kws)
Lang *lang;
gfType *cat = NULL;
char *lexed;
- static char *kwlist[] = {"lexed", "lang", "cat", NULL};
- if (!PyArg_ParseTupleAndKeywords(args, kws, "sO|O", kwlist,
- &lexed, &lang, &cat))
- return NULL;
+ static char *kwlist[] = {"lang", "lexed", "cat", NULL};
+ if (!PyArg_ParseTupleAndKeywords(args, kws, "Os|O", kwlist, &lang, &lexed, &cat))
+ return NULL;
if (!checkType(self, &PGFType)) return NULL;
if (!checkType(lang, &LangType)) return NULL;
if (cat) {
if (!checkType(cat, &gfTypeType)) return NULL;
} else {
- cat = gf_startCat(self);
- }
+ cat = gf_startCat(self);
+ }
return gf_parse(self, lang, cat, lexed);
}
@@ -154,9 +154,27 @@ readPGF(PyObject *self, PyObject *args)
}
+static PyObject*
+completions(PGF *self, PyObject *args, PyObject *kws)
+{ char *tokens;
+ Lang *lang;
+ gfType *cat = NULL;
+ static char *kwlist[] = {"lang", "tokens", "category", NULL};
+ if (!PyArg_ParseTupleAndKeywords(args, kws, "Os|O", kwlist, &lang, &tokens, &cat))
+ return NULL;
+ if (!checkType(self, &PGFType)) return NULL;
+ if (!checkType(lang, &LangType)) return NULL;
+ if (cat) {
+ if (!checkType(cat, &gfTypeType)) return NULL;
+ } else {
+ cat = gf_startCat(self);
+ }
+ return gf_completions(self, lang, cat, tokens);
+}
+
static PyMethodDef pgf_methods[] = {
- {"parse", (PyCFunction)parse, METH_VARARGS|METH_KEYWORDS,"Parse a string."},
+ {"parse", (PyCFunction)parse, METH_VARARGS|METH_KEYWORDS, "Parse a string."},
{"lin", (PyCFunction)linearize, METH_VARARGS,"Linearize tree."},
{"lang_code", (PyCFunction)languageCode, METH_VARARGS,"Get the language code."},
{"print_name", (PyCFunction)printName, METH_VARARGS,"Get the print name for a id."},
@@ -166,6 +184,7 @@ static PyMethodDef pgf_methods[] = {
{"functions", (PyCFunction)gf_functions, METH_NOARGS,"Get all functions."},
{"abstract", (PyCFunction)abstractName, METH_NOARGS,"Get the module abstract name."},
{"languages", (PyCFunction)gf_languages, METH_NOARGS,"Get the module languages."},
+ {"complete", (PyCFunction)completions, METH_VARARGS|METH_KEYWORDS, "Get completions for tokens."},
{NULL, NULL, 0, NULL} /* Sentinel */
};
@@ -281,12 +300,17 @@ initgf(void)
if (PyType_Ready(&t) < 0) return;
READYTYPE(CIdType, CId_repr, CId_dealloc)
+
PGFType.tp_methods = pgf_methods;
READYTYPE(PGFType, pgf_repr, PGF_dealloc)
+
READYTYPE(LangType, lang_repr, Lang_dealloc)
+
READYTYPE(gfTypeType, gfType_repr, gfType_dealloc)
- ExprType.tp_methods = expr_methods;
+
+ ExprType.tp_methods = expr_methods;
READYTYPE(ExprType, expr_repr, expr_dealloc)
+
TreeType.tp_methods = expr_methods; // Tree == Expr ?
READYTYPE(TreeType, tree_repr, Tree_dealloc)
@@ -311,4 +335,5 @@ PyModule_AddObject(m, "gf", (PyObject *)&t);
/* List utilities to be imported by FFI */
inline PyObject* newList() { return PyList_New(0); }
+inline PyObject* newString(const char *s) { return PyString_FromString(s); }
inline void append(PyObject* l, PyObject* ob) { PyList_Append(l, ob); }
diff --git a/contrib/py-bindings/test.py b/contrib/py-bindings/test.py
index ebfa0a0c8..6ec3f77cc 100644
--- a/contrib/py-bindings/test.py
+++ b/contrib/py-bindings/test.py
@@ -7,7 +7,6 @@
import gf
import unittest
-
samples = [
(['Odd', ['Number', 89]],
{'eng': "is 89 odd",
@@ -93,7 +92,7 @@ class TestParsing(unittest.TestCase):
l = gf.read_language(self.lang)
for abs,cnc in self.lexed:
rabs = exp2str(abs)
- ps = pgf.parse(cnc['eng'], l)
+ ps = pgf.parse(l, cnc['eng'])
self.failUnless(ps)
pt = rmprefix(ps[0])
self.assertEqual(pt,rabs)
@@ -108,7 +107,7 @@ class TestLinearize(unittest.TestCase):
def test_Linearize(self):
l = self.lang
for abs,cnc in self.samples:
- ts = self.pgf.parse(cnc['eng'], l)
+ ts = self.pgf.parse(l, cnc['eng'])
self.assertEqual(cnc['eng'],self.pgf.lin(l,ts[0]))
class TestTranslate(unittest.TestCase):
@@ -122,7 +121,7 @@ class TestTranslate(unittest.TestCase):
for i,l in self.langs:
for j,m in self.langs:
if i==j: continue
- parsed = self.pgf.parse(cnc[i],l)
+ parsed = self.pgf.parse(l, cnc[i])
assert len(parsed) == 1
lin = self.pgf.lin(m,parsed[0])
self.assertEqual(lin,cnc[j])
@@ -146,7 +145,7 @@ class TestUnapplyExpr(unittest.TestCase):
lg = 'eng'
lang = self.langs[lg]
for abs,cnc in self.samples:
- parsed = self.pgf.parse(cnc[lg],lang)
+ parsed = self.pgf.parse(lang, cnc[lg])
uparsed = self.deep_unapp(parsed[0])
self.assertEqual(abs,uparsed)
@@ -154,7 +153,7 @@ class TestUnapplyExpr(unittest.TestCase):
lg = 'eng'
lang = self.langs[lg]
cnc = self.samples[0][1]
- parsed = self.pgf.parse(cnc[lg],lang)
+ parsed = self.pgf.parse(lang, cnc[lg])
exp = parsed[0]
for t in 'Question Object Int'.split():
self.assertEqual(`exp.infer(self.pgf)`, t)
@@ -163,5 +162,26 @@ class TestUnapplyExpr(unittest.TestCase):
exp = uexp[1]
+class TestComplete(unittest.TestCase):
+ def setUp(self):
+ self.lexed = samples
+ self.pgf = gf.read_pgf('Query.pgf')
+ self.langs = dict([(lang2iso(l),l) for l in self.pgf.languages()])
+
+ def test_complete(self):
+ for (_,d) in self.lexed:
+ for l,text in d.items():
+ lang = self.langs[l]
+ for k in range(len(text)):
+ if text[k].isdigit() or text[k-1].isdigit(): # No completion for integer literals
+ continue
+ comps = self.pgf.complete(lang,text[:k])
+ self.assertNotEqual(comps, [],
+ msg="while completing '%s^%s'" % (text[:k],text[k:]))
+
+ self.assertTrue(any(w in text for w in comps),
+ msg="None of %s is in '%s'" % (comps,text))
+
+
if __name__ == '__main__':
unittest.main()