1
0
assert-gooder/assert-gooder.lua
Jon Michael Aanes aec232efcb Initial commit on assert-gooder. An improved version of assert
that automatically creates a useful error message, when the assert
fails.
2017-10-28 12:44:53 +02:00

91 lines
2.8 KiB
Lua

local lexer = require 'lua_lang'
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
local function get_assert_body_text (call_info)
if call_info.what == 'Lua' then
-- Find filetext
local filetext = nil
if call_info.source:find '^@' then
local f = io.open(call_info.short_src, 'r')
filetext = f:read '*all'
f:close()
elseif call_info.short_src:find '^%[string' then
filetext = call_info.source
else
error 'Not implemented yet!'
end
-- Get lines
local filetext = filetext .. '\n'
local lines_after, line_i = {}, 0
for line in filetext:gmatch '([^\r\n]*)[\r\n]' do
line_i = line_i + 1
if call_info.currentline == line_i then
lines_after[#lines_after+1] = line
end
end
-- Find body exclusively.
return table.concat(lines_after, '\n'):match('assert%s*(%b())'):sub(2, -2)
end
error 'Not implemented yet!'
end
local function get_assert_body (call_info)
local text = get_assert_body_text(call_info)
return lexer:lex(text), text
end
local function get_variable (var_name, level)
-- Local
local index = 0
repeat
index = index + 1
local name, val = debug.getlocal(level + 1, index)
if name == var_name then
local info = debug.getinfo(level + 1)
local is_par = index <= info.nparams
return val, is_par and ('argument #'..index) or 'local', info.name or is_par and ''
end
until not name
-- Up-value
local index, func = 0, debug.getinfo(level + 1).func
repeat
index = index + 1
local name, val = debug.getupvalue(func, index)
if name == var_name then return val, 'upvalue' end
until not name
-- Global
return getfenv(level + 1)[var_name], 'global'
end
local function get_value_of_string (string_str)
if string_str:sub(1, 1) == '"' or string_str:sub(1, 1) == '\'' then
return string_str:sub(2, -2)
end
assert(false)
end
--------------------------------------------------------------------------------
return function (condition)
if condition then return condition end
local call_info = debug.getinfo(2)
local tokens, body_text = get_assert_body(call_info)
if tokens[1].text == 'type' and tokens[2].token == 'LPAR' and tokens[3].token == 'IDENTIFIER' and tokens[4].token == 'RPAR'and tokens[5].token == 'EQ'and tokens[6].token == 'STRING' then
local var_name = tokens[3].text
local var_val, var_scope, is_func = get_variable(var_name, 2)
local func_name = (is_func == '' and ' to anonymous function') or is_func and (' to \''..is_func..'\'') or ''
error(('assertion failed! bad %s \'%s\'%s (%s expected, but got %s: %s)'):format(var_scope, var_name, func_name, get_value_of_string(tokens[6].text), type(var_val), var_val), 2)
else
error(('assertion failed! expression `%s` evaluated to %s'):format(body_text, condition), 2)
end
end