Модуль:Traversal
Материал из Указатель частей и соединений РККА 1941-1945
Для документации этого модуля может быть создана страница Модуль:Traversal/doc
--[==[
Нормализованный обход дерева детей. Отдает отдельно «начало даты» «начало уровня» «начало таба», «конец таба»...
Пока не делаем итератором. Но вложенного использования не бывает. Все использование за один фрейм, глобальные не делаем.
Функции init, get
--]==]
-- tree traversal
local tt = { }
local RowN -- номер обрабатываемой строка
local State -- надо что-то хранить о статусе, потому что из одной строки вылезает куча ответов
local Query -- обрабаываемый запрос
--local Father
--local Gene
local Prev -- содержимое предыдущего ряда
local LPrev -- глубина стека предыдущего ряда
local wasValue -- надо ли давать цезуру из-за смены отца
function tt.init( query, father )
RowN = 0
State = {}
Query = query
-- Father = father
-- Gene = string.match( father, '^[^(]+ %(([^()]+)%)' )
-- if type( report.knownBrackets[Gene] ) ~= 'string' then
-- Gene = nil
-- end
Prev = { ['tab-1'] = Query[1].tab0 }
LPrev = -1
end
function tt.get( para )
--[=[ между двумя элементами нам надо:
-6. завершить корпусные уровни
-5. завершить таб
-4. завершить ряд табов
-3. завершить фронтовые уровни
-2. завершить родителя
-1. завершить дату
1. открыть дату
2. открыть родителя
3. открыть фронтовые уровни
4. открыть ряд табов
5. открыть таб
6. открыть корпусные уровни
Отдает:
{ ']' } — закрытие таба
{ ']]' } — закрытие ряда
{ '}' } — закрытие даты
{ ')', false } — закрытие корпуса
{ ')', true } — закрытие армии
{ '|' } — смена папы
{ '{', дата } — смена даты
{ '[[' } — открытие ряда
{ '[',таб } — открытие таба
{ '(',n } — открытие армии уровня n
{ '(', nil } — открытие корпуса
{ i, row, (есть дети?) }
--]=]
if next( State ) then
local x = State[1]
table.remove( State, 1 )
return x
else
RowN = RowN + 1
local row = para or Query[RowN]
if not row then
if RowN > #Query+1 then
return nil
else
row = { tab0 = Query[1].tab0 }
end
end
local diff
if row.date ~= Prev.date then
diff = -1
else
for i = 0, LPrev do
if Prev['porno'..i] ~= row['porno'..i] then
diff = i
break
end
end
-- поскольку sql отдает только оконечные узлы, один результат не может полностью включать в себя другой
assert( diff, 'Неконсистентное состояние БД, два эквивалентных элемента ' .. RowN )
end
-- diff <= LPrev всегда. Это номер позиции в стеке, с которой начинается расхождение.
-- Он равен -1, если расхождение в дате. Он равен нулю, если у дерева неоднозначный отец (если юнит в таблицах встречается дважды).
-- diff == 0 и diff == -1 нужно обрабатывать особо; табы при этом закрываем.
-- корпусные и армейские закрывающие скобки могут потребоваться, если
-- LPrev > 1 и diff ~= LPrev. Больше 1, потому что дети верхнего уровня не в скобках
local prevtab = Prev['tab' .. LPrev]
if LPrev > 1 and diff ~= LPrev then
-- LPrev == 1 & diff = 0 — у нас был подчиненный, а теперь новое значение юнита без детей.
-- Скобку закрыть надо, если мы считаем, что дети исходного юнита заключаются в скобки. Но нет.
-- Поэтому нас интересуют только случаи, когда дети второго уровня
local h = LPrev
local limit = math.max( 1, diff )
while h > limit do
h = h - 1
if ( Prev['ass'..h] or '' ) ~= '' then -- если дошли до уровня армий, то может требоваться принудительное закрытие таба и ряда табов
if ( prevtab or '' ) ~= '' then
table.insert( State, { ']' } )
table.insert( State, { ']]' } ) -- ' .. table.concat( {LPrev,diff,h}, '/' ) )
prevtab = ''
end
h = h + 1 -- чтобы вернуться к циклу
break
end
table.insert( State, { ')' } )
end
if h ~= limit then -- если вышли по break, то закрываем таб и ряд (при условии, что они были)
-- if prevtab ~= '' and Prev['ass'..h] ~= '' then
-- table.insert( State, ']' )
-- table.insert( State, ']] ' .. table.concat( {LPrev,diff,h}, '/' ) )
-- prevtab = ''
-- end
-- h = h + 1 -- чтобы вернуться к циклу
end
while h > limit do
h = h - 1
table.insert( State, { ')', true } )
end
end
if diff == -1 and RowN ~= 1 then
if ( prevtab or '' ) ~= '' and ( row.tab0 or '' ) == '' then
table.insert( State, { ']' } )
table.insert( State, { ']]' } )
prevtab = ''
end
table.insert( State, { '}' } )
elseif diff == 0 then
if ( prevtab or '' ) ~= '' and ( row.tab0 or '' ) == '' then
table.insert( State, { ']' } )
table.insert( State, { ']]' } )
prevtab = ''
end
if wasValue then
table.insert( State, { '|' } )
wasValue = nil
end
end
if RowN > #Query then
return tt.get()
end
--TODO: выход
if diff == -1 then
table.insert( State, { '{', row.date } )
wasValue = nil
end
LPrev = 0
for i = math.max( diff, 1 ), 6 do
if ( row['porno'..i] or '' ) == '' then
LPrev = i-1
break
end
if ( row['ass'..i] or '' ) ~= '' then
if ( prevtab or '' ) ~= '' then
table.insert( State, { ']' } )
table.insert( State, { ']]' } )
prevtab = ''
end
elseif prevtab ~= row['tab'..i] then
if ( prevtab or '' ) == '' then
table.insert( State, { '[[' } )
else
table.insert( State, { ']' } )
end
table.insert( State, { '[', row['tab'..i] } )
prevtab = row['tab'..i]
end
table.insert( State, { i, row, ( row['porno'..(i+1)] or '' ) ~= '' } )
wasValue = true
if ( row['porno'..(i+1)] or '' ) ~= '' then
if ( row['ass'..i] or '' ) ~= '' then
table.insert( State, { '(', row['ass'..i] } )
else
table.insert( State, { '(' } )
end
end
end
Prev = row
end
return tt.get()
end
function tt.Test( unit )
-- local unit = mw.text.trim( --[[ frame.args[1] or frame:getParent().args[1] or --]] '20 армия' )
local query_tables =
'units=t0, units=t1, units=t2, units=t3, units=t4, units=t5, units=t6, '
.. --[['units=t0s,]] 'units=t1s, units=t2s, units=t3s, units=t4s, units=t5s, units=t6s'
local query_fields =
't0.fix=date, t0.war=war, '
.. 't0.unit=id0, t0.addendum=add0, t0.ref=ref0, t0.creating=form0, t0.ass=ass0, t0.tab=tab0, t0.hq=hq0, t0.fixunit=porno0, ' -- t0s.unit=s_id0, t0s.addendum=s_add0, t0s.ref=s_ref0, '
.. 't1.unit=id1, t1.addendum=add1, t1.ref=ref1, t1.creating=form1, t1.ass=ass1, t1.tab=tab1, t1.hq=hq1, t1.fixunit=porno1, t1s.unit=s_id1, t1s.addendum=s_add1, t1s.ref=s_ref1, '
.. 't2.unit=id2, t2.addendum=add2, t2.ref=ref2, t2.creating=form2, t2.ass=ass2, t2.tab=tab2, t2.hq=hq2, t2.fixunit=porno2, t2s.unit=s_id2, t2s.addendum=s_add2, t2s.ref=s_ref2, '
.. 't3.unit=id3, t3.addendum=add3, t3.ref=ref3, t3.creating=form3, t3.ass=ass3, t3.tab=tab3, t3.hq=hq3, t3.fixunit=porno3, t3s.unit=s_id3, t3s.addendum=s_add3, t3s.ref=s_ref3, '
.. 't4.unit=id4, t4.addendum=add4, t4.ref=ref4, t4.creating=form4, t4.ass=ass4, t4.tab=tab4, t4.hq=hq4, t4.fixunit=porno4, t4s.unit=s_id4, t4s.addendum=s_add4, t4s.ref=s_ref4, '
.. 't5.unit=id5, t5.addendum=add5, t5.ref=ref5, t5.creating=form5, t5.ass=ass5, t5.tab=tab5, t5.hq=hq5, t5.fixunit=porno5, t5s.unit=s_id5, t5s.addendum=s_add5, t5s.ref=s_ref5, '
.. 't6.unit=id6, t6.addendum=add6, t6.ref=ref6, t6.creating=form6, t6.ass=ass6, t6.tab=tab6, t6.hq=hq6, t6.fixunit=porno6, t6s.unit=s_id6, t6s.addendum=s_add6, t6s.ref=s_ref6, '
.. 't0._pageName=page, t0.page=bookpage'
local query_args = {
join = 't0.fixunit=t1.fixparent, ' -- t0.fixstruct=t0s.fixunit, '
.. 't1.fixunit=t2.fixparent, t1.fixstruct=t1s.fixunit, '
.. 't2.fixunit=t3.fixparent, t2.fixstruct=t2s.fixunit, '
.. 't3.fixunit=t4.fixparent, t3.fixstruct=t3s.fixunit, '
.. 't4.fixunit=t5.fixparent, t4.fixstruct=t4s.fixunit, '
.. 't5.fixunit=t6.fixparent, t5.fixstruct=t5s.fixunit, '
.. 't6.fixstruct=t6s.fixunit ',
where = 't0.unit="' .. unit .. '"',
orderBy = 'date, porno1, porno2, porno3, porno4, porno5, porno6',
limit = 5000,
}
local children = mw.ext.cargo.query( query_tables, query_fields, query_args )
mw.log( #children .. ' ------ ' )
tt.init( children, unit )
local result = {}
repeat
local u, c = tt.get()
if not u then
break
else
c = u[1]
if type( c ) == 'number' then
c = u[2]['id'..c]
end
end
table.insert( result, c )
if #result == 5000 then
break
end
until not u
return '[' .. #result .. '] : ' .. table.concat( result, ' : ' ) .. ' : END '
end
return tt