Нечувствительное к регистру совпадение в LPeg.re (Lua)
Я новичок в модулях "LPeg" и "re" Lua, в настоящее время я хочу написать шаблон, основанный на следующих правилах:
- Соответствует строке, начинающейся с "gv_$/gv$/v$/v_$/x$/xv$/dba_/all_/cdb_", и префикса "SYS.% S *" или "PUBLIC.% S *" необязательно
- Строка не должна следовать алфавитно-цифровой, т. Е. Шаблон не будет соответствовать "XSYS.DBA_OBJECTS", потому что он следует за "X"
- Шаблон не чувствителен к регистру
Например, ниже строки должны соответствовать шаблону:
,sys.dba_objects, --should return "sys.dba_objects"
SyS.Dba_OBJECTS
cdb_objects
dba_hist_snapshot) --should return "dba_hist_snapshot"
В настоящее время мой шаблон ниже, который может соответствовать только не буквенно-цифровой + строка в верхнем регистре:
p=re.compile[[
pattern <- %W {owner* name}
owner <- 'SYS.'/ 'PUBLIC.'
name <- {prefix %a%a (%w/"_"/"$"/"#")+}
prefix <- "GV_$"/"GV$"/"V_$"/"V$"/"DBA_"/"ALL_"/"CDB_"
]]
print(p:match(",SYS.DBA_OBJECTS"))
Мои вопросы:
- Как добиться сопоставления без учета регистра? Есть несколько тем о решении, но я слишком новый, чтобы понять
- Как точно вернуть только совпадающую строку, вместо того, чтобы плюс%W? Что-то вроде "(?=...)" в Java
Высоко ценится, если вы можете предоставить шаблон или связанные функции.
2 ответа
Вы можете попытаться настроить эту грамматику
local re = require're'
local p = re.compile[[
pattern <- ((s? { <name> }) / s / .)* !.
name <- (<owner> s? '.' s?)? <prefix> <ident>
owner <- (S Y S) / (P U B L I C)
prefix <- (G V '_'? '$') / (V '_'? '$') / (D B A '_') / (C D B '_')
ident <- [_$#%w]+
s <- (<comment> / %s)+
comment <- '--' (!%nl .)*
A <- [aA]
B <- [bB]
C <- [cC]
D <- [dD]
G <- [gG]
I <- [iI]
L <- [lL]
P <- [pP]
S <- [sS]
U <- [uU]
V <- [vV]
Y <- [yY]
]]
local m = { p:match[[
,sys.dba_objects, --should return "sys.dba_objects"
SyS.Dba_OBJECTS
cdb_objects
dba_hist_snapshot) --should return "dba_hist_snapshot"
]] }
print(unpack(m))
,,, печатает таблицу соответствия m
:
sys.dba_objects SyS.Dba_OBJECTS cdb_objects dba_hist_snapshot
Обратите внимание, что нечувствительности к регистру довольно сложно добиться с помощью лексера, поэтому каждая буква должна получить отдельное правило - в конечном итоге вам понадобится больше таких символов.
Эта грамматика заботится о комментариях в вашем образце и пропускает их вместе с пробелами, поэтому совпадения после "следует возвращать" не отображаются в выходных данных.
Вы можете возиться с prefix
а также ident
правила для указания дополнительных префиксов и разрешенных символов в именах объектов.
Замечания: !.
означает конец файла. !%nl
означает "не конец строки". ! p
а также & p
строят непотребляющие шаблоны, т.е. текущий указатель ввода не увеличивается при совпадении (вход проверяется только).
Заметка 2: print
с unpack
это грубый хак.
Примечание 3: Вот трассируемый LPeg, который можно использовать для отладки грамматик. Проходить true
для 3-го параграфа re.compile
чтобы получить трассировку выполнения с действием test/match/skip для каждого посещенного правила и позиции.
Наконец-то я получил решение, но не такое изящное, которое заключается в добавлении дополнительного параметра case_insensitive
в re.compile, re.find, re.match and re.gsub
функции. Когда значение параметра true
, а затем вызвать case_insensitive_pattern
переписать шаблон:
...
local fmt="[%s%s]"
local function case_insensitive_pattern(quote,pattern)
-- find an optional '%' (group 1) followed by any character (group 2)
local stack={}
local is_letter=nil
local p = pattern:gsub("(%%?)(.)",
function(percent, letter)
if percent ~= "" or not letter:match("%a") then
-- if the '%' matched, or `letter` is not a letter, return "as is"
if is_letter==false then
stack[#stack]=stack[#stack]..percent .. letter
else
stack[#stack+1]=percent .. letter
is_letter=false
end
else
if is_letter==false then
stack[#stack]=quote..stack[#stack]..quote
is_letter=true
end
-- else, return a case-insensitive character class of the matched letter
stack[#stack+1]=fmt:format(letter:lower(), letter:upper())
end
return ""
end)
if is_letter==false then
stack[#stack]=quote..stack[#stack]..quote
end
if #stack<2 then return stack[1] or (quote..pattern..quote) end
return '('..table.concat(stack,' ')..')'
end
local function compile (p, defs, case_insensitive)
if mm.type(p) == "pattern" then return p end -- already compiled
if case_insensitive==true then
p=p:gsub([[(['"'])([^\n]-)(%1)]],case_insensitive_pattern):gsub("%(%s*%((.-)%)%s*%)","(%1)")
end
local cp = pattern:match(p, 1, defs)
if not cp then error("incorrect pattern", 3) end
return cp
end
...