 
 (defrecord Cursor [path value])
 
-(defn run-pipe [program node inputs output stack]
+(defn run-pipe [id->node node inputs output stack]
   (let [vars #js {}
         calls #js []
         frame #js {:id (:id node) :vars vars :calls calls}]

          "c+1" ["c" "one"] (cljs-ref +)
          "nil" ["c" "c+1"] replace)})

(defn run-example [example input]
  (let [stack #js []
        program (into {} (for [pipe example] [(:id pipe) pipe]))
        output (atom input)]
    (try
      [(run-pipe program (get program "root") [(Cursor. [] input)] output stack) @output (aget stack 0)]
      (catch :default e [e @output (aget stack 0)]))))

(run-example example-b {"a" 1 "b" 2})
(run-example example-b {"a" 1 "c" 2})

      (aset vars id value))
    (doseq [{:keys [id inputs node]} (:nodes node)]
      (assert (not= :pipe (:type node)) "No inline pipes")
      (aset vars id (run-node id->node node (map #(aget vars %) inputs) output calls))) ; note calls, not stack
    (let [result (aget vars (-> node :nodes last :id))]
      (aset frame "result" result) ; return
      result)))

(defn run-node [id->node node inputs output stack]
  (case (:type node)
    :return (run-return id->node node inputs output stack)
    :data (run-data id->node node inputs output stack)
    :ref (run-ref id->node node inputs output stack)
    :match (run-match id->node node inputs output stack)
    :replace (run-replace id->node node inputs output stack)
    :output (run-output id->node node inputs output stack)))

(defn run-return [id->node node inputs output stack]
  (assert (= 1 (count inputs)))
  (first inputs))

(defn run-data [id->node node inputs output stack]
  (assert (empty? inputs))
  (Cursor. [] (run-data* id->node node inputs output stack)))

(defn run-data* [id->node node inputs output stack]
  (case (:kind node)
    :value (:value node)
    :vector (into []
                  (for [value (:value node)]
                    (run-data* id->node value inputs output stack)))
    :map (into {}
               (for [[key value] (:value node)]
                 [(run-data* id->node key inputs output stack) (run-data* id->node value inputs output stack)]))))

(defn run-ref [id->node node inputs output stack]
  (case (:kind node)
    :cljs (Cursor. [] (apply (:fn node) (map :value inputs)))
    :pipe (run-pipe id->node (get id->node (:id node)) inputs output stack)))

(defrecord MatchFailure [])

(defn check [bool]
  (when-not bool (throw (MatchFailure.))))

(defn run-match [id->node node inputs output stack]
  (assert (= 1 (count inputs)))
  (let [input (first inputs)]
    (loop [branches (:branches node)]
      (if-let [[{:keys [pattern inputs node]} & branches] (seq branches)]
        (try
          (let [vars #js {}]
            (run-match-pattern id->node pattern (:path input) (:value input) output stack vars)
            (run-node id->node node (map #(aget vars %) inputs) output stack))
          (catch MatchFailure _
            (recur branches)))
        (throw (MatchFailure.))))))

(defn run-match-pattern [id->node pattern input-path input-value output stack vars]
  (condp = (:type pattern)
    :match/any nil
    :match/bind (do (aset vars (:var pattern) (Cursor. input-path input-value))
                  (run-match-pattern id->node (:pattern pattern) input-path input-value output stack vars))
    :data (case (:kind pattern)
            :value (check (= input-value (:value pattern)))
            :vector (let [value (:value pattern)]
                      (check (vector? input-value))
                      (check (= (count input-value) (count value)))
                      (dotimes [i (count value)]
                        (run-match-pattern id->node (nth value i) (conj input-path i) (nth input-value i) output stack vars)))
            :map (let [value (:value pattern)]
                   (check (map? input-value))
                   (doseq [key-pattern (keys value)]
                     (let [key (:value key-pattern)]
                       (check (contains? input-value key))
                       (run-match-pattern id->node (get value key-pattern) (conj input-path key) (get input-value key) output stack vars)))))
    (check (:value (run-node id->node pattern [(Cursor. input-path input-value)] output stack)))))

(defn run-replace [id->node node inputs output stack]
  (swap! output assoc-in (:path (first inputs)) (:value (second inputs))))

(defn run-output [id->node node inputs output stack]
  (swap! output update-in (:path node) cons (:value (first inputs))))

(assoc-in {} [] :foo)
