如何在Perl6语法中匹配十六进制数组

时间:2019-06-03 06:08:21

标签: regex grammar perl6

我有一个像"39 3A 3B 9:;"这样的字符串,我想提取“ 39、3A,3B”

我尝试过

my $a = "39 3A 3B  9:;";
grammar Hex {
    token TOP { <hex_array>+ .* }
    token hex_array { <[0..9 A..F]> " " }
};
Hex.parse($a);

但这似乎不起作用。 甚至这似乎也不起作用。

my $a = "39 3A 3B ";
grammar Hex {
    token TOP { <hex_array>+ }
    token hex_array { <[0..9 A..F]> " " }
};
Hex.parse($a);

我确实尝试过语法:: Tracer TOP和hex_array都失败了

TOP
|  hex_array
|  * FAIL
* FAIL

2 个答案:

答案 0 :(得分:14)

P6正则表达式中的

<[abcdef...]>是匹配-一个-字符意义上的“字符类”。 1

获取所需内容的惯用方式是使用the ** quantifier

my $a = "39 3A 3B ";
grammar Hex {
  token TOP { <hex_array>+ }
  token hex_array { <[0..9 A..F]>**1..2 " " }
};
Hex.parse($a);

此答案的其余部分是关于“为什么”以及如何使用rule的“附加”材料。

您当然可以完全自由地匹配空白情况,只需将空白模式包含在任意单个令牌中,就像您在" "令牌中使用hex_array一样。

但是,在大多数情况下,最好在适当的时候使用rules代替。

首先,使用ws代替“”,\s*等。

让我们删除第二个token中的空格,然后将其移至第一个:

  token TOP { [ <hex_array> " " ]+ }
  token hex_array { <[0..9 A..F]>**1..2 }

我们添加了将[...]和一个空格组合在一起的方括号(hex_array),然后将+量词应用于该组合的原子。这是一个简单的更改,语法继续像以前一样工作,与以前一样匹配空格,只是现在hex_array标记不会捕获空格。

接下来,让我们切换到使用内置的ws token

  token TOP { [ <hex_array> <.ws> ]+ }

在理想的情况下,默认的<ws>\s*更有用。 2 如果默认的ws无法满足您的需求您可以指定自己的ws令牌。

我们使用<.ws>代替了<ws>,因为与\s*一样,<.ws>的使用避免了额外的空白捕获,而空白捕获可能只会使解析树变得混乱,并且浪费内存。

在将令牌串在一起的更高级别的解析规则中,几乎每个令牌之后,人们常常想要<.ws>之类的东西。但是,如果只是像这样明确地编写它,那将是高度重复的并且分散了<.ws>[ ... <.ws> ]的样板。为避免这种情况,有一个内置的隐式快捷方式,它表示为您插入样板的默认假设。此快捷方式是rule声明符,它依次使用:sigspace

使用rule(使用:sigspace

ruletoken完全相同,除了它在模式开始时打开:sigspace之外:

rule  {           <hex_array>+ }
token { :sigspace <hex_array>+ } # exactly the same thing

:sigspace(因此默认情况下在tokenregex中),模式中的所有文字空间(除非您引用它们)被忽略。通常,对于单个 token 的可读模式,这是理想的,因为它们通常指定要匹配的文字内容。

但是:sigspace生效后,原子的空间变得“重要”,因为它们被隐式转换为<.ws>[ ... <.ws> ]调用。这对于指定标记或子规则序列的可读模式是理想的,因为它是避免所有这些额外调用混乱的自然方法。

下面的第一个模式将匹配一个或多个hex_array令牌,它们之间或末尾没有空格匹配。最后两个将匹配一个或多个hex_array,且中间没有空格,然后在最后端带有或不带有空格:

  token TOP {           <hex_array>+ }
  #          ^ ignored ^            ^ ignored

  token TOP { :sigspace <hex_array>+ }
  #          ^ ignored ^            ^ significant

  rule TOP  {           <hex_array>+ }
  #          ^ ignored ^            ^ significant

NB 。副词(例如:sigspace)不是原子。第一个 atom 之前之前的空格(在上面, <hex_array>之前的空格)从不有效(无论:sigspace是否有效)。但是此后,如果:sigspace有效,则模式中所有未引用的空格都是“有效的”,也就是说,它将转换为<.ws>[ ... <.ws> ]

在上面的代码中,第二个标记和规则将单个 hex_array与之后的空格匹配,因为+之后和{{ 1}}表示该模式被重写为:

}

但是如果您输入的多个 token TOP { <hex_array>+ <.ws> } 令牌之间有一个或多个空格 ,则此重写的令牌将不匹配 。相反,您可能要写:

hex_array

它被重写为:

  rule TOP { <hex_array> + }
  # ignored ^           ^ ^ both these spaces are significant

这将与您的输入匹配。

结论

因此,在经过所有这些看似复杂的操作之后,实际上这只是我的详尽描述,我建议您将原始代码编写为:

  token TOP { [ <hex_array> <.ws> ]+ <.ws> }

这将比您的原始版本更灵活(我认为这将是一件好事,尽管对于某些用例当然不会),并且对于大多数P6er来说可能更容易阅读。

最后,要加强如何避免my $a = "39 3A 3B "; grammar Hex { rule TOP { <hex_array> + } token hex_array { <[0..9 A..F]>**1..2 } }; Hex.parse($a); 的三个杂物中的两个,请参阅What's the best way to be lax on whitespace in a perl6 grammar?。 (第三个问题是,是否需要在原子和量词之间放置一个空格,就像上面的rule<hex_array>之间的空格一样。)

脚语

1 如果要匹配多个字符,则将适当的量词附加到字符类。这是将事物变成the assumed behavior of a "character class" according to Wikipedia的明智方式。不幸的是,P6文档当前使该问题感到困惑,例如将真正的字符类和与多个字符匹配的其他规则(在标题Predefined character classes下合并在一起)。

2 默认的+规则旨在匹配单词之间的 ,其中“单词”是字母的连续序列(Unicode类别L),数字(Nd)或下划线。在代码中,它指定为:

ws

regex ws { <!ww> \s* } 是“字内”测试。因此ww的意思是“单词”内的 not <!ww>总是会在<ws>会成功的地方取得成功,但与\s*不同的是,它不会在一个单词的中间成功。 (就像其他用\s*量化的原子一样,简单的*总是匹配的,因为它匹配任意数量的空格,包括根本没有。)

答案 1 :(得分:5)

如果您不需要使用语法,则可以执行以下操作:

my $a = "39 3A 3B  9:;";
say $a.split(/\s+/).grep: * ~~ /<< <[0..9 A..F]> ** 2 >>/;

正则表达式将匹配这些两位数的六进制字符串。无论如何,您的语法问题可能在于您使用的空格数。在这种意义上,他们非常严格。