<html><head><meta http-equiv="Content-Type" content="text/html charset=windows-1252"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;"><div>Hi,</div><div><br></div><div>I’m writing a general C++ refactoring tool in Lisp that I’m currently calling “Improve”.</div><div><br></div><div>Combining the dynamic, interactive language Lisp and the most excellent clang C++ matcher/refactoring library </div><div>- it’s like a digital Reese’s peanut butter cup! (apologies to those with nut allergies)</div><div> </div><div>An example Improve script is below - anyone who uses the C++ ASTMatcher library should recognize what I’m up to.</div><div>The session generated from the script is available at: <a href="https://dl.dropboxusercontent.com/u/6229900/session.log">https://dl.dropboxusercontent.com/u/6229900/session.log</a></div><div>Search for “REPL echo” to jump from command to command within the output.</div><div><br></div><div>Improve has the capabilities of “clang-query” but it also lets you write short scripts to do source-to-source transformation.</div><div>It lets you quickly and interactively write ASTMatchers, specifying bound nodes and then access information about those nodes in small blocks of code that display information about the nodes or generate replacements. It’s not pretty at the moment - the idea is a workshop/test-bench for interactively querying and modifying C++ source code.</div><div><br></div><div>It’s based on clang’s ASTMatcher/Refactoring library (thanks klimek, chandlerc, sbenza, pcc - I haven’t met you guys but hopefully I’ll make it to an LLVM meeting this year) with a bunch of lisp code to eliminate the C++ boilerplate required to write refactoring tools - and did I mention it’s interactive!</div><div><br></div><div>I’ll be open-sourcing it as soon as I finish using it to implement moving garbage collection in the Lisp system/compiler I wrote that it’s hosted within.</div><div><br></div><div>Thoughts and comments are welcome.</div><div><br></div><div>Best,</div><div><br></div><div>.Chris.</div><div><br></div><div><br></div><div>
<div><div>Christian Schafmeister</div><div>Associate Professor</div><div>Chemistry Department</div><div>Temple University</div></div>
</div>
<br><div><br></div><div><br></div><div><font face="Courier"><br></font></div><div><div><font face="Courier"><br></font></div><div><font face="Courier">;;</font></div><div><font face="Courier">;; Load the tooling code</font></div><div><font face="Courier">;; Currently use load so we can edit/reload the code during development</font></div><div><font face="Courier">;; Later just (require 'clang-tool)</font></div><div><font face="Courier">;;</font></div><div><font face="Courier">(load "src:lisp;clang-tool.lsp")</font></div><div><font face="Courier"><br></font></div><div><font face="Courier">;;</font></div><div><font face="Courier">;; Load the JSONCompilationDatabase</font></div><div><font face="Courier">;; This will fill the global variable $* with a list of all the source files in the database</font></div><div><font face="Courier">;;</font></div><div><font face="Courier">(load-compilation-database "src:main;compile_commands.json")</font></div><div><font face="Courier"><br></font></div><div><font face="Courier"><br></font></div><div><font face="Courier">;; </font></div><div><font face="Courier">;; Set up a subset of 10 source filenames in $TEST to search over interactively</font></div><div><font face="Courier">xp;; You can set up any number of source filename lists to run matchers over</font></div><div><font face="Courier">;; for interactive matcher development</font></div><div><font face="Courier">(lclear $test)</font></div><div><font face="Courier">(ladd $test (subseq $* 0 10))</font></div><div><font face="Courier"><br></font></div><div><font face="Courier">;;</font></div><div><font face="Courier">;; Load the C++ ASTs for the filenames in $TEST</font></div><div><font face="Courier">(load-asts $test)</font></div><div><font face="Courier"><br></font></div><div><font face="Courier"><br></font></div><div><font face="Courier">#|</font></div><div><font face="Courier">A demo ASTMatcher.</font></div><div><font face="Courier">I'm looking for fields in class/structs where the class/struct does not inherit from StackBoundClass or GCObject</font></div><div><font face="Courier">and the field has a type that contain smart_ptr's that would be stored on the heap</font></div><div><font face="Courier">such as vector<XXX> where XXX is a struct/class that contains smart_ptr's</font></div><div><font face="Courier">The smart_ptr's in question will be Garbage Collected when I don't want them to be because</font></div><div><font face="Courier">they won't be connected to the root </font></div><div><font face="Courier">|#</font></div><div><font face="Courier">(defparameter *heap-smart-ptr-matcher*</font></div><div><font face="Courier"> '(:field-decl</font></div><div><font face="Courier"> (:has-decl-context</font></div><div><font face="Courier"> (:record-decl</font></div><div><font face="Courier"> (:bind :outer-decl (:record-decl))</font></div><div><font face="Courier"> (:unless (:any-of</font></div><div><font face="Courier"> (:is-derived-from (:matches-name ".*StackBoundClass.*"))</font></div><div><font face="Courier"> (:is-derived-from (:matches-name ".*GCObject.*"))))))</font></div><div><font face="Courier"> (:has-type</font></div><div><font face="Courier"> (:has-declaration</font></div><div><font face="Courier"> (:class-template-specialization-decl</font></div><div><font face="Courier"> (:bind :named-decl (:named-decl))</font></div><div><font face="Courier"> (:has-any-template-argument</font></div><div><font face="Courier"> (:refers-to-type</font></div><div><font face="Courier"> (:has-declaration</font></div><div><font face="Courier"> (:record-decl</font></div><div><font face="Courier"> (:bind :recdecl (:record-decl))</font></div><div><font face="Courier"> (:is-derived-from</font></div><div><font face="Courier"> (:record-decl</font></div><div><font face="Courier"> (:for-each</font></div><div><font face="Courier"> (:field-decl</font></div><div><font face="Courier"> (:bind :leaf (:field-decl))</font></div><div><font face="Courier"> (:has-type</font></div><div><font face="Courier"> (:has-declaration</font></div><div><font face="Courier"> (:class-template-specialization-decl</font></div><div><font face="Courier"> (:matches-name ".*smart_ptr.*")</font></div><div><font face="Courier"> ))))))))))))))))</font></div><div><font face="Courier"><br></font></div><div><font face="Courier">;;</font></div><div><font face="Courier">;; Run the matcher on the currently loaded subset of ASTs</font></div><div><font face="Courier">;; Using just the loaded subset of ASTs allows the matching to be fast and</font></div><div><font face="Courier">;; enables interactive development of matchers. Once a matcher is written</font></div><div><font face="Courier">;; it can be run on all source files using BATCH-MATCH-RUN (see below).</font></div><div><font face="Courier">;;</font></div><div><font face="Courier">;; Run code on each match, extracting info on the bound nodes using</font></div><div><font face="Courier">;; mtag-xxx functions that take a node TAG that corresponds to a (:bind :TAG (NODE))</font></div><div><font face="Courier">;; command in the matcher.</font></div><div><font face="Courier">;; Print info on each match</font></div><div><font face="Courier"><br></font></div><div><font face="Courier">(match-run *heap-smart-ptr-matcher*</font></div><div><font face="Courier"> :code #'(lambda () (format t "MATCH: ------------------~%~a~% :whole source-> ~a~%:leaf ~a~%~a~%"</font></div><div><font face="Courier"> (mtag-loc-start :whole)</font></div><div><font face="Courier"> (mtag-source :whole)</font></div><div><font face="Courier"> (mtag-source :leaf)</font></div><div><font face="Courier"> (list ":named-decl" (get-name (mtag-node :named-decl)))))</font></div><div><font face="Courier"> ;; :limit 10 ; This would limit the max number of matches processed to 10</font></div><div><font face="Courier"> )</font></div><div><font face="Courier"><br></font></div><div><font face="Courier"><br></font></div><div><font face="Courier">;;</font></div><div><font face="Courier">;; Run the matcher on source files one at a time - this allows us to run</font></div><div><font face="Courier">;; matchers on lots of source files without having to load all of their ASTs into</font></div><div><font face="Courier">;; memory at one time</font></div><div><font face="Courier"><br></font></div><div><font face="Courier">(batch-match-run *heap-smart-ptr-matcher*</font></div><div><font face="Courier"> :filenames $* ; $* is a global variable that contains a list of all source files</font></div><div><font face="Courier"> :code #'(lambda () (format t "MATCH: ------------------~%~a~% :whole source-> ~a~%:leaf ~a~%~a~%"</font></div><div><font face="Courier"> (mtag-loc-start :whole)</font></div><div><font face="Courier"> (mtag-source :whole)</font></div><div><font face="Courier"> (mtag-source :leaf)</font></div><div><font face="Courier"> (list ":named-decl" (get-name (mtag-node :named-decl)))))</font></div><div><font face="Courier"> )</font></div></div><div><br></div></body></html>