Created
November 5, 2014 15:20
-
-
Save susisu/91ef172b7ed1a8820fe1 to your computer and use it in GitHub Desktop.
Simple JSON Parser with Loquat https://github.com/susisu/Loquat
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Simple JSON Parser | |
* http://www.json.org/index.html | |
*/ | |
var lq = require("loquat"); | |
var util = require("util"); | |
/* | |
* Parsers | |
*/ | |
var json_symbol_colon = lexeme(lq.char(":")).label("colon"); | |
var json_symbol_comma = lexeme(lq.char(",")).label("comma"); | |
var json_symbol_open_brace = lexeme(lq.char("{")).label("open brace"); | |
var json_symbol_close_brace = lexeme(lq.char("}")).label("close brace"); | |
var json_symbol_open_bracket = lexeme(lq.char("[")).label("open bracket"); | |
var json_symbol_close_bracket = lexeme(lq.char("]")).label("close bracket"); | |
var json_value = new lq.LazyParser(function () { | |
return lq.choice([ | |
json_string, | |
json_number, | |
json_object, | |
json_array, | |
json_true, | |
json_false, | |
json_null | |
]); | |
}) | |
.label("value"); | |
var json_string = lexeme( | |
lq.manyChar( | |
lq.choice([ | |
lq.noneOf("\"\\\b\f\n\r\t"), | |
lq.char("\\").then( | |
lq.fmap(unescapeControlChar)(lq.oneOf("\"\\/bnfrt")), | |
lq.fmap(unescapeUnicodeChar)( | |
lq.char("u").then(lq.fmap(join)(lq.hexDigit.count(4))) | |
) | |
) | |
]) | |
) | |
.between(lq.char("\""), lq.char("\"")) | |
) | |
.label("string"); | |
var json_number = lexeme( | |
lq.fmap(parseFloat)( | |
lq.fmap(join)( | |
lq.sequence([ | |
lq.option("", lq.char("-")), | |
lq.choice([ | |
lq.char("0"), | |
lq.fmap(join)( | |
lq.sequence([ | |
lq.oneOf("123456789"), | |
lq.manyChar(lq.digit) | |
]) | |
) | |
]), | |
lq.option( | |
"", | |
lq.fmap(join)( | |
lq.sequence([ | |
lq.char("."), | |
lq.manyChar1(lq.digit) | |
]) | |
) | |
), | |
lq.option( | |
"", | |
lq.fmap(join)( | |
lq.sequence([ | |
lq.oneOf("eE"), | |
lq.option("", lq.oneOf("+-")), | |
lq.manyChar1(lq.digit) | |
]) | |
) | |
) | |
]) | |
) | |
) | |
) | |
.label("number"); | |
var json_object = lq.sequence([json_string, json_symbol_colon, json_value]) | |
.sepBy(json_symbol_comma) | |
.between(json_symbol_open_brace, json_symbol_close_brace) | |
.bind(function (key_values) { | |
var obj = {}; | |
for (var i = 0; i < key_values.length; i ++) { | |
obj[key_values[i][0]] = key_values[i][2]; | |
} | |
return lq.pure(obj); | |
}) | |
.label("object"); | |
var json_array = json_value | |
.sepBy(json_symbol_comma) | |
.between(json_symbol_open_bracket, json_symbol_close_bracket) | |
.label("array"); | |
var json_true = lexeme(lq.string("true").try().then(lq.pure(true))).label("true"); | |
var json_false = lexeme(lq.string("false").try().then(lq.pure(false))).label("false"); | |
var json_null = lexeme(lq.string("null").try().then(lq.pure(null))).label("null"); | |
/* | |
* Utility Functions | |
*/ | |
function lexeme (parser) { | |
return parser.left(lq.spaces); | |
} | |
function join (array) { | |
return array.join(""); | |
} | |
function unescapeControlChar (char) { | |
switch (char) { | |
case "\"": return "\""; | |
case "\\": return "\\"; | |
case "/" : return "/"; | |
case "b" : return "\b"; | |
case "n" : return "\n"; | |
case "f" : return "\f"; | |
case "r" : return "\r"; | |
case "t" : return "\t"; | |
default : return char; | |
} | |
} | |
function unescapeUnicodeChar (codeStr) { | |
return String.fromCharCode(parseInt(codeStr, 16)); | |
} | |
/* | |
* Main | |
*/ | |
var parser = lq.spaces.then(json_value).left(lq.eof); | |
process.stdin.setEncoding("utf8"); | |
process.stdout.write("> "); | |
process.stdin.on("readable", function () { | |
var chunk = process.stdin.read(); | |
if (chunk !== null) { | |
try { | |
console.time("parse"); | |
var result = lq.parse(parser, "", chunk); | |
console.timeEnd("parse"); | |
if (result.succeeded) { | |
process.stdout.write( | |
util.inspect(result.value, { colors: true }) + "\n" | |
); | |
} | |
else { | |
process.stdout.write(lq.show(result.error) + "\n"); | |
} | |
process.stdout.write("> "); | |
} | |
catch (error) { | |
console.error(error); | |
} | |
} | |
}); | |
process.stdin.on("end", function() { | |
process.stdout.write("\nbye\n"); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment