  lua-l archive

• Subject: Re: lpeg accumulator
• From: Sean Conner <sean@...>
• Date: Wed, 18 Sep 2013 16:36:19 -0400

```It was thus said that the Great Gavin Wraith once stated:
> I tried to use lpeg.Cf with a non-numeric accumulator (in
> lpeg 0.12) thus:
>
>   #!lua
>   local digit = lpeg.R "09"
>   local num = lpeg.C(digit^1)/tonumber
>   local comma = lpeg.C ","
>   local acc = function (t,x,y)
>                 t = t + x
>                 t = t + y
>               end
>   local vec = lpeg.Cf (num * comma * num, acc)
>   local pat = lpeg.Cc { 0, 0} * (vec + 1)^0
>   local t = pat:match "Try (3,4) plus (100,200)"
>   print ("(", t, ",", t, ")")
>
> but it spat it out with "attempt to index local 't' (a number value)".
> Are there good reasons why lpeg.Cf only works with number values?
>
> You can probably see what I am trying to do. Is the problem my
> poor coding or the type restriction on lpeg.Cf?
> Is the restriction necessary?

Your code doesn't parse what you think it parses.  Here's some code that
does actually parse "Try (3,4) plus (100,200)" correctly:

lpeg = require "lpeg"

-- **************************************************************************
-- This function receives to values (result, value) and an operation.  Based
-- on the operation, the values will either be added, or subtracted.
--
-- Each value is comprised of two pars, "r" and "i" (for lack of a better
-- term).
-- *************************************************************************

function acc(result,op,value)
if op == 'plus' then
result.r = result.r + value.r
result.i = result.i + value.i
elseif op == 'minus' then
result.r = result.r - value.r
result.i = result.i - value.i
end
return result
end

WS     = lpeg.P" "^0			-- define optional white space
number = lpeg.R"09"^1 / tonumber	-- parse a number 

-- -----------------------------------------------------------------
-- This parses a pair of numbers, given as "(r,i)" into a table, with the
-- first number in the "r" field, and the second number in the "i" field.
-- We allow liberal use of whitespace in this parse.
--
-- We could have changed this to:
--
--	pair = WS
--           * lpeg.P"(" * WS
--	     * lpeg.Ct( number * WS * lpeg.P"," * WS * number)
--	     * lpeg.P")" * WS
--
-- Then our table would be structured as:
--
--	{  = r ,  = i }
--
-- It depends upon how the results are used which version you want
-- to go with.  I'm going with the named fields.
-- -----------------------------------------------------------------

pair   = WS
* lpeg.P"(" * WS
* lpeg.Ct(
lpeg.Cg(number,"r")
* WS * lpeg.P"," * WS
* lpeg.Cg(number,"i")
)
* lpeg.P")" * WS

termop = lpeg.P"plus"		-- our "operations"
+ lpeg.P"minus"

-- ----------------------------------------------------------------
-- We skip past the opening "Try" bit (plus any white space), then set up a
-- folding capture.  If Cf() only receives one capture, then that's the
-- result.  If there are two or more, Cf() will then call our function acc()
-- to acculate the results across multiple matches.
--
-- The use of Cg() here will capture our termop, plus the pair and thus,
-- they become the second and third parameters to our acc() funciton.
-- termop could have been written as:
--
--	termop = C(lpeg.P"plus" + lpeg.P"minus")
--
-- and expr changed to read:
--
--	lpeg.Cg(pair * lpeg.Cg(WS * termop * WS * pair)^0,acc)
--
-- But I wrote it this way initially.  It's debatable as to which version is
-- "better".  Both work.
-- -------------------------------------------------------------------

expr   = lpeg.P"Try" * WS
* lpeg.Cf(pair * lpeg.Cg(WS * lpeg.C(termop) * WS * pair)^0,acc)

test = "Try (3,4) plus (100,200)"

x = expr:match(test)
print(x.r,x.i)

test = "Try (3,4) minus (5,5)"
x = expr:match(test)
print(x.r,x.i)

test = "Try (3,4) minus (5,5) plus (100,200)"
x = expr:match(test)
print(x.r,x.i)

-spc (Hope this helps some)

```