-- tostring replacement that assigns ids
local ts, id, nid, types = tostring, {}, 0, { table = 'tbl', thread = 'thr', userdata = 'uda', ['function'] = 'func' }
tostring = function(x)
	if not x or not types[type(x)] then return ts(x) end
	if not id[x] then nid = nid + 1; id[x] = types[type(x)] .. '.' .. nid end
	return id[x]
end

-- test of common types of errors
-- local function c(f,...) return f(...) end
-- local function b(...) return c(...) end
--local function a(...) return (pcall(b,...)) end
local function a(...) local s, e = pcall(...) if s then return s, e else return false, type(e) end end

s = 'some string'
local t = {}
-- error message tests
print('a(error)', a(error))
print('a(error,"msg")', a(error, "msg"))
print('a(error,"msg",0)', a(error, "msg", 0))
print('a(error,"msg",1)', a(error, "msg", 1))
print('a(error,"msg",2)', a(error, "msg", 2))
print('a(error,"msg",3)', a(error, "msg", 3))
print('a(error,"msg",4)', a(error, "msg", 4))
print('a(error,"msg",5)', a(error, "msg", 5))
print('a(error,"msg",6)', a(error, "msg", 6))

-- call errors
print('a(nil())', a(function() return n() end))
print('a(t())  ', a(function() return t() end))
print('a(s())  ', a(function() return s() end))
print('a(true())', a(function() local b = true; return b() end))

-- arithmetic errors
print('a(nil+1)', a(function() return nil + 1 end))
print('a(a+1)  ', a(function() return a + 1 end))
print('a(s+1)  ', a(function() return s + 1 end))
print('a(true+1)', a(function() local b = true; return b + 1 end))

-- table errors
print('a(nil.x)', a(function() return n.x end))
print('a(a.x)  ', a(function() return a.x end))
print('a(s.x)  ', a(function() return s.x end))
print('a(true.x)', a(function() local b = true; return b.x end))
print('a(nil.x=5)', a(function() n.x = 5 end))
print('a(a.x=5)  ', a(function() a.x = 5 end))
print('a(s.x=5)  ', a(function() s.x = 5 end))
print('a(true.x=5)', a(function() local b = true; b.x = 5 end))

-- len operator
print('a(#nil) ', a(function() return #n end))
print('a(#t)   ', a(function() return #t end))
print('a(#s)   ', a(function() return #s end))
print('a(#a)   ', a(function() return #a end))
print('a(#true)', a(function() local b = true; return #b end))

-- comparison errors
print('a(nil>1)', a(function() return nil > 1 end))
print('a(a>1)  ', a(function() return a > 1 end))
print('a(s>1)  ', a(function() return s > 1 end))
print('a(true>1)', a(function() local b = true; return b > 1 end))

-- unary minus errors
print('a(-nil)', a(function() return -n end))
print('a(-a)  ', a(function() return -a end))
print('a(-s)  ', a(function() return -s end))
print('a(-true)', a(function() local b = true; return -b end))

-- string concatenation
local function concatsuite(comparefunc)
	print('"a".."b"', comparefunc("a", "b"))
	print('"a"..nil', comparefunc("a", nil))
	print('nil.."b"', comparefunc(nil, "b"))
	print('"a"..{}', comparefunc("a", {}))
	print('{}.."b"', comparefunc({}, "b"))
	print('"a"..2', comparefunc("a", 2))
	print('2.."b"', comparefunc(2, "b"))
	print('"a"..print', comparefunc("a", print))
	print('print.."b"', comparefunc(print, "b"))
	print('"a"..true', comparefunc("a", true))
	print('true.."b"', comparefunc(true, "b"))
	print('nil..true', comparefunc(nil, true))
	print('"a"..3.5', comparefunc("a", 3.5))
	print('3.5.."b"', comparefunc(3.5, "b"))
end

local function strconcat(a, b)
	return (pcall(function() return a .. b end))
end

local function tblconcat(a, b)
	local t = { a, b }
	return (pcall(function() return table.concat(t, '-', 1, 2) end))
end

print('-------- string concatenation')
concatsuite(strconcat)
print('-------- table concatenation')
concatsuite(tblconcat)

-- pairs
print('-------- pairs tests')
print('a(pairs(nil))', a(function() return pairs(nil, {}) end))
print('a(pairs(a))  ', a(function() return pairs(a, {}) end))
print('a(pairs(s))  ', a(function() return pairs(s, {}) end))
print('a(pairs(t))  ', a(function() return pairs(t, {}) end))
print('a(pairs(true))', a(function() local b = true; return pairs(b, {}) end))

-- setmetatable
print('-------- setmetatable tests')
function sm(...)
	return tostring(setmetatable(...))
end

print('a(setmetatable(nil))', a(function() return sm(nil, {}) end))
print('a(setmetatable(a))  ', a(function() return sm(a, {}) end))
print('a(setmetatable(s))  ', a(function() return sm(s, {}) end))
print('a(setmetatable(true))', a(function() local b = true; return sm(b, {}) end))
print('a(setmetatable(t))  ', a(function() return sm(t, {}) end))
print('a(getmetatable(t))  ', a(function() return getmetatable(t), type(getmetatable(t)) end))
print('a(setmetatable(t*))  ', a(function() return sm(t, { __metatable = {} }) end))
print('a(getmetatable(t))  ', a(function() return getmetatable(t), type(getmetatable(t)) end))
print('a(setmetatable(t))  ', a(function() return sm(t, {}) end))
print('a(getmetatable(t))  ', a(function() return getmetatable(t), type(getmetatable(t)) end))
t = {}
print('a(setmetatable(t))  ', a(function() return sm(t, {}) end))
print('a(getmetatable(t))  ', a(function() return getmetatable(t), type(getmetatable(t)) end))
print('a(setmetatable(t*))  ', a(function() return sm(t, { __metatable = 'some string' }) end))
print('a(getmetatable(t))  ', a(function() return getmetatable(t), type(getmetatable(t)) end))
print('a(setmetatable(t))  ', a(function() return sm(t, {}) end))
print('a(getmetatable(t))  ', a(function() return getmetatable(t), type(getmetatable(t)) end))
print('a(setmetatable(t,nil))  ', a(function() return sm(t, nil) end))
print('a(setmetatable(t))  ', a(function() return sm(t) end))
print('a(setmetatable({},"abc"))  ', a(function() return sm({}, 'abc') end))

-- bad args to error!
print('error("msg","arg")', a(function() error('some message', 'some bad arg') end))

-- loadfile, dofile on missing files
print('loadfile("bogus.txt")', a(function() return loadfile("bogus.txt") end))
print('dofile("bogus.txt")', a(function() return dofile("bogus.txt") end))
