Added parser completions to py-bindings.

This commit is contained in:
jordi.saludes
2011-12-07 18:09:33 +00:00
parent 5bbd1d6f73
commit 550cbe764c
3 changed files with 102 additions and 16 deletions

View File

@@ -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 ()

View File

@@ -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); }

View File

@@ -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()