|
| 1 | +(ns cherry.internal.fn) |
| 2 | + |
| 3 | +#?(:cljs (def Exception js/Error)) |
| 4 | + |
| 5 | +(defn maybe-destructured |
| 6 | + [params body] |
| 7 | + (if (every? symbol? params) |
| 8 | + (cons params body) |
| 9 | + (loop [params params |
| 10 | + new-params (with-meta [] (meta params)) |
| 11 | + lets []] |
| 12 | + (if params |
| 13 | + (if (symbol? (first params)) |
| 14 | + (recur (next params) (conj new-params (first params)) lets) |
| 15 | + (let [gparam (gensym "p__")] |
| 16 | + (recur (next params) (conj new-params gparam) |
| 17 | + (-> lets (conj (first params)) (conj gparam))))) |
| 18 | + `(~new-params |
| 19 | + (let ~lets |
| 20 | + ~@body)))))) |
| 21 | + |
| 22 | +(defn core-fn |
| 23 | + [&form sigs] |
| 24 | + (let [name (if (symbol? (first sigs)) (first sigs) nil) |
| 25 | + sigs (if name (next sigs) sigs) |
| 26 | + sigs (if (vector? (first sigs)) |
| 27 | + (list sigs) |
| 28 | + (if (seq? (first sigs)) |
| 29 | + sigs |
| 30 | + ;; Assume single arity syntax |
| 31 | + (throw (Exception. |
| 32 | + (if (seq sigs) |
| 33 | + (str "Parameter declaration " |
| 34 | + (first sigs) |
| 35 | + " should be a vector") |
| 36 | + (str "Parameter declaration missing")))))) |
| 37 | + psig (fn* [sig] |
| 38 | + ;; Ensure correct type before destructuring sig |
| 39 | + (when (not (seq? sig)) |
| 40 | + (throw (Exception. |
| 41 | + (str "Invalid signature " sig |
| 42 | + " should be a list")))) |
| 43 | + (let [[params & body] sig |
| 44 | + _ (when (not (vector? params)) |
| 45 | + (throw (Exception. |
| 46 | + (if (seq? (first sigs)) |
| 47 | + (str "Parameter declaration " params |
| 48 | + " should be a vector") |
| 49 | + (str "Invalid signature " sig |
| 50 | + " should be a list"))))) |
| 51 | + conds (when (and (next body) (map? (first body))) |
| 52 | + (first body)) |
| 53 | + body (if conds (next body) body) |
| 54 | + conds (or conds (meta params)) |
| 55 | + pre (:pre conds) |
| 56 | + post (:post conds) |
| 57 | + body (if post |
| 58 | + `((let [~'% ~(if (< 1 (count body)) |
| 59 | + `(do ~@body) |
| 60 | + (first body))] |
| 61 | + ~@(map (fn* [c] `(assert ~c)) post) |
| 62 | + ~'%)) |
| 63 | + body) |
| 64 | + body (if pre |
| 65 | + (concat (map (fn* [c] `(assert ~c)) pre) |
| 66 | + body) |
| 67 | + body)] |
| 68 | + (maybe-destructured params body))) |
| 69 | + new-sigs (map psig sigs)] |
| 70 | + (with-meta |
| 71 | + (if name |
| 72 | + (list* 'fn* name new-sigs) |
| 73 | + (cons 'fn* new-sigs)) |
| 74 | + (meta &form)))) |
| 75 | + |
| 76 | +(defn |
| 77 | + ^{:doc "Same as (def name (core/fn [params* ] exprs*)) or (def |
| 78 | + name (core/fn ([params* ] exprs*)+)) with any doc-string or attrs added |
| 79 | + to the var metadata. prepost-map defines a map with optional keys |
| 80 | + :pre and :post that contain collections of pre or post conditions." |
| 81 | + :arglists '([name doc-string? attr-map? [params*] prepost-map? body] |
| 82 | + [name doc-string? attr-map? ([params*] prepost-map? body)+ attr-map?])} |
| 83 | + core-defn [_&form _&env name fdecl] |
| 84 | + ;; Note: Cannot delegate this check to def because of the call to (with-meta name ..) |
| 85 | + (if (instance? #?(:clj clojure.lang.Symbol :cljs Symbol) name) |
| 86 | + nil |
| 87 | + (throw |
| 88 | + #?(:clj (IllegalArgumentException. "First argument to defn must be a symbol") |
| 89 | + :cljs (js/Error. "First argument to defn must be a symbol")))) |
| 90 | + (let [m (if (string? (first fdecl)) |
| 91 | + {:doc (first fdecl)} |
| 92 | + {}) |
| 93 | + fdecl (if (string? (first fdecl)) |
| 94 | + (next fdecl) |
| 95 | + fdecl) |
| 96 | + m (if (map? (first fdecl)) |
| 97 | + (conj m (first fdecl)) |
| 98 | + m) |
| 99 | + fdecl (if (map? (first fdecl)) |
| 100 | + (next fdecl) |
| 101 | + fdecl) |
| 102 | + fdecl (if (vector? (first fdecl)) |
| 103 | + (list fdecl) |
| 104 | + fdecl) |
| 105 | + m (if (map? (last fdecl)) |
| 106 | + (conj m (last fdecl)) |
| 107 | + m) |
| 108 | + fdecl (if (map? (last fdecl)) |
| 109 | + (butlast fdecl) |
| 110 | + fdecl) |
| 111 | + m m #_(conj {:arglists (list 'quote (sigs fdecl))} m) |
| 112 | + ;; no support for :inline |
| 113 | + ;m (let [inline (:inline m) |
| 114 | + ; ifn (first inline) |
| 115 | + ; iname (second inline)] |
| 116 | + ; ;; same as: (if (and (= 'fn ifn) (not (symbol? iname))) ...) |
| 117 | + ; (if (if #?(:clj (clojure.lang.Util/equiv 'fn ifn) |
| 118 | + ; :cljs (= 'fn ifn)) |
| 119 | + ; (if #?(:clj (instance? clojure.lang.Symbol iname) |
| 120 | + ; :cljs (instance? Symbol iname)) false true)) |
| 121 | + ; ;; inserts the same fn name to the inline fn if it does not have one |
| 122 | + ; (assoc m |
| 123 | + ; :inline (cons ifn |
| 124 | + ; (cons (clojure.lang.Symbol/intern |
| 125 | + ; (.concat (.getName ^clojure.lang.Symbol name) "__inliner")) |
| 126 | + ; (next inline)))) |
| 127 | + ; m)) |
| 128 | + m (conj (if (meta name) (meta name) {}) m)] |
| 129 | + (cond |
| 130 | + #_(multi-arity-fn? fdecl) |
| 131 | + #_(multi-arity-fn name |
| 132 | + (if (comp/checking-types?) |
| 133 | + (update-in m [:jsdoc] conj "@param {...*} var_args") |
| 134 | + m) fdecl (:def-emits-var &env)) |
| 135 | + |
| 136 | + #_(variadic-fn? fdecl) |
| 137 | + #_(variadic-fn name |
| 138 | + (if (comp/checking-types?) |
| 139 | + (update-in m [:jsdoc] conj "@param {...*} var_args") |
| 140 | + m) fdecl (:def-emits-var &env)) |
| 141 | + |
| 142 | + :else |
| 143 | + (list 'def (with-meta name m) |
| 144 | + ;;todo - restore propagation of fn name |
| 145 | + ;;must figure out how to convey primitive hints to self calls first |
| 146 | + (cons `fn fdecl))))) |
0 commit comments