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)
---- 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" else return tostring(n) endend
---- Whether a table has a __tostring metamethod.--local function hasTostringMetamethod(t) return getmetatable(t) and type(getmetatable(t).__tostring)
---- 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
"boolean" then return tostring(value) elseif valueType
"string" then return renderString(value) elseif valueType
---- 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)
---- 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