                                (core/list form x))]
            (recur threaded (next forms)))
          x))))
+
+#?(:cljs
+   (core/defmacro ->>
+     "Threads the expr through the forms. Inserts x as the
+     last item in the first form, making a list of it if it is not a
+     list already. If there are more forms, inserts the first form as the
+     last item in second form, etc."
+     [x & forms]
+     (core/loop [x x, forms forms]
+       (if forms
+         (core/let [form (first forms)
+                    threaded (if (seq? form)
+                               (with-meta `(~(first form) ~@(next form) ~x) (meta form))
+                               (core/list form x))]
+           (recur threaded (next forms)))
+         x))))
+
+;#?(:cljs
+;   (core/defmacro ..
+;     "form => fieldName-symbol or (instanceMethodName-symbol args*)
+;
+;     Expands into a member access (.) of the first member on the first
+;     argument, followed by the next member on the result, etc. For
+;     instance:
+;
+;     (.. System (getProperties) (get \"os.name\"))
+;
+;     expands to:
+;
+;     (. (. System (getProperties)) (get \"os.name\"))
+;
+;     but is easier to write, read, and understand."
+;     ([x form] `(. ~x ~form))
+;     ([x form & more] `(.. (. ~x ~form) ~@more))))
+
+#?(:cljs
+   (core/defmacro comment
+     "Ignores body, yields nil"
+     [& body]))
+
+#?(:cljs
+   (core/defmacro cond
+     "Takes a set of test/expr pairs. It evaluates each test one at a
+     time.  If a test returns logical true, cond evaluates and returns
+     the value of the corresponding expr and doesn't evaluate any of the
+     other tests or exprs. (cond) returns nil."
+     {:added "1.0"}
+     [& clauses]
+     (core/when clauses
+       (core/list 'if (first clauses)
+         (if (next clauses)
+           (second clauses)
+           (throw (js/Error. "cond requires an even number of forms")))
+         (cons 'cljs.core/cond (next (next clauses)))))))
+
+#?(:cljs
+   (core/defmacro declare
+     "defs the supplied var names with no bindings, useful for making forward declarations."
+     [& names] `(do ~@(map #(core/list 'def (vary-meta % assoc :declared true)) names))))
+
+#?(:cljs
+   (core/defmacro doto
+     "Evaluates x then calls all of the methods and functions with the
+     value of x supplied at the front of the given arguments.  The forms
+     are evaluated in order.  Returns x.
+
+     (doto (new java.util.HashMap) (.put \"a\" 1) (.put \"b\" 2))"
+     [x & forms]
+     (core/let [gx (gensym)]
+       `(let [~gx ~x]
+          ~@(map (core/fn [f]
+                   (if (seq? f)
+                     `(~(first f) ~gx ~@(next f))
+                     `(~f ~gx)))
+              forms)
+          ~gx))))
+
+#?(:cljs
+   (core/defn- parse-impls [specs]
+     (core/loop [ret {} s specs]
+       (if (seq s)
+         (recur (assoc ret (first s) (take-while seq? (next s)))
+           (drop-while seq? (next s)))
+         ret))))
+
+#?(:cljs
+   (core/defn- emit-extend-protocol [p specs]
+     (core/let [impls (parse-impls specs)]
+       `(do
+          ~@(map (core/fn [[t fs]]
+                   `(extend-type ~t ~p ~@fs))
+              impls)))))
+
+#?(:cljs
+   (core/defmacro extend-protocol
+     "Useful when you want to provide several implementations of the same
+     protocol all at once. Takes a single protocol and the implementation
+     of that protocol for one or more types. Expands into calls to
+     extend-type:
+
+     (extend-protocol Protocol
+       AType
+         (foo [x] ...)
+         (bar [x y] ...)
+       BType
+         (foo [x] ...)
+         (bar [x y] ...)
+       AClass
+         (foo [x] ...)
+         (bar [x y] ...)
+       nil
+         (foo [x] ...)
+         (bar [x y] ...))
+
+     expands into:
+
+     (do
+      (clojure.core/extend-type AType Protocol
+        (foo [x] ...)
+        (bar [x y] ...))
+      (clojure.core/extend-type BType Protocol
+        (foo [x] ...)
+        (bar [x y] ...))
+      (clojure.core/extend-type AClass Protocol
+        (foo [x] ...)
+        (bar [x y] ...))
+      (clojure.core/extend-type nil Protocol
+        (foo [x] ...)
+        (bar [x y] ...)))"
+     [p & specs]
+     (emit-extend-protocol p specs)))
+
+#?(:cljs
+   (core/defn ^{:private true}
+   maybe-destructured
+     [params body]
+     (if (every? core/symbol? params)
+       (cons params body)
+       (core/loop [params params
+                   new-params (with-meta [] (meta params))
+                   lets []]
+         (if params
+           (if (core/symbol? (first params))
+             (recur (next params) (conj new-params (first params)) lets)
+             (core/let [gparam (gensym "p__")]
+               (recur (next params) (conj new-params gparam)
+                 (-> lets (conj (first params)) (conj gparam)))))
+           `(~new-params
+              (let ~lets
+                ~@body)))))))
+
+#?(:cljs
+   (core/defmacro fn
+     "params => positional-params* , or positional-params* & next-param
+     positional-param => binding-form
+     next-param => binding-form
+     name => symbol
+
+     Defines a function"
+     {:forms '[(fn name? [params*] exprs*) (fn name? ([params*] exprs*) +)]}
+     [& sigs]
+     (core/let [name (if (core/symbol? (first sigs)) (first sigs) nil)
+                sigs (if name (next sigs) sigs)
+                sigs (if (vector? (first sigs))
+                       (core/list sigs)
+                       (if (seq? (first sigs))
+                         sigs
+                         ;; Assume single arity syntax
+                         (throw (js/Error.
+                                  (if (seq sigs)
+                                    (core/str "Parameter declaration "
+                                      (core/first sigs)
+                                      " should be a vector")
+                                    (core/str "Parameter declaration missing"))))))
+                psig (fn* [sig]
+                       ;; Ensure correct type before destructuring sig
+                       (core/when (not (seq? sig))
+                         (throw (js/Error.
+                                  (core/str "Invalid signature " sig
+                                    " should be a list"))))
+                       (core/let [[params & body] sig
+                                  _ (core/when (not (vector? params))
+                                      (throw (js/Error..
+                                               (if (seq? (first sigs))
+                                                 (core/str "Parameter declaration " params
+                                                   " should be a vector")
+                                                 (core/str "Invalid signature " sig
+                                                   " should be a list")))))
+                                  conds (core/when (core/and (next body) (map? (first body)))
+                                          (first body))
+                                  body (if conds (next body) body)
+                                  conds (core/or conds (meta params))
+                                  pre (:pre conds)
+                                  post (:post conds)
+                                  body (if post
+                                         `((let [~'% ~(if (core/< 1 (count body))
+                                                        `(do ~@body)
+                                                        (first body))]
+                                             ~@(map (fn* [c] `(assert ~c)) post)
+                                             ~'%))
+                                         body)
+                                  body (if pre
+                                         (concat (map (fn* [c] `(assert ~c)) pre)
+                                           body)
+                                         body)]
+                         (maybe-destructured params body)))
+                new-sigs (map psig sigs)]
+       (with-meta
+         (if name
+           (list* 'fn* name new-sigs)
+           (cons 'fn* new-sigs))
+         (meta &form)))))
+
+#?(:cljs
+   (core/defmacro if-let
+     "bindings => binding-form test
+
+     If test is true, evaluates then with binding-form bound to the value of
+     test, if not, yields else"
+     ([bindings then]
+      `(if-let ~bindings ~then nil))
+     ([bindings then else & oldform]
+      (core/assert-args
+        (vector? bindings) "a vector for its binding"
+        (nil? oldform) "1 or 2 forms after binding vector"
+        (= 2 (count bindings)) "exactly 2 forms in binding vector")
+      (core/let [form (bindings 0) tst (bindings 1)]
+        `(let [temp# ~tst]
+           (if temp#
+             (let [~form temp#]
+               ~then)
+             ~else))))))
+
+#?(:cljs
+   (core/defmacro if-not
+     "Evaluates test. If logical false, evaluates and returns then expr,
+     otherwise else expr, if supplied, else nil."
+     ([test then] `(if-not ~test ~then nil))
+     ([test then else]
+      `(if (not ~test) ~then ~else))))
+
+#?(:cljs
+   (core/defmacro letfn
+     "fnspec ==> (fname [params*] exprs) or (fname ([params*] exprs)+)
+
+     Takes a vector of function specs and a body, and generates a set of
+     bindings of functions to their names. All of the names are available
+     in all of the definitions of the functions, as well as the body."
+     {:forms '[(letfn [fnspecs*] exprs*)],
+      :special-form true, :url nil}
+     [fnspecs & body]
+     `(letfn* ~(vec (interleave (map first fnspecs)
+                      (map #(cons `fn %) fnspecs)))
+        ~@body)))
+
+#?(:cljs
+   (core/defmacro memfn
+     "Expands into code that creates a fn that expects to be passed an
+     object and any args and calls the named instance method on the
+     object passing the args. Use when you want to treat a Java method as
+     a first-class fn. name may be type-hinted with the method receiver's
+     type in order to avoid reflective calls."
+     [name & args]
+     (core/let [t (with-meta (gensym "target")
+               (meta name))]
+       `(fn [~t ~@args]
+          (. ~t (~name ~@args))))))
+
+#?(:cljs
+   (core/defmacro when
+     "Evaluates test. If logical true, evaluates body in an implicit do."
+     [test & body]
+     (core/list 'if test (cons 'do body))))
+
+#?(:cljs
+   (core/defmacro when-first
+     "bindings => x xs
+
+     Roughly the same as (when (seq xs) (let [x (first xs)] body)) but xs is evaluated only once"
+     [bindings & body]
+     (core/assert-args
+       (vector? bindings) "a vector for its binding"
+       (= 2 (count bindings)) "exactly 2 forms in binding vector")
+     (core/let [[x xs] bindings]
+       `(when-let [xs# (seq ~xs)]
+          (let [~x (first xs#)]
+            ~@body)))))
+
+#?(:cljs
+   (core/defmacro when-let
+     "bindings => binding-form test
+
+     When test is true, evaluates body with binding-form bound to the value of test"
+     [bindings & body]
+     (core/assert-args
+       (vector? bindings) "a vector for its binding"
+       (= 2 (count bindings)) "exactly 2 forms in binding vector")
+     (core/let [form (bindings 0) tst (bindings 1)]
+       `(let [temp# ~tst]
+          (when temp#
+            (let [~form temp#]
+              ~@body))))))
+
+#?(:cljs
+   (core/defmacro when-not
+     "Evaluates test. If logical false, evaluates body in an implicit do."
+     [test & body]
+     (core/list 'if test nil (cons 'do body))))
+
+#?(:cljs
+   (core/defmacro while
+     "Repeatedly executes body while test expression is true. Presumes
+     some side-effect will cause test to become false/nil. Returns nil"
+     [test & body]
+     `(loop []
+        (when ~test
+          ~@body
+          (recur)))))
+
+#?(:cljs
+   (core/defmacro cond->
+     "Takes an expression and a set of test/form pairs. Threads expr (via ->)
+     through each form for which the corresponding test
+     expression is true. Note that, unlike cond branching, cond-> threading does
+     not short circuit after the first true test expression."
+     [expr & clauses]
+     (core/assert (even? (count clauses)))
+     (core/let [g (gensym)
+                pstep (core/fn [[test step]] `(if ~test (-> ~g ~step) ~g))]
+       `(let [~g ~expr
+              ~@(interleave (repeat g) (map pstep (partition 2 clauses)))]
+          ~g))))
+
+#?(:cljs
+   (core/defmacro cond->>
+     "Takes an expression and a set of test/form pairs. Threads expr (via ->>)
+     through each form for which the corresponding test expression
+     is true.  Note that, unlike cond branching, cond->> threading does not short circuit
+     after the first true test expression."
+     [expr & clauses]
+     (core/assert (even? (count clauses)))
+     (core/let [g (gensym)
+                pstep (core/fn [[test step]] `(if ~test (->> ~g ~step) ~g))]
+       `(let [~g ~expr
+              ~@(interleave (repeat g) (map pstep (partition 2 clauses)))]
+          ~g))))
+
+#?(:cljs
+   (core/defmacro as->
+     "Binds name to expr, evaluates the first form in the lexical context
+     of that binding, then binds name to that result, repeating for each
+     successive form, returning the result of the last form."
+     [expr name & forms]
+     `(let [~name ~expr
+            ~@(interleave (repeat name) forms)]
+        ~name)))
+
+#?(:cljs
+   (core/defmacro some->
+     "When expr is not nil, threads it into the first form (via ->),
+     and when that result is not nil, through the next etc"
+     [expr & forms]
+     (core/let [g (gensym)
+                pstep (core/fn [step] `(if (nil? ~g) nil (-> ~g ~step)))]
+       `(let [~g ~expr
+              ~@(interleave (repeat g) (map pstep forms))]
+          ~g))))
+
+#?(:cljs
+   (core/defmacro some->>
+     "When expr is not nil, threads it into the first form (via ->>),
+     and when that result is not nil, through the next etc"
+     [expr & forms]
+     (core/let [g (gensym)
+                pstep (core/fn [step] `(if (nil? ~g) nil (->> ~g ~step)))]
+       `(let [~g ~expr
+              ~@(interleave (repeat g) (map pstep forms))]
+          ~g))))
+
+#?(:cljs
+   (core/defmacro if-some
+     "bindings => binding-form test
+
+      If test is not nil, evaluates then with binding-form bound to the
+      value of test, if not, yields else"
+     ([bindings then]
+      `(if-some ~bindings ~then nil))
+     ([bindings then else & oldform]
+      (core/assert-args
+        (vector? bindings) "a vector for its binding"
+        (nil? oldform) "1 or 2 forms after binding vector"
+        (= 2 (count bindings)) "exactly 2 forms in binding vector")
+      (core/let [form (bindings 0) tst (bindings 1)]
+        `(let [temp# ~tst]
+           (if (nil? temp#)
+             ~else
+             (let [~form temp#]
+               ~then)))))))
+
+#?(:cljs
+   (core/defmacro when-some
+     "bindings => binding-form test
+
+      When test is not nil, evaluates body with binding-form bound to the
+      value of test"
+     [bindings & body]
+     (core/assert-args
+       (vector? bindings) "a vector for its binding"
+       (= 2 (count bindings)) "exactly 2 forms in binding vector")
+     (core/let [form (bindings 0) tst (bindings 1)]
+       `(let [temp# ~tst]
+          (if (nil? temp#)
+            nil
+            (let [~form temp#]
+              ~@body))))))
 
 (core/defn- ^{:dynamic true} assert-valid-fdecl
   "A good fdecl looks like (([a] ...) ([a b] ...)) near the end of defn."


(core/defn destructure [bindings]
  (core/let [bents (partition 2 bindings)
         pb (core/fn pb [bvec b v]
              (core/let [pvec
                     (core/fn [bvec b val]
                       (core/let [gvec (gensym "vec__")]
                         (core/loop [ret (core/-> bvec (conj gvec) (conj val))
                                     n 0
                                     bs b
                                     seen-rest? false]
                           (if (seq bs)
                             (core/let [firstb (first bs)]
                               (core/cond
                                 (= firstb '&) (recur (pb ret (second bs) (core/list `nthnext gvec n))
                                                      n
                                                      (nnext bs)
                                                      true)
                                 (= firstb :as) (pb ret (second bs) gvec)
                                 :else (if seen-rest?
                                         (throw
                                           #?(:clj (new Exception "Unsupported binding form, only :as can follow & parameter")
                                              :cljs (new js/Error "Unsupported binding form, only :as can follow & parameter")))
                                         (recur (pb ret firstb (core/list `nth gvec n nil))
                                                (core/inc n)
                                                (next bs)
                                                seen-rest?))))
                             ret))))
                     pmap
                     (core/fn [bvec b v]
                       (core/let [gmap (gensym "map__")
                                  defaults (:or b)]
                         (core/loop [ret (core/-> bvec (conj gmap) (conj v)
                                             (conj gmap) (conj `(if (seq? ~gmap) (apply core/hash-map ~gmap) ~gmap))
                                             ((core/fn [ret]
                                                (if (:as b)
                                                  (conj ret (:as b) gmap)
                                                  ret))))
                                     bes (reduce
                                          (core/fn [bes entry]
                                            (reduce #(assoc %1 %2 ((val entry) %2))
                                                    (dissoc bes (key entry))
                                                    ((key entry) bes)))
                                          (dissoc b :as :or)
                                          {:keys #(if (core/keyword? %) % (keyword (core/str %))),
                                           :strs core/str, :syms #(core/list `quote %)})]
                           (if (seq bes)
                             (core/let [bb (key (first bes))
                                        bk (val (first bes))
                                        has-default (contains? defaults bb)]
                               (recur (pb ret bb (if has-default
                                                   (core/list `get gmap bk (defaults bb))
                                                   (core/list `get gmap bk)))
                                      (next bes)))
                             ret))))]
                    (core/cond
                      (core/symbol? b) (core/-> bvec (conj (if (namespace b) (symbol (name b)) b)) (conj v))
                      (core/keyword? b) (core/-> bvec (conj (symbol (name b))) (conj v))

                      :else (throw
                              #?(:clj (new Exception (core/str "Unsupported binding form: " b))
                                 :cljs (new js/Error (core/str "Unsupported binding form: " b)))))))
             process-entry (core/fn [bvec b] (pb bvec (first b) (second b)))]
    (if (every? core/symbol? (map first bents))
      bindings
      (core/if-let [kwbs (seq (filter #(core/keyword? (first %)) bents))]
        (throw
          #?(:clj (new Exception (core/str "Unsupported binding key: " (ffirst kwbs)))
             :cljs (new js/Error (core/str "Unsupported binding key: " (ffirst kwbs)))))
        (reduce process-entry [] bents)))))

(core/defmacro let
  "binding => binding-form init-expr
