A Clojure library for working with AST structures for Java source code. The goal is to provide an expressive embedded language in Clojure for Java static metaprogramming. My motivation is to use it with the GWT compiler. It may have other useful applications. Currently very experimental. It does work, though, it turns percolator-specific Clojure…
Clojure Java
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
play
src
test/percolator/test
.gitignore
README.md
ideas
notes
project.clj
refactor

README.md

percolator

Warning

This works, but is still quite experimental. It may change radically. Comments are welcome.

Q & A

Q: WTF?

A: Extensible Java metaprogramming

This is a tool for writing arbitrarily complex compile-time logic for your Java projects. It is meant to become part of your Java build process, between you and the compiler.

Synopsis

It's a library for working with Java AST structures in Clojure. It's for static metaprogramming of Java code. It attempts to make that comprehensible, by describing the code template as an embedded language in Clojure... certain forms with special syntax represent Java AST nodes and their structure. This can be mixed with arbitrary Clojure code. That Clojure code can, of course, stick together Java AST however you like.

You can also extend percolator's syntax yourself, to make new shorthands for repetive implementation details in your Java code ... bringing something like the power of Clojure macros to help you DRY up your Java code.

If you've ever tried writing a program which vomits something into STDOUT which you then > into a .java file and pass to javac or similar, and wished there was a better way, this may be of interest.

Under the hood

At the time of writing, this library is < 800 lines of .clj code. It covers a good deal of the Java spec ... you can:

  • define classes and interfaces: supports inheritance and mixins, inner classes, anonymous classes, class member variables and methods.
  • declare variables of (most?) primitive types, or class types, supports every flag in Java, even those with peculiar names (an abstract, volatile, private, transient native? Is he protected?).
  • slag together compilation units, attaching classes to them, and slagging them together into packages, importing things... I don't know if there's a proper verb for this, it's usually done by a programmer
  • metaprogram unashamedly and without hesitation

The reason it's so simple and small is that it's just a wrapper around Google's thoroughly spiffy library, javaparser, and a tiny helpful smattering of Clojure.

A taste

This:

    closeButton.addClickHandler(new ClickHandler() {
        public void onClick(ClickEvent e) {
            dialogBox.hide();
            sendButton.setEnabled(true);
            sendButton.setFocus(true);
        }
    });

can be expressed this concisely:

    ( 'on-click closeButton
      ( 'hide   dialogBox  )
      ( 'enable sendButton )
      ( 'focus  sendButton ))

by extending percolator like:

(definterpreter interpret-statement-on-click [identifier-symbol & statements]
 `( '. ~identifier-symbol addClickHandler
    ( 'new ClickHandler
      ( 'method #{:public} void onClick [ (ClickEvent e) ] ~@statements ))))

(add-interpreters-to-scope :statement
  { 'on-click   on-click
    'hide       (interpreter [o]   `( '. ~o hide                  ))
    'enable     (interpreter [o]   `( '. ~o setEnabled true       ))
    'focus      (interpreter [o]   `( '. ~o setFocus true         ))
    })

Status

It works. There's an example at ./play/src/com/whatsys/test.clj which compiles with the GWT compiler.

Here's the intermediate form ./play/src/com/whatsys/client/Play.java

It is a goal of this library cover everything in the Java 1.5 spec. There shouldn't be any valid Java source code that cannot be produced with percolator. It's not there yet, see below. I have no plans to support Java 7, as the underlying library, javaparser, doesn't support it.

Notably missing

You cannot yet:

  • define a class with generics (but you can instantiate them, etc)
  • express Java 'float' literals or literals of int types smaller than long
  • express annotations at all
  • express Java array types
  • instantiate outer class from an inner class
  • probably a lot more...
  • FIXME fill this in

Usage

WYSIWYG. I've been experimenting with it in vim. It uses leiningen, and I use VimClojure. You might be able to toy with it the way I have been:

$ cd percolator/play
$ lein nailgun
$ vim src/com/whatsys/test.clj

and then eval percolator stuff in vim... if you are very fortunate, you might get compilable java source code in a vim buffer. Paste it into Play.java and try something along the lines of

$ lein gwt
$ lein gwt dev

...to compile with GWT and then start your devmode server

If you try this out, I'd love to hear what you think.

License

Copyright (C) 2012 Blake Miller

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