On Sun, May 1, 2011 at 2:10 AM, Ian Millington
<idmillington@gmail.com> wrote:
Task: Extend Lua to add mutator assignments: (+=, -=, etc: we need the
six numerical operators, but clearly once one is done, the pattern
should be pretty trivial for me to copy).
Metalua supports these out of the box. However, I found a bug in the current implementation: when the left-hand-side evaluation causes side-effects, those might be executed more than once. Here's a test showing it:
i = 0; function nexti() i+=1; return i end
t = { 1 }
t[nexti()] += nexti()
assert (#t==1 and t[1] == 3)
The fix is tedious but not very hard, see below. I'll do a maintenance release sometime soon. The relevant line above will compile to the same bytecode as "do local x1, x2 = t, nexti(); local x3 = x1[x2]; x1[x2] = x3 + nexti() end".
This might or might not suit your needs, depending on what you mean by "working at C level": "x+=1" will compile to the same bytecode as "x=x+1", which causes only one number addition:
1 [1] GETGLOBAL 0 -1 ; x
2 [1] ADD 0 0 -2 ; - 1
3 [1] SETGLOBAL 0 -1 ; x
Finally, if by "too much stuff in Metalua" you mean you want to get rid of syntax extensions, just refrain from loading mlp_ext.lua and you'll end up with plain Lua. If you think about performance issues, then there's none once your code is compiled. If compilation time is a big issue, then fair enough, but that's a pretty unusual situation.
-- fixed operator assignment generator, to be patched in compiler/mlp_ext.lua
local function op_assign(kw, op)
local function build_one_assignment(left, right)
if left.tag=='Index' then
local tvar, kvar, cvar =
mlp.gensym 'table', mlp.gensym 'key', mlp.gensym 'content'
return { tag='Do',
{ tag='Local', { tvar, kvar }, { left[1], left[2] } },
{ tag='Local', { cvar }, { { tag='Index', tvar, kvar } } },
{ tag='Set',
{ { tag='Index', tvar, kvar } },
{ { tag='Op', op, cvar, right } } } }
elseif left.tag=='Id' then
return { tag='Set', { left }, { { tag="Op", op, left, right } } }
else error ("invalid left-hand-side expr in assignment") end
end
local function build_all_assignments(left, right)
if #left==1 then return build_one_assignment(left[1], right[1])
else return table.imap (build_one_assignment, left, right) end
end
mlp.lexer :add (kw)
mlp.stat.assignments[kw] = build_all_assignments
end