fennelview.lua 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. local function view_quote(str)
  2. return ("\"" .. str:gsub("\"", "\\\"") .. "\"")
  3. end
  4. local short_control_char_escapes = {["\11"] = "\\v", ["\12"] = "\\f", ["\13"] = "\\r", ["\7"] = "\\a", ["\8"] = "\\b", ["\9"] = "\\t", ["\n"] = "\\n"}
  5. local long_control_char_esapes
  6. do
  7. local long = {}
  8. for i = 0, 31 do
  9. local ch = string.char(i)
  10. if not short_control_char_escapes[ch] then
  11. short_control_char_escapes[ch] = ("\\" .. i)
  12. long[ch] = ("\\%03d"):format(i)
  13. end
  14. end
  15. long_control_char_esapes = long
  16. end
  17. local function escape(str)
  18. local str = str:gsub("\\", "\\\\")
  19. local str = str:gsub("(%c)%f[0-9]", long_control_char_esapes)
  20. return str:gsub("%c", short_control_char_escapes)
  21. end
  22. local function sequence_key_3f(k, len)
  23. return ((type(k) == "number") and (1 <= k) and (k <= len) and (math.floor(k) == k))
  24. end
  25. local type_order = {["function"] = 5, boolean = 2, number = 1, string = 3, table = 4, thread = 7, userdata = 6}
  26. local function sort_keys(a, b)
  27. local ta = type(a)
  28. local tb = type(b)
  29. if ((ta == tb) and (ta ~= "boolean") and ((ta == "string") or (ta == "number"))) then
  30. return (a < b)
  31. else
  32. local dta = type_order[a]
  33. local dtb = type_order[b]
  34. if (dta and dtb) then
  35. return (dta < dtb)
  36. elseif dta then
  37. return true
  38. elseif dtb then
  39. return false
  40. elseif "else" then
  41. return (ta < tb)
  42. end
  43. end
  44. end
  45. local function get_sequence_length(t)
  46. local len = 1
  47. for i in ipairs(t) do
  48. len = i
  49. end
  50. return len
  51. end
  52. local function get_nonsequential_keys(t)
  53. local keys = {}
  54. local sequence_length = get_sequence_length(t)
  55. for k in pairs(t) do
  56. if not sequence_key_3f(k, sequence_length) then
  57. table.insert(keys, k)
  58. end
  59. end
  60. table.sort(keys, sort_keys)
  61. return keys, sequence_length
  62. end
  63. local function count_table_appearances(t, appearances)
  64. if (type(t) == "table") then
  65. if not appearances[t] then
  66. appearances[t] = 1
  67. for k, v in pairs(t) do
  68. count_table_appearances(k, appearances)
  69. count_table_appearances(v, appearances)
  70. end
  71. end
  72. else
  73. if (t and (t == t)) then
  74. appearances[t] = ((appearances[t] or 0) + 1)
  75. end
  76. end
  77. return appearances
  78. end
  79. local put_value = nil
  80. local function puts(self, ...)
  81. for _, v in ipairs({...}) do
  82. table.insert(self.buffer, v)
  83. end
  84. return nil
  85. end
  86. local function tabify(self)
  87. return puts(self, "\n", self.indent:rep(self.level))
  88. end
  89. local function already_visited_3f(self, v)
  90. return (self.ids[v] ~= nil)
  91. end
  92. local function get_id(self, v)
  93. local id = self.ids[v]
  94. if not id then
  95. local tv = type(v)
  96. id = ((self["max-ids"][tv] or 0) + 1)
  97. self["max-ids"][tv] = id
  98. self.ids[v] = id
  99. end
  100. return tostring(id)
  101. end
  102. local function put_sequential_table(self, t, length)
  103. puts(self, "[")
  104. self.level = (self.level + 1)
  105. for i = 1, length do
  106. puts(self, " ")
  107. put_value(self, t[i])
  108. end
  109. self.level = (self.level - 1)
  110. return puts(self, " ]")
  111. end
  112. local function put_key(self, k)
  113. if ((type(k) == "string") and k:find("^[-%w?\\^_`!#$%&*+./@~:|<=>]+$")) then
  114. return puts(self, ":", k)
  115. else
  116. return put_value(self, k)
  117. end
  118. end
  119. local function put_kv_table(self, t)
  120. puts(self, "{")
  121. self.level = (self.level + 1)
  122. for k, v in pairs(t) do
  123. tabify(self)
  124. put_key(self, k)
  125. puts(self, " ")
  126. put_value(self, v)
  127. end
  128. self.level = (self.level - 1)
  129. tabify(self)
  130. return puts(self, "}")
  131. end
  132. local function put_table(self, t)
  133. if already_visited_3f(self, t) then
  134. return puts(self, "#<table ", get_id(self, t), ">")
  135. elseif (self.level >= self.depth) then
  136. return puts(self, "{...}")
  137. elseif "else" then
  138. local non_seq_keys, length = get_nonsequential_keys(t)
  139. local id = get_id(self, t)
  140. if (self.appearances[t] > 1) then
  141. return puts(self, "#<", id, ">")
  142. elseif ((#non_seq_keys == 0) and (#t == 0)) then
  143. return puts(self, "{}")
  144. elseif (#non_seq_keys == 0) then
  145. return put_sequential_table(self, t, length)
  146. elseif "else" then
  147. return put_kv_table(self, t)
  148. end
  149. end
  150. end
  151. local function _0_(self, v)
  152. local tv = type(v)
  153. if (tv == "string") then
  154. return puts(self, view_quote(escape(v)))
  155. elseif ((tv == "number") or (tv == "boolean") or (tv == "nil")) then
  156. return puts(self, tostring(v))
  157. elseif (tv == "table") then
  158. return put_table(self, v)
  159. elseif "else" then
  160. return puts(self, "#<", tostring(v), ">")
  161. end
  162. end
  163. put_value = _0_
  164. local function fennelview(root, options)
  165. local options = (options or {})
  166. local inspector = {["max-ids"] = {}, appearances = count_table_appearances(root, {}), buffer = {}, depth = (options.depth or 128), ids = {}, indent = (options.indent or " "), level = 0}
  167. put_value(inspector, root)
  168. return table.concat(inspector.buffer)
  169. end
  170. return fennelview