Let’s visit the Parser, that is a separated module. It starts with a simple declaration:
'use strict'; var lexer; if (typeof lexer == 'undefined') lexer = require('./lexer'); var parser = (function () { var TokenType = lexer.TokenType; var binoperators = [ "+", "-", "*", "/", "==", "!=", "<", ">", "<=", ">=" ];
It uses and requires the lexer module. After this declaration, there are many expressions and commands. This is the expression “class” for a name (ie, foo):
function NameExpression(name) { this.isLeftValue = true; this.compile = function () { return name; }; this.getName = function () { return name; } this.collectContext = function (context) { context.declare(name); } }
In an upcoming post, I will describe the detection and construction of commands and expression. An expression should implement two methods: compile, that returns an string with the compiled JavaScript code associated to the expression, and collectContext, that allows the discover of used variables in an expression/command. In the above code, NameExpression declares its name to a context, an object that is recovering the used variables.
This is an IndexedExpression, composed by an expression and another one for the index (it’s like foo[42+1]):
function IndexedExpression(expr, indexpr) { this.isLeftValue = true; this.compile = function () { return expr.compile() + '[' + indexpr.compile() + ']'; }; this.collectContext = function (context) { expr.collectContext(context); } }
The collectContext visits the internal expression (I could add the visit of the index expression, too).
There are commands, like IfCommand:
function IfCommand(cond, thencmd, elsecmd) { this.compile = function () { var code = 'if (' + cond.compile() + ') { ' + thencmd.compile() + ' }'; if (elsecmd) code += ' else { ' + elsecmd.compile() + ' }'; return code; }; this.collectContext = function (context) { cond.collectContext(context); thencmd.collectContext(context); if (elsecmd) elsecmd.collectContext(context); } }
The distinction between commands and expressions is a formal one. Again, a command should implement compile and collectContext. The above code generates a JavaScript if command.
As usual, I followed TDD (Test-Driven Development) workflow. Partial tests example:
exports['Compile string without quotes inside'] = function (test) { test.equal(compileExpression("'foo'", test), "'foo'"); test.equal(compileExpression('"foo"', test), "'foo'"); } exports['Compile name'] = function (test) { test.equal(compileExpression("foo", test), "foo"); } exports['Qualified name'] = function (test) { test.equal(compileExpression("foo.bar", test), "foo.bar"); } exports['Indexed term'] = function (test) { test.equal(compileExpression("foo[bar]", test), "foo[bar]"); }
Remember: No TDD, no paradise! 😉
Next topics: how to recognize and build expressions and commands.
Stay tuned!
Angel “Java” Lopez
http://www.ajlopez.com