~technomancy/fennel

fennel/fennelview.lua -rw-r--r-- 11.2 KiB
705eaf84Phil Hagelberg Be more tolerant of weird Lua implementations. 2 hours 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
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
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 (ta < tb)
    end
  end
end
local function max_index_gap(kv)
  local gap = 0
  if (#kv > 0) then
    local _2_ = kv
    local _3_ = _2_[1]
    local i = _3_[1]
    local rest = {(table.unpack or unpack)(_2_, 2)}
    for _, _4_0 in ipairs(rest) do
      local _5_ = _4_0
      local k = _5_[1]
      if ((k - i) > gap) then
        gap = (k - i)
      end
      i = k
    end
  end
  return gap
end
local function fill_gaps(kv)
  do
    local missing_indexes = {}
    local i = 0
    for _, _2_0 in ipairs(kv) do
      local _3_ = _2_0
      local j = _3_[1]
      i = (i + 1)
      while (i < j) do
        table.insert(missing_indexes, i)
        i = (i + 1)
      end
    end
    for _, k in ipairs(missing_indexes) do
      table.insert(kv, k, {k})
    end
  end
  return kv
end
local function table_kv_pairs(t, options)
  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 not assoc_3f then
    local gap = max_index_gap(kv)
    if (max_index_gap(kv) > options["max-sparse-gap"]) then
      assoc_3f = true
    else
      fill_gaps(kv)
    end
  end
  if (#kv == 0) then
    return kv, "empty"
  else
    local function _3_()
      if assoc_3f then
        return "table"
      else
        return "seq"
      end
    end
    return kv, _3_()
  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, _3fk)
  if ("table" == type(t)) then
    seen[t] = true
    local _2_0, _3_0 = next(t, _3fk)
    if ((nil ~= _2_0) and (nil ~= _3_0)) then
      local k = _2_0
      local v = _3_0
      return (seen[k] or detect_cycle(k, seen) or seen[v] or detect_cycle(v, seen) or detect_cycle(t, seen, k))
    end
  end
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 = nil
local function concat_table_lines(elements, options, multiline_3f, indent, table_type, prefix)
  local indent_str = ("\n" .. string.rep(" ", indent))
  local open = nil
  local function _2_()
    if ("seq" == table_type) then
      return "["
    else
      return "{"
    end
  end
  open = ((prefix or "") .. _2_())
  local close = nil
  if ("seq" == table_type) then
    close = "]"
  else
    close = "}"
  end
  local oneline = (open .. table.concat(elements, " ") .. close)
  if (not options["one-line?"] and (multiline_3f or ((indent + #oneline) > options["line-length"]))) then
    return (open .. table.concat(elements, indent_str) .. close)
  else
    return oneline
  end
end
local function pp_associative(t, kv, options, indent, key_3f)
  local multiline_3f = false
  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
    local items = nil
    do
      local tbl_0_ = {}
      for _, _6_0 in pairs(kv) do
        local _7_ = _6_0
        local k = _7_[1]
        local v = _7_[2]
        local _8_
        do
          local k0 = pp(k, options, (indent0 + 1), true)
          local v0 = pp(v, options, (indent0 + slength(k0) + 1))
          multiline_3f = (multiline_3f or k0:find("\n") or v0:find("\n"))
          _8_ = (k0 .. " " .. v0)
        end
        tbl_0_[(#tbl_0_ + 1)] = _8_
      end
      items = tbl_0_
    end
    return concat_table_lines(items, options, multiline_3f, indent0, "table", prefix)
  end
end
local function pp_sequence(t, kv, options, indent)
  local multiline_3f = false
  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
    local items = nil
    do
      local tbl_0_ = {}
      for _, _3_0 in pairs(kv) do
        local _4_ = _3_0
        local _0 = _4_[1]
        local v = _4_[2]
        local _5_
        do
          local v0 = pp(v, options, indent0)
          multiline_3f = (multiline_3f or v0:find("\n"))
          _5_ = v0
        end
        tbl_0_[(#tbl_0_ + 1)] = _5_
      end
      items = tbl_0_
    end
    return concat_table_lines(items, options, multiline_3f, indent0, "seq", prefix)
  end
end
local function concat_lines(lines, options, indent, force_multi_line_3f)
  if (#lines == 0) then
    if options["empty-as-sequence?"] then
      return "[]"
    else
      return "{}"
    end
  else
    local oneline = nil
    local _2_
    do
      local tbl_0_ = {}
      for _, line in ipairs(lines) do
        tbl_0_[(#tbl_0_ + 1)] = line:gsub("^%s+", "")
      end
      _2_ = tbl_0_
    end
    oneline = table.concat(_2_, " ")
    if (not options["one-line?"] and (force_multi_line_3f or oneline:find("\n") or ((indent + #oneline) > options["line-length"]))) then
      return table.concat(lines, ("\n" .. string.rep(" ", indent)))
    else
      return oneline
    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_multi_line_3f = metamethod(t, 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_multi_line_3f)
    else
      local _0 = _3_0
      return 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, options)
      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 = string.gsub(tostring(n), ",", ".")
  return _2_0
end
local function colon_string_3f(s)
  return s:find("^[-%w?^_!$%&*+./@|<=>]+$")
end
local function pp_string(str, options, indent)
  local escs = nil
  local _2_
  if (options["escape-newlines?"] and (#str < (options["line-length"] - indent))) then
    _2_ = "\\n"
  else
    _2_ = "\n"
  end
  local function _4_(_241, _242)
    return ("\\%03d"):format(_242:byte())
  end
  escs = setmetatable({["\""] = "\\\"", ["\11"] = "\\v", ["\12"] = "\\f", ["\13"] = "\\r", ["\7"] = "\\a", ["\8"] = "\\b", ["\9"] = "\\t", ["\\"] = "\\\\", ["\n"] = _2_}, {__index = _4_})
  return ("\"" .. str:gsub("[%c\\\"]", escs) .. "\"")
end
local function make_options(t, options)
  local defaults = {["detect-cycles?"] = true, ["empty-as-sequence?"] = false, ["escape-newlines?"] = false, ["line-length"] = 80, ["max-sparse-gap"] = 10, ["metamethod?"] = true, ["one-line?"] = false, ["prefer-colon?"] = false, ["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
local function _2_(x, options, indent, colon_3f)
  local indent0 = (indent or 0)
  local options0 = (options or make_options(x))
  local tv = type(x)
  local function _4_()
    local _3_0 = getmetatable(x)
    if _3_0 then
      return _3_0.__fennelview
    else
      return _3_0
    end
  end
  if ((tv == "table") or ((tv == "userdata") and _4_())) then
    return pp_table(x, options0, indent0)
  elseif (tv == "number") then
    return number__3estring(x)
  else
    local function _5_()
      if (colon_3f ~= nil) then
        return colon_3f
      elseif ("function" == type(options0["prefer-colon?"])) then
        return options0["prefer-colon?"](x)
      else
        return options0["prefer-colon?"]
      end
    end
    if ((tv == "string") and colon_string_3f(x) and _5_()) then
      return (":" .. x)
    elseif (tv == "string") then
      return pp_string(x, options0, indent0)
    elseif ((tv == "boolean") or (tv == "nil")) then
      return tostring(x)
    else
      return ("#<" .. tostring(x) .. ">")
    end
  end
end
pp = _2_
local function view(x, options)
  return pp(x, make_options(x, options), 0)
end
return view