Module:Str find word

-- stable 2021-10-14 14:33 -- todo: xp into separate submodule local p = {} require('Module:No globals') local str = require('Module:String') local getArgs = require('Module:Arguments').getArgs local tableTools = require('Module:TableTools') local yesno = require('Module:Yesno') local report -- explain

-- hitwords, to build the returnstring: -- these are outer because the function _checkXXwords returns boolean T/F local hitsANDtable = {} local hitsORtable = {} -- explain = show debug info in preview: local br = ' ' local xpTable -- xp=explain: debug report local pipe = '&#x007C;' -- | in xp only

local function _xpFormat local box local divIn, divOut

divIn = br .. ' '	divOut = ' ' box = divIn .. table.concat(xpTable, br) .. divOut

return box end

local function _xpIsPreview local ifPreview = require('Module:If preview') return not (ifPreview._warning( {'is_preview'} ) == '') end

local function _xpInit --todo: lua efficiency, here? load subpage require('Module:sfw/explain') report = require('Module:User:DePiep/sfw/report')

end

local function _xpTitle local title

title = ' Preview &#123;&#123;Str find word&#125;&#125; explain: ' table.insert(xpTable, 1, title) end

local function _xpResult(bResult, msg2) local aye = '' local nay = '' local msg

bResult = yesno(bResult, false) msg = 'RESULT: ' if bResult == true then msg = msg .. 'TRUE ' .. aye	.. ' words found in source: <' .. msg2 .. '>'	else msg = msg .. 'FALSE ' .. nay .. ' ' .. msg2 end table.insert(xpTable, 2, msg) end

local function _xpYesPlain(sYesValue) local plain = require('Module:Plain text')

sYesValue = mw.text.killMarkers(sYesValue) :gsub('%[%[%s*[Ff][Ii][Ll][Ee]%s*:', 'File:') --prevent strip out files		:gsub('%[%[%s*[Ii][Mm][Aa][Gg][Ee]%s*:', '[[:Image:') --prevent strip out use of image:		:gsub('%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:', '[[:Category:') --prevent strip out categories	sYesValue = plain._main(sYesValue) -- also strips [[file: cat:	if mw.ustring.len(sYesValue) > 200 then		sYesValue = mw.ustring.sub( sYesValue, 1, 200 ) .. ' ...'	end	return sYesValue end

local function _xpReturnValue(sYeslist, sYes, sNo) local msgReturn

msgReturn = 'RETURN VALUE ' if sYeslist == '' then -- FALSE msgReturn = msgReturn .. pipe .. 'no= ' else -- TRUE if (sYes == nil) then msgReturn = msgReturn .. '(default ' .. pipe .. 'yes= list of words found): ' else -- TRUE msgReturn = msgReturn .. pipe .. 'yes= ' .. ' '		end end

table.insert(xpTable, 3, msgReturn) end

local function _xpCase(case) local msgCase = ''

if case == true then msgCase = 'case-sensitive: true (foo ≠ Foo)' else msgCase = 'case-sensitive: false (foo = Foo)' end table.insert(xpTable, 3, msgCase) end

local function _xpBuildResultSet(sourceWordTable, case,						bANDresult, andWordTable, 						bORresult, orWordTable, 						resultALL, yeslist)

_xpResult(resultALL, yeslist) _xpCase(case) table.insert(xpTable, 'source word list: <' .. table.concat(sourceWordTable, ', ') .. '>') if #andWordTable ~= 0 then table.insert(xpTable, 'AND words to find: <' .. table.concat(andWordTable, ', ') .. '>') table.insert(xpTable, 'AND words found: <' .. table.concat(hitsANDtable, ', ') .. '> ' .. string.upper(tostring(bANDresult))) end if #orWordTable ~= 0 then table.insert(xpTable, 'OR words to find: <' .. table.concat(orWordTable, ', ') .. '>') table.insert(xpTable, 'OR words found: <' .. table.concat(hitsORtable, ', ') .. '> ' .. string.upper(tostring(bORresult))) end end

local function _xpYesNoBothBlank table.insert(xpTable, 'both returnvalues ' .. pipe .. 'yes=' .. pipe .. 'no= are, so check is trivial.') end

local function _xpNoWords(sourceWordTable, andWordTable, orWordTable, sSource, sWords) if (#sourceWordTable == 0) then table.insert(xpTable, 'E01: no words in source \<' .. sSource .. '\>') end if (#andWordTable + #orWordTable == 0) then sWords = str._match( sWords, '^% +([.]*)% *$', 1, 1, false, '') table.insert(xpTable, 'E02: no words to find (word, andwords, orwords): <' .. sWords .. '>') end _xpResult(false, '(no words to find)') end

--               local function _decodeUnicode(str) -- turn ";#x0066;" into "f" return mw.text.decode(str) end

local function _buildWordTable(stringList) local wordTable = {} local hitWord = '' local count = 0

stringList = _decodeUnicode(stringList) count = 0 while count < 13 do		count = count + 1 -- function str._match( s, pattern, start, match_index, plain_flag, nomatch ) -- "returns a substring *or* the nomatch-value" (= applied '' here) hitWord = str._match(stringList .. ';', '%f[%w%-%_][%w%-%_]+%f[^%w%-%_]', 1, count, false, ) or  if hitWord == '' then -- no more words found break else table.insert(wordTable, hitWord) end end

return wordTable end

local function _escape_word(word) -- all punctuation (%p) will be %-escaped; but only the word-characters '-' and '_' expected here though. return str._escapePattern(word) end

local function _findWord(str_source, word) -- returns hitword or nil -- '%f[%w%-%_]' = frontier, see pattern-documentation local str_target = '' local iHit = 0

str_target = '%f[%w%-%_]' .. _escape_word(word) .. '%f[^%w%-%_]' iHit = mw.ustring.find(str_source, str_target, 1, false) or -1

if tonumber(iHit) > 0 then return word else return nil end end

local function _checkANDwords(str_source, andWordTable) -- returns hitTable (# >= 0) for True, nil for false -- AND-logic & andWordTable words: ALL words must be True local result1 = nil local bAND

bAND = true if tableTools.size(andWordTable) > 0 then for i, word in ipairs(andWordTable) do   		result1 = _findWord(str_source, word) or nil if result1 ~= nil or false then table.insert(hitsANDtable, result1) else bAND = false -- falsified; could continue to complete the table though todo -- break end end else -- no AND-list so *not* falsified bAND = true end return bAND end

local function _checkORwords(str_source, orWordTable) -- returns hitTable (# >= 0) for True, nil for false -- apply OR-logic with orWordTable words: at least one must be True local result1 = nil local bOR

bOR=false hitsORtable = {} if tableTools.size(orWordTable) > 0 then for i, word in ipairs(orWordTable) do   		result1 = _findWord(str_source, word) or nil if result1 == nil then else bOR = true table.insert(hitsORtable, result1) -- could break allright here, because one failure = falsified so job is done -- but complete the OR list for debug/report purposes (to list all positive OR-words) end end else -- no OR-list so *not* falsified (return empty table) bOR = true end

return bOR end -- ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== function p._main(s, w, andString, orString, case, yesVal, noVal, explain) -- return hitwords string when True, returns empty string '' when false -- input word strings parsed: local sourceWordTable = {} local andWordTable = {} local orWordTable = {} -- logical finding: local bANDresult = false local bORresult = false local resultALL = false -- return string (empty when False) local yeslist = ''

case = yesno(case, false) if case == true then else s = string.lower(s) w = string.lower(w) andString = string.lower(andString) orString =  string.lower(orString) end

sourceWordTable = _buildWordTable(s or '') andString = w .. '; ' .. andString andWordTable = _buildWordTable(andString or '') orWordTable = _buildWordTable(orString or '')

if (#sourceWordTable == 0) or (#andWordTable + #orWordTable == 0) then if explain then _xpNoWords(sourceWordTable, andWordTable, orWordTable, s, w .. ' ' .. andString .. ' ' .. orString) end return '' end

bANDresult = _checkANDwords(s, andWordTable) bORresult = _checkORwords(s, orWordTable) resultALL = (bANDresult) and (bORresult)

if resultALL then -- concat the Yeslist (= all hit words) yeslist = '' if bANDresult then yeslist = yeslist .. table.concat(hitsANDtable, ', ') end

if #hitsORtable > 0 then if #hitsANDtable > 0 then yeslist = yeslist .. '; '			end yeslist = yeslist .. table.concat(hitsORtable, ', ') end end if explain then if (yesVal == ) and (noVal == ) then _xpYesNoBothBlank end

_xpBuildResultSet(sourceWordTable, case,						bANDresult, andWordTable, 						bORresult, orWordTable, 						resultALL, yeslist) end if resultALL then return yeslist else return '' end end

function p.main(frame) local args = getArgs(frame, { removeBlanks = false } ) -- keep blanks to differentiate |yes= value local s = _decodeUnicode(args['s'] or args['source'] or args['1'] or '') local w = _decodeUnicode(args['w'] or args['word'] or args['2'] or '') local andString = _decodeUnicode(args['andw'] or args['andwords'] or '') local orString = _decodeUnicode(args['orw'] or args['orwords'] or '') local case = args['case'] or args['case_sensistive'] or false local yesVal = args['yes'] or '' or nil local noVal = args['no'] or '' local explain = args['explain'] or false local yeslist = '' local sresult = ''

explain = yesno(explain, false) if explain then if true then -- if _xpIsPreview then _xpInit _xpTitle explain = true else explain = false end end

if args['yes'] == nil then -- parameter yes = absent, will return default yeslist -- distinct from: yes=(some value, including blank '') yesVal = nil end

yeslist = p._main(s, w, andString, orString, case, yesVal, noVal, explain)

if yeslist == '' then -- FALSE sresult = noVal or '' elseif yesVal == nil then -- TRUE sresult = yeslist else -- TRUE sresult = yesVal end if explain then _xpReturnValue(yeslist, yesVal, noVal) return sresult .. _xpFormat else return sresult end end

function p.testP return yesno(_xpIsPreview ) end

function p.reportHi _xpInit report.Hi end

return p