Модуль: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] ~= '' then -- если дошли до уровня армий, то может требоваться принудительное закрытие таба и ряда табов
					if prevtab ~= '' 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 ~= '' and row.tab0 == '' then
				table.insert( State, { ']' } )
				table.insert( State, { ']]' } )
				prevtab = ''
			end
			table.insert( State, { '}' } )
		elseif diff == 0 then
			if prevtab ~= '' and row.tab0 == '' 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] == '' then
				LPrev = i-1
				break
			end
			if row['ass'..i] ~= '' then
				if prevtab ~= '' then
					table.insert( State, { ']' } )
					table.insert( State, { ']]' } )
					prevtab = ''
				end
			elseif prevtab ~= row['tab'..i] then
				if prevtab == '' 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] ~= '' 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