Jump to content

Module:Repr

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Mr. Stradivarius (talk | contribs) at 12:33, 22 February 2021 (use "value" instead of "v"). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

local defaultSettings = {
	pretty = false;
	tabs = true;
	semicolons = false;
	spaces = 4;
	sortKeys = true;
}
 
-- lua keywords
local keywords = {
	["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,
}
 
local function isLuaIdentifier(str)
	return type(str) == "string"
		-- must be nonempty
		and str:len() > 0
		-- can only contain a-z, A-Z, 0-9 and underscore
		and not str:find("[^%d%a_]")
		-- cannot begin with digit
		and not tonumber(str:sub(1, 1))
		-- cannot be keyword
		and not keywords[str]
end
 
local function reprRecursive(value, reprSettings, indent, depth, shown)
	if type(value) == "string" then
		return (("%q"):format(value):gsub("\\\n", "\\n"))
	elseif type(value) == "number" then
		if value == math.huge then return "math.huge" end
		if value == -math.huge then return "-math.huge" end
		return tostring(tonumber(value))
	elseif type(value) == "boolean" then
		return tostring(value)
	elseif type(value) == "nil" then
		return "nil"
	elseif type(value) == "table" and getmetatable(value) and type(getmetatable(value).__tostring) == "function" then
		return tostring(getmetatable(value).__tostring(value))
	elseif type(value) == "table" then
		if shown[value] then return "{CYCLIC}" end
		shown[value] = true
		local tabs = indent:rep(depth)
		local str = "{" .. (reprSettings.pretty and ("\n" .. indent .. tabs) or "")
		local isArray = true
		for k in pairs(value) do
			if type(k) ~= "number" or k < 1 or k == math.huge or k ~= math.floor(k) then
				isArray = false
				break
			end
		end
		if isArray then
			for i = 1, #value do
				if i ~= 1 then
					str = str .. (reprSettings.semicolons and ";" or ",") .. (reprSettings.pretty and ("\n" .. indent .. tabs) or " ")
				end
				depth = depth + 1
				str = str .. reprRecursive(value[i], reprSettings, indent, depth, shown)
				depth = depth - 1
			end
		else
			local keyOrder = {}
			local keyValueStrings = {}
			for k, v in pairs(value) do
				depth = depth + 1
				local kStr = isLuaIdentifier(k) and k or ("[" .. reprRecursive(k, reprSettings, indent, depth, shown) .. "]")
				local vStr = reprRecursive(v, reprSettings, indent, depth, shown)
				table.insert(keyOrder, kStr)
				keyValueStrings[kStr] = vStr
				depth = depth - 1
			end
			if reprSettings.sortKeys then table.sort(keyOrder) end
			local first = true
			for _, kStr in pairs(keyOrder) do
				if not first then
					str = str .. (reprSettings.semicolons and ";" or ",") .. (reprSettings.pretty and ("\n" .. indent .. tabs) or " ")
				end
				str = str .. ("%s = %s"):format(kStr, keyValueStrings[kStr])
				first = false
			end
		end
		shown[value] = false
		if reprSettings.pretty then
			str = str .. "\n" .. tabs
		end
		str = str .. "}"
		return str
	else
		return "<" .. type(value) .. ">"
	end
end

local function repr(value, reprSettings)
	reprSettings = reprSettings or {}
	for setting, settingValue in pairs(defaultSettings) do
		if reprSettings[setting] == nil then
			reprSettings[setting] = defaultSettings[setting]
		end
	end
	
	local indent = (" "):rep(reprSettings.spaces or defaultSettings.spaces)
	if reprSettings.tabs then
		indent = "\t"
	end

	local depth = 0	
	local shown = {} 

	return reprRecursive(value, reprSettings, indent, depth, shown)
end
 
return {
	isLuaIdentifier = isLuaIdentifier,
	repr = repr,
}