Skip to content

Commit

Permalink
Fix regression in SkipTo when ignoring an ignoreExpr, and failed to a…
Browse files Browse the repository at this point in the history
…lso ignore the ignorables of the target expr (Issue #500)
  • Loading branch information
ptmcg committed Jul 29, 2023
1 parent 411c8ab commit 54b39a5
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 9 deletions.
7 changes: 5 additions & 2 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ Version 3.2.0 will also discontinue support for Python versions 3.6 and 3.7.

Version 3.1.1 - (in development)
--------------------------------
- Fixed bug in Word(min), reported by Ricardo Coccioli, good catch! (Issue #502)
- Fixed regression in Word(min), reported by Ricardo Coccioli, good catch! (Issue #502)

- Fixed bug in bad exception messages raised by Forward expressions. PR submitted
by Kyle Sunden, thanks for your patience and collaboration on this (#493).

- Fixed bug in type annotation for enable_packrat, PR submitted by Mike Urbach, thanks! (Issue #498)
- Fixed regression in SkipTo, where ignored expressions were not checked when looking
for the target expression. Reported by catcombo, Issue #500.

- Fixed type annotation for enable_packrat, PR submitted by Mike Urbach, thanks! (Issue #498)

- Some general internal code cleanup. (Instigated by Michal Čihař, Issue #488)

Expand Down
2 changes: 1 addition & 1 deletion pyparsing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def __repr__(self):


__version_info__ = version_info(3, 1, 1, "final", 1)
__version_time__ = "29 Jul 2023 10:28 UTC"
__version_time__ = "29 Jul 2023 14:32 UTC"
__version__ = __version_info__.__version__
__versionTime__ = __version_time__
__author__ = "Paul McGuire <ptmcg.gm+pyparsing@gmail.com>"
Expand Down
41 changes: 35 additions & 6 deletions pyparsing/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,7 @@ def _skipIgnorables(self, instring: str, loc: int) -> int:
return loc
exprsFound = True
ignore_expr_fns = [e._parse for e in self.ignoreExprs]
last_loc = loc
while exprsFound:
exprsFound = False
for ignore_fn in ignore_expr_fns:
Expand All @@ -780,6 +781,10 @@ def _skipIgnorables(self, instring: str, loc: int) -> int:
exprsFound = True
except ParseException:
pass
# check if all ignore exprs matched but didn't actually advance the parse location
if loc == last_loc:
break
last_loc = loc
return loc

def preParse(self, instring: str, loc: int) -> int:
Expand Down Expand Up @@ -5325,8 +5330,7 @@ def __init__(
):
super().__init__(other)
failOn = failOn or fail_on
if ignore is not None:
self.ignore(ignore)
self.ignoreExpr = ignore
self.mayReturnEmpty = True
self.mayIndexError = False
self.includeMatch = include
Expand All @@ -5336,6 +5340,20 @@ def __init__(
else:
self.failOn = failOn
self.errmsg = "No match found for " + str(self.expr)
self.ignorer = Empty().leave_whitespace()
self._update_ignorer()

def _update_ignorer(self):
# rebuild internal ignore expr from current ignore exprs and assigned ignoreExpr
self.ignorer.ignoreExprs.clear()
for e in self.expr.ignoreExprs:
self.ignorer.ignore(e)
if self.ignoreExpr:
self.ignorer.ignore(self.ignoreExpr)

def ignore(self, expr):
super().ignore(expr)
self._update_ignorer()

def parseImpl(self, instring, loc, doActions=True):
startloc = loc
Expand All @@ -5344,7 +5362,9 @@ def parseImpl(self, instring, loc, doActions=True):
self_failOn_canParseNext = (
self.failOn.canParseNext if self.failOn is not None else None
)
self_preParse = self.preParse if self.callPreparse else None
ignorer_try_parse = (
self.ignorer.try_parse if self.ignorer.ignoreExprs else None
)

tmploc = loc
while tmploc <= instrlen:
Expand All @@ -5353,9 +5373,18 @@ def parseImpl(self, instring, loc, doActions=True):
if self_failOn_canParseNext(instring, tmploc):
break

if self_preParse is not None:
# skip grammar-ignored expressions
tmploc = self_preParse(instring, tmploc)
if ignorer_try_parse is not None:
# advance past ignore expressions
prev_tmploc = tmploc
while 1:
try:
tmploc = ignorer_try_parse(instring, tmploc)
except ParseBaseException:
break
# see if all ignorers matched, but didn't actually ignore anything
if tmploc == prev_tmploc:
break
prev_tmploc = tmploc

try:
self_expr_parse(instring, tmploc, doActions=False, callPreParse=False)
Expand Down
9 changes: 9 additions & 0 deletions tests/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1804,6 +1804,15 @@ def testSkipToPreParseIgnoreExprs(self):
else:
print(result.dump())

def testSkipToIgnoreExpr2(self):
a, star = pp.Literal.using_each("a*")
wrapper = a + ... + a
expr = star + pp.SkipTo(star, ignore=wrapper) + star

# pyparsing 3.0.9 -> ['*', 'a_*_a', '*']
# pyparsing 3.1.0 -> ['*', '', '*']
self.assertParseAndCheckList(expr, "*a_*_a*", ['*', 'a_*_a', '*'])

def testEllipsisRepetition(self):
word = pp.Word(pp.alphas).setName("word")
num = pp.Word(pp.nums).setName("num")
Expand Down

0 comments on commit 54b39a5

Please sign in to comment.