nomsu/nomsu_environment.lua
Bruce Hill 057f5b74eb Moving to better, more centralized versioning system. Now the Nomsu
version is just defined as a list at the top of nomsu.moon, and uses
X.Y.Z form instead of X.Y.Z.W. Added a ([...], from 2) method and a
([...], up to 5) method, and fixed a few upgrade bugs.
2019-02-06 15:34:00 -08:00

388 lines
11 KiB
Lua

local NomsuCode, LuaCode, Source
do
local _obj_0 = require("code_obj")
NomsuCode, LuaCode, Source = _obj_0.NomsuCode, _obj_0.LuaCode, _obj_0.Source
end
local List, Dict
do
local _obj_0 = require('containers')
List, Dict = _obj_0.List, _obj_0.Dict
end
local Text = require('text')
local SyntaxTree = require("syntax_tree")
local Files = require("files")
local Errhand = require("error_handling")
local C = require("colors")
local make_parser = require("parser")
local pretty_error = require("pretty_errors")
local make_tree
make_tree = function(tree, userdata)
tree.source = Source(userdata.filename, tree.start, tree.stop)
tree.start, tree.stop = nil, nil
tree = SyntaxTree(tree)
return tree
end
local Parsers = { }
for version = 1, NOMSU_VERSION[1] do
local peg_file
if package.nomsupath then
local pegpath = package.nomsupath:gsub("lib/%?%.nom", "?.peg"):gsub("lib/%?%.lua", "?.peg")
do
local path = package.searchpath("nomsu." .. tostring(version), pegpath, "/")
if path then
peg_file = io.open(path)
end
end
else
peg_file = io.open("nomsu." .. tostring(version) .. ".peg")
end
assert(peg_file, "No PEG file found for Nomsu version " .. tostring(version))
local peg_contents = peg_file:read('*a')
peg_file:close()
Parsers[version] = make_parser(peg_contents, make_tree)
end
local tree_to_nomsu, tree_to_inline_nomsu
do
local _obj_0 = require("nomsu_decompiler")
tree_to_nomsu, tree_to_inline_nomsu = _obj_0.tree_to_nomsu, _obj_0.tree_to_inline_nomsu
end
local compile, fail_at
do
local _obj_0 = require('nomsu_compiler')
compile, fail_at = _obj_0.compile, _obj_0.fail_at
end
local _module_imports = { }
local _importer_mt = {
__index = function(self, k)
return _module_imports[self][k]
end
}
local Importer
Importer = function(t, imports)
_module_imports[t] = imports or { }
t._IMPORTS = _module_imports[t]
return setmetatable(t, _importer_mt)
end
local _1_as_text
_1_as_text = function(x)
if x == true then
return "yes"
end
if x == false then
return "no"
end
return tostring(x)
end
local _1_as_list
_1_as_list = function(x)
local mt = getmetatable(x)
if mt.as_list then
return mt.as_list(x)
end
return x
end
local nomsu_environment
nomsu_environment = Importer({
next = next,
unpack = unpack or table.unpack,
setmetatable = setmetatable,
rawequal = rawequal,
getmetatable = getmetatable,
pcall = pcall,
yield = coroutine.yield,
resume = coroutine.resume,
coroutine_status_of = coroutine.status,
coroutine_wrap = coroutine.wrap,
coroutine_from = coroutine.create,
error = error,
package = package,
os = os,
require = require,
tonumber = tonumber,
tostring = tostring,
string = string,
xpcall = xpcall,
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,
jit = jit,
_VERSION = _VERSION,
LUA_VERSION = (jit and jit.version or _VERSION),
LUA_API = _VERSION,
Bit = (jit or _VERSION == "Lua 5.2") and require('bitops') or nil,
a_List = List,
a_Dict = Dict,
Text = Text,
lpeg = lpeg,
re = re,
Files = Files,
SyntaxTree = SyntaxTree,
TESTS = Dict({ }),
globals = Dict({ }),
LuaCode = LuaCode,
NomsuCode = NomsuCode,
Source = Source,
LuaCode_from = (function(src, ...)
return LuaCode:from(src, ...)
end),
NomsuCode_from = (function(src, ...)
return NomsuCode:from(src, ...)
end),
enhance_error = Errhand.enhance_error,
SOURCE_MAP = { },
getfenv = getfenv,
_1_as_nomsu = tree_to_nomsu,
_1_as_inline_nomsu = tree_to_inline_nomsu,
compile = compile,
at_1_fail = fail_at,
_1_as_text = _1_as_text,
_1_as_list = _1_as_list,
exit = os.exit,
quit = os.exit,
_1_parsed = function(nomsu_code, syntax_version)
if type(nomsu_code) == 'string' then
local filename = Files.spoof(nomsu_code)
nomsu_code = NomsuCode:from(Source(filename, 1, #nomsu_code), nomsu_code)
end
local source = nomsu_code.source
nomsu_code = tostring(nomsu_code)
local version = nomsu_code:match("^#![^\n]*nomsu[ ]+-V[ ]*([0-9.]+)")
if syntax_version then
syntax_version = tonumber(syntax_version:match("^[0-9]+"))
end
syntax_version = syntax_version or (version and tonumber(version:match("^[0-9]+")) or NOMSU_VERSION[1])
local parse = Parsers[syntax_version]
assert(parse, "No parser found for Nomsu syntax version " .. tostring(syntax_version))
local tree = parse(nomsu_code, source.filename)
if tree.shebang then
tree.version = tree.version or tree.shebang:match("nomsu %-V[ ]*([%d.]*)")
end
return tree
end,
Module = function(self, package_name)
local path
if package_name:match("%.nom$") or package_name:match("%.lua") then
path = package_name
else
local err
path, err = package.searchpath(package_name, package.nomsupath, "/")
if not path then
error(err)
end
end
path = path:gsub("^%./", "")
do
local ret = package.nomsuloaded[package_name] or package.nomsuloaded[path]
if ret then
return ret
end
end
local currently_running = { }
for i = 2, 999 do
local info = debug.getinfo(i, 'f')
if not (info.func) then
break
end
if info.func == self.Module then
local n, upper_path = debug.getlocal(i, 3)
table.insert(currently_running, upper_path)
assert(n == "path")
if upper_path == path then
local circle = table.concat(currently_running, "', which imports '")
local err_i = 2
info = debug.getinfo(err_i)
while info and (info.func == self.Module or info.func == self.use or info.func == self.export) do
err_i = err_i + 1
info = debug.getinfo(err_i)
end
fail_at((info or debug.getinfo(2)), "Circular import: File '" .. tostring(path) .. "' imports '" .. circle .. "'")
end
end
end
local mod = self:new_environment()
mod.MODULE_NAME = package_name
local code = Files.read(path)
if path:match("%.lua$") then
code = LuaCode:from(Source(path, 1, #code), code)
else
code = NomsuCode:from(Source(path, 1, #code), code)
end
local ret = mod:run(code)
if ret ~= nil then
mod = ret
end
package.nomsuloaded[package_name] = mod
package.nomsuloaded[path] = mod
return mod
end,
use = function(self, package_name)
local mod = self:Module(package_name)
local imports = assert(_module_imports[self])
for k, v in pairs(mod) do
imports[k] = v
end
local cr_imports = assert(_module_imports[self.COMPILE_RULES])
if mod.COMPILE_RULES then
for k, v in pairs(mod.COMPILE_RULES) do
cr_imports[k] = v
end
end
return mod
end,
export = function(self, package_name)
local mod = self:Module(package_name)
local imports = assert(_module_imports[self])
for k, v in pairs(_module_imports[mod]) do
if rawget(imports, k) == nil then
imports[k] = v
end
end
for k, v in pairs(mod) do
if rawget(self, k) == nil then
self[k] = v
end
end
local cr_imports = assert(_module_imports[self.COMPILE_RULES])
if mod.COMPILE_RULES then
for k, v in pairs(_module_imports[mod.COMPILE_RULES]) do
if rawget(cr_imports, k) == nil then
cr_imports[k] = v
end
end
for k, v in pairs(mod.COMPILE_RULES) do
if rawget(self.COMPILE_RULES, k) == nil then
self.COMPILE_RULES[k] = v
end
end
end
return mod
end,
run = function(self, to_run)
if not to_run then
error("Need both something to run and an environment")
end
if type(to_run) == 'string' then
local filename = Files.spoof(to_run)
to_run = NomsuCode:from(Source(filename, 1, #to_run), to_run)
return self:run(to_run)
elseif NomsuCode:is_instance(to_run) then
local tree = self._1_parsed(to_run)
if tree == nil then
return nil
end
return self:run(tree)
elseif SyntaxTree:is_instance(to_run) then
local filename = to_run.source.filename:gsub("\n.*", "...")
if to_run.type ~= "FileChunks" then
to_run = {
to_run
}
end
local ret = nil
for chunk_no, chunk in ipairs(to_run) do
local lua = self:compile(chunk)
lua:declare_locals()
lua:prepend("-- File: " .. tostring(filename) .. " chunk #" .. tostring(chunk_no) .. "\n")
ret = self:run(lua)
end
return ret
elseif LuaCode:is_instance(to_run) then
local source = to_run.source
local lua_string = to_run:text()
lua_string = lua_string:gsub("^#![^\n]*\n", "")
local run_lua_fn, err = load(lua_string, tostring(source), "t", self)
if not run_lua_fn then
local lines
do
local _accum_0 = { }
local _len_0 = 1
for i, line in ipairs(lua_string:lines()) do
_accum_0[_len_0] = ("%3d|%s"):format(i, line)
_len_0 = _len_0 + 1
end
lines = _accum_0
end
local line_numbered_lua = table.concat(lines, "\n")
error("Failed to compile generated code:\n" .. tostring(C("bright blue", line_numbered_lua)) .. "\n\n" .. tostring(err), 0)
end
local source_key = tostring(source)
if not (self.SOURCE_MAP[source_key] or self.OPTIMIZATION >= 2) 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)
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(to_run)
map[lua_line] = map[lua_line] or nomsu_line
map[0] = 0
self.SOURCE_MAP[source_key] = map
end
return run_lua_fn()
else
return error("Attempt to run unknown thing: " .. _1_as_lua(to_run))
end
end,
new_environment = function()
local env = Importer({ }, (function()
local _tbl_0 = { }
for k, v in pairs(nomsu_environment) do
_tbl_0[k] = v
end
return _tbl_0
end)())
env._ENV = env
env._G = env
env.TESTS = Dict({ })
env.COMPILE_RULES = Importer({ }, (function()
local _tbl_0 = { }
for k, v in pairs(nomsu_environment.COMPILE_RULES) do
_tbl_0[k] = v
end
return _tbl_0
end)())
return env
end
})
nomsu_environment._ENV = nomsu_environment
nomsu_environment._G = nomsu_environment
nomsu_environment.COMPILE_RULES = Importer(require('bootstrap'))
nomsu_environment.MODULE_NAME = "nomsu"
SOURCE_MAP = nomsu_environment.SOURCE_MAP
return nomsu_environment