local old_print = print function print(...) local info = debug.getinfo(2) local line = info.currentline local name = info.source old_print(string.format("%i%s> ",line,name),...) end function log(...) old_print(...) end -- HELPER FUNCTIONS -- Insert every item in t1 into t0 function just(...) local items = {...} return function() return table.unpack(items) end end id = function(...) return ... end function union(t0,t1) for index,item in pairs(t0) do t0[index] = t1 end return t0 end -- Iterate (ipairs without the index) function iterate(...) local iterator,tab,i = pairs(...) return function(...) local index,item = iterator(tab,i) i = index return item end,tab,i end function indices(t) if type(t) ~= "table" then error( string.format( "Argument 1 '%s' must be a table", tostring(t) ) ,2) end local indices = {} for index,_ in pairs(t) do table.insert(indices,index) end return indices end -- Ensures a table has a metatable function ensure_metatable(t) local meta = getmetatable(t) if not meta then meta = {} setmetatable(t,meta) end return meta end function strings(t,item) ensure_metatable(t).__tostring = item end -- Makes a table callable function calls(t,fun) ensure_metatable(t).__call = fun end -- Makes a table record access function indexes(t,fun) ensure_metatable(t).__index = fun end -- Makes a table record setting function modifies(t,fun) ensure_metatable(t).__newindex = fun end -- Shallow table to string function table_tostring(tab,sep) local items = {} for item in iterate(tab) do table.insert(items,tostring(item)) end return table.concat(items,sep) end function type_of(item) local type_name = type(item) if type_name == "table" then local meta = getmetatable(item) if not meta then return "table" else return meta end else return type_name end end -- DEBUGGING FUNCTIONS -- Runs a function function assert_meta(...) local metas = {...} return function(x,raise) local meta = getmetatable(x) for tab in iterate(metas) do if meta == tab then return end end local s_metas = {} for meta in iterate(metas) do table.insert(s_metas,tostring(meta)) end local level = 2 local msg = string.format( "Expected metatables '%s' but got '%s'", table.concat(s_metas,","), type(x) ) if raise then level = 3 msg = string.format(raise,msg) end error(msg,level) end end function assert_type(...) local types = {...} return function(x,raise) local t = type(x) for name in iterate(types) do if t == name then return end end local level = 2 local msg = string.format( "Expected type '%s' but got '%s'", table_tostring(types," or "), type(x)) -- For argument errors if raise then level = 3 msg = string.format(raise,msg) end error(msg,level) end end -- HELPER FUNCTIONS function case_of(t) return function(item,...) return (t[item] or error( string.format( "Couldn't match '%s' in case.", tostring(item) ),2 ))(...) end end -- Creates a (p)OOP class function class(name) local meta = {} union(meta,{restrict = {}}) meta.__index = meta strings(meta,just(name)) calls(meta,function(meta,...) local new = {} setmetatable(new,meta) if meta.new then new:new(...) end return new end) meta.__tostring = function(this) if meta.string then return this:string() end return name end return meta end -- Read a file's full contents function read(path) local file = io.open(path) if not file then error( string.match("Could not open file '%s'.",path),3 ) end local content = file:read("a") return content end return _G