Lua Lpeg
- 用基本匹配函數,組合匹配表達式
- 所有匹配函數返回userdata類型,是一個匹配模式(以下用pattern代替),可相互組合
lpeg.P
lpeg.P(value)
將給定的value,根據規則返回適當的pattern,規則如下:
-
value
是pattern,原封不動的返回這個pattern -
value
是string, 返回整個該字符串的pattern -
value
是非負整數,只有在輸入的字符串長度大于等于value個字符串時返回成功。 -
value
是負整數,只有在輸入的字符串長度還剩下不到value個字符才返回成功,lpeg.P(-n)
等價于-lpeg.P(n)
-
value
是boolean,總是返回成功或者失敗的pattern -
value
是table,將它理解為一個語法(Grammar) -
value
是function,返回pattern,它等價于一個match-time capture
用一個空字符串的匹配模式(lpeg.P(function)
等價于lpeg.Cmt("", function)
)。
示例:
- 都是從第一個字符串開始匹配,若沒匹配到就返回
nil
- 如果匹配到pattern,則返回匹配到的字符串長度加
1
local lpeg = require "lpeg"
local match = lpeg.match
local P = lpeg.P
-- value是string的情況
local ps = P'ab'
print(match(ps, "a")) ---> nil
print(match(ps, "abcd")) ---> 3 注:只匹配"ab"
-- value是非負整數
-- 注:完全匹配任意value個字符
local pn1 = P(3)
-- local pn1 = P(3.0) -- 這樣也是可以的
print(match(pn1, "12")) ---> nil 注:只有2個字符
print(match(pn1, "abcd")) ---> 4
-- 注:value不能是小數,但是小數部分為0是可以的
-- 如果是小數,總是返回1
local pn2 = P(3.1)
print(match(pn2, "")) ---> 1
-- value是負整數
-- 被匹配的字符串長度要小于value
local pn3 = P(-3) -- 等價于 local pn3 = - P(3)
print(match(pn3, "abcd")) ---> nil 注:字符串長度>=3了
print(match(pn3, "ab")) ---> 1
print(match(pn3, "")) ---> 1
-- value是boolean
local pb1 = P(true)
local str1 = "aaa" -- str1為任意字符串
print(match(pb1, str1)) ---> 1 注:當value為true時,不管str1是什么字符串,始終返回1
local pb2 = P(false)
local str2 = "aaa" -- str2為任意字符串
print(match(pb2, str2)) ---> nil 注:當value為false時,不管str2是什么字符串,始終返回nil
-- value是table
local m1 = Cs((P(1) / { a = 1, b = 2, c = 3 }) ^ 0)
print(match(m1, "abc")) ---> 123
-- value是function
-- 請跳到match-time capture(lpeg.Cmt)
lpeg.B
- 返回一個匹配模式,只有在輸入的字符串在當前位置是patt的謂語時,才被匹配到。
- patt在有調整長度的字符串中匹配,并且不包含捕獲
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local B = lpeg.B
local m1 = B"a"
local m2 = 1 * m1
local m3 = 1 * B"b"
print(match(m1, "a")) ---> nil
print(match(m2, "a")) ---> 2
print(match(m3, "a")) ---> nil
-- 注:這里的m3和m2的區別就是一個是a,一個是b,但得到的結果不一樣
local m4 = B(1)
local m5 = 1 * m4
print(match(m4, "a")) ---> nil
print(match(m5, "a")) ---> 2
print(match(-m4, "a")) ---> nil
local m6 = B(250)
local m7 = 250 * m6
print(match(m6, string.rep("a", 250))) ---> nil
print(match(m7, string.rep("a", 250))) ---> 251
lpeg.R
- 給定一個
2
字符的字符串(可以傳多個這樣的字符串),返回兩個字符之間范圍的pattern - 字符可以為任意字符編碼(閉區間)
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local R = lpeg.R
local r1 = R"09" -- 匹配任意數字
print(match(r1, "12345")) ---> 2 注:只要匹配到1個字符就返回
local r2 = R("az", "AZ") -- 可以傳多個2個字符組成的字符串
print(match(r2, "abcd")) ---> 2
print(match(r2, "Abcd")) ---> 2
-- 特別注意
local r3 = R()
local str = "aaa"
print(match(r3, str)) ---> nil
-- 當R沒有參數時,不管str為什么字符串,總返回nil
lpeg.S
- 給定一個字符串(字符串等價于字符的集合(set)),返回一個匹配該字符串里任意一個字符的pattern
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local S = lpeg.S
local s1 = S"+-*/"
print(match(s1, "+")) ---> 2
print(match(s1, "-")) ---> 2
print(match(s1, "*")) ---> 2
print(match(s1, "/")) ---> 2
-- 特別注意
local s2 = S""
local str = "aaa"
print(match(s2, str)) ---> nil
-- 注:當S的參數為空字符串的時候,不管str是什么字符串,總返回nil
lpeg.V
- 這個操作為語法(Grammar)生成一個變量(non-terminal),變量規則是閉包語法中的v
- 個人理解:因為語法是通過table來定義的,調用這個函數告訴lpeg在table里定義了哪些變量名
- 剩下的后面說哇~~
lpeg.locale
- 返回一個table,table里面的元素是lpeg提供的常用pattern,有以下匹配模式:alnum,alpha,cntrl,digit,graph,lower,print,punct,space,upper,xdigit
- 如果參數是table,則會把這些pattern放到參數的table里面
示例:
local lpeg = require "lpeg"
local lpegHelp = {}
lpeg.locale(lpegHelp)
local help = lpeg.locale()
Lpeg操作符
#patt
- 返回一個pattern。如果輸入的字符串被patt匹配到,這個pattern才會被匹配。
- 這個pattern被叫做,and謂語,等價于原生PEG中的
&patt
- 這個pattern從不產生任何捕獲。
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local P = lpeg.P
local C = lpeg.C
local V= lpeg.V
local p1 = P"aa"
local p2 = #p1
print(match(p1, "a")) ---> nil
print(match(p2, "a")) ---> nil
print(match(p1, "aaaa")) ---> 3
print(match(p2, "aaaa")) ---> 1
-- 進階
local pat = P{
"S";
S1 = C("abc") + 3,
S = #V("S1") -- 這里捕獲到了結果,但是#必須忽略這個結果
}
print(match(pat, "abc")) ---> 1
-- 調整長度(fixed length)
local m1 = #("a" * (P"bd" + "cd"))
local m2 = C(m1 * 2)
local m3 = C(m1 * 3)
local m4 = C(m1 * 4)
print(match(m1, "acd")) ---> 1
print(match(m2, "acd")) ---> ac
print(match(m3, "acd")) ---> acd
print(match(m4, "acd")) ---> nil
-- 解析:
-- 1. #patt后面跟著 * number時,是對捕獲到的值的長度進行調整
-- 2. number的值不能超過捕獲到的值的長度
-patt
- 返回一個pattern,只有patt沒有被匹配到的時候始終返回1,否則返回nil
- 這個pattern從不產生任何捕獲
- 這個pattern等價于patt的補集
- 個人理解:這里始終返回1是根據結果猜的,如果有其他情況后面再補上
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local P = lpeg.P
local p1 = P"aa"
local p2 = - p1
print(match(p1, "aa")) ---> 3
print(match(p2, "aa")) ---> nil
print(match(p1, "a")) ---> nil
print(match(p2, "a")) ---> 1
patt1 + patt2
- 返回一個pattern,匹配patt1或者patt2
- 如果patt1和patt2都是字符集合,這個操作,返回結果的并集。
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local P = lpeg.P
local p1 = P"aaa"
local p2 = P"aa"
local p3 = p1 + p2
print(match(p3, "a")) ---> nil
print(match(p3, "aa")) ---> 3
print(match(p3, "aaa")) ---> 4
patt1 - patt2
- 返回一個pattern,不捕獲patt2并且捕獲到patt1
- 當捕獲成功后,這個pattern產生patt1的所有捕獲,并且從不捕獲patt2
- 如果patt1和patt2都是字符集合,這個操作相當于兩個集合的差集。- patt2 = "" - patt2 = 0 - patt2。(如果patt2是字符集合,那么1 - patt2是補集)
local lpeg = require "lpeg"
local match = lpeg.match
local S = lpeg.S
local s1 = S"\1\0\2"
local s2 = S"\0"
local s3 = s1 - s2
print(match(s1, "\0")) ---> 2
print(match(s2, "\0")) ---> 2
print(match(s3, "\0")) ---> 0
print(match(s3, "\1")) ---> 2
-- 注:這里的s3是s1與s2的差集,也就是不配s2并且匹配s1
patt1 * patt2
- 返回一個pattern,它會先匹配patt1,如果匹配成功繼續匹配patt2
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local P = lpeg.P
local p1 = P"aa"
local p2 = P"bb"
local p3 = p1 * p2
print(match(p3, "aabb")) ---> 5
print(match(p3, "aaaa")) ---> nil
patt^n
- 如果n是非負整數,這個pattern將匹配n個或n個以上patt
- 如果n是負整數,那么這個pattern講最多匹配n個patt,也就是最少匹配0個,最多匹配n個
-
patt ^ 0
類似于lua里面正則表達式的*
-
patt ^ 1
類似于lua里面正則表達式的+
-
patt ^ -1
類似于lua里面正則表達式的?
- 在所有情況下,此pattern的結果是沒有回溯的貪婪模式,也就是說匹配最長的可能匹配到的序列
local lpeg = require "lpeg"
local match = lpeg.match
local P = lpeg.P
local p1 = P"aa"
local p2 = p1 ^ 0
local p3 = p1 ^ 3
local p4 = p1 ^ -1
print(match(p1, "aaaa")) ---> 3
print(match(p2, "aaaa")) ---> 5
print(match(p3, "aaaa")) ---> nil
print(match(p3, "aaaaaa")) ---> 7
print(match(p4, "aaaa")) ---> 3
print(match(p4, "a")) ---> 1 注:這里沒有匹配到也返回1
Lpeg語法(Grammars)
- 在lua的環境下,可以自定義一些patterns,讓新定義的pattern可以使用已經定義過的舊的pattern。然而,這種技術不允許定義遞歸匹配模式。對于遞歸匹配模式,我們需要真正的語法。
- Lpeg用tables來描述語法,每個條目就是一個語法規則。
- 通過調用
lpeg.V(v)
來創建一個pattren,它表示在語法中的一個引索V。 - 當table被轉換成了pattern(通過調用
lpeg.P
或者在一個pattern中使用),它被固定了。 - 當table已經固定,結果是一個匹配它初始規則的pattern。第一個引索的條目就是她初始規則。如果這個條目是字符串,那么它被認為是初始規則的名字。否則lpeg假定第一個條目就是table的初始規則。
捕獲(Captures)
- 捕獲是一種模式,該模式會根據匹配到的數據返回值(語義信息)。
- lpeg有幾種捕獲方式,產生的值會基于匹配,并且組合這個值產生新的值。
- 每個捕獲可能產生0個或多個值。
lpeg.C (patt)
- 創建一個簡單的捕獲,捕獲匹配到patt的子字符串。
- 捕獲的值是一個字符串。
- 如果patt有其他捕獲,他們的值將在這個值之后返回
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local P = lpeg.P
local R = lpeg.R
local S = lpeg.S
local C = lpeg.C
local p1 = P"aa" ^ 3
local cp1 = C(p1)
print(match(cp1, "aaaaaa")) ---> aaaaaa
print(match(cp1, "aa")) ---> nil
local r1 = R"09" ^ 0
local cr1 = C(r1)
print(match(cr1, "1234aaaa")) ---> 1234
print(#match(cr1, "aaaa")) ---> 0 注:這里返回的是空字符串,跟lpeg.P不同
local s1 = S"+-*/" ^ 0
local cs1 = C(s1)
print(match(cs1, "++++aaaa")) ---> ++++
print(#match(cs1, "aaaa")) ---> 0 注:這里返回的是空字符串,跟lpeg.P不同
-- 進階
local m1 = { [1] = C(C(1) * V(1) + -1) }
print(match(m1, "abc")) ---> "abc" "a" "bc" "c" "c" "" 注:這里的值添加了引號,最后一個是空字符串
-- 解析:
-- 過程:
-- ① (1)匹配到a,然后匹配V(1),由于C(1),產生結果a
-- ② (1)匹配到b,然后匹配V(1),由于C(1),產生結果b
-- ③ (1)匹配到c,然后匹配V(1),由于C(1),產生結果c
-- ④ (1)C(1) * V(1)沒有匹配到,匹配到-1,返回到③,由于-1,產生""
-- ③ (2)匹配到C(1) * V(1),返回到②,由于C(1) * V(1),產生c和④的組合,就是c
-- ② (2)匹配到C(1) * V(1),返回到①,由于C(1) * V(1),產生b和③的組合,就是bc
-- ① (2)匹配到C(1) * V(1),返回結果,由于C(1) * V(1),產生a和②的組合,就是abc
-- 這個過程是個遞歸的過程,看似有點復雜,根據結果,仔細分析一下就能得出結論啦
lpeg.Carg (n)
- 創建一個參數捕獲。
- 未完待續
lpeg.Cb (name)
- 創建一個回退捕獲
- 這個pattern匹配空字符串,并且通過最近的
group cpature(lpeg.Cg)
被命名的名字(名字可以是任何Lua類型),產生返回值 - 最近的
group cpature(lpeg.Cg)
被命名的名字的意思是,最近一次完整的給定名字的組捕獲 - 完整的捕獲意思是,整個模式對應的捕獲匹配。
- 外層的意思是,這個捕獲不是在另一個完整的捕獲
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local Cg = lpeg.Cg
local Cb = lpeg.Cb
local t = {}
local foo = function(s)
t[#t + 1] = s
return s .. "x"
end
local m1 = Cg(C(2) / foo, "y") * Cb"y"
* Cg(Cb"y" / foo, "y") * Cb"y"
* Cg(Cb"y" / foo, "y") * Cb"y"
print(match(m1, "ab")) ---> abx abxx abxxx
print(table.concat(t, " ")) ---> ab ab abx ab abx abxx
-- 解析:
-- ① C(2)捕獲到"ab",通過foo函數,向t插入"ab",并返回"abx",再給"abx"打上"y"的標記
-- ② 接下來的Cb"y",捕獲到打了"y"標記的"abx",這里產生了"abx"
-- ③ 第一個Cg(Cb"y" / foo, "y"),這里的Cb"y",從Cg(C(2) / foo, "y")開始,走①②流程
-- ④ 然后再匹配Cg(③的結果 / foo, "y") * Cb"y",向t插入"abx",此時t里面的內容是"ab" "ab" "abx",并通過foo函數返回"abxx",并打上"y"標記
-- ⑤ 接下來的Cb"y",捕獲到打了"y"標記的"abxx",這里產生了"abxx"
-- ⑥ 第二個Cg(Cb"y" / foo, "y"),重復③的流程
-- 最后m1捕獲到的結果是:"abx" "abxx" "abxxx"
-- t里面的結果是
-- {
-- "ab", -- 這個是Cg(C(2) / foo, "y") * Cb"y"的結果
-- "ab", "abx", -- 這個是第一個* Cg(Cb"y" / foo, "y") * Cb"y"的結果
-- "ab", "abx", "abxx" -- 這個是第二個* Cg(Cb"y" / foo, "y") * Cb"y"的結果
-- }
lpeg.Cg (patt [, name])
- 創建一個組捕獲。這個組捕獲的所有返回值都是通過patt捕獲到的簡單返回值
- 如果沒有給組取名字,那么這個組就是匿名的,或者這個組的名字是給定的名字(nil不能作為名字)
- 在大多數情況下,一個被命名的組,是沒有任何捕獲值返回的,只有在
back capture(lpeg.Cb)
或者table capture(lpeg.Ct)
才會有捕獲值返回
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local Cg = lpeg.Cg
local Cc = lpeg.Cc
local C = lpeg.C
local m1 = Cg(1)
print(match(m1, "x")) ---> x
local m2 = Cg(Cg(Cg(1)))
print(match(m2, "x")) ---> x
local m3 = Cg(Cg(Cg(C(1)) ^ 0) * Cg(Cc(1) * Cc(2)))
print(match(m3, "abc")) ---> a b c 1 2
local m4 = Ct(Cg(Cc(10), "hi") * C(1) ^ 0 * Cg(Cc(20), "ho"))
print(match(m4, "abc")) ---> { hi = 10, ho = 20, "a", "b", "c" }
-- 非字符串的組名
local print = print
local tab = {}
local m5 = Ct(Cg(1, print) * Cg(1, 23.5) * Cg(1, tab))
local result = match(m5, "abcdefghij")
print(result) ---> { [function: 0x10322db70] = "a", [23.5] = "b", [table: 0x7faaa2408660] = "c" }
print(result[print]) ---> a
print(result[tab]) ---> c
lpeg.Cp ()
- 創建一個位置捕獲。這個pattern匹配空字符串,并且返回捕獲的位置
- 這個捕獲值是數字類型
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local R = lpeg.R
local Cp = lpeg.Cp
local letter = R"az" + R"AZ"
local m1 = letter ^ 1
local m2 = Cp() * m1
local m3 = m2 * Cp()
local m4 = C(m3)
local m5 = Cp() * letter ^ 5 * Cp()
print(match(m1, "abcd")) ---> 5
print(match(m2, "abcd")) ---> 1
print(match(m3, "abcd")) ---> 1 5
print(match(m4, "abcd")) ---> abcd 1 5
print(match(m5, "abcd")) ---> nil
-- 解析:
-- 1. 通過m1和m2,推出:如果有Cp的pattern只返回Cp的結果
-- 2. 通過m3,推出:存在Cp時,并匹配成功,只返回Cp()相關結果
-- 3. 通過m4,推出:會優先捕獲其他pattern的結果,再返回Cp的結果
lpeg.Cc ([value, ...])
- 創建一個常量捕獲,這個pattern匹配到一個空字符串并且產生所有給定的值作為捕獲的值
示例
local lpeg = require "lpeg"
local match = lpeg.match
local R = lpeg.R
local Cc = lpeg.Cc
local m1 = C(R"09" ^ 1 * Cc("a"))
local m2 = C(R"09" ^ 1 * Cc("a", "b"))
local m3 = Cc(nil)
local m4 = Cc()
print(match(m1, "123")) ---> 123 a
print(match(m2, "123")) ---> 123 a b
print(match(m3, "123")) ---> nil
print(match(m4, "123")) ---> 1
----- 分割線 -----
local m5 = Cc() * Cc() * Cc(1) * Cc(20, 30, 40) * "a" * Cp()
print(match(m5, "aaa")) ---> 1 20 30 40 2
local m6 = Cc() * Cc() * Cc(nil) * Cc(1) * Cc() * Cc(20, 30, 40) * "a" * Cp()
local m7 = Cc(1) * Cc(2) * Cc(nil) * Cc(3) * Cc()
print(match(m6, "aaa")) ---> nil 1 20 30 40 2
print(match(m7, "aaa")) ---> 1 2 nil 3 注:這里只返回了4個結果
-- 根據結果,推出以下結論:
-- 1. Cc()單獨匹配時返回1,例如m4
-- 2. Cc()和其他太參數的Cc函數或其他pattern組合時,Cc()會被忽略,即Cc()什么都不會返回
lpeg.Cf (patt, func)
- 創建一個迭代捕獲。
- 如果這個patt產生了一個
C1, C2, ..., Cn
的捕獲列表,那么這個捕獲將產生一個值,這個值由func(...func(func(C1, C2), C3)..., Cn)
產生 - 也就是說,它會使用func對產生的捕獲值進行迭代
- 這個patt至少捕獲到一個值(任何類型的值),這個值來作為累積器的初始值。至少要這個條件成立
- 如果patt要一個特殊的初始值,應該在這個patt前面加一個
constant capture(lpeg.Cc)
- 對于后面的每一個捕獲,LPeg調用func,這個累積值作為func的第一個參數,這個捕獲產生的所有值作為這個func的額外參數,func的結果成為新的累積值。這個累積的最終值就是這個patt的結果
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local R = lpeg.R
local Cf = lpeg.Cf
local Cc = lpeg.Cc
local C = lpeg.C
local number = R"09" ^ 1 /tonumber
local list = number * ("," * number) ^ 0
function add(acc, cpatureNewValue)
return acc + cpatureNewValue
end
local sum = Cf(list, add)
print(match(sum, "10,30,43")) ---> 83
local f = function(x)
return x + 1
end
local m1 = Cc(0) * C(1) ^ 0
local m2 = Cf(m1, f)
print(match(m1, "alo alo")) ---> 0 "a" "l" "o" "" "a" "l" "o"
print(match(m2, "alo alo")) ---> 7
-- 注:通過m1的結果,推出:Cc(0)是第一個捕獲值,最后迭代出7
local m3 = Cf(Cc(1, 2, 3), error)
print(match(m3, "")) ---> 1 注:這里調用error直接返回1(個人理解)
local m4 = Ct(true) * Cg(C(R"az" ^ 1) * "=" * C(R"az" ^ 1) * ";") ^ 0
local m5 = Cf(m4, rawset)
print(match(m4, "a=b;c=du;xux=yuy;")) ---> {} a b c du xux yuy
print(match(m5, "a=b;c=du;xux=yuy;")) ---> { a= "b", c = "du", xux = "yuy" }
-- 注:這里的Ct(true)捕獲到一個空table,m5通過rawset向空table設置key-value
lpeg.Cs (patt)
- 創建一個替換捕獲
lpeg.Ct (patt)
- 創建一個table捕獲。這個捕獲返回一個table
- 返回所有patt的匿名捕獲的值,key從1開始
- 此外,每個被命名的捕獲組,第一個值是組名
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local R = lpeg.R
local Ct = lpeg.Ct
local C = lpeg.C
local Cc = lpeg.Cc
local letter = R"az" + R"AZ"
local m1 = letter ^ 1
local m2 = Ct(m1)
print(match(m1, "alo")) ---> 4
print(match(m2, "alo")) ---> {} 注:m1沒有返回捕獲的值
local m3 = C(m1)
local m4 = Ct(m3) * Cc("t")
print(match(m4, "alo")) ---> { "alo" } "t"
local m5 = Ct(C(C(letter) ^ 1))
print(match(m5, "alo")) ---> { "alo", "a", "l", "o" }
local m6 = Ct((Cp() * letter * Cp()) ^ 1)
local m7 = Ct(m6)
print(match(m6, "alo")) ---> { 1, 2, 2, 3, 3, 4 }
print(match(m7, "alo")) ---> { { 1, 2, 2, 3, 3, 4 } }
patt / string
- 創建一個字符串捕獲。這個操作會創建一個基于string的字符串捕獲
- 返回的捕獲的值是string的一個副本,string中的%是轉義字符,如果要表示%,則用%%來表示
- 字符串中的任何序列中有%n(n在1到9之間),在patt中匹配第n個捕獲。%0表示匹配到的整個序列
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local P = lpeg.P
local Cp = lpeg.Cp
local pi = "3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510"
local m1 = (P"1" / "a" + P"5" / "b" + P"9" / "c" + 1) ^ 0
print(match(m1, pi)) ---> a a b c b b c c c b a c a c c c b a
-- 注:這里返回的是捕獲到值后被替換的值
print(match(m1, "222")) ---> 4 注:這里只匹配到了1,所以返回4
local m2 = Cp() * P(3) * Cp() / "%2%1%1 - %0"
print(match(m2, "abcde")) ---> "411 - abc"
-- 解析:
-- 1. 先匹配Cp() * P(3) * Cp(),匹配到 1 4
-- 2. 然后通過 / "%2%1%1 - %0" 格式化結果
-- 3. 這里的%1對應1,%2對應4,%0對應"abc"
local m3 = C"a" / "%1%%%0"
print(match(m3, "a")) ---> a%a
local m4 = P(1) / "%0"
print(match(m4, "abc")) ---> a
local m5 = C(1) ^ 0 / "%2-%9-%0-%9"
print(match(m5, "0123456789")) ---> "1-8-0123456789-8"
local m6 = C(1) ^ 0 / "9-%1-%0-%3"
print(match(m6, "12345678901234567890")) ---> "9-1-12345678901234567890-3"
-- 注:%1 - %9 是所有捕獲的結果,%0 - %9 的值都只能是數字或者字符串,其他類型報錯
patt / number
- 創建一個有編號的捕獲
- 對于非0的number,返回的是被捕獲的第n個值
- 當number為0時,不會返回捕獲的值
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local C = lpeg.C
local m1 = C(1)
local m2 = C(C(m1 * C(2)) * C(3))
local m3 = m2 / 3
local m4 = m2 / 0
print(match(m2, "aaabcdefgh")) ---> aaabcd aaa a aa bcd
-- 注:這里返回值的順序是什么規律?按匹配的順序也不像哇~~~~
print(match(m3, "aaabcdefgh")) ---> a
print(match(m4, "aaabcdefgh")) ---> 7
-- 注:
-- 1. 如果是負數會報錯
-- 2. 如果number超出捕獲的長度,也會報錯
local m5 = m1 * (C(m1 * C(2)) * C(3) / 4) * m1
print(match(m5, "abcdefgh")) ---> a efg h
-- 解析:
-- ① 匹配m1,捕獲到a
-- ② (1)先匹配m1,捕獲到b
-- ② (2)再匹配C(2),捕獲到cd
-- ② (3)再匹配m1 * C(2),捕獲到bcd
-- ② (4)再匹配C(3),捕獲到efg
-- ② (5)再取②捕獲到的值的第4個捕獲值,最終得到efg
-- ③ (6)最后匹配m1,捕獲到h
patt / table
- 創建一個查詢捕獲。patt捕獲到的值作為table中的key,來引索table中的value
- table中的值是捕獲的最終值。如果table沒有key,將不會產生捕獲
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local P = lpeg.P
local pi = "3.14159 26535 89793 23846 26433 83279 50288 41971 69399 37510"
local m1 = (P(1) / { ["1"] = "a", ["5"] = "b", ["9"] = "c" }) ^ 0
print(match(m1, pi)) ---> a a b c b b c c c b a c a c c c b a
print(match(m1, "222")) ---> 4
patt / function
- 創建一個函數捕獲
- 這個pattern將調用給定的function,并把捕獲到的所有值當參數傳個這個function
- function的返回值是這個捕獲的最終值,如果function沒有返回值,也就沒有捕獲值
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local C = lpeg.C
local Cg = lpeg.Cg
local Cb = lpeg.Cb
local foo = function(s)
return s .. "0"
end
local m1 = Cg(C(3) / foo, "name") * Cb("name")
print(match(m1, "abc")) ---> abc0
lpeg.Cmt(patt, function)
- 創建一個匹配時的捕獲
- 不像其他捕獲,當有一個匹配時,會立即執行(甚至在一個很復雜的匹配模式中失敗)
- 它會強制執行所有嵌入的捕獲,并調用function
- 整個待匹配的字符串,當前匹配到的坐標,還有任何捕獲到的值是這個function的參數
- function返回的第一個值定義了匹配過程,如果返回的是一個數字,則表示匹配成功,返回的數字成為新的匹配位置
- 假設一個被匹配的字符串
s
,和當前的匹配位置i
,返回的數字必須在[i, len(s+1)]
- 如果返回
true
,匹配成功時,不消耗任何輸入(所以返回true
,等價于返回i
) - 如果返回
false
,nil
,或者無返回值,匹配失敗 - function額外的返回值會成為捕獲的值
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local S = lpeg.S
local P = lpeg.P
local R = lpeg.R
local V = lpeg.V
local C = lpeg.C
local Cs = lpeg.Cs
local Ct = lpeg.Ct
local Cg = lpeg.Cg
local Cmt = lpeg.Cmt
local space = S" \t\n" ^ 0
local id = function(s, i, ...)
return true, ...
end
local m1 = P{
"S",
S = V"atom" * space + Cmt(Ct("(" * space * (Cmt(V"S" ^ 1, id) + P(true)) * ")" * space), id),
atom = Cmt(C(R("AZ", "az", "09") ^ 1), id)
}
print(match(m1, "(a g () ((b) c) (d (e)))")) ---> { "a", "g", {}, { { "b" }, "c" }, { "d", { "e" } } }
local m2 = Cmt(1, id) ^ 0
print(match(m2, string.rep("a", 5))) ---> a a a a a
local id = function(s, i, x)
if x == "a" then
return i, 1, 3, 7
end
return nil, 2, 4, 6, 8
end
local m3 = (P(id) * 1 + Cmt(2, id) * 1 + Cmt(1, id) * 1) ^ 0
print(match(m3, "abababab")) ---> 1 3 7 1 3 7 1 3 7 1 3 7
-- 問題:這里每個Cmt后面要 * 1呢?不加的話會報錯
local ref = function(s, i, x)
-- print(s, i, x)
return match(x, s, i - #x)
end
local m4 = Cmt(P(1) ^ 0)
local m5 = P(1) * m4
local m6 = P(1) * Cmt(C(1) ^ 0)
print(match(m4, "alo")) ---> 4
print(match(m5, "alo")) ---> 4
print(match(m6, "alo")) ---> nil
-- 注:這里的m5和m6的區別,一個P,一個是C;
-- P后面跟了^0,所以要等P匹配完才產生捕獲;
-- 雖然C后面也跟了^0,但是C立馬產生捕獲,
-- 所以會立馬調用ref,就會匹配不到,然后直接返回nil
ref = function(s, i, a, b)
if a == b then
return i, a:upper()
end
end
local any = P(1)
local m7 = Cmt(C(R"az" ^ 1) * "-" * C(R"az" ^ 1), ref)
local m8 = (any - m7) ^ 0 * m7 * any ^ 0 * -1
print(match(m8, "abbbc-bc ddaa")) ---> BC
-- 解析:
-- ① (1)先匹配(any - m7) ^ 0,這里是不匹配m1,并且匹配any,匹配至少0次
-- ① (2)先匹配m7,這里會捕獲到"abbbc"和"bc"當做參數傳給ref,也就是ref的a和b兩個參數,然后"abbbc"不等于"bc",沒有返回值,所以也就沒有匹配到m7,且匹配到any
-- ② 重復①的過程,這次m7匹配到"bbbc"和"bc",也沒匹配到m7
-- ③ 重復①的過程,這次m7匹配到"bbc"和"bc",也沒匹配到m7
-- ④ 重復①的過程,這次m7匹配到"bc"和"bc",這回匹配到m7了,所以(any - m7) ^ 0的匹配終止,產生結果4
-- ⑤ 接著匹配m7,從位置4開始匹配,然后捕獲到"bc"和"bc",返回了一個捕獲值"BC"
local check = function(_, _, s1, s2)
return s1 == s2
end
local m9 = "[" * Cg(P"=" ^ 0, "init") * "[" * {
Cmt("]" * C(P"=" ^ 0) * "]" * Cb("init"), check) + 1 * V(1)
} / 0
print(match(m9, "[==[]]====]]]]==]===[]")) ---> 18
-- 解析:
-- ① 首先匹配"[" * Cg(P"=" ^ 0, "init") * "[",這里捕獲到"==",并命名為"init",返回位置5
-- ② (1)再匹配{ Cmt("]" * C(P"=" ^ 0) * "]" * Cb("init"), check) + 1 * V(1) } / 0,這里有兩個捕獲,一個是C(P"=" ^ 0),另一個是Cb("init")
-- ② (2)先匹配Cmt("]" * C(P"=" ^ 0) * "]" * Cb("init"), check),從位置5開始匹配,匹配到]],產生捕獲"",和前面捕獲的"=="比較,不相等,返回false,匹配失敗
-- ② (3)然后匹配1 * V(1),重復過程②(1)
-- ③ 直到匹配到]==]
Lpeg 小技巧
示例:
local lpeg = require "lpeg"
local match = lpeg.match
local P = lpeg.P
local R = lpeg.R
local V = lpeg.V
local C = lpeg.C
print(match(3, "aaaa")) ---> 4
print(match(P(3), "aaaa")) ---> 4
-- lpeg 對lua的數據類型默認使用lpeg.P來處理
local m1 = R"09" * -1
local m2 = R"09" * P(-1)
local m3 = R"09" * -P(1)
-- 這里的m1、m2、m3等價
print(match(m1, "9")) ---> 2
print(match(m1, "99")) ---> nil
print(match(m2, "9")) ---> 2
print(match(m2, "99")) ---> nil
print(match(m3, "9")) ---> 2
print(match(m3, "99")) ---> nil
-- lpeg.V、#patt
local tab = {
[1] = "(" * (((1 - S"()") + #P"(" * V(1)) ^ 0)
}
-- 這里的V(1)指的是tab[1]的規則
-- 這里的#P"("表示這條規則匹配到的"("的個數(個人結論,不太確定)
local digit = R"09"
local upper = R"AZ"
local lower = R"az"
local letter = S"" + upper + lower
local alpha = letter + digit + R()
local m1 = letter ^ 1
local m2 = m1:C()
local m3 = C(m1) -- 注:這里的m3等價于m2
local m4 = P{ [1] = m2 + (1 * V(1)) }
local m5 = m4 ^ 0
print(match(m1, " 4achou123...")) ---> nil
print(match(m4, " 4achou123...")) ---> achou
print(match(m5, " two words, one more ")) ---> two words one more
-- 解析:
-- m1是匹配所有字母,通過m4的語法,就可以匹配不以字母開頭的字符串
-- 關鍵點是 1 * V(1) 中的 1 *,這里的1等價于P(1),也就是至少匹配任意一個字符
-- 匹配過程:
-- (1) 第一個字符是空白符,m2是肯定沒匹配到,那么匹配(1 * V(1))
-- (2) 匹配到1的時候,符合匹配條件,再匹配V(1),此時又回到過程(1)
-- 直到都不符合匹配。整個過程是個遞歸的過程,最后把匹配到的結果返回
-- 語法例子
local m6 = P{ P"x" * V(1) + P"y" }
-- 這里的m6可以改為 local m6 = P{ P"x" * V(1) + -1 } 看看是什么結果
local m7 = -m6
local m8 = C(m7)
print(match(m6, "xxx")) ---> nil
print(match(m7, "xxx")) ---> 1
print(match(m8, "xxx")) ---> "" 注:這里是空字符串
-- 解析:
-- ① m6的匹配過程:一直匹配P"x" * V(1),知道最后一個x,然后匹配P"y",但是沒有沒匹配到,直接返回nil
-- ② m7根據①的返回結果,返回①的補集,這里nil的補集就是1
-- ③ m8根據②的返回結果,捕獲到一個空字符串