(ns nal.deriver.backward-rules
  (:require [nal.deriver.key-path :refer [rule-path]]
            [clojure.string :as s]))

Return true if rule allows backward inference.

http://pastebin.com/3zLX7rPx

(defn allow-backward?
  [{:keys [conclusions]}]
  (some #{:allow-backward} (:post (first conclusions))))
(defn has-prefix? [prefix cond]
  (when (keyword? cond)
    (s/starts-with? (str cond) prefix)))
(defn not-equal? [cond]
  (when (coll? cond)
    (= :!= (first cond))))
(defn check-not-equal [pre conclusion]
  (if (some not-equal? pre)
    (let [pre (remove not-equal? pre)]
      (if (and (coll? conclusion) (= 3 (count conclusion)))
        (let [[_ t1 t2] conclusion]
          (conj pre (list :!= t1 t2)))
        pre))
    pre))

If rule allows backward inference it will be expanded to three rules, where first one is the rule itself, and rest rules will be generated by swapping conclusion with every premise.

(defn expand-backward-rules
  [{:keys [p1 p2 conclusions pre] :as rule}]
  (mapcat (fn [{:keys [conclusion post] :as fc}]
            (let [post (reduce #(remove (partial has-prefix? %2) %1)
                               post [":t/" ":d/"])]
              (conj (map
                      (fn [r] (update r :pre conj :question?))
                      [(assoc rule :conclusions [(assoc fc :post post)])
                       (assoc rule :p1 conclusion
                                   :conclusions [{:conclusion p1
                                                  :post       post}]
                                   :full-path (rule-path conclusion p2)
                                   :pre (check-not-equal pre p1))
                       (assoc rule :p2 conclusion
                                   :conclusions [{:conclusion p2
                                                  :post       post}]
                                   :full-path (rule-path p1 conclusion)
                                   :pre (check-not-equal pre p2))])
                    rule)))
          conclusions))