 ;; This is a design spike. That means it's NOT TO BE USED.
 ;; IT'S A SKETCH.
 
-;; So I'm thinking about each Push instruction as a
-;; "transaction" involving a single Interpreter passing in and out.
+;; So I'm thinking about each Push instruction as a cascade of individual
+;; atomic transformations, using Clojure's thread-first macro to apply
+;; a sequence of DSL functions.
 
-;; The DSL for Push instructions could be a series of threaded (->) steps.
+;; The DSL for Push instructions could be a series of simple steps.
 ;; Everything would be assumed to happen inside one interpreter, and the interpreter
-;; (and some kind of local vars) would be passed step-to-step.
-;; - `count-of` would return the number of items of the named stack
-;; + `consume-as` would pop an item from the named stack and assign it to a (local) symbol
-;; - `consume-nth-as` would extract a numbered item from the stack and record it
-;; - `consume-stack-as` would grab the entire list as a single (local) symbol
-;; - `record-as` would save the current top item on the named stack
-;; - `record-nth-as` would save the nth item on the named stack
-;; - `discard` would pop an item from the named stack
-;; - `discard-stack` would empty the named stack
-;; + `send-to` would push the indicated value to that stack
-;;   (using the router and any filters found there)
-;; - `replace-stack` would replace the indicated stack with a new list
+;; (and some store of local scratch vars) would be passed step-to-step.
+;;
+; - `count-of [stackname :as local]`
+;    store the number of items in `stackname` under key `local`
+; - `consume [stackname]`
+;    pop an item (and discard it) from `stackname`
+;    raise an Exception if it's empty or undefined
+; - `consume [stackname :as local]`
+;    pop an item from `stackname` and store under key `local`
+;    raise an Exception if it's empty or undefined
+; - `consume [stackname :at where :as local]`
+;    delete an item from `stackname` at position `where` and store under key `local`
+;    raise an Exception if it's empty or undefined
+; - `consume-stack [stackname]`
+;    clear the named stack
+;    raise an Exception if it's undefined
+; - `consume-stack [stackname :as local]`
+;    save the entire stack into `local` and clear it
+;    raise an Exception if it's undefined
+; - `remember [stackname :as local]`
+;    store top item from `stackname` under key `local`
+;    raise an Exception if it's empty or undefined
+; - `remember [stackname :at where :as local]`
+;    store item in `stackname` at position `where` under key `local`
+;    raise an Exception if it's empty or undefined
+; - `remember-stack [stackname :as local]`
+;    save the entire stack to the `local`
+;    raise an Exception if it's undefined
+; - `place [stackname item]`
+;    push the item to the named stack
+;    raise an Exception if it's undefined
+; - `replace-stack [stackname new-stack]`
+;    replace the indicated stack with a new list
+;    raise an exception if it's undefined
 
-;; And I THINK that supports all defined Push instructions I've ever seen.
+;; And I THINK that supports all defined Push instructions I've ever seen....
 
 
 (def six-ints (make-interpreter :stacks {:integer '(6 5 4 3 2 1)}))
 
-
-(defn pop-item
-  [interpreter stack]
-  (let [old-stack (get-stack interpreter stack)]
-    (if (empty? old-stack)
-      [interpreter nil]
-      [(set-stack interpreter stack (pop old-stack)) (first old-stack)]
-    )))
-
-
-(fact "pop-item returns a vector of the item (or nil) and the changed interpreter"
-  (first (pop-item six-ints :integer)) => (set-stack six-ints :integer '(5 4 3 2 1))
-  (last  (pop-item six-ints :integer)) => 6
-  )
 
 (defrecord PushDslState [interpreter locals])
 

;   :needs {:boolean 0}
;   :tags [:combinator :core]
;   :transaction
;     (discard-stack :boolean)
;     )

; ;; expands to a function something like

; (defn boolean_flush
;   "empties the :boolean stack"
;   [interpreter]
;   (-> interpreter (discard-stack :boolean))
;   )


; (def-pushinstruction

      (throw (Exception. (str "Push DSL Error: " stack " is empty")))
      (make-PushDslState
        (set-stack interpreter stack (pop old-stack))
        (assoc old-locals as (first old-stack)))
    )))


(def sixier (make-PushDslState six-ints))


(fact "consume returns a new PushDslState with a changed :interpreter and new locals"
  (:locals (consume sixier :integer :as :int1)) => {:int1 6}
  (get-stack (:interpreter (consume sixier :integer :as :int1)) :integer) => '(5 4 3 2 1))


(fact "consume throws an exception if the stack is empty"
  (:locals (consume sixier :boolean :as :nope)) => (throws #"Push DSL Error:"))


(fact "consume can be thread-firsted"
  (let [two-popped (->  sixier
                        (consume :integer :as :int1)
                        (consume :integer :as :int2))]
    (:locals two-popped) => {:int1 6, :int2 5}
    (get-stack (:interpreter two-popped) :integer) => '(4 3 2 1)))


(defn place
  [dslstep stack args function]
  (let [result (apply function (map (:locals dslstep) args))
        interpreter (:interpreter dslstep)]
    (assoc dslstep :interpreter (push-item interpreter stack result))))


(fact "`place` will apply the specified inline function to the dslstate locals named"
  (let [integer-add (-> sixier
                        (consume :integer :as :int1)
                        (consume :integer :as :int2)
                        (place :integer [:int1 :int2] #(+ %1 %2)))]
  (get-stack (:interpreter integer-add) :integer) => '(11 4 3 2 1))) ;; 


; (def-pushinstruction
