135 lines
3.7 KiB
Lua
Executable File
Raw Blame History

-- functions for turning a format string into a callable function
-- they work by calling parse(), passing it the format string and
-- a table of code generators appropriate for whether we are reading
-- or writing.
-- The resulting code is then prefixed with some setup code and postfixed
-- with a return value and loadstring() is called on it to generate a function
-- Copyright <20> 2008 Ben "ToxicFrog" Kelly; see COPYING
local require,loadstring,setfenv,type,select,unpack,setmetatable
= require,loadstring,setfenv,type,select,unpack,setmetatable
local print,assert,error,xpcall,pairs,where
= print,assert,error,xpcall,pairs,debug.traceback
module((...))
local parse = require(_PACKAGE.."parser")
local function nulsafe_error(s)
return error(s:gsub('%z', '_'))
end
local function xpcall2(f, err, ...)
local args = {n=select('#', ...), ...}
return xpcall(function() return f(unpack(args, 1, args.n)) end, err)
end
local function err_generate(message, format, trace)
nulsafe_error([[
struct: internal error in code generator
This is an internal error in the struct library
Please report it as a bug and include the following information:
-- error message
]]..message.."\n\n"..[[
-- format string
]]..format.."\n\n"..[[
-- stack trace
]]..trace)
end
local function err_compile(message, format, source)
nulsafe_error([[
struct: syntax error in emitted lua source
This is an internal error in the struct library
Please report it as a bug and include the following information:
-- loadstring error
]]..message.."\n\n"..[[
-- format string
]]..format.."\n\n"..[[
-- emitted source
]]..source.."\n\n"..[[
-- stack trace
]])
end
local function err_execute(message, format, source, trace)
nulsafe_error([[
struct: runtime error in generated function
This is at some level an internal error in the struct library
It could be a genuine error in the emitted code (in which case this is a code
generation bug)
Alternately, it could be that you gave it a malformed format string, a bad
file descriptor, or data that does not match the given format (in which case
it is an argument validation bug and you should be getting an error anyways).
Please report this as a bug and include the following information:
-- execution error
]]..message.."\n\n"..[[
-- format string
]]..format.."\n\n"..[[
-- emitted source
]]..source.."\n\n"..[[
-- stack trace
]]..trace)
end
local function compile(format, gen, env)
local status,source = xpcall(function()
return parse(format, gen, true)
end,
function(message)
return { message, where("",2) }
end)
if not status then
if type(source[1]) == "function" then
error(source[1]()..source[2])
end
err_generate(source[1], format, source[2])
end
local fn,err = loadstring(source)
if not fn then
err_compile(err, format, source)
end
setfenv(fn, env)
local fn = function(...)
local status,ret,len = xpcall2(fn, function(message)
return { message, where("",2) }
end, ...)
-- call succeeded without errors
if status then return ret,len end
local message,where = ret[1],ret[2]
-- call generated a deliberate error; call the provided closure
-- it will either emit an error code or re-throw
if type(message) == "function" then return nil,message() end
-- call generated an internal error; re-throw with extra debug info
err_execute(message, format, source, where)
end
return fn
end
local gen_unpack = require(_PACKAGE.."gen_unpack")
local io_unpack = require(_PACKAGE.."io_unpack")
function _M.unpack(format)
return compile(format, gen_unpack, io_unpack)
end
local gen_pack = require(_PACKAGE.."gen_pack")
local io_pack = require(_PACKAGE.."io_pack")
function _M.pack(format)
return compile(format, gen_pack, io_pack)
end
return _M