使用pyparsing

时间:2016-10-04 04:35:34

标签: regex parsing grammar pyparsing

我正在尝试使用pyparsing编写一个简化的正则表达式解析器(仅支持*|运算符以及连接)。到目前为止,这是我的语法:

from pyparsing import alphas, Word, Forward

regular_expression = Forward()

character = Word(alphas, max=1) 
group     = '(' + regular_expression + ')'
star      = (character | group) + '*'

# A 'concat_expression' is any string concat of the above
# String concat with a 'concat_expression' also produces a 'concat_expression'
concat_expression = Forward()
concat_expression << ((character | group | star | concat_expression) +
                      (character | group | star))

or_expression = regular_expression + '|' + regular_expression

regular_expression << or_expression | concat_expression

当我尝试解析简单表达式(例如regular_expression.parseString("a"))时,我得到无限递归。我的连接定义有问题吗?

作为参考,我正在尝试调整this语法。

1 个答案:

答案 0 :(得分:1)

您目前正在查看的问题(无限递归)是由于语法中的左递归造成的。 pyparsing纯粹是从左到右的解析,没有前瞻(除非你在语法中明确地这样做)。因此,如果将列表定义为:

expr ::= expr | expr
然后它会旋转到污垢中。

我认为部分原因是你的解析器遍布整个地方,有很多冗余的递归术语。如果您可以停下来思考您正在解析的内容的定义,甚至将其捕获到一些简化的BNF中,它应该有助于澄清您的想法。

这是我得到的:

  • 一个表达式可以由片段组成,每个片段都是单个字符或里面的()表达式,可选地后跟一些重复指示符(&#39; *&#39;或&# 39;?&#39;或{}&#39; s中的整数等。)
  • 这些作品可以彼此相邻地运行
  • 这些作品可能与&#39; |&#39;分开。表明备选方案

或者更正式一点:

re_item     ::= (character | '(' re_expr ')') [repetition]
re_item_seq ::= re_item+
repetition  ::= '*' | '?'
re_expr     ::= re_item_seq [ '|' re_item_seq ]...

此解析器中没有左递归,因为re_expr只能在匹配左括号后递归。

翻译成pyparsing几乎是逐字的:

from pyparsing import (alphas, Word, Forward, oneOf, OneOrMore, Optional, 
    delimitedList, Group)

character = Word(alphas, exact=1).setName("character") # <- use 'exact', not 'max'

regular_expression = Forward()
group     = Group('(' + regular_expression + ')')
repetition = oneOf("* ?")

re_item = Group((character | group) + repetition) | character | group
re_item_seq = OneOrMore(re_item)

regular_expression <<= delimitedList(re_item_seq, '|')

测试一下:

regular_expression.runTests("""
    a
    a?
    sdlfj*(b|c)?
    """)

给出:

a
['a']


a?
[['a', '?']]
[0]:
  ['a', '?']


sdlfj*(b|c)?
['s', 'd', 'l', 'f', ['j', '*'], [['(', 'b', 'c', ')'], '?']]
[0]:
  s
[1]:
  d
[2]:
  l
[3]:
  f
[4]:
  ['j', '*']
[5]:
  [['(', 'b', 'c', ')'], '?']
  [0]:
    ['(', 'b', 'c', ')']
  [1]:
    ?

TL; DR - 读取&#34;左递归&#34;,您也可以访问此重新解析示例(将re反转为re可以匹配的候选字符串列表):{{3} }