 ;   :needs {:integer 2}
 ;   :makes {:integer 1}
 ;   :tags [:arithmetic :core]
-;     (consume-as :int1 :integer)
-;     (consume-as :int2 :integer)
-;     (send-to :integer [:int1 :int2] %(+ %1 %2))
+;     (consume :integer :as :int1)
+;     (consume :integer :as :int2)
+;     (place :integer [:int1 :int2] %(+ %1 %2))
 ;     )
 
-
-; ;; expands to a function something like
-
-; (defn integer_add
-;   "adds two :integers"
-;   [interpreter]
-;   (-> interpreter 
-;     (consume-as int1 :integer)
-;     (consume-as int2 :integer)
-;     (send-to :integer (+ int1 int2))))
-
-; ;;
 
 ; (def-pushinstruction
 ;   boolean-flush

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


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


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


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


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


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


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

;; now there's another possibility that strikes me:
;; we could use a syntax like (consume % :integer :as :int1)
;; and that would permit      (consume % :integer)
;; which might put the consumed item in a little vector stack
;;
;; why?
;; because then integer-add might be
;;   -> interpreter 
;;      (consume :integer)
;;      (consume :integer)
;;      #(+ %1 %2)           (which would use the stack as args)
;; but that might be much harder to read :/


;; a simpler version yet (using an invisible stack inside the Interpreter itself, maybe?):
;; (grab :integer)
;; (grab :integer)
;; (place :integer #(+ %1 %2))




; (def-pushinstruction

;; 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 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 simple steps.
;; Everything would be assumed to happen inside one interpreter, and the interpreter
;; (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....


(def six-ints (make-interpreter :stacks {:integer '(6 5 4 3 2 1)}))


(defrecord PushDslState [interpreter locals])

