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