Cleaned and fixed up colored console text output for error messages.

This commit is contained in:
Bruce Hill 2018-05-29 18:10:30 -07:00
parent e697689ddd
commit 8806d7639e
2 changed files with 156 additions and 104 deletions

158
nomsu.lua
View File

@ -124,18 +124,6 @@ LINE_STARTS = setmetatable({ }, {
end end
}) })
local LUA_METADATA = { } local LUA_METADATA = { }
local lua_line_to_nomsu_line
lua_line_to_nomsu_line = function(lua_filename, lua_line_no)
local metadata = assert(LUA_METADATA[lua_filename], "Failed to find nomsu metadata for: " .. tostring(lua_filename) .. ".")
local lua_offset = LINE_STARTS[metadata.lua_file][lua_line_no]
local best = metadata.nomsu_sources[1]
for lua, nomsu in pairs(metadata.lua_to_nomsu) do
if lua.start <= lua_offset and lua > best then
best = lua
end
end
return best:get_line_number()
end
do do
local STRING_METATABLE = getmetatable("") local STRING_METATABLE = getmetatable("")
STRING_METATABLE.__add = function(self, other) STRING_METATABLE.__add = function(self, other)
@ -453,19 +441,8 @@ do
local offset = 1 local offset = 1
local source = lua.source local source = lua.source
local nomsu_raw = FILE_CACHE[source.filename]:sub(source.start, source.stop) local nomsu_raw = FILE_CACHE[source.filename]:sub(source.start, source.stop)
local get_line_starts local nomsu_line_to_pos = LINE_STARTS[nomsu_raw]
get_line_starts = function(s) local lua_line_to_pos = LINE_STARTS[tostring(lua)]
local starts = { }
local pos = 1
for line in s:gmatch("[^\n]*\n") do
insert(starts, pos)
pos = pos + #line
end
insert(starts, pos)
return starts
end
local nomsu_line_to_pos = get_line_starts(nomsu_raw)
local lua_line_to_pos = get_line_starts(tostring(lua))
local pos_to_line local pos_to_line
pos_to_line = function(line_starts, pos) pos_to_line = function(line_starts, pos)
local lo, hi = 1, #line_starts local lo, hi = 1, #line_starts
@ -1459,10 +1436,10 @@ OPTIONS
return info return info
end end
if info.short_src or info.source or info.linedefine or info.currentline then if info.short_src or info.source or info.linedefine or info.currentline then
for k, v in pairs(nomsu.environment.ACTIONS) do do
if v == info.func then local arg_orders = nomsu.environment.ARG_ORDERS[info.func]
info.name = k if arg_orders then
break info.name = next(arg_orders)
end end
end end
do do
@ -1486,7 +1463,7 @@ OPTIONS
local print_err_msg local print_err_msg
print_err_msg = function(error_message, stack_offset) print_err_msg = function(error_message, stack_offset)
if stack_offset == nil then if stack_offset == nil then
stack_offset = 2 stack_offset = 3
end end
io.stderr:write(tostring(colored.red("ERROR:")) .. " " .. tostring(colored.bright(colored.red((error_message or "")))) .. "\n") io.stderr:write(tostring(colored.red("ERROR:")) .. " " .. tostring(colored.bright(colored.red((error_message or "")))) .. "\n")
io.stderr:write("stack traceback:\n") io.stderr:write("stack traceback:\n")
@ -1499,7 +1476,19 @@ OPTIONS
end end
end end
local nomsu_source = FILE_CACHE["nomsu.moon"] local nomsu_source = FILE_CACHE["nomsu.moon"]
local _, line_table = to_lua(nomsu_source) 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 local level = stack_offset
while true do while true do
local _continue_0 = false local _continue_0 = false
@ -1513,40 +1502,91 @@ OPTIONS
end end
level = level + 1 level = level + 1
local name = calling_fn.name local name = calling_fn.name
if calling_fn.linedefined == 0 then
name = "main chunk"
end
if name == "run_lua_fn" then if name == "run_lua_fn" then
_continue_0 = true _continue_0 = true
break break
end end
local line = nil local line = nil
_ = [=[ if metadata = nomsu.action_metadata[calling_fn.func] do
filename, start, stop = metadata.source\match("([^:]*):([0-9]*),([0-9]*)") local map = nomsu.source_map[calling_fn.source]
if filename if map then
file = FILE_CACHE[filename] if calling_fn.currentline then
line_no = 1 calling_fn.currentline = assert(map[calling_fn.currentline])
for _ in file\sub(1,tonumber(start))\gmatch("\n") do line_no += 1 end
offending_statement = file\sub(tonumber(start),tonumber(stop)) if calling_fn.linedefined then
if #offending_statement > 50 calling_fn.linedefined = assert(map[calling_fn.linedefined])
offending_statement = offending_statement\sub(1,50).."..." end
offending_statement = colored.red(offending_statement) if calling_fn.lastlinedefined then
line = colored.yellow(filename..":"..tostring(line_no).."\n "..offending_statement) calling_fn.lastlinedefined = assert(map[calling_fn.lastlinedefined])
else end
line = colored.yellow(metadata.source) calling_fn.short_src = calling_fn.source:match('"([^[]*)')
name = colored.bright(colored.yellow(metadata.aliases[1])) 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.red(err_line)
line = colored.yellow(tostring(filename) .. ":" .. tostring(calling_fn.currentline) .. "\n" .. tostring(offending_statement))
do
local arg_orders = nomsu.environment.ARG_ORDERS[calling_fn.func]
if arg_orders then
name = colored.bright(colored.yellow(next(arg_orders)))
else
name = colored.bright(colored.yellow("main chunk"))
end
end
else
if calling_fn.istailcall and not name then
name = "<tail call>"
end
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 and debug.getinfo(level + 1) then
local i = 1
while true do
local varname, val = debug.getlocal(level + 1, i)
if not varname then
break
end
if val == calling_fn.func then
name = varname
if not varname:match("%(") then
break
end
end
i = i + 1
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))
name = colored.bright(colored.cyan(name or "???"))
else else
if calling_fn.istailcall and not name line_num = calling_fn.currentline
name = "<tail call>" line = colored.blue(tostring(calling_fn.short_src) .. ":" .. tostring(calling_fn.currentline))
if calling_fn.short_src == "./nomsu.moon" and line_table name = colored.bright(colored.blue(name or "???"))
char = line_table[calling_fn.currentline] end
line_num = 1 if file then
for _ in nomsu_source\sub(1,char)\gmatch("\n") do line_num += 1 local err_line = get_line(file, line_num):sub(1, -2)
line = colored.cyan("#{calling_fn.short_src}:#{line_num}") local offending_statement = colored.red(err_line)
name = colored.bright(colored.cyan(name or "???")) line = line .. ("\n" .. offending_statement)
else end
line = colored.blue("#{calling_fn.short_src}:#{calling_fn.currentline}") end
name = colored.bright(colored.blue(name or "???")) end
_from = colored.dim colored.white "|" local _from = colored.dim(colored.white("|"))
io.stderr\write(("%32s %s %s\n")\format(name, _from, line)) io.stderr:write(("%32s %s %s\n"):format(name, _from, line))
]=]
_continue_0 = true _continue_0 = true
until true until true
if not _continue_0 then if not _continue_0 then

View File

@ -114,15 +114,6 @@ LINE_STARTS = setmetatable {}, {
-- nomsu_filename, nomsu_file, lua_filename, lua_file -- nomsu_filename, nomsu_file, lua_filename, lua_file
LUA_METADATA = {} LUA_METADATA = {}
lua_line_to_nomsu_line = (lua_filename, lua_line_no)->
metadata = assert LUA_METADATA[lua_filename], "Failed to find nomsu metadata for: #{lua_filename}."
lua_offset = LINE_STARTS[metadata.lua_file][lua_line_no]
best = metadata.nomsu_sources[1]
for lua,nomsu in pairs metadata.lua_to_nomsu
if lua.start <= lua_offset and lua > best
best = lua
return best\get_line_number!
-- Use + operator for string coercive concatenation (note: "asdf" + 3 == "asdf3") -- Use + operator for string coercive concatenation (note: "asdf" + 3 == "asdf3")
-- Use [] for accessing string characters, or s[{3,4}] for s:sub(3,4) -- Use [] for accessing string characters, or s[{3,4}] for s:sub(3,4)
-- Note: This globally affects all strings in this instance of Lua! -- Note: This globally affects all strings in this instance of Lua!
@ -420,17 +411,8 @@ class NomsuCompiler
source = lua.source source = lua.source
nomsu_raw = FILE_CACHE[source.filename]\sub(source.start, source.stop) nomsu_raw = FILE_CACHE[source.filename]\sub(source.start, source.stop)
get_line_starts = (s)-> nomsu_line_to_pos = LINE_STARTS[nomsu_raw]
starts = {} lua_line_to_pos = LINE_STARTS[tostring(lua)]
pos = 1
for line in s\gmatch("[^\n]*\n")
insert starts, pos
pos += #line
insert starts, pos
return starts
nomsu_line_to_pos = get_line_starts(nomsu_raw)
lua_line_to_pos = get_line_starts(tostring(lua))
pos_to_line = (line_starts, pos)-> pos_to_line = (line_starts, pos)->
-- Binary search for line number of position -- Binary search for line number of position
@ -1036,11 +1018,8 @@ OPTIONS
else debug_getinfo(thread,f,what) else debug_getinfo(thread,f,what)
if not info or not info.func then return info if not info or not info.func then return info
if info.short_src or info.source or info.linedefine or info.currentline if info.short_src or info.source or info.linedefine or info.currentline
for k,v in pairs(nomsu.environment.ACTIONS) if arg_orders = nomsu.environment.ARG_ORDERS[info.func]
if v == info.func info.name = next(arg_orders)
info.name = k
break
if map = nomsu.source_map[info.source] if map = nomsu.source_map[info.source]
if info.currentline if info.currentline
info.currentline = assert(map[info.currentline]) info.currentline = assert(map[info.currentline])
@ -1051,7 +1030,7 @@ OPTIONS
info.short_src = info.source\match('"([^[]*)') info.short_src = info.source\match('"([^[]*)')
return info return info
print_err_msg = (error_message, stack_offset=2)-> print_err_msg = (error_message, stack_offset=3)->
io.stderr\write("#{colored.red "ERROR:"} #{colored.bright colored.red (error_message or "")}\n") io.stderr\write("#{colored.red "ERROR:"} #{colored.bright colored.red (error_message or "")}\n")
io.stderr\write("stack traceback:\n") io.stderr\write("stack traceback:\n")
@ -1059,7 +1038,16 @@ OPTIONS
ok, to_lua = pcall -> require('moonscript.base').to_lua ok, to_lua = pcall -> require('moonscript.base').to_lua
if not ok then to_lua = -> nil if not ok then to_lua = -> nil
nomsu_source = FILE_CACHE["nomsu.moon"] nomsu_source = FILE_CACHE["nomsu.moon"]
_, line_table = to_lua(nomsu_source) LINE_TABLES = setmetatable {},
__index: (file)=>
_, line_table = to_lua(file)
self[file] = line_table or false
return line_table or false
get_line = (file, line_no)->
start = LINE_STARTS[file][line_no] or 1
stop = (LINE_STARTS[file][line_no+1] or 0) - 1
return file\sub(start, stop)
level = stack_offset level = stack_offset
while true while true
@ -1069,38 +1057,62 @@ OPTIONS
if calling_fn.func == run then break if calling_fn.func == run then break
level += 1 level += 1
name = calling_fn.name name = calling_fn.name
if calling_fn.linedefined == 0 then name = "main chunk"
if name == "run_lua_fn" then continue if name == "run_lua_fn" then continue
line = nil line = nil
[=[ if map = nomsu.source_map[calling_fn.source]
if metadata = nomsu.action_metadata[calling_fn.func] if calling_fn.currentline
filename, start, stop = metadata.source\match("([^:]*):([0-9]*),([0-9]*)") calling_fn.currentline = assert(map[calling_fn.currentline])
if filename if calling_fn.linedefined
file = FILE_CACHE[filename] calling_fn.linedefined = assert(map[calling_fn.linedefined])
line_no = 1 if calling_fn.lastlinedefined
for _ in file\sub(1,tonumber(start))\gmatch("\n") do line_no += 1 calling_fn.lastlinedefined = assert(map[calling_fn.lastlinedefined])
offending_statement = file\sub(tonumber(start),tonumber(stop)) calling_fn.short_src = calling_fn.source\match('"([^[]*)')
if #offending_statement > 50 filename,start,stop = calling_fn.source\match('"([^[]*)%[([0-9]+):([0-9]+)]"')
offending_statement = offending_statement\sub(1,50).."..." assert(filename)
offending_statement = colored.red(offending_statement) file = FILE_CACHE[filename]\sub(tonumber(start),tonumber(stop))
line = colored.yellow(filename..":"..tostring(line_no).."\n "..offending_statement) err_line = get_line(file, calling_fn.currentline)\sub(1,-2)
offending_statement = colored.red(err_line)
line = colored.yellow("#{filename}:#{calling_fn.currentline}\n#{offending_statement}")
if arg_orders = nomsu.environment.ARG_ORDERS[calling_fn.func]
name = colored.bright(colored.yellow next(arg_orders))
else else
line = colored.yellow(metadata.source) name = colored.bright(colored.yellow "main chunk")
name = colored.bright(colored.yellow(metadata.aliases[1]))
else else
if calling_fn.istailcall and not name if calling_fn.istailcall and not name
name = "<tail call>" name = "<tail call>"
if calling_fn.short_src == "./nomsu.moon" and line_table ok, file = pcall ->FILE_CACHE[calling_fn.short_src]
char = line_table[calling_fn.currentline] if not ok then file = nil
local line_num
if name == nil and debug.getinfo(level+1)
i = 1
while true
-- '+1' to get to one level higher than the callsite
varname, val = debug.getlocal(level+1, i)
if not varname then break
if val == calling_fn.func
name = varname
if not varname\match("%(")
break
i += 1
if file and calling_fn.short_src\match(".moon$") and LINE_TABLES[file]
char = LINE_TABLES[file][calling_fn.currentline]
line_num = 1 line_num = 1
for _ in nomsu_source\sub(1,char)\gmatch("\n") do line_num += 1 for _ in file\sub(1,char)\gmatch("\n") do line_num += 1
line = colored.cyan("#{calling_fn.short_src}:#{line_num}") line = colored.cyan("#{calling_fn.short_src}:#{line_num}")
name = colored.bright(colored.cyan(name or "???")) name = colored.bright(colored.cyan(name or "???"))
else else
line_num = calling_fn.currentline
line = colored.blue("#{calling_fn.short_src}:#{calling_fn.currentline}") line = colored.blue("#{calling_fn.short_src}:#{calling_fn.currentline}")
name = colored.bright(colored.blue(name or "???")) name = colored.bright(colored.blue(name or "???"))
if file
err_line = get_line(file, line_num)\sub(1,-2)
offending_statement = colored.red(err_line)
line ..= "\n"..offending_statement
_from = colored.dim colored.white "|" _from = colored.dim colored.white "|"
io.stderr\write(("%32s %s %s\n")\format(name, _from, line)) io.stderr\write(("%32s %s %s\n")\format(name, _from, line))
]=]
io.stderr\flush! io.stderr\flush!
run = -> run = ->