我有这个正则表达式:
regex = %r{\A(?<foo> a\g<foo>a | b\g<foo>b | c)\Z}x
当我对几个字符串进行测试时,它似乎与上下文无关的语法一样强大,因为它正确地处理递归.
regex.match("aaacaaa") # => #<MatchData "aaacaaa" foo:"aaacaaa"> regex.match("aacaa") # => #<MatchData "aacaa" foo:"aacaa"> regex.match("aabcbaa") # => #<MatchData "aabcbaa" foo:"aabcbaa"> regex.match("aaacaa") # => nil
“Fun with Ruby 1.9 Regular Expressions”有一个例子,他实际上安排正则表达式的所有部分,使其看起来像一个无上下文的语法如下:
sentence = %r{ (?<subject> cat | dog | gerbil ){0} (?<verb> eats | drinks| generates ){0} (?<object> water | bones | PDFs ){0} (?<adjective> big | small | smelly ){0} (?<opt_adj> (\g<adjective>\s)? ){0} The\s\g<opt_adj>\g<subject>\s\g<verb>\s\g<opt_adj>\g<object> }x
在重新布置正则表达式的部分的技术之间,以及我的递归命名捕获组的例子,这是否意味着Ruby 1.9正则表达式具有相当于上下文无关语法的权力?
解决方法
这是Ruby 1.9中使用的Oniguruma regexp引擎的令人敬畏的事情之一 – 它具有解析器的强大功能,并不限于识别常规语言.它有正面和负面的前瞻/ lookbehind,甚至可以用来识别一些不上下文的语言!以下列为例:
regexp = /\A(?<AB>a\g<AB>b|){0}(?=\g<AB>c)a*(?<BC>b\g<BC>c|){1}\Z/
这个正则表达式识别“abc”,“aabbcc”,“aaabbbccc”等字符串 – “a”,“b”和“c”的数字必须相等,否则将不匹配.
(一个限制:您不能在lookahead和lookbehind中使用命名组.)
虽然我没有在引擎盖下偷看,Oniguruma似乎通过简单的递归下降来处理命名组,当某事不匹配时备份.我已经观察到它不能处理左递归.例如:
irb(main):013:0> regexp = /(?<A>\g<A>a|)/ SyntaxError: (irb):13: never ending recursion: /(?<A>\g<A>a|)/ from C:/Ruby192/bin/irb:12:in `<main>'
我不清楚我的解析理论,但我认为这样一个非确定性的自上而下的解析器应该能够解析任何无上下文的语言. (“语言”,而不是“语法”;如果您的语法已经递归,则必须将其转换为正确的递归).如果不正确,请编辑此帖子.