檢查 module 的名字是否 遵循 Perl 6 的命名規(guī)范帽氓。模塊的名字可以是使用 2 個(gè)冒號(hào)分割的標(biāo)識(shí)符, 例如 File::Compare
。標(biāo)識(shí)符必須以字母字符 (a-z) 或下劃線開(kāi)頭东亦, 后面跟著 0 個(gè) 或多個(gè)字母數(shù)字字符杏节。但是并沒(méi)有那么簡(jiǎn)單, 有些模塊的名字只有一個(gè)標(biāo)識(shí)符而沒(méi)有冒號(hào),例如 Bailador 典阵, 而其它模塊可能有多個(gè)標(biāo)識(shí)符和 ::
組成奋渔。這看起來(lái)正符合 grammar 的胃口!
定義 grammar
Perl 6 Grammars 是由 regexes 構(gòu)建的壮啊。 我需要 2 個(gè) regexes: 一個(gè)用于匹配標(biāo)識(shí)符, 一個(gè)用于匹配雙冒號(hào)分隔符嫉鲸。對(duì)于標(biāo)識(shí)符 regex, 我使用:
<[A..Za..z_]> # begins with letter or underscore
<[A..Za..z0..9]> ** 0..* # zero or more alpanumeric
Perl 6 中歹啼,字符類(lèi)是使用 <[ ... ]>
來(lái)定義的玄渗, 范圍是使用 范圍操作符 ..
代替了短劃線 -
. 量詞使用 ** 0..*
代替了 {0,}
\:\: # colon pairs
使用 grammar
關(guān)鍵字定義 Grammars, 關(guān)鍵字后跟著 grammar 的名字. 我把這個(gè) grammar 叫做 Legal-Module-Name
grammar Legal-Module-Name{
...
}
現(xiàn)在我能把 regexes 作為 tokens 添加到 grammar 中了:
grammar Legal-Module-Name{
token identifier {
# leading alpha or _ only
<[A..Za..z_]>
<[A..Za..z0..9]> ** 0..*
}
token separator {
\:\: # colon pairs
}}
每一個(gè) Grammar 中都要有一個(gè) 叫做 TOP
的 token,它是這個(gè) grammar 的起始點(diǎn):
grammar Legal-Module-Name{
token TOP { # identifier followed by zero or more separator identifier pairs
^ <identifier> [<separator><identifier>] ** 0..* $
}
token identifier {
# leading alpha or _ only
<[A..Za..z_]>
<[A..Za..z0..9]> ** 0..*
}
token separator {
\:\: # colon pairs
}
}
TOP
定義了一個(gè)合法的模塊名狸眼,它以一個(gè)標(biāo)識(shí)符 token 開(kāi)始藤树,然后是 0 個(gè)或多個(gè) 分隔符和標(biāo)識(shí)符對(duì)兒。 這很好寫(xiě)并且很容易維護(hù)拓萌。假設(shè)我現(xiàn)在想要修改分隔符規(guī)則來(lái)包含短劃線 ('-')岁钓,我只需更新分隔符 token 的 regex, 不需要更新 TOP
了。
使用 grammar
Grammar 的 parse
方法對(duì)一個(gè)字符串應(yīng)用定義的 grammar屡限, 如果解析成功就返回一個(gè) match 對(duì)象品嚣。這段代碼解析了 $proposed_module_name
字符串, 結(jié)果要么打印出 match 對(duì)象,要么打印錯(cuò)誤信息如果模塊名不合法的話钧大。
my $proposed_module_name = 'Super::New::Module';
my $match_obj = Legal-Module-Name.parse($proposed_module_name);
if $match_obj{
say $match_obj;
}else{
say 'Invalid module name!';
}
這段代碼打印:
?Super::New::Module?
identifier => ?Super?
separator => ?::?
identifier => ?New?
separator => ?::?
identifier => ?Module?
從 match object 中提取內(nèi)容
我們能從 match 對(duì)象中提取匹配的 tokens翰撑。這里會(huì)使用 Perl 6 中到處可見(jiàn)的 quoting 語(yǔ)法(例如 命名正則和散列鍵)
say $match_obj<identifier>[0].Str; # Super
say $match_obj<identifier>[1].Str; # New
say $match_obj<identifier>[2].Str; # Module
say $match_obj<identifier>; # all 3 captures
Action 類(lèi)
Perl 6 允許你添加一個(gè) action 類(lèi)來(lái)為匹配到的 tokens 定義額外的行為。我想在模塊名有太多的標(biāo)識(shí)符時(shí)給出一個(gè)警告啊央,換句話說(shuō)眶诈,它是一個(gè)合法的名字,但是用戶可能想要縮短簡(jiǎn)化它瓜饥。 首先我定義了 action 類(lèi):
class Module::Name::Actions{
method TOP($/)
{
if $<identifier>.elems > 5
{
warn 'Module name has a lot of identifiers, consider simplifying the name';
}
}
}
這就是一個(gè) Perl 6 的類(lèi)的定義册养。 我添加了一個(gè)叫做 TOP
的方法,它匹配 grammar 中的第一個(gè) token压固。 我使用 命名指正則語(yǔ)法來(lái)計(jì)算所有的標(biāo)識(shí)符匹配,如果多于 5 個(gè)就發(fā)出警告靠闭。 這不會(huì)讓代碼停止運(yùn)行帐我, 但是會(huì)引起使用者重新考慮他們選擇的模塊名字。
然后我初始化了一個(gè) action 對(duì)象愧膀,并且把它作為參數(shù)傳遞給了Grammar 的 parse
方法:
my $actions = Module::Name::Actions.new;
my $match_obj = Legal-Module-Name.parse($proposed_module_name, :actions($actions));
解析期間拦键, 每當(dāng) 這個(gè) token (TOP) 出現(xiàn)時(shí),都會(huì)調(diào)用一次匹配 action 類(lèi)中的 TOP 方法檩淋。