lua-users home
lua-l archive

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


On Tue, Sep 13, 2016 at 2:59 PM, Sean Conner <sean@conman.org> wrote:
>
>   Seeing how there's going to be a bug fix for LPeg Real Soon Now (TM), I
> thought it might be a good time to float a proposal for a new lpeg function.
>
>   Some background:  I parse a lot of Internet related messages and URLs
> (email and SIP messages, sip:, tel:, http: and https: URLs, etc.) and it's
> amazing how often name/value pairs keep popping up.  Usually there are a
> fixed number of defined name/value pairs but the grammars almost always
> allow user defined pairs.  Since I use LPeg for all of my parsing needs, I
> like to parse the data into Lua tables and the most problematic part is
> handing open ended name/value pairs.
>
>   Let me give a simplified example:  A simple file of name/value pairs
> (alpha characters only---I want to keep things really simple) one per line,
> name and value separated by an '=' sign; order does not matter.  There are
> two fields defined, "foo" and "bar" (which if not provided, default values
> will be given).  Two examples follow:
>
>         Example 1:
>                 foo=de
>                 bar=true
>                 alpha=Sean
>                 bravo=Conner
>
>         Example 2:
>                 yankee=Sean
>                 zulu=Conner
>                 foo=se

How about this:

---
local lpeg = require"lpeg"

local input = [[
foo=de
bar=true
alpha=Sean
bravo=Conner
]]

local V = lpeg.V
local TABLE = lpeg.Cmt("", function(s,p) return p, {} end)
local function set (t, k, v,...) print("set", t, k, v, ...) t[k] = v end
local grammar = lpeg.locale {
    V "space"^0 * V "list" * V "space"^0;
    name = V "alnum"^1;
    value = V "alnum"^1;
    foo = lpeg.C "foo"  * V "space"^0 * "=" * V "space"^0 * lpeg.C(V "value");
    bar = lpeg.C "bar"  * V "space"^0 * "=" * V "space"^0 * lpeg.C(V "value");
    explicit =  lpeg.Cb "outer" * (V "foo" + V "bar") / set;
    other = lpeg.Cb "other" * lpeg.C(V "name") * V "space"^0 * "=" * V
"space"^0 * lpeg.C(V "value") / set;
    list = lpeg.Cg(TABLE, "outer") * lpeg.Cg(TABLE, "other") * lpeg.Cb
"outer" * (lpeg.Cb "outer" * lpeg.Cc "other" * lpeg.Cb "other" / set)
* ((V "explicit" + V "other") * V "space"^0)^0;
}
patt = lpeg.P(grammar)

print(patt:match(input)) --> {bar = "true", foo = "de", other = {alpha
= "Sean", bravo = "Conner"}}
---

The above should also work perfectly fine in nested invocations. Only
downside is using Cmt to create the table. I think Ct should have
worked, hence the bug report in the other thread.

-- 
Patrick Donnelly