From b67e5b5e3db3d4810614cb7540081827cb6f467c Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Sun, 28 Aug 2022 20:30:25 +0300 Subject: [PATCH] DOC: Expand the manual section about Querying Prolog --- README.org | 95 ++++++++++++++++++++++++++++++++++++++++++++------ sweep-tests.el | 6 ++-- sweep.c | 33 +++++++++++++----- 3 files changed, 111 insertions(+), 23 deletions(-) diff --git a/README.org b/README.org index 9c65dad..415f821 100644 --- a/README.org +++ b/README.org @@ -89,21 +89,94 @@ The different parts of =sweep= are structured as follows: :CUSTOM_ID: querying-prolog :END: -=sweep= provides the Elisp function =sweep-open-query= for initiating -Prolog queries. To examine the results of the query, the function -=sweep-next-solution= is used. When no more solutions are available, or -when otherwise further solutions are not required, the query must be -closed with either =sweep-cut-query= or =sweep-close-query=. - #+FINDEX: sweep-open-query +=sweep= provides the Elisp function =sweep-open-query= for invoking Prolog +predicates. The invoked predicate must be of arity two and will be +called in mode =p(+In, -Out)=, i.e. the predicate should treat the first +argument as input and expect a variable for the second argument which +should be unified with the some output. This restriction is placed in +order to facilitate a natural calling convention between Elisp, a +functional language, and Prolog, a logical one. + +The =sweep-open-query= function takes four arguments, the first three +are strings which denote: +- The name of the Prolog context module from which to execute the + query, +- The name of the module in which the invoked predicate is defined, + and +- The name of the predicate to call. + +The fourth argument to =sweep-open-query= is converted into a Prolog +term and used as the first argument of the predicate (see [[Conversion +of Elisp objects to Prolog terms]]). + #+FINDEX: sweep-next-solution -#+FINDEX: sweep-cut-query -#+FINDEX: sweep-close-query +The function =sweep-next-solution= can be used to examine the results of +a query. If the query succeeded, =sweep-next-solution= returns a cons +cell whose =car= is either the symbol =!= when the success was +deterministic or =t= otherwise, and the =cdr= is the current value of the +second (output) Prolog argument converted to an Elisp object (see +[[Conversion of Prolog terms to Elisp objects]]). If the query failed, +=sweep-next-solution= returns =nil=. -As an example, we show an invocation of the non-deterministic -predicate =lists:permutation/2= from Elisp, which yields the number of -different permutations of the list =(1 2 3 4 5)=: +#+FINDEX: sweep-cut-query +#+FINDEX: sweep-close-query +=sweep= only executes one Prolog query at a given time, thus queries +opened with =sweep-open-query= need to be closed before other queries +can be opened. When no more solutions are available for the current +query (i.e. after =sweep-next-solution= returned =nil=), or when otherwise +further solutions are not of interest, the query must be closed with +either =sweep-cut-query= or =sweep-close-query=. Both of these functions +close the current query, but =sweep-close-query= also destroys any +Prolog bindings created by the query. + +** Conversion of Elisp objects to Prolog terms + +=sweep= converts Elisp objects into Prolog terms to allow the Elisp +programmers to specify arguments for Prolog predicates invocations (see +=sweep-open-query=). Seeing as some Elisp objects, like Elisp compiled +functions, wouldn't be as useful for a passing to Prolog as others, +=sweep= only converts Elisp objects of certain types to Prolog, namely +we convert /trees of strings and numbers/: + +- Elisp strings are converted to equivalent Prolog strings. +- Elisp integers are converted to equivalent Prolog integers. +- Elisp floats are converted to equivalent Prolog floats. +- The Elisp =nil= object is converted to the Prolog empty list =[]=. +- Elisp cons cells are converted to Prolog lists whose head and tail + are the Prolog representations of the =car= and the =cdr= of the cons. + +** Conversion of Prolog terms to Elisp objects + +=sweep= converts Prolog terms into Elisp object to allow efficient +processing of Prolog query results in Elisp (see =sweep-next-solution=). + +- Prolog strings are converted to equivalent Elisp strings. +- Prolog integers are converted to equivalent Elisp integers. +- Prolog floats are converted to equivalent Elisp floats. +- A Prolog atom =foo= is converted to a cons cell =(atom . "foo")=. +- The Prolog empty list =[]= is converted to the Elisp =nil= object. +- Prolog lists are converted to Elisp cons cells whose =car= and =cdr= are + the representations of the head and the tail of the list. +- Prolog compounds are converted to list whose first element is the + symbol =compound=. The second element is a string denoting the functor + name of the compound, and the rest of the elements are the arguments + of the compound in their Elisp representation. +- All other Prolog terms (variables, blobs and dicts) are currently + represented in Elisp only by their type: + + Prolog variables are converted to the symbol =variable=, + + Prolog blobs are converted to the symbol =blob=, and + + Prolog dicts are converted to the symbol =dict=. + +** Example - counting solutions for a Prolog predicate in Elisp + +As an example of using the =sweep= interface for executing Prolog +queries, we show an invocation of the non-deterministic predicate +=lists:permutation/2= from Elisp where we count the number of different +permutations of the list =(1 2 3 4 5)=: + +#+name: count-list-permutations #+begin_src emacs-lisp (sweep-open-query "user" "lists" "permutation" '(1 2 3 4 5)) (let ((num 0) diff --git a/sweep-tests.el b/sweep-tests.el index c2dc790..072aaf0 100644 --- a/sweep-tests.el +++ b/sweep-tests.el @@ -11,8 +11,8 @@ (should (equal (sweep-cut-query) t))) (ert-deftest system:=/2 () - "Tests calling the Prolog predicate permutation/2 from Elisp." - (should (equal (sweep-open-query "user" "system" "=" (list 1 2 3)) t)) - (should (equal (sweep-next-solution) (list '! 1 2 3))) + "Tests unifying Prolog terms with =/2 from Elisp." + (should (equal (sweep-open-query "user" "system" "=" (list 1 nil (list "foo" "bar") 3.14)) t)) + (should (equal (sweep-next-solution) (list '! 1 nil (list "foo" "bar") 3.14))) (should (equal (sweep-next-solution) nil)) (should (equal (sweep-cut-query) t))) diff --git a/sweep.c b/sweep.c index bccb1ad..02fd5dd 100644 --- a/sweep.c +++ b/sweep.c @@ -91,12 +91,21 @@ term_to_value_integer(emacs_env *eenv, term_t t) { emacs_value v = NULL; int64_t l = -1; if (PL_get_int64(t, &l)) { - v = eenv->make_integer(eenv, l); } return v; } +static emacs_value +term_to_value_float(emacs_env *eenv, term_t t) { + emacs_value v = NULL; + double l = -1; + if (PL_get_float(t, &l)) { + v = eenv->make_float(eenv, l); + } + return v; +} + emacs_value term_to_value_string(emacs_env *eenv, term_t t) { char * string = NULL; @@ -142,12 +151,6 @@ term_to_value_blob(emacs_env *env, term_t t) { return env->intern(env, "blob"); } -emacs_value -term_to_value_float(emacs_env *env, term_t t) { - (void)t; - return env->intern(env, "float"); -} - emacs_value term_to_value_compound(emacs_env *env, term_t t) { atom_t name = 0; @@ -213,7 +216,7 @@ term_to_value(emacs_env *env, term_t t) { case PL_BLOB: return term_to_value_blob(env, t); case PL_FLOAT: - return term_to_value_blob(env, t); + return term_to_value_float(env, t); default: /* ethrow(env, "Prolog to Elisp conversion failed"); */ /* return NULL; */ @@ -232,6 +235,12 @@ value_to_term_integer(emacs_env *env, emacs_value v, term_t t) { return PL_put_int64(t, l); } +int +value_to_term_float(emacs_env *env, emacs_value v, term_t t) { + double l = env->extract_float(env, v); + return PL_put_float(t, l); +} + int value_to_term_list(emacs_env *env, emacs_value v, term_t t) { int r = -1; @@ -260,6 +269,8 @@ value_to_term(emacs_env *env, emacs_value v, term_t t) { r = value_to_term_integer(env, v, t); } else if (env->eq(env, vt, env->intern(env, "cons"))) { r = value_to_term_list(env, v, t); + } else if (env->eq(env, vt, env->intern(env, "float"))) { + r = value_to_term_float(env, v, t); } else r = -1; } else r = PL_put_nil(t); @@ -347,10 +358,12 @@ sweep_open_query(emacs_env *env, ptrdiff_t nargs, emacs_value *args, void *data) char * c = NULL; char * f = NULL; term_t a = PL_new_term_refs(2); + emacs_value r = enil(env); (void)data; (void)nargs; + if (PL_current_query() != 0) { ethrow(env, "Prolog is already executing a query"); goto cleanup; @@ -379,12 +392,14 @@ sweep_open_query(emacs_env *env, ptrdiff_t nargs, emacs_value *args, void *data) o = a+1; + r = et(env); + cleanup: if (c != NULL) free(c); if (m != NULL) free(m); if (f != NULL) free(f); - return et(env); + return r; } static emacs_value -- 2.39.2