aboutsummaryrefslogtreecommitdiff
path: root/nomsu_compiler.lua
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2018-11-08 15:23:22 -0800
committerBruce Hill <bruce@bruce-hill.com>2018-11-08 15:24:15 -0800
commit652c29bdef1f0991cc13bef59d6dc78b657ae9a4 (patch)
tree8e335399e77b1893657b9fa985db0738034daac3 /nomsu_compiler.lua
parent1f3660f393c1a17988a15b89f18686b28e51a9e7 (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.lua1409
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