Difference between revisions of "Module:Clade/transclude"
Jump to navigation
Jump to search
blackwiki>Jts1882 (in progress save) |
blackwiki>Jts1882 (don't substitute if bracedCapture follows targetX=) |
||
| Line 118: | Line 118: | ||
local mode = "label" | local mode = "label" | ||
| − | local targetType = "label%d" -- standard label of form |labelN= | + | local targetType = "label(%d)" -- standard label of form |labelN= (captures N) |
| − | local cladePrefix = "%d" -- standard node of form |N= | + | local cladePrefix = "(%d)" -- standard node of form |N= (captures N) |
for k,v in pairs(args) do | for k,v in pairs(args) do | ||
local section = mw.text.trim(v) | local section = mw.text.trim(v) | ||
if string.upper( section) == section then | if string.upper( section) == section then | ||
mode = "subtree" | mode = "subtree" | ||
| − | targetType = "target%u" | + | targetType = "target(%u)" -- targets of form targetX (X=uppercase letter) |
| − | cladePrefix = "subclade%u" -- subclades of form subcladeX | + | cladePrefix = "subclade(%u)" -- subclades of form subcladeX (captures X) |
end | end | ||
| − | + | ||
| − | + | --[=[ the pattern to capture is one of two forms: labelN=Name |N={...} | |
| − | + | targetX=NAME |subcladeX={...} | |
| − | local pattern = targetType.."= | + | labelN = [[ name ]] |N = {...} |
| + | or targetX = [[ name ]] |subcladeX = {...} | ||
| + | ]=] | ||
| + | local pattern = targetType.."=[%s%p]*"..section .. "[%s%p]*.-"..cladePrefix.."=.-(%b{})" | ||
-- this .- skips section tags before {{clade ...}} | -- this .- skips section tags before {{clade ...}} | ||
-- this .- skips |sublabel and styling following the label (but can return wrong clade when a subtree) | -- this .- skips |sublabel and styling following the label (but can return wrong clade when a subtree) | ||
| − | local | + | local index1, index2, selectedTree = string.match( content , pattern ) |
| + | -- note index1 and index2 should match (X=X or N=N) | ||
-- mode=="label" or mode=="subtree" -- does this matter | -- mode=="label" or mode=="subtree" -- does this matter | ||
| Line 145: | Line 149: | ||
]] | ]] | ||
| − | local | + | local pattern2 = "({%u-})" -- this also adds the subtree to |targetX={FABIDS} |
| − | -- which | + | -- which van cause duplicate substitution and large post expand size |
| − | -- but not if all subtrees basal (testcases2) | + | -- but not if all subtrees are basal (testcases2) |
| − | -- | + | --pattern2 = "%d=({%u-})" -- we only want to substitute in |n={FABIDS} |
-- but this causes issues with Eudicots | -- but this causes issues with Eudicots | ||
--pattern2 = "%u=({%u-})" | --pattern2 = "%u=({%u-})" | ||
| + | --TODO if content contains |N=pattern we wan the add the subtree at |targetX=pattern | ||
| − | + | if string.find(selectedTree, pattern2 ) then -- if a subtree that hasn't been substituted. | |
| − | if string.find(selectedTree, | + | --local i,j,target = string.find(value, pattern2) -- only one subtree |
| − | --local i,j,target = string.find(value, | ||
local i=0 | local i=0 | ||
| − | for | + | for bracedMarker in string.gmatch( selectedTree , pattern2 ) do |
i=i+1 | 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 not string.find(selectedTree, pattern3 ) then | ||
| − | + | local subtree = p._label (frame, content, bracedMarker) | |
| − | + | if subtree then | |
| − | + | if DEBUG then subtree = "[i="..i.."] ".. subtree end | |
| − | + | ||
| − | + | -- method 1: the subtree code is substituted | |
| − | + | selectedTree = string.gsub(selectedTree, bracedMarker, subtree, 1) | |
| − | + | --[[Note: 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 | |
| − | + | TODO add the subtree as a subtree within the clade structure | |
| − | end | + | substitute "|targetX={FABIDS} |subcladeX=subtree" before last brace of selectedTree |
| + | ]] | ||
| + | --method 2: the subtree is appended (before the final brace) | ||
| + | end | ||
| + | end --checkbracer marker with pattern 3 | ||
end | end | ||
end | end | ||
| − | |||
output = output .. selectedTree | output = output .. selectedTree | ||
else | else | ||
| Line 186: | Line 195: | ||
if output ~= "" then | if output ~= "" then | ||
| − | |||
return output -- preprocess moved to entry function | return output -- preprocess moved to entry function | ||
else | else | ||
Revision as of 13:24, 13 November 2019
| 26x26px | This module is rated as alpha. It is ready for third-party input, and may be used on a few pages to see if problems arise, but should be watched. Suggestions for new features or changes in their input and output mechanisms are welcome. |
Module for template {{Clade transclude}} with functions for partial transclusion of cladograms made with the {{Clade}} template.
Usage
{{#invoke:Clade|main}}
Parameters described at {{Clade transclude}}.
Testcases:
- {{Clade transclude/testcases}} (examples using nested subtrees)
- {{Clade transclude/testcases2}} (examples with all trees at basal clade)
require('Module:No globals')
--local DEBUG=true
local DEBUG=false
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']
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]
modifiedTree = p._xlabel(frame, modifiedTree, exclude, replace)
i=i+1
end
else
modifiedTree = selectedTree
end
--(5) wrap in outer clade if requested
local wrap = pargs['wrap']
if wrap and (label or subtree) then
local label1 = label or string.lower(subtree)
if wrap ~= "" then label1 = wrap end
output = "{{clade |label1=" .. p.firstToUpper(label1) .. "|1=" .. modifiedTree .. " }}" -- last space before double brace important
else
output = modifiedTree
end
--(6) return final tree
if output then
return frame:preprocess(output)
--return output
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 = "{{clade |label1=" .. p.firstToUpper(label1) .. "|1=" .. 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 {{clade ...}}
-- 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)
-- mode=="label" or mode=="subtree" -- does this matter
if selectedTree then
--if selectedTree then
--[[ what if we have something like {FABIDS}
its can be the whole captured selectedTree={FABIDS}, which needs expansion
or it can be part of a targetX={FABIDS}
]]
local pattern2 = "({%u-})" -- this also adds the subtree to |targetX={FABIDS}
-- which van cause duplicate substitution and large post expand size
-- but not if all subtrees are basal (testcases2)
--pattern2 = "%d=({%u-})" -- we only want to substitute in |n={FABIDS}
-- but this causes issues with Eudicots
--pattern2 = "%u=({%u-})"
--TODO if content contains |N=pattern we wan the add the subtree at |targetX=pattern
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 not string.find(selectedTree, pattern3 ) then
local subtree = p._label (frame, content, bracedMarker)
if subtree then
if DEBUG then subtree = "[i="..i.."] ".. subtree end
-- method 1: the subtree code is substituted
selectedTree = string.gsub(selectedTree, bracedMarker, subtree, 1)
--[[Note: 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
TODO add the subtree as a subtree within the clade structure
substitute "|targetX={FABIDS} |subcladeX=subtree" before last brace of selectedTree
]]
--method 2: the subtree is appended (before the final brace)
end
end --checkbracer marker with pattern 3
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 '<span class="error">Section for label not found</span>'
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 .. '<span class="error">Nothing pruned</span>'
--return frame:preprocess(fullOutput)
else
return '<span class="error">Section for label not found</span>' -- 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 = "<section begin="..section.."[ ]*/>(.-)<section end="..section.."[ ]*/>"
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 = "{{clade |label1=" .. label1 .. "|1=" .. value .. "}}"
end
output = output .. value
end
end
end
if output ~= "" then
--return frame:preprocess(output)
return output -- leave preprocessing for entry function
else
return '<span class="error">Section not found</span>'
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 = "(<section begin="..section.."[ ]*/>)(.-)(<section end="..section.."[ ]*/>)"
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 .. "<pre>" .. trimmedTree .. "</pre>"
fullOutput = trimmedTree
else
output = output .. p.errorMsg ("Failed to capture subclade with label "..section)
end
end
else
return '<span class="error">No page title found</span>'
end
if output ~= "" then
--return frame:preprocess(output)
return output -- leave preprocessing for entry function
else
return '<span class="error">Section not found</span>'
end
end
function p.firstToUpper(str)
return (str:gsub("^%l", string.upper))
end
p.errorMsg = function (message)
return '<span class="error">' .. message .. '</span>'
end
p.warningMsg = function (message)
return '<span class="warning">' .. message .. '</span>'
end
return p