Модуль:SummaryII/test2

Материал из свободной русской энциклопедии «Традиция»
Перейти к: навигация, поиск

Несколько параметров[править]

Параметры Возврат
название3 = Название5
стоимость = $88.14
масса = 14,88 kg
название2 = Название3, Название4
название = [[Название1]], [[Название2]]
дата создания2 = [[5 ноября]] [[1613]]
дата создания = * 9 января 1976 года,
* 20 апреля 1889,
* 4 ноября 1612
Ссылка = * http://traditio.wiki,
* http://traditio-ru.org,
* http://traditio.ru
 {
   [1] =  {
       ['Not matched'] =  {
       },
       cost =   {
           [1] = '$88.14'
       },
       ['название'] =  {
           ['1'] = 'Я сам знаю, какое название лучше'
       },
       ['датасоздания'] =  {
           [1] = '* 9 января 1976 года,
  • 20 апреля Свойство «Дата создания» имеет особое назначение, и его значение не может устанавливаться в семантической аннотации.1889,
  • 4 ноября Свойство «Дата создания» имеет особое назначение, и его значение не может устанавливаться в семантической аннотации.1612Свойство «Дата создания» имеет особое назначение, и его значение не может устанавливаться в семантической аннотации.',
           ['2'] = '5 ноября 1613Свойство «Дата создания» имеет особое назначение, и его значение не может устанавливаться в семантической аннотации.'
       },
       url =   {
           [1] = '* http://traditio.wiki,
       },
       ['масса'] =  {
           [1] = '14,88 kg'
       }
   }

}


--[[
Тесты SummaryII.
--]]

local table_and_array_functions = require "Модуль:Array"
local Set 						= require "Модуль:Set"
local merge 					= table_and_array_functions.table_merge
local service					= require "Модуль:SummaryII/service"
local strip						= service.strip
local strip_number				= service.strip_number

--[[
Class TemplateProcessor.

Зависимости:
	Модуль:Class,
	Модуль:Array,
	Модуль:SummaryII/service
	lpeg
	Модуль:Re
--]]
local Class = require ('Модуль:Class').create

-- Accelerators:
local Array						= table_and_array_functions.Array

local lpeg		= lpeg
local Ct		= lpeg.Ct
local cartesian	= require "Модуль:Array".cartesian

local deserialise_lpeg	= lpeg.serialize.load
local gsub				= mw.ustring.gsub

local TemplateProcessor = Class {
	lines			= {}
  , vectors_by_id	= {}
  , flat			= {}
  , lines_by_index	= {}
  
  , separators		= {}
  
  , defaults	= {}
  , overrides	= {}
  , appends		= {}
  
  , widgets				= {}
  , widgets_dispatcher	= {}  
  
  , protected	= {}
  , unique		= {}
  
  , log = ''

}

function TemplateProcessor:_init (ontology)
	self.lpeg = lpeg.P (false)
	local append_index = -9999
	for _, rule in ipairs (ontology) do
		if rule.type == 'lpeg' then
			self.lpeg = self.lpeg + deserialise_lpeg (rule.lpeg)
		elseif rule.type == 'default' then
			self:register_rule (self.defaults, rule.input, 1)
		elseif rule.type == 'override' then
			self:register_rule (self.overrides, rule.input, 1)
		elseif rule.type == 'append' then
			self:register_rule (self.appends, rule.input, append_index)
			append_index = append_index + 1
		elseif rule.type == 'widget' then
			self.widgets [#self.widgets + 1] = rule
			for _, id in ipairs (rule.ids or {}) do
				self.widgets_dispatcher [id] = self.widgets_dispatcher [id] or {}
				self.widgets_dispatcher [id] [#self.widgets_dispatcher [id] + 1] = rule
			end
		end
	end
end		-- function TemplateProcessor:_init (ontology)

function TemplateProcessor:register_rule (rules, input, index)
	for name, value in pairs (input) do
		rules [name .. index] = value
	end
end		-- function TemplateProcessor:register_rule (rules, id, index, input, index)

function TemplateProcessor:_call (params)
	-- Injecting overrides (protected defaults):
	self:process_params (self.overrides, 2)

	-- User input:
	self:process_params (params, 1)

	-- Injecting appendable defaults:
	self:process_params (self.appends, 1, true)
	
	-- Injecting overridable defaults:	
	self:process_params (self.defaults, 0)

	return self.lines --, self.widgets
end		-- function TemplateProcessor:_call (params)

function TemplateProcessor:process_params (associative, level, only_unique)
	for name, value in pairs (associative) do
		local id, index, line, vectors, separator = self:single (name, value)
		self:record_ids (id, index, line, vectors, separator, level, only_unique)
	end	-- for name, value in pairs (associative)
end		-- function TemplateProcessor:process_params (associative, level, only_unique)

function TemplateProcessor:record_ids (id, index, line, vectors, separator, level, only_unique)
	-- Do not try to override overrides (unoverridable defaults):
	if	level >= (self.protected [id] or 0) then
		self.unique [id] = self.unique [id] or Set ()
		for _, vector in ipairs (vectors or {}) do
			-- TODO: Need a hash of vector, not the vector itself.
			if only_unique and self.unique [id]:contains (vector) then
				vector = nil
			else
				self.unique [id]:add (vectors)
			end
		end

		self.lines [id] = self.lines [id] or {}
		self.lines [id] [index] = line
		
		self.vectors_by_id [id] = self.vectors_by_id [id] or {}
		self.vectors_by_id [id] [index] = vectors
		
		self.lines_by_index [index] = self.lines_by_index [index] or {}
		self.lines_by_index [index] [id] = line
		
		self.separators [id] = separator or self.separators [id]
		
		--[[
		-- Build widgets here:
		self.widgets [id]:process (id, index, line, vectors, self.separators [id])
		]]
		
		self.protected [id] = level
	end	-- if	level >= (self.protected [id] or 0)
end		-- function TemplateProcessor:record_ids (id, index, line, vectors, separator, level, only_unique)

function TemplateProcessor:build_widget (widget)
	local output = ''
	for index, line in pairs (self.lines [widget.id]) do
		output = output .. self.separators [widget.id] .. line
	end
	return output
end		-- function TemplateProcessor:build_widget (widget)
	
--[[
function TemplateProcessor.deduplicate (minuend, subtrahend)
	for _, append in ipairs (minuend) do
		local duplicated = false
		for __, vector in ipairs (subtrahend) do
			local duplicate = true
			for property, value in pairs (append) do
				if vector [property] ~= value then
					-- Current append does not duplicate the current vector:
					duplicate = false
					break
				end
			end
			if duplicate then
				duplicated = true
				break
			end
		end	-- for __, vector in ipairs (subtrahend)
		if duplicated then
			append = nil
		end
	end	-- for _, append in ipairs (minuend)
end		-- function TemplateProcessor.deduplicate (minuend, subtrahend)
]]

function TemplateProcessor:single (name, value)
	local glued = self:glue_param (name, value)
	local captures = Ct (self.lpeg):match (glued)
	if captures and type (captures) == 'table' then
		
		local index = captures.index or 1
		
		local line = ''
		local ordered_values = {}
		
		-- Initialise intravectors:
		local complete_vectors = Array ()
		local polyvector = {}
		local last_no = 0
		
		local separator = ', '
		for i, affixed in ipairs (captures) do
			
			-- Choose normalised SMW value:
			affixed.value = affixed.processed [2] or affixed.processed [1]
			-- Choose alias:
			affixed.alias = affixed.alias or affixed.processed [1]
			
			-- Append pure property list for later use:
			-- TODO: ordering is not needed any more.
			ordered_values [#ordered_values + 1] = {
				property	= affixed.property
			  , value		= affixed.value
			}
			
			-- Form intravectors:
			local no			= tonumber (affixed.no)
			local new_vectors	= {}
			local inject		= nil

			if no < last_no then
				-- New intravector:
				new_vectors = cartesian (polyvector)
				polyvector = {}				
				inject = captures.inject
			end
			
			polyvector [affixed.property] = polyvector [affixed.property] or {}
			polyvector [affixed.property] [#polyvector [affixed.property] + 1] = affixed.value
			last_no = no
			
			-- Append output line:
			line = line	.. self.substitute (affixed.output, affixed)

			if i == #captures then
				-- Last intravector:
				new_vectors = cartesian (polyvector)
				inject = captures.inject
			end
			
			if inject then
				for _, vector in ipairs (new_vectors) do
					line = line .. self.substitute (inject, vector)
				end
			end
							
			complete_vectors:append2self (new_vectors)
			
			separator = affixed.separtor or separator

		end	-- for _, affixed in ipairs (captures)
		
		-- Attach tail:
		line = line .. (captures.tail or '')
		
		for _, vector in ipairs (complete_vectors) do
			-- Infer properties:
			if captures.inferred then
				for property, value in pairs (captures.inferred) do
					vector [property] = self.substitute (value, vector)
				end
			end	-- if captures.inferred
		end	-- for _, vector in ipairs (complete_vectors)

		return captures.id, index, line, complete_vectors, separator --, captures, ordered_values
	else
		return 'Not matched', name .. ' = ' .. value
	end	-- if captures and type (captures) == 'table'
end	-- function TemplateProcessor:single (name, value)

function TemplateProcessor.substitute (format, values)
	return gsub (format, '@([^@]+)@', values)
end		-- function TemplateProcessor:substitute (format, values)

function TemplateProcessor.glue (args)
	return table.concat (args, mw.ustring.char (0))
end		-- function TemplateProcessor.glue (args)

function TemplateProcessor:glue_param (name, value)
	return strip (tostring (name)) .. '=' .. mw.text.trim (value)
end		-- function TemplateProcessor:glue_param (name, value)

-- end of class TemplateProcessor

local Widget = Class {
}

function Widget:_init (output)
	-- TODO: there can be several outputs, joined at the last moment:
	self.output = output
end		-- function Widget:_init (output)

function Widget:process (id, index, line, vectors, separator)
	self.output = TemplateProcessor.substitute (self.output, {['@' .. id .. '@'] = line})
	self.output = TemplateProcessor.substitute (self.output, {['@index@'] = index})
	-- TODO: This subctitution depends on widget type:
	self.output = TemplateProcessor.substitute (self.output, vectors [1])
end

function Widget:__tostring ()
	-- TODO: there can be several outputs, joined at the last moment:
	return self.output or ''
end		-- function Widget:__tostring ()

-- end of class Widget

local spaces = lpeg.S '  ' ^ 0
local html_tags = lpeg.P {
	'tags'
  , tags	= (lpeg.P '<' * (1 - lpeg.P '>') ^ 0 * lpeg.P '>') ^ 1
}
local balanced_parentheses = lpeg.P {'(' * ((1 - lpeg.S '()') + lpeg.V (1))^0 * ')'}
local separators = html_tags + lpeg.S ',;:*#\n' ^ 1

local function simulate_ontology (serialised_parsers, defaults, overrides, appends)
	local rules = {}
	for _, parser in ipairs (serialised_parsers or {}) do
		rules [#rules + 1] = {type = 'lpeg', lpeg = parser}
	end
	for _, default in ipairs (defaults or {}) do
		rules [#rules + 1] = {type = 'default', input = default}
	end
	for _, override in ipairs (overrides or {}) do
		rules [#rules + 1] = {type = 'override', input = override}
	end
	for _, append in ipairs (appends or {}) do
		rules [#rules + 1] = {type = 'append', input = append}
	end
	return rules
end		-- simulate_ontology (serialised_parsers, defaults, overrides, appends)

		
-- Module interface
local m = {}
m.test_params = function (frame)
	local serialise = require ('Модуль:Test').serialise
	local factory = require "Модуль:SummaryII/parsers".parser
	local ontology = simulate_ontology ({
		-- Parsers:
		factory ({'Название'}, 'string', 'Название', separators):serialise ()
	  , factory ({'Дата создания', 'создание'}, 'date', 'создания', separators):serialise ()
	  , factory ({'Масса'}, 'quantity', 'Масса', separators, {units = {'kg', 'g', 'tonne', 'tonnes', 'mg', 'mkg'}, default_unit = 'kg'}):serialise ()
	  , factory ({'Ссылка'}, 'URL', 'URL', separators, {default_protocol = 'http'}):serialise ()
	  , factory ({'Стоимость'}, 'quantity', 'Cost', separators, {units = {'руб.', 'долл.', 'USD', 'евро'}, preunits = {'$', '€', '£'}, default_unit = 'руб.'}):serialise ()
	}, {
		-- Defaults:
		{['Дата создания'] = '1 октября 2016'}
	}, {
		-- Overrides:
		{['Название'] = '[[Я сам знаю, какое название лучше]]'}
	}, {
		-- Appends:
		{['URL'] = 'https://traditio.wiki'}
	  , {['URL'] = 'http://traditio.wiki'}	
	})
	local parser = TemplateProcessor (ontology)
	local ret = [==[
{| class="wikitable"
! Параметры !! Возврат
|-
|
]==]
	for name, value in pairs (frame.args) do
		ret = ret .. '\n <nowiki>' .. tostring (name) .. ' = ' .. value .. '</nowiki>'
	end
	ret = ret .. '\n|\n ' .. serialise ({parser (frame.args)}) .. '\n|}'
	return frame:preprocess (ret)
end	

-- Обёртка для frame:expandTemplate, чтобы не передавать frame всю дорогу:
local function expandTemplate (template, params)
    return mw.getCurrentFrame ():expandTemplate{ title = template, args = params }
end


return m