[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- 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)