1 local unpack = unpack or table.unpack
2 local match, sub, gsub, format, byte, find
5 match, sub, gsub, format, byte, find = _obj_0.match, _obj_0.sub, _obj_0.gsub, _obj_0.format, _obj_0.byte, _obj_0.find
9 local _obj_0 = require("code_obj")
10 LuaCode, Source = _obj_0.LuaCode, _obj_0.Source
13 local SyntaxTree = require("syntax_tree")
14 local Files = require("files")
15 local pretty_error = require("pretty_errors")
17 fail_at = function(source, msg)
19 if SyntaxTree:is_instance(source) then
20 file = source:get_source_file()
21 source = source.source
22 elseif type(source) == 'string' then
23 source = Source:from_string(source)
24 elseif not Source:is_instance(source) then
25 assert(source.short_src and source.currentline)
26 file = Files.read(source.short_src)
27 assert(file, "Could not find " .. tostring(source.short_src))
28 local lines = file:lines()
30 for i = 1, source.currentline - 1 do
31 start = start + #lines[i]
33 local stop = start + #lines[source.currentline]
34 source = Source(source.short_src, start, stop)
36 if source and not file then
37 file = Files.read(source.filename)
41 local path = tostring(NOMSU_PREFIX) .. "/share/nomsu/" .. tostring(table.concat(NOMSU_VERSION, ".")) .. "/" .. tostring(source.filename)
42 file = Files.read(path)
46 error("Can't find file: " .. tostring(source.filename))
48 local title, err_msg, hint = msg:match("([^:]*):[ \n]+(.*)[ \n]+Hint: (.*)")
50 err_msg, hint = msg:match("(.*)[ \n]+Hint:[ \n]+(.*)")
54 title, err_msg = msg:match("([^:]*):[ \n]+(.*)")
60 local err_str = pretty_error({
67 filename = source.filename
69 return error(err_str, 0)
71 local re = require('re')
72 local math_expression = re.compile([[ (([*/^+-] / [0-9]+) " ")* [*/^+-] !. ]])
75 compile = function(self, tree)
77 error("No tree was passed in.")
79 if tree.version and tree.version < self.NOMSU_VERSION:up_to(#tree.version) and self._1_upgraded_from_2_to then
80 tree = self._1_upgraded_from_2_to(tree, tree.version, self.NOMSU_VERSION)
82 local _exp_0 = tree.type
83 if "Action" == _exp_0 then
84 local stub = tree.stub
85 local compile_action = self.COMPILE_RULES[stub]
86 if not compile_action and math_expression:match(stub) then
87 local lua = LuaCode:from(tree.source)
88 for i, tok in ipairs(tree) do
89 if type(tok) == 'string' then
92 local tok_lua = self:compile(tok)
93 if tok.type == "Action" then
94 tok_lua:parenthesize()
104 if not compile_action then
105 local seen_words = { }
107 for word in stub:gmatch("[^0-9 ][^ ]*") do
108 if not (seen_words[word]) then
109 seen_words[word] = true
110 table.insert(words, word)
114 local stub2 = table.concat(words, " ")
115 compile_action = self.COMPILE_RULES[stub2]
116 if compile_action then
117 if debug.getinfo(compile_action, 'u').isvararg then
124 if compile_action then
129 for _index_0 = 1, #tree do
130 local arg = tree[_index_0]
131 if type(arg) ~= "string" then
132 _accum_0[_len_0] = arg
138 local ret = compile_action(self, tree, unpack(args))
140 local info = debug.getinfo(compile_action, "S")
141 local filename = Source:from_string(info.source).filename
142 fail_at(tree, ("Compile error: The compile-time action here (" .. tostring(stub) .. ") failed to return any value. " .. "Hint: Look at the implementation of (" .. tostring(stub) .. ") in " .. tostring(filename) .. ":" .. tostring(info.linedefined) .. " and make sure it's returning something."))
144 if not (SyntaxTree:is_instance(ret)) then
145 ret.source = ret.source or tree.source
149 return self:compile(ret)
152 local lua = LuaCode:from(tree.source)
153 lua:add((stub):as_lua_id(), "(")
154 for argnum, arg in ipairs(tree:get_args()) do
155 local arg_lua = self:compile(arg)
156 if arg.type == "Block" and #arg > 1 then
157 arg_lua = LuaCode:from(arg.source, "(function()\n ", arg_lua, "\nend)()")
159 if lua:trailing_line_len() + #arg_lua > MAX_LINE then
160 lua:add(argnum > 1 and ",\n " or "\n ")
161 elseif argnum > 1 then
168 elseif "MethodCall" == _exp_0 then
169 local stub = tree:get_stub()
170 local compile_action = self.COMPILE_RULES[stub]
171 if compile_action then
172 local args = tree:get_args()
173 local ret = compile_action(self, tree, unpack(args))
175 local info = debug.getinfo(compile_action, "S")
176 local filename = Source:from_string(info.source).filename
177 fail_at(tree, ("Compile error: The compile-time method here (" .. tostring(stub) .. ") failed to return any value. " .. "Hint: Look at the implementation of (" .. tostring(stub) .. ") in " .. tostring(filename) .. ":" .. tostring(info.linedefined) .. " " .. "and make sure it's returning something."))
179 if not (SyntaxTree:is_instance(ret)) then
180 ret.source = ret.source or tree.source
184 return self:compile(ret)
187 local lua = LuaCode:from(tree.source)
188 local target_lua = self:compile(tree[1])
189 local target_text = target_lua:text()
190 if not (target_text:match("^%(.*%)$") or target_text:match("^[_a-zA-Z][_a-zA-Z0-9.]*$") or tree[1].type == "IndexChain") then
191 target_lua:parenthesize()
193 local self_lua = #tree > 2 and "_self" or target_lua
195 lua:add("(function(", self_lua, ")\n ")
201 if i > 2 and i == #tree then
204 lua:add(self_lua, ":")
205 lua:add((tree[i].stub):as_lua_id(), "(")
206 for argnum, arg in ipairs(tree[i]:get_args()) do
207 local arg_lua = self:compile(arg)
208 if arg.type == "Block" and #arg > 1 then
209 arg_lua = LuaCode:from(arg.source, "(function()\n ", arg_lua, "\nend)()")
211 if lua:trailing_line_len() + #arg_lua > MAX_LINE then
212 lua:add(argnum > 1 and ",\n " or "\n ")
213 elseif argnum > 1 then
221 lua:add("\nend)(", target_lua, ")")
224 elseif "EscapedNomsu" == _exp_0 then
225 local lua = LuaCode:from(tree.source, "SyntaxTree{")
226 local needs_comma, i = false, 1
229 if type(x) == 'number' then
231 elseif SyntaxTree:is_instance(x) then
232 return self:compile(x)
233 elseif Source:is_instance(x) then
234 return tostring(x):as_lua()
239 for k, v in pairs((SyntaxTree:is_instance(tree[1]) and tree[1].type == "EscapedNomsu" and tree) or tree[1]) do
240 local entry_lua = LuaCode()
243 elseif type(k) == 'string' and match(k, "[_a-zA-Z][_a-zA-Z0-9]*") then
244 entry_lua:add(k, "= ")
246 entry_lua:add("[", as_lua(k), "]= ")
248 entry_lua:add(as_lua(v))
252 if lua:trailing_line_len() + #(entry_lua:match("^[\n]*")) > MAX_LINE then
254 elseif needs_comma then
262 elseif "Block" == _exp_0 then
263 local lua = LuaCode:from(tree.source)
264 for i, line in ipairs(tree) do
265 if line.type == "Error" then
266 return self:compile(line)
269 for i, line in ipairs(tree) do
273 local line_lua = self:compile(line)
275 if not (line_lua:last(1) == ";" or line_lua:last(4):match("[^_a-zA-Z0-9]end$")) then
280 elseif "Text" == _exp_0 then
282 return LuaCode:from(tree.source, '""')
284 if #tree == 1 and type(tree[1]) == 'string' then
285 return LuaCode:from(tree.source, tree[1]:as_lua())
287 local lua = LuaCode:from(tree.source, "Text(")
289 local string_buffer = ""
291 add_bit = function(bit)
293 if lua:trailing_line_len() + #bit > MAX_LINE then
302 for i, bit in ipairs(tree) do
303 local _continue_0 = false
305 if type(bit) == "string" then
306 string_buffer = string_buffer .. bit
310 if string_buffer ~= "" then
311 for i = 1, #string_buffer, MAX_LINE do
312 add_bit(string_buffer:sub(i, i + MAX_LINE - 1):as_lua())
316 local bit_lua = self:compile(bit)
317 if bit.type == "Block" then
318 bit_lua = LuaCode:from(bit.source, "a_List(function(add)", "\n ", bit_lua, "\nend):joined()")
323 if not _continue_0 then
327 if string_buffer ~= "" then
328 for i = 1, #string_buffer, MAX_LINE do
329 add_bit(string_buffer:sub(i, i + MAX_LINE - 1):as_lua())
334 return LuaCode:from(tree.source, '""')
338 elseif "List" == _exp_0 or "Dict" == _exp_0 then
339 local typename = "a_" .. tree.type
341 return LuaCode:from(tree.source, typename, "{}")
343 local lua = LuaCode:from(tree.source)
347 if tree[i].type == 'Block' then
351 lua:add(typename, "(function(", (tree.type == 'List' and "add" or ("add, " .. ("add 1 ="):as_lua_id())), ")")
352 local body = self:compile(tree[i])
353 body:declare_locals()
354 lua:add("\n ", body, "\nend)")
362 local items_lua = LuaCode:from(tree[i].source)
364 if tree[i].type == "Block" then
367 local item_lua = self:compile(tree[i])
368 if item_lua:match("^%.[a-zA-Z_]") then
369 item_lua = item_lua:text():sub(2)
371 if tree.type == 'Dict' and tree[i].type == 'Index' then
372 item_lua = LuaCode:from(tree[i].source, item_lua, "=true")
374 items_lua:add(sep, item_lua)
375 if tree[i].type == "Comment" then
378 elseif items_lua:trailing_line_len() > MAX_LINE then
379 sep = items_lua:last(1) == ";" and "\n " or ",\n "
381 sep = items_lua:last(1) == ";" and " " or ", "
385 if items_lua:is_multiline() then
386 lua:add(LuaCode:from(items_lua.source, typename, "{\n ", items_lua, "\n}"))
388 lua:add(LuaCode:from(items_lua.source, typename, "{", items_lua, "}"))
394 elseif "Index" == _exp_0 then
395 local key_lua = self:compile(tree[1])
396 local key_str = key_lua:match('^"([a-zA-Z_][a-zA-Z0-9_]*)"$')
397 if key_str and key_str:is_lua_id() then
398 return LuaCode:from(tree.source, ".", key_str)
399 elseif key_lua:first(1) == "[" then
400 return LuaCode:from(tree.source, "[ ", key_lua, "]")
402 return LuaCode:from(tree.source, "[", key_lua, "]")
404 elseif "DictEntry" == _exp_0 then
406 if key.type ~= "Index" then
413 return LuaCode:from(tree.source, self:compile(key), "=", (tree[2] and self:compile(tree[2]) or "true"))
414 elseif "IndexChain" == _exp_0 then
415 local lua = self:compile(tree[1])
416 if lua:match("['\"}]$") or lua:match("]=*]$") then
419 if lua:text() == "..." then
420 return LuaCode:from(tree.source, "select(", self:compile(tree[2][1]), ", ...)")
424 if key.type ~= "Index" then
431 lua:add(self:compile(key))
434 elseif "Number" == _exp_0 then
435 local number = tostring(tree[1]):gsub("_", "")
436 return LuaCode:from(tree.source, number)
437 elseif "Var" == _exp_0 then
438 if tree[1].type == "MethodCall" then
439 return LuaCode:from(tree.source, self:compile(tree[1][1]), ".", tree[1][2]:get_stub():as_lua_id())
441 return LuaCode:from(tree.source, tree:as_var():as_lua_id())
442 elseif "FileChunks" == _exp_0 then
443 return error("Can't convert FileChunks to a single block of lua, since each chunk's " .. "compilation depends on the earlier chunks")
444 elseif "Comment" == _exp_0 then
445 return LuaCode:from(tree.source, "-- ", (tree[1]:gsub('\n', '\n-- ')))
446 elseif "Error" == _exp_0 then
447 local err_msg = pretty_error({
448 title = "Parse error",
451 source = tree:get_source_file(),
452 start = tree.source.start,
453 stop = tree.source.stop,
454 filename = tree.source.filename
456 return error(err_msg)
458 return error("Unknown type: " .. tostring(tree.type))