我想使用非常好的包pyparsing来解析以下类型的字符串。
atomname *和atomindex 1,2,3
atomname xxx,yyy或atomtype rrr,sss
硫醇
不是atomindex 1,2,3
not(atomindex 4,5,6)或atomname *
基于此解析,我将匹配链接到将要执行的特定函数调用 选择原子。
所有选择关键字(atomname,atomindex,thiol ......)都存储在一个列表中(即selkwds
)。
我尝试了这个,但失败了:
keyword = oneOf(selkwds,caseless=True).setParseAction(self.__parse_keyword)
func_call = Forward()
func_call << (keyword + commaSeparatedList).setParseAction(self.__parse_expression)
func_call = operatorPrecedence(func_call, [(NOT, 1, opAssoc.RIGHT, self.__not),
(AND, 2, opAssoc.LEFT , self.__and),
(OR , 2, opAssoc.LEFT , self.__or)])
其中self._and, self._or, self._not, self._parse_keyword, self._parse_expression
是修改已转换字符串的未来eval
的标记的方法。
你知道如何解决这个问题吗?
非常感谢
埃里克
答案 0 :(得分:2)
请参阅此修改后的解析器版本中的嵌入式注释:
from pyparsing import *
selkwds = "atomname atomindex atomtype thiol".split()
func_name = MatchFirst(map(CaselessKeyword, selkwds))
NOT,AND,OR = map(CaselessKeyword,"NOT AND OR".split())
keyword = func_name | NOT | AND | OR
func_call = Forward()
integer = Word(nums).setParseAction(lambda t: int(t[0]))
alphaword = Word(alphas,alphanums)
# you have to be specific about what kind of things can be an arg,
# otherwise, an argless function call might process the next
# keyword or boolean operator as an argument;
# this kind of lookahead is commonly overlooked by those who
# assume that the parser will try to do some kind of right-to-left
# backtracking in order to implicitly find a token that could be
# mistaken for the current repetition type; pyparsing is purely
# left-to-right, and only does lookahead if you explicitly tell it to
# I assume that a func_call could be a function argument, otherwise
# there is no point in defining it as a Forward
func_arg = ~keyword + (integer | func_call | alphaword)
# add Groups to give structure to your parsed data - otherwise everything
# just runs together - now every function call parses as exactly two elements:
# the keyword and a list of arguments (which may be an empty list, but will
# still be a list)
func_call << Group(func_name + Group(Optional(delimitedList(func_arg) | '*')))
# don't name this func_call, its confusing with what you've
# already defined above
func_call_expr = operatorPrecedence(func_call, [(NOT, 1, opAssoc.RIGHT),
(AND, 2, opAssoc.LEFT),
(OR , 2, opAssoc.LEFT)])
让我们测试一下:
tests = """\
atomname * and atomindex 1,2,3
atomname xxx,yyy or atomtype rrr,sss
thiol
not atomindex 1,2,3
not (atomindex 4,5,6) or atomname *""".splitlines()
for test in tests:
print test.strip()
print func_call_expr.parseString(test).asList()
print
打印:
atomname * and atomindex 1,2,3
[[['atomname', ['*']], 'AND', ['atomindex', [1, 2, 3]]]]
atomname xxx,yyy or atomtype rrr,sss
[[['atomname', ['xxx', 'yyy']], 'OR', ['atomtype', ['rrr', 'sss']]]]
thiol
[['thiol', []]]
not atomindex 1,2,3
[['NOT', ['atomindex', [1, 2, 3]]]]
not (atomindex 4,5,6) or atomname *
[[['NOT', ['atomindex', [4, 5, 6]]], 'OR', ['atomname', ['*']]]]