1ファイルだけのcall-graph
そのファイルで定義された関数だけ見る。let-fnやletの中で定義された関数は無視。
(use '[clojure.contrib.duck-streams :only (reader writer with-out-writer)]) (use '[clojure.contrib.str-utils :only (re-gsub)]) (defn read-sexps "Like read-lines but read with read function (not read-line)" [f] (let [rdr (-> (reader f) (clojure.lang.LineNumberingPushbackReader.))] ((fn step [] (if-let [item (read rdr false nil)] (lazy-seq (cons item (step))) (.close rdr)))))) ;;;exttract-*系をlazy-seqにする必要はあるのかな?(transientを止めて) (declare extract-conj! extract-cars) (defn- extract-conj! [trv x] (cond (symbol? x) (conj! trv x) (seq? x) (extract-cars (rest x) (extract-conj! trv (first x))) :else trv)) (defn- check-car?$ [coll] (fn [x] (some #(= % (first x)) coll))) (def car-is-let? (check-car?$ '[let if-let when-let for])) (def car-is-collfn? (check-car?$ '[map mapcat pmap reduce])) (def car-is-uses? (check-car?$ '[use import require])) (defn extract-cars ([sexp] (persistent! (extract-cars sexp (transient [])))) ([sexp acc] (reduce (fn [acc x] (if (seq? x) (cond (car-is-let? x) (reduce #(extract-cars %2 %1) acc (list (map fnext (partition 2 (fnext x))) (nnext x))) (car-is-collfn? x) (extract-cars (nnext x) (extract-conj!(extract-conj! acc (first x)) (fnext x))) :else (extract-cars (next x) (extract-conj! acc (first x)))) acc)) acc sexp))) (defn- get-name [def-form] (let [name (fnext def-form)] ;手抜き (if (symbol? name) name 'false))) (defn call-graph-map [xs] (let [xs (remove car-is-uses? xs) names (map get-name xs) name-map (reduce #(assoc! %1 %2 '()) (transient {}) names)] (persistent! (reduce (fn [m [name sexp]] (let [vs (reduce (fn [coll sym] (if (m sym) (cons sym coll) coll)) '() (extract-cars sexp))] (if (empty? vs) m (assoc! m name vs)))) name-map (zipmap names xs))))) (defn re-gsub* [m string] (reduce (fn [str [regexp replacement]] (re-gsub regexp replacement str)) string m)) (defn dump-with-graphviz-style ([cmap color label] (let [fmt (fn [s] (re-gsub* {#"->>" "aarrow", #"->" "arrow", #"\!" "_bang", #"\?" "_question", #"\$" "_dollar" #"\*" "_star" #"-" "_"} (str s)))] (println "digraph simple_hierarchy {") (println "\tgraph [concentrate = true];") (doseq [[k vs] cmap] (let [k* (fmt k)] (printf "\t%s [label=\"%s\"];\n" k* k) (doseq [v (set vs)] (printf "\t%s->%s[label=\"%s\", fontcolor=%s];\n" k* (fmt v) label color)))) (println "}"))) ([cmap] (dump-with-graphviz-style cmap "darkgreen" "call"))) (defn main [ifile ofile] (with-out-writer ofile (dump-with-graphviz-style (call-graph-map (read-sexps ifile)))))
何かが違う。書いていて違和感が残る。上手くclojureを使えない。