Comparing version 0.0.5 to 0.0.6
{ | ||
"name": "parsimmon", | ||
"version": "0.0.5", | ||
"version": "0.0.6", | ||
"description": "A monadic LL(infinity) parser combinator library", | ||
@@ -5,0 +5,0 @@ "keywords": ["parsing", "parse", "parser combinators"], |
@@ -33,2 +33,18 @@ var Parsimmon = {}; | ||
function furthestFailure(onFailure, myI, myExpected) { | ||
return function(stream, yourI, yourExpected) { | ||
if (myI > yourI) return onFailure(stream, myI, myExpected); | ||
else return onFailure.apply(this, arguments); | ||
}; | ||
} | ||
function furthestFailureSuccess(onSuccess, myFurthestFailureI, myFurthestExpected) { | ||
return function(stream, i, result, yourFurthestFailureI, yourFurthestExpected) { | ||
if (myFurthestFailureI > yourFurthestFailureI) { | ||
return onSuccess(stream, i, result, myFurthestFailureI, myFurthestExpected); | ||
} | ||
else return onSuccess.apply(this, arguments); | ||
}; | ||
} | ||
// -*- primitive combinators -*- // | ||
@@ -41,4 +57,6 @@ _.or = function(alternative) { | ||
function failure(stream, newI) { | ||
return alternative._(stream, i, onSuccess, onFailure); | ||
function failure(stream, newI, expected) { | ||
var altSuccess = furthestFailureSuccess(onSuccess, newI, expected); | ||
var altFailure = furthestFailure(onFailure, newI, expected); | ||
return alternative._(stream, i, altSuccess, altFailure); | ||
} | ||
@@ -54,5 +72,7 @@ }); | ||
function success(stream, newI, result) { | ||
function success(stream, newI, result, furthestFailureI, furthestExpected) { | ||
var nextParser = (next instanceof Parser ? next : next(result)); | ||
return nextParser._(stream, newI, onSuccess, onFailure); | ||
var nextSuccess = furthestFailureSuccess(onSuccess, furthestFailureI, furthestExpected); | ||
var nextFailure = furthestFailure(onFailure, furthestFailureI, furthestExpected); | ||
return nextParser._(stream, newI, nextSuccess, nextFailure); | ||
} | ||
@@ -82,11 +102,18 @@ }); | ||
while (self._(stream, i, success, failure)); | ||
return onSuccess(stream, i, xs); | ||
var furthestFailureI, furthestExpected; | ||
return onSuccess(stream, i, xs, furthestFailureI, furthestExpected); | ||
function success(stream, newI, x) { | ||
function success(stream, newI, x, successFurthestFailureI, successFurthestExpected) { | ||
i = newI; | ||
xs.push(x); | ||
furthestFailureI = successFurthestFailureI; | ||
furthestExpected = successFurthestExpected; | ||
return true; | ||
} | ||
function failure() { | ||
function failure(stream, newI, expected) { | ||
if (!(furthestFailureI > newI)) { | ||
furthestFailureI = newI; | ||
furthestExpected = expected; | ||
} | ||
return false; | ||
@@ -124,30 +151,30 @@ } | ||
var result = true; | ||
var failure; | ||
var furthestFailureI, furthestExpected; | ||
for (var times = 0; times < min; times += 1) { | ||
result = self._(stream, i, success, firstFailure); | ||
if (!result) return onFailure(stream, i, failure); | ||
result = self._(stream, i, success, failure); | ||
if (!result) return onFailure(stream, furthestFailureI, furthestExpected); | ||
} | ||
for (; times < max && result; times += 1) { | ||
result = self._(stream, i, success, secondFailure); | ||
result = self._(stream, i, success, failure); | ||
} | ||
return onSuccess(stream, i, xs); | ||
return onSuccess(stream, i, xs, furthestFailureI, furthestExpected); | ||
function success(stream, newI, x) { | ||
function success(stream, newI, x, successFurthestFailureI, successFurthestExpected) { | ||
i = newI; | ||
xs.push(x); | ||
i = newI; | ||
furthestFailureI = successFurthestFailureI; | ||
furthestExpected = successFurthestExpected; | ||
return true; | ||
} | ||
function firstFailure(stream, newI, msg) { | ||
failure = msg; | ||
i = newI; | ||
function failure(stream, newI, expected) { | ||
if (!(furthestFailureI > newI)) { | ||
furthestFailureI = newI; | ||
furthestExpected = expected; | ||
} | ||
return false; | ||
} | ||
function secondFailure(stream, newI, msg) { | ||
return false; | ||
} | ||
}); | ||
@@ -185,3 +212,3 @@ }; | ||
if (head === str) { | ||
return onSuccess(stream, i+len, head); | ||
return onSuccess(stream, i+len, head, -1); | ||
} | ||
@@ -204,3 +231,3 @@ else { | ||
var result = match[0]; | ||
return onSuccess(stream, i+result.length, result); | ||
return onSuccess(stream, i+result.length, result, -1); | ||
} | ||
@@ -235,7 +262,7 @@ else { | ||
return onSuccess(stream, i+1, stream.charAt(i)); | ||
return onSuccess(stream, i+1, stream.charAt(i), -1); | ||
}); | ||
var all = Parsimmon.all = Parser(function(stream, i, onSuccess, onFailure) { | ||
return onSuccess(stream, stream.length, stream.slice(i)); | ||
return onSuccess(stream, stream.length, stream.slice(i), -1); | ||
}); | ||
@@ -246,4 +273,4 @@ | ||
return onSuccess(stream, i, ''); | ||
return onSuccess(stream, i, '', -1); | ||
}); | ||
}); |
@@ -207,7 +207,7 @@ var assert = require('assert') | ||
var parser = any.then(function(ch) { | ||
return fail('character '+ch+' not allowed'); | ||
return fail('a character besides ' + ch); | ||
}).or(string('x')); | ||
assert.throws(function() { parser.parse('y'); }, | ||
partialEquals("Parse Error: expected 'x' at character 0, got 'y'\n parsing: 'y'")); | ||
partialEquals("Parse Error: expected a character besides y, got the end of the string\n parsing: 'y'")); | ||
assert.equal(parser.parse('x'), 'x'); | ||
@@ -247,2 +247,67 @@ }); | ||
}); | ||
suite('smart error messages', function() { | ||
// this is mainly about .or(), .many(), and .times(), but not about | ||
// their core functionality, so it's in its own test suite | ||
suite('or', function() { | ||
test('prefer longest branch', function() { | ||
var parser = string('abc').then(string('def')).or(string('ab').then(string('cd'))); | ||
assert.throws(function() { parser.parse('abc'); }, | ||
partialEquals("Parse Error: expected 'def', got the end of the string\n parsing: 'abc'")); | ||
}); | ||
test('prefer last of equal length branches', function() { | ||
var parser = string('abc').then(string('def')).or(string('abc').then(string('d'))); | ||
assert.throws(function() { parser.parse('abc'); }, | ||
partialEquals("Parse Error: expected 'd', got the end of the string\n parsing: 'abc'")); | ||
}); | ||
test('prefer longest branch even after a success', function() { | ||
var parser = string('abcdef').then(string('g')).or(string('ab')) | ||
.then(string('cd')).then(string('xyz')); | ||
assert.throws(function() { parser.parse('abcdef'); }, | ||
partialEquals("Parse Error: expected 'g', got the end of the string\n parsing: 'abcdef'")); | ||
}); | ||
}); | ||
suite('many', function() { | ||
test('prefer longest branch even in a .many()', function() { | ||
var atom = regex(/^[^()\s]+/); | ||
var sexpr = string('(').then(function() { return list; }).skip(string(')')); | ||
var list = optWhitespace.then(atom.or(sexpr)).skip(optWhitespace).many(); | ||
assert.deepEqual(list.parse('(a b) (c ((() d)))'), [['a', 'b'], ['c', [[[], 'd']]]]); | ||
assert.throws(function() { list.parse('(a b ()) c)'); }, | ||
partialEquals("Parse Error: expected EOF at character 10, got '...)'\n parsing: '(a b ()) c)'")); | ||
assert.throws(function() { list.parse('(a (b)) (() c'); }, | ||
partialEquals("Parse Error: expected ')', got the end of the string\n parsing: '(a (b)) (() c'")); | ||
}); | ||
test('prefer longest branch in .or() nested in .many()', function() { | ||
var parser = string('abc').then(string('def')).or(string('a')).many(); | ||
assert.deepEqual(parser.parse('aaabcdefaa'), ['a', 'a', 'def', 'a', 'a']); | ||
assert.throws(function() { parser.parse('aaabcde'); }, | ||
partialEquals("Parse Error: expected 'def' at character 5, got '...de'\n parsing: 'aaabcde'")); | ||
}); | ||
}); | ||
suite('times', function() { | ||
test('prefer longest branch in .times() too', function() { | ||
var parser = string('abc').then(string('def')).or(string('a')).times(3, 6); | ||
assert.throws(function() { parser.parse('aabcde'); }, | ||
partialEquals("Parse Error: expected 'def' at character 4, got '...de'\n parsing: 'aabcde'")); | ||
assert.throws(function() { parser.parse('aaaaabcde'); }, | ||
partialEquals("Parse Error: expected 'def' at character 7, got '...de'\n parsing: 'aaaaabcde'")); | ||
}); | ||
}); | ||
}); | ||
}); |
27195
480