Merge branch 'marg-drop'

This commit is contained in:
Jonas Enlund 2012-03-08 13:02:51 +02:00
commit ea633a4cd4
7 changed files with 75 additions and 26 deletions

View file

@ -48,6 +48,7 @@ Bugs can be reported using the github bug tracker.
* Rules for function definitions (make this more of a lint tool)
* Rules for collection lookup; "2 is a bad smell" [see this blog post](http://tech.puredanger.com/2011/10/12/2-is-a-smell/)
* Extract the "when to use" rules from [Joy of Clojure](http://joyofclojure.com/)
* gh-pages of the Marginalia docs as done [here](http://www.maybetechnology.com/2011/08/literate-programming-with-marginalia.html)
* Leiningen project.clj setting for rule exclusion
* Leiningen project.clj setting for a directory of rules to include
* More rules

View file

@ -3,5 +3,6 @@
:dependencies [[org.clojure/clojure "1.3.0"]
[org.clojure/core.logic "0.6.7"]
[org.clojure/tools.namespace "0.1.2"]]
:dev-dependencies [[lein-marginalia "0.7.0"]]
:eval-in-leiningen true
:warn-on-reflection false)

View file

@ -1,24 +1,36 @@
(ns jonase.kibit.core
"Kibit's core functionality uses core.logic to suggest idiomatic
replacements for patterns of code and remove general code lint"
(:require [clojure.core.logic :as logic]
[clojure.java.io :as io]
[clojure.string :as string]
[jonase.kibit.arithmetic :as arith]
[jonase.kibit.control-structures :as control]
[jonase.kibit.misc :as misc])
[jonase.kibit.rules :as core-rules])
(:import [clojure.lang LineNumberingPushbackReader]))
(def all-rules (merge control/rules
arith/rules
misc/rules))
;; The rule sets
;; -------------
;;
;; Rule sets are stored in individual files that have a top level
;; `(def rules '{...})`. The collection of rules are in the `rules`
;; directory.
;;
;; For more information, see: [rule](#jonase.kibit.rules) namespace
(def all-rules core-rules/all-rules)
(defn read-ns [r]
(lazy-seq
(let [form (read r false ::eof)
line-num (.getLineNumber r)]
(when-not (= form ::eof)
(cons (with-meta form {:line line-num}) (read-ns r))))))
(defn unify [expr rule]
;; Parsing the lines/forms
;; -----------------------
;;
;; The unifier compares a form/line against a single rule.
;; The unification generates a map.
;;
;; The `:rule` is a vector of the matching rule/alt pair that was used
;; to produce the `:alt`, the ideal alternative.
;; The line number (`:line`) is extracted from the metadata of the line,
;; courtesy of LineNumberingPushbackReader (See `read-ns` and `check-file`)
(defn unify
"TODO jonas"
[expr rule]
(let [[r s] (#'logic/prep rule)
alt (first (logic/run* [alt]
(logic/== expr r)
@ -29,21 +41,43 @@
:alt (seq alt)
:line (-> expr meta :line)})))
;; Loop over the rule set, recursively applying unification to find the best
;; possible alternative
(defn check-form
"Given an expression/line/form, return a map containing the alternative suggestion info, or `nil`"
([expr]
(check-form expr all-rules))
([expr rules]
(when (sequential? expr)
(some #(unify expr %) rules))))
(defn expr-seq [expr]
;; Building the parsable forms
;; ---------------------------
;;
;; We treat each line as a single form, since logic will match any form sequence on the line
;; The line numbers are added to the lines/forms' to metadata, `^{:line}`
(defn read-ns
"Generate a lazy sequence of lines from a [`LineNumberingPushbackReader`]( https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/LineNumberingPushbackReader.java )."
[r]
(lazy-seq
(let [form (read r false ::eof)
line-num (.getLineNumber r)]
(when-not (= form ::eof)
(cons (with-meta form {:line line-num}) (read-ns r))))))
(defn expr-seq
"TODO jonas, just a quick one"
[expr]
(tree-seq sequential?
seq
expr))
(defn check-file
"TODO jonas, just a quick one"
([reader]
(check-file reader all-rules))
([reader rules]
(keep check-form
(mapcat expr-seq (read-ns (LineNumberingPushbackReader. reader))))))

View file

@ -0,0 +1,16 @@
(ns jonase.kibit.rules
"`rules.clj` provides the core functionality for extracting
and merging rules from namespaces. There are shorthand `def`s
for rule the core rule sets"
(:require [jonase.kibit.rules.arithmetic :as arith]
[jonase.kibit.rules.control-structures :as control]
[jonase.kibit.rules.misc :as misc]))
(def rule-map {:control-structures control/rules
:arithmetic arith/rules
:misc misc/rules})
;; TODO: Consider a refactor for this into a function
;; `(defn rules-for-ns [& namespaces])`
(def all-rules (apply merge (vals rule-map)))

View file

@ -1,4 +1,4 @@
(ns jonase.kibit.arithmetic)
(ns jonase.kibit.rules.arithmetic)
(def rules
'{(+ ?x 1) (inc ?x)
@ -14,10 +14,8 @@
(> ?x 0) (pos? ?x)
(<= 1 ?x) (pos? ?x)
(< ?x 0) (neg? ?x)})
(comment
(< (+ 1 x) 0))
(< ?x 0) (neg? ?x)
(= ?x ?x) true
(== ?x ?x) true})

View file

@ -1,4 +1,4 @@
(ns jonase.kibit.control-structures)
(ns jonase.kibit.rules.control-structures)
(def rules
'{(if ?x ?y nil) (when ?x ?y)

View file

@ -1,4 +1,4 @@
(ns jonase.kibit.misc)
(ns jonase.kibit.rules.misc)
(def rules
'{(apply str (interpose ?x ?y)) (clojure.string/join ?x ?y)
@ -23,4 +23,3 @@
(map (fn [x] (inc x)) [1 2 3])
(map #(dec %) [1 2 3]))