Модуль:SummaryII/format

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

First item is Paragraph 1.
Paragraph 1
Iterating over items:
# item Paragraph 1
# item Paragraph 2
# item Paragraph 3
# item Paragraph 4

Iterating over dict:
* dictionary item val1
* dictionary item val2
* dictionary item val3


(no title2/3 but title = Heading)

--[[
	Dependencies:
--]]
local dep = require 'Module:SummaryII/dependencies'
local yield = dep.yield
local lc, sub, gsub, find = dep.lc, dep.sub, dep.gsub, dep.find
local trim, split = dep.trim, dep.split
local lpeg, regex, re = dep.lpeg, dep.regex, dep.re

local params = require 'Module:SummaryII/params'
local path_format = params.grammar
local opairs, sans = params.pairs, params.sans

local function grammar (injections)
	local P,		V,		set,	C,		Cc,			Cf,			Cg,			Cnil,			s
		= lpeg.P,	lpeg.V,	lpeg.S,	lpeg.C,	lpeg.Cc,	lpeg.Cf,	lpeg.Cg,	lpeg.Cc (nil),	lpeg.locale ().space
	local S, any = s ^ 0, P(1)
	local	star,	sharp,	exclaim,	question,	pipe,	open,	close,	escape
		=	'*',	'#',	'!',		'?',		'||',	'<<',	'>>',	'\\'
	
	local iterators = {
		[sharp]	= ipairs
	  , [star]	= opairs
	}
	local iter_signs = (function (definitions)
		local iter_signs = ''
		for sign, _ in pairs (definitions) do
			iter_signs = iter_signs .. sign
		end
		return set (iter_signs)
	end) (iterators)

	local flags = lpeg.S'g' ^ 0
	
	local path = path_format (injections, {pipe, close})	

	return P{'format'
	  , format	= V 'node' ^ 1 / function (...)
	  		local nodes = {...}
	  		return function (tbl)
	  			local result = ''
	  			for _, node in ipairs (nodes) do
	  				local expanded = node (tbl)
	  				if expanded == false then return false end
  					result = result .. expanded
	  			end
	  			return result
	  		end	-- return function (tbl)
	  	end	-- format = V 'node' ^ 1 / function (...)
	  
	  , node	= V'optional' + V'absent' + V'necessary' + V'str'
	  	
	  , optional= V'macro' * question / function (func)
  			return function (tbl)
  				return func (tbl) or ''
  			end
		end	-- optional = V'macro' * question / function (func)
	  , absent	= V'macro' * exclaim / function (func)
  			return function (tbl)
  				return not func (tbl) and ''
  			end
  		end	-- absent = V'macro' * exclaim / function (func)
  	  , necessary = V'macro' / function (func)
  			return function (tbl)
  				return func (tbl)
  			end
		end	-- necessary = V'macro' / function (func)

	  , macro	= open * S * (V'iter' + V'choice' + V'table' + V'scalar') * close + V'current'
  
	  , current = P (open .. close) * Cc (function (scalar)
	  		return tostring (scalar)
	  	end)
	  , choice	= question * V'format' * (pipe * V'format') ^ 0 / function (...)
	  		local choices = {...}
	  		return function (tbl)
	  			for _, format in ipairs (choices) do
	  				local formatted = format (tbl)
	  				if formatted then
	  					return formatted
	  				end
	  			end
	  			return false
	  		end	-- return function (tbl)
	  	end	-- choice	= question * V'format' * (pipe * V'format') ^ 0 / function (...)
	  , iter	= iter_signs / iterators * S * pipe * V'format' / function (iterator, format)
	  		return function (tbl)
	  			if not tbl then return false end
	  			local result = false
	  			for key, val in iterator (tbl) do
	  				-- Inject key into val here? Or in params?
	  				local formatted = format (val)
	  				if formatted then
	  					result = (result or '') .. formatted
	  				end
	  			end
	  			return result
	  		end
	  	end	-- ipairs = sharp * S * pipe * V'format' / function (format)
	  , table	= path * S * pipe * V'format' / function (query, format)
  			return function (tbl)
  				return format (query (tbl))
  			end
	  	end	-- table = path * S * pipe * V'format' / function (query, format)
	  , scalar	= path / function (query)
  			return function (tbl)
  				return query (tbl) or false
  			end
	  	end	-- scalar	= path / function (query)

  	  , str		= C (sans (open, close, pipe) ^ 1) / function (str)
  	  		return function (tbl)
  	  			return str
  	  		end
  	  	end	-- str = C (sans (open) ^ 1) / function (str)
	}	-- return P{'format', ...}
end	-- local function grammar (injections)

local function formatter (format, injections)
	local formatter = grammar (injections):match (format)
	if type (formatter) == 'function' then
		return function (tbl)
			return formatter (tbl) or ''
		end
	else
		return function (tbl)
			return 'Format not recognised'
		end
	end
end -- local function formatter (format, injections)

-- Temporary module interface for testing purposes:
return {
	test = function (frame)
		local format_string = [==[
Formatted:
<strong><<title>></strong>
<em><<subtitle>>?<em>
<<items||First item is <<1>>.>>
<<items.1>>
<<items||Iterating over items:<<#||
# item <<>>>>
>>
<<dict||Iterating over dict:<<*||
* dictionary item <<>>>>
>>
<<non-existent||Iterating over non-existent:<<*||
* dictionary item <<>>>>
>>?
<<?<<title2>>||<<title3>>||(no title2/3 but title = <<title>>)>>
]==]
--[==[
<<items.1|<<>>>>
<<#items ||<<.>>, >>
<<subtitle|instead=(no subtitle)|before=<em>|after=</em> >>
<<items|<<#|separator=<br />|before=Items:<br /> >>|No items>>
<<tbl|<<#|<<col1>> — <<col2>>|No rows|separator=<br /> >>|No table|before=Table:<br />1 — 2<br />|after=<br />-------->>
<<tbl2|instead=No rows (II)|separator=, |before=Table (II):<br />|after=<br />-------->>
<<tbl/#|body=<<col1>> — <<col2>> (<<title>>)|instead=No rows|separator=<br />|before=Table again:<br />1 — 2<br />|after=<br />-------->>
<<tbl/#/col1|instead=No rows|separator=<br />|before=Now first columns only:<br />1<br />|after=<br />-------->>
<<dict/*|<<@>> = <<.>>|No dictionary|separator=,<br />|before=Dictionary:<br />|after=<br />-------->>
<<x * y * z / # | x = <<x>>, y = <<y>>, z = <<z>>|No combinations<br />|separator = ,<br />|before = Combinations<br />|after = <br />-------->>
<<set/#|Name: <<.>>|No names<br />|unique=<<.>>|separator= ,<br />|before = Names<br />|after = <br />-------->>
<< <<index>> | before = Index: | after = <br />-------->>
<< items / first (<<number>>)| before = First <<number>> paragraphs:<br /> | separator = <br /> | after = <br />-------->>
<< squared (<<number>>) | before = Number squared: | after = <br />-------->>
not_set: << not_set>><br />--------
<<index * number | body = index: <<index>>, number: <<number>> | before = index * number: |after = <br />--------|instead = index * number failed <br />----------->>
<<not_set | body = not_set: <<not_set>>| before = not_set: |after = <br />--------|instead = not_set failed <br />----------->>
<<index * not_set | body = index: <<index>>, not_set: <<not_set>>| before = index * not_set: |after = <br />--------|instead = index * not_set failed <br />----------->>
<<index * not_set | before = index * not_set: |after = <br />--------|instead = index * not_set failed <br />----------->>
<<not_set1 * not_set2 | before = not_set1 * not_set2: |after = <br />--------|instead = not_set1 * not_set2 failed <br />----------->>
<<not_set1 * not_set2 | body = not_set1: <<not_set1>>, not_set2: <<not_set2>> | before = not_set1 * not_set2: |after = <br />--------|instead = not_set1 * not_set2 failed <br />----------->>
<<not_set1 * not_set2 / * | before = not_set1 * not_set2: |after = <br />--------|instead = not_set1 * not_set2 failed <br />----------->>
]==]

		return '<pre>' .. formatter (format_string) {
			title	= 'Heading'
		  , items	= {
		  		'Paragraph 1'
		  	  , 'Paragraph 2'
		  	  , 'Paragraph 3'
		  	  , 'Paragraph 4'		  	  
		  	}
		  , tbl		= {
		  		{col1 = '11', col2 = '12'}
		  	  , {col1 = '21', col2 = '22'}
		  	  , {col1 = '31', col2 = '32'}
		  	}
		  , tbl2 = {'First', 'second', 'third', 'fourth'}
		  , dict	= {
		  		key1	= 'val1'
		  	  , key2	= 'val2'
		  	  , key3	= 'val3'
		  	}
		  , x		= {'x1', 'x2', 'x3'}
		  , y		= {'y1', 'y2', 'y3'}
		  , z		= {'z1', 'z2', 'z3'}
		  , set		= {'mark', 'matthew', 'luke', 'john', 'mark'}
	  	  , number	= 3
	  	  , index	= 'title'
		} .. '</pre>'
	end		-- test = function (frame)
}	-- return