1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
--
-- A string.format wrapper that gracefully handles invalid arguments
--
local tostring = tostring;
local unpack = table.unpack or unpack; -- luacheck: ignore 113/unpack
local pack = require "util.table".pack; -- TODO table.pack in 5.2+
local type = type;
local dump = require "util.serialization".new("debug");
local num_type = math.type;
local expects_integer = num_type and { c = true, d = true, i = true, o = true, u = true, X = true, x = true, } or {};
local function format(formatstring, ...)
local args = pack(...);
local args_length = args.n;
-- format specifier spec:
-- 1. Start: '%%'
-- 2. Flags: '[%-%+ #0]'
-- 3. Width: '%d?%d?'
-- 4. Precision: '%.?%d?%d?'
-- 5. Option: '[cdiouxXaAeEfgGqs%%]'
--
-- The options c, d, E, e, f, g, G, i, o, u, X, and x all expect a number as argument, whereas q and s expect a string.
-- This function does not accept string values containing embedded zeros, except as arguments to the q option.
-- a and A are only in Lua 5.2+
-- process each format specifier
local i = 0;
formatstring = formatstring:gsub("%%[^cdiouxXaAeEfgGqs%%]*[cdiouxXaAeEfgGqs%%]", function(spec)
if spec ~= "%%" then
i = i + 1;
local arg = args[i];
local option = spec:sub(-1);
if arg == nil then
args[i] = "nil";
spec = "<%s>";
elseif option == "q" then
args[i] = dump(arg);
spec = "%s";
elseif option == "s" then
args[i] = tostring(arg);
elseif type(arg) ~= "number" then -- arg isn't number as expected?
args[i] = tostring(arg);
spec = "[%s]";
elseif expects_integer[option] and num_type(arg) ~= "integer" then
args[i] = tostring(arg);
spec = "[%s]";
end
end
return spec;
end);
-- process extra args
while i < args_length do
i = i + 1;
local arg = args[i];
if arg == nil then
args[i] = "<nil>";
else
args[i] = tostring(arg);
end
formatstring = formatstring .. " [%s]"
end
return formatstring:format(unpack(args));
end
return {
format = format;
};
|