Skip to content

Commit

Permalink
Initial implementation.
Browse files Browse the repository at this point in the history
Basic extraction of assignments, conditionals, and branching.
Basic abc metric to combine results
  • Loading branch information
JacobNinja committed Mar 6, 2014
0 parents commit dc5f705
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 0 deletions.
14 changes: 14 additions & 0 deletions .gitignore
@@ -0,0 +1,14 @@
/target
/lib
/classes
/checkouts
pom.xml
pom.xml.asc
*.jar
*.class
.lein-deps-sum
.lein-failures
.lein-plugins
.lein-repl-history

.nrepl-port
13 changes: 13 additions & 0 deletions README.md
@@ -0,0 +1,13 @@
# abc

A Clojure library designed to ... well, that part is up to you.

## Usage

FIXME

## License

Copyright © 2014 FIXME

Distributed under the Eclipse Public License, the same as Clojure.
3 changes: 3 additions & 0 deletions doc/intro.md
@@ -0,0 +1,3 @@
# Introduction to abc

TODO: write [great documentation](http://jacobian.org/writing/great-documentation/what-to-write/)
7 changes: 7 additions & 0 deletions project.clj
@@ -0,0 +1,7 @@
(defproject abc "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.5.1"]
[org.jruby/jrubyparser "0.5.3"]])
11 changes: 11 additions & 0 deletions src/abc/core.clj
@@ -0,0 +1,11 @@
(ns abc.core)

(defn metric-result [rs]
(fn [metric]
(let [metric-result (get-in rs [:metrics metric] 0)]
(* metric-result metric-result))))

(defn abc [rs]
(Math/sqrt (apply +
(map (metric-result rs)
[:branches :assignments :conditionals]))))
69 changes: 69 additions & 0 deletions src/abc/extractors/ruby.clj
@@ -0,0 +1,69 @@
(ns abc.extractors.ruby
(:import (org.jrubyparser Parser CompatVersion)
(org.jrubyparser.parser ParserConfiguration)
(java.io StringReader)))

(defn parse-ruby [rb]
(let [parser (Parser.)
config (ParserConfiguration. 0 CompatVersion/RUBY1_9)]
(.parse parser "" (StringReader. rb) config)))

(defn- children? [_] true)
(defn- children [n]
(.childNodes n))
(defn- make-tree [root]
(tree-seq children? children root))

(defn- defn-node? [n]
(instance? org.jrubyparser.ast.DefnNode n))
(defn- assignment-node? [n]
(instance? org.jrubyparser.ast.LocalAsgnNode n))
(defn- conditional-node? [n]
(instance? org.jrubyparser.ast.IfNode n))
(defn- branch-node? [n]
(instance? org.jrubyparser.ast.FCallNode n))

(defn- filter-nodes [root f]
(loop [tree root]
(cond (empty? tree) '()
(f (first tree)) (cons (first tree) (filter-nodes (rest tree) f))
(seq? (first tree)) (concat (filter-nodes (first tree) f)
(filter-nodes (rest tree) f))
:else (recur (rest tree)))))

(defn- line-number [n]
(inc (.. n getPosition getEndLine)))

(defn- line-range [n]
(let [source-position (.getPosition n)]
[(.getStartOffset source-position)
(.getEndOffset source-position)]))

(defn- extract [f]
(fn [root]
(let [nodes (filter-nodes (make-tree root) f)]
(when-not (empty? nodes)
(map (fn [n]
{:line (line-number n) :range (line-range n)})
nodes)))))

(defn- ruby-source [rb [start end]]
(apply str (take (- end start) (drop start rb))))

(defn- with-source [rb & fks]
(fn [root]
(reduce (fn [e [f k]]
(assoc e k
(map (fn [env]
(assoc env :source (ruby-source rb (:range env))))
((extract f) root))))
{}
fks)))

(defn parse [rb]
(let [root (parse-ruby rb)
defn-nodes (filter-nodes (make-tree root) defn-node?)]
(map (with-source rb [assignment-node? :assignments]
[conditional-node? :conditionals]
[branch-node? :branches])
defn-nodes)))
10 changes: 10 additions & 0 deletions test/abc/core_test.clj
@@ -0,0 +1,10 @@
(ns abc.core-test
(:require [clojure.test :refer :all]
[abc.core :refer :all]))

(deftest abc-test
(testing "metric"
(is (= (abc {:method "foo" :metrics {:conditionals 2
:branches 5
:assignments 10}}))
11)))
23 changes: 23 additions & 0 deletions test/abc/extractors/ruby_test.clj
@@ -0,0 +1,23 @@
(ns abc.extractors.ruby-test
(:require [clojure.test :refer :all]
[abc.extractors.ruby :refer :all]))

(def ruby
"def foo
bar = 3
wat if condition
method_call()
end")

(deftest ruby-extractor-test
(testing "assignment"
(is (= [{:line 2 :range [10 17] :source "bar = 3"}]
(:assignments (first (parse ruby))))))

(testing "conditional"
(is (= [{:line 3 :range [20 36] :source "wat if condition"}]
(:conditionals (first (parse ruby))))))

(testing "branching"
(is (= [{:line 4 :range [39 52] :source "method_call()"}]
(:branches (first (parse ruby)))))))

0 comments on commit dc5f705

Please sign in to comment.