如何在 Perl 6 中創(chuàng)建 Grammar?

檢查 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 方法檩淋。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末芬为,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蟀悦,更是在濱河造成了極大的恐慌媚朦,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件日戈,死亡現(xiàn)場(chǎng)離奇詭異询张,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)浙炼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)份氧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人弯屈,你說(shuō)我怎么就攤上這事蜗帜。” “怎么了资厉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵厅缺,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng)店归,這世上最難降的妖魔是什么阎抒? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮消痛,結(jié)果婚禮上且叁,老公的妹妹穿的比我還像新娘。我一直安慰自己秩伞,他們只是感情好逞带,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著纱新,像睡著了一般展氓。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上脸爱,一...
    開(kāi)封第一講書(shū)人閱讀 51,258評(píng)論 1 300
  • 那天遇汞,我揣著相機(jī)與錄音,去河邊找鬼簿废。 笑死空入,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的族檬。 我是一名探鬼主播歪赢,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼单料!你這毒婦竟也來(lái)了埋凯?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤扫尖,失蹤者是張志新(化名)和其女友劉穎白对,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體换怖,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躏结,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了狰域。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片媳拴。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖兆览,靈堂內(nèi)的尸體忽然破棺而出屈溉,到底是詐尸還是另有隱情,我是刑警寧澤抬探,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布子巾,位于F島的核電站帆赢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏线梗。R本人自食惡果不足惜椰于,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望仪搔。 院中可真熱鬧瘾婿,春花似錦、人聲如沸烤咧。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)煮嫌。三九已至笛谦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間昌阿,已是汗流浹背饥脑。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留懦冰,地道東北人好啰。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像儿奶,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鳄抒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容