Live post processing

It should be apparent now that running toString over a function in JavaScript is a valid way to perform tasks. It seems odd but, really, writing code that emits other code is as old as Lisp or possibly older. When I first came across how dependency injection works in AngularJS, I was both disgusted at the hack and impressed by the ingenuity of the solution.

If it is possible to do dependency injection by interpreting code on the fly, then what more could we do with it? The answer is: quite a lot. The first thing that comes to mind is that you could write domain specific languages.

We talked about DSLs in Chapter 5, Behavioral Patterns, and even created a very simple one. With the ability to load and rewrite JavaScript, we can take advantage of a syntax that is close to JavaScript but not wholly compatible. When interpreting the DSL, our interpreter would write out additional tokens needed to convert the code to actual JavaScript.

One of the nice features of TypeScript that I've always liked is that parameters to the constructors that are marked as public are automatically transformed into properties on the object. For instance, the TypeScript code that follows:

class Axe{
  constructor(public handleLength, public headHeight){}
}

Compiles to the following code:

var Axe = (function () {
  function Axe(handleLength, headHeight) {
    this.handleLength = handleLength;
    this.headHeight = headHeight;
  }
  return Axe;
})();

We could do something similar in our DSL. Starting with the Axe definition that follows:

class Axe{
  constructor(handleLength, /*public*/ headHeight){}
}

We've used a comment here to denote that headHeight should be public. Unlike the TypeScript version, we would like our source code to be valid JavaScript. Because comments are included in the toString function this works just fine.

The next thing to do is to actually emit new JavaScript from this. I've taken a naïve approach and used regular expressions. This approach would quickly get out of hand and probably only works with the well-formed JavaScript in the Axe class:

function publicParameters(func){
  var stringRepresentation = func.toString();
  var parameterString = stringRepresentation.match(/^function .*((.*))/)[1];
  var parameters = parameterString.split(",");
  var setterString = "";
  for(var i = 0; i < parameters.length; i++){
    if(parameters[i].indexOf("public") >= 0){
      var parameterName = parameters[i].split('/')[parameters[i].split('/').length-1].trim();
      setterString += "this." +  parameterName + " = " + parameterName + ";
";
    }
  }
  var functionParts = stringRepresentation.match(/(^.*{)([sS]*)/);
  return functionParts[1] + setterString + functionParts[2];
}

console.log(publicParameters(Axe));

Here we extract the parameters to the function and check for those that have the public annotation. The result of this function can be passed back into eval for use in the current object or written out to a file if we're using this function in a pre-processor. Typically use of eval in JavaScript is discouraged.

There are tons of different things that can be done using this sort of processing. Even without string post-processing there are some interesting programming concept we can explore by just wrapping methods.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.5.86