Jump to content

Module:table/compare

From Wiktionary, the free dictionary

local string_linesub_module = "Module:string/linesub" local table_get_metamethod_module = "Module:table/getMetamethod"  local error = error local rawequal = rawequal local require = require local sub = string.sub local traceback = debug.traceback local xpcall = xpcall  local function get_metamethod(...) get_metamethod = require(table_get_metamethod_module) return get_metamethod(...) end  local function linesub(...) linesub = require(string_linesub_module) return linesub(...) end  local _a, _b -- used to pass arguments to compare_for_xpcall() local NOT_COMPARABLE = {} -- sentinel  local function compare_for_xpcall() local a, b = _a, _b _a, _b = nil return a < b end  local function err_handler(err) -- Error message relate to comparing two table values, but the trace at the -- start of the message needs to be trimmed. Traceback will only refer to -- xpcall() on line 5 if the error occured in compare_for_xpcall(); if it -- originated further up the stack (i.e. in a metamethod), xpcall() will -- only be mentioned on a later line. if ( sub(err, -35) == "attempt to compare two table values" and linesub(traceback(), 5, 5) == "\t[C]: in function 'xpcall'" ) then return NOT_COMPARABLE end return err end  --[==[ A comparison function for tables, which returns {true} if {a} sorts before {b}, or otherwise {false}; it can be used as the sort function with {table.sort}.  By default, attempting to compare two tables will result in an error, unless both tables have the same {__lt} metamethod. This function will also use the {__lt} metamethod under the same circumstances, but will return {false} instead of throwing an error if the two tables are not comparable.]==] return function(a, b) -- If `a` and `b` are tables, they are only comparable if they both have the -- same __lt metamethod. get_unprotected_metatable() returns nil if there is -- no metatable, and false if the metatable is protected. local success_a, __lt_a = get_metamethod(a, "__lt") -- Not comparable if `a` has no __lt metamethod. if success_a and __lt_a == nil then return false end local success_b, __lt_b = get_metamethod(b, "__lt") if success_b then -- Not comparable if `b` has no __lt metamethod. if __lt_b == nil then return false -- If `a` and `b` have known __lt metamethods, they are only comparable -- if the two metamethods are primitively equal. elseif __lt_a ~= nil then return rawequal(__lt_a, __lt_b) and a < b end end -- Not possible to know if `a` and `b` are comparable if either or both have -- protected metatables, and neither are confirmed to have no __lt -- metamethod. Use xpcall() when comparing them, with an error handler that -- determines if the error was due to `a` and `b` not being comparable. -- Unlike pcall(), xpcall() can't pass on arguments to the called function, -- so use upvalues in the top-level of the module to circumvent this. _a, _b = a, b local success, result = xpcall(compare_for_xpcall, err_handler) if success then return result elseif result == NOT_COMPARABLE then return false end -- If the error wasn't due to the comparison, rethrow it. error(result) end