diff options
| author | Bruce Hill <bitbucket@bruce-hill.com> | 2018-06-19 01:12:43 -0700 |
|---|---|---|
| committer | Bruce Hill <bitbucket@bruce-hill.com> | 2018-06-19 01:12:54 -0700 |
| commit | 1c8c84f8d2bc27a49de2b9ffca7e08448cc9406d (patch) | |
| tree | 75264c346fabf720a871668cc82558596cf683b2 /error_handling.lua | |
| parent | d7d86e026831f60aae8193f1cfda7f53bf26a61e (diff) | |
Moved error logic into its own file.
Diffstat (limited to 'error_handling.lua')
| -rw-r--r-- | error_handling.lua | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/error_handling.lua b/error_handling.lua new file mode 100644 index 0000000..f5bd10b --- /dev/null +++ b/error_handling.lua @@ -0,0 +1,214 @@ +local debug_getinfo = debug.getinfo +local ok, to_lua = pcall(function() + return require('moonscript.base').to_lua +end) +if not ok then + to_lua = nil +end +local moonscript_line_tables = setmetatable({ }, { + __index = function(self, filename) + if not (to_lua) then + return nil + end + local _, line_table = to_lua(FILE_CACHE[filename]) + self[filename] = line_table + return line_table + end +}) +debug.getinfo = function(thread, f, what) + if what == nil then + f, what, thread = thread, f, nil + end + if type(f) == 'number' then + f = f + 1 + end + local info + if thread == nil then + info = debug_getinfo(f, what) + else + info = debug_getinfo(thread, f, what) + end + if not info or not info.func then + return info + end + if info.short_src or info.source or info.linedefine or info.currentline then + do + local map = SOURCE_MAP[info.source] + if map then + if info.currentline then + info.currentline = assert(map[info.currentline]) + end + if info.linedefined then + info.linedefined = assert(map[info.linedefined]) + end + if info.lastlinedefined then + info.lastlinedefined = assert(map[info.lastlinedefined]) + end + info.short_src = info.source:match('@([^[]*)') or info.short_src + end + end + end + return info +end +local print_err_msg +print_err_msg = function(error_message, stack_offset) + if stack_offset == nil then + stack_offset = 3 + end + io.stderr:write(tostring(colored.red("ERROR:")) .. " " .. tostring(colored.bright(colored.red((error_message or "")))) .. "\n") + io.stderr:write("stack traceback:\n") + ok, to_lua = pcall(function() + return require('moonscript.base').to_lua + end) + if not ok then + to_lua = function() + return nil + end + end + local LINE_TABLES = setmetatable({ }, { + __index = function(self, file) + local _, line_table = to_lua(file) + self[file] = line_table or false + return line_table or false + end + }) + local get_line + get_line = function(file, line_no) + local start = LINE_STARTS[file][line_no] or 1 + local stop = (LINE_STARTS[file][line_no + 1] or 0) - 1 + return file:sub(start, stop) + end + local level = stack_offset + while true do + local _continue_0 = false + repeat + local calling_fn = debug_getinfo(level) + if not calling_fn then + break + end + if calling_fn.func == run then + break + end + level = level + 1 + local name = calling_fn.name and "function '" .. tostring(calling_fn.name) .. "'" or nil + if calling_fn.linedefined == 0 then + name = "main chunk" + end + if name == "run_lua_fn" then + _continue_0 = true + break + end + local line = nil + do + local map = SOURCE_MAP[calling_fn.source] + if map then + if calling_fn.currentline then + calling_fn.currentline = assert(map[calling_fn.currentline]) + end + if calling_fn.linedefined then + calling_fn.linedefined = assert(map[calling_fn.linedefined]) + end + if calling_fn.lastlinedefined then + calling_fn.lastlinedefined = assert(map[calling_fn.lastlinedefined]) + end + local filename, start, stop = calling_fn.source:match('@([^[]*)%[([0-9]+):([0-9]+)]') + assert(filename) + local file = FILE_CACHE[filename]:sub(tonumber(start), tonumber(stop)) + local err_line = get_line(file, calling_fn.currentline):sub(1, -2) + local offending_statement = colored.bright(colored.red(err_line:match("^[ ]*(.*)"))) + name = "action '" .. tostring(calling_fn.name) .. "'" + line = colored.yellow(tostring(filename) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name) .. "\n " .. tostring(offending_statement)) + else + local file + ok, file = pcall(function() + return FILE_CACHE[calling_fn.short_src] + end) + if not ok then + file = nil + end + local line_num + if name == nil then + local search_level = level + local _info = debug.getinfo(search_level) + while _info and (_info.func == pcall or _info.func == xpcall) do + search_level = search_level + 1 + _info = debug.getinfo(search_level) + end + if _info then + for i = 1, 999 do + local varname, val = debug.getlocal(search_level, i) + if not varname then + break + end + if val == calling_fn.func then + name = "local '" .. tostring(varname) .. "'" + if not varname:match("%(") then + break + end + end + end + if not (name) then + for i = 1, _info.nups do + local varname, val = debug.getupvalue(_info.func, i) + if not varname then + break + end + if val == calling_fn.func then + name = "upvalue '" .. tostring(varname) .. "'" + if not varname:match("%(") then + break + end + end + end + end + end + end + if file and calling_fn.short_src:match(".moon$") and LINE_TABLES[file] then + local char = LINE_TABLES[file][calling_fn.currentline] + line_num = 1 + for _ in file:sub(1, char):gmatch("\n") do + line_num = line_num + 1 + end + line = colored.cyan(tostring(calling_fn.short_src) .. ":" .. tostring(line_num) .. " in " .. tostring(name or '?')) + else + line_num = calling_fn.currentline + if calling_fn.short_src == '[C]' then + line = colored.green(tostring(calling_fn.short_src) .. " in " .. tostring(name or '?')) + else + line = colored.blue(tostring(calling_fn.short_src) .. ":" .. tostring(calling_fn.currentline) .. " in " .. tostring(name or '?')) + end + end + if file then + local err_line = get_line(file, line_num):sub(1, -2) + local offending_statement = colored.bright(colored.red(err_line:match("^[ ]*(.*)$"))) + line = line .. ("\n " .. offending_statement) + end + end + end + io.stderr:write(" " .. tostring(line) .. "\n") + if calling_fn.istailcall then + io.stderr:write(" " .. tostring(colored.dim(colored.white(" (...tail calls...)"))) .. "\n") + end + _continue_0 = true + until true + if not _continue_0 then + break + end + end + return io.stderr:flush() +end +local err_hand +err_hand = function(error_message) + print_err_msg(error_message) + return os.exit(false, true) +end +local has_ldt, ldt = pcall(require, 'ldt') +local safe_run +if has_ldt then + safe_run = ldt.guard +else + safe_run = function(fn) + return xpcall(fn, err_hand) + end +end +return safe_run |
