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根据②的返回结果,捕获到一个空字符串