Source code for contracts.pyparsing_utils
from .syntax import (Forward, Suppress, FollowedBy, Group, OneOrMore, Optional,
opAssoc)
[docs]def myOperatorPrecedence(baseExpr, opList):
"""Helper method for constructing grammars of expressions made up of
operators working in a precedence hierarchy. Operators may be unary or
binary, left- or right-associative. Parse actions can also be attached
to operator expressions.
Parameters:
- baseExpr - expression representing the most basic element for the
- opList - list of tuples, one for each operator precedence level in
expression grammar; each tuple is of the form
(opExpr, numTerms, rightLeftAssoc, parseAction), where:
- opExpr is the pyparsing expression for the operator;
may also be a string, which will be converted to a Literal;
if numTerms is 3, opExpr is a tuple of two expressions, for the
two operators separating the 3 terms
- numTerms is the number of terms for this operator (must
be 1, 2, or 3)
- rightLeftAssoc is the indicator whether the operator is
right or left associative, using the pyparsing-defined
constants opAssoc.RIGHT and opAssoc.LEFT.
- parseAction is the parse action to be associated with
expressions matching this operator expression (the
parse action tuple member may be omitted)
"""
ret = Forward()
allops = [x[0] for x in opList]
opnames = ",".join(str(x) for x in allops)
ret.setName('operatorSystem(%s)' % opnames)
# parenthesis = Suppress('(') + ret + FollowedBy(NotAny(oneOf(allops)))
# - Suppress(')')
parenthesis = Suppress('(') - ret - Suppress(')')
lastExpr = parenthesis.setName('parenthesis(%s)' % opnames) | baseExpr
#lastExpr.setName('Base operand (%s) or parenthesis' % str(baseExpr))
for operDef in opList:
opExpr, arity, rightLeftAssoc, pa = (operDef + (None,))[:4]
if arity == 3:
if opExpr is None or len(opExpr) != 2:
raise ValueError(
"if numterms=3, opExpr must be a tuple or list of two "
"expressions")
opExpr1, opExpr2 = opExpr
thisExpr = Forward().setName("operation_with(%s)" % opExpr)
if rightLeftAssoc == opAssoc.LEFT:
if arity == 1:
matchExpr = FollowedBy(lastExpr + opExpr) + Group(
lastExpr + OneOrMore(opExpr))
elif arity == 2:
if opExpr is not None:
# matchExpr = Group(lastExpr +
# FollowedBy(opExpr) + OneOrMore(opExpr - lastExpr))
matchExpr = Group(
lastExpr + FollowedBy(opExpr) - OneOrMore(
opExpr - lastExpr))
else:
matchExpr = FollowedBy(lastExpr + lastExpr) + Group(
lastExpr + OneOrMore(lastExpr))
elif arity == 3:
matchExpr = FollowedBy(
lastExpr + opExpr1 + lastExpr + opExpr2 + lastExpr) + \
Group(
lastExpr + opExpr1 + lastExpr + opExpr2 +
lastExpr)
else:
raise ValueError(
"operator must be unary (1), binary (2), or ternary (3)")
elif rightLeftAssoc == opAssoc.RIGHT:
if arity == 1:
# try to avoid LR with this extra test
if not isinstance(opExpr, Optional):
opExpr = Optional(opExpr)
matchExpr = FollowedBy(opExpr.expr - thisExpr) - Group(
opExpr - thisExpr)
elif arity == 2:
if opExpr is not None:
matchExpr = FollowedBy(
lastExpr + opExpr - thisExpr) + Group(
lastExpr + OneOrMore(opExpr - thisExpr))
else:
matchExpr = FollowedBy(lastExpr + thisExpr) + Group(
lastExpr + OneOrMore(thisExpr))
elif arity == 3:
matchExpr = FollowedBy(
lastExpr + opExpr1 + thisExpr + opExpr2 + thisExpr) + \
Group(
lastExpr + opExpr1 + thisExpr + opExpr2 +
thisExpr)
else:
raise ValueError(
"operator must be unary (1), binary (2), or ternary (3)")
else:
raise ValueError(
"operator must indicate right or left associativity")
if pa:
matchExpr.setParseAction(pa)
thisExpr << (matchExpr | lastExpr).setName(
'operation with %s' % opExpr)
lastExpr = thisExpr
ret << lastExpr
return ret