diff options
Diffstat (limited to 'nomsu_decompiler.moon')
| -rw-r--r-- | nomsu_decompiler.moon | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/nomsu_decompiler.moon b/nomsu_decompiler.moon new file mode 100644 index 0000000..df1c4d6 --- /dev/null +++ b/nomsu_decompiler.moon @@ -0,0 +1,319 @@ +{:NomsuCode} = require "code_obj" +{:find, :sub, :match} = string +{:R,:P,:S} = require 'lpeg' +re = require 're' + +MAX_LINE = 90 + +-- Parsing helper functions +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")) +operator_patt = S("'`~!@$^&*+=|<>?/-")^1 * -1 +identifier_patt = (R("az","AZ","09") + P("_") + utf8_char_patt)^1 * -1 + +is_operator = (s)-> + return not not operator_patt\match(s) + +is_identifier = (s)-> + return not not identifier_patt\match(s) + +inline_escaper = re.compile("{~ (%utf8_char / ('\"' -> '\\\"') / ('\n' -> '\\n') / ('\t' -> '\\t') / ('\b' -> '\\b') / ('\a' -> '\\a') / ('\v' -> '\\v') / ('\f' -> '\\f') / ('\r' -> '\\r') / ('\\' -> '\\\\') / ([^ -~] -> escape) / .)* ~}", {utf8_char: utf8_char_patt, escape:(=> ("\\%03d")\format(@byte!))}) +inline_escape = (s)-> + return inline_escaper\match(s) + +escaper = re.compile("{~ (%utf8_char / ('\\' -> '\\\\') / [\n\r\t -~] / (. -> escape))* ~}", + {utf8_char: utf8_char_patt, escape:(=> ("\\%03d")\format(@byte!))}) +escape = (s)-> + return escaper\match(s) + +tree_to_inline_nomsu = (tree)-> + switch tree.type + when "Action" + nomsu = NomsuCode(tree.source) + if tree.target + inline_target = tree_to_inline_nomsu(tree.target) + if tree.target.type == "Action" + inline_target\parenthesize! + nomsu\append inline_target, "::" + + for i,bit in ipairs tree + if type(bit) == "string" + clump_words = (type(tree[i-1]) == 'string' and is_operator(bit) != is_operator(tree[i-1])) + nomsu\append " " if i > 1 and not clump_words + nomsu\append bit + else + arg_nomsu = tree_to_inline_nomsu(bit) + if bit.type == "Block" + if i > 1 and i < #tree + nomsu\append " " + unless i == #tree + arg_nomsu\parenthesize! + else + nomsu\append " " if i > 1 + if bit.type == "Action" + arg_nomsu\parenthesize! + nomsu\append arg_nomsu + return nomsu + + when "EscapedNomsu" + inner_nomsu = tree_to_inline_nomsu(tree[1]) + unless tree[1].type == "List" or tree[1].type == "Dict" or tree[1].type == "Var" + inner_nomsu\parenthesize! + return NomsuCode(tree.source, "\\", inner_nomsu) + + when "Block" + nomsu = NomsuCode(tree.source, ":") + for i,line in ipairs tree + nomsu\append(i == 1 and " " or "; ") + nomsu\append tree_to_inline_nomsu(line) + nomsu\parenthesize! if #tree > 1 + return nomsu + + when "Text" + add_text = (nomsu, tree)-> + for i, bit in ipairs tree + if type(bit) == 'string' + escaped = inline_escape(bit) + nomsu\append inline_escape(bit) + elseif bit.type == "Text" + add_text(nomsu, bit) + else + interp_nomsu = tree_to_inline_nomsu(bit) + if bit.type != "Var" and bit.type != "List" and bit.type != "Dict" + interp_nomsu\parenthesize! + elseif bit.type == "Var" and type(tree[i+1]) == 'string' and not match(tree[i+1], "^[ \n\t,.:;#(){}[%]]") + interp_nomsu\parenthesize! + nomsu\append "\\", interp_nomsu + nomsu = NomsuCode(tree.source) + add_text(nomsu, tree) + return NomsuCode(tree.source, '"', nomsu, '"') + + when "List", "Dict" + nomsu = NomsuCode(tree.source, (tree.type == "List" and "[" or "{")) + for i, item in ipairs tree + nomsu\append ", " if i > 1 + nomsu\append tree_to_inline_nomsu(item) + nomsu\append(tree.type == "List" and "]" or "}") + return nomsu + + when "DictEntry" + key, value = tree[1], tree[2] + nomsu = if key.type == "Text" and #key == 1 and is_identifier(key[1]) + NomsuCode(key.source, key[1]) + else tree_to_inline_nomsu(key) + nomsu\parenthesize! if key.type == "Action" or key.type == "Block" + assert(value.type != "Block", "Didn't expect to find a Block as a value in a dict") + nomsu\append ":" + if value + value_nomsu = tree_to_inline_nomsu(value) + value_nomsu\parenthesize! if value.type == "Block" + nomsu\append value_nomsu + return nomsu + + when "IndexChain" + nomsu = NomsuCode(tree.source) + for i, bit in ipairs tree + nomsu\append "." if i > 1 + local bit_nomsu + bit_nomsu = if i > 1 and bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string' and is_identifier(bit[1]) + bit[1] + else tree_to_inline_nomsu(bit) + assert bit.type != "Block" + if bit.type == "Action" or bit.type == "IndexChain" or (bit.type == "Number" and i < #tree) + bit_nomsu\parenthesize! + nomsu\append bit_nomsu + return nomsu + + when "Number" + return NomsuCode(tree.source, tostring(tree[1])) + + when "Var" + return NomsuCode(tree.source, "%", tree[1]) + + when "FileChunks" + error("Can't inline a FileChunks") + + when "Comment" + -- TODO: implement? + return nil + + when "Error" + error("Can't compile errors") + + else + error("Unknown type: #{tree.type}") + +tree_to_nomsu = (tree)-> + nomsu = NomsuCode(tree.source) + + -- For concision: + recurse = (t)-> + space = MAX_LINE - nomsu\trailing_line_len! + inline = true + for subtree in coroutine.wrap(-> (t\map(coroutine.yield) and nil)) + if subtree.type == "Block" + if #subtree > 1 or #tree_to_inline_nomsu(subtree)\text! > 20 + inline = false + + if inline + inline_nomsu = tree_to_inline_nomsu(t) + if #inline_nomsu\text! <= space + if t.type == "Action" + inline_nomsu\parenthesize! + return inline_nomsu + indented = tree_to_nomsu(t) + if t.type == "Action" + if indented\is_multiline! + return NomsuCode(t.source, "(..)\n ", indented) + else indented\parenthesize! + return indented + + switch tree.type + when "FileChunks" + should_clump = (prev_line, line)-> + if prev_line.type == "Action" and line.type == "Action" + if prev_line.stub == "use" then return line.stub == "use" + if prev_line.stub == "test" then return true + if line.stub == "test" then return false + return not recurse(prev_line)\is_multiline! + + for chunk_no, chunk in ipairs tree + nomsu\append "\n\n#{("~")\rep(80)}\n\n" if chunk_no > 1 + if chunk.type == "Block" + for line_no, line in ipairs chunk + if line_no > 1 + if should_clump(chunk[line_no-1], line) + nomsu\append "\n" + else + nomsu\append "\n\n" + nomsu\append tree_to_nomsu(line) + else + nomsu\append tree_to_nomsu(chunk) + nomsu\append('\n') unless nomsu\match("\n$") + return nomsu + + when "Action" + next_space = "" + if tree.target + target_nomsu = recurse(tree.target) + if (tree.target.type == "Block" or tree.target.type == "EscapedNomsu") and not target_nomsu\is_multiline! + target_nomsu\parenthesize! + nomsu\append target_nomsu + nomsu\append(target_nomsu\is_multiline! and "\n..::" or "::") + + for i,bit in ipairs tree + if type(bit) == "string" + unless next_space == " " and (type(tree[i-1]) == 'string' and is_operator(tree[i-1]) != is_operator(bit)) + nomsu\append next_space + nomsu\append bit + next_space = nomsu\trailing_line_len! > MAX_LINE and " \\\n.." or " " + else + bit_nomsu = recurse(bit) + if i < #tree and (bit.type == "Block" or bit.type == "EscapedNomsu") and not bit_nomsu\is_multiline! + bit_nomsu\parenthesize! + + if next_space == " " and not bit_nomsu\is_multiline! and nomsu\trailing_line_len! + #bit_nomsu\text! > MAX_LINE + next_space = " \\\n.." + unless next_space == " " and bit.type == "Block" + nomsu\append next_space + + nomsu\append bit_nomsu + next_space = bit_nomsu\is_multiline! and "\n.." or " " + + return nomsu + + when "EscapedNomsu" + return NomsuCode tree.source, "\\", recurse(tree[1]) + + when "Block" + for i, line in ipairs tree + line_nomsu = tree_to_nomsu(line) + nomsu\append line_nomsu + if i < #tree + -- number of lines > 2 (TODO: improve this) + nomsu\append(line_nomsu\match('\n[^\n]*\n') and "\n\n" or "\n") + return NomsuCode(tree.source, ":\n ", nomsu) + + when "Text" + -- Multi-line text has more generous wrap margins + max_line = math.floor(1.25*MAX_LINE) + add_text = (tree)-> + for i, bit in ipairs tree + if type(bit) == 'string' + bit = escape(bit) + for j, line in ipairs bit\lines! + if j > 1 + nomsu\append "\n" + elseif #line > 10 and nomsu\trailing_line_len! > max_line + nomsu\append "\\\n.." + + while #line > 0 + space = max_line - nomsu\trailing_line_len! + split = find(line, "[%p%s]", space) + if not split or split > space + 10 + split = space + 10 + if #line - split < 10 + split = #line + bite, line = sub(line, 1, split), sub(line, split+1, -1) + nomsu\append bite + nomsu\append "\\\n.." if #line > 0 + elseif bit.type == "Text" + add_text(bit) + else + nomsu\append "\\" + interp_nomsu = recurse(bit) + unless interp_nomsu\is_multiline! + if bit.type == "Var" + if type(tree[i+1]) == 'string' and not match(tree[i+1], "^[ \n\t,.:;#(){}[%]]") + interp_nomsu\parenthesize! + elseif bit.type == "EscapedNomsu" or bit.type == "Block" + interp_nomsu\parenthesize! + nomsu\append interp_nomsu + if interp_nomsu\is_multiline! + nomsu\append "\n.." + add_text(tree) + return NomsuCode(tree.source, '"\\\n ..', nomsu, '"') + + when "List", "Dict" + if #tree == 0 + nomsu\append(tree.type == "List" and "[]" or "{}") + return nomsu + for i, item in ipairs tree + item_nomsu = tree_to_inline_nomsu(item) + if #item_nomsu\text! > MAX_LINE + item_nomsu = recurse(item) + elseif item.type == "Block" or item.type == "EscapedNomsu" + item_nomsu\parenthesize! + nomsu\append item_nomsu + if i < #tree + nomsu\append((item_nomsu\is_multiline! or nomsu\trailing_line_len! + #tostring(item_nomsu) >= MAX_LINE) and '\n' or ', ') + return if tree.type == "List" then + NomsuCode(tree.source, "[..]\n ", nomsu) + else + NomsuCode(tree.source, "{..}\n ", nomsu) + + when "DictEntry" + key, value = tree[1], tree[2] + nomsu = if key.type == "Text" and #key == 1 and is_identifier(key[1]) + NomsuCode(key.source, key[1]) + else tree_to_inline_nomsu(key) + nomsu\parenthesize! if key.type == "Block" + value_nomsu = tree_to_nomsu(value) + if (value.type == "Block" or value.type == "EscapedNomsu") and not value_nomsu\is_multiline! + value_nomsu\parenthesize! + nomsu\append ": ", value_nomsu + return nomsu + + when "Comment" + nomsu\append "#", tree[1]\gsub("\n", "\n ") + return nomsu + + when "IndexChain", "Number", "Var", "Comment", "Error" + return tree_to_inline_nomsu tree + + else + error("Unknown type: #{tree.type}") + +return {:tree_to_nomsu, :tree_to_inline_nomsu} |
