Error when passing class to Pyparsing setParseAction() class for “if/else” structure
up vote
1
down vote
favorite
I'm trying to use pyparsing to create a very simple langage for a maze bot solver in python.
Because pyparsing seems powerfull but not easy to manipulate, i'm starting by a simple exemple with assignment and if [expression] then [code] elsif [expression] then [code] else [code]
structure
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}
'''
The code for evaluation of arithmetic expression during assignment
# 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), ]
)
The code to determine parsing of assignement and condition statement :
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))
I try to attach an Action using setParseAction
on simple_if_group
variable, calling the class IfEval
. Majority of example attach a function as Action, but in the case of a If/Else structure, i'm supposing that a more structured class is better to evaluate condition later... I'm not sure this is the good way, so i take any advice
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
Assignments using SET
works without problem :
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}
Now, even before evaluation, i'm trying to parse the second exemple which call the setParseAction
method to IFEval
class :
parsed = program.parseString()
return an str() error ? probably because i don't understand how the parseAction function when you try to use a class and not a method :
>>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
python-3.x pyparsing
add a comment |
up vote
1
down vote
favorite
I'm trying to use pyparsing to create a very simple langage for a maze bot solver in python.
Because pyparsing seems powerfull but not easy to manipulate, i'm starting by a simple exemple with assignment and if [expression] then [code] elsif [expression] then [code] else [code]
structure
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}
'''
The code for evaluation of arithmetic expression during assignment
# 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), ]
)
The code to determine parsing of assignement and condition statement :
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))
I try to attach an Action using setParseAction
on simple_if_group
variable, calling the class IfEval
. Majority of example attach a function as Action, but in the case of a If/Else structure, i'm supposing that a more structured class is better to evaluate condition later... I'm not sure this is the good way, so i take any advice
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
Assignments using SET
works without problem :
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}
Now, even before evaluation, i'm trying to parse the second exemple which call the setParseAction
method to IFEval
class :
parsed = program.parseString()
return an str() error ? probably because i don't understand how the parseAction function when you try to use a class and not a method :
>>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
python-3.x pyparsing
Set this aside for a bit and just think through what the BNF for this language would be. Doesn't need to be exhaustive; where you allow anarith-expression
just putarith-expression
. But it will help you conceptualize the commands, common phrase formats, etc.
– PaulMcG
Nov 8 at 14:14
add a comment |
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I'm trying to use pyparsing to create a very simple langage for a maze bot solver in python.
Because pyparsing seems powerfull but not easy to manipulate, i'm starting by a simple exemple with assignment and if [expression] then [code] elsif [expression] then [code] else [code]
structure
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}
'''
The code for evaluation of arithmetic expression during assignment
# 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), ]
)
The code to determine parsing of assignement and condition statement :
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))
I try to attach an Action using setParseAction
on simple_if_group
variable, calling the class IfEval
. Majority of example attach a function as Action, but in the case of a If/Else structure, i'm supposing that a more structured class is better to evaluate condition later... I'm not sure this is the good way, so i take any advice
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
Assignments using SET
works without problem :
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}
Now, even before evaluation, i'm trying to parse the second exemple which call the setParseAction
method to IFEval
class :
parsed = program.parseString()
return an str() error ? probably because i don't understand how the parseAction function when you try to use a class and not a method :
>>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
python-3.x pyparsing
I'm trying to use pyparsing to create a very simple langage for a maze bot solver in python.
Because pyparsing seems powerfull but not easy to manipulate, i'm starting by a simple exemple with assignment and if [expression] then [code] elsif [expression] then [code] else [code]
structure
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}
'''
The code for evaluation of arithmetic expression during assignment
# 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), ]
)
The code to determine parsing of assignement and condition statement :
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))
I try to attach an Action using setParseAction
on simple_if_group
variable, calling the class IfEval
. Majority of example attach a function as Action, but in the case of a If/Else structure, i'm supposing that a more structured class is better to evaluate condition later... I'm not sure this is the good way, so i take any advice
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
Assignments using SET
works without problem :
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}
Now, even before evaluation, i'm trying to parse the second exemple which call the setParseAction
method to IFEval
class :
parsed = program.parseString()
return an str() error ? probably because i don't understand how the parseAction function when you try to use a class and not a method :
>>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
python-3.x pyparsing
python-3.x pyparsing
asked Nov 5 at 13:32
reyman64
281956
281956
Set this aside for a bit and just think through what the BNF for this language would be. Doesn't need to be exhaustive; where you allow anarith-expression
just putarith-expression
. But it will help you conceptualize the commands, common phrase formats, etc.
– PaulMcG
Nov 8 at 14:14
add a comment |
Set this aside for a bit and just think through what the BNF for this language would be. Doesn't need to be exhaustive; where you allow anarith-expression
just putarith-expression
. But it will help you conceptualize the commands, common phrase formats, etc.
– PaulMcG
Nov 8 at 14:14
Set this aside for a bit and just think through what the BNF for this language would be. Doesn't need to be exhaustive; where you allow an
arith-expression
just put arith-expression
. But it will help you conceptualize the commands, common phrase formats, etc.– PaulMcG
Nov 8 at 14:14
Set this aside for a bit and just think through what the BNF for this language would be. Doesn't need to be exhaustive; where you allow an
arith-expression
just put arith-expression
. But it will help you conceptualize the commands, common phrase formats, etc.– PaulMcG
Nov 8 at 14:14
add a comment |
1 Answer
1
active
oldest
votes
up vote
1
down vote
(This is a huge! question - you'll get better response on SO if you can strip your problem down to something small.)
Looking at the last 2 lines of your traceback:
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
You have passed a ParseResults
to parseString
, not a string. Then when pyparsing tries to call str functions on the input parameter, the ParseResults interprets these as attempts to read attributes, which it default to returning ''. Dissecting this a bit more:
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
is okay for parse actions that take simple tokens, but this one is some complex, I'd recommend that you print(tokens.dump())
as the first line in your parse action, to better visualize just what kind of structure you are getting.
Your method for detecting IF, ELSIF, and ELSE is also error-prone, and you are better off using results names in your grammar. Instead of:
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)
do:
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_')))
Now here is a parse action that makes use of those results names:
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)
For this sample (note starting small, then getting more complicated):
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)
We get:
[['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 }
Thanks for your very detailled answer !
– reyman64
Nov 8 at 19:36
One more question perhaps. About evaluation which follow this parsing, what did you advice ? A see some tutorials usingisInstance(...)
to pattern match object and apply the good evaluation for the good element. Is it a best pratice ?
– reyman64
Nov 8 at 19:44
These aren't pyparsing tutorials, I hope. Please look at the SimpleBool.py and eval_arith.py examples in the pyparsing examples directory.
– PaulMcG
Nov 8 at 22:38
Thx, i looks the examples, if i understand well with simple example, i think a more complex example (like if/else evaluation) in pyparsing examples directory could be very cool for beginer like me in parsing.
– reyman64
Nov 10 at 16:11
1
Why, because i follow this type of tutorial to try to make if/else parsing, and there are lot ofisinstance()
call : ralsina.me/weblog/posts/creating-languages-for-dummies.html
– reyman64
Nov 10 at 16:16
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
(This is a huge! question - you'll get better response on SO if you can strip your problem down to something small.)
Looking at the last 2 lines of your traceback:
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
You have passed a ParseResults
to parseString
, not a string. Then when pyparsing tries to call str functions on the input parameter, the ParseResults interprets these as attempts to read attributes, which it default to returning ''. Dissecting this a bit more:
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
is okay for parse actions that take simple tokens, but this one is some complex, I'd recommend that you print(tokens.dump())
as the first line in your parse action, to better visualize just what kind of structure you are getting.
Your method for detecting IF, ELSIF, and ELSE is also error-prone, and you are better off using results names in your grammar. Instead of:
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)
do:
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_')))
Now here is a parse action that makes use of those results names:
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)
For this sample (note starting small, then getting more complicated):
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)
We get:
[['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 }
Thanks for your very detailled answer !
– reyman64
Nov 8 at 19:36
One more question perhaps. About evaluation which follow this parsing, what did you advice ? A see some tutorials usingisInstance(...)
to pattern match object and apply the good evaluation for the good element. Is it a best pratice ?
– reyman64
Nov 8 at 19:44
These aren't pyparsing tutorials, I hope. Please look at the SimpleBool.py and eval_arith.py examples in the pyparsing examples directory.
– PaulMcG
Nov 8 at 22:38
Thx, i looks the examples, if i understand well with simple example, i think a more complex example (like if/else evaluation) in pyparsing examples directory could be very cool for beginer like me in parsing.
– reyman64
Nov 10 at 16:11
1
Why, because i follow this type of tutorial to try to make if/else parsing, and there are lot ofisinstance()
call : ralsina.me/weblog/posts/creating-languages-for-dummies.html
– reyman64
Nov 10 at 16:16
add a comment |
up vote
1
down vote
(This is a huge! question - you'll get better response on SO if you can strip your problem down to something small.)
Looking at the last 2 lines of your traceback:
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
You have passed a ParseResults
to parseString
, not a string. Then when pyparsing tries to call str functions on the input parameter, the ParseResults interprets these as attempts to read attributes, which it default to returning ''. Dissecting this a bit more:
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
is okay for parse actions that take simple tokens, but this one is some complex, I'd recommend that you print(tokens.dump())
as the first line in your parse action, to better visualize just what kind of structure you are getting.
Your method for detecting IF, ELSIF, and ELSE is also error-prone, and you are better off using results names in your grammar. Instead of:
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)
do:
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_')))
Now here is a parse action that makes use of those results names:
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)
For this sample (note starting small, then getting more complicated):
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)
We get:
[['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 }
Thanks for your very detailled answer !
– reyman64
Nov 8 at 19:36
One more question perhaps. About evaluation which follow this parsing, what did you advice ? A see some tutorials usingisInstance(...)
to pattern match object and apply the good evaluation for the good element. Is it a best pratice ?
– reyman64
Nov 8 at 19:44
These aren't pyparsing tutorials, I hope. Please look at the SimpleBool.py and eval_arith.py examples in the pyparsing examples directory.
– PaulMcG
Nov 8 at 22:38
Thx, i looks the examples, if i understand well with simple example, i think a more complex example (like if/else evaluation) in pyparsing examples directory could be very cool for beginer like me in parsing.
– reyman64
Nov 10 at 16:11
1
Why, because i follow this type of tutorial to try to make if/else parsing, and there are lot ofisinstance()
call : ralsina.me/weblog/posts/creating-languages-for-dummies.html
– reyman64
Nov 10 at 16:16
add a comment |
up vote
1
down vote
up vote
1
down vote
(This is a huge! question - you'll get better response on SO if you can strip your problem down to something small.)
Looking at the last 2 lines of your traceback:
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
You have passed a ParseResults
to parseString
, not a string. Then when pyparsing tries to call str functions on the input parameter, the ParseResults interprets these as attempts to read attributes, which it default to returning ''. Dissecting this a bit more:
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
is okay for parse actions that take simple tokens, but this one is some complex, I'd recommend that you print(tokens.dump())
as the first line in your parse action, to better visualize just what kind of structure you are getting.
Your method for detecting IF, ELSIF, and ELSE is also error-prone, and you are better off using results names in your grammar. Instead of:
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)
do:
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_')))
Now here is a parse action that makes use of those results names:
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)
For this sample (note starting small, then getting more complicated):
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)
We get:
[['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 }
(This is a huge! question - you'll get better response on SO if you can strip your problem down to something small.)
Looking at the last 2 lines of your traceback:
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
You have passed a ParseResults
to parseString
, not a string. Then when pyparsing tries to call str functions on the input parameter, the ParseResults interprets these as attempts to read attributes, which it default to returning ''. Dissecting this a bit more:
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
is okay for parse actions that take simple tokens, but this one is some complex, I'd recommend that you print(tokens.dump())
as the first line in your parse action, to better visualize just what kind of structure you are getting.
Your method for detecting IF, ELSIF, and ELSE is also error-prone, and you are better off using results names in your grammar. Instead of:
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)
do:
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_')))
Now here is a parse action that makes use of those results names:
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)
For this sample (note starting small, then getting more complicated):
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)
We get:
[['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 }
edited Nov 8 at 14:36
answered Nov 8 at 12:01
PaulMcG
45.4k866111
45.4k866111
Thanks for your very detailled answer !
– reyman64
Nov 8 at 19:36
One more question perhaps. About evaluation which follow this parsing, what did you advice ? A see some tutorials usingisInstance(...)
to pattern match object and apply the good evaluation for the good element. Is it a best pratice ?
– reyman64
Nov 8 at 19:44
These aren't pyparsing tutorials, I hope. Please look at the SimpleBool.py and eval_arith.py examples in the pyparsing examples directory.
– PaulMcG
Nov 8 at 22:38
Thx, i looks the examples, if i understand well with simple example, i think a more complex example (like if/else evaluation) in pyparsing examples directory could be very cool for beginer like me in parsing.
– reyman64
Nov 10 at 16:11
1
Why, because i follow this type of tutorial to try to make if/else parsing, and there are lot ofisinstance()
call : ralsina.me/weblog/posts/creating-languages-for-dummies.html
– reyman64
Nov 10 at 16:16
add a comment |
Thanks for your very detailled answer !
– reyman64
Nov 8 at 19:36
One more question perhaps. About evaluation which follow this parsing, what did you advice ? A see some tutorials usingisInstance(...)
to pattern match object and apply the good evaluation for the good element. Is it a best pratice ?
– reyman64
Nov 8 at 19:44
These aren't pyparsing tutorials, I hope. Please look at the SimpleBool.py and eval_arith.py examples in the pyparsing examples directory.
– PaulMcG
Nov 8 at 22:38
Thx, i looks the examples, if i understand well with simple example, i think a more complex example (like if/else evaluation) in pyparsing examples directory could be very cool for beginer like me in parsing.
– reyman64
Nov 10 at 16:11
1
Why, because i follow this type of tutorial to try to make if/else parsing, and there are lot ofisinstance()
call : ralsina.me/weblog/posts/creating-languages-for-dummies.html
– reyman64
Nov 10 at 16:16
Thanks for your very detailled answer !
– reyman64
Nov 8 at 19:36
Thanks for your very detailled answer !
– reyman64
Nov 8 at 19:36
One more question perhaps. About evaluation which follow this parsing, what did you advice ? A see some tutorials using
isInstance(...)
to pattern match object and apply the good evaluation for the good element. Is it a best pratice ?– reyman64
Nov 8 at 19:44
One more question perhaps. About evaluation which follow this parsing, what did you advice ? A see some tutorials using
isInstance(...)
to pattern match object and apply the good evaluation for the good element. Is it a best pratice ?– reyman64
Nov 8 at 19:44
These aren't pyparsing tutorials, I hope. Please look at the SimpleBool.py and eval_arith.py examples in the pyparsing examples directory.
– PaulMcG
Nov 8 at 22:38
These aren't pyparsing tutorials, I hope. Please look at the SimpleBool.py and eval_arith.py examples in the pyparsing examples directory.
– PaulMcG
Nov 8 at 22:38
Thx, i looks the examples, if i understand well with simple example, i think a more complex example (like if/else evaluation) in pyparsing examples directory could be very cool for beginer like me in parsing.
– reyman64
Nov 10 at 16:11
Thx, i looks the examples, if i understand well with simple example, i think a more complex example (like if/else evaluation) in pyparsing examples directory could be very cool for beginer like me in parsing.
– reyman64
Nov 10 at 16:11
1
1
Why, because i follow this type of tutorial to try to make if/else parsing, and there are lot of
isinstance()
call : ralsina.me/weblog/posts/creating-languages-for-dummies.html– reyman64
Nov 10 at 16:16
Why, because i follow this type of tutorial to try to make if/else parsing, and there are lot of
isinstance()
call : ralsina.me/weblog/posts/creating-languages-for-dummies.html– reyman64
Nov 10 at 16:16
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53155419%2ferror-when-passing-class-to-pyparsing-setparseaction-class-for-if-else-struc%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Set this aside for a bit and just think through what the BNF for this language would be. Doesn't need to be exhaustive; where you allow an
arith-expression
just putarith-expression
. But it will help you conceptualize the commands, common phrase formats, etc.– PaulMcG
Nov 8 at 14:14