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