lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]



On 10 Sep 2019, at 18:41, Jonathan Goble <jcgoble3@gmail.com> wrote:

On Mon, Sep 9, 2019 at 11:22 PM Sean Conner <sean@conman.org> wrote:

 Normally I reach for LPEG to handle my parsing chores, but I have a
project where that ... well, I'd be reimplementing a form of regex anyway,
so why not use Lua patterns and avoid that mess.

 But I have an issue that I do not know the answer to---I suspect there
isn't an answer but "use a real regex or LPEG".  But I thought I would ask
anyway.

 I have a pattern that looks like this BNF:

       TEXT    = 'A' - 'Z' / 'a' - 'z'
       DIGIT   = '0' - '9'

       pattern = 1*TEXT [ ';' 1*DIGIT ]

 In English, text, optionally followed by a semicolon and some digits.  So
some valid examples:

       foo
       foo;1
       foo;444

 Invalid examples are

       foo;
       foo23

 If there's a semicolon, it must be followed by a digit; if there's no
semicolon, no digits.  This is trivial (to me) in LPEG.  No so with Lua
patterns.  There's:

       "%a%;?(%d+)

but that allows "foo23" to slip through.  What I would like would be:

       %a(%;(%d+))?

although that doesn't work, since the '?' can (if I'm reading the
documentation right) only follow a character class, not a grouping.

Correct.

 Am I missing something?

 -spc (I mean, besides "using LPEG"?)

I feel like the typical way of doing this kind of thing in Lua is a
two-step process.

Untested example:

result = teststr:match "%a(%;?%d*)"

if result then
   if #result == 0 then
       print "match with no semicolon or number"
   elseif result:match "%;%d+" then
       print "match with semicolon and number"
   else
       print "no match (semicolon without number or vice versa)"
   end
else
   print "no match"
end

Adjust to suit your specific needs.


I also came up with a two-step method but wasn’t sure if it was really applicable in the actual use case. My solution does not seem very elegant but handles Sean’s five examples properly:

> function double_match (s)
>> local text = s:match('^(%a+)$')
>>   if text then
>>     return text, nil
>>   else
>>     return s:match('^(%a+);(%d+)')
>>   end
>> end
> = double_match('foo')
foo nil
> = double_match('foo;1')
foo 1
> = double_match('foo;444')
foo 444
> = double_match('foo;')
nil
> = double_match('foo23')
nil

Peter