summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorptmcg <ptmcg@9bf210a0-9d2d-494c-87cf-cfb32e7dff7b>2017-03-06 02:30:49 +0000
committerptmcg <ptmcg@9bf210a0-9d2d-494c-87cf-cfb32e7dff7b>2017-03-06 02:30:49 +0000
commit754bb208f2484f2eefd53f15a2f5ea76e9cb6c17 (patch)
treee2f9b13a2449ceb45479e3d70ee2b7ca962f7194
parent236250b5706bac5ee080b15555abc9f46e473f5b (diff)
downloadpyparsing-754bb208f2484f2eefd53f15a2f5ea76e9cb6c17.tar.gz
Add minor enht for infixNotation, to accept a sequence of parse actions at each precedence level
git-svn-id: svn://svn.code.sf.net/p/pyparsing/code/trunk@460 9bf210a0-9d2d-494c-87cf-cfb32e7dff7b
-rw-r--r--src/CHANGES3
-rw-r--r--src/pyparsing.py15
-rw-r--r--src/unitTests.py71
3 files changed, 83 insertions, 6 deletions
diff --git a/src/CHANGES b/src/CHANGES
index 99214c9..91eaf63 100644
--- a/src/CHANGES
+++ b/src/CHANGES
@@ -23,6 +23,9 @@ Version 2.2.0 - March, 2017
- Minor internal change when using '-' operator, to be compatible
with ParserElement.streamline() method.
+- Expanded infixNotation to accept a list or tuple of parse actions
+ to attach to an operation.
+
- New unit test added for dill support for storing pyparsing parsers.
Ordinary Python pickle can be used to pickle pyparsing parsers as
long as they do not use any parse actions. The 'dill' module is an
diff --git a/src/pyparsing.py b/src/pyparsing.py
index af5fcd0..e8aefc8 100644
--- a/src/pyparsing.py
+++ b/src/pyparsing.py
@@ -61,7 +61,7 @@ The pyparsing module handles some of the problems that are typically vexing when
"""
__version__ = "2.2.0"
-__versionTime__ = "03 Mar 2017 01:09 UTC"
+__versionTime__ = "06 Mar 2017 02:06 UTC"
__author__ = "Paul McGuire <ptmcg@users.sourceforge.net>"
import string
@@ -1226,7 +1226,7 @@ class ParserElement(object):
def setParseAction( self, *fns, **kwargs ):
"""
- Define action to perform when successfully matching parse element definition.
+ Define one or more actions to perform when successfully matching parse element definition.
Parse action fn is a callable method with 0-3 arguments, called as C{fn(s,loc,toks)},
C{fn(loc,toks)}, C{fn(toks)}, or just C{fn()}, where:
- s = the original string being parsed (see note below)
@@ -1264,7 +1264,7 @@ class ParserElement(object):
def addParseAction( self, *fns, **kwargs ):
"""
- Add parse action to expression's list of parse actions. See L{I{setParseAction}<setParseAction>}.
+ Add one or more parse actions to expression's list of parse actions. See L{I{setParseAction}<setParseAction>}.
See examples in L{I{copy}<copy>}.
"""
@@ -5039,7 +5039,9 @@ def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ):
constants C{opAssoc.RIGHT} and C{opAssoc.LEFT}.
- parseAction is the parse action to be associated with
expressions matching this operator expression (the
- parse action tuple member may be omitted)
+ parse action tuple member may be omitted); if the parse action
+ is passed a tuple or list of functions, this is equivalent to
+ calling C{setParseAction(*fn)} (L{ParserElement.setParseAction})
- lpar - expression for matching left-parentheses (default=C{Suppress('(')})
- rpar - expression for matching right-parentheses (default=C{Suppress(')')})
@@ -5112,7 +5114,10 @@ def infixNotation( baseExpr, opList, lpar=Suppress('('), rpar=Suppress(')') ):
else:
raise ValueError("operator must indicate right or left associativity")
if pa:
- matchExpr.setParseAction( pa )
+ if isinstance(pa, (tuple, list)):
+ matchExpr.setParseAction(*pa)
+ else:
+ matchExpr.setParseAction(pa)
thisExpr <<= ( matchExpr.setName(termName) | lastExpr )
lastExpr = thisExpr
ret <<= lastExpr
diff --git a/src/unitTests.py b/src/unitTests.py
index e20de50..2bc7da7 100644
--- a/src/unitTests.py
+++ b/src/unitTests.py
@@ -1322,7 +1322,7 @@ class InfixNotationGrammarTest3(ParseTestCase):
expr = infixNotation( operand,
[
("!", 1, opAssoc.LEFT),
- ("^", 2, opAssoc.RIGHT),
+ ("^", 2, opAssoc.LEFT),
(signop, 1, opAssoc.RIGHT),
(multop, 2, opAssoc.LEFT),
(plusop, 2, opAssoc.LEFT),
@@ -1366,6 +1366,75 @@ class InfixNotationGrammarTest4(ParseTestCase):
assert str(results) == expected, "failed to match expected results, got '%s'" % str(results)
print_()
+class InfixNotationGrammarTest5(ParseTestCase):
+ from pyparsing import infixNotation, opAssoc, pyparsing_common, Literal, oneOf, ParseResults
+
+ expop = Literal('**')
+ signop = oneOf('+ -')
+ multop = oneOf('* /')
+ plusop = oneOf('+ -')
+
+ class ExprNode(object):
+ def __init__(self, tokens):
+ self.tokens = tokens[0]
+
+ def eval(self):
+ return None
+
+ class NumberNode(ExprNode):
+ def eval(self):
+ return self.tokens
+
+ class SignOp(ExprNode):
+ def eval(self):
+ mult = {'+': 1, '-': -1}[self.tokens[0]]
+ return mult * self.tokens[1].eval()
+
+ class BinOp(ExprNode):
+ def eval(self):
+ ret = self.tokens[0].eval()
+ for op, operand in zip(self.tokens[1::2], self.tokens[2::2]):
+ ret = self.opn_map[op](ret, operand.eval())
+ return ret
+
+ class ExpOp(BinOp):
+ opn_map = {'**': lambda a, b: b ** a}
+
+ class MultOp(BinOp):
+ import operator
+ opn_map = {'*': operator.mul, '/': operator.truediv}
+
+ class AddOp(BinOp):
+ import operator
+ opn_map = {'+': operator.add, '-': operator.sub}
+
+ operand = pyparsing_common.number().setParseAction(NumberNode)
+ expr = infixNotation(operand,
+ [
+ (expop, 2, opAssoc.LEFT, (lambda pr: [pr[0][::-1]], ExpOp)),
+ (signop, 1, opAssoc.RIGHT, SignOp),
+ (multop, 2, opAssoc.LEFT, MultOp),
+ (plusop, 2, opAssoc.LEFT, AddOp),
+ ])
+
+ tests = """\
+ 2+7
+ 2**3
+ 2**3**2
+ 3**9
+ 3**3**2
+ """
+
+ for t in tests.splitlines():
+ t = t.strip()
+ if not t:
+ continue
+
+ parsed = expr.parseString(t)
+ eval_value = parsed[0].eval()
+ assert eval_value == eval(t), "Error evaluating %r, expected %r, got %r" % (t, eval(t), eval_value)
+
+
class PickleTest_Greeting():
def __init__(self, toks):
self.salutation = toks[0]