code / nomsu

Lines6.6K Lua5.1K PEG1.3K make117
2 others 83
Markdown60 Bourne Again Shell23
(426 lines)
1 local NomsuCode, LuaCode, Source
2 do
3 local _obj_0 = require("code_obj")
4 NomsuCode, LuaCode, Source = _obj_0.NomsuCode, _obj_0.LuaCode, _obj_0.Source
5 end
6 local List, Dict
7 do
8 local _obj_0 = require('containers')
9 List, Dict = _obj_0.List, _obj_0.Dict
10 end
11 local Text = require('text')
12 local SyntaxTree = require("syntax_tree")
13 local Files = require("files")
14 local Errhand = require("error_handling")
15 local C = require("colors")
16 local make_parser = require("parser")
17 local pretty_error = require("pretty_errors")
18 local make_tree
19 make_tree = function(tree, userdata)
20 tree.source = Source(userdata.filename, tree.start, tree.stop)
21 tree.start, tree.stop = nil, nil
22 tree = SyntaxTree(tree)
23 return tree
24 end
25 local Parsers = { }
26 for version = 1, NOMSU_VERSION[1] do
27 local peg_file
28 if package.nomsupath then
29 local pegpath = package.nomsupath:gsub("lib/%?%.nom", "?.peg"):gsub("lib/%?%.lua", "?.peg")
30 do
31 local path = package.searchpath("nomsu." .. tostring(version), pegpath, "/")
32 if path then
33 peg_file = io.open(path)
34 end
35 end
36 else
37 peg_file = io.open("nomsu." .. tostring(version) .. ".peg")
38 end
39 assert(peg_file, "No PEG file found for Nomsu version " .. tostring(version))
40 local peg_contents = peg_file:read('*a')
41 peg_file:close()
42 Parsers[version] = make_parser(peg_contents, make_tree)
43 end
44 local tree_to_nomsu, tree_to_inline_nomsu
45 do
46 local _obj_0 = require("nomsu_decompiler")
47 tree_to_nomsu, tree_to_inline_nomsu = _obj_0.tree_to_nomsu, _obj_0.tree_to_inline_nomsu
48 end
49 local compile, fail_at
50 do
51 local _obj_0 = require('nomsu_compiler')
52 compile, fail_at = _obj_0.compile, _obj_0.fail_at
53 end
54 local _module_imports = { }
55 local _importer_mt = {
56 __index = function(self, k)
57 return _module_imports[self][k]
58 end
60 local Importer
61 Importer = function(t, imports)
62 _module_imports[t] = imports or { }
63 t._IMPORTS = _module_imports[t]
64 return setmetatable(t, _importer_mt)
65 end
66 local _1_as_text
67 _1_as_text = function(x)
68 if x == true then
69 return "yes"
70 end
71 if x == false then
72 return "no"
73 end
74 return tostring(x)
75 end
76 local nomsu_pairs
77 nomsu_pairs = function(self)
78 local mt = getmetatable(self)
79 if mt and mt.__next then
80 return mt.__next, self, nil
81 else
82 return next, self, nil
83 end
84 end
85 local inext
86 if _VERSION == "Lua 5.2" or _VERSION == "Lua 5.1" then
87 inext = function(self, i)
88 local value = self[i + 1]
89 if value ~= nil then
90 return i + 1, value
91 end
92 end
93 else
94 inext = ipairs({ })
95 end
96 local nomsu_ipairs
97 nomsu_ipairs = function(self)
98 local mt = getmetatable(self)
99 if mt and mt.__inext then
100 return mt.__inext, self, 0
101 else
102 return inext, self, 0
103 end
104 end
105 local nomsu_environment
106 nomsu_environment = Importer({
107 next = next,
108 inext = inext,
109 unpack = unpack or table.unpack,
110 setmetatable = setmetatable,
111 rawequal = rawequal,
112 getmetatable = getmetatable,
113 pcall = pcall,
114 yield = coroutine.yield,
115 resume = coroutine.resume,
116 coroutine_status_of = coroutine.status,
117 coroutine_wrap = coroutine.wrap,
118 coroutine_from = coroutine.create,
119 error = error,
120 package = package,
121 os = os,
122 require = require,
123 tonumber = tonumber,
124 tostring = tostring,
125 string = string,
126 xpcall = xpcall,
127 print = print,
128 loadfile = loadfile,
129 rawset = rawset,
130 _VERSION = _VERSION,
131 collectgarbage = collectgarbage,
132 rawget = rawget,
133 rawlen = rawlen,
134 table = table,
135 assert = assert,
136 dofile = dofile,
137 loadstring = loadstring,
138 lua_type_of = type,
139 select = select,
140 math = math,
141 io = io,
142 load = load,
143 pairs = nomsu_pairs,
144 ipairs = ipairs,
145 _ipairs = nomsu_ipairs,
146 jit = jit,
147 _VERSION = _VERSION,
148 LUA_VERSION = (jit and jit.version or _VERSION),
149 LUA_API = _VERSION,
150 Bit = (jit or _VERSION == "Lua 5.2") and require('bitops') or nil,
151 a_List = List,
152 a_Dict = Dict,
153 Text = Text,
154 lpeg = lpeg,
155 re = re,
156 Files = Files,
157 SyntaxTree = SyntaxTree,
158 TESTS = Dict({ }),
159 globals = Dict({ }),
160 LuaCode = LuaCode,
161 NomsuCode = NomsuCode,
162 Source = Source,
163 LuaCode_from = (function(src, ...)
164 return LuaCode:from(src, ...)
165 end),
166 NomsuCode_from = (function(src, ...)
167 return NomsuCode:from(src, ...)
168 end),
169 enhance_error = Errhand.enhance_error,
170 SOURCE_MAP = { },
171 getfenv = getfenv,
172 _1_as_nomsu = tree_to_nomsu,
173 _1_as_inline_nomsu = tree_to_inline_nomsu,
174 compile = compile,
175 at_1_fail = fail_at,
176 _1_as_text = _1_as_text,
177 _1_as_an_iterable = _1_as_an_iterable,
178 exit = os.exit,
179 quit = os.exit,
180 _1_parsed = function(nomsu_code, syntax_version)
181 if type(nomsu_code) == 'string' then
182 local filename = Files.spoof(nomsu_code)
183 nomsu_code = NomsuCode:from(Source(filename, 1, #nomsu_code), nomsu_code)
184 end
185 local source = nomsu_code.source
186 nomsu_code = tostring(nomsu_code)
187 local version = nomsu_code:match("^#![^\n]*nomsu[ ]+-V[ ]*([0-9.]+)")
188 if syntax_version then
189 syntax_version = tonumber(syntax_version:match("^[0-9]+"))
190 end
191 syntax_version = syntax_version or (version and tonumber(version:match("^[0-9]+")) or NOMSU_VERSION[1])
192 local parse = Parsers[syntax_version]
193 assert(parse, "No parser found for Nomsu syntax version " .. tostring(syntax_version))
194 local tree = parse(nomsu_code, source.filename)
195 if tree.shebang then
196 local shebang_version = tree.shebang:match("nomsu %-V[ ]*([%d.]+)")
197 if shebang_version and shebang_version ~= "" then
198 tree.version = tree.version or List((function()
199 local _accum_0 = { }
200 local _len_0 = 1
201 for v in shebang_version:gmatch("%d+") do
202 _accum_0[_len_0] = tonumber(v)
203 _len_0 = _len_0 + 1
204 end
205 return _accum_0
206 end)())
207 end
208 end
209 return tree
210 end,
211 Module = function(self, package_name)
212 local path
213 if package_name:match("%.nom$") or package_name:match("%.lua") then
214 path = package_name
215 else
216 local err
217 path, err = package.searchpath(package_name, package.nomsupath, "/")
218 if not path then
219 error(err)
220 end
221 end
222 path = path:gsub("^%./", "")
224 local ret = package.nomsuloaded[package_name] or package.nomsuloaded[path]
225 if ret then
226 return ret
227 end
228 end
229 local currently_running = { }
230 for i = 2, 999 do
231 local info = debug.getinfo(i, 'f')
232 if not (info.func) then
233 break
234 end
235 if info.func == self.Module then
236 local n, upper_path = debug.getlocal(i, 3)
237 table.insert(currently_running, upper_path)
238 assert(n == "path")
239 if upper_path == path then
240 local circle = table.concat(currently_running, "', which imports '")
241 local err_i = 2
242 info = debug.getinfo(err_i)
243 while info and (info.func == self.Module or info.func == self.use or info.func == self.export) do
244 err_i = err_i + 1
245 info = debug.getinfo(err_i)
246 end
247 fail_at((info or debug.getinfo(2)), "Circular import: File '" .. tostring(path) .. "' imports '" .. circle .. "'")
248 end
249 end
250 end
251 local mod = self:new_environment()
252 mod.MODULE_NAME = package_name
253 local code = Files.read(path)
254 if path:match("%.lua$") then
255 code = LuaCode:from(Source(path, 1, #code), code)
256 else
257 code = NomsuCode:from(Source(path, 1, #code), code)
258 end
259 local ret = mod:run(code)
260 if ret ~= nil then
261 mod = ret
262 end
263 package.nomsuloaded[package_name] = mod
264 package.nomsuloaded[path] = mod
265 return mod
266 end,
267 use = function(self, package_name)
268 local mod = self:Module(package_name)
269 local imports = assert(_module_imports[self])
270 for k, v in pairs(mod) do
271 imports[k] = v
272 end
273 local cr_imports = assert(_module_imports[self.COMPILE_RULES])
274 if mod.COMPILE_RULES then
275 for k, v in pairs(mod.COMPILE_RULES) do
276 cr_imports[k] = v
277 end
278 end
279 return mod
280 end,
281 export = function(self, package_name)
282 local mod = self:Module(package_name)
283 local imports = assert(_module_imports[self])
284 for k, v in pairs(_module_imports[mod]) do
285 if rawget(imports, k) == nil then
286 imports[k] = v
287 end
288 end
289 for k, v in pairs(mod) do
290 if rawget(self, k) == nil then
291 self[k] = v
292 end
293 end
294 local cr_imports = assert(_module_imports[self.COMPILE_RULES])
295 if mod.COMPILE_RULES then
296 for k, v in pairs(_module_imports[mod.COMPILE_RULES]) do
297 if rawget(cr_imports, k) == nil then
298 cr_imports[k] = v
299 end
300 end
301 for k, v in pairs(mod.COMPILE_RULES) do
302 if rawget(self.COMPILE_RULES, k) == nil then
303 self.COMPILE_RULES[k] = v
304 end
305 end
306 end
307 return mod
308 end,
309 run = function(self, to_run)
310 if not to_run then
311 error("Need both something to run and an environment")
312 end
313 if type(to_run) == 'string' then
314 local filename = Files.spoof(to_run)
315 to_run = NomsuCode:from(Source(filename, 1, #to_run), to_run)
316 return self:run(to_run)
317 elseif NomsuCode:is_instance(to_run) then
318 local tree = self._1_parsed(to_run)
319 if tree == nil then
320 return nil
321 end
322 return self:run(tree)
323 elseif SyntaxTree:is_instance(to_run) then
324 local filename = to_run.source.filename:gsub("\n.*", "...")
325 local version = to_run.version
326 if to_run.type ~= "FileChunks" then
327 to_run = {
328 to_run
330 end
331 local ret = nil
332 for chunk_no, chunk in ipairs(to_run) do
333 chunk.version = version
334 local lua = self:compile(chunk)
335 lua:declare_locals()
336 lua:prepend("-- File: " .. tostring(filename) .. " chunk #" .. tostring(chunk_no) .. "\n")
337 ret = self:run(lua)
338 end
339 return ret
340 elseif LuaCode:is_instance(to_run) then
341 local source = to_run.source
342 local lua_string = to_run:text()
343 lua_string = lua_string:gsub("^#![^\n]*\n", "")
344 local run_lua_fn, err = load(lua_string, tostring(source), "t", self)
345 if not run_lua_fn then
346 local lines
348 local _accum_0 = { }
349 local _len_0 = 1
350 for i, line in ipairs(lua_string:lines()) do
351 _accum_0[_len_0] = ("%3d|%s"):format(i, line)
352 _len_0 = _len_0 + 1
353 end
354 lines = _accum_0
355 end
356 local line_numbered_lua = table.concat(lines, "\n")
357 error("Failed to compile generated code:\n" .. tostring(C("bright blue", line_numbered_lua)) .. "\n\n" .. tostring(err), 0)
358 end
359 local source_key = tostring(source)
360 if not (self.SOURCE_MAP[source_key] or self.OPTIMIZATION >= 2) then
361 local map = { }
362 local file = Files.read(source.filename)
363 if not file and NOMSU_PREFIX then
364 file = Files.read(tostring(NOMSU_PREFIX) .. "/share/nomsu/" .. tostring(table.concat(NOMSU_VERSION, ".")) .. "/" .. tostring(source.filename))
365 end
366 if not file then
367 error("Failed to find file: " .. tostring(source.filename))
368 end
369 local nomsu_str = file:sub(source.start, source.stop)
370 local lua_line = 1
371 local nomsu_line = Files.get_line_number(file, source.start)
372 local map_sources
373 map_sources = function(s)
374 if type(s) == 'string' then
375 for nl in s:gmatch("\n") do
376 map[lua_line] = map[lua_line] or nomsu_line
377 lua_line = lua_line + 1
378 end
379 else
380 if s.source and s.source.filename == source.filename then
381 nomsu_line = Files.get_line_number(file, s.source.start)
382 end
383 local _list_0 = s.bits
384 for _index_0 = 1, #_list_0 do
385 local b = _list_0[_index_0]
386 map_sources(b)
387 end
388 end
389 end
390 map_sources(to_run)
391 map[lua_line] = map[lua_line] or nomsu_line
392 map[0] = 0
393 self.SOURCE_MAP[source_key] = map
394 end
395 return run_lua_fn()
396 else
397 return error("Attempt to run unknown thing: " .. _1_as_lua(to_run))
398 end
399 end,
400 new_environment = function()
401 local env = Importer({ }, (function()
402 local _tbl_0 = { }
403 for k, v in pairs(nomsu_environment) do
404 _tbl_0[k] = v
405 end
406 return _tbl_0
407 end)())
408 env._ENV = env
409 env._G = env
410 env.TESTS = Dict({ })
411 env.COMPILE_RULES = Importer({ }, (function()
412 local _tbl_0 = { }
413 for k, v in pairs(nomsu_environment.COMPILE_RULES) do
414 _tbl_0[k] = v
415 end
416 return _tbl_0
417 end)())
418 return env
419 end
421 nomsu_environment._ENV = nomsu_environment
422 nomsu_environment._G = nomsu_environment
423 nomsu_environment.COMPILE_RULES = Importer(require('bootstrap'), nil)
424 nomsu_environment.MODULE_NAME = "nomsu"
425 SOURCE_MAP = nomsu_environment.SOURCE_MAP
426 return nomsu_environment