Module:Arguments: Difference between revisions
m>Mr. Stradivarius (fix undefined next() behaviour bug by checking for metatable.donePairs in the __index metamethod; also, format the module so it fits into 80 characters) |
m>Mr. Stradivarius (memoize nils using a separate nilArgs table; this fixes a bug where nil values were being iterated over with pairs()) |
||
| Line 7: | Line 7: | ||
local arguments = {} | local arguments = {} | ||
-- Generate four different tidyVal functions, so that we don't have to check the | -- Generate four different tidyVal functions, so that we don't have to check the | ||
| Line 76: | Line 74: | ||
luaArgs = frame | luaArgs = frame | ||
end | end | ||
-- Set | -- Set the order of precedence of the argument tables. If the variables are | ||
-- | -- nil, nothing will be added to the table, which is how we avoid clashes | ||
-- | -- between the frame/parent args and the Lua args. | ||
local | local argTables = {fargs} | ||
argTables[#argTables + 1] = pargs | |||
argTables[#argTables + 1] = luaArgs | |||
--[[ | --[[ | ||
| Line 113: | Line 112: | ||
end | end | ||
end | end | ||
--[[ | |||
-- Set up the args, metaArgs and nilArgs tables. args will be the one | |||
-- accessed from functions, and metaArgs will hold the actual arguments. Nil | |||
-- arguments are memoized in nilArgs, and the metatable connects all of them | |||
-- together. | |||
--]] | |||
local args, metaArgs, nilArgs, metatable = {}, {}, {}, {} | |||
setmetatable(args, metatable) | |||
local function mergeArgs(iterator, tables) | local function mergeArgs(iterator, tables) | ||
| Line 124: | Line 132: | ||
for _, t in ipairs(tables) do | for _, t in ipairs(tables) do | ||
for key, val in iterator(t) do | for key, val in iterator(t) do | ||
if metaArgs[key] == nil then | |||
local tidiedVal = tidyVal(key, val) | local tidiedVal = tidyVal(key, val) | ||
if tidiedVal == nil then | if tidiedVal == nil then | ||
nilArgs[key] = true | |||
else | else | ||
metaArgs[key] = tidiedVal | metaArgs[key] = tidiedVal | ||
| Line 136: | Line 143: | ||
end | end | ||
end | end | ||
--[[ | --[[ | ||
-- Define metatable behaviour. Arguments are memoized in the metaArgs table, | -- Define metatable behaviour. Arguments are memoized in the metaArgs table, | ||
-- and are only fetched from the argument tables once. | -- and are only fetched from the argument tables once. Fetching arguments | ||
-- | -- from the argument tables is the most resource-intensive step in this | ||
-- Also, we keep a record in the metatable of when pairs and ipairs have | -- module, so we try and avoid it where possible. For this reason, nil | ||
-- arguments are also memoized, in the nilArgs table. Also, we keep a record | |||
-- in the metatable of when pairs and ipairs have been called, so we do not | |||
-- run pairs and ipairs on the argument tables more than once. We also do | |||
-- not run ipairs on fargs and pargs if pairs has already been run, as all | |||
-- the arguments will already have been copied over. | |||
--]] | --]] | ||
metatable.__index = function (t, key) | metatable.__index = function (t, key) | ||
--[[ | |||
-- Fetches an argument when the args table is indexed. First we check | |||
-- to see if the value is memoized, and if not we try and fetch it from | |||
-- the argument tables. When we check memoization, we need to check | |||
-- metaArgs before nilArgs, as both can be non-nil at the same time. | |||
-- If the argument is not present in metaArgs, we also check whether | |||
-- pairs has been run yet. If pairs has already been run, we return nil. | |||