编程语言
首页 > 编程语言> > python-如何在PyParsing中验证动态定义的语法元素

python-如何在PyParsing中验证动态定义的语法元素

作者:互联网

我正在使用PyParsing实现一个相当复杂的语法的解析器. (如果可以的话,使用起来确实很愉快!)

语法有点“动态”,可以定义(各种)字母,进而定义其他定义中允许的元素.举个例子:

alphabet: a b c
lists:
s1 = a b
s2 = b c x

在此,字母意在定义列表定义中允许的元素.例如s1是有效的,但s2包含无效的x.

没有这种验证的简单PyParsing解析器可能看起来像这样:

from pyparsing import Literal, lineEnd, Word, alphanums,\
    OneOrMore, Group, Suppress, dictOf

def fixedToken(literal):
    return Suppress(Literal(literal))

Element = Word(alphanums)

Alphabet = Group(OneOrMore(~lineEnd + Element))
AlphaDef = fixedToken("alphabet:") + Alphabet

ListLine = OneOrMore(~lineEnd + Element)
Lists = dictOf(Word(alphanums) + fixedToken("="), ListLine)

Start = AlphaDef + fixedToken("lists:") + Lists

if __name__ == "__main__":

    data = """
    alphabet: a b c
    lists:
    s1 = a b
    s2 = b c x
    """

    res = Start.parseString(data)
    for k, v in sorted(res.items()):
        print k, "=", v

这将解析&给出输出:

Alphabet= set(['a', 'c', 'b'])
s1 = ['a', 'b']
s2 = ['b', 'c', 'x']

但是,我希望解析器为s2引发ParseException(或类似的),因为它包含无效的x.理想情况下,我希望能够对ListLine进行定义,使其类似于以下内容:OneOrMore(oneOf(Alphabet))-但显然,这需要一些动态解释,只有在实际解析了Alphabet&之后,才能进行动态解释.组装好的.

我发现的一种解决方案是将解析动作添加到1.记住字母并2.验证行:

# ...
Alphabet = Group(OneOrMore(~lineEnd + Element))
def alphaHold(toks):
    alphaHold.alpha = set(*toks)
    print "Alphabet=", alphaHold.alpha
Alphabet.addParseAction(alphaHold)

AlphaDef = fixedToken("alphabet:") + Alphabet

ListLine = OneOrMore(~lineEnd + Element)
def lineValidate(toks):
    unknown = set(toks).difference(alphaHold.alpha)
    if len(unknown):
        msg= "Unknown element(s): {}".format(unknown)
        print msg
        raise ParseException(msg)
ListLine.addParseAction(lineValidate)
# ...

这几乎提供了所需的输出:

Alphabet= set(['a', 'c', 'b'])
Unknown element(s): set(['x'])
s1 = ['a', 'b']

但是不幸的是,PyParsing捕获了解析操作抛出的异常,因此这种方法在技术上是失败的.我可能错过了在PyParsing中实现此目标的另一种方法吗?

解决方法:

您已经接近完成这项工作.在许多情况下,pyparsing解析器会根据先前解析的文本动态调整自身.诀窍是使用Forward占位符表达式,然后将所需的值插入到占位符中作为解析操作的一部分(非常接近您现在拥有的值).像这样:

Element = Forward()

Alphabet = OneOrMore(~lineEnd + oneOf(list(alphas)))
def alphaHold(toks):
    Element << oneOf(toks.asList())
Alphabet.setParseAction(alphaHold)

从这里开始,我认为您的其余代码按原样运行良好.实际上,您甚至不需要行验证功能,因为pyparsing仅会使用此方法将有效的元素名称匹配为元素.

您可能会发现pyparsing的错误报告有些模糊.在某些明智的地方,使用’-‘代替”可以使事情变得更好.由于pyparsing使用ParseExceptions进行表达式匹配/不匹配的所有内部信号传递,因此当您进入已定义的表达式,但是包含的表达式具有无效匹配时,它不会自动识别.您可以告诉pyparsing使用’-‘运算符来检测到它,如下所示:

ListDef = listName + '=' - OneOrMore(~lineEnd + Element)

pyparsing获得名称和’=’符号后,发现的任何无效Element都会立即引发ParseSyntaxException,这将在该点停止pyparsing对文本的扫描,并在无效元素的位置报告异常.

标签:pyparsing,python
来源: https://codeday.me/bug/20191123/2063937.html