lua 区间比较_Lua解析神器Lpeg

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值