Модуль:Песочница/Pok: различия между версиями

Нет описания правки
Нет описания правки
Строка 3: Строка 3:
local CURRENT_PROJECT
local CURRENT_PROJECT


-- Рекурсивно собирает текст из произвольной структуры (строки/таблицы/смешанного дерева)
local function try_call_expand(node)
local function gather_text(x, out)
    if type(node) ~= "table" then return nil end
    local f = node.expand
    if type(f) ~= "function" then return nil end
    local ok, res = pcall(function() return f(node) end)
    if ok and res and res ~= "" then return res end
    ok, res = pcall(function() return f() end)
    if ok and res and res ~= "" then return res end
    ok, res = pcall(f, node)
    if ok and res and res ~= "" then return res end
    return nil
end
 
local function gather_text(x, out, seen)
     out = out or {}
     out = out or {}
     local t = type(x)
     seen = seen or {}
    if t == "string" then
    if type(x) == "string" then
         table.insert(out, x)
         table.insert(out, x)
         return out
         return out
     elseif t == "number" or t == "boolean" then
     elseif type(x) == "number" or type(x) == "boolean" then
         table.insert(out, tostring(x))
         table.insert(out, tostring(x))
         return out
         return out
     elseif t == "table" then
     elseif type(x) == "table" then
         -- если таблица выглядит как массив, обходим по индексам в порядке 1..n
         if seen[x] then return out end
        seen[x] = true
        local s = try_call_expand(x)
        if s and s ~= "" then
            table.insert(out, s)
            return out
        end
        if x.text and type(x.text) == "string" and x.text ~= "" then
            table.insert(out, x.text)
        end
         local i = 1
         local i = 1
         while x[i] ~= nil do
         while x[i] ~= nil do
             gather_text(x[i], out)
             gather_text(x[i], out, seen)
             i = i + 1
             i = i + 1
         end
         end
        -- затем обойдём именованные поля на случай, если там что-то полезное
         for k, v in pairs(x) do
         for k, v in pairs(x) do
             if type(k) ~= "number" then
             if type(k) ~= "number" then
                -- часто узлы имеют поля вроде "text", "args" и т.п. — обрабатываем их рекурсивно
                 if type(v) == "string" or type(v) == "number" or type(v) == "boolean" then
                 if k == "text" or k == "wikitext" or k == "value" or k == "src" then
                    table.insert(out, tostring(v))
                    gather_text(v, out)
                elseif type(v) == "table" then
                else
                    gather_text(v, out, seen)
                    -- если значение простое, тоже возьмём его
                    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
             end
         end
         end
        return out
    else
        -- функции, userdata и т.п. игнорируем
         return out
         return out
     end
     end
    return out
end
end


local function extract_string(v)
local function extract_string(v)
     if v == nil then return nil end
     if v == nil then return nil end
     local t = type(v)
     if type(v) == "string" then if v == "" then return nil end; return v end
    if t == "string" then
     if type(v) == "number" or type(v) == "boolean" then return tostring(v) end
        if v == "" then return nil end
     if type(v) == "table" then
        return v
    end
     if t == "number" or t == "boolean" then return tostring(v) end
     if t == "table" then
         local parts = gather_text(v)
         local parts = gather_text(v)
         if #parts == 0 then return nil end
         if #parts == 0 then return nil end
Строка 61: Строка 69:
end
end


-- безопасная сериализация для диагностики
local function short_serialize(obj, depth, seen)
local function short_serialize(obj, depth, seen)
     depth = depth or 0
     depth = depth or 0
Строка 67: Строка 74:
     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 > 3 then return "{...}" end
     if depth > 2 then return "{...}" end
     seen[obj] = true
     seen[obj] = true
     local parts = {}
     local parts = {}
Строка 85: Строка 92:
function p.set_path(frame)
function p.set_path(frame)
     local raw
     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
         raw = frame.args[1] or nil
        -- пробуем getArgument как запасной вариант
         if not raw then
         if not raw then
             local ok, a = pcall(function() return frame:getArgument(1) end)
             local ok, a = pcall(function() return frame:getArgument(1) end)
Строка 107: Строка 112:
     end
     end


    -- diagnostics if nothing extracted
     local diag = "NOT_SET"
     local diag = "NOT_SET"
     if type(frame) == "table" then
     if type(frame) == "table" then