Files
EMPTYHEAD/src/cljs/emptyhead/thought/eval.cljs
2025-02-06 19:47:55 +01:00

48 lines
1.9 KiB
Clojure

(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))))