Модуль:Песочница/Pok: различия между версиями
Pok (обсуждение | вклад) Нет описания правки |
Pok (обсуждение | вклад) Нет описания правки |
||
| Строка 3: | Строка 3: | ||
local CURRENT_PROJECT | local CURRENT_PROJECT | ||
local function | -- Рекурсивно собирает текст из произвольной структуры (строки/таблицы/смешанного дерева) | ||
if type(v) == "string" then | local function gather_text(x, out) | ||
out = out or {} | |||
local t = type(x) | |||
if t == "string" then | |||
table.insert(out, x) | |||
return out | |||
elseif t == "number" or t == "boolean" then | |||
table.insert(out, tostring(x)) | |||
return out | |||
elseif t == "table" then | |||
-- если таблица выглядит как массив, обходим по индексам в порядке 1..n | |||
local i = 1 | |||
while x[i] ~= nil do | |||
gather_text(x[i], out) | |||
i = i + 1 | |||
end | |||
-- затем обойдём именованные поля на случай, если там что-то полезное | |||
for k, v in pairs(x) do | |||
if type(k) ~= "number" then | |||
-- часто узлы имеют поля вроде "text", "args" и т.п. — обрабатываем их рекурсивно | |||
if k == "text" or k == "wikitext" or k == "value" or k == "src" then | |||
gather_text(v, out) | |||
else | |||
-- если значение простое, тоже возьмём его | |||
if type(v) == "string" or type(v) == "number" or type(v) == "boolean" then | |||
gather_text(v, out) | |||
elseif type(v) == "table" then | |||
gather_text(v, out) | |||
end | |||
end | |||
end | |||
end | |||
return out | |||
else | |||
-- функции, userdata и т.п. игнорируем | |||
return out | |||
end | |||
end | |||
local function extract_string(v) | |||
if v == nil then return nil end | |||
local t = type(v) | |||
if t == "string" then | |||
if v == "" then return nil end | if v == "" then return nil end | ||
return v | return v | ||
end | |||
if t == "number" or t == "boolean" then return tostring(v) end | |||
if t == "table" then | |||
local parts = gather_text(v) | |||
if #parts == 0 then return nil end | |||
local s = table.concat(parts, "") | |||
return | if s == "" then return nil end | ||
return s | |||
end | end | ||
return nil | return nil | ||
end | end | ||
local function | -- безопасная сериализация для диагностики | ||
local function short_serialize(obj, depth, seen) | |||
depth = depth or 0 | depth = depth or 0 | ||
seen = seen or {} | seen = seen or {} | ||
if type(obj) ~= "table" then return tostring(obj) end | if type(obj) ~= "table" then return tostring(obj) end | ||
if seen[obj] then return "<cycle>" end | if seen[obj] then return "<cycle>" end | ||
if depth > | if depth > 3 then return "{...}" end | ||
seen[obj] = true | seen[obj] = true | ||
local parts = {} | local parts = {} | ||
for k, v in pairs(obj) do | local i = 1 | ||
while obj[i] ~= nil do | |||
table.insert(parts, short_serialize(obj[i], depth+1, seen)) | |||
i = i + 1 | |||
end | |||
for k,v in pairs(obj) do | |||
if type(k) ~= "number" then | |||
table.insert(parts, tostring(k) .. "=" .. short_serialize(v, depth+1, seen)) | |||
end | |||
end | end | ||
return "{" .. table.concat(parts, ", ") .. "}" | return "{" .. table.concat(parts, ", ") .. "}" | ||
| Строка 37: | Строка 84: | ||
function p.set_path(frame) | function p.set_path(frame) | ||
local | local raw | ||
-- | -- первый вариант: стандартный #invoke:GetField|set_path|VALUE | ||
if type(frame) == "table" and frame.args then | if type(frame) == "table" and frame.args then | ||
raw = frame.args[1] or nil | |||
if | -- пробуем getArgument как запасной вариант | ||
if not raw then | |||
local ok, a = pcall(function() return frame:getArgument(1) end) | local ok, a = pcall(function() return frame:getArgument(1) end) | ||
if ok | if ok then raw = a end | ||
end | end | ||
if | if not raw then | ||
local | local ok2, a2 = pcall(function() return frame:getArgument("GetFieldPath") end) | ||
if | if ok2 then raw = a2 end | ||
end | end | ||
elseif type(frame) == "string" then | elseif type(frame) == "string" then | ||
raw = frame | |||
end | end | ||
local val = | local val = extract_string(raw) | ||
if val and val ~= "" then | if val and val ~= "" then | ||
CURRENT_PROJECT = val | CURRENT_PROJECT = val | ||
return "OK:" .. tostring(CURRENT_PROJECT) | return "OK:" .. tostring(CURRENT_PROJECT) | ||
end | end | ||
-- nothing | |||
local | -- diagnostics if nothing extracted | ||
local diag = "NOT_SET" | |||
if type(frame) == "table" then | if type(frame) == "table" then | ||
diag = diag .. "; raw_args=" .. short_serialize(frame.args or {}) | |||
local ok1, a1 = pcall(function() return frame:getArgument(1) end) | |||
diag = diag .. "; getArgument(1)=" .. (ok1 and short_serialize(a1) or "(err)") | |||
local ok2, a2 = pcall(function() return frame:getArgument("GetFieldPath") end) | |||
diag = diag .. "; getArgument('GetFieldPath')=" .. (ok2 and short_serialize(a2) or "(err)") | |||
else | else | ||
diag = diag .. "; frame_type=" .. type(frame) .. "; frame_val=" .. tostring(frame) | |||
end | end | ||
return | return diag | ||
end | end | ||
function p._debug_get_cached() | function p._debug_get_cached() | ||
if CURRENT_PROJECT | if not CURRENT_PROJECT then return "(nil)" end | ||
return tostring(CURRENT_PROJECT) | return tostring(CURRENT_PROJECT) | ||
end | end | ||
function p.inspect(frame) | function p.inspect(frame) | ||
local f = frame or mw.getCurrentFrame() | |||
if not f then return "NO_FRAME" end | |||
local out = {} | local out = {} | ||
local depth = 0 | local depth = 0 | ||
while f and depth < | while f and depth < 10 do | ||
table.insert(out, ("FRAME depth=" .. depth)) | |||
table.insert(out, (" args = " .. short_serialize(f.args or {}))) | |||
local ok1, a1 = pcall(function() return f:getArgument(1) end) | |||
table.insert(out, (" getArgument('1') = " .. (ok1 and short_serialize(a1) or "(err)"))) | |||
local okN, aN = pcall(function() return f:getArgument("GetFieldPath") end) | |||
table.insert(out, (" getArgument('GetFieldPath') = " .. (okN and short_serialize(aN) or "(err)"))) | |||
table.insert(out, (" has:getParent = " .. tostring(type(f.getParent) == "function"))) | |||
table.insert(out, (" has:callParserFunction = " .. tostring(type(f.callParserFunction) == "function"))) | |||
local ok1, a1 = pcall(function() return f:getArgument( | |||
f = (type(f.getParent) == "function") and f:getParent() or nil | f = (type(f.getParent) == "function") and f:getParent() or nil | ||
depth = depth + 1 | depth = depth + 1 | ||
end | end | ||
table.insert(out, ("CURRENT_PROJECT = " .. tostring(CURRENT_PROJECT))) | |||
return table.concat(out, "\n") | return table.concat(out, "\n") | ||
end | end | ||
function p.get_module_name(frame, pagePath) | function p.get_module_name(frame, pagePath) | ||
local | local project = pagePath and extract_string(pagePath) or CURRENT_PROJECT | ||
if (not project or project == "") and frame then | |||
local ok, res = pcall(function() return frame:callParserFunction{ name = "var", args = { "GetFieldPath" } } end) | |||
if ok and res and res ~= "" then project = tostring(res) end | |||
if (not project or project == "") and | |||
local ok, res = pcall(function() return | |||
if ok and res and res ~= "" then project = res end | |||
end | end | ||
if not project or project == "" then project = "default" end | if not project or project == "" then project = "default" end | ||