Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

No Support for Dictionaries #50

Open
zhanwenchen opened this issue May 24, 2019 · 1 comment
Open

No Support for Dictionaries #50

zhanwenchen opened this issue May 24, 2019 · 1 comment
Milestone

Comments

@zhanwenchen
Copy link

from pyswip import Prolog
prolog = Prolog()
list(prolog.query('Customer = customer{age: 26, name: jodie}'))

The output is

[{'Customer': 'dict(customer, jodie, name, 26, age)'}]

This behavior does not match Prolog's. In the SWI-Prolog terminal, the query

Customer = customer{age: 26, name: jodie}.

yields the result

Customer = customer{age:26, name:jodie}.
@zhanwenchen zhanwenchen changed the title Can't Query Dictionaries No Support for Dictionaries May 24, 2019
@allComputableThings
Copy link

allComputableThings commented May 16, 2020

I have a monkey-patch for dicts deserialization. Replace these functions in easy.py

def getTerm(t, functor_to_constructor=None):
    """
    :param t:
    :param functor_to_constructor:
    :return:
    """
    if t is None:
        return None
    p = PL_term_type(t)
    if p < PL_TERM:
        res = _getterm_router[p](t)
    elif PL_is_list(t):
        res = getList(t, functor_to_constructor=functor_to_constructor,
                      visited=visited)
    elif p == PL_DICT:
        # assert PL_is_functor(t)
        res = getDict(t
                      , functor_to_constructor=functor_to_constructor)

    else:
        res = getFunctor(t, functor_to_constructor=functor_to_constructor)
    return res

def getDict(term, functor_to_constructor=None):
    """
    Return t as a list.
    """

    if isinstance(term, Term):
        term = term.handle
    elif not isinstance(term, (c_void_p, int)):
        raise ArgumentTypeError((str(Term), str(int)), str(type(term)))

    f = functor_t()
    if PL_get_functor(term, byref(f)):
        # get args
        args = []
        arity = PL_functor_arity(f.value)
        # let's have all args be consecutive
        a0 = PL_new_term_refs(arity)
        for i, a in enumerate(range(1, arity + 1)):
            if PL_get_arg(a, term, a0 + i):
                args.append(getTerm(a0 + i,
                                    functor_to_constructor=functor_to_constructor))
            else:
                raise Exception("Missing arg")

        def pairwise(t):
            it = iter(t)
            return zip(it,it)

        tag = args[0]  # Dict Tag. Looks something like a class name.

        d = {k.value:v
             for v, k in pairwise(args[1:])}
        if functor_to_constructor is not None \
                and tag.value in functor_to_constructor:
            # Treat the tag as a class name and the keys
            # as named constructor parameters.
            return functor_to_constructor[tag.value](**d)

        return d
    else:
        res = getFunctor(term, functor_to_constructor=functor_to_constructor)
        return res

... parsing the output is straightforward.

However, the bigger problems is that dict seem to misbehave with the .query call, which pyswip implements as:

pyrun(GoalString,BindingList) :- 
      (atom_chars(A,GoalString),
      atom_to_term(A,Goal,BindingList),
      call(Goal))).

Asserting this rountine in swipl, we can test it with dicts without data-marshalling concerns:

?- pyrun("fact(X)",BindingList).    
BindingList = ['X'=123].

For the dict query we get:

?- pyrun("A = point{x:1,y:2}.x.", BindingList).
BindingList = ['A'=point{x:1, y:2}.x].

or more verbosely:

?- atom_chars(A__,"X=point{x:1, y:2}.x"), atom_to_term(A__,Goal,BindingList), call(Goal).
A__ = 'X=point{x:1, y:2}.x',
Goal =  (point{x:1, y:2}.x=point{x:1, y:2}.x),
BindingList = ['X'=point{x:1, y:2}.x].

The vanilla swipl query without pyrun should be:

?- A = point{x:1,y:2}.x
A = 1.

so pyrun has changed the query somewhere and caused A not to be bound to 1 in the goal. :-(

Can anyone suggest a fix?

@yuce yuce added this to the v0.3.3 milestone Oct 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants