【问题标题】:Pyparsing - Rule AmbiguityPyparsing - 规则歧义
【发布时间】:2015-06-20 20:39:33
【问题描述】:

我正在编写一个 Pyparsing 语法来将 Creole markup 转换为 HTML。我被卡住了,因为尝试解析这两个结构时存在一些冲突:

图片链接:{{image.jpg|title}}
忽略格式:{{{text}}}

我解析图片链接的方式如下(注意这个转换得很好):

def parse_image(s, l, t):
    try:
        link, title = t[0].split("|")
    except ValueError:
        raise ParseFatalException(s,l,"invalid image link reference: " + t[0])
    return '<img src="{0}" alt="{1}" />'.format(link, title)

image = QuotedString("{{", endQuoteChar="}}")
image.setParseAction(parse_image)

接下来,我写了一条规则,这样当遇到 {{{text}}} 时,只需返回左大括号和右大括号之间的内容而不进行格式化:

n = QuotedString("{{{", endQuoteChar="}}}")
n.setParseAction(lambda x: x[0])

但是,当我尝试运行以下测试用例时:

text = italic | bold | hr | newline | image | n
print text.transformString("{{{ //ignore formatting// }}}")

我得到以下堆栈跟踪:

Traceback (most recent call last):
File "C:\Users\User\py\kreyol\parser.py", line 36, in <module>
print text.transformString("{{{ //ignore formatting// }}}")
File "C:\Python27\lib\site-packages\pyparsing.py", line 1210, in transformString
raise exc
pyparsing.ParseFatalException: invalid image link reference: { //ignore formatting//  (at char 0), (line:1, col:1)

据我了解,解析器首先遇到 {{ 并尝试将文本解析为图像而不是没有格式化的文本。我该如何解决这种歧义?

【问题讨论】:

  • 我没有使用 pyparsing,但快速浏览一下建议 Regex("{{[^{].*}}" 应该可以工作(对于图像......即定义图像是两个 {' s 后跟 { 以外的任何内容,后跟两个 }} 的任何内容)

标签: python parsing markup pyparsing creole


【解决方案1】:

问题在于这个表达式:

text = italic | bold | hr | newline | image | n

Pyparsing 严格从左到右工作,没有前瞻。使用“|”运算符,您构造一个 pyparsing MatchFirst 表达式,它将匹配所有备选方案的 first 匹配项,即使后面的匹配项更好。

您可以改为使用“^”运算符将评估更改为使用“最长匹配”:

text = italic ^ bold ^ hr ^ newline ^ image ^ n

这将有性能损失,因为每个表达式都经过测试,即使没有更好匹配的可能性。

更简单的解决方案是重新排序备选列表中的表达式:在image 之前测试n

text = italic | bold | hr | newline | n | image

现在在评估备选方案时,它将在 image 的前导 {{ 之前查找 n 的前导 {{{

当人们定义数字术语时经常会出现这种情况,并且不小心定义了以下内容:

integer = Word(nums)
realnumber = Combine(Word(nums) + '.' + Word(nums))
number = integer | realnumber

在这种情况下,number 永远不会匹配realnumber,因为前导整数部分将被解析为整数。与您的情况一样,解决方法是使用“^”运算符,或者只是重新排序:

number = realnumber | integer

【讨论】:

  • 非常感谢!如果我根据最长匹配进行评估,转换大型文档需要很长时间吗?或者对于像标记转换器这样微不足道的东西,性能损失可以忽略不计?
  • Wiki 标记是非常困难的解析器。它们必须处理标记属性的嵌套,通常有重载的符号,有时对缩进敏感(尤其难以用 pyparsing 实现)。例如,我看到 Creole 使用 '**' 表示粗体 用于项目符号列表中的子项 - 解决这些问题可能很棘手。
  • 我不会试图预测性能会如何——它太依赖于你的解析器的其余部分,也依赖于 wiki 输入。现在您已经了解了这些选项,您可以自己尝试一下。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多