預定義 Subrules
下面這些是為任意 grammar 或 regex 預定義好的 subrules
:
-
ident
匹配一個標識符.
-
upper
匹配單個大寫字符.
-
lower
匹配單個小寫字符.
-
alpha
匹配單個字母字符, 或者是一個下劃線.
要匹配不帶下劃線的 Unicode 字母字符, 使用
<:alpha>
. -
digit
匹配單個數字.
-
xdigit
匹配單個十六進制數字.
-
print
匹配單個可打印字符.
-
graph
匹配單個圖形字符.
-
cntrl
匹配單個控制字符. (等價于 <:Cc> 屬性). 控制字符通常不產生輸出, 相反, 它們以某種方式控制末端:例如換行符和退格符都是控制字符. 所有使用
ord()
之后小于 32 的字符通常歸類為控制字符. 就像 ord() 的值為 127 的字符是控制字符(DEL) 一樣, 128 到 159 之間的也是控制字符. -
punct
匹配單個標點符號字符(即, 任何來自于 Unicode General Category "Punctuation" 的字符).
-
alnum
匹配單個字母數字字符. 那等價于
<+alpha +digit>
. -
wb
在單詞邊界匹配成功并返回一個零寬匹配. 一個單詞邊界是這樣一個點, 它的一邊是一個
\w
, 另一邊是一個\W
. (以任一順序), 字符串的開頭和結尾被看作為匹配\W
. -
ww
在兩個單詞字符之間匹配(零寬匹配).
-
ws
在兩個單詞字符之間匹配要求的空白, 否則匹配可選的空白. 這等價于
\s*
(ws
不要求使用ww
subrule). -
space
匹配單個空白字符character (和
\s 相同
). -
blank
匹配單個 "blank" 字符 -- 在大部分區域, 這相當于
space
和tab
. -
before
pattern
執行向前查看-- 例如, 檢查我們是否在某個能匹配的位置. 如果匹配成功返回一個零寬 Match 對象.
-
after
pattern
執行向后查看 --例如,檢查當前位置之前的字符串是否匹配
<pattern>
(在結尾錨定). 如果匹配成功就返回一個零寬 Match 對象. -
<?>
匹配一個 null 字符串,即, 總是返回真.
-
<!>
<?>
的反轉, 總是返回假
S05-mass/stdrules.t lines 310–326
?
反斜線改良
很多
\p
和\P
屬性變成諸如<alpha>
和<-alpha>
等內在的 grammar rules. 它們可以使用上面提到的字符類標記法進行組合.<[-]+alpha+digit>
. 不管高層級字符類的名字, 所有的低層級 Unicode 屬性總是可以使用一個前置冒號訪問, 即, 在尖括號中使用 pair 標記法.因此<+:Lu+:Lt>
等價于<+upper+title>
.\L...\E
,\U...\E
, 和\Q...\E
序列被廢棄了. 單個字符的大小寫修飾符\l
和\u
也被廢棄了. 在極少需要使用它們的地方, 你可以使用<{ lc $regex }>
,<{tc $word}>
, 等.就像上面提到的,
\b
和\B
單詞邊界斷言被廢棄了, 并且被<|w>
(或<wb>
) 和<!|w>
(或<!wb>
) 零寬斷言代替.\G
序列也沒有了. 使用:p
代替. (注意, 雖然, 在模式內使用:p
沒有影響, 因為每個內部模式都被隱式的錨定到當前位置) 查看下面的 at 斷言.向后引用 (例如.
\1
,\2
, 等.) 都沒有了; 可以使用$0
,$1
, 等代替. 因為正則中變量不再進行插值.
S05-capture/dot.t lines 56–61
?
數字變量被假定是每次都會改變的, 因此被看作是程序化的, 不像普通變量那樣.
?新的反斜線序列,
\h
和\v
, 分別匹配水平空白和垂直空白, 包括 Unicode. 水平空白被定義為任何匹配\s
并且不匹配\v
的東西. 垂直空白被定義為下面的任一方式:
U+000A LINE FEED (LF)
U+000B LINE TABULATION
U+000C FORM FEED (FF)
U+000D CARRIAGE RETURN (CR)
U+0085 NEXT LINE (NEL)
U+2028 LINE SEPARATOR
U+2029 PARAGRAPH SEPARATOR
?
注意 U+000D
(CARRIAGE RETURN) 被認為是垂直空白.
?
\s
現在匹配任何 Unicode 空白字符.新的反斜線序列和
\N
匹配除邏輯換行符之外的任何字符, 它是\n
的否定.-
其它新的大寫的反斜線序列也都是它們小寫身份的反義:
-
\H
匹配任何非水平空白字符. -
\V
匹配任何非垂直空白字符. -
\T
匹配任何非 tab 字符. -
\R
匹配任何非 return 字符. -
\F
匹配任何非格式字符. -
\E
匹配任何非轉義字符. -
\X...
匹配任何非指定的(指定為十六進制)字符. - 在普通字符串中反斜線轉義字面字符串在 regexes 中是允許的(\a, \x 等等). 然而, 這個規則的例外是
\b
., 它被禁用了,為了避免跟之前作為單詞邊界斷言沖突. 要匹配字面反斜線, 使用\c8
,\x8
或雙引號引起的\b
.
-
便捷的字符類
因為歷史原因和使用方便, 下面的字符類可以作為反斜線序列使用:
\d <digit> A digit
\D <-digit> A nondigit
\w <alnum> A word character
\W <-alnum> A non-word character
\s <sp> A whitespace character
\S <-sp> A non-whitespace character
\h A horizontal whitespace
\H A non-horizontal whitespace
\v A vertical whitespace
\V A non-vertical whitespace
Regexes構成一等語言
而不僅僅是字符串.
Perl 5 的
qr/pattern/
正則構造器滾蛋了.Perl 6 中的正則構造:
regex { pattern } # 總是把 {...} 作為模式分隔符
rx / pattern / # 幾乎能使用任何字符作為模式分割符
S05-metasyntax/delimiters.t lines 6–20
你不能使用空格或字母數字作為分隔符.你可以使用圓括號作為你的 rx 分隔符, 但是這只有當你插入空格時才行(標識符后面直接跟著圓括號會被認為是函數調用):
rx ( pattern ) # okay
rx( 1,2,3 ) # 嘗試調用 rx 函數
(在 Perl 6 中 這對所有類似 quotelike 的結構都適用.)
//
匹配能使用的地方 rx
這種形式也能直接作為模式使用. regex
這種形式實際上是一個方法定義, 必須用于 grammar 類中.
- 如果
regex
或rx
需要修飾符, 就把修飾符直接放在開放分隔符前面:
$regex = regex :s:i { my name is (.*) };
$regex = rx:s:i / my name is (.*) /; # same thing
如果使用任何括號字符作為分隔符, 那么最后的修飾符之后是需要空格的. ( 否則, 它會被看作修飾符的參數)
- 不能使用冒號作為分隔符. 修飾符之間可以有空格:
$regex = rx :s :i / my name is (.*) /;
正則構建器的名字是從 qr 修改而來, 因為它不再是一個能插值的 quote-like 操作符.
rx
是regex
的簡寫形式.(不要對regular expressions 感到困惑, 除了它們是的時候 )像語法指示的那樣, 它現在跟
sub { ... }
構建器很像. 實際上, 這種類似物深深根植于 Perl 6 中.就像一個原始的
{...}
現在總是一個閉包 (它仍然能在特定上下文中被立即執行并在其它上下文中作為對象傳遞), 所以原始的/.../
總是一個Regex
對象(它可能仍然在特定上下文中被立即匹配并在其它上下文中作為對象傳遞.)特別地, 在 value 上下文(
sink
,Boolean
,string
或numeric
),或它是~~
的顯式參數時,/.../
會立即匹配. 否則, 它就是一個跟顯式的 regex 形式同一的Regex
構建器, 所以這個:
$var = /pattern/;
不再進行匹配并把結果設置為 $var
. 相反, 它把一個 Regex
對象賦值給 $var
.
- 這種情況總是可以使用
m{...}
或rx{...}
進行區分:
$match = m{pattern}; # 立刻匹配 regex, 然后把匹配結果賦值給變量
$regex = rx{pattern}; # Assign regex expression itself
- 注意前面有個像這樣魔法般的用法:
@list = split /pattern/, $str;
現在來看就是理所當然的了.
- 就是現在, 建立一個像 grep 那樣的用戶自定義子例程也成為可能:
sub my_grep($selector, *@list) {
given $selector {
when Regex { ... }
when Code { ... }
when Hash { ... }
# etc.
}
}
當你調用 my_grep
時, 第一個參數被綁定到 item 上下文, 所以傳遞 {...}
或 /.../
產生 Code
或 Regex
對象, switch 語句隨后選中它作為條件.(正常的 grep 只是讓智能匹配操作符做了所有的工作)
- 就像
rx
擁有變體一樣,regex
聲明符也有. 特別地, regex 有兩個用在 grammars 中的變體:token
和rule
.
token聲明長這樣:
token ident { [ <alpha> | \- ] \w* }
默認從不回溯. 即, 它傾向于保留任何目前位置掃描到的東西.所以,上面的代碼等價于:
regex ident { [ <alpha>: | \-: ]: \w*: }
但是相當易讀. 在 token 中裸的 *
, +
和 ?
量詞絕不回溯. 在普通的 regexes 中, 使用 *:
, +:
, 或 ?:
阻止量詞的回溯. 如果你確實要回溯, 在量詞后面添加 ?
或 !
. ?
像平常一樣進行非懶惰匹配, 而 !
強制進行貪婪匹配. token
聲明符就是
regex :ratchet { ... }
的簡寫.
另外一個是 rule
聲明符, 像 token 一樣, 它默認也不會回溯. 此外, 一個 rule
這樣的正則表達式也采取了 :sigspace
修飾符. rule
實際上是
regex :ratchet :sigspace { ... }
的簡寫. ratchet 這個單詞的意思是: (防倒轉的)棘齒, 意思它是不能回溯的!
- Perl 5 的
?...?
語法(成功一次)極少使用, 并且現在能使用state
變量以更清晰的方式模擬:
$result = do { state $x ||= m/ pattern /; } # 只在第一次匹配
要重置模式, 僅僅讓 $x = 0
盡管你想要 $x
可見, 你還是必須避免使用 block:
$result = state $x ||= m/ pattern /;
...
$x = 0;
回溯控制
在這些被認為是程序的而非陳述性的模式的部分中, 你可以控制回溯行為.
默認的, 在
rx
,m
,s
中的回溯是貪婪的. 在普通的 regex 聲明中它們也是貪婪的. 在 rule 和 token 聲明中, 回溯必須是顯式的.為了強制前面的原子執行節儉回溯(有時也是所謂的"急切的匹配" 或 "最少化的匹配"), 要在原子后面追加一個
:?
或?
. 如果前面的 token 是一個量詞,:
就可以被省略, 所以*?
就像在 Perl 5 中那樣起作用.為了強制前面的原子執行貪婪回溯, 在原子后面追加一個
:!
. 如果前面的 token 是一個量詞,:
可以被省略. (Perl 5 沒有對應的結構, 因為在 Perl 5 中回溯默認是貪婪的.)為了強制前面的原子不執行回溯, 使用不帶
?
或!
的單個:
. 單個冒號讓正則引擎不再重試前面的原子:
S05-mass/rx.t lines 8–32
ms/ \( <expr> [ , <expr> ]*: \) /
(i.e. there's no point trying fewer `` matches, if there's no closing parenthesis on the horizon)
當修飾一個量詞的時候, 可以使用 +
代替 :
, 這種情況下, 量詞常常是所謂的占有量詞.
ms/ \( <expr> [ , <expr> ]*+ \) / # same thing
為了強制表達式中所有的原子默認不去回溯, 要使用 :ratchet
或 rule
或 token
.
- 對雙冒號進行求值會拋棄當前 LTM 備選分支中所有保存的選擇點。
S05-mass/rx.t lines 33–51
?
ms/ [ if :: <expr> <block>
| for :: <list> <block>
| loop :: <loop_controls>? <block>
]
/
?
到目前為止 2016.4.29 ,(::
) 還沒有實現。
::
還有通過 |
從「最長 token」中隱藏右側的任何陳述性匹配的效果。為了確定性,只有 ::
左側的東西被求值。
如果沒有當前 LTM 備選分支,那么 ::
什么也不會做?!府斍啊故潜粍討B地定義的,而非詞法地。subrule 中的 ::
會影響閉合的備選分支。
? ?
- 對
::>
進行求值會拋棄當前最里面的臨時備選分支中所有保存的選擇點。它因此表現得像 "then" 那樣。
ms/ [
|| <?{ $a == 1 }> ::> <foo>
|| <?{ $a == 2 }> ::> <bar>
|| <?{ $a == 3 }> ::> <baz>
]
/
這兒省略了一部分還沒實現的內容。
Regex 子例程, 具名和匿名
sub
和regex
之間的類推更為相似.就像你可以有匿名子例程和具名子例程...
所以你也可以有匿名正則和具名正則(還有 tokens 和 rules)
token ident { [<alpha>|\-] \w* }
# 然后...
@ids = grep /<ident>/, @strings;
- 就像上面的例子標示的那樣, 引用具名正則也是可以的, 例如:
regex serial_number { <[A..Z]> \d**8 }
token type { alpha | beta | production | deprecated | legacy }
在其它作為具名斷言的正則表達式中:
rule identification { [soft|hard]ware <type> <serial_number> }
這些使用關鍵字聲明的 regexes 官方類型為 method
, 是從 Routine
派生出來的.
?
通常, 任何 subrule 的錨定是由它的調用上下文控制的. 當 regex, token, 或 rule 方法被作為 subrule 調用時, 前面被錨定到當前位置(與 :p
一樣), 而后面沒有被錨定, 因為調用上下文可能會繼續解析. 然而, 當這樣一個方法被直接智能匹配, 它會自動的錨定兩端到字符串的開始和結尾. 因此, 你可以使用一個匿名的 regex 子例程作為單獨的模式來直接模式匹配:
?
$string ~~ regex { \d+ }
$string ~~ token { \d+ }
$string ~~ rule { \d+ }
它們等價于
$string ~~ m/^ \d+ $/;
$string ~~ m/^ \d+: $/;
$string ~~ m/^ <.ws> \d+: <.ws> $/;
基本經驗就是關鍵字定義的方法絕對不會做 .*?
那樣的掃描, 而如引號那種形式的 m//
和s///
在缺少顯式錨定的時候會做這樣的掃描.
rx//
和//
這兩種形式, 怎么掃描都可以: 當被直接用在智能匹配或布爾上下文中時, 但是當它被作為一個 subrule 間接被調用時,就不會掃描. 即, 當直接使用時, rx//
返回的對象表現的像 m//
, 但是用作 subrule 時, 表現的就像 regex {}
:
$pattern = rx/foo/;
$string ~~ $pattern; # 等價于 m/foo/;
$string ~~ /'[' <$pattern> ']'/ # 等價于 /'[foo]'/
空模式是非法的
S05-mass/rx.t lines 2378–2392
S05-metasyntax/null.t lines 17–25
在 Perl 6 中, Null regex
是非法的:
//
要匹配一個零寬字符, 需要顯式地表示 null 匹配:
/ '' /;
/ <?> /;
例如:
split /''/, $string
分割字符串。所以這樣也行:
split '', $string
同樣地,匹配一個空的分支,使用這個:
/a|b|c|<?>/
/a|b|c|''/
更容易捕獲錯誤:
/a|b|c|/
作為一種特殊情況, 匹配中的第一個 null 分支會被忽略:
ms/ [
| if :: <expr> <block>
| for :: <list> <block>
| loop :: <loop_controls>? <block>
]
/
這在格式化 regex 時會有用。
但是注意, 只有第一個分支
是特殊的, 如果你這樣寫:
ms/ [
if :: <expr> <block> |
for :: <list> <block> |
loop :: <loop_controls>? <block> |
]
/
就是錯的。因為最后一個分支是 null 。
然而, non-null句法結構有一種退化的情況能匹配 null 字符串:
$something = "";
/a|b|c|$something/;
特別地, <?>
總是成功地匹配 null 字符串, 并且 <!>
總是讓任何匹配都會失敗.