Path: Computer > Lua Related Stuff > LuaStrict
LuaStrict
One of the two Lua “features” that really annoy me is the fact that globals don't have to be declared. Locals have, thank you, but globals spring into existence whenever you reference them: bonjour typos. (BTW, the other thing that's driving me nuts is that you can't assign nil to a variable without making it undefined. But that's a different story. Or is it?)
Not all is lost, though. There's a simple code example in the Lua distribution (see etc/strict.lua) that sort of solves this problem, at least for global variables in the main program. I have taken this example, extended it for use with modules and put the code itself into a module.
While doing that I threw in a few other things: there are now two modes (weak, similar to what strict.lua does, and strong which requires full declaration of all globals), there is a customisable error handler and more.
Have a closer look if you also fight against undeclared globals. Here's the code for the module (requires Lua 5.1 or later):
local g=_G
module('Strict')
strict,strong=true,false
function handler(m,n,v)
m=getModulename(m)
if m=='_G' then m=' in main program'
else m=' in module '..m end
if (v~=nil) then g.error('invalid assignment to '..n..m,3)
else g.error(n..' not declared'..m,3) end
end
local function noaccess1() -- for __newindex
local d=g.debug.getinfo(3,'S')
local w=d and d.what or 'C'
if strong then return w~='C' end
return w~='main' and w~='C'
end
local function noaccess2() -- for __index
local d=g.debug.getinfo(3,'S')
local w=d and d.what or 'C'
return w~='C'
end
function declareGlobal(n,v,m)
m=m or g; -- no module means n will go into _G
if not isDeclared(n,m) then -- if not there, go ahead
g.getmetatable(m).__Idle_declared[n]=true
if v~=nil then g.rawset(m,n,v) end
end
end
function registerModule(m)
-- based on Roberto's code in etc/strict.lua
if g.type(m)~='table' then return end
local mt=g.getmetatable(m)
if mt==nil then
mt={}
g.setmetatable(m,mt)
end
if mt.__Idle_declared==nil then mt.__Idle_declared={}
else return end -- already registered
mt.__newindex=function (t,n,v)
if not mt.__Idle_declared[n] then
if strict and noaccess1() then handler(t,n,v)
else mt.__Idle_declared[n]=true end
end
-- the call to g.rawget() seems superfluous but if the call to handler()
-- already initialised n it'll not be overwritten here
if mt.__Idle_declared[n] and g.rawget(t,n)==nil then g.rawset(t,n,v) end
end
mt.__index=function (t,n)
if strict and not mt.__Idle_declared[n] and noaccess2() then handler(t,n,nil)
else return g.rawget(t,n) end
end
end
function getModulename(m)
for k,v in g.pairs(g.package.loaded) do
if m==v then return k end
end
return '<UNKNOWN>'
end
function isDeclared(n,m)
m=m or g;
return g.getmetatable(m).__Idle_declared[n] or g.rawget(m,n)~=nil
end
-- Register loaded modules (may be too much hand-holding for some)
for k,v in g.pairs(g.package.loaded) do registerModule(v) end
There are also a few simple usage examples which will hopefully make up for the total lack of documentation.
You can download all files (the module and examples) in a tidy little archive.
$updated from: LuaStrict.htxt Fri 22 Jan 2010 17:50:08 thomasl$