lua-users home
lua-l archive

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


Hi,
I used Lua for some years and I'm currently in need of 
explicit global declarations.

Roberto Ierusalimschy wrote:
> Follows a nice script to detect missing "local" declarations and other
> "wrong" uses of global variables.
> 
> Simply put it in your path and add  require"strict" to your program
> (or call "lua -lstrict").

Your code looks good, but I want to declare every global variables explicitly, 
in all of my Lua code. Also, I want to allow nil values for globals.
So I made up an another alternative, including several part of your idea.

features
- explicit global declarations by,
  global "myvar"
- global variables can be nil
  (implicitly declared global variables are automatically registered as "declared")
- user can select implicit or explicit variable declaration on main chunk
  (default behavior is : "allow implicit declaration on main chunk")
- explicit declarations can be "off" by calling use_implicit_globals()
- functions can implicitly declared only on main chunk

- if you want to run "explicitly" created lua codes without this module, 
  just define these functions to avoid any error.

  function global() end
  function use_explicit_globals() end
  function use_implicit_globals() end



A declaration is implicit or explicit, depending on where the 
declaration occurs. (extending Roberto's logic)

----------------------------     C    | main chunk |  other(in function)
-------------------------------------------------------------------------
normal Lua ----------------- implicit |  implicit  | implicit
use_explicit_globals() ----- implicit |  implicit  | explicit
use_explicit_globals(true) and
-----value is not function - implicit |  explicit  | explicit
---------value is function - implicit |  implicit  | explicit


some drawbacks:
- it may leads to bad condition if user change the global table manually by setfenv.
- an implicitly-declared-global-variables, which value is nil 
  when use_explicit_globals() is called, can't be registered.


Any feedback is welcome.


----------- example ------------

require "explicit_globals"
use_explicit_globals(true) -- use explicit declaration, even for main chunk

--- OK case
global "my_global_variable"
my_global_variable = 1
local a = my_global_variable

--- NG case
my_undeclared_variable = 1 -- NG
local a = my_undeclared_variable2 -- NG

--- functions declarations
function undeclared_global_func() end  --- ok
undeclared_global_func2 = function() end  --- ok

function test()
  global "my_global_func"
  function my_global_func() end --- ok

  function undeclared_global_func3() end  --- NG
end
test()


----------- explicit_globals.lua ------------

-- table for checking global var declaration
local gdef = {}
local gdef_enabled = nil

-- the function to define global vars
function global(n)
  if not gdef_enabled then
    error("calling function global() when explicit-globals not enabled",2)
  end
  if gdef[n] == true then
    error("re-definition of global \"" .. tostring(n) .. "\"",2)
  end
  gdef[n] = true
  return
end

local function registerglobal(t,n,v)
  gdef[n] = true
  rawset(t,n,v)
end

-- forbid implicit definition of global vars
function use_explicit_globals( is_explicit_in_main_chunk )

  -- set metatable for gloval environment
  local mt = getmetatable(_G)
  if mt == nil then
    mt = {}
    setmetatable(_G, mt)
  end

  -- include implicitly-declared globals in gdef table, 
  -- in case some of it becomes nil and __index,__newindex be called.
  -- drawback: if the value is nil now, it will not be included.
  for k,v in pairs(_G) do
    gdef[k] = true
  end

  mt.__newindex = function (t,n,v)
    if gdef[n] ~= nil then                 -- declared global
      rawset(t,n,v);return
    end
    local w = debug.getinfo(2,"S").what
    if w == "C" then                       -- in C chunk
      registerglobal(t,n,v);return
    end
    if ( not is_explicit_in_main_chunk and w == "main" ) then -- in main chunk
      registerglobal(t,n,v);return
    end
    if type(v) == "function" then
      -- funtcion value
      if w == "main" then 
        -- declatration of functions in main chunk is ok.
        registerglobal(t,n,v);return
      else
        error("assignment of undeclared global function \"" .. tostring(n) .. "\" outside of main chunk. use global(\"var\", val)", 2)
      end
    else
      -- not function value
      error("assignment of undeclared global \"" .. tostring(n) .. "\". use global(\"var\", val)", 2)
    end
  end
  mt.__index = function (t,n)
    if gdef[n] == nil and debug.getinfo(2,"S").what ~= "C" then
      error("attempt to use undeclared global \"" .. tostring(n) .. "\". use global(\"var\", val)", 2)
    end
    return rawget(t, n)
  end
  gdef_enabled = true
end

-- back to normal, implicit global declaration.
function use_implicit_globals()
  local mt = getmetatable(_G)
  if mt ~= nil then
    mt.__newindex = nil
    mt.__index = nil
  end
  gdef_enabled = nil
end

----------- explicit_globals.lua ------------

-- 
Makoto Hamanaka <naraxx800@yahoo.co.jp>