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 Sat 18 Jan 2014 13:14:24 thomasl (By Thomas Lauer)$