Skip to content

Commit

Permalink
Add simplified version of JS parser to handle JSON parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
cscott committed Dec 7, 2022
1 parent dc0eb74 commit 29e5181
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 12 deletions.
43 changes: 43 additions & 0 deletions bin/parse_json.js
Original file line number Diff line number Diff line change
@@ -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);
});
});
200 changes: 200 additions & 0 deletions parse_json.js
Original file line number Diff line number Diff line change
@@ -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;

});
9 changes: 6 additions & 3 deletions tests.js
Original file line number Diff line number Diff line change
@@ -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 */;
Expand Down
26 changes: 17 additions & 9 deletions write-lua-bytecode.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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) {
Expand All @@ -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"+
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 29e5181

Please sign in to comment.