This is possible by way of the debug
library - namely the getlocal
and setlocal
functions. If you can't use this library (or access the C API), then you're out of luck.
You can extend your global environment with a specially crafted _L
table, that when accessed performs linear lookups of the current set of locals.
Reading a local variable simply finds a matching variable name, and returns its value. Writing to a local variable requires you to discover its index in the stack frame, and then update the value accordingly. Note that you cannot create new locals.
Here's a simple example that works with Lua 5.1 (but not Lua 5.2+).
local function find_local (key)
local n = 1
local name, sname, sn, value
repeat
name, value = debug.getlocal(3, n)
if name == key then
sname = name
sn = n
end
n = n + 1
until not name
return sname, sn
end
_G._L = setmetatable({}, {
metatable = false,
__newindex = function (self, key, value)
local _, index = find_local(key)
if not index then
error(('local %q does not exist.'):format(key))
end
debug.setlocal(2, index, value)
end,
__index = function (_, key)
return find_local(key)
end
})
In use:
local foo = 'bar'
print(_L['foo']) --> 'bar'
_L['foo'] = 'qux'
print(_L['foo']) --> 'qux'
local function alter_inside (key)
local a, b, c = 5, 6, 7
_L[key] = 11
print(a, b, c)
end
alter_inside('a') --> 11 6 7
alter_inside('b') --> 5 11 7
alter_inside('c') --> 5 6 11
You could write this in a different manner, using plain functions instead of the table combined with read / write operations (__index
, __newindex
).
See §2.4 – Metatables and Metamethods
if the above use of metatables is a brand new topic for you.
In Lua 5.2+, you can use the special _ENV
tables to adjust your current chunk's environment, but note that this is not the same as using local
variables.
local function clone (t)
local o = {}
for k, v in pairs(t) do o[k] = v end
return o
end
local function alter_inside (key)
local _ENV = clone(_ENV)
a = 5
b = 6
c = 7
_ENV[key] = 11
print(a, b, c)
end
alter_inside('a') --> 11 6 7
alter_inside('b') --> 5 11 7
alter_inside('c') --> 5 6 11
As a final note, also consider that this (ab)use of locals might not be the best approach.
You could simply store your variables in a table, when appropriate, to achieve the same results with far less overhead. This approach is highly recommended.
local function alter_inside (key)
-- `ls` is an arbitrary name, always use smart variable names.
local ls = { a = 5, b = 6, c = 7 }
ls[key] = 11
print(ls.a, ls.b, ls.c)
end
alter_inside('a') --> 11 6 7
alter_inside('b') --> 5 11 7
alter_inside('c') --> 5 6 11
Don't dig yourself into a hole trying to solve unnecessary problems.