Hash :
8fbe02c6
Author :
Thomas de Grivel
Date :
2023-06-19T15:36:04
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
;; cl-facts
;; Copyright 2011, 2023 Thomas de Grivel <thodg@kmx.io>
;;
;; Permission is hereby granted to use this software granted
;; the above copyright notice and this permission paragraph
;; are included in all copies and substantial portions of this
;; software.
;;
;; THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
;; PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
;; AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
;; THIS SOFTWARE.
(in-package :facts)
;; Bindings
(defun binding-p (sym)
(when (typep sym 'symbol)
(char= #\? (char (symbol-name sym) 0))))
(defun collect-bindings (form &optional bindings)
(typecase form
(null bindings)
(symbol (if (binding-p form)
(pushnew form bindings)
bindings))
(cons (collect-bindings (car form)
(collect-bindings (cdr form)
bindings)))
(t bindings)))
(defun gensym-bindings (bindings)
(mapcar (lambda (b)
(cons b (gensym (concatenate 'string
(string-upcase (subseq (string b) 1))
"-"))))
bindings))
;; Ordering of join patterns
(defun fact-binding-count (x)
(+ (if (binding-p (pop x)) 1 0)
(if (binding-p (pop x)) 1 0)
(if (binding-p (pop x)) 1 0)))
(defun pattern< (a b)
(< (fact-binding-count a)
(fact-binding-count b)))
(defun sort-bindings (pattern)
"Transforms ((?s ?p ?o) (?s x y)) into ((?s x y) (?s ?p ?o)). Huge optimization."
(sort pattern #'pattern<))
;; Fact specifications
(defun expand-positive-spec (spec)
(destructuring-bind (s p o &rest more-p-o) spec
(labels ((expand/po (p-o-list result)
(if (endp p-o-list)
result
(destructuring-bind (p o &rest list) p-o-list
(expand/po list (cons `(,s ,p ,o)
result))))))
(nreverse (expand/po more-p-o
(cons `(,s ,p ,o)
nil))))))
(defun expand-negative-spec (spec)
(destructuring-bind (not s p o &rest more-p-o) spec
(assert (eq :not not))
(labels ((expand/po (p-o-list result)
(if (endp p-o-list)
result
(destructuring-bind (p o &rest list) p-o-list
(expand/po list (cons `(:not ,s ,p ,o)
result))))))
(nreverse (expand/po more-p-o
(cons `(:not ,s ,p ,o)
nil))))))
(defun expand-spec (spec)
(declare (type sequence spec))
(let ((len (length spec)))
(if (zerop (mod (- len 3) 2))
(expand-positive-spec spec)
(expand-negative-spec spec))))
(defun expand-specs (specs)
"
Facts specification
For any values of subject S, predicate P, object O we can write a fact as a triple :
(S P O)
A join between multiple facts is written as a set of facts :
((S1 P1 O1) ... (Sn Pn On))
For more predicates and objects for the same subject we can also write :
((S P1 O1 ... Pn On))
which is equivalent to :
((S P1 O1) ... (S Pn On))
"
(mapcan #'expand-spec specs))