| var Parser = P(function(_, super_, Parser) { | |
| // The Parser object is a wrapper for a parser function. | |
| // Externally, you use one to parse a string by calling | |
| // var result = SomeParser.parse('Me Me Me! Parse Me!'); | |
| // You should never call the constructor, rather you should | |
| // construct your Parser from the base parsers and the | |
| // parser combinator methods. | |
| function parseError(stream, message) { | |
| if (stream) { | |
| stream = "'"+stream+"'"; | |
| } | |
| else { | |
| stream = 'EOF'; | |
| } | |
| throw 'Parse Error: '+message+' at '+stream; | |
| } | |
| _.init = function(body) { this._ = body; }; | |
| _.parse = function(stream) { | |
| return this.skip(eof)._(''+stream, success, parseError); | |
| function success(stream, result) { return result; } | |
| }; | |
| // -*- primitive combinators -*- // | |
| _.or = function(alternative) { | |
| pray('or is passed a parser', alternative instanceof Parser); | |
| var self = this; | |
| return Parser(function(stream, onSuccess, onFailure) { | |
| return self._(stream, onSuccess, failure); | |
| function failure(newStream) { | |
| return alternative._(stream, onSuccess, onFailure); | |
| } | |
| }); | |
| }; | |
| _.then = function(next) { | |
| var self = this; | |
| return Parser(function(stream, onSuccess, onFailure) { | |
| return self._(stream, success, onFailure); | |
| function success(newStream, result) { | |
| var nextParser = (next instanceof Parser ? next : next(result)); | |
| pray('a parser is returned', nextParser instanceof Parser); | |
| return nextParser._(newStream, onSuccess, onFailure); | |
| } | |
| }); | |
| }; | |
| // -*- optimized iterative combinators -*- // | |
| _.many = function() { | |
| var self = this; | |
| return Parser(function(stream, onSuccess, onFailure) { | |
| var xs = []; | |
| while (self._(stream, success, failure)); | |
| return onSuccess(stream, xs); | |
| function success(newStream, x) { | |
| stream = newStream; | |
| xs.push(x); | |
| return true; | |
| } | |
| function failure() { | |
| return false; | |
| } | |
| }); | |
| }; | |
| _.times = function(min, max) { | |
| if (arguments.length < 2) max = min; | |
| var self = this; | |
| return Parser(function(stream, onSuccess, onFailure) { | |
| var xs = []; | |
| var result = true; | |
| var failure; | |
| for (var i = 0; i < min; i += 1) { | |
| result = self._(stream, success, firstFailure); | |
| if (!result) return onFailure(stream, failure); | |
| } | |
| for (; i < max && result; i += 1) { | |
| result = self._(stream, success, secondFailure); | |
| } | |
| return onSuccess(stream, xs); | |
| function success(newStream, x) { | |
| xs.push(x); | |
| stream = newStream; | |
| return true; | |
| } | |
| function firstFailure(newStream, msg) { | |
| failure = msg; | |
| stream = newStream; | |
| return false; | |
| } | |
| function secondFailure(newStream, msg) { | |
| return false; | |
| } | |
| }); | |
| }; | |
| // -*- higher-level combinators -*- // | |
| _.result = function(res) { return this.then(succeed(res)); }; | |
| _.atMost = function(n) { return this.times(0, n); }; | |
| _.atLeast = function(n) { | |
| var self = this; | |
| return self.times(n).then(function(start) { | |
| return self.many().map(function(end) { | |
| return start.concat(end); | |
| }); | |
| }); | |
| }; | |
| _.map = function(fn) { | |
| return this.then(function(result) { return succeed(fn(result)); }); | |
| }; | |
| _.skip = function(two) { | |
| return this.then(function(result) { return two.result(result); }); | |
| }; | |
| // -*- primitive parsers -*- // | |
| var string = this.string = function(str) { | |
| var len = str.length; | |
| var expected = "expected '"+str+"'"; | |
| return Parser(function(stream, onSuccess, onFailure) { | |
| var head = stream.slice(0, len); | |
| if (head === str) { | |
| return onSuccess(stream.slice(len), head); | |
| } | |
| else { | |
| return onFailure(stream, expected); | |
| } | |
| }); | |
| }; | |
| var regex = this.regex = function(re) { | |
| pray('regexp parser is anchored', re.toString().charAt(1) === '^'); | |
| var expected = 'expected '+re; | |
| return Parser(function(stream, onSuccess, onFailure) { | |
| var match = re.exec(stream); | |
| if (match) { | |
| var result = match[0]; | |
| return onSuccess(stream.slice(result.length), result); | |
| } | |
| else { | |
| return onFailure(stream, expected); | |
| } | |
| }); | |
| }; | |
| var succeed = Parser.succeed = function(result) { | |
| return Parser(function(stream, onSuccess) { | |
| return onSuccess(stream, result); | |
| }); | |
| }; | |
| var fail = Parser.fail = function(msg) { | |
| return Parser(function(stream, _, onFailure) { | |
| return onFailure(stream, msg); | |
| }); | |
| }; | |
| var letter = Parser.letter = regex(/^[a-z]/i); | |
| var letters = Parser.letters = regex(/^[a-z]*/i); | |
| var digit = Parser.digit = regex(/^[0-9]/); | |
| var digits = Parser.digits = regex(/^[0-9]*/); | |
| var whitespace = Parser.whitespace = regex(/^\s+/); | |
| var optWhitespace = Parser.optWhitespace = regex(/^\s*/); | |
| var any = Parser.any = Parser(function(stream, onSuccess, onFailure) { | |
| if (!stream) return onFailure(stream, 'expected any character'); | |
| return onSuccess(stream.slice(1), stream.charAt(0)); | |
| }); | |
| var all = Parser.all = Parser(function(stream, onSuccess, onFailure) { | |
| return onSuccess('', stream); | |
| }); | |
| var eof = Parser.eof = Parser(function(stream, onSuccess, onFailure) { | |
| if (stream) return onFailure(stream, 'expected EOF'); | |
| return onSuccess(stream, stream); | |
| }); | |
| }); |