(ns emptyhead.thought.eval "Implements evaluation of thoughts." (:require [emptyhead.idea.protocol :as prtc] [emptyhead.thought.extend :as extend] [emptyhead.thought.crud :as thought] [emptyhead.util.logging :as logging] [emptyhead.thought.return :as return] [emptyhead.idea.property :as prop] [emptyhead.idea.crud :as idea] [emptyhead.util.magic :as magic])) (defn- impl! [thought & [parent]] (let [impl-idea (-> thought prtc/value thought/operator magic/thought-impl-prop prop/just-property)] (if-not impl-idea (logging/error (str "No implementation for thought `" (thought/operator thought) "`.") {:thought thought :parent parent :type :unimplemented-thought}) ((prtc/copy-fn :implementation impl-idea) thought parent)))) (def root-thought (thought/make-thought :root)) ;; at what point do returns get cleared? (defn execute! "Execute `thought` with `parent`, applying aspects to `thought` according to its :extension-stages. Returns (potentially modified) `parent`." [thought & [parent]] (loop [th (prtc/copy thought) parent (or parent root-thought)] (let [cur (first (thought/stages thought)) [extensions th] (extend/pop-stage th) ;; Execute extensions, potentially modifying th th (reduce #(execute! %2 %1) th extensions) ;; If it's time for `thought`'s implementation to run, do so, ;; potentially modifying `parent`. [parent return] (if (= cur [:thought (thought/operator th)]) (impl! th parent) parent) ;; Fold return value into `parent`. parent (return/with-return parent (thought/operator th) return)] ;; Recur if there's remaining aspects, otherwise return `parent`. (if (not-empty (thought/stages th)) (recur th parent) parent))))