forked from GitHub/gf-core
Added parser completions to py-bindings.
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
{-# LANGUAGE ForeignFunctionInterface #-}
|
{-# LANGUAGE ForeignFunctionInterface #-}
|
||||||
-- GF Python bindings -- Jordi Saludes, upc.edu 2010
|
--
|
||||||
|
-- GF Python bindings
|
||||||
-- Jordi Saludes, upc.edu 2010
|
-- Jordi Saludes, upc.edu 2010
|
||||||
--
|
--
|
||||||
|
|
||||||
module PyGF where
|
module PyGF where
|
||||||
|
|
||||||
import PGF
|
import PGF
|
||||||
@@ -9,6 +11,8 @@ import Foreign
|
|||||||
import CString
|
import CString
|
||||||
import Foreign.C.Types
|
import Foreign.C.Types
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
|
import Data.Map (keys, (!))
|
||||||
|
import Data.Char (isSpace)
|
||||||
|
|
||||||
#include "pygf.h"
|
#include "pygf.h"
|
||||||
|
|
||||||
@@ -73,6 +77,7 @@ instance Storable Tree where
|
|||||||
deRefStablePtr sp
|
deRefStablePtr sp
|
||||||
-}
|
-}
|
||||||
|
|
||||||
|
|
||||||
foreign export ccall gf_freePGF :: Ptr PGF -> IO ()
|
foreign export ccall gf_freePGF :: Ptr PGF -> IO ()
|
||||||
foreign export ccall gf_freeType :: Ptr Type -> IO ()
|
foreign export ccall gf_freeType :: Ptr Type -> IO ()
|
||||||
foreign export ccall gf_freeLanguage :: Ptr Language -> IO ()
|
foreign export ccall gf_freeLanguage :: Ptr Language -> IO ()
|
||||||
@@ -141,6 +146,18 @@ listToPy mk ls = do
|
|||||||
poke pl l
|
poke pl l
|
||||||
pyl << pl
|
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 ()
|
-- foreign export ccall "gf_freeArray" free :: Ptr a -> IO ()
|
||||||
|
|
||||||
|
|
||||||
@@ -256,6 +273,29 @@ gf_functiontype ppgf pcid = do
|
|||||||
_ -> return nullPtr
|
_ -> 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 "newLang" pyLang :: IO (Ptr Language)
|
||||||
foreign import ccall "newPGF" pyPGF :: IO (Ptr PGF)
|
foreign import ccall "newPGF" pyPGF :: IO (Ptr PGF)
|
||||||
foreign import ccall "newTree" pyTree :: IO (Ptr Tree)
|
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 "newCId" pyCId :: IO (Ptr CId)
|
||||||
foreign import ccall "newExpr" pyExpr :: IO (Ptr Expr)
|
foreign import ccall "newExpr" pyExpr :: IO (Ptr Expr)
|
||||||
foreign import ccall "newList" pyList :: IO (Ptr ())
|
foreign import ccall "newList" pyList :: IO (Ptr ())
|
||||||
|
foreign import ccall "newString" pyString :: CString -> IO (Ptr ())
|
||||||
foreign import ccall "append" (<<) :: Ptr () -> Ptr a -> IO ()
|
foreign import ccall "append" (<<) :: Ptr () -> Ptr a -> IO ()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// GF Python bindings
|
// GF Python bindings
|
||||||
// Jordi Saludes, upc.edu 2010
|
// Jordi Saludes, upc.edu 2010, 2011
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
@@ -33,6 +33,7 @@ NEWGF(Tree,GF_Tree,TreeType,"gf.tree","gf tree")
|
|||||||
DEALLOCFN(CId_dealloc, CId, gf_freeCId, "freeCId")
|
DEALLOCFN(CId_dealloc, CId, gf_freeCId, "freeCId")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* PGF methods, constructor and destructor */
|
/* PGF methods, constructor and destructor */
|
||||||
|
|
||||||
DEALLOCFN(PGF_dealloc, PGF, gf_freePGF, "freePGF")
|
DEALLOCFN(PGF_dealloc, PGF, gf_freePGF, "freePGF")
|
||||||
@@ -123,17 +124,16 @@ parse(PGF *self, PyObject *args, PyObject *kws)
|
|||||||
Lang *lang;
|
Lang *lang;
|
||||||
gfType *cat = NULL;
|
gfType *cat = NULL;
|
||||||
char *lexed;
|
char *lexed;
|
||||||
static char *kwlist[] = {"lexed", "lang", "cat", NULL};
|
static char *kwlist[] = {"lang", "lexed", "cat", NULL};
|
||||||
if (!PyArg_ParseTupleAndKeywords(args, kws, "sO|O", kwlist,
|
if (!PyArg_ParseTupleAndKeywords(args, kws, "Os|O", kwlist, &lang, &lexed, &cat))
|
||||||
&lexed, &lang, &cat))
|
return NULL;
|
||||||
return NULL;
|
|
||||||
if (!checkType(self, &PGFType)) return NULL;
|
if (!checkType(self, &PGFType)) return NULL;
|
||||||
if (!checkType(lang, &LangType)) return NULL;
|
if (!checkType(lang, &LangType)) return NULL;
|
||||||
if (cat) {
|
if (cat) {
|
||||||
if (!checkType(cat, &gfTypeType)) return NULL;
|
if (!checkType(cat, &gfTypeType)) return NULL;
|
||||||
} else {
|
} else {
|
||||||
cat = gf_startCat(self);
|
cat = gf_startCat(self);
|
||||||
}
|
}
|
||||||
return gf_parse(self, lang, cat, lexed);
|
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[] = {
|
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."},
|
{"lin", (PyCFunction)linearize, METH_VARARGS,"Linearize tree."},
|
||||||
{"lang_code", (PyCFunction)languageCode, METH_VARARGS,"Get the language code."},
|
{"lang_code", (PyCFunction)languageCode, METH_VARARGS,"Get the language code."},
|
||||||
{"print_name", (PyCFunction)printName, METH_VARARGS,"Get the print name for a id."},
|
{"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."},
|
{"functions", (PyCFunction)gf_functions, METH_NOARGS,"Get all functions."},
|
||||||
{"abstract", (PyCFunction)abstractName, METH_NOARGS,"Get the module abstract name."},
|
{"abstract", (PyCFunction)abstractName, METH_NOARGS,"Get the module abstract name."},
|
||||||
{"languages", (PyCFunction)gf_languages, METH_NOARGS,"Get the module languages."},
|
{"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 */
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -281,12 +300,17 @@ initgf(void)
|
|||||||
if (PyType_Ready(&t) < 0) return;
|
if (PyType_Ready(&t) < 0) return;
|
||||||
|
|
||||||
READYTYPE(CIdType, CId_repr, CId_dealloc)
|
READYTYPE(CIdType, CId_repr, CId_dealloc)
|
||||||
|
|
||||||
PGFType.tp_methods = pgf_methods;
|
PGFType.tp_methods = pgf_methods;
|
||||||
READYTYPE(PGFType, pgf_repr, PGF_dealloc)
|
READYTYPE(PGFType, pgf_repr, PGF_dealloc)
|
||||||
|
|
||||||
READYTYPE(LangType, lang_repr, Lang_dealloc)
|
READYTYPE(LangType, lang_repr, Lang_dealloc)
|
||||||
|
|
||||||
READYTYPE(gfTypeType, gfType_repr, gfType_dealloc)
|
READYTYPE(gfTypeType, gfType_repr, gfType_dealloc)
|
||||||
ExprType.tp_methods = expr_methods;
|
|
||||||
|
ExprType.tp_methods = expr_methods;
|
||||||
READYTYPE(ExprType, expr_repr, expr_dealloc)
|
READYTYPE(ExprType, expr_repr, expr_dealloc)
|
||||||
|
|
||||||
TreeType.tp_methods = expr_methods; // Tree == Expr ?
|
TreeType.tp_methods = expr_methods; // Tree == Expr ?
|
||||||
READYTYPE(TreeType, tree_repr, Tree_dealloc)
|
READYTYPE(TreeType, tree_repr, Tree_dealloc)
|
||||||
|
|
||||||
@@ -311,4 +335,5 @@ PyModule_AddObject(m, "gf", (PyObject *)&t);
|
|||||||
/* List utilities to be imported by FFI */
|
/* List utilities to be imported by FFI */
|
||||||
|
|
||||||
inline PyObject* newList() { return PyList_New(0); }
|
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); }
|
inline void append(PyObject* l, PyObject* ob) { PyList_Append(l, ob); }
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
import gf
|
import gf
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
samples = [
|
samples = [
|
||||||
(['Odd', ['Number', 89]],
|
(['Odd', ['Number', 89]],
|
||||||
{'eng': "is 89 odd",
|
{'eng': "is 89 odd",
|
||||||
@@ -93,7 +92,7 @@ class TestParsing(unittest.TestCase):
|
|||||||
l = gf.read_language(self.lang)
|
l = gf.read_language(self.lang)
|
||||||
for abs,cnc in self.lexed:
|
for abs,cnc in self.lexed:
|
||||||
rabs = exp2str(abs)
|
rabs = exp2str(abs)
|
||||||
ps = pgf.parse(cnc['eng'], l)
|
ps = pgf.parse(l, cnc['eng'])
|
||||||
self.failUnless(ps)
|
self.failUnless(ps)
|
||||||
pt = rmprefix(ps[0])
|
pt = rmprefix(ps[0])
|
||||||
self.assertEqual(pt,rabs)
|
self.assertEqual(pt,rabs)
|
||||||
@@ -108,7 +107,7 @@ class TestLinearize(unittest.TestCase):
|
|||||||
def test_Linearize(self):
|
def test_Linearize(self):
|
||||||
l = self.lang
|
l = self.lang
|
||||||
for abs,cnc in self.samples:
|
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]))
|
self.assertEqual(cnc['eng'],self.pgf.lin(l,ts[0]))
|
||||||
|
|
||||||
class TestTranslate(unittest.TestCase):
|
class TestTranslate(unittest.TestCase):
|
||||||
@@ -122,7 +121,7 @@ class TestTranslate(unittest.TestCase):
|
|||||||
for i,l in self.langs:
|
for i,l in self.langs:
|
||||||
for j,m in self.langs:
|
for j,m in self.langs:
|
||||||
if i==j: continue
|
if i==j: continue
|
||||||
parsed = self.pgf.parse(cnc[i],l)
|
parsed = self.pgf.parse(l, cnc[i])
|
||||||
assert len(parsed) == 1
|
assert len(parsed) == 1
|
||||||
lin = self.pgf.lin(m,parsed[0])
|
lin = self.pgf.lin(m,parsed[0])
|
||||||
self.assertEqual(lin,cnc[j])
|
self.assertEqual(lin,cnc[j])
|
||||||
@@ -146,7 +145,7 @@ class TestUnapplyExpr(unittest.TestCase):
|
|||||||
lg = 'eng'
|
lg = 'eng'
|
||||||
lang = self.langs[lg]
|
lang = self.langs[lg]
|
||||||
for abs,cnc in self.samples:
|
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])
|
uparsed = self.deep_unapp(parsed[0])
|
||||||
self.assertEqual(abs,uparsed)
|
self.assertEqual(abs,uparsed)
|
||||||
|
|
||||||
@@ -154,7 +153,7 @@ class TestUnapplyExpr(unittest.TestCase):
|
|||||||
lg = 'eng'
|
lg = 'eng'
|
||||||
lang = self.langs[lg]
|
lang = self.langs[lg]
|
||||||
cnc = self.samples[0][1]
|
cnc = self.samples[0][1]
|
||||||
parsed = self.pgf.parse(cnc[lg],lang)
|
parsed = self.pgf.parse(lang, cnc[lg])
|
||||||
exp = parsed[0]
|
exp = parsed[0]
|
||||||
for t in 'Question Object Int'.split():
|
for t in 'Question Object Int'.split():
|
||||||
self.assertEqual(`exp.infer(self.pgf)`, t)
|
self.assertEqual(`exp.infer(self.pgf)`, t)
|
||||||
@@ -163,5 +162,26 @@ class TestUnapplyExpr(unittest.TestCase):
|
|||||||
exp = uexp[1]
|
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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user