lua-users home
lua-l archive

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




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