Module:Clade/transclude

require('strict')

local DEBUG=false --DEBUG=true -- comment out or not runtime or debug

local p ={} local pargs ={}

p.main = function(frame) -- called from template pargs = frame:getParent.args local output local selectedTree -- subtree extracted from page content local modifiedTree -- subtree after pruning and grafting -- (1) get page local page = pargs['page'] or frame.args['page'] if not page then return p.errorMsg("Target page not provided") end -- (2) get content of page (move from _section, _label, etc) local content local title = mw.title.new( mw.text.trim(page)) --, ns) -- creates object if page doesn't exist (and valid page name)	                                            --TODO: could use mw.title.makeTitle, but that needs ns	if title then  		if  title.exists then	  		content = title:getContent			if not content then return p.errorMsg("Content of " .. page .. " not loaded.") end		else			return p.errorMsg('Page with title "' .. page .. '" not found.')    	end	end	-- (3) select from content	local section =  pargs['section'] or pargs['section1'] or pargs[1] 	if section then		selectedTree = p._section(frame, content, section)	end	local label =  pargs['label'] or pargs['label1'] or pargs[1] 	if label then		selectedTree = p._label(frame, content, label)	end   	--TODO does this need to be separate from label?	local subtree =  pargs['subtree'] or pargs['subtree1'] or pargs[1] 	if subtree then		selectedTree = p._label(frame, content, subtree)	end      if not selectedTree then -- if none of options retrieve anything    	p.errorMsg("Nothing retrieved for selection option " .. (label or subtree or section or "none"))   end

if DEBUG then return selectedTree end     --- returns the code captured without processing --(4) modify content (excise and replace; prune and graft) local exclude = pargs['exclude'] or pargs['exclude1'] if exclude then if pargs['exclude'] then pargs['exclude1'] = pargs['exclude'] end if pargs['replace'] then pargs['replace1'] = pargs['replace'] end modifiedTree = selectedTree local i = 1 while pargs['exclude'..i] do	      local exclude = pargs['exclude'..i]		   local replace = pargs['replace'..i] or " "  -- must be something modifiedTree = p._xlabel(frame, modifiedTree, exclude, replace) i=i+1 end else modifiedTree = selectedTree end --(5) other options - suppress hidden elements if pargs['nohidden'] then modifiedTree = modifiedTree:gsub("lade hidden", "lade") end - suppress authorities (or anything in small tags) if pargs['noauthority'] then modifiedTree = modifiedTree:gsub(" .- ", "") end - suppress images if pargs['noimages'] then modifiedTree = modifiedTree:gsub("%", "") end - wrap in outer clade local wrap = pargs['wrap'] if wrap and (label or subtree) then local label1 = label or string.lower(subtree) local styleString = "" if pargs['style'] then  styleString = '|style=' .. pargs['style'] end if wrap ~= "" then label1 = wrap end output = "" -- last space before double brace important else output	= modifiedTree end --(6) return final tree if output then if pargs['raw'] then return output else return frame:preprocess(output) end end return p.errorMsg("No valid option for transclusion") end

--=============================== extract LABELS or SUBTREES======================================= p.label = function (frame, page, ...) local page = frame.args[1] --"User:Jts1882/sandbox/test/Passeriformes" local label = frame.args[1] or frame.args['label'] or frame.args['label1'] local wrap = frame.args['wrap'] local output = p._label (frame, page, frame.args[2], frame.args[3], frame.args[4], frame.args[5] ) if wrap then local label1 = string.lower(frame.args[2]) if wrap ~= "" then label1 = wrap end output = "" end return frame:preprocess(output) end

p._label = function (frame, content, ... ) --	local page = "User:Jts1882/sandbox/test/Passeriformes" --	local label = frame.args[1] or frame.args['label'] local args = { ... }	local output = "" if not args[1] then return p.errorMsg ("Label name not provided") end local mode = "label" local targetType = "label(%d)"                  -- standard label of form |labelN= (captures N)	local cladePrefix = "(%d)"                       -- standard node of form |N= (captures N)	for k,v in pairs(args) do		local section = mw.text.trim(v) if string.upper( section) == section then mode       = "subtree" targetType = "target(%u)"               -- targets of form targetX (X=uppercase letter) cladePrefix = "subclade(%u)"            -- subclades of form subcladeX (captures X)	    end --[=[ the pattern to capture is one of two forms: labelN=Name |N={...} targetX=NAME |subcladeX={...} labelN     =          name                |N           =    {...} or   targetX      =          name                |subcladeX   =    {...} ]=]       local pattern = targetType.."=[%s%p]*"..section .. "[%s%p]+.-"..cladePrefix.."=.-(%b{})" -- this .- skips section tags before -- this .- skips |sublabel and styling following the label (but can return wrong clade when a subtree)

local index1, index2, selectedTree = string.match( content, pattern ) -- note index1 and index2 should match (X=X or N=N)

if selectedTree then --N={FABIDS} we want to substitute the subtree                   but not when the form is |targetX={FABIDS} local pattern2 = "({%u-})"  -- this captures both |N={FABIDS} and |targetX={FABIDS} -- we only want to substitute a subtree in the first kind -- will exclude second with pattern3 test below if string.find(selectedTree, pattern2 ) then         -- if a subtree that hasn't been substituted. --local i,j,target = string.find(value, pattern2) -- only one subtree local i=0 for bracedMarker in string.gmatch( selectedTree, pattern2 ) do                   i=i+1

-- bracedMarker is either a marker in the tree or part of following --    targetX={bracedMarker} ... |subcladeX=s then local pattern3 = "target(%u)=[%s]*"..bracedMarker

--?? if selectedTree == bracedMarker if not string.find(selectedTree, pattern3 ) then local subtree = p._label (frame, content, bracedMarker) if subtree then

-- method 1: the subtree code is substituted into main tree		           	 	        this substitutes the subtree within the clade structure before processing		            	 	        thus there will be a problem with large trees exceeding the expansion depth		            	 	        however, they can be pruned before processing --disable method 1		           	 	selectedTree = string.gsub(selectedTree, bracedMarker, subtree, 1)

--targetX={FABIDS} |subcladeX=subtree" before last double brace of selectedTree		           	 	    use capture in pattern3 to find X local i,j,X = string.find(content, pattern3) if selectedTree == bracedMarker then selectedTree = subtree else selectedTree = selectedTree:sub(1,-3) -- trim final double brace .. "\n|target" .. X .. "=" .. bracedMarker .. "\n|subclade" .. X .. "=" .. subtree .. ""		           	 	          .. "\n }}" end end end --substitution of subtree end end

output = output .. selectedTree else output = output .. p.errorMsg ("Failed to capture subclade with " .. mode .. " " ..section) end

end if output ~= "" then return output -- preprocess moved to entry function else return ' Section for label not found ' end end

--================================== exclude LABEL ================================================

p.xlabel = function (frame, page, ...) local page = frame.args[1] --"User:Jts1882/sandbox/test/Passeriformes" local label = frame.args[1] or frame.args['label'] or frame.args['label1'] -- page, target tree, subtrees to exclude ... --                      page,   include clade, multple clades to exclude | return p._xlabel (frame, page, frame.args[2], frame.args[3], frame.args[4], frame.args[5])

end p._xlabel = function (frame, targetTree, exclude, replace)

local fullOutput = targetTree --local fullOutput = p._section(frame, page, target)

local output=targetTree -- return unmodified tree if nothing happens local section = exclude local targetType = "label%d" local cladePrefix = "%d" if string.upper( section) == section then targetType = "target%u"              -- by convention subtrees must be uppercase cladePrefix = "subclade%u" end

--               label      =         name                        |n=   {...} local pattern = "("..targetType.."=[%s%p]*"..section .. "[%s%p]*.-"..cladePrefix.."=.-)(%b{})" -- ^^ this .- skips section tags before clade -- ^^this .- skips |sublabel and styling following the label (but can return wrong clade when a subtree) local value = string.match( fullOutput, pattern ) if value then local trimmedTree, matches = string.gsub(fullOutput, pattern, "%1"..replace)--replaces pattern with capture %1 return trimmedTree else local message = "" if string.upper(section) == section then message = "; subtree may have been substituted, try label" end output = output .. p.warningMsg ("Failed to capture subclade for exclusion with label "..section..message) end

if output ~= "" then return output .. ' Nothing pruned ' --return frame:preprocess(fullOutput) else return ' Section for label not found ' -- shouldn't get here end end

--======================================== SECTION ================================== p.section = function (frame) -target page  sections return frame:preprocess(p._section(frame, mw.text.trim(frame.args[1]),frame.args[2],frame.args[3],frame.args[4],frame.args[5])) end p._section = function (frame,content,...) local args = { ... }	local output = ""

for k,v in pairs(args) do		local section = mw.text.trim(v) -- note: using the non-greedy - in (.-) to allow capture of several sections 		   this allows internal clade structures to be closed without capturing sisters clades		    e.g. see section Tyranni in User:Jts1882/sandbox/test/Passeriformes local pattern = "(.-)"

for value in string.gmatch( content, pattern ) do		   if value then if frame.args.wrap or frame:getParent.args.wrap then local label1 = frame.args.wrap or frame:getParent.args.wrap if label1 == "" then label1 = section end value = "" end output = output .. value end

end end if pargs['norefs'] or pargs['noref'] then                                               -- strip out references --output =  mw.text.killMarkers( output ) if output:find("', "") output = output:gsub("", "")                                     -- %C works, %w%p%s%c doesn't	   end end if output ~= "" then --return frame:preprocess(output) return output -- leave preprocessing for entry function else return ' Section not found ' end

end

p.xsection = function (frame) local page = frame.args[1] --"User:Jts1882/sandbox/test/Passeriformes" local label = frame.args[1] or frame.args['label'] or frame.args['label1'] -- page, target tree, sections to exclude ... return frame:preprocess(p._xsection(frame, page ,frame.args[2],frame.args[3],frame.args[4],frame.args[5])) end

p._xsection = function (frame,page, target, ...) local args = { ... }	local output = "" local title = mw.title.new( page) --, ns) -- creates object if page doesn't exist (and valid page name)	                                            --TODO: could use mw.title.makeTitle, but that needs ns	if title and title.exists then 		local content = title:getContent		local fullOutput =  p._section(frame, page, target) 	    output=fullOutput		for k,v in pairs(args) do			local section = mw.text.trim(v)			-- note: using the non-greedy - in (.-) to allow capture of several sections 			    this allows internal clade structures to be closed without capturing sisters clades			    e.g. see section Tyranni in User:Jts1882/sandbox/test/Passeriformes						local pattern = "()(.-)()"

local value = string.match( fullOutput, pattern )

if value then local trimmedTree, matches = string.gsub(fullOutput, pattern, "replacement string")--replaces pattern with capture %1 output = output .. trimmedTree output = output .. " " .. trimmedTree .. " "               fullOutput = trimmedTree else output = output .. p.errorMsg ("Failed to capture subclade with label "..section) end

end

else return ' No page title found ' end if output ~= "" then --return frame:preprocess(output) return output -- leave preprocessing for entry function else return ' Section not found ' end

end

function p.firstToUpper(str) return (str:gsub("^%l", string.upper)) end p.errorMsg = function (message) return ' ' .. message .. ' ' end p.warningMsg = function (message) return ' ' .. message .. ' ' end return p