Don't like ads? PRO users don't see any ads ;-)
Guest

Untitled

By: a guest on May 2nd, 2012  |  syntax: None  |  size: 4.61 KB  |  hits: 13  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. var _ = require("underscore");
  2. var rl = require("readline");
  3.  
  4. LPAREN = '('
  5. RPAREN = ')'
  6. IDENT = 'IDENT'
  7. EOF = -1
  8.  
  9. function Lexer(source) {
  10.         this.source = source
  11.         this.line = 1
  12.         this.column = 1
  13.         this.token = ''
  14.         this.pos = 0
  15. }
  16.  
  17. function is_whitespace(c) {
  18.         switch (c) {
  19.                 case ' ':
  20.                 case '\t':
  21.                 case '\n':
  22.                 case '\r':
  23.                         return true;
  24.         }
  25.         return false;
  26. }
  27.  
  28. Lexer.prototype.char = function() {
  29.         if (this.pos >= this.source.length) {
  30.                 return EOF;
  31.         }
  32.         return this.source.charAt(this.pos);
  33. }
  34.  
  35. Lexer.prototype.next = function() {
  36.         var char = this.char();
  37.         if (char == '\r\n' || char == '\r' || char == '\n') {
  38.                 this.line++;
  39.                 this.column = 0;
  40.         }
  41.         this.pos++;
  42.         this.column++;
  43.         return this.char();
  44. }
  45.  
  46. Lexer.prototype.scan_whitespace = function() {
  47.         var char;
  48.         while (is_whitespace(char = this.char())) {
  49.                 this.next();
  50.         }
  51. }
  52.  
  53. Lexer.prototype.scan_ident = function() {
  54.         var char;
  55.         this.token = '';
  56.         while ((char = this.char()) !== EOF && char.match(/[a-z\-\.]/)) {
  57.                 this.token += char;
  58.                 this.next();
  59.         }
  60.         return this.token;
  61. }
  62.  
  63. Lexer.prototype.scan = function() {
  64.         var char;
  65.         while ((char = this.char()) !== EOF) {
  66.                 if (char === LPAREN) {
  67.                         this.next();
  68.                         return ['LPAREN', LPAREN];
  69.                 }
  70.                 else if (char === RPAREN) {
  71.                         this.next();
  72.                         return ['RPAREN', RPAREN];
  73.                 }
  74.                 else if (char.match(/[a-z\-\.]/)) {
  75.                         return ['IDENT', this.scan_ident()];
  76.                 }
  77.                 else if (is_whitespace(char)) {
  78.                         this.scan_whitespace();
  79.                 }
  80.                 else {
  81.                         console.log("unexpected input on line", this.line);
  82.                         return EOF;
  83.                 }
  84.         }
  85.         return EOF;
  86. }
  87.  
  88. function LispNode() {
  89.        
  90. }
  91.  
  92. LispNode.prototype = Array.prototype;
  93.  
  94. LispNode.prototype.form = function() {
  95.         return this[0];
  96. }
  97.  
  98. function Parser(lexer) {
  99.         this.lexer = lexer;
  100. }
  101.  
  102. Parser.prototype.parse = function() {
  103.         var token;
  104.         var form = new LispNode();
  105.         var form_stack = [];
  106.         while ((token = this.lexer.scan()) !== EOF) {
  107.                 //console.log('token', token);
  108.                 switch (token[0]) {
  109.                         case 'LPAREN':
  110.                                 var newform = new LispNode();
  111.                                 form.push(newform);
  112.                                 form_stack.push(form);
  113.                                 form = newform;
  114.                                 break;
  115.                         case 'RPAREN':
  116.                                 form = form_stack.pop();
  117.                                 break;
  118.                         default:
  119.                                 form.push(token[1]);
  120.                                 break;
  121.                 }
  122.         }
  123.         return form;
  124. }
  125.  
  126. function Compiler() {
  127.         this.output = "";
  128. }
  129.  
  130. Compiler.prototype.compile_define_form = function(node) {
  131.         var output = "";
  132.         output += "var " + node[1] + " = " + this.compile(node[2]) + ";";
  133.         return output;
  134. }
  135.  
  136. Compiler.prototype.compile_begin_form = function(node) {
  137.         var compiler = this;
  138.         var forms = _.map(node.slice(1), function(n) { return compiler.compile(n); });
  139.         forms[forms.length-1] = "return " + forms[forms.length-1];
  140.         return "(function() { " + forms.join("; ") + "})()";
  141. }
  142.  
  143. Compiler.prototype.compile_if_form = function(node) {
  144.         if (node.length < 3) {
  145.                 console.log("if form requires 3 elements");
  146.                 return;
  147.         }
  148.         var output = "";
  149.         output += "(function() { ";
  150.         output += "if (" + this.compile(node[1]) + ") { return " + this.compile(node[2]) + "; } ";
  151.         if (node.length == 4) {
  152.                 output += "else { return " + this.compile(node[3]) + "} ";
  153.         }
  154.         output += "})()";
  155.         return output;
  156. }
  157.  
  158. Compiler.prototype.compile_fn_form = function(node) {
  159.         var compiler = this;
  160.         var output = "function(" + node[1].slice(0).join(", ") + ") { return " + compiler.compile(node[2]) + "; }";
  161.         return output;
  162. }
  163.  
  164. Compiler.prototype.compile_binary_op = function(node, op) {
  165.         var compiler = this;
  166.         var tests = _.map(node.slice(1), function(n) { return compiler.compile(n); });
  167.         return "(" + tests.join(" " + op + " ") + ")";
  168. }
  169.  
  170. Compiler.prototype.compile_js_layer = function(node, form) {
  171.         return this.compile(node[2]) + "." + this.compile(node[1]);
  172. }
  173.  
  174. Compiler.prototype.compile = function(root) {
  175.         if (!root['form']) {
  176.                 return "(" + root + ")";
  177.         }
  178.         switch (root.form()) {
  179.                 case "define":
  180.                 case "begin":
  181.                 case "if":
  182.                 case "fn":
  183.                         return this["compile_"+root.form()+"_form"](root);
  184.                 case "equals":
  185.                 case "and":
  186.                 case "or":
  187.                 case "add":
  188.                 case "sub":
  189.                         var table = {equals: "===", and: "&&", or: "||", add: "+", sub: "-"};
  190.                         return this.compile_binary_op(root, table[root.form()]);
  191.                 case ".":
  192.                         return this.compile_js_layer(root, root.form());
  193.                 default:
  194.                         var compiler = this;
  195.                         var args = _.map(root.slice(1), function(n) { return compiler.compile(n); });
  196.                         return root.form() + "(" + args.join(", ") + ")";
  197.         }
  198. }
  199.  
  200. var rli = rl.createInterface(process.stdin, process.stdout, function() {
  201.        
  202. })
  203. process.stdout.write('> ');
  204. rli.on("line", function(source) {
  205.         var lexer = new Lexer(source); // function(a, b) { return add(a, b); }
  206.         var parser = new Parser(lexer);
  207.         var ast = parser.parse();
  208.         var compiler = new Compiler();
  209.         for (var i = 0; i < ast.length; i++) {
  210.                 //console.log('-- COMPILED FORM', i, '--');
  211.                 console.log(compiler.compile(ast[i]));
  212.         }
  213.         process.stdout.write('> ');
  214. });