Parse Ruby source code in Clojure, and more
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
examples
src/graaaph
test/graaaph
.gitignore
README.md
project.clj
tree.png

README.md

graaaph

graaaph is a Clojure library designed to make it easy to consume, manipulate, and analyze JRuby's AST representation of Ruby code from Clojure. It ties together a bunch of interesting Clojure libraries in the hopes of creating a unified Clojure toolkit for exploring generated Ruby ASTs.

Ruby code is parsed and transformed by the Java jruby-parser library, manipulated by Clojure's zipper library, analyzed with core.logic, and visualized using @ztellman's Rhizome.

Usage

Get code as a map, with a lot of data:

(use 'graaaph.core)

(parse-ruby-code "def a;'ok';end")

;; {:position {:file "", :start-line 0, :end-line 0, :start-offset 0, :end-offset 14}, :type "ROOTNODE", :value nil, :name nil}
;; {:position {:file "", :start-line 0, :end-line 0, :start-offset 0, :end-offset 14}, :type "NEWLINENODE", :value nil, :name nil}
;; {:position {:file "", :start-line 0, :end-line 0, :start-offset 0, :end-offset 14}, :type "DEFNNODE", :value nil, :name "a"}
;; {:position {:file "", :start-line 0, :end-line 0, :start-offset 4, :end-offset 5}, :type "ARGUMENTNODE", :value nil, :name "a"}
;; {:position {:file "", :start-line 0, :end-line 0, :start-offset 6, :end-offset 6}, :type "ARGSNODE", :value nil, :name nil}
;; {:position {:file "", :start-line 0, :end-line 0, :start-offset 6, :end-offset 11}, :type "NEWLINENODE", :value nil, :name nil}
;; {:position {:file "", :start-line 0, :end-line 0, :start-offset 6, :end-offset 10}, :type "STRNODE", :value nil, :name nil}

Or as a zipper, but still tasting like Java:

(use 'graaaph.core)

(-> (ruby-code-zipper "1")
    z/next
    z/next
    z/node
    .getValue) ;; From the jruby-parser API
;; 1

Or use one of a (slowly) growing number of core.logic relations such as nodetypeo and dupeo:

(def ruby-code "class Dude
                  def awesome
                    'first awesome'
                  end
                  #
                  def cool
                    'not awesome'
                  end
                  #
                  def awesome
                    'second awesome'
                  end
                  #
                  def bro
                  end
                  #
                  def bro
                  end
                end")

(defn get-duplicate-method-names [ruby-code]
  (let [nodes   (parse-ruby-code ruby-code)
        results (l/run* [q]
                  (l/fresh [a b c d]
                    (l/== a nodes)
                    (nodetypeo a "DEFNNODE" c)
                    (dupeo c d)
                    (l/== q d)))]
        results))

(get-duplicate-method-names ruby-code)
;;=> (({:type "DEFNNODE", :value nil, :name "awesome"} {:type "DEFNNODE", :value nil, :name "bro"}))

Examine your ast by generating an image of the tree

(view-ruby-ast ruby-code)

;; or save it as a file
(save-ruby-ast-image ruby-code "tree.png")

Transform source code with a zipper and then transform back to source code

(ruby-code-zipper ruby-code)
;; [#<RootNode (RootNode, (NewlineNode, (ClassNode, (Colon2ImplicitNode:Dude), (BlockNode, (NewlineNode, (DefnNode:awesome, (ArgumentNode:awesome), (ArgsNode), (NewlineNode, (StrNode)))), (NewlineNode, (DefnNode:cool, (ArgumentNode:cool), (ArgsNode), (NewlineNode, (StrNode)))), (NewlineNode, (DefnNode:awesome, (ArgumentNode:awesome), (ArgsNode), (NewlineNode, (StrNode)))), (NewlineNode, (DefnNode:bro, (ArgumentNode:bro), (ArgsNode))), (NewlineNode, (DefnNode:bro, (ArgumentNode:bro), (ArgsNode)))))))> nil]

(zipper-to-source (ruby-code-zipper ruby-code))
#<StringWriter class Dude
  def awesome
    "first awesome"
  end
  def cool
    "not awesome"
  end
  def awesome
    "second awesome"
  end
  def bro
  end
  def bro
  end
end>

Tests

lein test

Plans

  • Associate original jruby-parser node information as metadata to node map object, for altering and rehydrating source as ast

Credits

  • @swannodette for the amazing guidance and work on nodeattro, mapo, filtero, rembero, etc.

License

Copyright © 2013 Michael R. Bernstein and contributors

Distributed under the Eclipse Public License, the same as Clojure.