Question

I'm making a serialization library for Lua, and I'm using LPeg to parse the string. I've got K/V pairs working (with the key explicitly named), but now I'm going to add auto-indexing.

It'll work like so:

@"value"
@"value2"

Will evaluate to

{
  [1] = "value"
  [2] = "value2"
}

I've already got the value matching working (strings, tables, numbers, and Booleans all work perfectly), so I don't need help with that; what I'm looking for is the indexing. For each match of @[value pattern], it should capture the number of @[value pattern]'s found - in other words, I can match a sequence of values ("@"value1" @"value2") but I don't know how to assign them indexes according to the number of matches. If that's not clear enough, just comment and I'll attempt to explain it better.

Here's something of what my current pattern looks like (using compressed notation):

local process = {} -- Process a captured value
  process.number = tonumber
  process.string = function(s) return s:sub(2, -2) end -- Strip of opening and closing tags
  process.boolean = function(s) if s == "true" then return true else return false end

number = [decimal number, scientific notation] / process.number
string = [double or single quoted string, supports escaped quotation characters] / process.string
boolean = P("true") + "false" / process.boolean
table = [balanced brackets] / [parse the table]

type = number + string + boolean + table

at_notation = (P("@") * whitespace * type) / [creates a table that includes the key and value]

As you can see in the last line of code, I've got a function that does this:

k,v matched in the pattern
-- turns into --
{k, v}
-- which is then added into an "entry table" (I loop through it and add it into the return table)
Was it helpful?

Solution

Based on what you've described so far, you should be able to accomplish this using a simple capture and table capture.

Here's a simplified example I knocked up to illustrate:

lpeg = require 'lpeg'
l = lpeg.locale(lpeg)


whitesp = l.space ^ 0
bool_val    = (l.P "true" + "false") / function (s) return s == "true" end
num_val     = l.digit ^ 1 / tonumber
string_val  = '"' * l.C(l.alnum ^ 1) * '"'
val = bool_val + num_val + string_val
at_notation = l.Ct( (l.P "@" * whitesp * val * whitesp) ^ 0 )

local testdata = [[
@"value1"
  @42
@  "value2"
@true
]]

local res = l.match(at_notation, testdata)

The match returns a table containing the contents:

{
  [1] = "value1",
  [2] = 42,
  [3] = "value2",
  [4] = true
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top