| 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); |
| }); |
| }); |