我目前熟悉LPeg解析器模块。为此,我想将版本字符串(例如11.4
)与列表进行匹配。
这样的列表是一个语法紧凑的字符串,也可以包含范围。这是一个类似EBNF,但无论如何都是非常简单的语法(我把它写下来因为下面的LPeg代码有点难以阅读):
S = R, { ',', R }
R = N, [ '-', N ]
N = digit+, [ '.', digit+ ]
示例字符串为1-9,10.1-11,12
。这是我的巨大代码:
local L = require "lpeg"
local LV, LP, LC, LR, floor = L.V, L.P, L.C, L.R, math.floor
local version = "7.25"
local function check(a, op, b)
if op and a+0 <= version and version <= b+0 then
return a..op..b -- range
elseif not op and floor(version) == floor(a+0) then
return a -- single item
end
end
local grammar = LP({ "S",
S = LV"R" * (LP"," * LV"R")^0,
R = LV"V" * (LC(LP"-") * LV"V")^-1 / check,
V = LC(LV"D" * (LP"." * LV"D")^-1),
D = (LR("09")^1),
})
function checkversion(str)
return grammar:match(str)
end
因此,您可以将其称为checkversion("1-7,8.1,8.3,9")
,如果当前版本与列表不匹配,则应该获取nil
。
现在,问题是,如果对check
的所有调用都没有返回任何内容(意味着,如果版本不匹配),grammar:match(...)
实际上没有任何捕获,因此返回当前位置的字符串。但这正是我不想要的,我希望checkversion
如果没有匹配则返回nil
或false
,否则返回为true,实际上就像string:match
一样。
如果另一方面,如果不匹配,我会从false
返回nil
或check
,我最终会得到匹配的返回值,例如nil, "1", nil, nil
基本上不可能处理。
有什么想法吗?
答案 0 :(得分:1)
我认为你可以或+
不断捕获nil:
grammar = grammar + lpeg.Cc(nil)
答案 1 :(得分:1)
这是我最终使用的模式:
nil_capturing_pattern * lpeg.Cc(nil)
我将它合并到S
规则中的语法中(请注意,这还包括更改语法以“正确”确定版本顺序,因为在版本编号“4.7”&lt;“4.11”为真,但不是在微积分中)
local Minor_mag = log10(Minor);
local function check(a, am, op, b, bm)
if op then
local mag = floor(max(log10(am), log10(bm), Minor_mag, 1))+1;
local a, b, v = a*10^mag+am, b*10^mag+bm, Major*10^mag+Minor;
if a <= v and v <= b then
return a..op..b;
end
elseif a == Major and (am == "0" or am == Minor) then
return a.."."..am;
end
end
local R, V, C, Cc = lpeg.R, lpeg.V, lpeg.C, lpeg.Cc
local g = lpeg.P({ "S",
S = V("R") * ("," * V("R"))^0 * Cc(nil),
R = (V("Vm") + V("VM")) * (C("-") * (V("Vm") + V("VM")))^-1 / check,
VM = V("D") * Cc("0"),
Vm = V("D") * "." * V("D"),
D = C(R("09")^1),
});
答案 2 :(得分:0)
match
的多次返回并非无法处理,如果您以一种使处理更容易的方式捕获它们。我添加了一个功能matched
来执行该操作,并将false
的后备返回添加到您的check
。
do
local L = require "lpeg"
local LV, LP, LC, LR, floor = L.V, L.P, L.C, L.R, math.floor
local version = 6.25
local function check(a, op, b)
if op and a+0 <= version and version <= b+0 then
return a..op..b -- range
elseif not op and floor(version) == floor(a+0) then
return a -- single item
end
return false
end
local grammar = LP({ "S",
S = LV"R" * (LP"," * LV"R")^0,
R = LV"V" * (LC(LP"-") * LV"V")^-1 / check,
V = LC(LV"D" * (LP"." * LV"D")^-1),
D = (LR("09")^1),
})
local function matched(...)
local n = select('#',...)
if n == 0 then return false end
for i=1,n do
if select(i,...) then return true end
end
return false
end
function checkversion(ver,str)
version = ver
return matched(grammar:match(str))
end
end
我将整个内容包含在do ... end
中,以便在此处用作version
的upvalue的本地check
具有约束范围,并向checversion()
添加参数使更清楚地运行几个测试用例。例如:
cases = { 1, 6.25, 7.25, 8, 8.5, 10 }
for _,v in ipairs(cases) do
print(v, checkversion(v, "1-7,8.1,8.3,9"))
end
跑步时,我得到:
C:\Users\Ross\Documents\tmp\SOQuestions>q18793493.lua 1 true 6.25 true 7.25 false 8 true 8.5 true 10 false C:\Users\Ross\Documents\tmp\SOQuestions>
请注意,nil
或false
在这种情况下同样有效。收集一个列表可以作为一个普通的Lua数组表来处理,而不用考虑漏洞,这感觉很精明。