Lately I’ve been working on the ECMA3 conformance in IronJS, but last night I did a small side-tour into something completely different: JavaScript Quotations. The ideal is similar to the one found in F# code quotations or Lisp macros, not as evolved as any of them though – but still pretty nice. I wanna say right now that this is my own extension to the JavaScript language and it’s not something you can do in any browser or other implementation (that I know of).

What it does is it introduces a new symbol, @ – stolen from F#, which gives you access to the syntax tree of a function during runtime and allows you to modify it as you see fit and then compile it to a regular JavaScript function. While it could be abused to no end it allows for some pretty interesting possibilities. The example I’m going to show creates a function which reads a property out of an object, and optionally compiles a console.log statement into the function body.

function makeLoggedPropertyReader(includeLog, propertyName) { // Note the @ symbol infront of the function keyword var quoted = @function (x) { if(x) { console.log(x); } return x._; }; // This is how the quoted structure looks like, // it's basically a syntax tree that you can // traverse, modify as you see fit and then compile /* quoted = { type: 19, // function body: [ { type: 18, // if statement test: { type: 5, // identifier value: "x" }, trueBranch: [ { type: 9, // method call target: { type: 5, // identifier value: "console" }, member: { type: 5, // identifier value: "log" }, arguments: [ { type: 5, // identifier value: "x" } ] } ], elseBranch: { type: 0 // void node } }, { type: 25, // return value: { type: 43, // property accessor object: { type: 5, // identifier value: "x" }, name: { type: 5, // identifier value: "_" // the value we're going to replace } } } ] } */ // The first statement in the qouted body // is the ifStatement, which we will conditionally // remove depending on the boolean value of includeLog if(!includeLog) { quoted.body[0] = Quotations.voidStatement(); } // Pull the second statement out of the function body var returnStmt = quoted.body[1]; // Pull the value node out of the return statement var propertyAccessor = returnStmt.value; // Set the value of the "name" node of the property accessor // to the string value of the propertyName that is passed in propertyAccessor.name.value = propertyName.toString(); // We've modified our qouted expression // and we can now compile it so it // becomes a return quoted.compile(); } // And here we'll use it: var logged = makeLoggedPropertyReader(true, "myProp"); var notLogged = makeLoggedPropertyReader(false, "myProp"); var myObj = {myProp: "hello world"} var xValue = logged(myObj); // will return and print the value of myProp to console.log var xValue = notLogged(myObj); // will only return the value of myProp