 ; - `replace-stack [stackname new-stack]`
 ;    replace the indicated stack with a new list
 ;    raise an exception if it's undefined
+; - `calculate [fn :as local]
+;    save the result of `fn` under key `local`
 
 ;; And I THINK that supports all defined Push instructions I've ever seen....
 

  ([interpreter locals] (->PushDslState interpreter locals)))


(defn consume
  [dslstep stack & {:keys [as]}]
  (let [interpreter (:interpreter dslstep)
        old-stack (get-stack interpreter stack)
        old-locals (:locals dslstep)]
    (if (empty? old-stack)
      (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

;;
; - `count-of [stackname :as local]`
;    store the number of items in `stackname` under key `local`
;    raise an Exception if the stack doesn't exist
; - `consume [stackname]`
;    pop an item (and discard it) from `stackname`
;    raise an Exception if it's empty or undefined
