0

我正在研究一个 ruby​​ 基础词法分析器。为了提高性能,我将所有标记的正则表达式加入到一个带有匹配组名称的大正则表达式中。生成的正则表达式如下所示:

/\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查看完整代码。

关于如何使它至少更快一点的任何想法?是否有任何选项可以更轻松地计算“成功”匹配组?

4

2 回答 2

1

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

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

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

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

于 2012-11-19T13:00:29.870 回答
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()方法应该会有所帮助。

于 2012-11-19T13:59:26.857 回答