Perl 6 中的正則表達式(一)

標題

Synopsis 5: Regexes and Rules

版本

創建于: 2002/06/24

上次修改: 2015/05/12
版本: 180

不論何時, 在 grammar 中引用遞歸模式時, 通常更偏好使用 tokenrule, 而不是 regex.

概覽

作為常規表達式記法的擴展, Perl 6 原生地實現了 Parsing Expression Grammars(PEGs). PEGs 要求你為有歧義的那部分提供一個 主從秩序. Perl 6 的 主從秩序 由一個多級的平局擇優法測試決定:

    1) Most-derived only/proto hides less-derived of the same name
    2) 最長 token 匹配: food\s+ beats foo by 2 or more positions
    3) 最長字面值前綴: food\w* beats foo\w* by 1 position
    4) 對于一個給定的 proto, multis from a more-derived grammar win
    5) 在一個給定的編譯單元中, 出現較早的備選分支或 multi 勝出.

#3 會把任何初始的字面序列當作最長字面值前綴. 如果在最長 token 匹配中有一個嵌入的備選分支, 那些備選分支會擴展字面值前綴, 把備選分支也作為字面值的一部分. 如果所有的備選分支都是字面值的, 那么字面值也能延伸到備選分支的末尾, 當它們重新聚合時. 否則, 備選分支的末尾會終止所有的最長字面值前綴, 即使分支全部是字面值的. 例如:

/ a [ 1 | 2 ] b /   # 最長字面值是 'a1b' 和 'a2b'
/ a [ 1 | 2\w ] b / # 最長文字是'a1 和 'a2', \w 不是字面值
/ a <[ 1 2 ]> b /   # 最長字面值是 'a'

注意, 這種情況下, 字符類和備選分支被不同地對待. 在字符類中包含一個最長的字面字符串太普遍了.

就像最長 token 匹配一樣, 最長字面值前綴貫穿于 subrules 中. 如果 subrule 是一個 protoregex, 它會被看作帶有 | 的備選分支, 后面跟著擴展或終止最長字面值前綴的同一個 rules.

除了這個主從秩序以外, 如果任何 rule 選擇于主從秩序回溯之下, 那么選擇下一個最好的 rule. 即, 主從秩序決定了候選者列表; 正是因為選擇一個候選者并不意味著放棄其它候選者. 然而, 能通過一個恰當的回溯控制顯式地放棄其它候選者(有時叫它 cut 操作符, 但是 Perl 6 有多個 cut , 取決于你想切掉多少)

還有, 任何在 #1 下被選中執行的 rule 可以選擇委托給它的祖先來執行; PEG 不對此回溯.

新的匹配結果和捕獲變量

附屬的匹配對象現在可通過 $/ 變量獲取, 它隱式是詞法作用域的. 通過這個變量來訪問最近一次的匹配. 單獨的捕獲變量(例如 $0, $1等) 正是 $/ 中的元素.
順便說一下, 不像 Perl 5, Perl 6 中的捕獲變量現在從 $0 開始編號而不是 $1. 查看下面.
為了檢測 Perl 5的不相關的 $/ 變量的意外使用, Perl 6 的 $/ 變量不能被直接賦值.

$/ = $x;   # 不支持使用  $/ 變量作為輸入記錄分隔符 (input record separator)
$/ := $x;  # OK, 綁定
$/ RR= $x; # OK, 元操作符
($/) = $x; # OK, 列表賦值

沒變的語法特性

下面的正則特性語法和 Perl 5 是一樣的:

S05-mass/rx.t lines 100–247

  • 捕獲: (…)
  • 重復量詞: *, +, 和 ?
  • 備選分支: |
  • 反斜線轉義: \
  • 最少匹配后綴: ??, *?, +?

雖然 | 的語法沒有變, 但是默認的語義稍微改變了一點. 我們試圖混合一個令人滿意的描述性的和程序上的匹配, 以至于我們能擁有其中兩者. 簡言之, 你不用給 grammar 寫你自己的 tokener了, 因為 Perl 會幫你寫好. 查看下面的 Longest-token 匹配.

S05-metasyntax/longest-alternative.t lines 6–52

use v6;
use Test;

plan 53;

#L<S05/Unchanged syntactic features/"While the syntax of | does not change">

my $str = 'a' x 7;

{
    ok $str ~~ m:c(0)/a|aa|aaaa/, 'basic sanity with |';
    is ~$/, 'aaaa', 'Longest alternative wins 1';

    ok $str ~~ m:c(4)/a|aa|aaaa/, 'Second match still works';
    is ~$/, 'aa',   'Longest alternative wins 2';

    ok $str ~~ m:c(6)/a|aa|aaaa/, 'Third match still works';
    is ~$/, 'a',    'Only one alternative left';

    ok $str !~~ m:c(7)/a|aa|aaaa/, 'No fourth match';
}

# now test with different order in the regex - it shouldn't matter at all

#?niecza skip 'Regex modifier g not yet implemented'
{
    ok $str ~~ m:c/aa|a|aaaa/, 'basic sanity with |, different order';
    is ~$/, 'aaaa', 'Longest alternative wins 1, different order';

    ok $str ~~ m:c/aa|a|aaaa/, 'Second match still works, different order'; # c -> 從上次匹配結束的位置匹配繼續匹配
    is ~$/, 'aa',   'Longest alternative wins 2, different order';

    ok $str ~~ m:c/aa|a|aaaa/, 'Third match still works, different order';
    is ~$/, 'a',    'Only one alternative left, different order';

    ok $str !~~ m:c/aa|a|aaaa/, 'No fourth match, different order';
}

{
    my @list = <a aa aaaa>;
    ok $str ~~ m/ @list /, 'basic sanity with interpolated arrays';
    is ~$/, 'aaaa', 'Longest alternative wins 1';

    ok $str ~~ m:c(4)/ @list /, 'Second match still works';
    is ~$/, 'aa',   'Longest alternative wins 2';

    ok $str ~~ m:c(6)/ @list /, 'Third match still works';
    is ~$/, 'a',    'Only one alternative left';

    ok $str !~~ m:c(7)/ @list /, 'No fourth match';
}

簡化的模式詞法解析

不像傳統的正則表達式那樣, Perl 6 不要求你記住數量眾多的元字符. 相反, Perl 6 通過一個簡單的 rule 將字符進行了分類. 在正則表達式中, 所有根字符為下劃線(_)或擁有一個以 L(例如, 字母) 或 N(例如, 數字)開頭的 Unicode 類別的字形(字素) 總是字面的.(例如, 自己和自己匹配的). 它們必須使用 \ 轉義以使它們變成元語法的(這時單個字母數字字符本身是元語法的, 但是任何在字母數字后面緊緊跟隨的字符不是).

所有其它的字形 — 包括空白符 — 正好與此相反:它們總是被認為是元語法的.(例如, 自身和自身不匹配), 必須被轉義或引用以使它們變為字面值. 按照傳統, 它們可以使用 \ 單獨轉義, 但是在 Perl 6中, 它們也能像下面這樣被括起來.

把一個或多個任意類型的字形序列放在單引號中能使它們變為字面值.(如果使用和當前語言相同的插值語義, 雙引號也是允許的 ) 引號創建了一個能量化的原子, 所以,

S05-metasyntax/single-quotes.t lines 16–27

moose*

量詞只作用在字母 e 上, 并匹配 mooseee, 而

'moose'*

量詞作用在整個字符串上并匹配 moosemoose.

下面有個表格總結了這些區別:

                 字母數字的           非字母數字的                 混合的

 Literal glyphs   a    1    _        \*  \$  \.   \\   \'       K\-9\!
 Metasyntax      \a   \1   \_         *   $   .    \    '      \K-\9!
 Quoted glyphs   'a'  '1'  '_'       '*' '$' '.' '\\' '\''     'K-9!'

換句話說, 標識符字形是字面值的(或者在被轉義時是元語法的), 非標識符字形是元語法的(或者在被轉義時是字面值的), 而且單引號包圍起來的東西是字面值的。

注意, 目前在 Perl 6 中不是所有的非標識符字形作為元語法都是有意義的(例如 \1\_-!)。更準確地說, 所有非轉義的非標識符字形都有可能是元語法的, 而且留作將來之用。如果你使用了這樣一個序列, 那么編譯器會拋出一個有用的編譯時錯誤以標示你要么需要引起這個序列, 要么定義一個新的操作符以識別它。

S05-metasyntax/unknown.t lines 6–48

分號字符被特別地保留作非意義(non-meaningful)元字符; 如果看到了未被引起的分號, 那么編譯器就會抱怨那個正則表達式沒有終止符。

S05-metasyntax/regex.t lines 73–129

修飾符

  • /x 語法擴展不在需要了.. 在 Perl 6 中 /x 是默認的了.(事實上, 這是強制的 -- 唯一能用回舊語法的方式是使用 :Perl5/:P5 修飾符)

S05-modifier/perl5_4.t lines 7–119
S05-modifier/perl5_2.t lines 7–119
S05-modifier/perl5_9.t lines 7–112
S05-modifier/perl5_1.t lines 7–119
S05-modifier/perl5_8.t lines 7–128
S05-modifier/perl5_5.t lines 7–126
S05-modifier/perl5_7.t lines 7–119
S05-modifier/perl5_6.t lines 7–130
S05-modifier/perl5_3.t lines 7–119
S05-modifier/perl5_0.t lines 9–113

  • 沒有 /s/m 修飾符了(變成了元字符來替代它們-看下面.)
  • 沒有 /e求值修飾符用于替換了, 相反, 使用:
s/pattern/{ doit() }/

或:

s[pattern] = doit()

代替 /ee 的是:

s/pattern/{ EVAL doit() }/

或:

s[pattern] = doit().EVAL
  • 修飾符現在作為副詞放在匹配或替換的開頭:
m:g:i/\s* (\w*) \s* ,?/;

每個修飾符必須以自己的冒號開始. 必須使用空格把模式分隔符和最后一個修飾符隔開, 如果它會被看作為前面那個修飾符的參數.(只有當下一個字符是左圓括號時才為真).

  • 單字符修飾符也有更長的版本:
:i        :ignorecase   忽略大小寫
:m        :ignoremark   忽略記號
:g        :global       全局
:r        :ratchet      回溯
  • :i (或 :ignorecase) 修飾符在詞法作用域但不是它的動態作用域中忽略大小寫. 即, subrules 總是使用它們自己的大小寫設置. 大小寫轉換的次數取決于當前上下文. 在字節和代碼點模式中, 要求級別為1 的大小寫轉換. 在字形模式下, 需要級別為 2 的大小寫轉換.

S05-modifier/ignorecase.t lines 24–87

  • :ii (或 :samecase) 變體可能被用在替換上以把被替換掉的字符串的大小寫模式修改成和匹配字符串的大小寫模式一樣。它暗示著該修飾符和上面的 :i 修飾符的語義擁有相同的模式。所以把 :i:ii 放在一塊沒有必要。

S05-modifier/ii.t lines 8–30

  • 如果模式匹配的時候沒有使用 :sigspace 修飾符, 那么 case info is carried across on a character by character basis. 如果右側字符串比左側字符串長, 那么最后一個字符的大小寫被重復。 如果可能的話, 不管結果字母是不在單詞的開頭, 都會執行單詞首字母大寫; 如果沒有可用的單詞首字母大寫字符, 就會使用對應的大寫字符。(該策略可以在本地作用域中通過依賴于語言的 Unicode 聲明來根據特定語言的正交規則替換 titlecase。)不攜帶大小寫信息的字符保持它們對應的替換字符串不變。

  • 如果模式匹配使用了 :sigspace 修飾符, 那么then a slightly smarter algorithm is used which attempts to determine if there is a uniform capitalization policy over each matched word, and applies the same policy to each replacement word. If there doesn't seem to be a uniform policy on the left, the policy for each word is carried over word by word, with the last pattern word replicated if necessary. If a word does not appear to have a recognizable policy, the replacement word is translated character for character as in the non-sigspace case. Recognized policies include:

S05-modifier/ii.t lines 31–75

lc()
uc()
tc()
tclc()
tcuc()

任何情況下,只有模式匹配的正式匹配到的字符串部分才被計數,所以任何形式的向前匹配或語境匹配沒有包含在分析之內。

  • :m (或 :ignoremark) 修飾符的作用域非常像 :ignorecase, 除了它忽略記號(重音和諸如此類) 而非大小寫之外。它等價于接收每個字素(in both target and pattern), 把兩者都轉換為 NFD (最大限度地分解)并比較那兩個根字符(Unicode 無記號字符)而忽略任何末尾記號字符。記號字符只在判斷斷言真假的時候忽略記號字符; 實際匹配的的文本包含了所有的忽略字符, 包含任何跟在最后的根字符后面的東西。
    S05-modifier/ignoremark.t lines 14–39

S05-modifier/ignorecase-and-ignoremark.t lines 15–35

  • :mm (或 :samemark) 變體能用于替換以把替換字符串更改為和匹配字符串相同的記號/重音。這暗示著它和上面的 :m 的模式語義相同, 所以, 把 :m:mm 放在一塊是沒有必要的。
    S05-modifier/samemark.t lines 9–39

  • 通過字根對字符執行記號信息。如果右側字符串比左側字符串長, 那么剩余的字符在被替換時不使用任何修改。 (注意 NFD/NFC 的區別通常不太重要, 因為 Perl 把它封裝在了字素模式中。) 在 :sigspace 修飾符下, 會逐字應用之前的規則。

  • :c (或 :continue) 修飾符讓 pattern 從字符串中指定的位置繼續掃描 (默認為 $/ ?? $/.to !! 0):
    S05-modifier/continue.t lines 6–50

m:c($p)/ pattern /     # 從位置 $p 開始掃描, $p 是字符串中的位置,而非模式中的位置
  use v6;


  #L<S05/Modifiers/"The :c">

  my regex simple { . a };
  my $string = "1a2a3a";

  {
      $string ~~ m:c/<&simple>/;               # 1a
      $string ~~ m:c/<&simple>/;               # 2a
      $string ~~ m:c/<&simple>/;               # 3a
      $string ~~ m:c/<&simple>/;               # no more 'a's to match

  }

  {
      my $m = $string.match(/.a/);             # 1a
      $m = $string.match(/.a/, :c(2));         # 2a
      $m = $string.match(/.a/, :c(4));         # 3a

  }

  # this batch not starting on the exact point, and out of order
  {
      my $m = $string.match(/.a/, :c(0));      # 1a
      $m = $string.match(/.a/, :c(3));         # 3a
      $m = $string.match(/.a/, :c(1));         # 2a

  }

  {
      my $m = $string.match(/.a/);             # 1a
      $m = $string.match(/.a/, :continue(2));  # 2a
      $m = $string.match(/.a/, :continue(4));  # 3a

  }

注意, 這不會自動把 pattern 錨定到開始位置. (使用 :p 可以). 提供給 splitpattern 默認有一個隱式的 :c 修飾符.

m:pos($p)/ pattern /  # match at position $p

如果參數被省略,它就默認為 ($/ ?? $/.to !! 0). (Unlike in Perl 5, the string itself has no clue where its last match ended.) All subrule matches are implicitly passed their starting position. Likewise, the pattern you supply to a Perl macro's is parsed trait has an implicit :p modifier.

注意

m:c($p)/pattern/

正等價于

m:p($p)/.*? <( pattern )> /

所有的 :g, :ov, :nth, :x:p 都是不兼容的, 并且會失敗, 推薦使用 :c 代替. 允許使用 :ex 修飾符, 但是只會在那個位置產生匹配.

  • 新的 :s (:sigspace) 修飾符讓空白序列起作用,這些空白被匹配規則 <.ws> 代替. 只有緊跟在匹配結構(原子, 量詞化原子)之后的空白序列是合適的。因此, 出現在任何 regex 開頭的空白都會被忽略, 為了讓能夠參與最長 token 匹配的備選分支更容易書寫。即:

S05-grammar/ws.t lines 5–34

m:s/ next cmd '='   <condition>/

等價于:

m/ next <.ws> cmd <.ws> '=' <.ws> <condition>/

同樣等價于:

m/ next \s+ cmd \s* '=' \s* <condition>/

但是在這種情況:

m:s{(a|\*) (b|\+)}

或等價的:

m { (a|\*) <.ws> (b|\+) }

<.ws> 直到看見數據才能決定要做什么. 它仍舊能做對事情. 不過不能, 定義你自己的 ws, 而 :sigspace 會使用它.`
僅僅當 rule 可能參與最長 token 匹配時, rule 前面的空白才會被忽略, 但是在任何顯式備選分支的前面這同樣適用, 因為同樣的原因。如果你想在一組備選分支前面匹配有意義的空格(sigspace), 把你的空白格放在含有備選分支的括號外面。
? ?
當你這樣寫:

rule TOP { ^ <stuff> $ }

這等價于

token TOP { ^ <.ws> <stuff> <.ws> $ <.ws> }

但是注意最后一個 <.ws> 總是匹配空字符串,因為 $ 錨定字符串的末尾. 如果你的 TOP rule 沒有使用 ^ 錨定, 它不會匹配開頭的空白.

特別地, 下面的構造會把后面跟著的空白轉換為 sigspace:

任何原子或量詞化的原子
$foo @bar
'a' "$b"
^ $ ^^ $$
(...) [...] <...> 作為整個原子
(...)* [...]* <...>* 作為量詞化的原子
<( 和 )>
? 和 ? (但是不要那樣使用 ? )

然而這些并不會:

開口的 ( 或 [
| 或 ||
& 或 &&
** % 或 %%
:foo 聲明, 包括 :my 和 :sigspace 自身
{...}

當我們說 sigspace 能跟在原子或量詞化的原子后面時, 我們是說 sigspace 能出現在原子和它的量詞之間:
?

ms/ <atom> * /      # 意味著 / [<atom><.ws>]* /

(如果每個原子匹配空白, 那么就沒有必要匹配后面的量詞了)。If each atom matches whitespace, then it doesn't need to match after the quantifier.)

一般地, 你不需要在 grammars 中使用 :sigspace, 因為解析規則會為你自動處理空白策略。在該上下文中, 空白常會包含注釋, 根據 grammar 如何去定義它的空白規則。盡管默認的 <.ws> subrule 識別不了注釋結構, 任何 grammar 能隨意重寫 <.ws> 規則。<.ws> rule 并不意味著在哪兒都是一樣。

S05-grammar/ws.t lines 6–34

:sigspace 傳遞一個參數也是可能的, 指定一個完全不同的 subrule 以應用。 這可以是任何 rule, 它不需要必須匹配空白。當談論這個修飾符的時候, 把模式中的有意義的空白和正被匹配的"空白"區分開很重要, 所以我們會把模式中的空白叫做 sigspace, 而通常保留 whitespace 來標示當前 grammar<.ws> 所匹配到的任何東西。 sigspacewhitespace 之間的一致性是隱喻性的, 這就是為什么一致性既有用又令人迷惑。

除了智能空白匹配, :ss (或 :samespace) 變體還可以用在替換上做智能空白映射。(即, :ss 暗示了 :s )。 對于左側每個包含 sigspace<ws> 調用, 所匹配到的空白被復制到右側對應的坑(slot)里, 在想要空白替換的替換字符中由單個空白符表示。如果右側比左側的空白坑多, 則那些右手邊的字符保持自身不變。如果右側沒有足夠的空白坑映射所有從匹配中得到的空白坑, 那么算法會嘗試通過從空白列表中隨機地拼接"普通"空白符來使信息丟失最小化。 從最不貴重(valuable)到最貴重, 排列順序為:
?

spaces
tabs
所有其他水平空白, 包括 Unicode
換行符 (包括作為一個單元的 crlf)
所有其他垂直空白, 包括 Unicode

這些規則的主要意圖是發生在行邊界這樣的替換時最小化格式分裂。也就是說, 當然, 不保證結果正是人們想要的。
?
:s 修飾符很重要, 以至于為它們定義了匹配變體:
S05-modifier/sigspace.t lines 27–48
S05-substitution/subst.t lines 212–222
?

ms/match some words/                        # 和 m:sigspace  相同
ss/match some words/replace those words/    # 和 s:samespace 相同

注意 ss/// 是就 :ss 而言所定義的, 所以:

$_ = "a b\nc\td";
ss/b c d/x y z/;

結果就是 a x\ny\tz.

  • 新修飾符可指定 Unicode 級別:
m:bytes  / .**2 /       # 匹配兩個字節
m:codes  / .**2 /       # match two codepoints
m:graphs / .**2 /       # match two language-independent graphemes
m:chars  / .**2 /       # match two characters at current max level
  • 新的 :Perl5/:P5 修飾符允許使用 Perl 5 正則語法 (現在不允許你把修飾符放在后面). 例如,
m:P5/(?mi)^(?:[a-z]|\d){1,2}(?=\s)/

等價于 Perl 6 語法:

m/ :i ^^ [ <[a..z]> || \d ] ** 1..2 <?before \s> /
  • 任何一個整數修飾符指定一個計數. 數字后面的字符決定了計數的種類:
  • 如果后面跟著一個 x, 則意味著重復. 一般使用 :x(4) 這種形式:

S05-modifier/repetition-exhaustive.t lines 16–30
S05-modifier/repetition.t lines 6–29

s:4x [ (<.ident>) '=' (\N+) $$] = "$0 => $1";

等同于:

s:x(4) [ (<.ident>) '=' (\N+) $$] = "$0 => $1";

這幾乎等同于:

s:c[ (<.ident>) '=' (\N+) $$] = "$0 => $1" for 1..4;

except that the string is unchanged unless all four matches are found. However, ranges are allowed, so you can say :x(1..4) to change anywhere from one to four matches.

s:3rd/(\d+)/@data[$0]/;

等同于

s:nth(3)/(\d+)/@data[$0]/;

它等價于

m/(\d+)/ && m:c/(\d+)/ && s:c/(\d+)/@data[$0]/;

?例如:

$_ =  "123abc456def789hij"
s:3rd/(\d+)/"PERL"/;  # $_ => 123abc456def"PERL"hij
s:2rd/(\d+)/PHP/;     # $_ => 123abcPHPdef"PERL"hij
$_ = "0abc1DEF2cfg3OOP";
my @data =('-', '_', ':');
s:3rd/(\d+)/@data[$0]/;    # $_ => 0abc1DEF:cfg3OOP

:nth 的參數允許是一個整數列表, 但是這樣的列表應該是單調遞增的.(任何小于等于前一個值的值都會被忽略.) 所以:
?

:nth(2,4,6...*)    # return only even matches
:nth(1,1,*+*...*)  # match only at 1,2,3,5,8,13...

例子:

$_ = "1 2 3 4 5 6 7 8 9";
my @a = m:nth(2,4,6,8)/(\d+)/; # 返回 2 4 6 8

該選項不再要求支持智能匹配。你可以 grep 一個整數列表如果你真的需要那個能力:

:nth(grep *.oracle, 1..*)

如果 :nth:x 都出現了, 那么相匹配的子例程查找使用 :nth 匹配的子匹配。如果第后 nth 個匹配的數量與 :x 中的約束相容, 那么整個匹配成功, 并且子匹配的可能的數量最高。如果 :nth 不是單個標量, 則 :nth:x 通常才有意義。

  • 使用新的 :ov (:overlap) 修飾符, 當前的 regex 會在所有可能的字符位置上(包括重疊)匹配, 并在列表上下文中返回所有的匹配, 或在 item 上下文中返回間斷的匹配。任一位子的第一個匹配會被返回。匹配保證了相對于開始位置, 會以從左到右的順序返回。

S05-modifier/overlapping.t lines 16–67

$str = "abracadabra";
if $str ~~ m:overlap/ a (.*) a / {
     @substrings = slice @();    # bracadabr cadabr dabr br
 }

匹配確保會相對于起始位置以從左到右的順序返回。每個起始位置之內的順序不能收到保證, 并且可能依賴于模式和匹配引擎兩者。(推測: 或者我們能強制回溯引擎語義。或者我們可以保證一點也沒有順序, 除非模式以 :: 開始或者某種這樣的東西抑制了 DFAish 方法。)
?

$str = "abracadabra";
if $str ~~ m:exhaustive/ a (.*?) a / {
   say "@()";    # br brac bracad bracadabr c cad cadabr d dabr br
}

注意一旦發現第一個匹配, 上面的 ~~ 就返回, 而匹配的剩余部分可能通過 @() 來懶惰地執行。
?

  • 新的 :rw 修飾符讓該 regex 要求當前的字符串可以修改而非假設寫時復制語義。 $/ 中所有的捕獲變成了左值字符串, 這樣如果你修改, 例如, $1, 那么原來的字符串在那個位置被修改, 并且所有其它字段的的位置也相應地修改(不管它是什么意思)。如果沒有該修飾符, (特別是如果它尚未實現或永遠不會被實現), 那么 $/ 的所有片段都被當作是寫時復制的, 如果不是只讀的話。

[猜測: 這應該真正把模式和字符串變量聯合在一起, 而不是一個字符串值(可能是不可變的)]

  • 新的 :r:ratchet 修飾符讓這個 regex 默認不回溯。 (通常你不直接使用這個修飾符, 因為在 tokenrule 聲明符中已經隱式地包含了這個修飾符, 也就是說 tokenrule 默認也是不回溯的。) 這個修飾符的作用是在每個原子(atom)后面暗指一個 :, 包括但不限于 *, +, 和 ? 量詞, 還有備選分支。量詞化原子上的顯式回溯修飾符, 例如 **, 會重寫這個修飾符。

S05-modifier/ratchet.t lines 5–27
S05-mass/rx.t lines 92–99

m/:s alignment '=' [:i left|right|cent[er|re]] /

就像外面的修飾符一樣, 只有圓括號能夠作為副詞的參數被識別為合法的括號. 特別地:

m/:foo[xxx]/        Parses as :foo [xxx]
m/:foo{xxx}/        Parses as :foo {xxx}
m/:foo<xxx>/        Parses as :foo <xxx>
  • 用戶自定義修飾符成為可能:
m:fuzzy/pattern/;
  • 用戶自定義修飾符也能接收參數, 但是只能在圓括號中:
m:fuzzy('bare')/pattern/;
  • 要使用括號作為你的分隔符你必須隔開:
m:fuzzy (pattern);

或者把 pattern 放在最后:

m:fuzzy(fuzzyargs); pattern ;
  • 任何 grammar regex 實際上是一種方法, 并且你可以在這樣一個子例程中使用一個冒號跟著任何作用域聲明符來聲明一個變量, 這些聲明符包括 my, our, stateconstant (作為類似的聲明符, temp 和 let 也能被識別). 單個語句(直到結尾的分號或行末尾的閉括號為止) 被解析為普通的 Perl 6 代碼:

S05-modifier/my.t lines 5–87

token prove-nondeterministic-parsing {
    :my $threshold = rand;
    'maybe' \s+ <it($threshold)>
}

這種作用域聲明符不會終止最長 token 匹配, 所以無效的聲明符可以作為作為一個掛鉤來掛起副作用而不改變隨后的模式匹配:

rule breaker {
    :state $ = say "got here at least once";
    ...
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,656評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,855評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,254評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,473評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,014評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,833評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,016評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,273評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,730評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,006評論 2 374

推薦閱讀更多精彩內容