1. 2

Like GHC rewrite rules.

Allows writing transformations over source code, inside of the source code. Biggest use-case would be performance optimisations, like in the linked example. But other things are possible, such as this awesome one:

/* forall. */
/* true */
/* false */

console.log(true);

Becomes:

console.log(false);

Any more use-cases? Any suggestions?

  1.  

  2. 2

    This is pretty rad. It would be nice to keep on applying rewrite rules until you get to a normal form (which would let the example support a.map(b).map©.map(d) without changes), but it would be problematic if a normal form didn’t exist. As it is now, it seems more like a straight up and down preprocessor than a system of rewriting rules at this point. Looking at the docs on the GHC rewrite rules, it says, “GHC makes no attempt to make sure that the rules are confluent or terminating.” which you could also say.

    If you wanted to add knobs, you could also add checks which try to guess if rules are repeating, and a maximum depth to rewrite to.

    How is this implemented? Do you parse the js to an ast, or is it more brute force string matching?

    edit: Also, seems like you could use this for obfuscation. Another example of performance optimization this could do would be inlining, or loop unrolling. You could also use it for writing javascript-like languages, although it seems like js is already flexible enough to do everything you can do in the languages that can be written with this as without. You could probably build new flow control with this (unless?), but I suspect you can already do that with js, although I’m not that familiar with it.

    1. 2

      Yes, rules should be applied as far as possible. I’ll also make no attempt to ensure termination. That’s a TODO (just a problem with the way I’m using the estraverse library).

      The rules are definitely not applied using string matching. The rules are actually valid JavaScript and parsed using esprima. The “forall a b c.” lets Rephrase know which identifiers in the “from” AST are actually meant to be “holes”.

      Each node in the JavaScript tree is then pattern matched using the “holey” tree and a substitution map is made from Identifier –> Node. Substitutes are made over the occurrences of those identifiers in the “to” AST and replace the original matching node.

      Easy :)