code / nomsu

Lines6.6K Lua5.1K PEG1.3K make117
2 others 83
Markdown60 Bourne Again Shell23
(705 lines)
1 local NomsuCode
2 NomsuCode = require("code_obj").NomsuCode
3 local find, sub, match
4 do
5 local _obj_0 = string
6 find, sub, match = _obj_0.find, _obj_0.sub, _obj_0.match
7 end
8 local R, P, S
9 do
10 local _obj_0 = require('lpeg')
11 R, P, S = _obj_0.R, _obj_0.P, _obj_0.S
12 end
13 local re = require('re')
14 local pretty_error = require("pretty_errors")
15 local MAX_LINE = 80
16 local GOLDEN_RATIO = ((math.sqrt(5) - 1) / 2)
17 local utf8_char_patt = (R("\194\223") * R("\128\191") + R("\224\239") * R("\128\191") * R("\128\191") + R("\240\244") * R("\128\191") * R("\128\191") * R("\128\191"))
18 local operator_char = S("#'`~@^&*+=<>?/%!|\\-") + (P("\xE2") * (R("\x88\x8B") + R("\xA8\xAB")) * R("\128\191"))
19 local operator_patt = operator_char ^ 1 * -1
20 local identifier_patt = (R("az", "AZ", "09") + P("_") + (-operator_char * utf8_char_patt)) ^ 1 * -1
21 local is_operator
22 is_operator = function(s)
23 return type(s) == 'string' and not not operator_patt:match(s)
24 end
25 local is_identifier
26 is_identifier = function(s)
27 return type(s) == 'string' and not not identifier_patt:match(s)
28 end
29 local can_be_unary
30 can_be_unary = function(t)
31 return t.type == "Action" and #t == 2 and is_operator(t[1]) and type(t[2]) ~= 'string' and t[2].type ~= "Block" and not (t[2].type == "Number" and t[1] == "-")
32 end
33 local inline_escaper = re.compile("{~ (%utf8_char / ('\"' -> '\\\"') / ('\n' -> '\\n') / ('\t' -> '\\t') / ('\b' -> '\\b') / ('\a' -> '\\a') / ('\v' -> '\\v') / ('\f' -> '\\f') / ('\r' -> '\\r') / ('\\' -> '\\\\') / ([^ -~] -> escape) / .)* ~}", {
34 utf8_char = utf8_char_patt,
35 escape = (function(self)
36 return ("\\%03d"):format(self:byte())
37 end)
38 })
39 local inline_escape
40 inline_escape = function(s)
41 return inline_escaper:match(s)
42 end
43 local escaper = re.compile("{~ (%utf8_char / ('\\' -> '\\\\') / [\n\r\t -~] / (. -> escape))* ~}", {
44 utf8_char = utf8_char_patt,
45 escape = (function(self)
46 return ("\\%03d"):format(self:byte())
47 end)
48 })
49 local escape
50 escape = function(s)
51 return escaper:match(s)
52 end
53 local tree_to_inline_nomsu
54 tree_to_inline_nomsu = function(tree)
55 local _exp_0 = tree.type
56 if "Action" == _exp_0 then
57 local nomsu = NomsuCode:from(tree.source)
58 if can_be_unary(tree) then
59 nomsu:add(tree[1])
60 local arg_nomsu = tree_to_inline_nomsu(tree[2])
61 if tree[2].type == "MethodCall" or tree[2].type == "Action" then
62 arg_nomsu:parenthesize()
63 end
64 nomsu:add(arg_nomsu)
65 return nomsu
66 end
67 local num_args, num_words = 0, 0
68 for i, bit in ipairs(tree) do
69 if type(bit) == "string" then
70 num_words = num_words + 1
71 local clump_words
72 if type(tree[i - 1]) == 'string' then
73 clump_words = is_operator(bit) ~= is_operator(tree[i - 1])
74 else
75 clump_words = bit == "'"
76 end
77 if i > 1 and not clump_words then
78 nomsu:add(" ")
79 end
80 nomsu:add(bit)
81 else
82 num_args = num_args + 1
83 local arg_nomsu = tree_to_inline_nomsu(bit)
84 if tree[i + 1] == "'" and bit.type == "Action" and can_be_unary(bit) then
85 arg_nomsu:parenthesize()
86 end
87 if bit.type == "Block" then
88 if i ~= #tree then
89 if i > 1 then
90 nomsu:add(" ")
91 end
92 arg_nomsu:parenthesize()
93 end
94 else
95 if i > 1 then
96 nomsu:add(" ")
97 end
98 if bit.type == "MethodCall" then
99 arg_nomsu:parenthesize()
100 elseif bit.type == "Action" and not can_be_unary(bit) then
101 arg_nomsu:parenthesize()
102 end
103 end
104 nomsu:add(arg_nomsu)
105 end
106 end
107 if num_args == 1 and num_words == 0 then
108 nomsu:add("()")
109 end
110 return nomsu
111 elseif "MethodCall" == _exp_0 then
112 local target_nomsu = tree_to_inline_nomsu(tree[1])
113 if tree[1].type == "Block" then
114 target_nomsu:parenthesize()
115 end
116 local nomsu = NomsuCode:from(tree.source, target_nomsu, ", ")
117 if #tree > 2 then
118 nomsu:add("(")
119 end
120 for i = 2, #tree do
121 if i > 2 then
122 nomsu:add("; ")
123 end
124 nomsu:add(tree_to_inline_nomsu(tree[i]))
125 end
126 if #tree > 2 then
127 nomsu:add(")")
128 end
129 return nomsu
130 elseif "EscapedNomsu" == _exp_0 then
131 local inner_nomsu = tree_to_inline_nomsu(tree[1])
132 if not (tree[1].type == "List" or tree[1].type == "Dict" or tree[1].type == "Var") then
133 inner_nomsu:parenthesize()
134 end
135 return NomsuCode:from(tree.source, "\\", inner_nomsu)
136 elseif "Block" == _exp_0 then
137 local nomsu = NomsuCode:from(tree.source, ":")
138 for i, line in ipairs(tree) do
139 nomsu:add(i == 1 and " " or "; ")
140 nomsu:add(tree_to_inline_nomsu(line))
141 end
142 if #tree > 1 then
143 nomsu:parenthesize()
144 end
145 return nomsu
146 elseif "Text" == _exp_0 then
147 local add_text
148 add_text = function(nomsu, tree)
149 for i, bit in ipairs(tree) do
150 if type(bit) == 'string' then
151 local escaped = inline_escape(bit)
152 nomsu:add(inline_escape(bit))
153 elseif bit.type == "Text" then
154 add_text(nomsu, bit)
155 else
156 local interp_nomsu = tree_to_inline_nomsu(bit)
157 if bit.type ~= "Var" and bit.type ~= "List" and bit.type ~= "Dict" then
158 interp_nomsu:parenthesize()
159 elseif bit.type == "Var" and type(bit[1]) == 'string' and type(tree[i + 1]) == 'string' and not match(tree[i + 1], "^[ \n\t,.:;#(){}[%]]") then
160 interp_nomsu:parenthesize()
161 end
162 nomsu:add("\\", interp_nomsu)
163 end
164 end
165 end
166 local nomsu = NomsuCode:from(tree.source)
167 add_text(nomsu, tree)
168 return NomsuCode:from(tree.source, '"', nomsu, '"')
169 elseif "List" == _exp_0 or "Dict" == _exp_0 then
170 local nomsu = NomsuCode:from(tree.source, (tree.type == "List" and "[" or "{"))
171 for i, item in ipairs(tree) do
172 if i > 1 then
173 nomsu:add(", ")
174 end
175 local item_nomsu = tree_to_inline_nomsu(item, true)
176 if item.type == "MethodCall" or (item.type == "Block" and i < #tree) then
177 item_nomsu:parenthesize()
178 end
179 nomsu:add(item_nomsu)
180 end
181 nomsu:add(tree.type == "List" and "]" or "}")
182 return nomsu
183 elseif "DictEntry" == _exp_0 then
184 local key, value = tree[1], tree[2]
185 local nomsu = NomsuCode:from(tree.source)
186 if key.type ~= "Index" then
187 key = {
188 type = "Index",
189 source = key.source,
190 key
192 end
193 nomsu:add(tree_to_inline_nomsu(key))
194 if value then
195 nomsu:add(" = ")
196 local value_nomsu = tree_to_inline_nomsu(value)
197 if value.type == "Block" or value.type == "Action" or value.type == "MethodCall" then
198 value_nomsu:parenthesize()
199 end
200 nomsu:add(value_nomsu)
201 end
202 return nomsu
203 elseif "Index" == _exp_0 then
204 local key = tree[1]
205 local nomsu = NomsuCode:from(key.source, ".")
206 local key_nomsu
207 if key.type == "Text" and #key == 1 and is_identifier(key[1]) then
208 key_nomsu = key[1]
209 else
210 key_nomsu = tree_to_inline_nomsu(key)
211 end
212 local _exp_1 = key.type
213 if "Block" == _exp_1 or "Action" == _exp_1 or "MethodCall" == _exp_1 or "IndexChain" == _exp_1 then
214 key_nomsu:parenthesize()
215 end
216 return NomsuCode:from(key.source, ".", key_nomsu)
217 elseif "IndexChain" == _exp_0 then
218 local nomsu = NomsuCode:from(tree.source)
219 local target = tree[1]
220 local target_nomsu = tree_to_inline_nomsu(target)
221 local _exp_1 = target.type
222 if "Action" == _exp_1 or "MethodCall" == _exp_1 or "EscapedNomsu" == _exp_1 then
223 target_nomsu:parenthesize()
224 elseif "Number" == _exp_1 then
225 if target_nomsu:text():match("%.") then
226 target_nomsu:parenthesize()
227 end
228 end
229 nomsu:add(target_nomsu)
230 for i = 2, #tree do
231 if tree[i].type ~= "Index" then
232 tree[i] = {
233 type = "Index",
234 source = tree[i].source,
235 tree[i]
237 end
238 nomsu:add(tree_to_inline_nomsu(tree[i]))
239 end
240 return nomsu
241 elseif "Number" == _exp_0 then
242 local n = tostring(tree[1])
243 local s
244 if n:match("^-*0x") then
245 s = n:upper():gsub("0X", "0x")
246 elseif tree.hex and tonumber((n:gsub("_", ""))) < 0 then
247 s = ("-0x%X"):format(-tree[1])
248 elseif tree.hex then
249 s = ("0x%X"):format(tree[1])
250 else
251 s = n
252 end
253 return NomsuCode:from(tree.source, s)
254 elseif "Var" == _exp_0 then
255 local varname = tree[1]
256 if type(varname) == "string" then
257 return NomsuCode:from(tree.source, "$", varname)
258 else
259 return NomsuCode:from(tree.source, "$(", tree_to_inline_nomsu(varname), ")")
260 end
261 elseif "FileChunks" == _exp_0 then
262 return error("Can't inline a FileChunks")
263 elseif "Comment" == _exp_0 then
264 return NomsuCode:from(tree.source)
265 elseif "Error" == _exp_0 then
266 local err_msg = pretty_error({
267 title = "Parse error",
268 error = tree.error,
269 hint = tree.hint,
270 source = tree:get_source_file(),
271 start = tree.source.start,
272 stop = tree.source.stop,
273 filename = tree.source.filename
275 return error(err_msg)
276 else
277 return error("Unknown type: " .. tostring(tree.type))
278 end
279 end
280 local tree_to_nomsu
281 tree_to_nomsu = function(tree)
282 local nomsu = NomsuCode:from(tree.source)
283 local recurse
284 recurse = function(t, argnum)
285 if argnum == nil then
286 argnum = nil
287 end
288 local space = MAX_LINE - nomsu:trailing_line_len()
289 local try_inline = true
290 for subtree in coroutine.wrap(function()
291 return (t:with(coroutine.yield) and nil)
292 end) do
293 local _exp_0 = subtree.type
294 if "Comment" == _exp_0 then
295 try_inline = false
296 elseif "Block" == _exp_0 then
297 if #subtree > 1 then
298 try_inline = false
299 end
300 elseif "Text" == _exp_0 then
301 local indented = tree_to_nomsu(subtree)
302 local indented_lines
304 local _accum_0 = { }
305 local _len_0 = 1
306 local _list_0 = indented:text():lines()
307 for _index_0 = 1, #_list_0 do
308 local line = _list_0[_index_0]
309 if line:match("^ +([^ ].*)") then
310 _accum_0[_len_0] = line
311 _len_0 = _len_0 + 1
312 end
313 end
314 indented_lines = _accum_0
315 end
316 for i = #indented_lines, 1, -1 do
317 if indented_lines[i]:match("^ *\\;$") then
318 table.remove(indented_lines, i)
319 end
320 end
321 if #indented_lines > 1 or (#indented_lines == 1 and #indented_lines[1] > MAX_LINE + 8) then
322 try_inline = false
323 end
324 end
325 end
326 local inline_nomsu
327 if try_inline then
328 inline_nomsu = tree_to_inline_nomsu(t)
329 if t.type == "MethodCall" then
330 inline_nomsu:parenthesize()
331 elseif t.type == "Action" and not can_be_unary(t) then
332 inline_nomsu:parenthesize()
333 end
334 if #inline_nomsu:text() <= space or #inline_nomsu:text() <= 8 then
335 if t.type ~= "Text" then
336 return inline_nomsu
337 end
338 end
339 end
340 local indented = tree_to_nomsu(t)
341 if t.type == "Action" or t.type == "MethodCall" then
342 if indented:is_multiline() then
343 if argnum == nil or argnum == 1 then
344 return NomsuCode:from(t.source, "(\n ", indented, "\n)")
345 else
346 return NomsuCode:from(t.source, "\n ", indented)
347 end
348 elseif argnum and argnum > 1 then
349 return NomsuCode:from(t.source, "\n ", indented)
350 else
351 indented:parenthesize()
352 end
353 end
354 local indented_lines
356 local _accum_0 = { }
357 local _len_0 = 1
358 local _list_0 = indented:text():lines()
359 for _index_0 = 1, #_list_0 do
360 local line = _list_0[_index_0]
361 if line:match("^ +([^ ].*)") then
362 _accum_0[_len_0] = line
363 _len_0 = _len_0 + 1
364 end
365 end
366 indented_lines = _accum_0
367 end
368 if t.type == "Text" then
369 for i = #indented_lines, 1, -1 do
370 if indented_lines[i]:match("^ *\\;$") then
371 table.remove(indented_lines, i)
372 end
373 end
374 end
375 if inline_nomsu and (#inline_nomsu:text() < MAX_LINE or #inline_nomsu:text() <= space) and #indented_lines <= 1 then
376 return inline_nomsu
377 end
378 return indented
379 end
380 local _exp_0 = tree.type
381 if "FileChunks" == _exp_0 then
382 if tree.shebang then
383 nomsu:add(tree.shebang, "\n")
384 end
385 for chunk_no, chunk in ipairs(tree) do
386 if chunk_no > 1 then
387 nomsu:add("\n\n" .. tostring(("~"):rep(80)) .. "\n\n")
388 end
389 if chunk.type == "Block" then
390 nomsu:add(NomsuCode:from(chunk.source, table.unpack(tree_to_nomsu(chunk).bits, 2)))
391 else
392 nomsu:add(tree_to_nomsu(chunk))
393 end
394 end
395 return nomsu
396 elseif "Action" == _exp_0 then
397 if can_be_unary(tree) and not can_be_unary(tree[2]) then
398 nomsu:add(tree[1])
399 nomsu:add(recurse(tree[2]))
400 return nomsu
401 end
402 local next_space = ""
403 local word_buffer = { }
404 local num_args, num_words = 0, 0
405 for i, bit in ipairs(tree) do
406 local _continue_0 = false
407 repeat
408 if type(bit) == "string" then
409 num_words = num_words + 1
410 if #word_buffer > 0 and is_operator(bit) == is_operator(word_buffer[#word_buffer]) then
411 table.insert(word_buffer, " ")
412 end
413 table.insert(word_buffer, bit)
414 _continue_0 = true
415 break
416 end
417 if #word_buffer > 0 then
418 local words = table.concat(word_buffer)
419 if next_space == " " then
420 if nomsu:trailing_line_len() + #words > MAX_LINE and nomsu:trailing_line_len() > 8 then
421 next_space = "\n.."
422 elseif word_buffer[1] == "'" then
423 next_space = ""
424 end
425 end
426 nomsu:add(next_space, words)
427 word_buffer = { }
428 next_space = " "
429 end
430 num_args = num_args + 1
431 local bit_nomsu = recurse(bit, i)
432 if tree[i + 1] == "'" and bit.type == "Action" and not bit_nomsu:is_multiline() and can_be_unary(bit) then
433 bit_nomsu:parenthesize()
434 end
435 if bit.type == "Block" then
436 if not bit_nomsu:is_multiline() and (#bit_nomsu:text() > nomsu:trailing_line_len() * GOLDEN_RATIO and #bit_nomsu:text() > 8) or #bit_nomsu:text() + nomsu:trailing_line_len() > MAX_LINE then
437 bit_nomsu = tree_to_nomsu(bit)
438 end
439 elseif (not bit_nomsu:is_multiline() and nomsu:trailing_line_len() + #bit_nomsu:text() > MAX_LINE and nomsu:trailing_line_len() > 8) then
440 if next_space == " " and #bit_nomsu:text() < MAX_LINE then
441 if i == #tree then
442 bit_nomsu = tree_to_inline_nomsu(bit)
443 next_space = "\n "
444 elseif bit.type == "List" or bit.type == "Dict" then
445 bit_nomsu = tree_to_nomsu(bit)
446 else
447 next_space = "\n.."
448 end
449 elseif bit.type == 'Action' or bit.type == "MethodCall" then
450 bit_nomsu = NomsuCode:from(bit.source, "\n ", tree_to_nomsu(bit))
451 else
452 bit_nomsu = tree_to_nomsu(bit)
453 end
454 end
455 if not (next_space == " " and bit_nomsu:text():match("^[:\n]")) then
456 nomsu:add(next_space)
457 end
458 nomsu:add(bit_nomsu)
459 next_space = (bit.type == "Block" or bit_nomsu:text():matches("\n [^\n]*$")) and "\n.." or " "
460 _continue_0 = true
461 until true
462 if not _continue_0 then
463 break
464 end
465 end
466 if #word_buffer > 0 then
467 local words = table.concat(word_buffer)
468 if next_space == " " then
469 if nomsu:trailing_line_len() + #words > MAX_LINE + 8 and nomsu:trailing_line_len() > 8 then
470 next_space = "\n.."
471 elseif word_buffer[1] == "'" then
472 next_space = ""
473 end
474 end
475 nomsu:add(next_space, words)
476 next_space = " "
477 end
478 if num_args == 1 and num_words == 0 then
479 if next_space ~= " " then
480 nomsu:add(next_space)
481 end
482 nomsu:add("()")
483 end
484 return nomsu
485 elseif "MethodCall" == _exp_0 then
486 local target_nomsu = recurse(tree[1])
487 if tree[1].type == "Block" and not target_nomsu:is_multiline() then
488 target_nomsu:parenthesize()
489 end
490 nomsu:add(target_nomsu, ", ")
491 local inner_nomsu = NomsuCode()
492 for i = 2, #tree do
493 if i > 2 then
494 inner_nomsu:add("\n")
495 end
496 inner_nomsu:add(tree_to_nomsu(tree[i]))
497 end
498 if #tree == 2 and nomsu:trailing_line_len() + #inner_nomsu:text():match("^[^\n]*") < MAX_LINE then
499 nomsu:add(inner_nomsu)
500 else
501 nomsu:add("\n ", inner_nomsu)
502 end
503 return nomsu
504 elseif "EscapedNomsu" == _exp_0 then
505 nomsu = recurse(tree[1])
506 if tree[1].type == 'Block' and not nomsu:is_multiline() then
507 nomsu:parenthesize()
508 end
509 return NomsuCode:from(tree.source, "\\", nomsu)
510 elseif "Block" == _exp_0 then
511 local prev_line, needs_space = nil, { }
512 for i, line in ipairs(tree) do
513 local line_nomsu = tree_to_nomsu(line)
514 if i > 1 then
515 nomsu:add("\n")
516 if tree[i - 1].type ~= "Comment" then
517 needs_space[i] = (line_nomsu:is_multiline() and prev_line:is_multiline())
518 if (tree[i].type == "Comment" or needs_space[i] or needs_space[i - 1] or prev_line:text():match("\n [^\n]*$")) then
519 nomsu:add("\n")
520 end
521 end
522 end
523 nomsu:add(line_nomsu)
524 prev_line = line_nomsu
525 end
526 return NomsuCode:from(tree.source, ":\n ", nomsu)
527 elseif "Text" == _exp_0 then
528 local max_line = MAX_LINE + 8
529 local add_text
530 add_text = function(tree)
531 for i, bit in ipairs(tree) do
532 if type(bit) == 'string' then
533 bit = escape(bit)
534 for j, line in ipairs(bit:lines()) do
535 if j > 1 then
536 if nomsu:text():match(" $") then
537 nomsu:add("\\;")
538 end
539 nomsu:add("\n")
540 elseif #line > 10 and nomsu:trailing_line_len() > max_line then
541 nomsu:add("\\\n..")
542 end
543 while #line > 0 do
544 local space = max_line - nomsu:trailing_line_len()
545 local split = find(line, "[%p%s]", space)
546 if not split or split > space + 16 then
547 split = space + 16
548 end
549 if #line - split < 16 then
550 split = #line
551 end
552 local bite
553 bite, line = sub(line, 1, split), sub(line, split + 1, -1)
554 nomsu:add(bite)
555 if #line > 0 then
556 nomsu:add("\\\n..")
557 end
558 end
559 end
560 elseif bit.type == "Text" then
561 add_text(bit)
562 else
563 nomsu:add("\\")
564 local interp_nomsu = recurse(bit)
565 if interp_nomsu:is_multiline() then
566 local curr_indent = nomsu:text():match("\n( *)[^\n]*$") or nomsu:text():match("^( *)")
567 interp_nomsu = NomsuCode((interp_nomsu:text():gsub("\n", "\n" .. curr_indent)))
568 else
569 local space = max_line - nomsu:trailing_line_len()
570 if bit.type == "Var" then
571 local next_str = tree[i + 1]
572 while type(next_str) == 'table' and next_str.type == 'Text' do
573 next_str = next_str[1]
574 end
575 if type(next_str) == 'string' and not match(next_str, "^[ \n\t,.:;#(){}[%]]") then
576 interp_nomsu:parenthesize()
577 end
578 elseif #interp_nomsu:text() > space then
579 local interp_nomsu2
580 if bit.type == "Action" or bit.type == "MethodCall" then
581 interp_nomsu2 = NomsuCode:from(bit.source, "(\n ", tree_to_nomsu(bit), "\n)")
582 else
583 interp_nomsu2 = tree_to_nomsu(bit)
584 end
585 if #interp_nomsu2:text():lines() > 3 or #interp_nomsu2:text() >= MAX_LINE * GOLDEN_RATIO then
586 local curr_indent = nomsu:text():match("\n( *)[^\n]*$") or nomsu:text():match("^( *)")
587 interp_nomsu2 = NomsuCode((interp_nomsu2:text():gsub("\n", "\n" .. curr_indent)))
588 interp_nomsu = interp_nomsu2
589 else
590 nomsu:add("\n..\\")
591 if bit.type == "EscapedNomsu" or bit.type == "Block" or bit.type == "IndexChain" then
592 interp_nomsu:parenthesize()
593 end
594 end
595 elseif bit.type == "EscapedNomsu" or bit.type == "Block" or bit.type == "IndexChain" then
596 interp_nomsu:parenthesize()
597 elseif bit.type == "Action" and can_be_unary(bit) then
598 interp_nomsu:parenthesize()
599 end
600 end
601 nomsu:add(interp_nomsu)
602 if interp_nomsu:is_multiline() and bit.type == "Block" then
603 nomsu:add("\n..")
604 end
605 end
606 end
607 end
608 add_text(tree)
609 if nomsu:text():match(" $") then
610 nomsu:add("\\;")
611 end
612 return NomsuCode:from(tree.source, '("\n ', nomsu, '\n")')
613 elseif "List" == _exp_0 or "Dict" == _exp_0 then
614 if #tree == 0 then
615 nomsu:add(tree.type == "List" and "[]" or "{}")
616 return nomsu
617 end
618 if #tree == 1 and tree[1].type == "Block" then
619 local block_lua = recurse(tree[1])
620 if block_lua:is_multiline() then
621 block_lua:add("\n")
622 end
623 if tree.type == "List" then
624 return NomsuCode:from(tree.source, "[", block_lua, "]")
625 else
626 return NomsuCode:from(tree.source, "{", block_lua, "}")
627 end
628 end
629 local sep = ''
630 local prev_item, needs_space = nil, { }
631 for i, item in ipairs(tree) do
632 local item_nomsu
633 if item.type == 'MethodCall' then
634 item_nomsu = recurse(item)
635 elseif item.type == 'Comment' then
636 item_nomsu = tree_to_nomsu(item)
637 if i > 1 then
638 sep = '\n'
639 end
640 elseif item.type == 'Block' and #item == 1 then
641 item_nomsu = tree_to_nomsu(item[1])
642 item_nomsu:prepend(": ")
643 if i > 1 then
644 sep = '\n'
645 end
646 else
647 item_nomsu = tree_to_inline_nomsu(item)
648 if nomsu:trailing_line_len() + #item_nomsu:text() > MAX_LINE then
649 if i > 1 then
650 sep = '\n'
651 end
652 item_nomsu = item.type == "Action" and tree_to_nomsu(item) or recurse(item)
653 end
654 end
655 nomsu:add(sep)
656 if sep == '\n' then
657 if i > 1 and tree[i - 1].type ~= "Comment" then
658 needs_space[i] = (item_nomsu:is_multiline() and prev_item:is_multiline())
659 if (tree[i].type == "Comment" or needs_space[i] or needs_space[i - 1] or prev_item:text():match("\n [^\n]*$")) then
660 nomsu:add("\n")
661 end
662 end
663 end
664 nomsu:add(item_nomsu)
665 prev_item = item_nomsu
666 if item_nomsu:is_multiline() or item.type == 'Comment' or item.type == "Block" or nomsu:trailing_line_len() + #tostring(item_nomsu) >= MAX_LINE then
667 sep = '\n'
668 else
669 sep = ', '
670 end
671 end
672 if tree.type == "List" then
673 return NomsuCode:from(tree.source, "[\n ", nomsu, "\n]")
674 else
675 return NomsuCode:from(tree.source, "{\n ", nomsu, "\n}")
676 end
677 elseif "DictEntry" == _exp_0 then
678 local key, value = tree[1], tree[2]
679 nomsu = NomsuCode:from(tree.source)
680 if key.type ~= "Index" then
681 key = {
682 type = "Index",
683 source = key.source,
684 key
686 end
687 nomsu:add(tree_to_nomsu(key))
688 if value then
689 local value_nomsu = recurse(value)
690 nomsu:add(" = ", value_nomsu)
691 end
692 return nomsu
693 elseif "Comment" == _exp_0 then
694 nomsu:add("###", (tree[1]:gsub("\n", "\n ")))
695 return nomsu
696 elseif "IndexChain" == _exp_0 or "Index" == _exp_0 or "Number" == _exp_0 or "Var" == _exp_0 or "Comment" == _exp_0 or "Error" == _exp_0 then
697 return tree_to_inline_nomsu(tree)
698 else
699 return error("Unknown type: " .. tostring(tree.type))
700 end
701 end
702 return {
703 tree_to_nomsu = tree_to_nomsu,
704 tree_to_inline_nomsu = tree_to_inline_nomsu