(ns narjure.narsese (:require [instaparse.core :as i] [clojure.java.io :as io] [narjure.defaults :refer :all])) | |
(def bnf-file "narsese.bnf") | |
Loads narsese.bnf into instaparse | (def parser (i/parser (io/resource bnf-file) :auto-whitespace :standard)) |
(def copulas {"-->" 'inheritance "<->" 'similarity "{--" 'instance "--]" 'property "{-]" 'instance-property "==>" 'implication "=/>" 'predictive-implication "=|>" 'concurrent-implication "=\\>" 'retrospective-implication "<=>" 'equivalence "</>" 'predictive-equivalence "<|>" 'concurrent-equivalence}) | |
(def compound-terms {"{" 'ext-set "[" 'int-set "&" 'ext-intersection "|" 'int-intersection "-" 'ext-difference "~" 'int-difference "*" 'product "(" 'product "/" 'ext-image "\\" 'int-image "--" 'negation "||" 'disjunction "&&" 'conjunction "&/" 'sequential-events "&|" 'parallel-events}) | |
(defn get-compound-term [[_ operator-srt]] (compound-terms operator-srt)) | |
(def actions {"." :belief "?" :question}) | |
(def ^:dynamic *action* (atom nil)) (def ^:dynamic *lvars* (atom [])) (def ^:dynamic *truth* (atom [])) (def ^:dynamic *budget* (atom [])) | |
(defn keep-cat [fun col] (into [] (comp (mapcat fun) (filter (complement nil?))) col)) | |
(defn dispatcher [data] (if (string? data) :default (first data))) | |
(defmulti element dispatcher) | |
(defn get-copula [[_ [_ cop-symbol]]] (copulas cop-symbol)) | |
(defmethod element :sentence [[_ & data]] (let [filtered (group-by string? data)] (reset! *action* (actions (first (filtered true)))) (let [cols (filtered false) last-el (last cols)] (when (= :truth (first last-el)) (element last-el)) (element (first cols))))) | |
(defmethod element :statement [[_ & data]] (if-let [copula (get-copula data)] `[~copula ~@(keep-cat element data)] (keep-cat element data))) | |
(defmethod element :task [[_ & data]] `[~@(keep-cat element data)]) | |
looks strange but it is because of special syntax for negation --bird. | (defn get-comp-operator [second-el data] (let [first-el-type (get-in (vec data) [0 0])] (if (some #{(first second-el)} [:op-negation :op-int-set :op-ext-set :op-product]) second-el ((if (= :term first-el-type) second first) data)))) |
(defmethod element :compound-term [[_ second-el & data]] (let [comp-operator (get-comp-operator second-el data)] `[~(get-compound-term comp-operator) ~@(keep-cat element (remove string? data))])) | |
(def var-prefixes {"#" "d_" "?" "?"}) (defmethod element :variable [[_ type [_ v]]] (let [v (symbol (str (var-prefixes type) v))] (swap! *lvars* conj v) v)) | |
(defmethod element :word [[_ word]] (symbol word)) | |
(defmethod element :truth [[_ & data]] (mapv element data)) | |
(defmethod element :budget [[_ & data]] `[[~@(mapv element data)]]) | |
(defmacro double-element [n a] `(defmethod element ~n [[t# d#]] (let [d# (Double/parseDouble d#)] (swap! ~a conj d#) d#))) | |
(double-element :frequency *truth*) (double-element :confidence *truth*) (double-element :priority *budget*) (double-element :durability *budget*) (double-element :quality *budget*) | |
(defmethod element :task [[_ & data]] (when (= :budget (ffirst data)) (element (first data))) (element (last data))) | |
(defmethod element :term [[_ & data]] (when (seq? data) (keep element data))) | |
(defmethod element :default [_]) | |
Fetch terms from task. TODO check for variables in statemnts, ignore subterm if it contains variable | (defn terms [statement] (into #{statement} (rest statement))) |
(defn check-truth-value [v] (concat v (nthrest truth-value (count v)))) | |
(defn check-budget [v act] (let [budget (budgets act)] (concat v (nthrest budget (count v))))) | |
Parses a Narsese string into task ready for inference | (defn parse [narsese-str] (let [data (parser narsese-str)] (if-not (i/failure? data) (binding [*action* (atom nil) *lvars* (atom []) *truth* (atom []) *budget* (atom [])] (let [statement (element data) act @*action*] {:action act :lvars @*lvars* :truth (check-truth-value @*truth*) :budget (check-budget @*budget* act) :statement statement :terms (terms statement)})) data))) |