243 lines
8.2 KiB
JavaScript
243 lines
8.2 KiB
JavaScript
|
var errors = function() {
|
||
|
function ParseError(pos, msgs) {
|
||
|
this.msgs = [].concat(msgs)
|
||
|
this.line = pos.line
|
||
|
this.column = pos.column }
|
||
|
ParseError.prototype.show = function() {
|
||
|
return this.msgs.join(', ') +
|
||
|
" at" +
|
||
|
" line: " + this.line +
|
||
|
" column: " + this.column }
|
||
|
ParseError.prototype.combine = function(errs) {
|
||
|
if (!'length' in errs) errs = [errs]
|
||
|
return new CombinedError([this].concat(errs)) }
|
||
|
|
||
|
function CombinedError(errs) {
|
||
|
this.errs = errs ? errs : [] }
|
||
|
CombinedError.prototype.combine = function(errs) {
|
||
|
if (!'length' in errs) errs = [errs]
|
||
|
this.errs = this.errs.concat(errs) }
|
||
|
|
||
|
function unknown_error(pos) {
|
||
|
return new ParseError(pos, "Unknown error") }
|
||
|
function unexpect_error(pos, msg) {
|
||
|
return new ParseError(pos, "Unexpected " + msg) }
|
||
|
function expect_error(pos, msg) {
|
||
|
return new ParseError(pos, "Expected " + msg) }
|
||
|
|
||
|
function merge_errors() {
|
||
|
var a0 = arguments[0]
|
||
|
var ar = Array.prototype.slice.call(arguments, 1)
|
||
|
return ar.length ? a0.combine(ar) : a0 }
|
||
|
|
||
|
return {
|
||
|
Parse: ParseError
|
||
|
, unknown: unknown_error
|
||
|
, unexpect: unexpect_error
|
||
|
, expect: expect_error
|
||
|
, merge: merge_errors }}()
|
||
|
|
||
|
var p_string = function() {
|
||
|
function Parse_String_State(str) {
|
||
|
function Parse_State(pos, line, column) {
|
||
|
this.pos = pos || 0
|
||
|
this.line = line || 1
|
||
|
this.column = column || 1 }
|
||
|
Parse_State.prototype.str = str
|
||
|
Parse_State.prototype.increment = function(c) {
|
||
|
var newline = c == '\n'
|
||
|
return new Parse_State(this.pos+1, newline?this.line+1:this.line, newline?1:this.column+1) }
|
||
|
Parse_State.prototype.getToken = function() {
|
||
|
return this.str[this.pos] }
|
||
|
Parse_State.prototype.length = function() {
|
||
|
return this.str.length }
|
||
|
return Parse_State }
|
||
|
|
||
|
|
||
|
function char(x) {
|
||
|
return function(t) { return x == t }}
|
||
|
function digit() { return function(x){ var cx = x.charCodeAt(0); return cx < 58 && cx > 47 }}
|
||
|
function letter() {
|
||
|
return function(x) {
|
||
|
var cx = x.charCodeAt(0)
|
||
|
return (cx < 91 && cx > 64) || (cx < 123 && cx > 96) }}
|
||
|
function whitespace() {
|
||
|
return function(x) {
|
||
|
return x == ' ' || x == '\t' || x == '\r' || x == '\n' }}
|
||
|
|
||
|
function control() {
|
||
|
return function(x) {
|
||
|
return x.charCodeAt(0) < 32 }}
|
||
|
|
||
|
function run(p, input, cb) {
|
||
|
var str_state = new (Parse_String_State(input))()
|
||
|
function pok(x) {
|
||
|
cb(null, x) }
|
||
|
function perr(err) {
|
||
|
cb(err) }
|
||
|
p(str_state, pok, perr, pok, perr) }
|
||
|
|
||
|
return {
|
||
|
Parse_State: Parse_String_State
|
||
|
, char: char
|
||
|
, digit: digit
|
||
|
, letter: letter
|
||
|
, whitespace: whitespace
|
||
|
, control: control
|
||
|
, run: run }}()
|
||
|
|
||
|
|
||
|
var combinators = function() {
|
||
|
function always(x) {
|
||
|
return function(state, cok, cerr, eok, eerr) {
|
||
|
eok(x, state) }}
|
||
|
|
||
|
// avoiding infinite recursions
|
||
|
function wrap(f) {
|
||
|
return function(x) {
|
||
|
return function(state, cok, cerr, eok, eerr) {
|
||
|
return f(x)(state, cok, cerr, eok, eerr) }}}
|
||
|
|
||
|
function bind_2(p, f) {
|
||
|
return function(state, cok, cerr, eok, eerr) {
|
||
|
function pcok(item, state) {
|
||
|
var q = f(item)
|
||
|
q(state, cok, cerr, cok, cerr) }
|
||
|
function peok(item, state) {
|
||
|
wrap(f)(item)(state, cok, cerr, eok, eerr) }
|
||
|
p(state, pcok, cerr, peok, eerr) }}
|
||
|
|
||
|
function bind() {
|
||
|
var ar = arguments, ari, arn = ar.length - 1
|
||
|
if (arn == 1) return bind_2(arguments[0], arguments[1])
|
||
|
var last = ar[arn]
|
||
|
var res = new Array(arn), resi
|
||
|
return function(state, cok, cerr, eok, eerr) {
|
||
|
ari = resi = 0;
|
||
|
bind(ar[ari++], function bind_arg2(x) {
|
||
|
res[resi++] = x
|
||
|
if (resi == arn) return last.apply(this, res)
|
||
|
else return bind(ar[ari++], bind_arg2) })
|
||
|
(state, cok, cerr, eok, eerr)}}
|
||
|
|
||
|
function next(p, q) {
|
||
|
return bind(p, function() { return q }) }
|
||
|
|
||
|
function nxtnxt() {
|
||
|
var ar = Array.prototype.slice.call(arguments)
|
||
|
return ar.reduceRight(function(p, c) {
|
||
|
return next(c, p) }) }
|
||
|
|
||
|
function never(err) {
|
||
|
return function(state, cok, cerr, eok, eerr) {
|
||
|
eerr(errors.unknown(state)) } }
|
||
|
|
||
|
function either(p, q) {
|
||
|
return function(state, cok, cerr, eok, eerr) {
|
||
|
function p_eerr(err_from_p) {
|
||
|
function q_eerr(err_from_q) {
|
||
|
eerr(errors.merge(err_from_p, err_from_q)) }
|
||
|
q(state, cok, cerr, eok, q_eerr) }
|
||
|
p(state, cok, cerr, eok, p_eerr) } }
|
||
|
|
||
|
function attempt(p) {
|
||
|
return function(state, cok, cerr, eok, eerr) {
|
||
|
p(state, cok, eerr, eok, eerr) }}
|
||
|
|
||
|
function token(consume_p) {
|
||
|
return function(state, cok, cerr, eok, eerr) {
|
||
|
var t = state.getToken()
|
||
|
if (!t) eerr(errors.unexpect(state, 'end of input'))
|
||
|
else if (consume_p(t)) cok(t, state.increment(t))
|
||
|
else eerr(errors.unexpect(state, "token " + t)) } }
|
||
|
|
||
|
function times(n, p) {
|
||
|
if (n == 0) return always(null)
|
||
|
return function (state, cok, cerr, eok, eerr) {
|
||
|
var res = new Array(n)
|
||
|
var resi = 0
|
||
|
function pcok(item, state) {
|
||
|
function peok(item, state) {
|
||
|
while(resi < n) res[resi++] = item
|
||
|
cok(res, state) }
|
||
|
res[resi++] = item
|
||
|
if (resi < n) p(state, pcok, cerr, pcok, eerr)
|
||
|
else if (resi == n) cok(res, state) }
|
||
|
function peok(item, state) {
|
||
|
while(resi < n) res[resi++] = item
|
||
|
eok(res, state) }
|
||
|
p(state, pcok, cerr, peok, eerr) } }
|
||
|
|
||
|
function many(p) {
|
||
|
function many_err() {
|
||
|
throw new TypeError('`many` applied to parser that accepts an empty string') }
|
||
|
function safe_p(state, cok, cerr, eok, eerr) {
|
||
|
p(state, cok, cerr, many_err, eerr) }
|
||
|
return either(
|
||
|
bind(safe_p, wrap(many)(p), function(x, xs) {
|
||
|
return always([x].concat(xs)) })
|
||
|
, always([])) }
|
||
|
|
||
|
function many1(p) {
|
||
|
return bind(p, many(p), function(x, xs) {
|
||
|
return always([x].concat(xs) )})}
|
||
|
|
||
|
function lookahead(p) {
|
||
|
return function(state, cok, cerr, eok, eerr) {
|
||
|
function ok(t) {
|
||
|
eok(t, state) }
|
||
|
p(state, ok, cerr, eok, eerr) } }
|
||
|
|
||
|
function choice(first) {
|
||
|
/* either + varargs */
|
||
|
var ar = Array.prototype.slice.call(arguments, 1)
|
||
|
if (ar.length == 0 && !first) return never
|
||
|
if (ar.length == 0 && first) return first
|
||
|
return either(first, choice.apply(null, ar)) }
|
||
|
|
||
|
function eof() {
|
||
|
return function(state, cok, cerr, eok, eerr) {
|
||
|
if (state.pos == state.length()) eok(null, state)
|
||
|
else eerr(errors.expect(state, "end of input")) } }
|
||
|
|
||
|
function between(open, close, p) {
|
||
|
return bind(open, p, close, function(o, x, c) { return always(x) }) }
|
||
|
|
||
|
return {
|
||
|
always: always
|
||
|
, bind: bind
|
||
|
, next: next
|
||
|
, nxtnxt: nxtnxt
|
||
|
, never: never
|
||
|
, wrap: wrap
|
||
|
, either: either
|
||
|
, attempt: attempt
|
||
|
, many: many
|
||
|
, many1: many1
|
||
|
, times: times
|
||
|
, token: token
|
||
|
, lookahead: lookahead
|
||
|
, choice: choice
|
||
|
, between: between
|
||
|
, eof: eof }}()
|
||
|
|
||
|
var funp = function() {
|
||
|
// functional programming helpers, especially useful with token()
|
||
|
function not(f) {
|
||
|
return function(x) { return !f(x) }}
|
||
|
function and(p, q) {
|
||
|
return function(x) { return p(x) && q(x) }}
|
||
|
function or(p, q) { /* todo more than 2 args */
|
||
|
if (arguments.length > 2) return Array.prototype.slice.call(arguments).reduce(function(p,q){return or(p,q)})
|
||
|
return function(x) { return p(x) || q(x) }}
|
||
|
return {
|
||
|
not: not
|
||
|
, and: and
|
||
|
, or: or }}()
|
||
|
|
||
|
module.exports = {
|
||
|
combinators: combinators
|
||
|
, errors: errors
|
||
|
, p_string: p_string
|
||
|
, funp: funp }
|