aboutsummaryrefslogtreecommitdiff
path: root/error_handling.lua
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2019-01-18 20:46:04 -0800
committerBruce Hill <bruce@bruce-hill.com>2019-01-18 20:46:10 -0800
commit5a99a24176d9dd2cea3d802989b47d55bd89e932 (patch)
tree3f1e49d960fb4cd5d803276f860566ffe95a3604 /error_handling.lua
parent13cab23e204ede4f54e81f500418ece276970f31 (diff)
Better error handling.
Diffstat (limited to 'error_handling.lua')
-rw-r--r--error_handling.lua138
1 files changed, 89 insertions, 49 deletions
diff --git a/error_handling.lua b/error_handling.lua
index 27022aa..8d6e7d9 100644
--- a/error_handling.lua
+++ b/error_handling.lua
@@ -1,5 +1,6 @@
local debug_getinfo = debug.getinfo
local Files = require("files")
+local pretty_error = require("pretty_errors")
local RED = "\027[31m"
local BRIGHT_RED = "\027[31;1m"
local RESET = "\027[0m"
@@ -54,16 +55,7 @@ debug.getinfo = function(thread, f, what)
end
info.short_src = info.source:match('@([^[]*)') or info.short_src
if info.name then
- do
- local tmp = info.name:match("^A_([a-zA-Z0-9_]*)$")
- if tmp then
- info.name = tmp:gsub("_", " "):gsub("x([0-9A-F][0-9A-F])", function(self)
- return string.char(tonumber(self, 16))
- end)
- else
- info.name = info.name
- end
- end
+ info.name = "action '" .. tostring(calling_fn.name:from_lua_id()) .. "'"
else
info.name = "main chunk"
end
@@ -72,12 +64,78 @@ debug.getinfo = function(thread, f, what)
end
return info
end
-local print_error
-print_error = function(error_message, start_fn, stop_fn)
- io.stderr:write(tostring(RED) .. "ERROR: " .. tostring(BRIGHT_RED) .. tostring(error_message or "") .. tostring(RESET) .. "\n")
- io.stderr:write("stack traceback:\n")
- local level = 1
- local found_start = false
+local enhance_error
+enhance_error = function(error_message, start_fn, stop_fn)
+ if not (error_message and error_message:match("\x1b")) then
+ error_message = error_message or ""
+ do
+ local fn = error_message:match("attempt to call a nil value %(global '(.*)'%)")
+ if fn then
+ if fn:match("x[0-9A-F][0-9A-F]") then
+ error_message = "The action '" .. tostring(fn:from_lua_id()) .. "' is not defined."
+ end
+ end
+ end
+ local level = 2
+ while true do
+ local calling_fn = debug_getinfo(level)
+ if not calling_fn then
+ break
+ end
+ level = level + 1
+ local filename, file, line_num
+ do
+ local map = SOURCE_MAP and SOURCE_MAP[calling_fn.source]
+ if map then
+ if calling_fn.currentline then
+ line_num = assert(map[calling_fn.currentline])
+ end
+ local start, stop
+ filename, start, stop = calling_fn.source:match('@([^[]*)%[([0-9]+):([0-9]+)]')
+ if not filename then
+ filename, start = calling_fn.source:match('@([^[]*)%[([0-9]+)]')
+ end
+ assert(filename)
+ file = Files.read(filename)
+ else
+ filename = calling_fn.short_src
+ file = Files.read(filename)
+ if calling_fn.short_src:match("%.moon$") and type(MOON_SOURCE_MAP[file]) == 'table' then
+ local char = MOON_SOURCE_MAP[file][calling_fn.currentline]
+ line_num = file:line_number_at(char)
+ else
+ line_num = calling_fn.currentline
+ end
+ end
+ end
+ if file and filename and line_num then
+ local start = 1
+ local lines = file:lines()
+ for i = 1, line_num - 1 do
+ start = start + #lines[i] + 1
+ end
+ local stop = start + #lines[line_num]
+ start = start + #lines[line_num]:match("^ *")
+ error_message = pretty_error({
+ title = "Error",
+ error = error_message,
+ source = file,
+ start = start,
+ stop = stop,
+ filename = filename
+ })
+ break
+ end
+ if calling_fn.func == xpcall then
+ break
+ end
+ end
+ end
+ local ret = {
+ tostring(RED) .. "ERROR: " .. tostring(BRIGHT_RED) .. tostring(error_message or "") .. tostring(RESET),
+ "stack traceback:"
+ }
+ local level = 2
while true do
local _continue_0 = false
repeat
@@ -85,19 +143,15 @@ print_error = function(error_message, start_fn, stop_fn)
if not calling_fn then
break
end
- level = level + 1
- if not (found_start) then
- if calling_fn.func == start_fn then
- found_start = true
- end
- _continue_0 = true
+ if calling_fn.func == xpcall 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
+ if name == "function 'run_lua_fn'" then
_continue_0 = true
break
end
@@ -120,16 +174,7 @@ print_error = function(error_message, start_fn, stop_fn)
end
assert(filename)
if calling_fn.name then
- do
- local tmp = calling_fn.name:match("^A_([a-zA-Z0-9_]*)$")
- if tmp then
- name = "action '" .. tostring(tmp:gsub("_", " "):gsub("x([0-9A-F][0-9A-F])", function(self)
- return string.char(tonumber(self, 16))
- end)) .. "'"
- else
- name = "action '" .. tostring(calling_fn.name) .. "'"
- end
- end
+ name = "action '" .. tostring(calling_fn.name:from_lua_id()) .. "'"
else
name = "main chunk"
end
@@ -192,10 +237,7 @@ print_error = function(error_message, start_fn, stop_fn)
end
if file and (calling_fn.short_src:match("%.moon$") or file:match("^#![^\n]*moon\n")) and type(MOON_SOURCE_MAP[file]) == 'table' then
local char = MOON_SOURCE_MAP[file][calling_fn.currentline]
- line_num = 1
- for _ in file:sub(1, char):gmatch("\n") do
- line_num = line_num + 1
- end
+ line_num = file:line_number_at(char)
line = tostring(CYAN) .. tostring(calling_fn.short_src) .. ":" .. tostring(line_num) .. " in " .. tostring(name or '?') .. tostring(RESET)
else
line_num = calling_fn.currentline
@@ -216,12 +258,9 @@ print_error = function(error_message, start_fn, stop_fn)
end
end
end
- io.stderr:write(line, "\n")
+ table.insert(ret, line)
if calling_fn.istailcall then
- io.stderr:write(" " .. tostring(DIM) .. "(...tail calls...)" .. tostring(RESET) .. "\n")
- end
- if calling_fn.func == stop_fn then
- break
+ table.insert(ret, " " .. tostring(DIM) .. "(...tail calls...)" .. tostring(RESET))
end
_continue_0 = true
until true
@@ -229,19 +268,20 @@ print_error = function(error_message, start_fn, stop_fn)
break
end
end
- return io.stderr:flush()
+ return table.concat(ret, "\n")
end
local guard
guard = function(fn)
- local error_handler
- error_handler = function(error_message)
- print_error(error_message, error_handler, fn)
- local EXIT_FAILURE = 1
- return os.exit(EXIT_FAILURE)
+ local err
+ ok, err = xpcall(fn, enhance_error)
+ if not ok then
+ io.stderr:write(err)
+ io.stderr:flush()
+ return os.exit(1)
end
- return xpcall(fn, error_handler)
end
return {
guard = guard,
+ enhance_error = enhance_error,
print_error = print_error
}