优化ruby regexp - 许多匹配组

时间:2012-11-19 12:26:35

标签: ruby regex optimization pattern-matching lexer

我正在研究一个红宝石垒手词法分析器。为了提高性能,我将所有令牌的正则表达式加入了一个具有匹配组名称的大正则表达式中。生成的正则表达式如下:

/\A(?<__anonymous_-1038694222803470993>(?-mix:\n+))|\A(?<__anonymous_-1394418499721420065>(?-mix:\/\/[\A\n]*))|\A(?<__anonymous_3077187815313752157>(?-mix:include\s+"[\A"]+"))|\A(?<LET>(?-mix:let\s))|\A(?<IN>(?-mix:in\s))|\A(?<CLASS>(?-mix:class\s))|\A(?<DEF>(?-mix:def\s))|\A(?<DEFM>(?-mix:defm\s))|\A(?<MULTICLASS>(?-mix:multiclass\s))|\A(?<FUNCNAME>(?-mix:![a-zA-Z_][a-zA-Z0-9_]*))|\A(?<ID>(?-mix:[a-zA-Z_][a-zA-Z0-9_]*))|\A(?<STRING>(?-mix:"[\A"]*"))|\A(?<NUMBER>(?-mix:[0-9]+))/

我将它与我的字符串匹配,产生一个MatchData,其中只解析了一个令牌:

bigregex =~ "\n ... garbage"
puts $~.inspect

哪个输出

#<MatchData
 "\n"
 __anonymous_-1038694222803470993:"\n"
 __anonymous_-1394418499721420065:nil
 __anonymous_3077187815313752157:nil
 LET:nil
 IN:nil
 CLASS:nil
 DEF:nil
 DEFM:nil
 MULTICLASS:nil
 FUNCNAME:nil
 ID:nil
 STRING:nil
 NUMBER:nil>

因此,正则表达式实际上匹配了“\ n”部分。现在,我需要计算它所属的匹配组(从#inspect输出中可以清楚地看到它是_ 匿名 -1038694222803470993,但我需要以编程方式获取它。)

除了遍历#names之外,我找不到任何其他选项:

m.names.each do |n|
  if m[n]
    type = n.to_sym
    resolved_type = (n.start_with?('__anonymous_') ? nil : type)
    val = m[n]
    break
  end
end

验证匹配组是否匹配。

这里的问题是它很慢(我花了大约10%的时间在循环中;还有8%抓住@input[@pos..-1]以确保 \ A 按预期匹配字符串的开头(我不丢弃输入,只是将@pos移到其中)。

您可以在GH repo查看完整代码。

关于如何使它至少快一点的任何想法?有没有选择让“成功”匹配组更容易?

2 个答案:

答案 0 :(得分:1)

我可能完全误解了这一点,但是我假设除了一个令牌之外的所有令牌都不是nil而且那是你之后的那个?

如果是这样,那么根据您正在使用的正则表达式的风格,您可以使用否定前瞻来检查非nil

([^\n:]+:(?!nil)[^\n\>]+)

这将匹配整个令牌,即NAME:value

答案 1 :(得分:1)

您可以使用正则表达式方法.captures().names()执行此操作:

matching_string = "\n ...garbage"   # or whatever this really is in your code
@input = matching_string.match bigregex   # bigregex = your regex
arr = @input.captures

arr.each_with_index do |value, index|     
  if not value.nil?
    the_name_you_want = @input.names[index]
  end
end

或者,如果您期望多个成功的值,您可以这样做:

success_names_arr = []
success_names_arr.push(@input.names[index]) #within the above loop

与您原来的想法非常相似,但如果您正在寻找效率.captures()方法应该有所帮助。