]> git.eshelyaron.com Git - dict.git/commitdiff
DOC: Expand the manual section about Querying Prolog
authorEshel Yaron <me@eshelyaron.com>
Sun, 28 Aug 2022 17:30:25 +0000 (20:30 +0300)
committerEshel Yaron <me@eshelyaron.com>
Sun, 28 Aug 2022 17:30:25 +0000 (20:30 +0300)
README.org
sweep-tests.el
sweep.c

index 9c65dadfd909c29e5ad367d2764fda2ea2e6b008..415f821a81327a29128ab74584bc7d57983cac32 100644 (file)
@@ -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)
index c2dc790cabbb4e7e93345563fd8dffa714f8b94a..072aaf012799ac4706ca9a0e7fd56cf344135779 100644 (file)
@@ -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 bccb1ad40bbf31b13231292b7670eaad00c22b72..02fd5dd5f5291f8fb44dc5937440fa469878dcad 100644 (file)
--- 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