123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170 |
- local function view_quote(str)
- return ("\"" .. str:gsub("\"", "\\\"") .. "\"")
- end
- local short_control_char_escapes = {["\11"] = "\\v", ["\12"] = "\\f", ["\13"] = "\\r", ["\7"] = "\\a", ["\8"] = "\\b", ["\9"] = "\\t", ["\n"] = "\\n"}
- local long_control_char_esapes
- do
- local long = {}
- for i = 0, 31 do
- local ch = string.char(i)
- if not short_control_char_escapes[ch] then
- short_control_char_escapes[ch] = ("\\" .. i)
- long[ch] = ("\\%03d"):format(i)
- end
- end
- long_control_char_esapes = long
- end
- local function escape(str)
- local str = str:gsub("\\", "\\\\")
- local str = str:gsub("(%c)%f[0-9]", long_control_char_esapes)
- return str:gsub("%c", short_control_char_escapes)
- end
- local function sequence_key_3f(k, len)
- return ((type(k) == "number") and (1 <= k) and (k <= len) and (math.floor(k) == k))
- end
- local type_order = {["function"] = 5, boolean = 2, number = 1, string = 3, table = 4, thread = 7, userdata = 6}
- local function sort_keys(a, b)
- local ta = type(a)
- local tb = type(b)
- if ((ta == tb) and (ta ~= "boolean") and ((ta == "string") or (ta == "number"))) then
- return (a < b)
- else
- local dta = type_order[a]
- local dtb = type_order[b]
- if (dta and dtb) then
- return (dta < dtb)
- elseif dta then
- return true
- elseif dtb then
- return false
- elseif "else" then
- return (ta < tb)
- end
- end
- end
- local function get_sequence_length(t)
- local len = 1
- for i in ipairs(t) do
- len = i
- end
- return len
- end
- local function get_nonsequential_keys(t)
- local keys = {}
- local sequence_length = get_sequence_length(t)
- for k in pairs(t) do
- if not sequence_key_3f(k, sequence_length) then
- table.insert(keys, k)
- end
- end
- table.sort(keys, sort_keys)
- return keys, sequence_length
- end
- local function count_table_appearances(t, appearances)
- if (type(t) == "table") then
- if not appearances[t] then
- appearances[t] = 1
- for k, v in pairs(t) do
- count_table_appearances(k, appearances)
- count_table_appearances(v, appearances)
- end
- end
- else
- if (t and (t == t)) then
- appearances[t] = ((appearances[t] or 0) + 1)
- end
- end
- return appearances
- end
- local put_value = nil
- local function puts(self, ...)
- for _, v in ipairs({...}) do
- table.insert(self.buffer, v)
- end
- return nil
- end
- local function tabify(self)
- return puts(self, "\n", self.indent:rep(self.level))
- end
- local function already_visited_3f(self, v)
- return (self.ids[v] ~= nil)
- end
- local function get_id(self, v)
- local id = self.ids[v]
- if not id then
- local tv = type(v)
- id = ((self["max-ids"][tv] or 0) + 1)
- self["max-ids"][tv] = id
- self.ids[v] = id
- end
- return tostring(id)
- end
- local function put_sequential_table(self, t, length)
- puts(self, "[")
- self.level = (self.level + 1)
- for i = 1, length do
- puts(self, " ")
- put_value(self, t[i])
- end
- self.level = (self.level - 1)
- return puts(self, " ]")
- end
- local function put_key(self, k)
- if ((type(k) == "string") and k:find("^[-%w?\\^_`!#$%&*+./@~:|<=>]+$")) then
- return puts(self, ":", k)
- else
- return put_value(self, k)
- end
- end
- local function put_kv_table(self, t)
- puts(self, "{")
- self.level = (self.level + 1)
- for k, v in pairs(t) do
- tabify(self)
- put_key(self, k)
- puts(self, " ")
- put_value(self, v)
- end
- self.level = (self.level - 1)
- tabify(self)
- return puts(self, "}")
- end
- local function put_table(self, t)
- if already_visited_3f(self, t) then
- return puts(self, "#<table ", get_id(self, t), ">")
- elseif (self.level >= self.depth) then
- return puts(self, "{...}")
- elseif "else" then
- local non_seq_keys, length = get_nonsequential_keys(t)
- local id = get_id(self, t)
- if (self.appearances[t] > 1) then
- return puts(self, "#<", id, ">")
- elseif ((#non_seq_keys == 0) and (#t == 0)) then
- return puts(self, "{}")
- elseif (#non_seq_keys == 0) then
- return put_sequential_table(self, t, length)
- elseif "else" then
- return put_kv_table(self, t)
- end
- end
- end
- local function _0_(self, v)
- local tv = type(v)
- if (tv == "string") then
- return puts(self, view_quote(escape(v)))
- elseif ((tv == "number") or (tv == "boolean") or (tv == "nil")) then
- return puts(self, tostring(v))
- elseif (tv == "table") then
- return put_table(self, v)
- elseif "else" then
- return puts(self, "#<", tostring(v), ">")
- end
- end
- put_value = _0_
- local function fennelview(root, options)
- local options = (options or {})
- local inspector = {["max-ids"] = {}, appearances = count_table_appearances(root, {}), buffer = {}, depth = (options.depth or 128), ids = {}, indent = (options.indent or " "), level = 0}
- put_value(inspector, root)
- return table.concat(inspector.buffer)
- end
- return fennelview
|