• Subject: Re: LPEG: set of flags
• From: Sean Conner <sean@...>
• Date: Mon, 27 Dec 2021 17:46:17 -0500

```It was thus said that the Great Александр Машин once stated:
> Dear all,
>
> is there an LPEG rule to describe a set of characters, in which
> characters can be in any order, and each can be present only once or not
> at all?
>
> Examples, assuming that allowed flags are 'i', 'm', 's':
> * matching:
> ** 'ims',
> ** 'si',
> ** 'i',
> ** '';
> * not matching:
> ** 'iu' (unknown flag),
> ** 'imi' (i repeats twice).

There is no predefined rule to do what you ask, but you can write such a
rule:

local lpeg = require "lpeg"

local function F(flag)
return lpeg.Cmt(
lpeg.P(flag) * lpeg.Carg(1),
function(subject,position,arg)
if not arg[flag] then
arg[flag] = true
return position
end
end
)
end

local chkflags = (F"i" + F"m" + F"s")^0 * lpeg.P(-1)

print("ims" , chkflags:match("ims", 1, {}))
print("si"  , chkflags:match("si",  1, {}))
print("i"   , chkflags:match("i",   1, {}))
print(""    , chkflags:match("",    1, {}))
print("iu"  , chkflags:match("iu",  1, {}))
print("imi" , chkflags:match("imi", 1, {}))

For this to work, you'll need to pass in the starting position and a
table.  The table is used to record the flags we've seen before.  This table
is retrieved by the lpeg.Carg() function.  To ensure the check returns a
match failure, I used lpeg.Cmt() to wrap the flag pattern.  I ended the rule
with an lpeg.P(-1) to ensure all the input has been checked---this is to
catch flags that aren't defined.

I used lpeg.Carg() to make this easier to write and test.  It could be
written without, but it gets a bit more verbose and harder to test:

local flagi
local flagm
local flags

local chkflags = lpeg.Cmt(lpeg.P"i",function(subject,position,capture)
if not flagi then
flagi = true
return position
end
end)
+ lpeg.Cmt(lpeg.P"m", ... ) -- rest left as an exercise
+ lpeg.Cmt(lpeg.P"s", ... ) -- for the reader to finish

print("ims",chkflags:match "ims") flagi = false flagm = false flags = false
print("si" ,chkflags:match "si")  flagi = ... -- left as exercise

There are probably other ways this could be done.  This was just the first
thing that came to mind.

-spc (Hope this helps)
```