135 lines
3.7 KiB
Lua
Executable File
135 lines
3.7 KiB
Lua
Executable File
-- 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
|