Comparing version 0.7.0 to 0.7.1
@@ -57,5 +57,18 @@ // pass | ||
// For ensuring we have the right argument types | ||
function assertParser(p) { | ||
if (!(p instanceof Parser)) throw new Error('not a parser: '+p); | ||
} | ||
function assertNumber(x) { | ||
if (typeof x !== 'number') throw new Error('not a number: '+x); | ||
} | ||
function assertRegexp(x) { | ||
if (!(x instanceof RegExp)) throw new Error('not a regex: '+x); | ||
} | ||
function assertFunction(x) { | ||
if (typeof x !== 'function') throw new Error('not a function: '+x); | ||
} | ||
function assertString(x) { | ||
if (typeof x !== 'string') throw new Error('not a string: '+x) | ||
} | ||
@@ -69,3 +82,4 @@ function formatExpected(expected) { | ||
function formatGot(stream, error) { | ||
var i = error.index; | ||
var index = error.index; | ||
var i = index.offset; | ||
@@ -78,3 +92,4 @@ if (i === stream.length) return ', got the end of the stream' | ||
return ' at character ' + i + ', got ' + prefix + stream.slice(i, i+12) + suffix | ||
return ' at line ' + index.line + ' column ' + index.column | ||
+ ', got ' + prefix + stream.slice(i, i+12) + suffix | ||
} | ||
@@ -87,2 +102,5 @@ | ||
_.parse = function(stream) { | ||
if (typeof stream !== 'string') { | ||
throw new Error('.parse must be called with a string as its argument'); | ||
} | ||
var result = this.skip(eof)._(stream, 0); | ||
@@ -95,3 +113,3 @@ | ||
status: false, | ||
index: result.furthest, | ||
index: makeLineColumnIndex(stream, result.furthest), | ||
expected: result.expected | ||
@@ -106,2 +124,6 @@ }; | ||
for (var j = 0; j < numParsers; j += 1) { | ||
assertParser(parsers[j]); | ||
} | ||
return Parser(function(stream, i) { | ||
@@ -143,2 +165,6 @@ var result; | ||
for (var j = 0; j < numParsers; j += 1) { | ||
assertParser(parsers[j]); | ||
} | ||
return Parser(function(stream, i) { | ||
@@ -154,2 +180,20 @@ var result; | ||
var sepBy = Parsimmon.sepBy = function(parser, separator) { | ||
// Argument asserted by sepBy1 | ||
return sepBy1(parser, separator).or(Parsimmon.of([])); | ||
}; | ||
var sepBy1 = Parsimmon.sepBy1 = function(parser, separator) { | ||
assertParser(parser); | ||
assertParser(separator); | ||
var pairs = separator.then(parser).many(); | ||
return parser.chain(function(r) { | ||
return pairs.map(function(rs) { | ||
return [r].concat(rs); | ||
}) | ||
}) | ||
}; | ||
// -*- primitive combinators -*- // | ||
@@ -229,2 +273,5 @@ _.or = function(alternative) { | ||
assertNumber(min); | ||
assertNumber(max); | ||
return Parser(function(stream, i) { | ||
@@ -271,2 +318,5 @@ var accum = []; | ||
_.map = function(fn) { | ||
assertFunction(fn); | ||
var self = this; | ||
@@ -304,2 +354,4 @@ return Parser(function(stream, i) { | ||
assertString(str); | ||
return Parser(function(stream, i) { | ||
@@ -318,2 +370,6 @@ var head = stream.slice(i, i+len); | ||
var regex = Parsimmon.regex = function(re, group) { | ||
assertRegexp(re); | ||
if (group) assertNumber(group); | ||
var anchored = RegExp('^(?:'+re.source+')', (''+re).slice((''+re).lastIndexOf('/')+1)); | ||
@@ -370,2 +426,4 @@ var expected = '' + re; | ||
var test = Parsimmon.test = function(predicate) { | ||
assertFunction(predicate); | ||
return Parser(function(stream, i) { | ||
@@ -391,2 +449,4 @@ var char = stream.charAt(i); | ||
var takeWhile = Parsimmon.takeWhile = function(predicate) { | ||
assertFunction(predicate); | ||
return Parser(function(stream, i) { | ||
@@ -415,6 +475,22 @@ var j = i; | ||
var index = Parsimmon.index = Parser(function(stream, i) { | ||
return makeSuccess(i, i); | ||
}); | ||
var makeLineColumnIndex = function(stream, i) { | ||
var lines = stream.slice(0, i).split("\n"); | ||
// Note that unlike the character offset, the line and column offsets are | ||
// 1-based. | ||
var lineWeAreUpTo = lines.length; | ||
var columnWeAreUpTo = lines[lines.length - 1].length + 1; | ||
return { | ||
offset: i, | ||
line: lineWeAreUpTo, | ||
column: columnWeAreUpTo | ||
}; | ||
}; | ||
var index | ||
= Parsimmon.index | ||
= Parser(function(stream, i) { | ||
return makeSuccess(i, makeLineColumnIndex(stream, i)); | ||
}); | ||
//- fantasyland compat | ||
@@ -421,0 +497,0 @@ |
@@ -1,1 +0,1 @@ | ||
var Parsimmon={};Parsimmon.Parser=function(){"use strict";function r(n){if(!(this instanceof r))return new r(n);this._=n}var n=r.prototype;function t(r,n){return{status:true,index:r,value:n,furthest:-1,expected:[]}}function e(r,n){return{status:false,index:-1,value:null,furthest:r,expected:[n]}}function u(r,n){if(!n)return r;if(r.furthest>n.furthest)return r;var t=r.furthest===n.furthest?r.expected.concat(n.expected):n.expected;return{status:r.status,index:r.index,value:r.value,furthest:n.furthest,expected:t}}function a(n){if(!(n instanceof r))throw new Error("not a parser: "+n)}function i(r){if(r.length===1)return r[0];return"one of "+r.join(", ")}function s(r,n){var t=n.index;if(t===r.length)return", got the end of the stream";var e=t>0?"'...":"'";var u=r.length-t>12?"...'":"'";return" at character "+t+", got "+e+r.slice(t,t+12)+u}var o=Parsimmon.formatError=function(r,n){return"expected "+i(n.expected)+s(r,n)};n.parse=function(r){var n=this.skip(E)._(r,0);return n.status?{status:true,value:n.value}:{status:false,index:n.furthest,expected:n.expected}};var f=Parsimmon.seq=function(){var n=[].slice.call(arguments);var e=n.length;return r(function(r,a){var i;var s=new Array(e);for(var o=0;o<e;o+=1){i=u(n[o]._(r,a),i);if(!i.status)return i;s[o]=i.value;a=i.index}return u(t(a,s),i)})};var c=Parsimmon.seqMap=function(){var r=[].slice.call(arguments);var n=r.pop();return f.apply(null,r).map(function(r){return n.apply(null,r)})};var v=Parsimmon.custom=function(n){return r(n(t,e))};var l=Parsimmon.alt=function(){var n=[].slice.call(arguments);var t=n.length;if(t===0)return d("zero alternates");return r(function(r,t){var e;for(var a=0;a<n.length;a+=1){e=u(n[a]._(r,t),e);if(e.status)return e}return e})};n.or=function(r){return l(this,r)};n.then=function(r){if(typeof r==="function"){throw new Error("chaining features of .then are no longer supported, use .chain instead")}a(r);return f(this,r).map(function(r){return r[1]})};n.many=function(){var n=this;return r(function(r,e){var a=[];var i;var s;for(;;){i=u(n._(r,e),i);if(i.status){e=i.index;a.push(i.value)}else{return u(t(e,a),i)}}})};n.times=function(n,e){if(arguments.length<2)e=n;var a=this;return r(function(r,i){var s=[];var o=i;var f;var c;for(var v=0;v<n;v+=1){f=a._(r,i);c=u(f,c);if(f.status){i=f.index;s.push(f.value)}else return c}for(;v<e;v+=1){f=a._(r,i);c=u(f,c);if(f.status){i=f.index;s.push(f.value)}else break}return u(t(i,s),c)})};n.result=function(r){return this.map(function(n){return r})};n.atMost=function(r){return this.times(0,r)};n.atLeast=function(r){var n=this;return c(this.times(r),this.many(),function(r,n){return r.concat(n)})};n.map=function(n){var e=this;return r(function(r,a){var i=e._(r,a);if(!i.status)return i;return u(t(i.index,n(i.value)),i)})};n.skip=function(r){return f(this,r).map(function(r){return r[0]})};n.mark=function(){return c(b,this,b,function(r,n,t){return{start:r,value:n,end:t}})};n.desc=function(n){var t=this;return r(function(r,e){var u=t._(r,e);if(!u.status)u.expected=[n];return u})};var m=Parsimmon.string=function(n){var u=n.length;var a="'"+n+"'";return r(function(r,i){var s=r.slice(i,i+u);if(s===n){return t(i+u,s)}else{return e(i,a)}})};var h=Parsimmon.regex=function(n,u){var a=RegExp("^(?:"+n.source+")",(""+n).slice((""+n).lastIndexOf("/")+1));var i=""+n;if(u==null)u=0;return r(function(r,n){var s=a.exec(r.slice(n));if(s){var o=s[0];var f=s[u];if(f!=null)return t(n+o.length,f)}return e(n,i)})};var p=Parsimmon.succeed=function(n){return r(function(r,e){return t(e,n)})};var d=Parsimmon.fail=function(n){return r(function(r,t){return e(t,n)})};var g=Parsimmon.letter=h(/[a-z]/i).desc("a letter");var x=Parsimmon.letters=h(/[a-z]*/i);var P=Parsimmon.digit=h(/[0-9]/).desc("a digit");var _=Parsimmon.digits=h(/[0-9]*/);var y=Parsimmon.whitespace=h(/\s+/).desc("whitespace");var w=Parsimmon.optWhitespace=h(/\s*/);var O=Parsimmon.any=r(function(r,n){if(n>=r.length)return e(n,"any character");return t(n+1,r.charAt(n))});var k=Parsimmon.all=r(function(r,n){return t(r.length,r.slice(n))});var E=Parsimmon.eof=r(function(r,n){if(n<r.length)return e(n,"EOF");return t(n,null)});var z=Parsimmon.test=function(n){return r(function(r,u){var a=r.charAt(u);if(u<r.length&&n(a)){return t(u+1,a)}else{return e(u,"a character matching "+n)}})};var A=Parsimmon.oneOf=function(r){return z(function(n){return r.indexOf(n)>=0})};var q=Parsimmon.noneOf=function(r){return z(function(n){return r.indexOf(n)<0})};var M=Parsimmon.takeWhile=function(n){return r(function(r,e){var u=e;while(u<r.length&&n(r.charAt(u)))u+=1;return t(u,r.slice(e,u))})};var W=Parsimmon.lazy=function(n,t){if(arguments.length<2){t=n;n=undefined}var e=r(function(r,n){e._=t()._;return e._(r,n)});if(n)e=e.desc(n);return e};var b=Parsimmon.index=r(function(r,n){return t(n,n)});n.concat=n.or;n.empty=d("empty");n.of=r.of=Parsimmon.of=p;n.ap=function(r){return c(this,r,function(r,n){return r(n)})};n.chain=function(n){var t=this;return r(function(r,e){var a=t._(r,e);if(!a.status)return a;var i=n(a.value);return u(i._(r,a.index),a)})};return r}(); | ||
var Parsimmon={};Parsimmon.Parser=function(){"use strict";function r(n){if(!(this instanceof r))return new r(n);this._=n}var n=r.prototype;function t(r,n){return{status:true,index:r,value:n,furthest:-1,expected:[]}}function e(r,n){return{status:false,index:-1,value:null,furthest:r,expected:[n]}}function i(r,n){if(!n)return r;if(r.furthest>n.furthest)return r;var t=r.furthest===n.furthest?r.expected.concat(n.expected):n.expected;return{status:r.status,index:r.index,value:r.value,furthest:n.furthest,expected:t}}function u(n){if(!(n instanceof r))throw new Error("not a parser: "+n)}function a(r){if(typeof r!=="number")throw new Error("not a number: "+r)}function o(r){if(!(r instanceof RegExp))throw new Error("not a regex: "+r)}function s(r){if(typeof r!=="function")throw new Error("not a function: "+r)}function f(r){if(typeof r!=="string")throw new Error("not a string: "+r)}function c(r){if(r.length===1)return r[0];return"one of "+r.join(", ")}function v(r,n){var t=n.index;var e=t.offset;if(e===r.length)return", got the end of the stream";var i=e>0?"'...":"'";var u=r.length-e>12?"...'":"'";return" at line "+t.line+" column "+t.column+", got "+i+r.slice(e,e+12)+u}var l=Parsimmon.formatError=function(r,n){return"expected "+c(n.expected)+v(r,n)};n.parse=function(r){if(typeof r!=="string"){throw new Error(".parse must be called with a string as its argument")}var n=this.skip(M)._(r,0);return n.status?{status:true,value:n.value}:{status:false,index:L(r,n.furthest),expected:n.expected}};var m=Parsimmon.seq=function(){var n=[].slice.call(arguments);var e=n.length;for(var a=0;a<e;a+=1){u(n[a])}return r(function(r,u){var a;var o=new Array(e);for(var s=0;s<e;s+=1){a=i(n[s]._(r,u),a);if(!a.status)return a;o[s]=a.value;u=a.index}return i(t(u,o),a)})};var h=Parsimmon.seqMap=function(){var r=[].slice.call(arguments);var n=r.pop();return m.apply(null,r).map(function(r){return n.apply(null,r)})};var p=Parsimmon.custom=function(n){return r(n(t,e))};var d=Parsimmon.alt=function(){var n=[].slice.call(arguments);var t=n.length;if(t===0)return _("zero alternates");for(var e=0;e<t;e+=1){u(n[e])}return r(function(r,t){var e;for(var u=0;u<n.length;u+=1){e=i(n[u]._(r,t),e);if(e.status)return e}return e})};var g=Parsimmon.sepBy=function(r,n){return x(r,n).or(Parsimmon.of([]))};var x=Parsimmon.sepBy1=function(r,n){u(r);u(n);var t=n.then(r).many();return r.chain(function(r){return t.map(function(n){return[r].concat(n)})})};n.or=function(r){return d(this,r)};n.then=function(r){if(typeof r==="function"){throw new Error("chaining features of .then are no longer supported, use .chain instead")}u(r);return m(this,r).map(function(r){return r[1]})};n.many=function(){var n=this;return r(function(r,e){var u=[];var a;var o;for(;;){a=i(n._(r,e),a);if(a.status){e=a.index;u.push(a.value)}else{return i(t(e,u),a)}}})};n.times=function(n,e){if(arguments.length<2)e=n;var u=this;a(n);a(e);return r(function(r,a){var o=[];var s=a;var f;var c;for(var v=0;v<n;v+=1){f=u._(r,a);c=i(f,c);if(f.status){a=f.index;o.push(f.value)}else return c}for(;v<e;v+=1){f=u._(r,a);c=i(f,c);if(f.status){a=f.index;o.push(f.value)}else break}return i(t(a,o),c)})};n.result=function(r){return this.map(function(n){return r})};n.atMost=function(r){return this.times(0,r)};n.atLeast=function(r){var n=this;return h(this.times(r),this.many(),function(r,n){return r.concat(n)})};n.map=function(n){s(n);var e=this;return r(function(r,u){var a=e._(r,u);if(!a.status)return a;return i(t(a.index,n(a.value)),a)})};n.skip=function(r){return m(this,r).map(function(r){return r[0]})};n.mark=function(){return h(C,this,C,function(r,n,t){return{start:r,value:n,end:t}})};n.desc=function(n){var t=this;return r(function(r,e){var i=t._(r,e);if(!i.status)i.expected=[n];return i})};var P=Parsimmon.string=function(n){var i=n.length;var u="'"+n+"'";f(n);return r(function(r,a){var o=r.slice(a,a+i);if(o===n){return t(a+i,o)}else{return e(a,u)}})};var w=Parsimmon.regex=function(n,i){o(n);if(i)a(i);var u=RegExp("^(?:"+n.source+")",(""+n).slice((""+n).lastIndexOf("/")+1));var s=""+n;if(i==null)i=0;return r(function(r,n){var a=u.exec(r.slice(n));if(a){var o=a[0];var f=a[i];if(f!=null)return t(n+o.length,f)}return e(n,s)})};var y=Parsimmon.succeed=function(n){return r(function(r,e){return t(e,n)})};var _=Parsimmon.fail=function(n){return r(function(r,t){return e(t,n)})};var E=Parsimmon.letter=w(/[a-z]/i).desc("a letter");var O=Parsimmon.letters=w(/[a-z]*/i);var k=Parsimmon.digit=w(/[0-9]/).desc("a digit");var b=Parsimmon.digits=w(/[0-9]*/);var z=Parsimmon.whitespace=w(/\s+/).desc("whitespace");var A=Parsimmon.optWhitespace=w(/\s*/);var q=Parsimmon.any=r(function(r,n){if(n>=r.length)return e(n,"any character");return t(n+1,r.charAt(n))});var B=Parsimmon.all=r(function(r,n){return t(r.length,r.slice(n))});var M=Parsimmon.eof=r(function(r,n){if(n<r.length)return e(n,"EOF");return t(n,null)});var R=Parsimmon.test=function(n){s(n);return r(function(r,i){var u=r.charAt(i);if(i<r.length&&n(u)){return t(i+1,u)}else{return e(i,"a character matching "+n)}})};var W=Parsimmon.oneOf=function(r){return R(function(n){return r.indexOf(n)>=0})};var j=Parsimmon.noneOf=function(r){return R(function(n){return r.indexOf(n)<0})};var F=Parsimmon.takeWhile=function(n){s(n);return r(function(r,e){var i=e;while(i<r.length&&n(r.charAt(i)))i+=1;return t(i,r.slice(e,i))})};var I=Parsimmon.lazy=function(n,t){if(arguments.length<2){t=n;n=undefined}var e=r(function(r,n){e._=t()._;return e._(r,n)});if(n)e=e.desc(n);return e};var L=function(r,n){var t=r.slice(0,n).split("\n");var e=t.length;var i=t[t.length-1].length+1;return{offset:n,line:e,column:i}};var C=Parsimmon.index=r(function(r,n){return t(n,L(r,n))});n.concat=n.or;n.empty=_("empty");n.of=r.of=Parsimmon.of=y;n.ap=function(r){return h(this,r,function(r,n){return r(n)})};n.chain=function(n){var t=this;return r(function(r,e){var u=t._(r,e);if(!u.status)return u;var a=n(u.value);return i(a._(r,u.index),u)})};return r}(); |
@@ -57,5 +57,18 @@ // pass | ||
// For ensuring we have the right argument types | ||
function assertParser(p) { | ||
if (!(p instanceof Parser)) throw new Error('not a parser: '+p); | ||
} | ||
function assertNumber(x) { | ||
if (typeof x !== 'number') throw new Error('not a number: '+x); | ||
} | ||
function assertRegexp(x) { | ||
if (!(x instanceof RegExp)) throw new Error('not a regex: '+x); | ||
} | ||
function assertFunction(x) { | ||
if (typeof x !== 'function') throw new Error('not a function: '+x); | ||
} | ||
function assertString(x) { | ||
if (typeof x !== 'string') throw new Error('not a string: '+x) | ||
} | ||
@@ -69,3 +82,4 @@ function formatExpected(expected) { | ||
function formatGot(stream, error) { | ||
var i = error.index; | ||
var index = error.index; | ||
var i = index.offset; | ||
@@ -78,3 +92,4 @@ if (i === stream.length) return ', got the end of the stream' | ||
return ' at character ' + i + ', got ' + prefix + stream.slice(i, i+12) + suffix | ||
return ' at line ' + index.line + ' column ' + index.column | ||
+ ', got ' + prefix + stream.slice(i, i+12) + suffix | ||
} | ||
@@ -87,2 +102,5 @@ | ||
_.parse = function(stream) { | ||
if (typeof stream !== 'string') { | ||
throw new Error('.parse must be called with a string as its argument'); | ||
} | ||
var result = this.skip(eof)._(stream, 0); | ||
@@ -95,3 +113,3 @@ | ||
status: false, | ||
index: result.furthest, | ||
index: makeLineColumnIndex(stream, result.furthest), | ||
expected: result.expected | ||
@@ -106,2 +124,6 @@ }; | ||
for (var j = 0; j < numParsers; j += 1) { | ||
assertParser(parsers[j]); | ||
} | ||
return Parser(function(stream, i) { | ||
@@ -143,2 +165,6 @@ var result; | ||
for (var j = 0; j < numParsers; j += 1) { | ||
assertParser(parsers[j]); | ||
} | ||
return Parser(function(stream, i) { | ||
@@ -154,2 +180,20 @@ var result; | ||
var sepBy = Parsimmon.sepBy = function(parser, separator) { | ||
// Argument asserted by sepBy1 | ||
return sepBy1(parser, separator).or(Parsimmon.of([])); | ||
}; | ||
var sepBy1 = Parsimmon.sepBy1 = function(parser, separator) { | ||
assertParser(parser); | ||
assertParser(separator); | ||
var pairs = separator.then(parser).many(); | ||
return parser.chain(function(r) { | ||
return pairs.map(function(rs) { | ||
return [r].concat(rs); | ||
}) | ||
}) | ||
}; | ||
// -*- primitive combinators -*- // | ||
@@ -229,2 +273,5 @@ _.or = function(alternative) { | ||
assertNumber(min); | ||
assertNumber(max); | ||
return Parser(function(stream, i) { | ||
@@ -271,2 +318,5 @@ var accum = []; | ||
_.map = function(fn) { | ||
assertFunction(fn); | ||
var self = this; | ||
@@ -304,2 +354,4 @@ return Parser(function(stream, i) { | ||
assertString(str); | ||
return Parser(function(stream, i) { | ||
@@ -318,2 +370,6 @@ var head = stream.slice(i, i+len); | ||
var regex = Parsimmon.regex = function(re, group) { | ||
assertRegexp(re); | ||
if (group) assertNumber(group); | ||
var anchored = RegExp('^(?:'+re.source+')', (''+re).slice((''+re).lastIndexOf('/')+1)); | ||
@@ -370,2 +426,4 @@ var expected = '' + re; | ||
var test = Parsimmon.test = function(predicate) { | ||
assertFunction(predicate); | ||
return Parser(function(stream, i) { | ||
@@ -391,2 +449,4 @@ var char = stream.charAt(i); | ||
var takeWhile = Parsimmon.takeWhile = function(predicate) { | ||
assertFunction(predicate); | ||
return Parser(function(stream, i) { | ||
@@ -415,6 +475,22 @@ var j = i; | ||
var index = Parsimmon.index = Parser(function(stream, i) { | ||
return makeSuccess(i, i); | ||
}); | ||
var makeLineColumnIndex = function(stream, i) { | ||
var lines = stream.slice(0, i).split("\n"); | ||
// Note that unlike the character offset, the line and column offsets are | ||
// 1-based. | ||
var lineWeAreUpTo = lines.length; | ||
var columnWeAreUpTo = lines[lines.length - 1].length + 1; | ||
return { | ||
offset: i, | ||
line: lineWeAreUpTo, | ||
column: columnWeAreUpTo | ||
}; | ||
}; | ||
var index | ||
= Parsimmon.index | ||
= Parser(function(stream, i) { | ||
return makeSuccess(i, makeLineColumnIndex(stream, i)); | ||
}); | ||
//- fantasyland compat | ||
@@ -421,0 +497,0 @@ |
{ | ||
"name": "parsimmon", | ||
"version": "0.7.0", | ||
"version": "0.7.1", | ||
"description": "A monadic LL(infinity) parser combinator library", | ||
"keywords": ["parsing", "parse", "parser combinators"], | ||
"author": "Jeanine Adkisson <jneen at jneen dot net>", | ||
"contributors": [ | ||
"Brian Mock <brian@mockbrian.com>" | ||
], | ||
"repository": "git://github.com/jneen/parsimmon", | ||
@@ -21,5 +24,4 @@ | ||
}, | ||
"dependencies": { | ||
"pjs": "5.x" | ||
}, | ||
"license": "MIT", | ||
"dependencies": {}, | ||
"scripts": { | ||
@@ -26,0 +28,0 @@ "test": "make test" |
@@ -7,9 +7,13 @@ [![Build Status](https://secure.travis-ci.org/jneen/parsimmon.png)](http://travis-ci.org/jneen/parsimmon) | ||
(by @jneen and @laughinghan) | ||
**Authors:** [@jneen](https://github.com/jneen) and [@laughinghan](https://github.com/laughinghan) | ||
**Maintainer:** [@wavebeem](https://github.com/wavebeem) | ||
Parsimmon is a small library for writing big parsers made up of lots of little parsers. The API is inspired by parsec and Promises/A. | ||
Parsimmon supports IE7 and newer browsers, along with Node.js. It can be used as a standard Node module through npm, or through `build/parsimmon.browser.js` directly in the browser through a script tag, where it exports a global variable called `Parsimmon`. | ||
## Quick Example | ||
``` js | ||
```javascript | ||
var regex = Parsimmon.regex; | ||
@@ -49,3 +53,3 @@ var string = Parsimmon.string; | ||
``` js | ||
```javascript | ||
string('foo').map(function(x) { return x + 'bar'; }) | ||
@@ -56,3 +60,3 @@ ``` | ||
``` js | ||
```javascript | ||
digits.map(function(x) { return parseInt(x) * 2; }) | ||
@@ -64,10 +68,9 @@ ``` | ||
Calling `.parse(str)` on a parser parses the string, and returns an | ||
object with a `status` flag, indicating whether the parse succeeded. | ||
If it succeeded, the `value` attribute will contain the yielded value. | ||
Otherwise, the `index` and `expected` attributes will contain the | ||
index of the parse error, and a message indicating what was expected. | ||
Calling `.parse(str)` on a parser parses the string, and returns an object with | ||
a `status` flag, indicating whether the parse succeeded. If it succeeded, the | ||
`value` attribute will contain the yielded value. Otherwise, the `index` and | ||
`expected` attributes will contain the index of the parse error (with `offset`, | ||
`line` and `column` properties), and a message indicating what was expected. | ||
The error object can be passed along with the original source to | ||
`Parsimmon.formatError(source, error)` to obtain a human-readable | ||
error string. | ||
`Parsimmon.formatError(source, error)` to obtain a human-readable error string. | ||
@@ -79,2 +82,7 @@ ## Full API | ||
`"my-string"`, and will yield the same. | ||
- `Parsimmon.oneOf("abc")` is a parser that expects to find | ||
one of the characters `"a"`, `"b"`, or `"c"`, and will yield the same. | ||
- `Parsimmon.noneOf("abc")` is a parser that expects to find | ||
any character except one of the characters `"a"`, `"b"`, or `"c"`, | ||
and will yield the same. | ||
- `Parsimmon.regex(/myregex/, group=0)` is a parser that expects the stream | ||
@@ -87,7 +95,16 @@ to match the given regex, and yields the given match group, or the | ||
that it expects to find in order, yielding an array of the results. | ||
- `Parsimmon.seqMap(parser1, parser2, ..., function(result1, result2, ...) { return anotherResult; })`: | ||
matches all parsers sequentially, passing their results to the callback | ||
at the end, returning its value. Works like `seq` and `map` combined but | ||
without any arrays. | ||
- `Parsimmon.alt(p1, p2, ... pn)` accepts a variable number of parsers, | ||
and yields the value of the first one that succeeds, backtracking in between. | ||
- `Parsimmon.lazy(f)` accepts a function that returns a parser, which | ||
is evaluated the first time the parser is used. This is useful for | ||
referencing parsers that haven't yet been defined. | ||
- `Parsimmon.sepBy(content, separator)` accepts two parsers, and expects multiple | ||
`content`s, separated by `separator`s. Yields an array of `contents`. | ||
- `Parsimmon.sepBy1(content, separator)` same as `Parsimmon.sepBy`, but expects | ||
`content` to succeed at least once. | ||
- `Parsimmon.lazy(f)` accepts a function that returns a parser, which is | ||
evaluated the first time the parser is used. This is useful for | ||
referencing parsers that haven't yet been defined, and for implementing | ||
recursive parsers. | ||
- `Parsimmon.lazy(desc, f)` is the same as `Parsimmon.lazy` but also | ||
@@ -105,4 +122,6 @@ sets `desc` as the expected value (see `.desc()` below) | ||
- `Parsimmon.eof` expects the end of the stream. | ||
- `Parsimmon.index` is a parser that yields the current index of the parse. | ||
- `Parsimmon.test(pred)` yield a single characer if it passes the predicate. | ||
- `Parsimmon.index` is a parser that yields an object an object representing | ||
the current offset into the parse: it has a 0-based character `offset` | ||
property and 1-based `line` and `column` properties. | ||
- `Parsimmon.test(pred)` yield a single character if it passes the predicate. | ||
- `Parsimmon.takeWhile(pred)` yield a string containing all the next characters that pass the predicate. | ||
@@ -167,5 +186,7 @@ | ||
expects `parser` at least `n` times. Yields an array of the results. | ||
- `parser.mark()` yields an object with `start`, `value`, and `end` keys, where | ||
`value` is the original value yielded by the parser, and `start` and `end` are | ||
the indices in the stream that contain the parsed text. | ||
- `parser.mark()` yields an object with `start`, `value`, and `end` keys, | ||
where `value` is the original value yielded by the parser, and `start` and | ||
`end` are are objects with a 0-based `offset` and 1-based `line` and | ||
`column` properties that represent the position in the stream that | ||
contained the parsed text. | ||
- `parser.desc(description)` returns a new parser whose failure message is the passed | ||
@@ -177,3 +198,3 @@ description. For example, `string('x').desc('the letter x')` will indicate that | ||
These apply to most parsers for traditional langauges - it's possible | ||
These apply to most parsers for traditional languages - it's possible | ||
you may need to do something different for yours! | ||
@@ -187,3 +208,3 @@ | ||
``` js | ||
```javascript | ||
var ignore = whitespace.or(comment.many()); | ||
@@ -195,3 +216,3 @@ function lexeme(p) { return p.skip(ignore); } | ||
``` js | ||
```javascript | ||
var lparen = lexeme(string('(')); | ||
@@ -202,14 +223,17 @@ var rparen = lexeme(string(')')); | ||
1. Forward-declare one or more top-level expressions with `lazy`, | ||
referring to parsers that have not yet been defined. Generally, this | ||
takes the form of a large `.alt()` call | ||
1. Forward-declare one or more top-level expressions with `lazy`, referring to | ||
parsers that have not yet been defined. Generally, this takes the form of a | ||
large `.alt()` call | ||
``` js | ||
```javascript | ||
var expr = lazy('an expression', function() { return Parsimmon.alt(p1, p2, ...); }); | ||
``` | ||
With `.lazy` you could also recursively refer to `expr` in its own | ||
definition. | ||
1. Then build your parsers from the inside out - these should return | ||
AST nodes or other objects specific to your domain. | ||
``` js | ||
```javascript | ||
var p1 = ... | ||
@@ -222,3 +246,3 @@ var p2 = ... | ||
``` js | ||
```javascript | ||
return ignore.then(expr.many()); | ||
@@ -225,0 +249,0 @@ ``` |
@@ -56,5 +56,18 @@ var Parsimmon = {}; | ||
// For ensuring we have the right argument types | ||
function assertParser(p) { | ||
if (!(p instanceof Parser)) throw new Error('not a parser: '+p); | ||
} | ||
function assertNumber(x) { | ||
if (typeof x !== 'number') throw new Error('not a number: '+x); | ||
} | ||
function assertRegexp(x) { | ||
if (!(x instanceof RegExp)) throw new Error('not a regex: '+x); | ||
} | ||
function assertFunction(x) { | ||
if (typeof x !== 'function') throw new Error('not a function: '+x); | ||
} | ||
function assertString(x) { | ||
if (typeof x !== 'string') throw new Error('not a string: '+x) | ||
} | ||
@@ -68,3 +81,4 @@ function formatExpected(expected) { | ||
function formatGot(stream, error) { | ||
var i = error.index; | ||
var index = error.index; | ||
var i = index.offset; | ||
@@ -77,3 +91,4 @@ if (i === stream.length) return ', got the end of the stream' | ||
return ' at character ' + i + ', got ' + prefix + stream.slice(i, i+12) + suffix | ||
return ' at line ' + index.line + ' column ' + index.column | ||
+ ', got ' + prefix + stream.slice(i, i+12) + suffix | ||
} | ||
@@ -86,2 +101,5 @@ | ||
_.parse = function(stream) { | ||
if (typeof stream !== 'string') { | ||
throw new Error('.parse must be called with a string as its argument'); | ||
} | ||
var result = this.skip(eof)._(stream, 0); | ||
@@ -94,3 +112,3 @@ | ||
status: false, | ||
index: result.furthest, | ||
index: makeLineColumnIndex(stream, result.furthest), | ||
expected: result.expected | ||
@@ -105,2 +123,6 @@ }; | ||
for (var j = 0; j < numParsers; j += 1) { | ||
assertParser(parsers[j]); | ||
} | ||
return Parser(function(stream, i) { | ||
@@ -142,2 +164,6 @@ var result; | ||
for (var j = 0; j < numParsers; j += 1) { | ||
assertParser(parsers[j]); | ||
} | ||
return Parser(function(stream, i) { | ||
@@ -153,2 +179,20 @@ var result; | ||
var sepBy = Parsimmon.sepBy = function(parser, separator) { | ||
// Argument asserted by sepBy1 | ||
return sepBy1(parser, separator).or(Parsimmon.of([])); | ||
}; | ||
var sepBy1 = Parsimmon.sepBy1 = function(parser, separator) { | ||
assertParser(parser); | ||
assertParser(separator); | ||
var pairs = separator.then(parser).many(); | ||
return parser.chain(function(r) { | ||
return pairs.map(function(rs) { | ||
return [r].concat(rs); | ||
}) | ||
}) | ||
}; | ||
// -*- primitive combinators -*- // | ||
@@ -228,2 +272,5 @@ _.or = function(alternative) { | ||
assertNumber(min); | ||
assertNumber(max); | ||
return Parser(function(stream, i) { | ||
@@ -270,2 +317,5 @@ var accum = []; | ||
_.map = function(fn) { | ||
assertFunction(fn); | ||
var self = this; | ||
@@ -303,2 +353,4 @@ return Parser(function(stream, i) { | ||
assertString(str); | ||
return Parser(function(stream, i) { | ||
@@ -317,2 +369,6 @@ var head = stream.slice(i, i+len); | ||
var regex = Parsimmon.regex = function(re, group) { | ||
assertRegexp(re); | ||
if (group) assertNumber(group); | ||
var anchored = RegExp('^(?:'+re.source+')', (''+re).slice((''+re).lastIndexOf('/')+1)); | ||
@@ -369,2 +425,4 @@ var expected = '' + re; | ||
var test = Parsimmon.test = function(predicate) { | ||
assertFunction(predicate); | ||
return Parser(function(stream, i) { | ||
@@ -390,2 +448,4 @@ var char = stream.charAt(i); | ||
var takeWhile = Parsimmon.takeWhile = function(predicate) { | ||
assertFunction(predicate); | ||
return Parser(function(stream, i) { | ||
@@ -414,6 +474,22 @@ var j = i; | ||
var index = Parsimmon.index = Parser(function(stream, i) { | ||
return makeSuccess(i, i); | ||
}); | ||
var makeLineColumnIndex = function(stream, i) { | ||
var lines = stream.slice(0, i).split("\n"); | ||
// Note that unlike the character offset, the line and column offsets are | ||
// 1-based. | ||
var lineWeAreUpTo = lines.length; | ||
var columnWeAreUpTo = lines[lines.length - 1].length + 1; | ||
return { | ||
offset: i, | ||
line: lineWeAreUpTo, | ||
column: columnWeAreUpTo | ||
}; | ||
}; | ||
var index | ||
= Parsimmon.index | ||
= Parser(function(stream, i) { | ||
return makeSuccess(i, makeLineColumnIndex(stream, i)); | ||
}); | ||
//- fantasyland compat | ||
@@ -420,0 +496,0 @@ |
@@ -22,11 +22,19 @@ suite('parser', function() { | ||
res = parser.parse('y') | ||
assert.ok(!res.status) | ||
assert.equal("'x'", res.expected); | ||
assert.equal(0, res.index); | ||
res = parser.parse('y'); | ||
assert.deepEqual(res, { | ||
status: false, | ||
index: { | ||
offset: 0, | ||
line: 1, | ||
column: 1 | ||
}, | ||
expected: ["'x'"] | ||
}); | ||
assert.equal( | ||
"expected 'x' at character 0, got 'y'", | ||
"expected 'x' at line 1 column 1, got 'y'", | ||
Parsimmon.formatError('y', res) | ||
); | ||
assert.throws(function() { string(34) }); | ||
}); | ||
@@ -41,3 +49,7 @@ | ||
status: false, | ||
index: 0, | ||
index: { | ||
offset: 0, | ||
line: 1, | ||
column: 1 | ||
}, | ||
expected: ['/[0-9]/'] | ||
@@ -47,5 +59,11 @@ }); | ||
status: false, | ||
index: 1, | ||
index: { | ||
offset: 1, | ||
line: 1, | ||
column: 2 | ||
}, | ||
expected: ['EOF'] | ||
}); | ||
assert.throws(function() { regex(42) }); | ||
assert.throws(function() { regex(/a/, 'not a number') }); | ||
}); | ||
@@ -68,3 +86,7 @@ | ||
status: false, | ||
index: 7, | ||
index: { | ||
offset: 7, | ||
line: 1, | ||
column: 8 | ||
}, | ||
expected: ["')'", "/[^\\)]/"] | ||
@@ -74,5 +96,10 @@ }); | ||
status: false, | ||
index: 0, | ||
index: { | ||
offset: 0, | ||
line: 1, | ||
column: 1 | ||
}, | ||
expected: ["'('"] | ||
}); | ||
assert.throws(function() { seq('not a parser') }); | ||
}); | ||
@@ -107,3 +134,11 @@ | ||
assert.deepEqual(failer().parse('a'), {status: false, index: 0, expected: ['nothing']}) | ||
assert.deepEqual(failer().parse('a'), { | ||
status: false, | ||
index: { | ||
offset: 0, | ||
line: 1, | ||
column: 1 | ||
}, | ||
expected: ['nothing']} | ||
) | ||
}); | ||
@@ -153,4 +188,56 @@ | ||
}); | ||
}) | ||
assert.throws(function() { alt('not a parser') }); | ||
}); | ||
suite('Parsimmon.sepBy/sepBy1', function() { | ||
var chars = regex(/[a-zA-Z]+/); | ||
var comma = string(','); | ||
var csvSep = Parsimmon.sepBy(chars, comma); | ||
var csvSep1 = Parsimmon.sepBy1(chars, comma); | ||
test('successful, returns an array of parsed elements', function(){ | ||
var input = 'Heres,a,csv,string,in,our,restrictive,dialect'; | ||
var output = ['Heres', 'a', 'csv', 'string', 'in', 'our', 'restrictive', 'dialect'] | ||
assert.deepEqual(csvSep.parse(input).value, output); | ||
assert.deepEqual(csvSep1.parse(input).value, output); | ||
assert.throws(function() { Parsimmon.sepBy('not a parser') }); | ||
assert.throws(function() { | ||
Parsimmon.sepBy(string('a'), 'not a parser') | ||
}); | ||
}); | ||
test('sepBy succeeds with the empty list on empty input, sepBy1 fails', function() { | ||
assert.deepEqual(csvSep.parse('').value, []); | ||
assert.deepEqual(csvSep1.parse(''), { | ||
status: false, | ||
index: { | ||
offset: 0, | ||
line: 1, | ||
column: 1 | ||
}, | ||
expected: ['/[a-zA-Z]+/'] | ||
}); | ||
}); | ||
test('does not tolerate trailing separators', function() { | ||
var input = 'Heres,a,csv,with,a,trailing,comma,'; | ||
var output = { | ||
status: false, | ||
index: { | ||
offset: 34, | ||
line: 1, | ||
column: 35 | ||
}, | ||
expected: ['/[a-zA-Z]+/'] | ||
}; | ||
assert.deepEqual(csvSep.parse(input), output); | ||
assert.deepEqual(csvSep1.parse(input), output); | ||
}); | ||
}); | ||
suite('then', function() { | ||
@@ -163,3 +250,7 @@ test('with a parser, uses the last return value', function() { | ||
expected: ["'x'"], | ||
index: 0 | ||
index: { | ||
offset: 0, | ||
line: 1, | ||
column: 1 | ||
} | ||
}) | ||
@@ -169,5 +260,14 @@ assert.deepEqual(parser.parse('xz'), { | ||
expected: ["'y'"], | ||
index: 1 | ||
index: { | ||
offset: 1, | ||
line: 1, | ||
column: 2 | ||
} | ||
}); | ||
}); | ||
test('errors when argument is not a parser', function() { | ||
assert.throws(function() { | ||
string('x').then('not a parser') | ||
}); | ||
}); | ||
}); | ||
@@ -208,2 +308,5 @@ | ||
}); | ||
test('asserts that a function was given', function() { | ||
assert.throws(function() { string('x').map('not a function') }); | ||
}); | ||
}); | ||
@@ -225,2 +328,5 @@ | ||
}); | ||
test('asserts that a parser was given', function() { | ||
assert.throws(function() { string('x').skip('not a parser') }); | ||
}); | ||
}); | ||
@@ -247,2 +353,5 @@ | ||
}); | ||
test('asserts that a parser was given', function() { | ||
assert.throws(function() { string('x').or('not a parser') }); | ||
}); | ||
}); | ||
@@ -326,2 +435,8 @@ | ||
}); | ||
test('checks that argument types are correct', function() { | ||
assert.throws(function() { string('x').times('not a number') }); | ||
assert.throws(function() { string('x').times(1, 'not a number') }); | ||
assert.throws(function() { string('x').atLeast('not a number') }); | ||
assert.throws(function() { string('x').atMost('not a number') }); | ||
}); | ||
}); | ||
@@ -340,3 +455,7 @@ | ||
status: false, | ||
index: 1, | ||
index: { | ||
offset: 1, | ||
line: 1, | ||
column: 2 | ||
}, | ||
expected: ['a character besides y'] | ||
@@ -364,3 +483,7 @@ }); | ||
status: false, | ||
index: 2, | ||
index: { | ||
offset: 2, | ||
line: 1, | ||
column: 3 | ||
}, | ||
expected: ['+'] | ||
@@ -373,3 +496,7 @@ }); | ||
status: false, | ||
index: 2, | ||
index: { | ||
offset: 2, | ||
line: 1, | ||
column: 3 | ||
}, | ||
expected: ['*'] | ||
@@ -391,2 +518,3 @@ }); | ||
assert.equal(parser.parse('.').status, false); | ||
assert.throws(function() { Parsimmon.test('not a function') }); | ||
}); | ||
@@ -401,9 +529,22 @@ | ||
assert.equal(parser.parse('').value, ''); | ||
assert.throws(function() { Parsimmon.takeWhile('not a function') }); | ||
}); | ||
test('index', function() { | ||
var parser = regex(/^x*/).then(index); | ||
assert.equal(parser.parse('').value, 0); | ||
assert.equal(parser.parse('xx').value, 2); | ||
assert.equal(parser.parse('xxxx').value, 4); | ||
var parser = regex(/^[x\n]*/).then(index); | ||
assert.deepEqual(parser.parse('').value, { | ||
offset: 0, | ||
line: 1, | ||
column: 1 | ||
}); | ||
assert.deepEqual(parser.parse('xx').value, { | ||
offset: 2, | ||
line: 1, | ||
column: 3 | ||
}); | ||
assert.deepEqual(parser.parse('xx\nxx').value, { | ||
offset: 5, | ||
line: 2, | ||
column: 3 | ||
}); | ||
}); | ||
@@ -414,4 +555,26 @@ | ||
var parser = optWhitespace.then(ys).skip(optWhitespace); | ||
assert.deepEqual(parser.parse('').value, { start: 0, value: '', end: 0 }); | ||
assert.deepEqual(parser.parse(' yy ').value, { start: 1, value: 'yy', end: 3 }); | ||
assert.deepEqual( | ||
parser.parse('').value, | ||
{ | ||
value: '', | ||
start: { offset: 0, line: 1, column: 1 }, | ||
end: { offset: 0, line: 1, column: 1 } | ||
} | ||
); | ||
assert.deepEqual( | ||
parser.parse(' yy ').value, | ||
{ | ||
value: 'yy', | ||
start: { offset: 1, line: 1, column: 2 }, | ||
end: { offset: 3, line: 1, column: 4 } | ||
} | ||
); | ||
assert.deepEqual( | ||
parser.parse('\nyy ').value, | ||
{ | ||
value: 'yy', | ||
start: { offset: 1, line: 2, column: 1 }, | ||
end: { offset: 3, line: 2, column: 3 } | ||
} | ||
); | ||
}); | ||
@@ -429,3 +592,7 @@ | ||
status: false, | ||
index: 3, | ||
index: { | ||
offset: 3, | ||
line: 1, | ||
column: 4 | ||
}, | ||
expected: ["'def'"] | ||
@@ -440,3 +607,7 @@ }); | ||
status: false, | ||
index: 3, | ||
index: { | ||
offset: 3, | ||
line: 1, | ||
column: 4 | ||
}, | ||
expected: ["'d'", "'def'"] | ||
@@ -452,3 +623,7 @@ }); | ||
status: false, | ||
index: 6, | ||
index: { | ||
offset: 6, | ||
line: 1, | ||
column: 7 | ||
}, | ||
expected: ["'g'"] | ||
@@ -472,3 +647,7 @@ }); | ||
status: false, | ||
index: 10, | ||
index: { | ||
offset: 10, | ||
line: 1, | ||
column: 11 | ||
}, | ||
expected: ['EOF', "'('", "an atom"] | ||
@@ -479,3 +658,7 @@ }); | ||
status: false, | ||
index: 13, | ||
index: { | ||
offset: 13, | ||
line: 1, | ||
column: 14 | ||
}, | ||
expected: ["')'", "'('", "an atom"] | ||
@@ -493,3 +676,7 @@ }); | ||
status: false, | ||
index: 5, | ||
index: { | ||
offset: 5, | ||
line: 1, | ||
column: 6 | ||
}, | ||
expected: ["'def'"] | ||
@@ -506,3 +693,7 @@ }); | ||
status: false, | ||
index: 4, | ||
index: { | ||
offset: 4, | ||
line: 1, | ||
column: 5 | ||
}, | ||
expected: ["'def'"] | ||
@@ -513,3 +704,7 @@ }); | ||
status: false, | ||
index: 7, | ||
index: { | ||
offset: 7, | ||
line: 1, | ||
column: 8 | ||
}, | ||
expected: ["'def'"] | ||
@@ -528,3 +723,7 @@ }); | ||
status: false, | ||
index: 0, | ||
index: { | ||
offset: 0, | ||
line: 1, | ||
column: 1 | ||
}, | ||
expected: ['the letter x'] | ||
@@ -535,3 +734,7 @@ }); | ||
status: false, | ||
index: 1, | ||
index: { | ||
offset: 1, | ||
line: 1, | ||
column: 2 | ||
}, | ||
expected: ['the letter y'] | ||
@@ -548,3 +751,7 @@ }); | ||
status: false, | ||
index: 0, | ||
index: { | ||
offset: 0, | ||
line: 1, | ||
column: 1 | ||
}, | ||
expected: ['the letter x'] | ||
@@ -555,3 +762,7 @@ }); | ||
status: false, | ||
index: 1, | ||
index: { | ||
offset: 1, | ||
line: 1, | ||
column: 2 | ||
}, | ||
expected: ['the letter y'] | ||
@@ -558,0 +769,0 @@ }); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No License Found
License(Experimental) License information could not be found.
Found 1 instance in 1 package
83487
0
17
0
1923
246
1