~technomancy/fennel

ref: 8cf5bb8eab82d80ffc2c135d32135d07f06ef61e fennel/fennelview.lua -rw-r--r-- 9.6 KiB
8cf5bb8eAndrey Orst fennelview rewrite 5 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
local type_order = {["function"] = 5, boolean = 2, number = 1, string = 3, table = 4, thread = 7, userdata = 6}
local function sort_keys(_0_0, _1_0)
  local _1_ = _0_0
  local a = _1_[1]
  local _2_ = _1_0
  local b = _2_[1]
  local ta = type(a)
  local tb = type(b)
  if ((ta == tb) and ((ta == "string") or (ta == "number"))) then
    return (a < b)
  else
    local dta = type_order[ta]
    local dtb = type_order[tb]
    if (dta and dtb) then
      return (dta < dtb)
    elseif dta then
      return true
    elseif dtb then
      return false
    else
      return false
    end
  end
end
local function table_kv_pairs(t)
  local assoc_3f = false
  local kv = {}
  local insert = table.insert
  for k, v in pairs(t) do
    if (type(k) ~= "number") then
      assoc_3f = true
    end
    insert(kv, {k, v})
  end
  table.sort(kv, sort_keys)
  if (#kv == 0) then
    return kv, "empty"
  else
    local function _2_()
      if assoc_3f then
        return "table"
      else
        return "seq"
      end
    end
    return kv, _2_()
  end
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
    else
      appearances[t] = ((appearances[t] or 0) + 1)
    end
  end
  return appearances
end
local function save_table(t, seen)
  local seen0 = (seen or {len = 0})
  local id = (seen0.len + 1)
  if not seen0[t] then
    seen0[t] = id
    seen0.len = id
  end
  return seen0
end
local function detect_cycle(t, seen)
  local seen0 = (seen or {})
  seen0[t] = true
  for k, v in pairs(t) do
    if ((type(k) == "table") and (seen0[k] or detect_cycle(k, seen0))) then
      return true
    end
    if ((type(v) == "table") and (seen0[v] or detect_cycle(v, seen0))) then
      return true
    end
  end
  return nil
end
local function visible_cycle_3f(t, options)
  return (options["detect-cycles?"] and detect_cycle(t) and save_table(t, options.seen) and (1 < (options.appearances[t] or 0)))
end
local function table_indent(t, indent, id)
  local opener_length = nil
  if id then
    opener_length = (#tostring(id) + 2)
  else
    opener_length = 1
  end
  return (indent + opener_length)
end
local pp = {}
local function concat_table_lines(elements, options, multiline_3f, indent, table_type, prefix)
  local function _2_()
    if ("seq" == table_type) then
      return "["
    else
      return "{"
    end
  end
  local function _3_()
    local _3_
    if (table_type == "seq") then
      _3_ = options["sequential-length"]
    else
      _3_ = options["associative-length"]
    end
    if (not options["one-line?"] and (multiline_3f or (#elements > _3_) or (indent > 40))) then
      return ("\n" .. string.rep(" ", indent))
    else
      return " "
    end
  end
  local function _4_()
    if ("seq" == table_type) then
      return "]"
    else
      return "}"
    end
  end
  return ((prefix or "") .. _2_() .. table.concat(elements, _3_()) .. _4_())
end
local function pp_associative(t, kv, options, indent, key_3f)
  local multiline_3f = false
  local elements = {}
  local id = options.seen[t]
  if (options.level >= options.depth) then
    return "{...}"
  elseif (id and options["detect-cycles?"]) then
    return ("@" .. id .. "{...}")
  else
    local visible_cycle_3f0 = visible_cycle_3f(t, options)
    local id0 = (visible_cycle_3f0 and options.seen[t])
    local indent0 = table_indent(t, indent, id0)
    local slength = nil
    local function _3_()
      local _2_0 = rawget(_G, "utf8")
      if _2_0 then
        return _2_0.len
      else
        return _2_0
      end
    end
    local function _4_(_241)
      return #_241
    end
    slength = ((options["utf8?"] and _3_()) or _4_)
    local prefix = nil
    if visible_cycle_3f0 then
      prefix = ("@" .. id0)
    else
      prefix = ""
    end
    for i, _6_0 in pairs(kv) do
      local _7_ = _6_0
      local k = _7_[1]
      local v = _7_[2]
      if ((type(k) == "table") or (type(v) == "table")) then
        multiline_3f = true
      end
      local k0 = pp.pp(k, options, (indent0 + 1), true)
      local v0 = pp.pp(v, options, (indent0 + slength(k0) + 1))
      table.insert(elements, (k0 .. " " .. v0))
    end
    return concat_table_lines(elements, options, multiline_3f, indent0, "table", prefix)
  end
end
local function pp_sequence(t, kv, options, indent)
  local multiline_3f = false
  local elements = {}
  local id = options.seen[t]
  if (options.level >= options.depth) then
    return "[...]"
  elseif (id and options["detect-cycles?"]) then
    return ("@" .. id .. "[...]")
  else
    local visible_cycle_3f0 = visible_cycle_3f(t, options)
    local id0 = (visible_cycle_3f0 and options.seen[t])
    local indent0 = table_indent(t, indent, id0)
    local prefix = nil
    if visible_cycle_3f0 then
      prefix = ("@" .. id0)
    else
      prefix = ""
    end
    for _, _3_0 in pairs(kv) do
      local _4_ = _3_0
      local _0 = _4_[1]
      local v = _4_[2]
      if (type(v) == "table") then
        multiline_3f = true
      end
      table.insert(elements, pp.pp(v, options, indent0))
    end
    return concat_table_lines(elements, options, multiline_3f, indent0, "seq", prefix)
  end
end
local function concat_lines(lines, options, indent, one_line_3f)
  if (#lines == 0) then
    if options["empty-as-sequence?"] then
      return "[]"
    else
      return "{}"
    end
  else
    if (not options["one-line?"] and not one_line_3f) then
      return table.concat(lines, ("\n" .. string.rep(" ", indent)))
    else
      local function _2_()
        local tbl_0_ = {}
        for _, line in ipairs(lines) do
          tbl_0_[(#tbl_0_ + 1)] = line:gsub("^%s+", " ")
        end
        return tbl_0_
      end
      return table.concat(_2_())
    end
  end
end
local function pp_metamethod(t, metamethod, options, indent)
  if (options.level >= options.depth) then
    if options["empty-as-sequence?"] then
      return "[...]"
    else
      return "{...}"
    end
  else
    local _ = nil
    local function _2_(_241)
      return visible_cycle_3f(_241, options)
    end
    options["visible-cycle?"] = _2_
    _ = nil
    local lines, force_one_line_3f = metamethod(t, pp.pp, options, indent)
    options["visible-cycle?"] = nil
    local _3_0 = type(lines)
    if (_3_0 == "string") then
      return lines
    elseif (_3_0 == "table") then
      return concat_lines(lines, options, indent, force_one_line_3f)
    else
      local _0 = _3_0
      return error("Error: __fennelview metamethod must return a table of lines")
    end
  end
end
local function pp_table(x, options, indent)
  options.level = (options.level + 1)
  local x0 = nil
  do
    local _2_0 = nil
    if options["metamethod?"] then
      local _3_0 = x
      if _3_0 then
        local _4_0 = getmetatable(_3_0)
        if _4_0 then
          _2_0 = _4_0.__fennelview
        else
          _2_0 = _4_0
        end
      else
        _2_0 = _3_0
      end
    else
    _2_0 = nil
    end
    if (nil ~= _2_0) then
      local metamethod = _2_0
      x0 = pp_metamethod(x, metamethod, options, indent)
    else
      local _ = _2_0
      local _4_0, _5_0 = table_kv_pairs(x)
      if (true and (_5_0 == "empty")) then
        local _0 = _4_0
        if options["empty-as-sequence?"] then
          x0 = "[]"
        else
          x0 = "{}"
        end
      elseif ((nil ~= _4_0) and (_5_0 == "table")) then
        local kv = _4_0
        x0 = pp_associative(x, kv, options, indent)
      elseif ((nil ~= _4_0) and (_5_0 == "seq")) then
        local kv = _4_0
        x0 = pp_sequence(x, kv, options, indent)
      else
      x0 = nil
      end
    end
  end
  options.level = (options.level - 1)
  return x0
end
local function number__3estring(n)
  local _2_0, _3_0, _4_0 = math.modf(n)
  if ((nil ~= _2_0) and (_3_0 == 0)) then
    local int = _2_0
    return tostring(int)
  else
    local _5_
    do
      local frac = _3_0
      _5_ = (((_2_0 == 0) and (nil ~= _3_0)) and (frac < 0))
    end
    if _5_ then
      local frac = _3_0
      return ("-0." .. tostring(frac):gsub("^-?0.", ""))
    elseif ((nil ~= _2_0) and (nil ~= _3_0)) then
      local int = _2_0
      local frac = _3_0
      return (int .. "." .. tostring(frac):gsub("^-?0.", ""))
    end
  end
end
local function colon_string_3f(s)
  return s:find("^[-%w?\\^_!$%&*+./@:|<=>]+$")
end
local function make_options(t, options)
  local defaults = {["associative-length"] = 4, ["detect-cycles?"] = true, ["empty-as-sequence?"] = false, ["metamethod?"] = true, ["one-line?"] = false, ["sequential-length"] = 10, ["utf8?"] = true, depth = 128}
  local overrides = {appearances = count_table_appearances(t, {}), level = 0, seen = {len = 0}}
  for k, v in pairs((options or {})) do
    defaults[k] = v
  end
  for k, v in pairs(overrides) do
    defaults[k] = v
  end
  return defaults
end
pp.pp = function(x, options, indent, key_3f)
  local indent0 = (indent or 0)
  local options0 = (options or make_options(x))
  local tv = type(x)
  local function _3_()
    local _2_0 = getmetatable(x)
    if _2_0 then
      return _2_0.__fennelview
    else
      return _2_0
    end
  end
  if ((tv == "table") or ((tv == "userdata") and _3_())) then
    return pp_table(x, options0, indent0)
  elseif (tv == "number") then
    return number__3estring(x)
  elseif ((tv == "string") and key_3f and colon_string_3f(x)) then
    return (":" .. x)
  elseif (tv == "string") then
    return string.format("%q", x)
  elseif ((tv == "boolean") or (tv == "nil")) then
    return tostring(x)
  else
    return ("#<" .. tostring(x) .. ">")
  end
end
local function fennelview(x, options)
  return pp.pp(x, make_options(x, options), 0)
end
return fennelview