124 lines
3.8 KiB
JavaScript
124 lines
3.8 KiB
JavaScript
var p = require('./parsec')
|
|
var c = p.combinators
|
|
var s = p.p_string
|
|
var f = p.funp
|
|
|
|
var char = function(ch) {
|
|
return c.token(p.p_string.char(ch)) }
|
|
var digit = function() {
|
|
return c.token(p.p_string.digit()) }
|
|
|
|
function literal(x, y) {
|
|
return c.bind(
|
|
c.nxtnxt.apply(null, x.split('').map(char))
|
|
, function() { return c.always(y) }) }
|
|
|
|
function many_str(parser) {
|
|
return c.bind(c.many(parser), function(x) { return c.always(x.join('')) }) }
|
|
|
|
function many1_str(parser) {
|
|
return c.bind(c.many1(parser), function(x) { return c.always(x.join('')) }) }
|
|
|
|
var line_comment = c.nxtnxt(
|
|
char('/')
|
|
, char('/')
|
|
, c.many(c.token(f.not(p.p_string.char('\n'))))
|
|
, char('\n'))
|
|
|
|
|
|
var inline_comment_content = c.next(
|
|
c.many(c.token(f.not(p.p_string.char('*'))))
|
|
, char('*'))
|
|
|
|
var inline_comment = c.nxtnxt(
|
|
char('/')
|
|
, char('*')
|
|
, c.many1(inline_comment_content)
|
|
, char('/'))
|
|
|
|
var comment = c.either(line_comment, inline_comment)
|
|
|
|
var opt_space = c.many(c.either(c.token(p.p_string.whitespace()), comment))
|
|
function between_whitespace(parser) {
|
|
return c.between(opt_space, opt_space, parser) }
|
|
|
|
function optional(def, parser) {
|
|
return c.either(parser, c.always(def)) }
|
|
|
|
// I was legitimately running out of stack space :-)
|
|
var trampoline = function() {
|
|
var i = 0
|
|
return function trampoline(parser) {
|
|
return function(state, cok, cerr, eok, eerr) {
|
|
// decrease this number if you get stack overflow errors :P
|
|
if (i++ < 15) return parser(state, cok, cerr, eok, eerr)
|
|
i=0
|
|
process.nextTick(function() { parser(state, cok, cerr, eok, eerr) })} } }()
|
|
|
|
|
|
var string = c.between(
|
|
char('"'), char('"')
|
|
, many_str(c.either(
|
|
c.token(f.not(f.or(s.control(), s.char('"'), s.char('\\'))))
|
|
// escaped chars
|
|
, c.bind(
|
|
char('\\')
|
|
, c.choice(
|
|
char('"'), char('\\'), char('/'), char('b')
|
|
, char('f'), char('n'), char('r'), char('t'))
|
|
, function(_, n) { return c.always('"\\/\b\f\n\r\t'['"\\/bfnrt'.indexOf(n)]) }))))
|
|
var number = c.bind(
|
|
// sign
|
|
optional('', char('-'))
|
|
// integer part
|
|
, c.either(char('0'), many1_str(digit()))
|
|
// fractional part
|
|
, optional('', c.next(char('.'), many1_str(digit())))
|
|
// 'e' part
|
|
, optional(0, c.bind(
|
|
c.either(char('e'), char('E'))
|
|
, c.choice(char('+'), char('-'), c.always('+'))
|
|
, many1_str(digit())
|
|
, function(_, sign, x) {
|
|
return c.always(+(sign+x)) }))
|
|
, function(sign, int, frac, exp) {
|
|
return c.always(+(sign + int + '.' + frac + 'e' + exp)) })
|
|
|
|
function array() {
|
|
return c.between(
|
|
char('['), char(']')
|
|
, c.many(c.bind(
|
|
c.wrap(value)() // avoid infinite recursion
|
|
// separator
|
|
, c.either(char(','), c.lookahead(char(']'))) // wrong: [1,2,] will work
|
|
, function(a) { return c.always(a) }))) }
|
|
|
|
function object() {
|
|
return c.between(
|
|
char('{')
|
|
, char('}')
|
|
, c.bind(
|
|
c.many(c.bind(
|
|
between_whitespace(string)
|
|
, char(':')
|
|
, c.wrap(value)() // avoid infinite recursion
|
|
, c.either(char(','), c.lookahead(char('}'))) // wrong: {"1":2,} will work
|
|
, function(k, _, v, _) {
|
|
return c.always([k, v]) }))
|
|
, function(pairs) {
|
|
var obj = {}
|
|
for (var i = 0; i < pairs.length; i++)
|
|
obj[pairs[i][0]] = pairs[i][1]
|
|
return c.always(obj)})) }
|
|
|
|
function value() {
|
|
return trampoline(between_whitespace(c.choice(
|
|
string
|
|
, number
|
|
, object()
|
|
, array()
|
|
, literal('true', true)
|
|
, literal('false', false)
|
|
, literal('null', null)))) }
|
|
module.exports.value = value
|