Search code examples
javascriptclojureclojurescriptsweet.js

Add syntax to JavaScript with Clojure


I want to add new syntax to my JavaScript files much like Sweet.js, but using Clojure/ClojureScript to do the transformation.

Here is what I would like to do:

//original

specialFunction add(a, b) {
   return a + b;
}


//transformed

function add(a, b) {
   return a + b;
}
registerSpecialFunction({
   name: "add",
   args: ["a", "b"],
   func: add
});

Correct me if I'm wrong, but I think it would work best by:

  1. parse the JS file into an AST
  2. do the transformation
  3. print the resulting AST back out as JavaScript

Any idea on how to do parts 1 and 3? Do I even have the right idea here?


Solution

  • Just as a heads up, this may be a non-trivial task, but probably a great learning exercise.

    You'll have to re-implement the concept or macros from Sweet.js or the idea of custom parser extensions from Babylon (parser for Babel) if you want the same control from Clojure.

    Either way, you'll need to write a parser that understands a superset of JavaScript's syntax. You might want to look at parser generators such as instaparse (Clojure) and peg.js (JavaScript).

    Then you need to decide whether you want to make a fixed number of additions to the language grammar (like Babel) or allow macros to define their own grammar extensions/replacement rules (like Sweet.js). At this point, you'll need to write some kind of engine for transforming the AST generated by your parser.

    Macros can be implemented in a number of ways, everything from replacement rules like you'd find in C and C++ to full blown compile-time evaluated functions that work directly with the AST like you'd find in Clojure.

    After parsing and transforming the AST with this new tool, you'll need to transform it into a valid JavaScript AST. It'll make things easier to maintain compatibility with the ESTree specification as this will allow you to use tools like escodegen to actually generate the JavaScript code from the AST itself.

    Of course, piggybacking tools like peg.js and escodegen is only possible if you're writing your tool as ClojureScript and compiling and running it against NodeJS. The other option is to find compatible tools within the JVM ecosystem and use them with JVM compiled Clojure instead.

    The JavaScript ecosystem has a range of good tools available for parsing, transforming and generating ES code (have a look through the Babel packages for example/inspiration) but you'll have to remember that if you are writing ClojureScript and running it under Node, you are in fact creating a JavaScript executable and it might have just been easier to start with JavaScript in the first place.