首页 > 解决方案 > 是否可以在插值的正则表达式中进行捕获?

问题描述

我想从现有的值列表中生成正则表达式,但是当我尝试在其中使用捕获时,匹配中不存在捕获。是否不可能使用插值进行捕获,还是我做错了什么?

my @keys = <foo bar baz>;
my $test-pattern = @keys.map({ "<$_>" }).join(' || ');

grammar Demo1 {
  token TOP {
    [
      || <foo>
      || <bar>
      || <baz>
    ] ** 1..* % \s+
  }

  token foo { 1 }
  token bar { 2 }
  token baz { 3 }
}

grammar Demo2 {
  token TOP {
    [ <$test-pattern> ] ** 1..* % \s+
  }

  token foo { 1 }
  token bar { 2 }
  token baz { 3 }
}

say $test-pattern, "\n" x 2, Demo1.parse('1 2 3'), "\n" x 2, Demo2.parse('1 2 3');
<foo> || <bar> || <baz>

「1 2 3」
 foo => 「1」
 bar => 「2」
 baz => 「3」

「1 2 3」

标签: regexrakustring-interpolation

解决方案


确定一个原子是否<...>可以毫不费力地捕获的规则是它是否以字母或下划线开头。

如果断言以字母或下划线开头,则需要/解析标识符,并使用该标识符作为封闭匹配对象中的键来捕获匹配项。例如,<foo::baz-bar qux>以字母开头并在 key 下捕获foo::baz-bar

如果断言不字母或下划线开头,则默认情况下它不会捕获。


要捕获第一个字符不是字母或下划线的断言的结果,您可以将其放在括号中或命名:

( <$test-pattern> ) ** 1..* % \s+

或者,命名断言:

<test-pattern=$test-pattern> ** 1..* % \s+

或(只是具有相同命名效果的另一种方式):

$<test-pattern>=<$test-pattern> ** 1..* % \s+

如果您所做的只是在括号中放置一个其他非捕获断言,那么您还没有为该断言打开捕获。相反,您只是将它包装在一个外部捕获中。断言保持非捕获,并且非捕获断言的任何子捕获数据被丢弃

<$test-pattern>因此,上面显示的第一个解决方案的输出(将断言包装在括号中)是:

「1 2 3」
 0 => 「1」
 0 => 「2」
 0 => 「3」

有时这就是您想要简化解析树和/或节省内存的原因。

相反,如果您使用上面显示的任何一种命名形式命名一个非捕获断言,那么通过这样做,您将其转换为捕获断言,这意味着将保留任何子捕获细节。因此,命名的解决方案产生:

「1 2 3」
 test-pattern => 「1」
  foo => 「1」
 test-pattern => 「2」
  bar => 「2」
 test-pattern => 「3」
  baz => 「3」

推荐阅读