diff --git a/bin/parse_json.js b/bin/parse_json.js new file mode 100755 index 0000000..eaee7d0 --- /dev/null +++ b/bin/parse_json.js @@ -0,0 +1,43 @@ +#!/usr/bin/env node +var requirejs = require('requirejs'); +var readline = require('readline'); + +requirejs.config({ + nodeRequire: require, + baseUrl: __dirname + '/..' +}); +requirejs(['./extensions', './parse_json', './bcompile', './binterp'], function(_, parse_json, bcompile, binterp) { + // empty top-level frame + var frame = Object.create(null); + + var rl = readline.createInterface(process.stdin, process.stdout); + rl.setPrompt('> '); + rl.prompt(); + + rl.on('line', function(line) { + try { + var tree = parse_json(line); + var bc = bcompile(tree); + var result = binterp.binterp( + bc, 0, frame, undefined, undefined, Object.create(null) + ); + if (result !== undefined) { + console.log(result); + } + } catch (err) { + console.log(err); + } + rl.setPrompt('> '); + rl.prompt(); + }).on('SIGINT', function() { + console.log('^C'); + rl.setPrompt('> '); + rl.prompt(); + // Simulate ctrl+u to delete the line written previously + rl.write(null, {ctrl: true, name: 'u'}); + }).on('close', function() { + console.log('exit'); + console.log('goodbye!'); + process.exit(0); + }); +}); diff --git a/parse_json.js b/parse_json.js new file mode 100644 index 0000000..3a38d8b --- /dev/null +++ b/parse_json.js @@ -0,0 +1,200 @@ +// # parse_json.js +// +// Parser for JSON written in Simplified JavaScript. +// +// A stripped-down version of the TDOP JavaScript parser in parse.js +define(["text!parse_json.js", "tokenize"], function make_parse_json(parse_json_source, tokenize) { + var symbol_table = {}; + var token; + var tokens; + var token_nr; + + var itself = function () { + return this; + }; + + var error = function(obj, message, t) { + t = t || obj; + t.name = "Syntax Error"; + if (t.from || t.to) { message += ' ['+t.from+'-'+t.to+']'; } + t.message = message; + /*console.warn(JSON.stringify(t));*/ + Object.Throw(t); + }; + + var hasOwnProperty = function(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + }; + + var advance = function (id) { + var a, o, t, v; + if (id && token.id !== id) { + error(token, "Expected '" + id + "'."); + } + if (token_nr >= tokens.length) { + token = symbol_table["(end)"]; + return; + } + t = tokens[token_nr]; + token_nr += 1; + v = t.value; + a = t.type; + if (a === "name" && hasOwnProperty(symbol_table, v)) { + o = symbol_table[v]; + } else if (a === "operator") { + o = symbol_table[v]; + if (!o) { + error(t, "Unknown operator: " + v); + } + } else if (a === "string" || a === "number") { + o = symbol_table["(literal)"]; + a = "literal"; + } else { + error(t, "Unexpected token."); + } + token = Object.create(o); + token.from = t.from; + token.to = t.to; + token.value = v; + token.arity = a; + return token; + }; + + var expression = function (rbp) { + var left; + var t = token; + advance(); + left = t.nud(); + while (rbp < token.lbp) { + t = token; + advance(); + left = t.led(left); + } + return left; + }; + + var original_symbol = { + nud: function () { + error(this, "Undefined: " + this.value); + }, + led: function (left) { + error(this, "Missing operator."); + } + }; + + var symbol = function (id, bp) { + var s = hasOwnProperty(symbol_table, id) ? symbol_table[id] : null; + bp = bp || 0; + if (s) { + if (bp >= s.lbp) { + s.lbp = bp; + } + } else { + s = Object.create(original_symbol); + s.id = s.value = id; + s.lbp = bp; + symbol_table[id] = s; + } + return s; + }; + + var constant = function (s, v) { + var x = symbol(s); + x.nud = function () { + //scope.reserve(this); + this.value = symbol_table[this.id].value; + this.arity = "literal"; + return this; + }; + x.value = v; + return x; + }; + + var prefix = function (id, nud) { + var s = symbol(id); + s.nud = nud || function () { + //scope.reserve(this); + this.first = expression(70); + this.arity = "unary"; + return this; + }; + return s; + }; + + symbol("(end)"); + symbol(":"); + symbol("]"); + symbol("}"); + symbol(","); + + constant("true", true); + constant("false", false); + constant("null", null); + + symbol("(literal)").nud = itself; + + prefix("-"); + + prefix("[", function () { + var a = []; + if (token.id !== "]") { + while (true) { + a.push(expression(0)); + if (token.id !== ",") { + break; + } + advance(","); + } + } + advance("]"); + this.first = a; + this.arity = "unary"; + return this; + }); + + prefix("{", function () { + var a = [], n, v; + if (token.id !== "}") { + while (true) { + n = token; + if (n.arity !== "name" && n.arity !== "literal") { + error(token, "Bad property name."); + } + advance(); + advance(":"); + v = expression(0); + v.key = n.value; + a.push(v); + if (token.id !== ",") { + break; + } + advance(","); + } + } + advance("}"); + this.first = a; + this.arity = "unary"; + return this; + }); + + var parse_json = function(source) { + tokens = tokenize(source, '=<>!+-*&|/%^', '=<>&|'); + token_nr = 0; + advance(); + var e = expression(0); + advance("(end)"); + var tree = [{ + value: "return", + arity: "statement", + first: e + }]; + return tree; + }; + + parse_json.__module_name__ = "parse_json"; + parse_json.__module_init__ = make_parse_json; + parse_json.__module_deps__ = ["tokenize"]; + parse_json.__module_source__ = parse_json_source; + return parse_json; + +}); diff --git a/tests.js b/tests.js index 55a6782..0c0f6c9 100644 --- a/tests.js +++ b/tests.js @@ -1,15 +1,18 @@ // A collection of interesting test cases. define(["text!tests.js", "str-escape", /* These modules are just imported to make test cases out of them. */ - "tokenize", "parse", "jcompile", "crender", "bytecode-table", + "tokenize", "parse", "parse_json", "jcompile", "crender", + "bytecode-table", "literal-map", "bcompile", "binterp", "stdlib", "events", "banalyze", "asm-llvm"], function make_tests(tests_source, str_escape, - tokenize, parse, jcompile, crender, bytecode_table, + tokenize, parse, parse_json, jcompile, crender, + bytecode_table, LiteralMap, bcompile, binterp, stdlib, events, banalyze, asm_llvm) { var deps = ["tests_source", "str-escape", - "tokenize", "parse", "jcompile", "crender", "bytecode-table", + "tokenize", "parse", "parse_json", "jcompile", "crender", + "bytecode-table", "literal-map", "bcompile", "binterp", "stdlib", "events", "banalyze", "asm-llvm"]; var test=[], i=2/* skip tests_source and str_escape */; diff --git a/write-lua-bytecode.js b/write-lua-bytecode.js index c45770f..93cae2f 100644 --- a/write-lua-bytecode.js +++ b/write-lua-bytecode.js @@ -2,19 +2,16 @@ // startup code and standard library, as a Lua file. // // Run it under `node` with the CLI in `bin/write-lua-bytecode.js` -define(['./parse', './bcompile', './banalyze', './bytecode-table', './top-level', './str-escape', './literal-map', './tests', './stdlib', './json', './extensions'], function(parse, bcompile, banalyze, bytecode_table, top_level, str_escape, LiteralMap, tests, stdlib, json) { +define(['./parse', './parse_json', './bcompile', './banalyze', './bytecode-table', './top-level', './str-escape', './literal-map', './tests', './stdlib', './json', './extensions'], function(parse, parse_json, bcompile, banalyze, bytecode_table, top_level, str_escape, LiteralMap, tests, stdlib, json) { banalyze = null; // Disable banalyze for now. - // json = null; // Uncomment to disable JSON support var fake_require = "var __modules__ = {};\n"+ "define = function _define(name, deps, init_func) {\n"+ " var d = deps.map(function(m) { return __modules__[m]; });\n"+ " __modules__[name] = init_func.apply(this, d);\n"+ "};\n"; - var make_compile_from_source = function(parse, bcompile, banalyze, LiteralMap, TOP_LEVEL) { - var compile_from_source = function (source, as_object) { - source = source || '{ return 1+2; }'; - var tree = parse(source, TOP_LEVEL); + var make_compile_from_source = function(parse, parse_json, bcompile, banalyze, LiteralMap, TOP_LEVEL) { + var compile_analyze_and_encode = function(tree, as_object) { var bc = bcompile(tree, true/*don't desugar frame get*/); if (banalyze) { var literalMap = LiteralMap.New(bc.literals); @@ -25,6 +22,11 @@ define(['./parse', './bcompile', './banalyze', './bytecode-table', './top-level' var result = as_object ? bc : bc.encode(); return result; }; + var compile_from_source = function (source, as_object) { + source = source || '{ return 1+2; }'; + var tree = parse(source, TOP_LEVEL); + return compile_analyze_and_encode(tree, as_object); + }; compile_from_source.make_repl = function() { var state = null; return function(source) { @@ -33,21 +35,27 @@ define(['./parse', './bcompile', './banalyze', './bytecode-table', './top-level' return bcompile(rv.tree).encode(); }; }; + compile_from_source.parse_json = function(source, as_object) { + source = "" + source; + var tree = parse_json(source); + return compile_analyze_and_encode(tree, as_object); + }; return compile_from_source; }; var cfs_source = make_compile_from_source.toSource ? make_compile_from_source.toSource() : make_compile_from_source.toString(); - cfs_source = 'define("compile_from_source", ["parse","bcompile","banalyze","literal-map", "top-level"], '+ + cfs_source = 'define("compile_from_source", ["parse","parse_json","bcompile","banalyze","literal-map","top-level"], '+ cfs_source + ');'; var top_level_source = 'define("top-level", [], function() { return ' + str_escape(top_level) + '; });'; var source = '{\n'+ stdlib.source()+'\n'+ - (json ? (json.source()+'\n') : "") + + json.source()+'\n' + fake_require + tests.lookup("tokenize")+"\n"+ tests.lookup("parse")+"\n"+ + tests.lookup("parse_json")+"\n"+ tests.lookup("bytecode-table")+"\n"+ tests.lookup("literal-map")+"\n"+ tests.lookup("bcompile")+"\n"+ @@ -77,7 +85,7 @@ define(['./parse', './bcompile', './banalyze', './bytecode-table', './top-level' */ // console.log(source); - var compile_from_source = make_compile_from_source(parse, bcompile, banalyze, LiteralMap, top_level); + var compile_from_source = make_compile_from_source(parse, parse_json, bcompile, banalyze, LiteralMap, top_level); var bc = compile_from_source(source, true/*as object*/); var lua_esc = function(str) {