Lua中的LPeg.re大小写不敏感匹配

我对Lua中的"LPeg"和"re"模块都很陌生,目前我想基于以下规则编写一个模式:

  1. 匹配以"gv_$/gv$/v$/v_$/x$/xv$/dba_/all_/cdb_"开头的字符串,并且前缀"SYS.%s*"或"PUBLIC.%s*"是可选的。
  2. 字符串不应跟随字母数字,即该模式不会匹配"XSYS.DBA_OBJECTS",因为它跟随"X"。
  3. 模式不区分大小写。

例如,下面的字符串应该匹配该模式:

,sys.dba_objects,       --应该返回 "sys.dba_objects"
SyS.Dba_OBJECTS
cdb_objects
dba_hist_snapshot)      --应该返回 "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"))

我的问题是:

  1. 如何实现大小写不敏感匹配?有一些关于解决方案的主题,但是我太新手了,无法理解。
  2. 如何仅返回匹配的字符串,而不必额外添加%W?类似于Java中的"(?=...)"

如果您能提供模式或相关函数,将不胜感激。

点赞
用户40691
用户40691

你可以尝试调整此语法:

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

请注意,大小写不敏感很难通过词法分析器实现,因此每个字母都必须有一个单独的规则 - 最终需要更多这样的规则。

此语法处理示例中的注释并跳过它们以及空格符,因此“应返回”后的匹配不会出现在输出中。

您可以调整prefixident规则以指定对象名称中的其他前缀和允许的字符。

注意:!.表示文件结束。 !%nl表示“不是行尾”。 !p&p构建非消耗模式,即在匹配时不会增加当前输入指针(仅对输入进行测试)。

注意2:使用unpack进行print是一个不好的方法。

注意3:这是一个LPEG可追踪的 re,可用于调试语法。在re.compile的第三个参数中传递true以获取测试/匹配/跳过每个规则和位置访问时的执行跟踪。

2016-06-28 16:50:12
用户3754826
用户3754826

最终我得到了一个解决方案,但不够优雅,即在re.compilere.findre.matchre.gsub函数中添加一个额外的参数case_insensitive。当参数值为true时,调用case_insensitive_pattern重写模式:

...
local fmt="[%s%s]"
local function case_insensitive_pattern(quote,pattern)
    -- 查找可选的 '%' (group 1),后跟任何字符 (group 2)
    local stack={}
    local is_letter=nil
    local p = pattern:gsub("(%%?)(.)",
        function(percent, letter)
            if percent ~= "" or not letter:match("%a") then
                -- 如果 '%' 匹配,或者`letter`不是字母,则"原样返回"
                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
                -- 否则,返回大小写不敏感的字符类所匹配的字母
                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   -- 已编译
  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
...
2016-07-01 17:40:31