summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorptmcg <ptmcg@9bf210a0-9d2d-494c-87cf-cfb32e7dff7b>2015-11-04 09:15:43 +0000
committerptmcg <ptmcg@9bf210a0-9d2d-494c-87cf-cfb32e7dff7b>2015-11-04 09:15:43 +0000
commite34e6dc341fed963fc3d455987194d6ce5635b1f (patch)
tree994dcd0b963762dbc02bbe6bb4eb93a50dea9cd0
parente20235d80f9b0e7d19c7491e130edeadf2144d7c (diff)
downloadpyparsing-e34e6dc341fed963fc3d455987194d6ce5635b1f.tar.gz
Fixed a bug in Or, when a parse action on an alternative raises an exception
git-svn-id: svn://svn.code.sf.net/p/pyparsing/code/trunk@298 9bf210a0-9d2d-494c-87cf-cfb32e7dff7b
-rw-r--r--src/CHANGES4
-rw-r--r--src/pyparsing.py32
-rw-r--r--src/unitTests.py33
3 files changed, 56 insertions, 13 deletions
diff --git a/src/CHANGES b/src/CHANGES
index 4969eb6..dc1fcef 100644
--- a/src/CHANGES
+++ b/src/CHANGES
@@ -19,6 +19,10 @@ Version 2.0.6 -
a pyparsing user, but I've lost the email thread - finally figured out
a fairly clean way to do this.
+- Fixed a bug in Or, when a parse action on an alternative raises an
+ exception, other potentially matching alternatives were not always tried.
+ Reported by TheVeryOmni on the pyparsing wiki, thanks!
+
- Fixed a bug to dump() introduced in 2.0.4, where list values were shown
in duplicate.
diff --git a/src/pyparsing.py b/src/pyparsing.py
index 8ee2a18..1bbd510 100644
--- a/src/pyparsing.py
+++ b/src/pyparsing.py
@@ -58,7 +58,7 @@ The pyparsing module handles some of the problems that are typically vexing when
"""
__version__ = "2.0.6"
-__versionTime__ = "31 Oct 2015 23:41"
+__versionTime__ = "4 Nov 2015 02:26"
__author__ = "Paul McGuire <ptmcg@users.sourceforge.net>"
import string
@@ -2450,8 +2450,8 @@ class Or(ParseExpression):
def parseImpl( self, instring, loc, doActions=True ):
maxExcLoc = -1
- maxMatchLoc = -1
maxException = None
+ matches = []
for e in self.exprs:
try:
loc2 = e.tryParse( instring, loc )
@@ -2465,18 +2465,26 @@ class Or(ParseExpression):
maxException = ParseException(instring,len(instring),e.errmsg,self)
maxExcLoc = len(instring)
else:
- if loc2 > maxMatchLoc:
- maxMatchLoc = loc2
- maxMatchExp = e
+ # save match among all matches, to retry longest to shortest
+ matches.append((loc2, e))
- if maxMatchLoc < 0:
- if maxException is not None:
- maxException.msg = self.errmsg
- raise maxException
- else:
- raise ParseException(instring, loc, "no defined alternatives to match", self)
+ if matches:
+ matches.sort(key=lambda x: -x[0])
+ for _,e in matches:
+ try:
+ return e._parse( instring, loc, doActions )
+ except ParseException as err:
+ err.__traceback__ = None
+ if err.loc > maxExcLoc:
+ maxException = err
+ maxExcLoc = err.loc
+
+ if maxException is not None:
+ maxException.msg = self.errmsg
+ raise maxException
+ else:
+ raise ParseException(instring, loc, "no defined alternatives to match", self)
- return maxMatchExp._parse( instring, loc, doActions )
def __ixor__(self, other ):
if isinstance( other, basestring ):
diff --git a/src/unitTests.py b/src/unitTests.py
index 422ceb3..947d441 100644
--- a/src/unitTests.py
+++ b/src/unitTests.py
@@ -2261,6 +2261,35 @@ class AddConditionTest(ParseTestCase):
assert result.asList() == [[7],[9]], "failed to properly process conditions"
+class PatientOrTest(ParseTestCase):
+ def runTest(self):
+ import pyparsing as pp
+
+ # Two expressions and a input string which could - syntactically - be matched against
+ # both expressions. The "Literal" expression is considered invalid though, so this PE
+ # should always detect the "Word" expression.
+ def validate(token):
+ if token[0] == "def":
+ raise pp.ParseException("signalling invalid token")
+ return token
+
+ a = pp.Word("de").setName("Word")#.setDebug()
+ b = pp.Literal("def").setName("Literal").setParseAction(validate)#.setDebug()
+ c = pp.Literal("d").setName("d")#.setDebug()
+
+ # The "Literal" expressions's ParseAction is not executed directly after syntactically
+ # detecting the "Literal" Expression but only after the Or-decision has been made
+ # (which is too late)...
+ try:
+ result = (a ^ b ^ c).parseString("def")
+ assert result.asList() == ['de'], "failed to select longest match, chose %s" % result
+ except ParseException:
+ failed = True
+ else:
+ failed = False
+ assert not failed, "invalid logic in Or, fails on longest match with exception in parse action"
+
+
class MiscellaneousParserTests(ParseTestCase):
def runTest(self):
import pyparsing
@@ -2470,6 +2499,7 @@ def makeTestSuite():
suite.addTest( LocatedExprTest() )
suite.addTest( PopTest() )
suite.addTest( AddConditionTest() )
+ suite.addTest( PatientOrTest() )
suite.addTest( MiscellaneousParserTests() )
if TEST_USING_PACKRAT:
# retest using packrat parsing (disable those tests that aren't compatible)
@@ -2492,7 +2522,8 @@ def makeTestSuiteTemp():
#~ suite.addTest( RepeaterTest() )
#~ suite.addTest( LocatedExprTest() )
#~ suite.addTest( AddConditionTest() )
- suite.addTest( WithAttributeParseActionTest() )
+ #~ suite.addTest( WithAttributeParseActionTest() )
+ suite.addTest( PatientOrTest() )
return suite