注意:这不是一个可行的解决方案(至少目前还不是)。它依赖于未发布的 pyparsing 更改,这些更改甚至还没有通过所有单元测试。我发布它只是为了描述解决方案的可能方法。
噢!这比我想象的要困难得多。为了实现,我使用了 pyparsing 的忽略机制,并将解析操作附加到lpar
and rpar
要忽略的表达式<NL>
位于括号内,但不在括号外。这还需要添加清除的能力ignoreExprs
通过调用列出expr.ignore(None)
。您的代码可能如下所示:
import pyparsing as pp
# works with and without packrat
pp.ParserElement.enablePackrat()
pp.ParserElement.setDefaultWhitespaceChars(' \t')
operand = pp.Word(pp.nums)
var = pp.Word(pp.alphas)
arith_expr = pp.Forward()
arith_expr.ignore(pp.pythonStyleComment)
lpar = pp.Suppress("(")
rpar = pp.Suppress(")")
# code to implement selective ignore of NL's inside ()'s
NL = pp.Suppress("\n")
base_ignore = arith_expr.ignoreExprs[:]
ignore_stack = base_ignore[:]
def lpar_pa():
ignore_stack.append(NL)
arith_expr.ignore(NL)
#~ print('post-push', arith_expr.ignoreExprs)
def rpar_pa():
ignore_stack.pop(-1)
arith_expr.ignore(None)
for e in ignore_stack:
arith_expr.ignore(e)
#~ print('post-pop', arith_expr.ignoreExprs)
def reset_stack(*args):
arith_expr.ignore(None)
for e in base_ignore:
arith_expr.ignore(e)
#~ print('post-reset', arith_expr.ignoreExprs)
lpar.addParseAction(lpar_pa)
rpar.addParseAction(rpar_pa)
arith_expr.setFailAction(reset_stack)
arith_expr.addParseAction(reset_stack)
# now define the infix notation as usual
arith_expr <<= pp.infixNotation(operand | var,
[
("-", 1, pp.opAssoc.RIGHT),
(pp.oneOf("* /"), 2, pp.opAssoc.LEFT),
(pp.oneOf("- +"), 2, pp.opAssoc.LEFT),
],
lpar=lpar, rpar=rpar
)
assignment = var + '=' + arith_expr
# Try it out!
assignment.runTests([
"""a = 1 + 3""",
"""a = (1 + 3)""",
"""a = 1 + 2 + (
3 +
1)""",
"""a = 1 + 2 + ((
3 +
1))""",
"""a = 1 + 2 +
3""",
], fullDump=False)
Prints:
a = 1 + 3
['a', '=', ['1', '+', '3']]
a = (1 + 3)
['a', '=', ['1', '+', '3']]
a = 1 + 2 + (
3 +
1)
['a', '=', ['1', '+', '2', '+', ['3', '+', '1']]]
a = 1 + 2 + ((
3 +
1))
['a', '=', ['1', '+', '2', '+', ['3', '+', '1']]]
a = 1 + 2 +
3
a = 1 + 2 +
^
FAIL: Expected end of text, found '+' (at char 10), (line:1, col:11)>Exit code: 0
因此,这并非不可能,但确实需要一些英勇的努力。