|
| 1 | +(ns ^:no-doc rewrite-clj.node.fn |
| 2 | + (:require [rewrite-clj.node.protocols :as node] |
| 3 | + [clojure.walk :as w])) |
| 4 | + |
| 5 | +;; ## Conversion |
| 6 | + |
| 7 | +(defn- construct-fn |
| 8 | + "Construct function form." |
| 9 | + [syms vararg body] |
| 10 | + (list |
| 11 | + 'fn* |
| 12 | + (vec |
| 13 | + (concat |
| 14 | + syms |
| 15 | + (if vararg |
| 16 | + (list '& vararg)))) |
| 17 | + body)) |
| 18 | + |
| 19 | +(defn- sym-index |
| 20 | + "Get index based on the substring following the parameter's `%`. |
| 21 | + Zero means vararg." |
| 22 | + [^String n] |
| 23 | + (cond (= n "&") 0 |
| 24 | + (= n "") 1 |
| 25 | + (re-matches #"\d+" n) (Long/parseLong n) |
| 26 | + :else (throw (Exception. "arg literal must be %, %& or %integer.")))) |
| 27 | + |
| 28 | +(defn- symbol->gensym |
| 29 | + "If symbol starting with `%`, convert to respective gensym." |
| 30 | + [sym-seq vararg? max-n sym] |
| 31 | + (if (symbol? sym) |
| 32 | + (let [nm (name sym)] |
| 33 | + (if (.startsWith nm "%") |
| 34 | + (let [i (sym-index (subs nm 1))] |
| 35 | + (if (and (= i 0) (not (realized? vararg?))) |
| 36 | + (deliver vararg? true)) |
| 37 | + (swap! max-n max i) |
| 38 | + (nth sym-seq i)))))) |
| 39 | + |
| 40 | +(defn- fn-walk |
| 41 | + "Walk the form and create an expand function form." |
| 42 | + [form] |
| 43 | + (let [syms (for [i (range) |
| 44 | + :let [base (if (= i 0) |
| 45 | + "rest__" |
| 46 | + (str "p" i "__")) |
| 47 | + s (name (gensym base))]] |
| 48 | + (symbol (str s "#"))) |
| 49 | + vararg? (promise) |
| 50 | + max-n (atom 0) |
| 51 | + body (w/prewalk |
| 52 | + #(or (symbol->gensym syms vararg? max-n %) %) |
| 53 | + form)] |
| 54 | + (construct-fn |
| 55 | + (take @max-n (rest syms)) |
| 56 | + (if (deref vararg? 0 nil) |
| 57 | + (first syms)) |
| 58 | + body))) |
| 59 | + |
| 60 | +;; ## Node |
| 61 | + |
| 62 | +(defrecord FnNode [children] |
| 63 | + node/Node |
| 64 | + (tag [_] :fn) |
| 65 | + (printable-only? [_] |
| 66 | + false) |
| 67 | + (sexpr [_] |
| 68 | + (fn-walk (node/sexprs children))) |
| 69 | + (length [_] |
| 70 | + (+ 3 (node/sum-lengths children))) |
| 71 | + (string [_] |
| 72 | + (str "#(" (node/concat-strings children) ")")) |
| 73 | + |
| 74 | + node/InnerNode |
| 75 | + (inner? [_] |
| 76 | + true) |
| 77 | + (children [_] |
| 78 | + children) |
| 79 | + (replace-children [this children'] |
| 80 | + (assoc this :children children')) |
| 81 | + |
| 82 | + Object |
| 83 | + (toString [this] |
| 84 | + (node/string this))) |
| 85 | + |
| 86 | +(node/make-printable! FnNode) |
| 87 | + |
| 88 | +;; ## Constructor |
| 89 | + |
| 90 | +(defn fn-node |
| 91 | + "Create node representing an anonymous function." |
| 92 | + [children] |
| 93 | + (->FnNode children)) |
0 commit comments