我正在尝试使用pyparsing为python中的迷宫机器人求解器创建一个very simple langage。
因为pyparsing似乎功能强大,但操作起来并不容易,所以我从一个简单的带有赋值和if [expression] then [code] elsif [expression] then [code] else [code]
结构的示例开始
simple_example_assignement = '''
SET x 3 + 2
SET y 2
'''
simple_example_condition = '''
IF x NOT MATCH 3 THEN { SET b 2 }
ELSIF y NOT MATCH 2 THEN {SET d 4}
ELSE { SET c 3}
'''
赋值期间算术表达式的求值代码
# store variable value for evaluation
vars = {}
class EvalAddOp():
"""Class to evaluate addition and subtraction expressions."""
def __init__(self, tokens):
self.value = tokens[0]
print(self.value)
def eval(self, vars_):
if type(self.value[0]) in [EvalAddOp]:
print("ENTER EVAL ADD OPP")
sum = self.value[0].eval(vars_)
else:
sum = self.checkNum(self.value[0], vars_)
return self.ops(sum, vars_)
def checkNum(self, val, _vars):
print(type(val), " = ", val)
if type(val) in [int, float]:
return val
elif type(val) in [EvalAddOp]:
return val.eval(_vars)
else:
return _vars[val]
def ops(self, sum, vars_):
for op, val in operatorOperands(self.value[1:]):
if op == '+':
sum += self.checkNum(val, vars_)
if op == '-':
sum -= self.checkNum(val, vars_)
return sum
def eval_expression(expr):
if isinstance(expr, str):
if expr[0] in '"\'': # string literal
return expr[1:-1] # remove quotes
else:
return vars.get(expr)
elif isinstance(expr, EvalAddOp):
return expr.eval(vars)
return expr
integer = Word(nums).setParseAction(lambda t: int(t[0]))
variable = Word(alphas, exact=1)
operand = integer | variable
plusop = oneOf('+ -')
signop = oneOf('+ -')
multop = oneOf('* /')
matching = Keyword('MATCH')
arithmeticExpression = infixNotation(operand,
[(signop, 1, opAssoc.RIGHT),
(multop, 2, opAssoc.LEFT),
(plusop, 2, opAssoc.LEFT, EvalAddOp), ]
)
确定分配和条件语句解析的代码:
expression = Forward()
exprOperators = Forward()
code_block = Forward()
literal = quotedString ^ pyparsing_common.number
commonExpression = literal ^ variable ^ arithmeticExpression
matchingExpression = Group(commonExpression + exprOperators + commonExpression)
expression << matchingExpression ^ commonExpression
exprOperators << infixNotation(matching,[("NOT", 1, opAssoc.RIGHT)])
# Assignment rules
set_assignment = Group(Keyword('SET') + variable + commonExpression)
# If/Else rules
simple_if_stmt = Keyword('IF') + expression + Keyword('THEN') + code_block
else_if_stmt = Keyword('ELSIF') + expression + Keyword('THEN') + code_block
else_stmt = Keyword('ELSE') + code_block
simple_if_group = Group(simple_if_stmt + Optional(OneOrMore(else_if_stmt)) + Optional(else_stmt)).setParseAction(IfEval)
# all possible statements in the example prorgam
stmt = set_assignment ^ simple_if_group
# Code to evaluate
code_block << Group(Literal('{').suppress() + OneOrMore(stmt) + Literal('}').suppress()).setName('code block')
program = Dict(OneOrMore(stmt))
我尝试在setParseAction
变量上使用simple_if_group
附加一个Action,并调用类IfEval
。大部分示例都将一个函数附加为Action,但是在If / Else结构的情况下,我认为结构化更好的类在以后评估条件时会更好...我不确定这是否是好方法,因此我接受任何建议
class IFEval():
def __init__(self):
self.ifStructure = {}
def __len__(self):
return len(self.ifStructure)
def __getitem__(self, item):
return self.ifStructure["item"]
def __setitem__(self, key, value):
self.ifStructure[key] = value
def __delitem__(self, key):
pass
def __copy__(self):
return self.ifStructure[:]
@traceParseAction
def IfEval(s, l, tokens):
if_stmt = IFEval()
if Keyword("IF").parseString(tokens[0][1]):
if_stmt["then_codeblock"] = tokens[0][3]
if Keyword("ELSIF").parseString(tokens[0][4]):
if_stmt["elsif_codeblock"] = tokens[0][6]
if Keyword("ELSE").parseString(tokens[0][8]):
if_stmt["else_codeblock"] = tokens[0][9]
return if_stmt
使用SET
进行分配可以正常工作:
parsed = program.parseString(simple_example_assignement)
for _, name, value in parsed:
vars[name] = eval_expression(value)
print(vars)
[3, '+', 2]
<class 'int'> = 3
<class 'int'> = 2
{'y': 2, 'x': 5}
现在,即使在评估之前,我也试图解析第二个示例,该示例将setParseAction
方法调用到IFEval
类:
parsed = program.parseString()
返回str()错误?可能是因为当您尝试使用类而不是方法时,我不了解parseAction函数的方式:
>>entering IfEval(line: 'IF x NOT MATCH 3 THEN { SET b 2 } ', 21, ([(['IF', (['x', (['NOT', 'MATCH'], {}), 3], {}), 'THEN', ([(['SET', 'b', 2], {})], {}), 'ELSIF', (['y', (['NOT', 'MATCH'], {}), 2], {}), 'THEN', ([(['SET', 'd', 4], {})], {}), 'ELSE', ([(['SET', 'c', 3], {})], {})], {})], {}))
<<leaving IfEval (exception: 'str' object is not callable)
Traceback (most recent call last):
File "/home/reyman/Projets/cours/exercice/labyrinthe_matplot_python/parsingLanguage.py", line 246, in <module>
parsed = program.parseString(conditional_test)
File "/home/reyman/.pyenv/versions/labyrinthes/lib/python3.5/site-packages/pyparsing.py", line 1666, in parseString
loc, tokens = self._parse( instring, 0 )
File "/home/reyman/.pyenv/versions/labyrinthes/lib/python3.5/site-packages/pyparsing.py", line 1412, in _parseNoCache
loc,tokens = self.parseImpl( instring, preloc, doActions )
File "/home/reyman/.pyenv/versions/labyrinthes/lib/python3.5/site-packages/pyparsing.py", line 3805, in parseImpl
return self.expr._parse( instring, loc, doActions, callPreParse=False )
File "/home/reyman/.pyenv/versions/labyrinthes/lib/python3.5/site-packages/pyparsing.py", line 1412, in _parseNoCache
loc,tokens = self.parseImpl( instring, preloc, doActions )
File "/home/reyman/.pyenv/versions/labyrinthes/lib/python3.5/site-packages/pyparsing.py", line 4033, in parseImpl
loc, tmptokens = self_expr_parse( instring, preloc, doActions )
File "/home/reyman/.pyenv/versions/labyrinthes/lib/python3.5/site-packages/pyparsing.py", line 1412, in _parseNoCache
loc,tokens = self.parseImpl( instring, preloc, doActions )
File "/home/reyman/.pyenv/versions/labyrinthes/lib/python3.5/site-packages/pyparsing.py", line 3555, in parseImpl
return e._parse( instring, loc, doActions )
File "/home/reyman/.pyenv/versions/labyrinthes/lib/python3.5/site-packages/pyparsing.py", line 1445, in _parseNoCache
tokens = fn( instring, tokensStart, retTokens )
File "/home/reyman/.pyenv/versions/labyrinthes/lib/python3.5/site-packages/pyparsing.py", line 1082, in wrapper
ret = func(*args[limit[0]:])
File "/home/reyman/.pyenv/versions/labyrinthes/lib/python3.5/site-packages/pyparsing.py", line 4588, in z
ret = f(*paArgs)
File "/home/reyman/.pyenv/versions/labyrinthes/lib/python3.5/site-packages/pyparsing.py", line 1082, in wrapper
ret = func(*args[limit[0]:])
File "/home/reyman/Projets/cours/exercice/labyrinthe_matplot_python/parsingLanguage.py", line 99, in IfEval
if Keyword("IF").parseString(tokens[0][1]):
File "/home/reyman/.pyenv/versions/labyrinthes/lib/python3.5/site-packages/pyparsing.py", line 1664, in parseString
instring = instring.expandtabs()
TypeError: 'str' object is not callable
答案 0 :(得分:1)
(这是一个很大的问题!如果您可以将问题缩小到较小的范围,您将在SO上获得更好的响应。)
查看回溯的最后两行:
File "/home/reyman/Projets/cours/exercice/labyrinthe_matplot_python/parsingLanguage.py", line 99, in IfEval
if Keyword("IF").parseString(tokens[0][1]):
File "/home/reyman/.pyenv/versions/labyrinthes/lib/python3.5/site-packages/pyparsing.py", line 1664, in parseString
instring = instring.expandtabs()
TypeError: 'str' object is not callable
您已将ParseResults
传递给parseString
,而不是字符串。然后,当pyparsing尝试在输入参数上调用str函数时,ParseResults会将这些函数解释为尝试读取属性的尝试,默认情况下将返回''。进一步剖析:
instring.expandtabs()
^^^^^^^^
a ParseResults, not a str
^^^^^^^^^^
an undefined attribute in the ParseResults, so returns ''
^^
trying to "call" the str - but str's aren't callable, so exception!
traceParseAction
对于采用简单标记的解析动作是可以的,但是这有点复杂,我建议您print(tokens.dump())
作为解析动作的第一行,以更好地可视化什么您正在获得一种结构。
您用于检测IF,ELSIF和ELSE的方法也容易出错,因此最好在语法中使用结果名称。代替:
simple_if_stmt = Keyword('IF') + expression + Keyword('THEN') + code_block
else_if_stmt = Keyword('ELSIF') + expression + Keyword('THEN') + code_block
else_stmt = Keyword('ELSE') + code_block
simple_if_group = Group(simple_if_stmt + Optional(OneOrMore(else_if_stmt)) + Optional(else_stmt)).setParseAction(IfEval)
这样做:
from pyparsing import *
# fake expressions, not intended to replace those in the original problem
ident = pyparsing_common.identifier
integer = pyparsing_common.integer
expression = ident + "MATCH" + (ident | integer)
code_block = originalTextFor(nestedExpr('{', '}'))
simple_if_stmt = Group(Keyword('IF') + expression('condition')
+ Keyword('THEN') + code_block('code'))
else_if_stmt = Group(Keyword('ELSIF') + expression('condition')
+ Keyword('THEN') + code_block('code'))
else_stmt = Group(Keyword('ELSE') + code_block('code'))
simple_if_group = Group(simple_if_stmt('if_')
+ Optional(OneOrMore(else_if_stmt('elsif*')))
+ Optional(else_stmt('else_')))
现在是使用这些结果名称的解析动作:
def IfStatement(s, l, tokens):
# peel of outer grouping layer
tokens = tokens[0]
# dump out inner structure of parsed results
print(tokens.dump())
print('IF:', tokens.if_.condition, '->', tokens.if_.code)
if 'elsif' in tokens:
for elsif in tokens.elsif:
print('ELSIF:', elsif.condition, '->', elsif.code)
if 'else_' in tokens:
print('ELSE:', '->', tokens.else_.code)
print()
simple_if_group.addParseAction(IfStatement)
对于此示例(请注意从小开始,然后变得更加复杂):
sample = """\
IF X MATCH Y THEN { this is some code }
IF X MATCH Y THEN { this is some code }
ELSE { do this instead }
IF X MATCH Y THEN { this is some Y code }
ELSIF X MATCH Z THEN { this is some Z code }
ELSIF X MATCH A THEN { this is some A code }
ELSE { do this instead }
"""
result = OneOrMore(simple_if_group).parseString(sample)
我们得到:
[['IF', 'X', 'MATCH', 'Y', 'THEN', '{ this is some code }']]
- if_: ['IF', 'X', 'MATCH', 'Y', 'THEN', '{ this is some code }']
- code: '{ this is some code }'
- condition: ['X', 'MATCH', 'Y']
IF: ['X', 'MATCH', 'Y'] -> { this is some code }
[['IF', 'X', 'MATCH', 'Y', 'THEN', '{ this is some code }'], ['ELSE', '{ do this instead }']]
- else_: ['ELSE', '{ do this instead }']
- code: '{ do this instead }'
- if_: ['IF', 'X', 'MATCH', 'Y', 'THEN', '{ this is some code }']
- code: '{ this is some code }'
- condition: ['X', 'MATCH', 'Y']
IF: ['X', 'MATCH', 'Y'] -> { this is some code }
ELSE: -> { do this instead }
[['IF', 'X', 'MATCH', 'Y', 'THEN', '{ this is some Y code }'], ['ELSIF', 'X', 'MATCH', 'Z', 'THEN', '{ this is some Z code }'], ['ELSIF', 'X', 'MATCH', 'A', 'THEN', '{ this is some A code }'], ['ELSE', '{ do this instead }']]
- else_: ['ELSE', '{ do this instead }']
- code: '{ do this instead }'
- elsif: [['ELSIF', 'X', 'MATCH', 'Z', 'THEN', '{ this is some Z code }'], ['ELSIF', 'X', 'MATCH', 'A', 'THEN', '{ this is some A code }']]
[0]:
['ELSIF', 'X', 'MATCH', 'Z', 'THEN', '{ this is some Z code }']
- code: '{ this is some Z code }'
- condition: ['X', 'MATCH', 'Z']
[1]:
['ELSIF', 'X', 'MATCH', 'A', 'THEN', '{ this is some A code }']
- code: '{ this is some A code }'
- condition: ['X', 'MATCH', 'A']
- if_: ['IF', 'X', 'MATCH', 'Y', 'THEN', '{ this is some Y code }']
- code: '{ this is some Y code }'
- condition: ['X', 'MATCH', 'Y']
IF: ['X', 'MATCH', 'Y'] -> { this is some Y code }
ELSIF: ['X', 'MATCH', 'Z'] -> { this is some Z code }
ELSIF: ['X', 'MATCH', 'A'] -> { this is some A code }
ELSE: -> { do this instead }