diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2018-11-08 15:23:22 -0800 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2018-11-08 15:24:15 -0800 |
| commit | 652c29bdef1f0991cc13bef59d6dc78b657ae9a4 (patch) | |
| tree | 8e335399e77b1893657b9fa985db0738034daac3 /nomsu_compiler.lua | |
| parent | 1f3660f393c1a17988a15b89f18686b28e51a9e7 (diff) | |
Major overhaul, splitting nomsu_compiler into nomsu_environment,
nomsu_compiler, and nomsu_decompiler. Also added comprehensions.
Diffstat (limited to 'nomsu_compiler.lua')
| -rw-r--r-- | nomsu_compiler.lua | 1409 |
1 files changed, 181 insertions, 1228 deletions
diff --git a/nomsu_compiler.lua b/nomsu_compiler.lua index c7fa456..0538776 100644 --- a/nomsu_compiler.lua +++ b/nomsu_compiler.lua @@ -2,20 +2,11 @@ local lpeg = require('lpeg') local R, P, S R, P, S = lpeg.R, lpeg.P, lpeg.S local re = require('re') -local Files = require('files') local List, Dict, Text do local _obj_0 = require('containers') List, Dict, Text = _obj_0.List, _obj_0.Dict, _obj_0.Text end -colors = require('consolecolors') -colored = setmetatable({ }, { - __index = function(_, color) - return (function(msg) - return colors[color] .. tostring(msg or '') .. colors.reset - end) - end -}) local insert, remove, concat do local _obj_0 = table @@ -27,15 +18,17 @@ do local _obj_0 = string match, sub, gsub, format, byte, find = _obj_0.match, _obj_0.sub, _obj_0.gsub, _obj_0.format, _obj_0.byte, _obj_0.find end -local NomsuCode, LuaCode, Source +local LuaCode, Source do local _obj_0 = require("code_obj") - NomsuCode, LuaCode, Source = _obj_0.NomsuCode, _obj_0.LuaCode, _obj_0.Source + LuaCode, Source = _obj_0.LuaCode, _obj_0.Source end local SyntaxTree = require("syntax_tree") -local make_parser = require("parser") -local pretty_error = require("pretty_errors") -SOURCE_MAP = { } +local Importer, import_to_1_from, _1_forked +do + local _obj_0 = require('importer') + Importer, import_to_1_from, _1_forked = _obj_0.Importer, _obj_0.import_to_1_from, _obj_0._1_forked +end table.map = function(t, fn) return setmetatable((function() local _accum_0 = { } @@ -47,389 +40,51 @@ table.map = function(t, fn) return _accum_0 end)(), getmetatable(t)) end -table.fork = function(t, values) - return setmetatable(values or { }, { - __index = t +local pretty_error = require("pretty_errors") +local compile_error +compile_error = function(tree, err_msg, hint) + if hint == nil then + hint = nil + end + local err_str = pretty_error({ + title = "Compile error", + error = err_msg, + hint = hint, + source = tree:get_source_code(), + start = tree.source.start, + stop = tree.source.stop, + filename = tree.source.filename }) + return error(err_str, 0) end -table.copy = function(t) - return setmetatable((function() - local _tbl_0 = { } - for k, v in pairs(t) do - _tbl_0[k] = v - end - return _tbl_0 - end)(), getmetatable(t)) -end -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")) -local operator_patt = S("'`~!@$^&*+=|<>?/-") ^ 1 * -1 -local identifier_patt = (R("az", "AZ", "09") + P("_") + utf8_char_patt) ^ 1 * -1 -local is_operator -is_operator = function(s) - return not not operator_patt:match(s) -end -local is_identifier -is_identifier = function(s) - return not not identifier_patt:match(s) -end -local 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 = (function(self) - return ("\\%03d"):format(self:byte()) - end) -}) -local inline_escape -inline_escape = function(s) - return inline_escaper:match(s) -end -local escaper = re.compile("{~ (%utf8_char / ('\\' -> '\\\\') / [\n\r\t -~] / (. -> escape))* ~}", { - utf8_char = utf8_char_patt, - escape = (function(self) - return ("\\%03d"):format(self:byte()) - end) -}) -local escape -escape = function(s) - return escaper:match(s) -end -local make_tree -make_tree = function(tree, userdata) - tree.source = Source(userdata.filename, tree.start, tree.stop) - tree.start, tree.stop = nil, nil - do - local _accum_0 = { } - local _len_0 = 1 - for _index_0 = 1, #tree do - local t = tree[_index_0] - if SyntaxTree:is_instance(t) and t.type == "Comment" then - _accum_0[_len_0] = t - _len_0 = _len_0 + 1 +local math_expression = re.compile([[ (([*/^+-] / [0-9]+) " ")* [*/^+-] !. ]]) +local compile_math_expression +compile_math_expression = function(compile, tree, ...) + local lua = LuaCode(tree.source) + for i, tok in ipairs(tree) do + if type(tok) == 'string' then + lua:append(tok) + else + local tok_lua = compile(tok) + if tok.type == "Action" then + tok_lua:parenthesize() end + lua:append(tok_lua) end - tree.comments = _accum_0 - end - if #tree.comments == 0 then - tree.comments = nil - end - for i = #tree, 1, -1 do - if SyntaxTree:is_instance(tree[i]) and tree[i].type == "Comment" then - table.remove(tree, i) + if i < #tree then + lua:append(" ") end end - tree = SyntaxTree(tree) - return tree -end -local Parsers = { } -local max_parser_version = 4 -for version = 1, max_parser_version do - local peg_file = io.open("nomsu." .. tostring(version) .. ".peg") - if not peg_file and package.nomsupath then - for path in package.nomsupath:gmatch("[^;]+") do - peg_file = io.open(path .. "/nomsu." .. tostring(version) .. ".peg") - if peg_file then - break - end - end - end - assert(peg_file, "could not find nomsu .peg file") - local peg_contents = peg_file:read('*a') - peg_file:close() - Parsers[version] = make_parser(peg_contents, make_tree) + return lua end local MAX_LINE = 80 -local NomsuCompiler = setmetatable({ }, { - __tostring = function(self) - return "Nomsu" - end -}) -local _anon_chunk = 0 -do - NomsuCompiler.can_optimize = function() - return false - end - NomsuCompiler.environment = { - NOMSU_COMPILER_VERSION = 11, - NOMSU_SYNTAX_VERSION = max_parser_version, - next = next, - unpack = unpack, - setmetatable = setmetatable, - coroutine = coroutine, - rawequal = rawequal, - getmetatable = getmetatable, - pcall = pcall, - error = error, - package = package, - os = os, - require = require, - tonumber = tonumber, - tostring = tostring, - string = string, - xpcall = xpcall, - module = module, - print = print, - loadfile = loadfile, - rawset = rawset, - _VERSION = _VERSION, - collectgarbage = collectgarbage, - rawget = rawget, - rawlen = rawlen, - table = table, - assert = assert, - dofile = dofile, - loadstring = loadstring, - lua_type_of = type, - select = select, - math = math, - io = io, - load = load, - pairs = pairs, - ipairs = ipairs, - List = List, - Dict = Dict, - lpeg = lpeg, - re = re, - Files = Files, - SyntaxTree = SyntaxTree, - TESTS = Dict({ }), - globals = Dict({ }), - LuaCode = LuaCode, - NomsuCode = NomsuCode, - Source = Source, - nomsu = NomsuCompiler, - __imported = Dict({ }), - __parent = nil - } - setmetatable(NomsuCompiler.environment, { - __index = function(self, key) - do - local imported = rawget(self, "__imported") - if imported then - local ret = imported[key] - if not (ret == nil) then - return ret - end - end - end - do - local parent = rawget(self, "__parent") - if parent then - return parent[key] - end - end - end - }) - if _VERSION == "Lua 5.4" then - NomsuCompiler.environment.ipairs = function(x) - do - local mt = getmetatable(x) - if mt then - if mt.__ipairs then - return mt.__ipairs(x) - end - end - end - return ipairs(x) - end - end - if jit or _VERSION == "Lua 5.2" then - NomsuCompiler.environment.bit = require("bitops") - end - NomsuCompiler.fork = function(self) - local f = setmetatable({ }, { - __index = self - }) - f.environment = setmetatable({ - __parent = self.environment, - __imported = Dict({ }), - nomsu = f, - COMPILE_ACTIONS = setmetatable({ - __parent = self.environment.COMPILE_ACTIONS, - __imported = Dict({ }) - }, getmetatable(self.environment)) - }, getmetatable(self.environment)) - return f - end - NomsuCompiler.parse = function(self, nomsu_code, source, version) - if source == nil then - source = nil - end - if version == nil then - version = nil - end - source = source or nomsu_code.source - nomsu_code = tostring(nomsu_code) - if not (source) then - source = Source("anonymous chunk #" .. tostring(_anon_chunk), 1, #nomsu_code) - _anon_chunk = _anon_chunk + 1 - end - version = version or nomsu_code:match("^#![^\n]*nomsu[ ]+-V[ ]*([0-9.]+)") - local syntax_version = version and tonumber(version:match("^[0-9]+")) or max_parser_version - local parse = Parsers[syntax_version] or Parsers[max_parser_version] - local tree = parse(nomsu_code, source.filename) - local find_errors - find_errors = function(t) - if t.type == "Error" then - return coroutine.yield(t) - else - for k, v in pairs(t) do - local _continue_0 = false - repeat - if not (SyntaxTree:is_instance(v)) then - _continue_0 = true - break - end - find_errors(v) - _continue_0 = true - until true - if not _continue_0 then - break - end - end - end - end - local errs - do - local _accum_0 = { } - local _len_0 = 1 - for err in coroutine.wrap(function() - return find_errors(tree) - end) do - _accum_0[_len_0] = err - _len_0 = _len_0 + 1 - end - errs = _accum_0 - end - local num_errs = #errs - if num_errs > 0 then - local err_strings - do - local _accum_0 = { } - local _len_0 = 1 - for i, t in ipairs(errs) do - if i <= 3 then - _accum_0[_len_0] = pretty_error({ - title = "Parse error", - error = t.error, - hint = t.hint, - source = t:get_source_code(), - start = t.source.start, - stop = t.source.stop, - filename = t.source.filename - }) - _len_0 = _len_0 + 1 - end - end - err_strings = _accum_0 - end - if num_errs > 3 then - table.insert(err_strings, "\027[31;1m +" .. tostring(num_errs - #errs) .. " additional errors...\027[0m\n") - end - error(table.concat(err_strings, '\n\n'), 0) - end - return tree - end - NomsuCompiler.compile_error = function(self, tree, err_msg, hint) - if hint == nil then - hint = nil - end - local err_str = pretty_error({ - title = "Compile error", - error = err_msg, - hint = hint, - source = tree:get_source_code(), - start = tree.source.start, - stop = tree.source.stop, - filename = tree.source.filename - }) - return error(err_str, 0) - end - local add_lua_bits - add_lua_bits = function(self, val_or_stmt, code, compile_actions) - local cls = val_or_stmt == "value" and LuaCode.Value or LuaCode - local operate_on_text - operate_on_text = function(text) - local lua = cls(text.source) - for _index_0 = 1, #text do - local bit = text[_index_0] - if type(bit) == "string" then - lua:append(bit) - elseif bit.type == "Text" then - lua:append(operate_on_text(bit)) - else - local bit_lua = self:compile(bit, compile_actions) - if not (bit_lua.is_value) then - self:compile_error(bit, "Can't use this as a string interpolation value, since it's not an expression.") - end - lua:append(bit_lua) - end - end - return lua - end - return operate_on_text(code) - end - local add_lua_string_bits - add_lua_string_bits = function(self, val_or_stmt, code) - local cls_str = val_or_stmt == "value" and "LuaCode.Value(" or "LuaCode(" - if code.type ~= "Text" then - return LuaCode.Value(code.source, cls_str, tostring(code.source):as_lua(), ", ", self:compile(code), ")") - end - local add_bit_lua - add_bit_lua = function(lua, bit_lua) - local bit_leading_len = #(bit_lua:match("^[^\n]*")) - lua:append(lua:trailing_line_len() + bit_leading_len > MAX_LINE and ",\n " or ", ") - return lua:append(bit_lua) - end - local operate_on_text - operate_on_text = function(text) - local lua = LuaCode.Value(text.source, cls_str, tostring(text.source):as_lua()) - for _index_0 = 1, #text do - local bit = text[_index_0] - if type(bit) == "string" then - add_bit_lua(lua, bit:as_lua()) - elseif bit.type == "Text" then - add_bit_lua(lua, operate_on_text(bit)) - else - local bit_lua = self:compile(bit) - if not (bit_lua.is_value) then - self:compile_error(bit, "Can't use this as a string interpolation value, since it's not an expression.") - end - add_bit_lua(lua, bit_lua) - end - end - lua:append(")") - return lua - end - return operate_on_text(code) - end - local math_expression = re.compile([[ (([*/^+-] / [0-9]+) " ")* [*/^+-] !. ]]) - local compile_math_expression - compile_math_expression = function(self, tree, ...) - local lua = LuaCode.Value(tree.source) - for i, tok in ipairs(tree) do - if type(tok) == 'string' then - lua:append(tok) - else - local tok_lua = self:compile(tok, compile_actions) - if not (tok_lua.is_value) then - self:compile_error(tok, "Can't use this as a value in a math expression, since it's not a value.") - end - if tok.type == "Action" then - tok_lua:parenthesize() - end - lua:append(tok_lua) - end - if i < #tree then - lua:append(" ") - end - end - return lua - end - NomsuCompiler.environment.COMPILE_ACTIONS = setmetatable({ - __imported = Dict({ }), - [""] = function(self, tree, fn, ...) - local lua = LuaCode.Value(tree.source) - lua:append(self:compile(fn, compile_actions)) - if not (lua:text():is_lua_id()) then +local compile = setmetatable({ + action = Importer({ + [""] = function(compile, tree, fn, ...) + local lua = LuaCode(tree.source) + local fn_lua = compile(fn) + lua:append(fn_lua) + if not (fn_lua:text():match("^%(.*%)$") or fn_lua:text():match("^[_a-zA-Z][_a-zA-Z0-9.]*$")) then lua:parenthesize() end lua:append("(") @@ -437,259 +92,99 @@ do if i > 1 then lua:append(", ") end - lua:append(self:compile(select(i, ...), compile_actions)) + lua:append(compile(select(i, ...))) end lua:append(")") return lua end, - ["Lua"] = function(self, tree, code) - return add_lua_string_bits(self, 'statements', code) - end, - ["Lua value"] = function(self, tree, code) - return add_lua_string_bits(self, 'value', code) - end, - ["lua >"] = function(self, tree, code) + ["Lua"] = function(compile, tree, code) if code.type ~= "Text" then - return LuaCode(tree.source, "nomsu:run_lua(", self:compile(code), ", nomsu);") + return LuaCode(code.source, "LuaCode(", tostring(code.source):as_lua(), ", ", compile(code), ")") + end + local add_bit_lua + add_bit_lua = function(lua, bit_lua) + local bit_leading_len = #(bit_lua:match("^[^\n]*")) + lua:append(lua:trailing_line_len() + bit_leading_len > MAX_LINE and ",\n " or ", ") + return lua:append(bit_lua) + end + local operate_on_text + operate_on_text = function(text) + local lua = LuaCode(text.source, "LuaCode(", tostring(text.source):as_lua()) + for _index_0 = 1, #text do + local bit = text[_index_0] + if type(bit) == "string" then + add_bit_lua(lua, bit:as_lua()) + elseif bit.type == "Text" then + add_bit_lua(lua, operate_on_text(bit)) + else + add_bit_lua(lua, compile(bit)) + end + end + lua:append(")") + return lua end - return add_lua_bits(self, "statements", code) + return operate_on_text(code) + end, + ["Lua value"] = function(compile, tree, code) + return compile.action["Lua"](compile, tree, code) end, - ["= lua"] = function(self, tree, code) + ["lua >"] = function(compile, tree, code) if code.type ~= "Text" then - return LuaCode.Value(tree.source, "nomsu:run_lua(", self:compile(code), ":as_statements('return '), nomsu)") + return tree end - return add_lua_bits(self, "value", code) - end, - ["use"] = function(self, tree, path) - if path.type == 'Text' and #path == 1 and type(path[1]) == 'string' then - if not (self:import_file(path[1])) then - self:compile_error(tree, "Could not find anything to import for " .. tostring(path)) + local operate_on_text + operate_on_text = function(text) + local lua = LuaCode(text.source) + for _index_0 = 1, #text do + local bit = text[_index_0] + if type(bit) == "string" then + lua:append(bit) + elseif bit.type == "Text" then + lua:append(operate_on_text(bit)) + else + lua:append(compile(bit)) + end end + return lua end - return LuaCode(tree.source, "nomsu:import_file(" .. tostring(self:compile(path)) .. ")") + return operate_on_text(code) end, - ["tests"] = function(self, tree) - return LuaCode.Value(tree.source, "TESTS") + ["= lua"] = function(compile, tree, code) + return compile.action["lua >"](compile, tree, code) end, - ["test"] = function(self, tree, body) - local test_str = table.concat((function() - local _accum_0 = { } - local _len_0 = 1 - for _index_0 = 1, #body do - local line = body[_index_0] - _accum_0[_len_0] = tostring(self:tree_to_nomsu(line)) - _len_0 = _len_0 + 1 - end - return _accum_0 - end)(), "\n") - return LuaCode(tree.source, "TESTS[" .. tostring(tostring(tree.source):as_lua()) .. "] = ", test_str:as_lua()) + ["use"] = function(compile, tree, path) + return LuaCode(tree.source, "run_file_1_in(" .. tostring(compile(path)) .. ", _ENV)") end, - ["is jit"] = function(self, tree, code) - return LuaCode.Value(tree.source, jit and "true" or "false") + ["tests"] = function(compile, tree) + return LuaCode(tree.source, "TESTS") end, - ["Lua version"] = function(self, tree, code) - return LuaCode.Value(tree.source, _VERSION:as_lua()) + ["test"] = function(compile, tree, body) + return LuaCode(tree.source, "TESTS[" .. tostring(tostring(tree.source):as_lua()) .. "] = ", body:as_lua()) end, - __parent = setmetatable({ }, { - __index = function(self, key) - if type(key) == 'string' and math_expression:match(key) then - return compile_math_expression - end - end - }) - }, getmetatable(NomsuCompiler.environment)) - NomsuCompiler.import = function(self, mod) - for k, v in pairs(mod) do - local _continue_0 = false - repeat - if k == "__imported" or k == "__parent" then - _continue_0 = true - break - end - self.environment.__imported[k] = v - _continue_0 = true - until true - if not _continue_0 then - break - end - end - for k, v in pairs(mod.COMPILE_ACTIONS) do - local _continue_0 = false - repeat - if k == "__imported" or k == "__parent" then - _continue_0 = true - break - end - self.environment.COMPILE_ACTIONS.__imported[k] = self.environment.COMPILE_ACTIONS.__imported[k] or v - _continue_0 = true - until true - if not _continue_0 then - break - end - end - end - NomsuCompiler.import_file = function(self, path) - local found = false - for _, f in Files.walk(path) do - if match(f, "%.lua$") or match(f, "%.nom$") or match(f, "^/dev/fd/[012]$") then - found = true - self:import(self:run_file(f)) - end - end - return found - end - NomsuCompiler.run = function(self, to_run, compile_actions) - local source = to_run.source or Source(to_run, 1, #to_run) - if type(source) == 'string' then - source = Source:from_string(source) - end - if not Files.read(source.filename) then - Files.spoof(source.filename, to_run) - end - local tree - if SyntaxTree:is_instance(to_run) then - tree = to_run - else - tree = self:parse(to_run, source) - end - if tree == nil then - return nil - end - if tree.type ~= "FileChunks" then - tree = { - tree - } - end - local ret = nil - local all_lua = { } - for _index_0 = 1, #tree do - local chunk = tree[_index_0] - local lua = self:compile(chunk, compile_actions):as_statements("return ") - lua:declare_locals() - lua:prepend("-- File: " .. tostring(source.filename:gsub("\n.*", "...")) .. "\n") - insert(all_lua, tostring(lua)) - ret = self:run_lua(lua) - end - return ret - end - local _running_files = { } - local _loaded_files = { } - NomsuCompiler.run_file = function(self, filename, compile_actions) - if _loaded_files[filename] then - return _loaded_files[filename] - end - for i, running in ipairs(_running_files) do - if running == filename then - local loop - do - local _accum_0 = { } - local _len_0 = 1 - for j = i, #_running_files do - _accum_0[_len_0] = _running_files[j] - _len_0 = _len_0 + 1 - end - loop = _accum_0 - end - insert(loop, filename) - error("Circular import, this loops forever: " .. tostring(concat(loop, " -> ")) .. "...") - end - end - insert(_running_files, filename) - local mod = self:fork() - local ret = mod.environment - mod.from_file = filename - if match(filename, "%.lua$") then - local file = assert(Files.read(filename), "Could not find file: " .. tostring(filename)) - ret = mod:run_lua(LuaCode(Source(filename, 1, #file), file)) or ret - elseif match(filename, "%.nom$") or match(filename, "^/dev/fd/[012]$") then - local ran_lua - if self.can_optimize(filename) then - local lua_filename = gsub(filename, "%.nom$", ".lua") - do - local file = Files.read(lua_filename) - if file then - ret = mod:run_lua(LuaCode(Source(lua_filename, 1, #file), file)) or ret - ran_lua = true - end - end - end - if not (ran_lua) then - local file = Files.read(filename) - if not file then - error("Tried to run file that does not exist: " .. tostring(filename)) - end - ret = mod:run(NomsuCode(Source(filename, 1, #file), file), compile_actions) or ret - end - else - error("Invalid filetype for " .. tostring(filename), 0) - end - _loaded_files[filename] = ret - remove(_running_files) - return ret - end - NomsuCompiler.run_lua = function(self, lua) - local lua_string = tostring(lua) - local run_lua_fn, err = load(lua_string, tostring(source or lua.source), "t", self.environment) - if not run_lua_fn then - local line_numbered_lua = concat((function() - local _accum_0 = { } - local _len_0 = 1 - for i, line in ipairs(Files.get_lines(lua_string)) do - _accum_0[_len_0] = format("%3d|%s", i, line) - _len_0 = _len_0 + 1 - end - return _accum_0 - end)(), "\n") - error("Failed to compile generated code:\n" .. tostring(colored.bright(colored.blue(colored.onblack(line_numbered_lua)))) .. "\n\n" .. tostring(err), 0) - end - local source = lua.source or Source(lua_string, 1, #lua_string) - local source_key = tostring(source) - if not (SOURCE_MAP[source_key]) then - local map = { } - local file = Files.read(source.filename) - if not file then - error("Failed to find file: " .. tostring(source.filename)) - end - local nomsu_str = file:sub(source.start, source.stop) - assert(type(nomsu_str) == 'string') - local lua_line = 1 - local nomsu_line = Files.get_line_number(file, source.start) - local map_sources - map_sources = function(s) - if type(s) == 'string' then - for nl in s:gmatch("\n") do - map[lua_line] = map[lua_line] or nomsu_line - lua_line = lua_line + 1 - end - else - if s.source and s.source.filename == source.filename then - nomsu_line = Files.get_line_number(file, s.source.start) - end - local _list_0 = s.bits - for _index_0 = 1, #_list_0 do - local b = _list_0[_index_0] - map_sources(b) - end - end - end - map_sources(lua) - map[lua_line] = map[lua_line] or nomsu_line - map[0] = 0 - SOURCE_MAP[source_key] = map + ["is jit"] = function(compile, tree, code) + return LuaCode(tree.source, "jit") + end, + ["Lua version"] = function(compile, tree, code) + return LuaCode(tree.source, "_VERSION") + end, + ["nomsu environment"] = function(compile, tree) + return LuaCode(tree.source, "_ENV") end - return run_lua_fn() - end - NomsuCompiler.compile = function(self, tree, compile_actions, force_value) + }) +}, { + __import = function(self, other) + import_to_1_from(self.action, other.action) + end, + __call = function(compile, tree, force_value) if force_value == nil then force_value = false end - compile_actions = compile_actions or self.environment.COMPILE_ACTIONS if tree.version then do - local get_version = self[("Nomsu version"):as_lua_id()] + local get_version = compile.action[("Nomsu version"):as_lua_id()] if get_version then do - local upgrade = self[("1 upgraded from 2 to"):as_lua_id()] + local upgrade = compile.action[("1 upgraded from 2 to"):as_lua_id()] if upgrade then tree = upgrade(tree, tree.version, get_version()) end @@ -700,7 +195,10 @@ do local _exp_0 = tree.type if "Action" == _exp_0 then local stub = tree.stub - local compile_action = compile_actions[stub] + local compile_action = compile.action[stub] + if not compile_action and math_expression:match(stub) then + compile_action = compile_math_expression + end if compile_action and not tree.target then local args do @@ -715,23 +213,24 @@ do end args = _accum_0 end - local ret = compile_action(self, tree, unpack(args)) + local ret = compile_action(compile, tree, unpack(args)) if ret == nil then local info = debug.getinfo(compile_action, "S") local filename = Source:from_string(info.source).filename - self:compile_error(tree, "The compile-time action here (" .. tostring(stub) .. ") failed to return any value.", "Look at the implementation of (" .. tostring(stub) .. ") in " .. tostring(filename) .. ":" .. tostring(info.linedefined) .. " and make sure it's returning something.") + compile_error(tree, "The compile-time action here (" .. tostring(stub) .. ") failed to return any value.", "Look at the implementation of (" .. tostring(stub) .. ") in " .. tostring(filename) .. ":" .. tostring(info.linedefined) .. " and make sure it's returning something.") end if not (SyntaxTree:is_instance(ret)) then return ret end if ret ~= tree then - return self:compile(ret, compile_actions) + return compile(ret) end end - local lua = LuaCode.Value(tree.source) + local lua = LuaCode(tree.source) if tree.target then - local target_lua = self:compile(tree.target, compile_actions) - if tostring(target_lua):match("^%(.*%)$") or tostring(target_lua):match("^[_a-zA-Z][_a-zA-Z0-9]*$") then + local target_lua = compile(tree.target) + local target_text = target_lua:text() + if target_text:match("^%(.*%)$") or target_text:match("^[_a-zA-Z][_a-zA-Z0-9.]*$") then lua:append(target_lua, ":") else lua:append("(", target_lua, "):") @@ -746,16 +245,7 @@ do _continue_0 = true break end - local arg_lua = self:compile(tok, compile_actions, true) - if not (arg_lua.is_value) then - if tok.type == "Block" then - self:compile_error(tok, "Can't compile action (" .. tostring(stub) .. ") with a Block as an argument.", "Maybe there should be a compile-time action with that name that isn't being found?") - elseif tok.type == "Action" then - self:compile_error(tok, "Can't use this as an argument to (" .. tostring(stub) .. "), since it's not an expression, it produces: " .. tostring(tostring(arg_lua)), "Check the implementation of (" .. tostring(tok.stub) .. ") to see if it is actually meant to produce an expression.") - else - self:compile_error(tok, "Can't use this as an argument to (" .. tostring(stub) .. "), since it's not an expression, it produces: " .. tostring(tostring(arg_lua))) - end - end + local arg_lua = compile(tok, true) insert(args, arg_lua) _continue_0 = true until true @@ -767,14 +257,14 @@ do lua:append(")") return lua elseif "EscapedNomsu" == _exp_0 then - local lua = LuaCode.Value(tree.source, "SyntaxTree{") + local lua = LuaCode(tree.source, "SyntaxTree{") local needs_comma, i = false, 1 local as_lua as_lua = function(x) if type(x) == 'number' then return tostring(x) elseif SyntaxTree:is_instance(x) then - return self:compile(x, compile_actions) + return compile(x) else return x:as_lua() end @@ -808,51 +298,23 @@ do local _len_0 = 1 for _index_0 = 1, #tree do local line = tree[_index_0] - _accum_0[_len_0] = self:compile(line, compile_actions):as_statements() + _accum_0[_len_0] = compile(line) _len_0 = _len_0 + 1 end return _accum_0 end)(), "\n") return lua else - local lua = LuaCode.Value(tree.source) - local values - do - local _accum_0 = { } - local _len_0 = 1 - for _index_0 = 1, #tree do - local line = tree[_index_0] - _accum_0[_len_0] = self:compile(line) - _len_0 = _len_0 + 1 - end - values = _accum_0 - end - local all_values = true - for _index_0 = 1, #values do - local v = values[_index_0] - all_values = all_values and v.is_value - end - if all_values then - if #values == 1 then - return values[1] - end - lua:append("(") - lua:concat_append(values, " and nil or ") - lua:append(")") - else - lua:append("((function()") - for i, v in ipairs(values) do - if v.is_value then - v = v:as_statements(i == #values and 'return ' or '') - end - lua:append("\n ", v) - end - lua:append("\nend)())") + local lua = LuaCode(tree.source) + lua:append("((function()") + for i, line in ipairs(tree) do + lua:append("\n ", compile(line)) end + lua:append("\nend)())") return lua end elseif "Text" == _exp_0 then - local lua = LuaCode.Value(tree.source) + local lua = LuaCode(tree.source) local string_buffer = "" for i, bit in ipairs(tree) do local _continue_0 = false @@ -869,17 +331,12 @@ do lua:append(string_buffer:as_lua()) string_buffer = "" end - local bit_lua = self:compile(bit, compile_actions) - if not (bit_lua.is_value) then - local src = ' ' .. gsub(tostring(self:compile(bit, compile_actions)), '\n', '\n ') - local line = tostring(bit.source.filename) .. ":" .. tostring(Files.get_line_number(Files.read(bit.source.filename), bit.source.start)) - self:compile_error(bit, "Can't this as a string interpolation value, since it's not an expression.") - end + local bit_lua = compile(bit) if #lua.bits > 0 then lua:append("..") end if bit.type ~= "Text" then - bit_lua = LuaCode.Value(bit.source, "tostring(", bit_lua, ")") + bit_lua = LuaCode(bit.source, "tostring(", bit_lua, ")") end lua:append(bit_lua) _continue_0 = true @@ -898,68 +355,65 @@ do lua:parenthesize() end return lua - elseif "List" == _exp_0 then - local lua = LuaCode.Value(tree.source, "List{") - lua:concat_append((function() - local _accum_0 = { } - local _len_0 = 1 - for _index_0 = 1, #tree do - local e = tree[_index_0] - _accum_0[_len_0] = self:compile(e, compile_actions) - _len_0 = _len_0 + 1 + elseif "List" == _exp_0 or "Dict" == _exp_0 then + local lua = LuaCode(tree.source, tostring(tree.type) .. "{") + local i = 1 + local sep = '' + while i <= #tree do + local item = tree[i] + if item.type == "Block" then + break end - return _accum_0 - end)(), ", ", ",\n ") - lua:append("}") - return lua - elseif "Dict" == _exp_0 then - local lua = LuaCode.Value(tree.source, "Dict{") - lua:concat_append((function() - local _accum_0 = { } - local _len_0 = 1 - for _index_0 = 1, #tree do - local e = tree[_index_0] - _accum_0[_len_0] = self:compile(e, compile_actions) - _len_0 = _len_0 + 1 + lua:append(sep) + if item.type == "Comment" then + lua:append(compile(item), "\n") + sep = '' + else + local item_lua = compile(item) + lua:append(item_lua) + sep = ', ' end - return _accum_0 - end)(), ", ", ",\n ") + i = i + 1 + end lua:append("}") + if i <= #tree then + lua = LuaCode(tree.source, "(function()\n local it = ", lua) + while i <= #tree do + lua:append("\n ") + if tree[i].type == 'Block' or tree[i].type == 'Comment' then + lua:append(compile(tree[i])) + elseif tree[i].type == "DictEntry" then + lua:append("it[ ", compile(tree[i][1]), "] = ", (tree[i][2] and compile(tree[i][2]) or "true")) + else + lua:append("it:add(", compile(tree[i]), ")") + end + i = i + 1 + end + lua:append("\n return it\nend)()") + end return lua elseif "DictEntry" == _exp_0 then local key, value = tree[1], tree[2] - local key_lua = self:compile(key, compile_actions) - if not (key_lua.is_value) then - self:compile_error(tree[1], "Can't use this as a dict key, since it's not an expression.") - end - local value_lua = value and self:compile(value, compile_actions) or LuaCode.Value(key.source, "true") - if not (value_lua.is_value) then - self:compile_error(tree[2], "Can't use this as a dict value, since it's not an expression.") - end - local key_str = match(tostring(key_lua), [=[^["']([a-zA-Z_][a-zA-Z0-9_]*)['"]$]=]) + local key_lua = compile(key) + local value_lua = value and compile(value) or LuaCode(key.source, "true") + local key_str = match(key_lua:text(), [=[^["']([a-zA-Z_][a-zA-Z0-9_]*)['"]$]=]) if key_str and key_str:is_lua_id() then return LuaCode(tree.source, key_str, "=", value_lua) - elseif sub(tostring(key_lua), 1, 1) == "[" then + elseif sub(key_lua:text(), 1, 1) == "[" then return LuaCode(tree.source, "[ ", key_lua, "]=", value_lua) else return LuaCode(tree.source, "[", key_lua, "]=", value_lua) end elseif "IndexChain" == _exp_0 then - local lua = self:compile(tree[1], compile_actions) - if not (lua.is_value) then - self:compile_error(tree[1], "Can't index into this, since it's not an expression.") - end - local first_char = sub(tostring(lua), 1, 1) + local lua = compile(tree[1]) + local first_char = sub(lua:text(), 1, 1) if first_char == "{" or first_char == '"' or first_char == "[" then lua:parenthesize() end for i = 2, #tree do local key = tree[i] - local key_lua = self:compile(key, compile_actions) - if not (key_lua.is_value) then - self:compile_error(key, "Can't use this as an index, since it's not an expression.") - end - local key_lua_str = tostring(key_lua) + local key_lua = compile(key) + local key_lua_str = key_lua:text() local lua_id = match(key_lua_str, "^['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]$") if lua_id and lua_id:is_lua_id() then lua:append("." .. tostring(lua_id)) @@ -971,9 +425,9 @@ do end return lua elseif "Number" == _exp_0 then - return LuaCode.Value(tree.source, tostring(tree[1])) + return LuaCode(tree.source, tostring(tree[1])) elseif "Var" == _exp_0 then - return LuaCode.Value(tree.source, (tree[1]):as_lua_id()) + return LuaCode(tree.source, (tree[1]):as_lua_id()) elseif "FileChunks" == _exp_0 then return error("Can't convert FileChunks to a single block of lua, since each chunk's " .. "compilation depends on the earlier chunks") elseif "Comment" == _exp_0 then @@ -984,506 +438,5 @@ do return error("Unknown type: " .. tostring(tree.type)) end end - NomsuCompiler.tree_to_inline_nomsu = function(self, tree, parenthesize_blocks, check, len) - if parenthesize_blocks == nil then - parenthesize_blocks = false - end - if check == nil then - check = nil - end - if len == nil then - len = 0 - end - local recurse - recurse = function(tree, nomsu, parenthesize_blocks) - if nomsu == nil then - nomsu = nil - end - if parenthesize_blocks == nil then - parenthesize_blocks = false - end - return self:tree_to_inline_nomsu(tree, parenthesize_blocks, check, len + (nomsu and #tostring(nomsu) or 0)) - end - local _exp_0 = tree.type - if "FileChunks" == _exp_0 then - return error("Can't inline a FileChunks") - elseif "Comment" == _exp_0 then - return NomsuCode(tree.source, "") - elseif "Error" == _exp_0 then - return error("Can't compile errors") - elseif "Action" == _exp_0 then - local nomsu = NomsuCode(tree.source) - if tree.target then - local inline_target = self:tree_to_inline_nomsu(tree.target) - if tree.target.type == "Action" then - inline_target:parenthesize() - end - nomsu:append(inline_target, "::") - end - for i, bit in ipairs(tree) do - if type(bit) == "string" then - local clump_words = (type(tree[i - 1]) == 'string' and is_operator(bit) ~= is_operator(tree[i - 1])) - if i > 1 and not clump_words then - nomsu:append(" ") - end - nomsu:append(bit) - else - local arg_nomsu = recurse(bit, nomsu, parenthesize_blocks or (i == 1 or i < #tree)) - if not (arg_nomsu:match("^:") or i == 1) then - nomsu:append(" ") - end - if bit.type == "Action" then - arg_nomsu:parenthesize() - end - nomsu:append(arg_nomsu) - end - if check then - check(len, nomsu, tree) - end - end - return nomsu - elseif "EscapedNomsu" == _exp_0 then - local inner_nomsu = recurse(tree[1]) - if not (tree[1].type == "List" or tree[1].type == "Dict" or tree[1].type == "Var") then - inner_nomsu:parenthesize() - end - local nomsu = NomsuCode(tree.source, "\\", inner_nomsu) - if check then - check(len, nomsu, tree) - end - return nomsu - elseif "Block" == _exp_0 then - local nomsu = NomsuCode(tree.source, ":") - if check then - check(len, nomsu, tree) - end - for i, line in ipairs(tree) do - nomsu:append(i == 1 and " " or "; ") - nomsu:append(recurse(line, nomsu, i == 1 or i < #tree)) - if check then - check(len, nomsu, tree) - end - end - if #tree > 1 or parenthesize_blocks then - nomsu:parenthesize() - end - return nomsu - elseif "Text" == _exp_0 then - local add_text - add_text = function(nomsu, tree) - for i, bit in ipairs(tree) do - if type(bit) == 'string' then - local escaped = inline_escape(bit) - nomsu:append(inline_escape(bit)) - elseif bit.type == "Text" then - add_text(nomsu, bit) - else - local interp_nomsu = recurse(bit, nomsu) - if bit.type ~= "Var" and bit.type ~= "List" and bit.type ~= "Dict" then - interp_nomsu:parenthesize() - elseif bit.type == "Var" and type(tree[i + 1]) == 'string' and not match(tree[i + 1], "^[ \n\t,.:;#(){}[%]]") then - interp_nomsu:parenthesize() - end - nomsu:append("\\", interp_nomsu) - end - if check then - check(len, nomsu, tree) - end - end - end - local nomsu = NomsuCode(tree.source) - add_text(nomsu, tree) - return NomsuCode(tree.source, '"', nomsu, '"') - elseif "List" == _exp_0 or "Dict" == _exp_0 then - local nomsu = NomsuCode(tree.source, (tree.type == "List" and "[" or "{")) - for i, item in ipairs(tree) do - if i > 1 then - nomsu:append(", ") - end - nomsu:append(recurse(item, nomsu)) - if check then - check(len, nomsu, tree) - end - end - nomsu:append(tree.type == "List" and "]" or "}") - return nomsu - elseif "DictEntry" == _exp_0 then - local key, value = tree[1], tree[2] - local nomsu - if key.type == "Text" and #key == 1 and is_identifier(key[1]) then - nomsu = NomsuCode(key.source, key[1]) - else - nomsu = recurse(key) - end - if key.type == "Action" or key.type == "Block" then - nomsu:parenthesize() - end - assert(value.type ~= "Block", "Didn't expect to find a Block as a value in a dict") - nomsu:append(":") - if value then - local value_nomsu = recurse(value, nomsu) - if value.type == "Block" then - value_nomsu:parenthesize() - end - nomsu:append(value_nomsu) - end - if check then - check(len, nomsu, tree) - end - return nomsu - elseif "IndexChain" == _exp_0 then - local nomsu = NomsuCode(tree.source) - for i, bit in ipairs(tree) do - if i > 1 then - nomsu:append(".") - end - local bit_nomsu - if i > 1 and bit.type == "Text" and #bit == 1 and type(bit[1]) == 'string' and is_identifier(bit[1]) then - bit_nomsu = bit[1] - else - bit_nomsu = recurse(bit, nomsu) - end - assert(bit.type ~= "Block") - if bit.type == "Action" or bit.type == "IndexChain" or (bit.type == "Number" and i < #tree) then - bit_nomsu:parenthesize() - end - nomsu:append(bit_nomsu) - if check then - check(len, nomsu, tree) - end - end - return nomsu - elseif "Number" == _exp_0 then - return NomsuCode(tree.source, tostring(tree[1])) - elseif "Var" == _exp_0 then - return NomsuCode(tree.source, "%", tree[1]) - else - return error("Unknown type: " .. tostring(tree.type)) - end - end - NomsuCompiler.tree_to_nomsu = function(self, tree, pop_comments) - if pop_comments == nil then - pop_comments = nil - end - if not (pop_comments) then - local comment_set = { } - local find_comments - find_comments = function(t) - if t.comments and t.source.filename == tree.source.filename then - local _list_0 = t.comments - for _index_0 = 1, #_list_0 do - local c = _list_0[_index_0] - comment_set[c] = true - end - end - local _list_0 = t - for _index_0 = 1, #_list_0 do - local x = _list_0[_index_0] - if SyntaxTree:is_instance(x) then - find_comments(x) - end - end - end - find_comments(tree) - local comments - do - local _accum_0 = { } - local _len_0 = 1 - for c in pairs(comment_set) do - _accum_0[_len_0] = c - _len_0 = _len_0 + 1 - end - comments = _accum_0 - end - table.sort(comments, function(a, b) - return (a.source.start > b.source.start) - end) - pop_comments = function(pos, prefix, suffix) - if prefix == nil then - prefix = '' - end - if suffix == nil then - suffix = '' - end - local nomsu = NomsuCode(tree.source) - for i = #comments, 1, -1 do - if comments[i].source.start > pos then - break - end - local comment - comment, comments[i] = comments[i][1], nil - nomsu:append("#" .. (gsub(comment, "\n", "\n ")) .. "\n") - if comment:match("^\n.") then - nomsu:append("\n") - end - end - if #nomsu.bits == 0 then - return '' - end - if not (prefix == '') then - nomsu:prepend(prefix) - end - if not (suffix == '') then - nomsu:append(suffix) - end - return nomsu - end - end - local recurse - recurse = function(t, pos) - if pos == nil then - pos = 0 - end - if type(pos) ~= 'number' then - pos = #tostring(pos):match("[ ]*([^\n]*)$") - end - local space = MAX_LINE - pos - local inline - for prefix, nomsu, tree in coroutine.wrap(function() - inline = self:tree_to_inline_nomsu(t, false, coroutine.yield) - end) do - local len = #tostring(nomsu) - if prefix + len > MAX_LINE then - break - end - if tree.type == "Block" and (#tree > 1 or len > 20) then - break - end - if tree.type == "Text" then - local check_for_nl - check_for_nl = function(tree) - local found_nl = false - for i, b in ipairs(tree) do - if type(b) ~= 'string' and b.type == "Text" and check_for_nl(b) then - return true - end - if i == 1 and type(b) == 'string' then - b = b:match('^[\n]*(.*)') - end - found_nl = found_nl or (type(b) == 'string' and b:match('\n')) - if found_nl and (type(b) ~= 'string' or b:match('[^\n]')) then - return true - end - end - end - if check_for_nl(tree) then - break - end - end - end - if inline and #tostring(inline) <= space then - return inline - end - local indented = self:tree_to_nomsu(t, pop_comments, space) - if t.type == "Action" and not (tree.type == "Block" or tree.type == "FileChunks") then - indented = NomsuCode(t.source, "(..)\n ", pop_comments(t.source.start), indented) - end - return indented - end - local _exp_0 = tree.type - if "FileChunks" == _exp_0 then - local nomsu = NomsuCode(tree.source, pop_comments(tree.source.start)) - local should_clump - should_clump = function(prev_line, line) - if prev_line.type == "Action" and line.type == "Action" then - if prev_line.stub == "use" then - return line.stub == "use" - end - if prev_line.stub == "test" then - return true - end - if line.stub == "test" then - return false - end - end - return not recurse(prev_line):is_multiline() - end - for chunk_no, chunk in ipairs(tree) do - if chunk_no > 1 then - nomsu:append("\n\n" .. tostring(("~"):rep(80)) .. "\n\n") - end - nomsu:append(pop_comments(chunk.source.start)) - if chunk.type == "Block" then - for line_no, line in ipairs(chunk) do - if line_no > 1 then - if should_clump(chunk[line_no - 1], line) then - nomsu:append("\n", pop_comments(line.source.start, '\n')) - else - nomsu:append("\n\n", pop_comments(line.source.start)) - end - end - nomsu:append(self:tree_to_nomsu(line, pop_comments)) - end - nomsu:append(pop_comments(chunk.source.stop, '\n')) - else - nomsu:append(recurse(chunk)) - end - end - nomsu:append(pop_comments(tree.source.stop, '\n')) - if not (nomsu:match("\n$")) then - nomsu:append('\n') - end - return nomsu - elseif "Action" == _exp_0 then - local pos, next_space = tree.source.start, '' - local nomsu = NomsuCode(tree.source, pop_comments(pos)) - if tree.target then - if tree.target.type == "Block" then - nomsu:append(recurse(tree.target, #nomsu:match('[^\n]*$'))) - pos = tree.target.source.stop - next_space = inline and "::" or "\n..::" - else - local target_nomsu = recurse(tree.target, #nomsu:match('[^\n]*$')) - if tree.target.type == "Action" and not target_nomsu:is_multiline() then - target_nomsu:parenthesize() - end - nomsu:append(target_nomsu) - pos = tree.target.source.stop - next_space = target_nomsu:is_multiline() and "\n..::" or "::" - end - end - for i, bit in ipairs(tree) do - if next_space == "\n.." then - nomsu:append("\n", pop_comments(pos), '..') - next_space = "" - elseif next_space == " " and nomsu:trailing_line_len() > MAX_LINE then - nomsu:append(" \\\n", pop_comments(pos), '..') - next_space = "" - end - if type(bit) == "string" then - if not (type(tree[i - 1]) == 'string' and is_operator(tree[i - 1]) ~= is_operator(bit)) then - nomsu:append(next_space) - end - nomsu:append(bit) - next_space = ' ' - elseif bit.type == "Block" then - nomsu:append(recurse(bit, #nomsu:match('[^\n]*$'))) - pos = bit.source.stop - next_space = inline and " " or "\n.." - else - nomsu:append(next_space) - local bit_nomsu = recurse(bit, #nomsu:match('[^\n]*$')) - if bit.type == "Action" and not bit_nomsu:is_multiline() then - bit_nomsu:parenthesize() - end - nomsu:append(bit_nomsu) - pos = bit.source.stop - next_space = bit_nomsu:is_multiline() and "\n.." or " " - end - end - nomsu:append(pop_comments(tree.source.stop, '\n')) - return nomsu - elseif "EscapedNomsu" == _exp_0 then - local val_nomsu = recurse(tree[1], 1) - if tree[1].type == "Action" and not val_nomsu:is_multiline() then - val_nomsu:parenthesize() - end - return NomsuCode(tree.source, "\\", val_nomsu) - elseif "Block" == _exp_0 then - local nomsu = NomsuCode(tree.source, pop_comments(tree.source.start)) - for i, line in ipairs(tree) do - nomsu:append(pop_comments(line.source.start, i > 1 and '\n' or '')) - local line_nomsu = recurse(line) - nomsu:append(line_nomsu) - if i < #tree then - nomsu:append(line_nomsu:match('\n[^\n]*\n') and "\n\n" or "\n") - end - end - nomsu:append(pop_comments(tree.source.stop, '\n')) - return NomsuCode(tree.source, ":\n ", nomsu) - elseif "Text" == _exp_0 then - local max_line = math.floor(1.5 * MAX_LINE) - local add_text - add_text = function(nomsu, tree) - for i, bit in ipairs(tree) do - if type(bit) == 'string' then - bit = escape(bit) - local bit_lines = Files.get_lines(bit) - for j, line in ipairs(bit_lines) do - if j > 1 then - nomsu:append("\n") - elseif #line > 10 and nomsu:trailing_line_len() > max_line then - nomsu:append("\\\n..") - end - while #line > 0 do - local space = max_line - nomsu:trailing_line_len() - local split = find(line, "[%p%s]", space) - if not split or split > space + 10 then - split = space + 10 - end - if #line - split < 10 then - split = #line - end - local bite - bite, line = sub(line, 1, split), sub(line, split + 1, -1) - nomsu:append(bite) - if #line > 0 then - nomsu:append("\\\n..") - end - end - end - elseif bit.type == "Text" then - add_text(nomsu, bit) - else - nomsu:append("\\") - local interp_nomsu = recurse(bit, #nomsu:match('[^\n]*$')) - if not (interp_nomsu:is_multiline()) then - if bit.type == "Var" then - if type(tree[i + 1]) == 'string' and not match(tree[i + 1], "^[ \n\t,.:;#(){}[%]]") then - interp_nomsu:parenthesize() - end - elseif bit.type ~= "List" and bit.type ~= "Dict" then - interp_nomsu:parenthesize() - end - end - nomsu:append(interp_nomsu) - if interp_nomsu:is_multiline() then - nomsu:append("\n..") - end - end - end - end - local nomsu = NomsuCode(tree.source) - add_text(nomsu, tree) - return NomsuCode(tree.source, '"\\\n ..', nomsu, '"') - elseif "List" == _exp_0 or "Dict" == _exp_0 then - assert(#tree > 0) - local nomsu = NomsuCode(tree.source, pop_comments(tree[1].source.start)) - for i, item in ipairs(tree) do - if nomsu:trailing_line_len() == 0 then - nomsu:append(pop_comments(item.source.start)) - end - local inline_nomsu = self:tree_to_inline_nomsu(item) - local item_nomsu = #tostring(inline_nomsu) <= MAX_LINE and inline_nomsu or recurse(item, #nomsu:match('[^\n]*$')) - nomsu:append(item_nomsu) - if i < #tree then - nomsu:append((item_nomsu:is_multiline() or nomsu:trailing_line_len() + #tostring(item_nomsu) >= MAX_LINE) and '\n' or ', ') - end - end - nomsu:append(pop_comments(tree.source.stop, '\n')) - if tree.type == "List" then - return NomsuCode(tree.source, "[..]\n ", nomsu) - else - return NomsuCode(tree.source, "{..}\n ", nomsu) - end - elseif "DictEntry" == _exp_0 then - local key, value = tree[1], tree[2] - local nomsu - if key.type == "Text" and #key == 1 and is_identifier(key[1]) then - nomsu = NomsuCode(key.source, key[1]) - else - nomsu = self:tree_to_inline_nomsu(key) - end - if key.type == "Action" or key.type == "Block" then - nomsu:parenthesize() - end - nomsu:append(": ", recurse(value, #tostring(nomsu))) - return nomsu - elseif "IndexChain" == _exp_0 or "Number" == _exp_0 or "Var" == _exp_0 or "Comment" == _exp_0 or "Error" == _exp_0 then - return self:tree_to_inline_nomsu(tree) - else - return error("Unknown type: " .. tostring(tree.type)) - end - end -end -return NomsuCompiler +}) +return compile |
