Modul:Repr
Bu modul uchun Modul:Repr/doc nomli hujjat sahifasini yaratishingiz mumkin
require('Module:No globals')
local libraryUtil = require("libraryUtil")
local checkType = libraryUtil.checkType
local checkTypeForNamedArg = libraryUtil.checkTypeForNamedArg
local defaultOptions = {
pretty = false,
tabs = true,
semicolons = false,
spaces = 4,
sortKeys = true,
depth = 0,
}
-- 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 = {
["and"] = true,
["break"] = true,
["do"] = true,
["else"] = true,
["elseif"] = true,
["end"] = true,
["false"] = true,
["for"] = true,
["function"] = true,
["if"] = true,
["in"] = true,
["local"] = true,
["nil"] = true,
["not"] = true,
["or"] = true,
["repeat"] = true,
["return"] = true,
["then"] = true,
["true"] = true,
["until"] = true,
["while"] = true,
}
--[[
-- 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)
end
end
--[[
-- 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 = {prefix, whitespaceAtNextDepth}
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 .. " ") .. suffix
end
--[[
-- 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 = "{"
local suffix = "}"
if context.pretty then
return prettyPrintItemsAtDepth(
items,
prefix,
suffix,
context.separator,
context.indent,
depth
)
else
return renderItems(items, prefix, suffix, context.separator)
end
end
--[[
-- 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 "{CYCLIC}"
end
context.shown[t] = true
local result = renderNormalTable(t, context, depth)
context.shown[t] = false
return result
end
--[[
-- 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 .. ">"
end
end
--[[
-- 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 ret
end
--[[
-- Get the indent from the options table.
--]]
local function getIndent(options)
if options.tabs then
return "\t"
else
return string.rep(" ", options.spaces)
end
end
--[[
-- 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 .. renderedArgs
end
return {
_isLuaIdentifier = isLuaIdentifier,
repr = repr,
invocationRepr = invocationRepr,
}