我尝试用pyparsing解析一种简单的模板语言,它看起来像这样:
if <expr>: <statements>
elif <expr>:
if <expr>:
<statements>
else:
<statements>
<statements>
其中语句要么是具有有效Python代码的if块,要么是其他任何文本。如果一个字符串以“If”关键字开头,这意味着它马上就是一个If块。实际上,文本行也可以包含{variable}
类型的插值,但仅此而已。在
此语法的完整有效示例:
^{pr2}$为了检测Python表达式,我将pyparsing.Token
子类化,它似乎可以工作:
import ast
from pyparsing import *
class PythonExpression(Token):
name = 'PythonExpression'
def parseImpl(self, s, loc, doActions=True):
max_loc = s.find('\n') if '\n' in s else len(s)
best_loc = None
for n in range(loc + 1, max_loc + 1):
try:
tree = ast.parse(s[loc:n])
except:
continue
if isinstance(tree, ast.Module):
if len(tree.body) is 1:
if isinstance(tree.body[0], ast.Expr):
best_loc = n
if best_loc is not None:
return best_loc, s[loc:best_loc]
raise ParseException(s, loc, 'invalid Python expression')
expr = 'if foo[1 : "bar:baz"] == 1 : passqwe'
print (Keyword('if') + PythonExpression()).parseString(expr).asList()
结果
['if', 'foo[1 : "bar:baz"] == 1 ']
但是,我对pyparsing.indentedBlock
的用法有点迷茫,似乎无法使它解析整个语法。我的最后一次尝试是这样(注意,它只包含if
语句实现;还有可选的elif
和else
块):
ParserElement.setDefaultWhitespaceChars(' \t')
colon = Literal(':').suppress()
if_clause = PythonExpression() + colon
if_statement = Group(Keyword('if') + if_clause)
non_white = Regex(r'\S+')
anything = Combine(non_white + restOfLine)
statement = Forward()
indent_stack = [1]
if_block = Group(if_statement + ((anything) | indentedBlock(statement, indent_stack)))
other = ~Keyword('if') + anything
statement << (if_block | other)
parser = OneOrMore(indentedBlock(statement, indent_stack, False))
data = """\
if foo[1:2]:
bar
baz
if foo[3]:
bar : baz
if foo[4]: bar
baz
foo
"""
pprint.pprint(parser.parseString(data).asList())
它开始正确解析,但随后停止:
[[[[['if', 'foo[1:2]'], [['bar'], ['baz']]]]]]
我也尝试过显式地将+ lineEnd.suppress()
添加到anything
中,但似乎没有什么帮助。我肯定我在这里做了些蠢事,可能和新词有关,但我真的搞不清楚。在
顺便说一句,在上面的例子中,我如何检测anything
中的插值模式(如果上面的例子可以工作的话),以便foo {bar} baz
被解析为一个['foo',Var('bar'),'baz']?检测{var}
很容易,但是对于纯文本来说,什么是正确的表达式呢?如果没有足够的贪婪来消耗所有的内容,并且不会扰乱if/elif/else逻辑(我尝试使用SkipTo
,但这变得相当麻烦)?在
编辑:添加用于解析插值的单独语法,示例如下:
class Substitution(object):
def __init__(self, s, l, t):
self.name = t[0]
def __repr__(self):
return 'Substitution(%r)' % self.name
ParserElement.setDefaultWhitespaceChars(' \t')
lbrace = Literal('{').suppress()
rbrace = Literal('}').suppress()
name = Word(alphas, alphanums + '_')
substitution = Combine(lbrace + name + rbrace).setParseAction(Substitution)
text = SkipTo(substitution | lineEnd.suppress(), include=True).leaveWhitespace()
parser = OneOrMore(text | substitution)
parser.parseString('hello \n {world} {invalid.sub} \n foo {bar} baz ').asList()
输出
['hello ',
[' ', Substitution('world')],
' {invalid.sub} ',
[' foo ', Substitution('bar')],
' baz ']
如您所见,在本例中,解析器在将行分组到一起时并不十分正确(它不允许text
跟在substitution
后面),但它显示了这一点。这些Substitution
对象随后将在运行时进行处理。在
目前没有回答
相关问题 更多 >
编程相关推荐