Я пытаюсь использовать pyparsing для создания очень простого языка для решателя лабиринта в python.
Поскольку 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
, вызывая класс 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