183 lines
6.6 KiB
Clojure
183 lines
6.6 KiB
Clojure
(ns emptyhead.repl.core
|
|
(:require [clojure.string :as str]
|
|
[emptyhead.thought.extend :as extend]
|
|
[emptyhead.thought.eval :as teval]
|
|
[emptyhead.idea.property :as prop]
|
|
[emptyhead.util.magic :as magic]
|
|
[emptyhead.idea.protocol :as prtc]
|
|
[emptyhead.idea.memtag :as memtag]
|
|
[emptyhead.thought.define :as def]
|
|
[emptyhead.thought.crud :as thought]
|
|
[emptyhead.lib.core]
|
|
[emptyhead.lib.context :as ctx]))
|
|
|
|
(defonce context (ctx/new-context))
|
|
(defn clear-context [] (set! context (ctx/new-context)))
|
|
|
|
(defn undot-operator [dotted-sym]
|
|
(mapv keyword (str/split (name dotted-sym) #"\.")))
|
|
|
|
(defn eval! [thought & [parent]]
|
|
(set! context (teval/execute! thought parent)))
|
|
|
|
(defn parse-command [cmd]
|
|
(cond
|
|
(= cmd '.BEGIN) (thought/register-thought! [:emptyhead :repl :begin-block])
|
|
(= cmd '.END) (thought/register-thought! [:emptyhead :repl :end-block])
|
|
|
|
(symbol? cmd) (thought/register-thought!
|
|
[:emptyhead :compose :append]
|
|
:data (thought/register-thought! (undot-operator cmd)))
|
|
|
|
:else (thought/register-thought!
|
|
[:emptyhead :compose :append]
|
|
:data (thought/register-thought! [:emptyhead :core :return] :data cmd))))
|
|
|
|
(def/define! [:emptyhead :repl :begin-block]
|
|
(fn [thought parent]
|
|
(let [ctx-creator (thought/register-thought! [:emptyhead :core :context :construct])
|
|
;; Create a new context with `parent` as parent.
|
|
[parent ctx] (->> parent (teval/execute! ctx-creator) thought/pop-stack)
|
|
ctx (prop/just-property ctx)
|
|
;; Make `parent` put the new context on its stack during its execution flow
|
|
;; TODO refactor
|
|
parent (teval/execute! (thought/register-thought!
|
|
[:emptyhead :compose :append]
|
|
:data (thought/register-thought! [:emptyhead :core :return]
|
|
:data (memtag/uid-of ctx)))
|
|
parent)]
|
|
|
|
;; NOTE `ctx` becomes the new `parent`!
|
|
[ctx
|
|
ctx])))
|
|
|
|
(def/define! [:emptyhead :repl :end-block]
|
|
;; Exiting context, so its parent becomes the new parent.
|
|
(fn [thought parent]
|
|
(let [parent-ctx (-> parent prtc/value :data :parent prop/just-property)]
|
|
[parent-ctx nil])))
|
|
|
|
(defn read [& input]
|
|
(map parse-command input))
|
|
|
|
(defn pprint-tos [thought]
|
|
(let [printer #(println (str "EMPTYHEAD> " %))
|
|
tos (-> thought prtc/value :return last)]
|
|
(-> tos printer)
|
|
tos))
|
|
|
|
(defn run [& input]
|
|
(clear-context)
|
|
(run! #(eval! % context) (apply read input))
|
|
(pprint-tos context)
|
|
(prtc/reference (teval/execute! context context)))
|
|
|
|
;; XXX an annoying class of bug is eg forgetting the return value here, which yields just
|
|
;; >No protocol method Idea.value defined for type null:
|
|
;; In this case originationg from [[emptyhead.thought.eval/execute!]]
|
|
;; Widely catching these errors earlier would be wise since we don't have
|
|
;; a good stack trace for this stuff
|
|
(def/define! [:emptyhead :debug :track]
|
|
(fn [thought parent]
|
|
(let [ctx (:_parent (prtc/value parent))]
|
|
(print "RUNNING " (thought/operator parent) " ON ")
|
|
(print "CONTEXT " ctx " WITH \n")
|
|
(print "STACK: ")
|
|
(print (:return ctx))
|
|
(print "\n\n")
|
|
[parent nil])))
|
|
|
|
(def sample (thought/register-thought! [:foo]))
|
|
|
|
;; XXX This has to have no :PRE-EXECUTE stage otherwise it will loop on itself ww
|
|
(defonce tracker (thought/register-thought! [:emptyhead :debug :track] :ext-stages [[:EXECUTE]]))
|
|
|
|
(defn enable-tracking []
|
|
(extend/register-extension! tracker [:PRE-EXECUTE]))
|
|
|
|
;; XXX accidentally registering one extension multiple times is a known footgun right now
|
|
;; and probably needs some feature to prevent this
|
|
;; (but mind that it shouldn't be *illegal*)
|
|
;; Another issue is just general... garbage collection, that's a big thing
|
|
(defn disable-tracking []
|
|
(map #(extend/remove-extension! % [:PRE-EXECUTE]) (extend/extensions-of-stage [:PRE-EXECUTE])))
|
|
|
|
(defn name-idea
|
|
[idea context name]
|
|
(prop/register-property! idea [:emptyhead :name name (prtc/reference context)]))
|
|
|
|
(defn resolve-name
|
|
[context name]
|
|
(prop/just-property [:emptyhead :name name (prtc/reference context)]))
|
|
|
|
(defn deref-name
|
|
[self name]
|
|
(or (and (= (thought/operator self) [:emptyhead :core :context])
|
|
(resolve-name self name))
|
|
(and (prtc/val-fn :_parent self)
|
|
(deref-name (prtc/val-fn :_parent self) name))))
|
|
|
|
(def/define! [:GRAB]
|
|
(fn [thought parent]
|
|
(let [arg-num (inc (or (get-in parent [:data :last-arg]) -1))]
|
|
[(assoc-in parent [:data :last-arg] arg-num)
|
|
(-> parent prtc/value :_parent prtc/value :return (#(nth % (- (count %) arg-num 1))))])))
|
|
|
|
(def/define! [:RETURN]
|
|
(fn [thought parent]
|
|
(let [[parent val] (thought/pop-stack parent)]
|
|
[(update-in parent [:data :proc-ret] (fnil conj []) val) nil])))
|
|
|
|
;; FIXME this wil bind two things to the same name if you name severa things the same
|
|
;; FIXME this needs to wrap data in an [:emptyhead :core :return] and then evaluate that on deref
|
|
(def/define! [:emptyhead :core :assign-name]
|
|
(fn [_ parent]
|
|
(let [[parent name] (thought/pop-stack parent)
|
|
[_ op] (thought/pop-stack parent)
|
|
exe (prop/just-property op)]
|
|
(extend/register-extension! exe (into [:_name] name))
|
|
[parent nil])))
|
|
|
|
;; FIXME this is a weird use of the extension system -- figure out how names should actually work!
|
|
;; (in light of the way extensions usually work, see e.g. [emptyhead.thought.extend/get-extensions]) comment
|
|
;; FIXME this needs to wrap data in an [:emptyhead :core :return] and then evaluate that on deref
|
|
(def/define! [:emptyhead :core :deref-name]
|
|
(fn [_ parent]
|
|
(let [[parent name] (thought/pop-stack parent)
|
|
deref (memtag/uid-of
|
|
(prop/just-property (magic/extension-prop (into [:_name] name))))]
|
|
[parent deref])))
|
|
|
|
(def tst
|
|
'(.BEGIN
|
|
"<> -- COMMENCE PROCEDURE -- <>"
|
|
emptyhead.io.print
|
|
|
|
GRAB emptyhead.io.print
|
|
GRAB emptyhead.io.print
|
|
|
|
"<> -- PROCEDURE COMPLETE -- <>"
|
|
|
|
42 RETURN
|
|
69 RETURN
|
|
.END
|
|
emptyhead.io.print
|
|
|
|
[:meme]
|
|
emptyhead.core.assign-name
|
|
|
|
"hello"
|
|
"goodbye"
|
|
[:meme]
|
|
emptyhead.core.deref-name
|
|
emptyhead.core.execute
|
|
emptyhead.core.pop ;; pop deref'd name
|
|
|
|
"\nReturn values of previous:\n"
|
|
emptyhead.io.print
|
|
emptyhead.core.pop
|
|
|
|
[:meme]
|
|
emptyhead.core.deref-name
|
|
emptyhead.core.execute))
|