如何匹配 perl6 语法中的十六进制数组
How do I match a hex array in perl6 grammar
我有一个像 "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);
我试过 Grammar::Tracer TOP 和 hex_array 都失败了
TOP
| hex_array
| * FAIL
* FAIL
如果不需要使用语法,可以这样做:
my $a = "39 3A 3B 9:;";
say $a.split(/\s+/).grep: * ~~ /<< <[0..9 A..F]> ** 2 >>/;
正则表达式将匹配这些 2 位六进制字符串。无论如何,您的语法问题可能在于您使用的空格数;他们在这个意义上非常严格。
<[abcdef...]>
在 P6 正则表达式中是 "character class" 在 match-one-character sense.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);
此答案的其余部分是 "bonus" material 关于为什么以及如何使用 rule
s。
您当然可以完全自由地匹配白色space 情况,方法是在任意单个标记中包含白色space 模式,就像您在 hex_array
中使用 " "
所做的那样令牌。
但是,在适当的时候使用 rule
s 是个好习惯——大多数时候都是这样。
首先,使用ws
代替“”、\s*
等
让我们删除第二个 token
中的 space 并将其移至第一个:
token TOP { [ <hex_array> " " ]+ }
token hex_array { <[0..9 A..F]>**1..2 }
我们添加了组合 hex_array
和 space 的方括号 ([...]
),然后将 +
量词应用于该组合原子。这是一个简单的更改,语法继续像以前一样工作,像以前一样匹配 space,只是现在 space 不会被 hex_array
标记捕获。
接下来,让我们切换到使用内置 ws
token
:
token TOP { [ <hex_array> <.ws> ]+ }
默认值 <ws>
比 \s*
更有用,以理想的方式。2 如果默认值 ws
没有不执行您需要的操作,您可以指定自己的 ws
令牌。
我们使用 <.ws>
而不是 <ws>
,因为与 \s*
一样,使用 <.ws>
可以避免额外捕获白色 space只会弄乱解析树并浪费内存。
在将标记串在一起的更高级别解析规则中的几乎每个标记之后,人们通常需要类似 <.ws>
的东西。但如果它只是明确地写成那样,它将是高度重复和分散注意力的 <.ws>
和 [ ... <.ws> ]
样板文件。为避免 隐含 的内置快捷方式表示为您插入样板文件的默认假设。此快捷方式是 rule
声明符,它又使用 :sigspace
.
使用 rule
(使用 :sigspace
)
A rule
完全 与 token
相同,只是它在模式开始时打开 :sigspace
:
rule { <hex_array>+ }
token { :sigspace <hex_array>+ } # exactly the same thing
没有 :sigspace
(所以默认在token
s和regex
s),all 模式中的 space 文字(除非您引用它们)被 忽略 。这对于个人 token
s 的可读模式通常是可取的,因为它们通常指定要匹配的文字内容。
但是一旦:sigspace
生效,spaces after atoms become "significant" -- because they're implicitly converted to <.ws>
或 [ ... <.ws> ]
来电。这对于指定标记或子规则序列的可读模式是可取的,因为这是避免所有这些额外调用混乱的自然方式。
下面的第一个模式将匹配一个或多个 hex_array
标记,在它们之间或末尾没有匹配 space。最后两个将匹配一个或多个 hex_array
s,没有干预 spaces,然后 with or without spaces 在最后:
token TOP { <hex_array>+ }
# ^ ignored ^ ^ ignored
token TOP { :sigspace <hex_array>+ }
# ^ ignored ^ ^ significant
rule TOP { <hex_array>+ }
# ^ ignored ^ ^ significant
NB. 副词(如 :sigspace
)不是原子。 before 第一个 atom(在上面,spaces before <hex_array>
) 从不 显着(无论 :sigspace
是否有效)。但此后,如果 :sigspace
有效,则模式中的所有 non-quoted 间距都是 "significant" -- 也就是说,它被转换为 <.ws>
或 [ ... <.ws> ]
。
在上面的代码中,第二个标记和规则将匹配 单个 hex_array
和后面的 space,因为 space 紧跟在 +
之后和 }
之前意味着模式被重写为:
token TOP { <hex_array>+ <.ws> }
但是,如果您的输入有 多个 hex_array
个带有一个或多个 spaces 在他们之间。相反,你会想写:
rule TOP { <hex_array> + }
# ignored ^ ^ ^ both these spaces are significant
重写为:
token TOP { [ <hex_array> <.ws> ]+ <.ws> }
这将匹配您的输入。
结论
所以,在所有这些表面上的复杂性之后,我建议您可以将原始代码编写为:
my $a = "39 3A 3B ";
grammar Hex {
rule TOP { <hex_array> + }
token hex_array { <[0..9 A..F]>**1..2 }
};
Hex.parse($a);
这将比您原来的匹配更灵活(我认为这将是一件好事,当然它可能不适用于某些用例)并且对于大多数 P6 用户来说可能更容易阅读。
最后,为了加强如何避免 rule
的三个陷阱中的两个,另请参阅 。 (第三个问题是你是否需要在原子和量词之间放置一个 space,就像上面 <hex_array>
和 +
之间的 space 一样。)
脚注
1如果要匹配多个字符,则在字符class后附加一个合适的量词。这是明智的做法,the assumed behavior of a "character class" according to Wikipedia. Unfortunately the P6 doc currently confuses the issue, eg lumping together both genuine character classes and other rules that match multiple characters under the heading Predefined character classes.
2默认 ws
规则旨在匹配 单词 ,其中 "word" 是字母(Unicode 类别 L)、数字 (Nd) 或下划线的连续序列.在代码中,它指定为:
regex ws { <!ww> \s* }
ww
是一个 "within word" 测试。所以 <!ww>
表示 而不是 在 "word" 中。 <ws>
总是会在 \s*
会成功的地方成功 -- 除了与 \s*
不同的是,它 不会 在单词中间成功。 (就像用 *
量化的任何其他原子一样,普通的 \s*
将始终匹配,因为它匹配任意数量的 space,包括 none .)
我有一个像 "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);
我试过 Grammar::Tracer TOP 和 hex_array 都失败了
TOP
| hex_array
| * FAIL
* FAIL
如果不需要使用语法,可以这样做:
my $a = "39 3A 3B 9:;";
say $a.split(/\s+/).grep: * ~~ /<< <[0..9 A..F]> ** 2 >>/;
正则表达式将匹配这些 2 位六进制字符串。无论如何,您的语法问题可能在于您使用的空格数;他们在这个意义上非常严格。
<[abcdef...]>
在 P6 正则表达式中是 "character class" 在 match-one-character sense.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);
此答案的其余部分是 "bonus" material 关于为什么以及如何使用 rule
s。
您当然可以完全自由地匹配白色space 情况,方法是在任意单个标记中包含白色space 模式,就像您在 hex_array
中使用 " "
所做的那样令牌。
但是,在适当的时候使用 rule
s 是个好习惯——大多数时候都是这样。
首先,使用ws
代替“”、\s*
等
让我们删除第二个 token
中的 space 并将其移至第一个:
token TOP { [ <hex_array> " " ]+ }
token hex_array { <[0..9 A..F]>**1..2 }
我们添加了组合 hex_array
和 space 的方括号 ([...]
),然后将 +
量词应用于该组合原子。这是一个简单的更改,语法继续像以前一样工作,像以前一样匹配 space,只是现在 space 不会被 hex_array
标记捕获。
接下来,让我们切换到使用内置 ws
token
:
token TOP { [ <hex_array> <.ws> ]+ }
默认值 <ws>
比 \s*
更有用,以理想的方式。2 如果默认值 ws
没有不执行您需要的操作,您可以指定自己的 ws
令牌。
我们使用 <.ws>
而不是 <ws>
,因为与 \s*
一样,使用 <.ws>
可以避免额外捕获白色 space只会弄乱解析树并浪费内存。
在将标记串在一起的更高级别解析规则中的几乎每个标记之后,人们通常需要类似 <.ws>
的东西。但如果它只是明确地写成那样,它将是高度重复和分散注意力的 <.ws>
和 [ ... <.ws> ]
样板文件。为避免 隐含 的内置快捷方式表示为您插入样板文件的默认假设。此快捷方式是 rule
声明符,它又使用 :sigspace
.
使用 rule
(使用 :sigspace
)
A rule
完全 与 token
相同,只是它在模式开始时打开 :sigspace
:
rule { <hex_array>+ }
token { :sigspace <hex_array>+ } # exactly the same thing
没有 :sigspace
(所以默认在token
s和regex
s),all 模式中的 space 文字(除非您引用它们)被 忽略 。这对于个人 token
s 的可读模式通常是可取的,因为它们通常指定要匹配的文字内容。
但是一旦:sigspace
生效,spaces after atoms become "significant" -- because they're implicitly converted to <.ws>
或 [ ... <.ws> ]
来电。这对于指定标记或子规则序列的可读模式是可取的,因为这是避免所有这些额外调用混乱的自然方式。
下面的第一个模式将匹配一个或多个 hex_array
标记,在它们之间或末尾没有匹配 space。最后两个将匹配一个或多个 hex_array
s,没有干预 spaces,然后 with or without spaces 在最后:
token TOP { <hex_array>+ }
# ^ ignored ^ ^ ignored
token TOP { :sigspace <hex_array>+ }
# ^ ignored ^ ^ significant
rule TOP { <hex_array>+ }
# ^ ignored ^ ^ significant
NB. 副词(如 :sigspace
)不是原子。 before 第一个 atom(在上面,spaces before <hex_array>
) 从不 显着(无论 :sigspace
是否有效)。但此后,如果 :sigspace
有效,则模式中的所有 non-quoted 间距都是 "significant" -- 也就是说,它被转换为 <.ws>
或 [ ... <.ws> ]
。
在上面的代码中,第二个标记和规则将匹配 单个 hex_array
和后面的 space,因为 space 紧跟在 +
之后和 }
之前意味着模式被重写为:
token TOP { <hex_array>+ <.ws> }
但是,如果您的输入有 多个 hex_array
个带有一个或多个 spaces 在他们之间。相反,你会想写:
rule TOP { <hex_array> + }
# ignored ^ ^ ^ both these spaces are significant
重写为:
token TOP { [ <hex_array> <.ws> ]+ <.ws> }
这将匹配您的输入。
结论
所以,在所有这些表面上的复杂性之后,我建议您可以将原始代码编写为:
my $a = "39 3A 3B ";
grammar Hex {
rule TOP { <hex_array> + }
token hex_array { <[0..9 A..F]>**1..2 }
};
Hex.parse($a);
这将比您原来的匹配更灵活(我认为这将是一件好事,当然它可能不适用于某些用例)并且对于大多数 P6 用户来说可能更容易阅读。
最后,为了加强如何避免 rule
的三个陷阱中的两个,另请参阅 <hex_array>
和 +
之间的 space 一样。)
脚注
1如果要匹配多个字符,则在字符class后附加一个合适的量词。这是明智的做法,the assumed behavior of a "character class" according to Wikipedia. Unfortunately the P6 doc currently confuses the issue, eg lumping together both genuine character classes and other rules that match multiple characters under the heading Predefined character classes.
2默认 ws
规则旨在匹配 单词 ,其中 "word" 是字母(Unicode 类别 L)、数字 (Nd) 或下划线的连续序列.在代码中,它指定为:
regex ws { <!ww> \s* }
ww
是一个 "within word" 测试。所以 <!ww>
表示 而不是 在 "word" 中。 <ws>
总是会在 \s*
会成功的地方成功 -- 除了与 \s*
不同的是,它 不会 在单词中间成功。 (就像用 *
量化的任何其他原子一样,普通的 \s*
将始终匹配,因为它匹配任意数量的 space,包括 none .)