【问题标题】:Building a regex with sub in Perl 6在 Perl 6 中使用 sub 构建正则表达式
【发布时间】:2017-11-10 20:35:23
【问题描述】:

在学习了how to pass regexes as arguments 之后,我尝试使用sub 构建我的第一个正则表达式,但我再次陷入困境。很抱歉下面的复杂规则,我已尽力简化它们。我至少需要一些线索来解决这个问题。

regex 应该由交替组成,每个交替由 leftmiddleright 组成,其中 leftright 应该成对出现,middle 的变体取决于选择了right

Pairs 数组包含一对 leftright

my Pair @leftright =
  A => 'a',
  ...
  Z => 'z',
  ;

Middle 变体从哈希中读取:

my Regex %middle = 
  z => / foo /,
  a => / bar /,
  m => / twi /,
  r => / bin /,
  ...
  ;

如果rightz%middle<a>,则应选择%middle<z> — 如果righta 等。

所以,生成的正则表达式应该是

my token word {
    | A <%middle[a]> a
    | Z <%middle[z]> z
    | ...
}

或者,更一般地

my token word {
    | <left=@leftright[0].key> 
      <middle=%middle{@leftright[0].value}> 
      <right=@leftright[0].value> 
    | (the same for index == 1)
    | (the same for index == 2)
    | (the same for index == 3)
 ...
}

它应该匹配AbaraZfooz

如何构建token word(可以在例如grammar中使用)和sub,它将从@leftright中获取每一对,根据right的值放置合适的%middle{}然后将它们合并为一个regex?

my Regex sub sub_word(Pair @l_r, Regex %m) {
...
}
my token word {
    <{sub_word(@leftright, %middle)}> 
}

比赛结束后我需要知道leftmiddleright的值:

"Abara" ~~ &word;
say join '|', $<left>, $<middle>, $<right> # A|bar|a

【问题讨论】:

  • 听起来你在说什么,大意是,给定一个权利是X,中间是/ waldo /。但是您还没有说应该在Xwaldo 之间检测到什么程序关系。 (当然,您写了z 而不是X/ zfoo / 而不是/ waldo /,但这没有区别,除非您的意思是/ zfoo / 中的z 不仅是为了帮助人类理解,而且也是被程序检测到。在这种情况下,不,我认为你不能这样做——我认为你的程序不能内省地知道/ zfoo / 模式包含z。)
  • 应该构建令牌word 的子组件的输入参数是什么?应该在正则表达式解析开始构建预定义的token 之前使用 sub,还是应该在正则表达式解析器中使用它?能举个简单的例子吗?
  • @raiph 谢谢这是我的错(我考虑过),我并不是要反省regex。我将重新表述这部分,制作一个hash
  • @HåkonHægland 输入参数应该是@leftright%middle,并且应该在解析开始之前构建模式。如果只有一个变体,则模式将类似于&lt;$left&gt; &lt;$middle&gt; &lt;$right&gt;。这里应该是&lt;@leftright[0].key&gt; &lt;%middle{@leftright[0].value}&gt; &lt;@leftright[0].value&gt; | (the same for index == 1 | then 2, 3 etc)... 所以问题是我例如不知道如何在循环中连接 regexes 和交替 |
  • 我已经修改了问题的文本。希望,现在好多了。

标签: raku


【解决方案1】:

我还不能使用token 做到这一点,但这里有一个使用EVALRegex 的解决方案(而且我使用%middle 作为Str 的哈希值而不是Regex):

my Regex sub build_pattern (%middle, @leftrigth) {
    my $str = join '|', @leftright.map(
        {join ' ',"\$<left>='{$_.key}'", "\$<middle>='{%middle{$_.value}}'", "\$<right>='{$_.value}'"});
    );
    my Regex $regex = "rx/$str/".EVAL;

    return $regex;
}

my Regex $pat = build_pattern(%middle, @leftright);

say $pat;
my $res = "Abara" ~~ $pat;
say $res;

输出

rx/$<left>='A' $<middle>='bar' $<right>='a'|$<left>='Z' $<middle>='foo' $<right>='z'/
「Abara」
 left => 「A」
 middle => 「bar」
 right => 「a」

有关我为什么选择使用EVAL 的更多信息,请参阅How can I interpolate a variable into a Perl 6 regex?

【讨论】:

  • 谢谢!可能,这将是最有效的方法,但就我而言,我需要有可能分析$/。例如。我需要知道哪个middle 匹配。我会在Q中添加这个,抱歉我没有事先写。
  • 那么也许在中间部分放置一个捕获组?像这样:我的 $pat = rx/'A' ('bar') 'a'|'Z' ('foo') 'z'/。然后$0将包含成功匹配后的中间部分..
  • 是否可以对命名(非位置)捕获做同样的事情?这对我来说是一个非常好的解决方案。
  • @EugeneBarsky 是的,这是个好主意!请参阅我的更新答案以获取建议
  • @EugeneBarsky 我们需要转义$,因为它在双引号内,而且我们不希望它被插值。即,文字 $ 必须在第一个 join 调用中存活下来。对于另一个问题:单引号存在,因为我们没有在正则表达式中使用 sigspace,因此所有文字字符串都应该用引号引起来。如果没有,我们会在编译正则表达式时收到警告(这里是:当使用EVAL 时)。
猜你喜欢
  • 1970-01-01
  • 2018-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多