diff --git a/src/pyswip/core.py b/src/pyswip/core.py index ab70825..6b61817 100644 --- a/src/pyswip/core.py +++ b/src/pyswip/core.py @@ -531,8 +531,8 @@ def check_and_call(*args): PL_version.restype = c_uint PL_VERSION = PL_version(PL_VERSION_SYSTEM) - if PL_VERSION < 80200: - raise Exception("swi-prolog >= 8.2.0 is required") + if PL_VERSION < 80400: + raise Exception("swi-prolog >= 8.4.0 is required") except AttributeError: raise Exception("swi-prolog version number could not be determined") @@ -548,103 +548,60 @@ def check_and_call(*args): # /* PL_unify_term( arguments */ -if PL_VERSION < 80200: - # constants (from SWI-Prolog.h) - # PL_unify_term() arguments - PL_VARIABLE = 1 # nothing - PL_ATOM = 2 # const char - PL_INTEGER = 3 # int - PL_FLOAT = 4 # double - PL_STRING = 5 # const char * - PL_TERM = 6 # - # PL_unify_term() - PL_FUNCTOR = 10 # functor_t, arg ... - PL_LIST = 11 # length, arg ... - PL_CHARS = 12 # const char * - PL_POINTER = 13 # void * - # /* PlArg::PlArg(text, type) */ - # define PL_CODE_LIST (14) /* [ascii...] */ - # define PL_CHAR_LIST (15) /* [h,e,l,l,o] */ - # define PL_BOOL (16) /* PL_set_feature() */ - # define PL_FUNCTOR_CHARS (17) /* PL_unify_term() */ - # define _PL_PREDICATE_INDICATOR (18) /* predicate_t (Procedure) */ - # define PL_SHORT (19) /* short */ - # define PL_INT (20) /* int */ - # define PL_LONG (21) /* long */ - # define PL_DOUBLE (22) /* double */ - # define PL_NCHARS (23) /* unsigned, const char * */ - # define PL_UTF8_CHARS (24) /* const char * */ - # define PL_UTF8_STRING (25) /* const char * */ - # define PL_INT64 (26) /* int64_t */ - # define PL_NUTF8_CHARS (27) /* unsigned, const char * */ - # define PL_NUTF8_CODES (29) /* unsigned, const char * */ - # define PL_NUTF8_STRING (30) /* unsigned, const char * */ - # define PL_NWCHARS (31) /* unsigned, const wchar_t * */ - # define PL_NWCODES (32) /* unsigned, const wchar_t * */ - # define PL_NWSTRING (33) /* unsigned, const wchar_t * */ - # define PL_MBCHARS (34) /* const char * */ - # define PL_MBCODES (35) /* const char * */ - # define PL_MBSTRING (36) /* const char * */ - - REP_ISO_LATIN_1 = 0x0000 # output representation - REP_UTF8 = 0x1000 - REP_MB = 0x2000 - -else: - PL_VARIABLE = 1 # nothing - PL_ATOM = 2 # const char * - PL_INTEGER = 3 # int - PL_RATIONAL = 4 # rational number - PL_FLOAT = 5 # double - PL_STRING = 6 # const char * - PL_TERM = 7 - - PL_NIL = 8 # The constant [] - PL_BLOB = 9 # non-atom blob - PL_LIST_PAIR = 10 # [_|_] term - - # # PL_unify_term( - PL_FUNCTOR = 11 # functor_t, arg ... - PL_LIST = 12 # length, arg ... - PL_CHARS = 13 # const char * - PL_POINTER = 14 # void * - # PlArg::PlArg(text, type - PL_CODE_LIST = 15 # [ascii...] - PL_CHAR_LIST = 16 # [h,e,l,l,o] - PL_BOOL = 17 # PL_set_prolog_flag( - PL_FUNCTOR_CHARS = 18 # PL_unify_term( - _PL_PREDICATE_INDICATOR = 19 # predicate_t= Procedure - PL_SHORT = 20 # short - PL_INT = 21 # int - PL_LONG = 22 # long - PL_DOUBLE = 23 # double - PL_NCHARS = 24 # size_t, const char * - PL_UTF8_CHARS = 25 # const char * - PL_UTF8_STRING = 26 # const char * - PL_INT64 = 27 # int64_t - PL_NUTF8_CHARS = 28 # size_t, const char * - PL_NUTF8_CODES = 29 # size_t, const char * - PL_NUTF8_STRING = 30 # size_t, const char * - PL_NWCHARS = 31 # size_t, const wchar_t * - PL_NWCODES = 32 # size_t, const wchar_t * - PL_NWSTRING = 33 # size_t, const wchar_t * - PL_MBCHARS = 34 # const char * - PL_MBCODES = 35 # const char * - PL_MBSTRING = 36 # const char * - PL_INTPTR = 37 # intptr_t - PL_CHAR = 38 # int - PL_CODE = 39 # int - PL_BYTE = 40 # int - # PL_skip_list( - PL_PARTIAL_LIST = 41 # a partial list - PL_CYCLIC_TERM = 42 # a cyclic list/term - PL_NOT_A_LIST = 43 # Object is not a list - # dicts - PL_DICT = 44 - - REP_ISO_LATIN_1 = 0x0000 # output representation - REP_UTF8 = 0x00100000 - REP_MB = 0x00200000 +PL_VARIABLE = 1 # nothing +PL_ATOM = 2 # const char * +PL_INTEGER = 3 # int +PL_RATIONAL = 4 # rational number +PL_FLOAT = 5 # double +PL_STRING = 6 # const char * +PL_TERM = 7 + +PL_NIL = 8 # The constant [] +PL_BLOB = 9 # non-atom blob +PL_LIST_PAIR = 10 # [_|_] term + +# # PL_unify_term( +PL_FUNCTOR = 11 # functor_t, arg ... +PL_LIST = 12 # length, arg ... +PL_CHARS = 13 # const char * +PL_POINTER = 14 # void * +# PlArg::PlArg(text, type +PL_CODE_LIST = 15 # [ascii...] +PL_CHAR_LIST = 16 # [h,e,l,l,o] +PL_BOOL = 17 # PL_set_prolog_flag( +PL_FUNCTOR_CHARS = 18 # PL_unify_term( +_PL_PREDICATE_INDICATOR = 19 # predicate_t= Procedure +PL_SHORT = 20 # short +PL_INT = 21 # int +PL_LONG = 22 # long +PL_DOUBLE = 23 # double +PL_NCHARS = 24 # size_t, const char * +PL_UTF8_CHARS = 25 # const char * +PL_UTF8_STRING = 26 # const char * +PL_INT64 = 27 # int64_t +PL_NUTF8_CHARS = 28 # size_t, const char * +PL_NUTF8_CODES = 29 # size_t, const char * +PL_NUTF8_STRING = 30 # size_t, const char * +PL_NWCHARS = 31 # size_t, const wchar_t * +PL_NWCODES = 32 # size_t, const wchar_t * +PL_NWSTRING = 33 # size_t, const wchar_t * +PL_MBCHARS = 34 # const char * +PL_MBCODES = 35 # const char * +PL_MBSTRING = 36 # const char * +PL_INTPTR = 37 # intptr_t +PL_CHAR = 38 # int +PL_CODE = 39 # int +PL_BYTE = 40 # int +# PL_skip_list( +PL_PARTIAL_LIST = 41 # a partial list +PL_CYCLIC_TERM = 42 # a cyclic list/term +PL_NOT_A_LIST = 43 # Object is not a list +# dicts +PL_DICT = 44 + +REP_ISO_LATIN_1 = 0x0000 # output representation +REP_UTF8 = 0x00100000 +REP_MB = 0x00200000 # /******************************** # * NON-DETERMINISTIC CALL/RETURN * @@ -890,12 +847,6 @@ def PL_STRINGS_MARK(): PL_get_string_chars = _lib.PL_get_string PL_get_string_chars.argtypes = [term_t, POINTER(c_char_p), c_int_p] -PL_get_chars = _lib.PL_get_chars # FIXME: -PL_get_chars.argtypes = [term_t, POINTER(c_char_p), c_uint] -PL_get_chars.restype = c_int - -PL_get_chars = check_strings(None, 1)(PL_get_chars) - PL_get_integer = _lib.PL_get_integer PL_get_integer.argtypes = [term_t, POINTER(c_int)] PL_get_integer.restype = c_int @@ -943,7 +894,6 @@ def PL_STRINGS_MARK(): PL_atom_chars.restype = c_char_p PL_atom_wchars = _lib.PL_atom_wchars -PL_atom_wchars.argtypes = [atom_t, POINTER(c_size_t)] PL_atom_wchars.restype = c_wchar_p PL_predicate = _lib.PL_predicate @@ -973,6 +923,7 @@ def PL_STRINGS_MARK(): PL_get_list.restype = c_int PL_get_chars = _lib.PL_get_chars # FIXME +PL_get_wchars = _lib.PL_get_wchars PL_close_query = _lib.PL_close_query PL_close_query.argtypes = [qid_t] @@ -1012,10 +963,6 @@ def PL_STRINGS_MARK(): PL_unify_atom.argtypes = [term_t, atom_t] PL_unify_atom.restype = c_int -PL_unify_atom_chars = _lib.PL_unify_atom_chars -PL_unify_atom_chars.argtypes = [term_t, c_char_p] -PL_unify_atom_chars.restype = c_int - PL_unify_string_chars = _lib.PL_unify_string_chars PL_unify_string_chars.argtypes = [term_t, c_char_p] PL_unify_string_chars.restype = c_void_p @@ -1174,8 +1121,6 @@ def PL_STRINGS_MARK(): PL_is_initialised = _lib.PL_is_initialised intptr_t = c_long -ssize_t = intptr_t -wint_t = c_uint PL_thread_self = _lib.PL_thread_self PL_thread_self.restype = c_int diff --git a/src/pyswip/easy.py b/src/pyswip/easy.py index a1470ae..95e19cf 100644 --- a/src/pyswip/easy.py +++ b/src/pyswip/easy.py @@ -26,6 +26,7 @@ PL_register_atom, PL_atom_wchars, PL_get_atom, + PL_get_wchars, PL_unregister_atom, PL_new_term_ref, PL_compare, @@ -72,6 +73,7 @@ PL_STRING, PL_INTEGER, PL_FLOAT, + PL_NIL, PL_Q_NODEBUG, PL_Q_CATCH_EXCEPTION, PL_FA_NONDETERMINISTIC, @@ -89,6 +91,7 @@ atom_t, create_string_buffer, c_char_p, + c_wchar_p, functor_t, c_int, c_long, @@ -120,7 +123,7 @@ def __init__(self, expected, got): Exception.__init__(self, msg) -class Atom(object): +class Atom: __slots__ = "handle", "chars" def __init__(self, handleOrChars, chars=None): @@ -306,7 +309,7 @@ def __hash__(self): return self.handle -class Functor(object): +class Functor: __slots__ = "handle", "name", "arity", "args", "__value", "a0" func = {} @@ -439,8 +442,9 @@ def putList(l, ls): # noqa: E741 def getAtomChars(t): """If t is an atom, return it as a string, otherwise raise InvalidTypeError.""" - s = c_char_p() - if PL_get_chars(t, byref(s), CVT_ATOM | REP_UTF8): + s = c_wchar_p() + ln = c_size_t() + if PL_get_wchars(t, byref(ln), byref(s), CVT_ATOM | REP_UTF8): return s.value else: raise InvalidTypeError("atom") @@ -497,6 +501,8 @@ def getTerm(t): p = PL_term_type(t) if p < PL_TERM: res = _getterm_router[p](t) + elif p == PL_NIL: + return [] elif PL_is_list(t): res = getList(t) elif p == PL_DICT: diff --git a/src/pyswip/errors.py b/src/pyswip/errors.py new file mode 100644 index 0000000..79d38bc --- /dev/null +++ b/src/pyswip/errors.py @@ -0,0 +1,14 @@ + +__all__ = "PrologError", "NestedQueryError" + +class PrologError(Exception): + pass + + +class NestedQueryError(PrologError): + """ + SWI-Prolog does not accept nested queries, that is, opening a query while the previous one was not closed. + As this error may be somewhat difficult to debug in foreign code, it is automatically treated inside PySwip + """ + + pass diff --git a/src/pyswip/prolog.py b/src/pyswip/prolog.py index 0c87c03..977215c 100644 --- a/src/pyswip/prolog.py +++ b/src/pyswip/prolog.py @@ -59,6 +59,7 @@ term_t, control_t, ) +from pyswip.errors import PrologError, NestedQueryError __all__ = "PrologError", "NestedQueryError", "Prolog" @@ -67,19 +68,6 @@ RE_PLACEHOLDER = re.compile(r"%p") -class PrologError(Exception): - pass - - -class NestedQueryError(PrologError): - """ - SWI-Prolog does not accept nested queries, that is, opening a query while the previous one was not closed. - As this error may be somewhat difficult to debug in foreign code, it is automatically treated inside PySwip - """ - - pass - - def __initialize(): args = [] args.append("./") diff --git a/tests/105.pl b/tests/105.pl new file mode 100644 index 0000000..140bcdd --- /dev/null +++ b/tests/105.pl @@ -0,0 +1 @@ +Prolog file with error \ No newline at end of file diff --git a/tests/139.pl b/tests/139.pl new file mode 100644 index 0000000..0e3ef90 --- /dev/null +++ b/tests/139.pl @@ -0,0 +1,4 @@ +:-op(800, fx, ¬). +:-op(802, xfy, ∨). + +m_Proposition_Binary_x_y(X ∨ Y, X, Y). \ No newline at end of file diff --git a/tests/test_issues.py b/tests/test_issues.py index 411360e..eb1e335 100644 --- a/tests/test_issues.py +++ b/tests/test_issues.py @@ -29,6 +29,8 @@ import sys import unittest +import pytest + class TestIssues(unittest.TestCase): """Each test method is named after the issue it is testing. The docstring @@ -290,3 +292,25 @@ def test_functor_return(self): soln = [s["Y"] for s in p.query("friend(john,Y), father(Y,kur)", maxresult=1)] self.assertEqual(soln[0], "son(miki)") + + @pytest.mark.skip("Skip until v0.3.3") + def test_issue_141(self): + from pyswip import Prolog + fixture = [ + {"X": 1, "C": "x"}, + {"X": 2, "C": "y"}, + ] + ls = list(Prolog.query("X = point{x:1,y:2}.C.")) + self.assertEqual(fixture, ls) + + @pytest.mark.skip("Skip until v0.3.3") + def test_issue_139(self): + from pyswip import Prolog + Prolog.consult("139.pl", relative_to=__file__) + list(Prolog.query(f"m_Proposition_Binary_x_y(∨(¬(p), q), A, B).")) + + @pytest.mark.skip("Skip until v0.3.3") + def test_issue_105(self): + from pyswip import Prolog + self.assertRaises(Exception, lambda: Prolog.consult("105.pl", catcherrors=False)) +