Search code examples
lualua-patterns

Pattern ^u.meta(\.|$) not working as expected


I have this pattern:

^u.meta(\.|$)

EXPECTED BEHAVIOUR

^u.meta(\.|$) will match all the roles like:

u.meta
u.meta.admin
u.meta.admin.system
u.meta.*

Where as it should not match something like below:

u.meta_admin
u.meta_admin_system

I have tested this pattern with https://regex101.com/ online regexp tester.

PROBLEM:

I have to implement this pattern with lua script. but getting invalid escape sequence near '\.':

-- lua script

> return  string.match("u.meta.admin", '^u.meta(\.|$)')
stdin:1: invalid escape sequence near '\.'

And I tried adding double \\ as well as removing '\' escape char in that regexp but got nil in return:

-- lua script

> return string.match("u.meta.admin", '^u.meta(\\.|$)')
nil

> return  string.match("u.meta.admin", '^u.meta(.|$)')
nil

Solution

  • See Lua regex docs:

    The character % works as an escape for those magic characters.

    Also, the (...|...) alternation is not supported in Lua. Instead, I guess, you need a word boundary here, like %f[set] frontier pattern:

    %f[set], a frontier pattern; such item matches an empty string at any position such that the next character belongs to set and the previous character does not belong to set. The set set is interpreted as previously described. The beginning and the end of the subject are handled as if they were the character \0.

    So, you can use

    return string.match("u.meta.admin", '^u%.meta%f[%A]')
    

    To only match at the end or before a .:

    return string.match("u.meta", '^u%.meta%f[\0.]')
    

    To match only if the admin is not followed with a letter or an underscore, use a negated character class [^%a_]:

    return string.match("u.meta_admin", '^u%.meta%f[[^%a_]]')
    

    See IDEONE demo to check the difference between the two expressions.

    print(string.match("u.meta", '^u%.meta%f[\0.]')) -- u.meta
    print(string.match("u.meta.admin", '^u%.meta%f[\0.]')) -- u.meta
    print(string.match("u.meta-admin", '^u%.meta%f[\0.]')) -- nil
    print(string.match("u.meta", '^u%.meta%f[%A]')) -- u.meta
    print(string.match("u.meta.admin", '^u%.meta%f[%A]')) -- u.meta
    print(string.match("u.meta-admin", '^u%.meta%f[%A]')) -- u.meta
    -- To exclude a match if `u.admin` is followed with `_`:
    print(string.match("u.meta_admin", '^u%.meta%f[[^%a_]]')) -- nil
    

    NOTE To match the end of the string, instead of \0, you can safely use %z (as @moteus noted in his comment) (see this reference):

    %z    the character with representation 0