lua-users home
lua-l archive

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


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[1] = t[1] + x
>                 t[2] = t[2] + 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[1], ",", t[2], ")")
> 
> 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 [1]

-- -----------------------------------------------------------------
-- 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:
--
--	{ [1] = r , [2] = 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)