Module:Repr Explained

require('strict')local libraryUtil = require("libraryUtil")local checkType = libraryUtil.checkTypelocal checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg

local defaultOptions =

-- Define the reprRecursive variable here so that we can call the reprRecursive-- function from renderSequence and renderKeyValueTable without getting-- "Tried to read nil global reprRecursive" errors.local reprRecursive

local luaKeywords =

---- Whether the given value is a valid Lua identifier (i.e. whether it can be-- used as a variable name.)--local function isLuaIdentifier(str) return type(str)

"string" -- Must start with a-z, A-Z or underscore, and can only contain -- a-z, A-Z, 0-9 and underscore and str:find("^[%a_][%a%d_]*$") ~= nil -- Cannot be a Lua keyword and not luaKeywords[str]end

---- Render a string representation.--local function renderString(s) return (("%q"):format(s):gsub("\\\n", "\\n"))end

---- Render a number representation.--local function renderNumber(n) if n

math.huge then return "math.huge" elseif n

-math.huge then return "-math.huge" else return tostring(n) endend

---- Whether a table has a __tostring metamethod.--local function hasTostringMetamethod(t) return getmetatable(t) and type(getmetatable(t).__tostring)

"function"end

---- Pretty print a sequence of string representations.-- This can be made to represent different constructs depending on the values-- of prefix, suffix, and separator. The amount of whitespace is controlled by-- the depth and indent parameters.--local function prettyPrintItemsAtDepth(items, prefix, suffix, separator, indent, depth) local whitespaceAtCurrentDepth = "\n" .. indent:rep(depth) local whitespaceAtNextDepth = whitespaceAtCurrentDepth .. indent local ret = local first = items[1] if first ~= nil then table.insert(ret, first) end for i = 2, #items do table.insert(ret, separator) table.insert(ret, whitespaceAtNextDepth) table.insert(ret, items[i]) end table.insert(ret, whitespaceAtCurrentDepth) table.insert(ret, suffix) return table.concat(ret)end

---- Render a sequence of string representations.-- This can be made to represent different constructs depending on the values of-- prefix, suffix and separator.--local function renderItems(items, prefix, suffix, separator) return prefix .. table.concat(items, separator .. " ") .. suffixend

---- Render a regular table (a non-cyclic table with no __tostring metamethod).-- This can be a sequence table, a key-value table, or a mix of the two.--local function renderNormalTable(t, context, depth) local items =

-- Render the items in the sequence part local seen = for i, value in ipairs(t) do table.insert(items, reprRecursive(t[i], context, depth + 1)) seen[i] = true end -- Render the items in the key-value part local keyOrder = local keyValueStrings = for k, v in pairs(t) do if not seen[k] then local kStr = isLuaIdentifier(k) and k or ("[" .. reprRecursive(k, context, depth + 1) .. "]") local vStr = reprRecursive(v, context, depth + 1) table.insert(keyOrder, kStr) keyValueStrings[kStr] = vStr end end if context.sortKeys then table.sort(keyOrder) end for _, kStr in ipairs(keyOrder) do table.insert(items, string.format("%s = %s", kStr, keyValueStrings[kStr])) end -- Render the table structure local prefix = "" if context.pretty then return prettyPrintItemsAtDepth(items, prefix, suffix, context.separator, context.indent, depth ) else return renderItems(items, prefix, suffix, context.separator) endend

---- Render the given table.-- As well as rendering regular tables, this function also renders cyclic tables-- and tables with a __tostring metamethod.--local function renderTable(t, context, depth) if hasTostringMetamethod(t) then return tostring(t) elseif context.shown[t] then return "" end context.shown[t] = true local result = renderNormalTable(t, context, depth) context.shown[t] = false return resultend

---- Recursively render a string representation of the given value.--function reprRecursive(value, context, depth) if value

nil then return "nil" end local valueType = type(value) if valueType

"boolean" then return tostring(value) elseif valueType

"number" then return renderNumber(value) elseif valueType

"string" then return renderString(value) elseif valueType

"table" then return renderTable(value, context, depth) else return "<" .. valueType .. ">" endend

---- Normalize a table of options passed by the user.-- Any values not specified will be assigned default values.--local function normalizeOptions(options) options = options or local ret = for option, defaultValue in pairs(defaultOptions) do local value = options[option] if value ~= nil then if type(value)

type(defaultValue) then ret[option] = value else error(string.format('Invalid type for option "%s" (expected %s, received %s)', option, type(defaultValue), type(value) ), 3 ) end else ret[option] = defaultValue end end return retend

---- Get the indent from the options table.--local function getIndent(options) if options.tabs then return "\t" else return string.rep(" ", options.spaces) endend

---- Render a string representation of the given value.--local function repr(value, options) checkType("repr", 2, options, "table", true) options = normalizeOptions(options) local context =

context.pretty = options.pretty if context.pretty then context.indent = getIndent(options) else context.indent = "" end if options.semicolons then context.separator = ";" else context.separator = "," end context.sortKeys = options.sortKeys context.shown = local depth = options.depth return reprRecursive(value, context, depth)end

---- Render a string representation of the given function invocation.--local function invocationRepr(keywordArgs) checkType("invocationRepr", 1, keywordArgs, "table") checkTypeForNamedArg("invocationRepr", "funcName", keywordArgs.funcName, "string") checkTypeForNamedArg("invocationRepr", "args", keywordArgs.args, "table", true) checkTypeForNamedArg("invocationRepr", "options", keywordArgs.options, "table", true) local options = normalizeOptions(keywordArgs.options) local depth = options.depth

options.depth = depth + 1 local items = if keywordArgs.args then for _, arg in ipairs(keywordArgs.args) do table.insert(items, repr(arg, options)) end end

local prefix = "(" local suffix = ")" local separator = "," local renderedArgs if options.pretty then renderedArgs = prettyPrintItemsAtDepth(items, prefix, suffix, separator, getIndent(options), depth ) else renderedArgs = renderItems(items, prefix, suffix, separator) end return keywordArgs.funcName .. renderedArgsend

return