mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-04-09 04:59:31 -06:00
Added parser completions to py-bindings.
This commit is contained in:
@@ -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 ()
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user