Module:Str find word and Module:Str find word/sandbox: Difference between pages
Appearance
(Difference between pages)
Content deleted Content added
Undid revision 1147915801 by Lemondoge (talk): oh dear. I checked with testcases - don't know how this goofed |
No edit summary |
||
Line 1: | Line 1: | ||
-- 2023-04-17 STABLE wrt basics, quotes "" '' * with base sep; working on resltstring & report |
|||
-- todo: report options, more options |
|||
-- todo: options count, pattern, out-table, out-htmllist, keepinputordersource |
|||
require('strict') |
require('strict') |
||
local p |
local p = {} |
||
local |
local mArgs = require('Module:Arguments') |
||
local str |
local str = require('Module:String') |
||
local yesno = require('Module:Yesno') |
local yesno = require('Module:Yesno') |
||
local |
local tTools = require('Module:TableTools') |
||
local strDeEnCode = require('Module:DecodeEncode') |
|||
local iMaxWords = 16 |
|||
local iMaxWords = 12 -- alpha-status, Apr2023. when stable, can be higher |
|||
local warningIMaxWordsReached = nil |
|||
local |
local tArgs = {} |
||
local report -- |
local report = nil -- initinated when explain=T |
||
local function parseReportType( tArgs ) |
|||
-- Initialise the /report subpage. |
|||
local xpReportTF = false |
|||
-- only invoked when 'explain' asked |
|||
local xpReportType = yesno( tArgs.explain, tArgs.explain ) or false -- to be parsed beyond T/F |
|||
local function initReport() |
|||
-- in: nil, false: FALSE type=nil |
|||
-- in: true, preview: type=true TRUE (dflt: if prev) |
|||
-- in: doc, testcases: by page TRUE (persistent on those pages) |
|||
-- in: foo, other: FALSE |
|||
xpReportTF = false |
|||
if yesno( xpReportType, false ) == nil then -- nil, false |
|||
elseif xpReportType == 'testcases' then |
|||
xpReportType = 'testcases' |
|||
xpReportTF = true |
|||
elseif xpReportType == 'doc' then |
|||
xpReportType = 'doc' |
|||
xpReportTF = true |
|||
elseif xpReportType == true then |
|||
xpReportType = 'preview' |
|||
xpReportTF = true |
|||
else |
|||
xpReportTF = false -- unk word |
|||
end |
|||
tArgs.explain = xpReportTF |
|||
return xpReportType |
|||
end |
|||
local function initReport( tArgs ) |
|||
report = require('Module:Str find word/report') |
report = require('Module:Str find word/report') |
||
report.xpCheckExplain() -- dummy |
|||
end |
|||
local function isPreview( ) -- here or in report? |
|||
local ifPreview = require('Module:If preview') |
|||
-- return not ( ifPreview._warning( {'is_preview'} ) == '' ) |
|||
return ifPreview.main( true, false ) |
|||
end |
end |
||
-- Turn "A" into "A" etc. asap |
-- Turn "A" into "A" etc. asap |
||
-- and reduce multi-spaces (including nbsp etc.) into single space |
-- and reduce multi-spaces (including nbsp etc.) into single space |
||
local function |
local function sDecodeTrim( str ) |
||
if str == nil then return nil end |
|||
return mw.ustring.gsub(mw.text.decode(str), '%s+', ' ') |
|||
str = mw.ustring.gsub( strDeEnCode._decode( str ), '%s+' , ' ' ) |
|||
return mw.text.trim( str ) |
|||
end |
end |
||
-- %-Escape any word (character string) before feeding it into a string pattern function |
-- %-Escape any word (character string) before feeding it into a string pattern function |
||
-- will be %-escaped: "([%(%)%.%%%+%-%*%?%[%^%$%]])" = 12 characters ().%+-*?[^$] |
|||
-- all punctuation (%p) will be %-escaped |
|||
local function escape_word(word) |
local function escape_word( word ) |
||
return str._escapePattern(word) |
return str._escapePattern( word ) |
||
end |
|||
-- remove \' \" outer pair (& rm outer spaces); |
|||
-- any result (=the inner string) is trimmed by T/F option (case " abc "). |
|||
local function removeOuterQuotes( s, bTrimAfter ) |
|||
if s == nil then return nil end |
|||
if mw.ustring.match( s, "^%s*\'" ) ~= nil then |
|||
s = mw.ustring.gsub( s, "^%s*%\'(.*)%\'%s*$", "%1" ) |
|||
elseif mw.ustring.match( s, '^%s*\"' ) ~= '' then |
|||
s = mw.ustring.gsub( mw.text.trim( s ), '^%\"(.*)%\"$', '%1' ) |
|||
end |
|||
if bTrimAfter == true then |
|||
s = mw.text.trim( s ) |
|||
end |
|||
return s |
|||
end |
|||
-- separator-in |
|||
-- todo: check characters '" _ {}(); & accept?' |
|||
local function setSepIn( sSep, sDefaultSep ) |
|||
if sSep == nil then return sDecodeTrim( sDefaultSep ) end |
|||
-- remove all %w (alphanumeric) and %s (WS) |
|||
sSep = mw.ustring.gsub( sDecodeTrim( sSep ), '[%w%s]*', '' ) or '' |
|||
if sSep == '' then |
|||
return sDecodeTrim( sDefaultSep ) |
|||
else |
|||
return sSep |
|||
end |
|||
end |
|||
-- separator |
|||
local function setSepOut( sSep, sDefaultSep ) |
|||
sSep = sDecodeTrim( sSep ) or nil |
|||
if sSep == nil then return sDefaultSep end |
|||
sSep = removeOuterQuotes( sSep, false ) |
|||
if sSep == '' then |
|||
return sDefaultSep |
|||
else |
|||
return sSep |
|||
end |
|||
end |
|||
-- Check whether a single word is in a table (simple array of words) |
|||
-- returns hitword or nil; iPosition is helper to keep outlist ordered |
|||
local function findWordInTable( tSource, word ) |
|||
---local bHit = false |
|||
---local iPosition = -1 |
|||
for i, v in ipairs( tSource ) do |
|||
if v == word then |
|||
--- bHit = true --- del todo |
|||
---iPosition = i |
|||
return word |
|||
--- break |
|||
end |
|||
end |
|||
return nil |
|||
end |
end |
||
-- Reads and parses a word list and returns a table with words (simple array) |
-- Reads and parses a word list and returns a table with words (simple array) |
||
-- words list can be: source, andwords-to-check, orwords-to-check |
-- words list can be: source, andwords-to-check, orwords-to-check |
||
-- step 1: |
-- step 1: basic preparation of the csv wordstring |
||
-- step 2: |
-- step 2: when case-insensitive, turn string into lowercase |
||
-- step 3: read |
-- step 3: read (parse) quoted '..' |
||
-- step 4: read (parse) quoted ".." |
|||
-- step 4: when booleans=T, change boolean words into true/false (module:yesno rules) |
|||
-- step 5: read (parse) comma-separated words |
|||
-- all words returned are trimmed, TODO and all ws into single-plainspace? |
|||
-- step 6: merge quoted wordlists; keep in order |
|||
-- only T/F words are edited, other words remain, untouched |
|||
-- step 7: when booleans=T, change boolean words into true/false (module:yesno rules) |
|||
-- step 8: replace synonyms (by inout "|_nov=November, 11" input) |
|||
-- step 9: remove duplicates from wordtable (rm latest) |
|||
-- all words returned are trimmed |
|||
-- return the table (a straight array) |
-- return the table (a straight array) |
||
local function buildWordTable( |
local function buildWordTable( sWordlist ) |
||
local wordTable = {} |
local wordTable = {} |
||
local hitWord = '' |
local hitWord = '' |
||
local hitCount = |
local hitCount = -1 |
||
local _ |
|||
if sWordlist == '' then return wordTable end |
|||
local sPattern |
|||
local cQ1 = '_Q0027_' -- U+0027 = \' |
|||
local cQ2 = '_Q0022_' -- U+0022 = \" |
|||
local tQ1hits = {} -- Q1-hits, reused to restore order |
|||
local tQ2hits = {} -- Q2-hits, reused to restore order |
|||
local sMsg = '' -- xpmessage only |
|||
local xpHasQuotes = false |
|||
-- Step 1: |
-- Step 1: prepare sWordList |
||
sDecodeTrim( sWordlist ) |
|||
if yesno(tArgs.case, true) == false then |
|||
if sWordlist == '' or sWordlist == nil then return wordTable end |
|||
sWordlist = tArgs.sep .. sWordlist .. tArgs.sep |
|||
-- test. dev only: |
|||
xpHasQuotes = mw.ustring.match( sWordlist, '[\"\']' ) ~= '' -- unused |
|||
if xpHasQuotes then |
|||
--- report.xpMessage( 'xpHasQuotes [unused]: ' .. tostring( xpHasQuotes ) ) |
|||
end |
end |
||
-- Step 2: |
-- Step 2: case sensitive |
||
if yesno( tArgs.case, true ) == false then |
|||
-- then remove them from the string: |
|||
sWordlist = string.lower( sWordlist ) |
|||
-- replaced by single comma; idle & keeps word separation |
|||
end |
|||
--- if yesno(tArgs.literals, false) then |
|||
if false then |
|||
local _, sCount |
|||
_, sCount = mw.ustring.gsub(sWordlist, '"', '') |
|||
if sCount > 1 then |
|||
local litWord = '' |
|||
local i, j |
|||
-- Step 3: Q1 read quotes (single quotes '..') |
|||
while sCount > 1 do -- could do here: only when even? |
|||
sPattern = '%f[^' .. tArgs.sep_pattern .. ']%s*%b\'\'%s*%f[' .. tArgs.sep_pattern .. ']' |
|||
i = string.find(sWordlist, '%"', 1, false) |
|||
-- initial: |
|||
j = string.find(sWordlist, '%"', i+1, false) |
|||
hitWord = sDecodeTrim( mw.ustring.match( sWordlist, sPattern ) ) or '' |
|||
while hitWord ~= '' do |
|||
if #litWord > 0 then -- not an empty string or spaces only |
|||
--- now into function/ to check if both \' and \" are not mixed |
|||
xpLitWordCount = xpLitWordCount + 1 |
|||
--- hitWord = sDecodeTrim( mw.ustring.gsub( hitWord, "^%\'(.+)%\'$", "%1" ) ) -- remove outer Qs \" |
|||
table.insert(wordTable, litWord) |
|||
hitWord = removeOuterQuotes( hitWord, true ) |
|||
end |
|||
table.insert( tQ1hits, hitWord ) |
|||
-- remove from source, and do next gsub search: |
|||
sWordlist = mw.ustring.gsub( sWordlist, sPattern, cQ1, 1 ) -- removes current 1st hit; replace with code |
|||
.. escape_word(litWord) |
|||
-- next |
|||
.. '%s*%"', ',') |
|||
hitWord = sDecodeTrim( mw.ustring.match( sWordlist, sPattern ) ) or '' |
|||
end |
|||
end |
|||
end |
end |
||
--- report.xpMessage( 'sWL1: ' .. sWordlist ) |
|||
--- report.xpMessage( 'Qhits: ' .. table.concat( tQ1hits, '; ' ) ) |
|||
-- Step |
-- Step 4: Q2 read quotes (double quotes "..") |
||
sPattern = '%f[^' .. tArgs.sep_pattern .. ']%s*%b\"\"%s*%f[' .. tArgs.sep_pattern .. ']' |
|||
hitCount = 0 |
|||
-- initial search |
|||
sWordlist = tArgs.sep .. sWordlist .. tArgs.sep |
|||
hitWord = sDecodeTrim( mw.ustring.match( sWordlist, sPattern ) ) or '' |
|||
local eSep |
|||
while hitWord ~= '' do |
|||
eSep = escape_word(tArgs.sep) |
|||
--- hitWord = sDecodeTrim( mw.ustring.gsub( hitWord, '^%\"(.+)%\"$', '%1' ) ) -- remove outer Qs \" |
|||
local patstring = '%f[^' .. eSep .. '][^' .. eSep .. ']+%f[' .. eSep .. ']' |
|||
hitWord = removeOuterQuotes( hitWord, true ) |
|||
if yesno(tArgs.explain, false) then |
|||
table.insert( tQ2hits, hitWord ) |
|||
report.xpMessage('1.eSep: ' .. eSep) -- dev |
|||
sWordlist = mw.ustring.gsub( sWordlist, sPattern, cQ2, 1 ) -- removes current '1st' hit; replace with code |
|||
report.xpMessage('2.pattern: ' .. patstring) -- dev |
|||
-- next |
|||
hitWord = sDecodeTrim( mw.ustring.match( sWordlist, sPattern ) ) or '' |
|||
end |
end |
||
---report.xpMessage( 'sWL2:' .. sWordlist ) |
|||
while hitCount <= iMaxWords do |
|||
---report.xpMessage( 'Qhits: ' .. table.concat( tQ2hits, '; ' ) ) |
|||
hitCount = hitCount + 1 |
|||
-- Step 5: parse plain sep-delimited words |
|||
sPattern = '%f[^' .. tArgs.sep_pattern .. '][^' .. tArgs.sep_pattern .. ']+%f[' .. tArgs.sep_pattern .. ']' |
|||
hitCount = 0 |
|||
while hitCount < iMaxWords do |
|||
hitWord = sDecodeTrim( str._match( sWordlist, sPattern, 1, hitCount + 1, false, tArgs.sep ) ) or '' |
|||
hitWord = |
if hitWord == sDecodeTrim(tArgs.sep) then |
||
hitWord = mw.text.trim(hitWord) |
|||
if hitWord == tArgs.sep then |
|||
-- no more words found in the string |
-- no more words found in the string |
||
break |
break |
||
elseif hitWord ~= '' then |
elseif hitWord ~= '' then |
||
hitCount = hitCount + 1 |
|||
table.insert(wordTable, hitWord) |
|||
table.insert( wordTable, hitWord ) |
|||
else -- blank word, to skip (note: but blank quotes as in .., " ", ..are kept = blank dcell '') |
|||
hitCount = hitCount + 1 |
|||
end |
end |
||
end |
|||
if hitCount >= iMaxWords then report.xpMessage( 'ERR701 wordcount ' .. hitCount .. ' > maxwords' .. iMaxWords ) end |
|||
warningIMaxWordsReached = 'Max number of words (' .. tostring(iMaxWords) .. ') reached. Extra words are ignored.' |
|||
-- Step 6: merge quoted words & wordtable, keep order |
|||
.. ' (' .. mw.ustring.sub(mw.text.trim(sWordlist), 1, 90) .. ' ...). ' |
|||
for iQ, sQW in ipairs( tQ1hits ) do |
|||
end |
|||
for iW, sW in ipairs( wordTable ) do |
|||
if sW == cQ1 then |
|||
wordTable[iW] = sQW |
|||
break |
|||
end |
|||
end |
|||
end |
|||
for iQ, sQW in ipairs( tQ2hits ) do |
|||
for iW, sW in ipairs( wordTable ) do |
|||
if sW == cQ2 then |
|||
wordTable[iW] = sQW |
|||
break |
|||
end |
|||
end |
|||
end |
|||
-- Step |
-- Step 7: when read as booleans, converse words to true/false |
||
if tArgs.booleans then |
|||
-- todo: check parameter here not elsewhere |
|||
if tArgs.booleans then -- TODO if Yesno(tArgs.booleans) ... |
|||
local sBool |
local sBool |
||
for i, v in ipairs(wordTable) do |
for i, v in ipairs( wordTable ) do |
||
sBool = yesno(v) |
sBool = yesno( v ) |
||
if sBool ~= nil then |
if sBool ~= nil then |
||
wordTable[i] = tostring(sBool) |
wordTable[i] = tostring( sBool ) |
||
end |
end |
||
end |
end |
||
end |
end |
||
-- Step 8: replace synonyms |
|||
return wordTable |
|||
if #tArgs['synonymsTables'] >= 1 then |
|||
end |
|||
for aka1, tAkas in pairs ( tArgs['synonymsTables'] ) do |
|||
for iW, w in ipairs( wordTable ) do |
|||
if findWordInTable( tAkas, w ) then -- todo must be ... ~= nil ??? 26-3 |
|||
wordTable[iW] = aka1 |
|||
end |
|||
end |
|||
end |
|||
end |
|||
if true then |
|||
-- Check whether a single word is in a table (a simple array of words) |
|||
wordTable = tTools.removeDuplicates( wordTable ) |
|||
-- returns hitword or nil |
|||
else -- lol works but not needed, use ttools |
|||
local function findWordInTable(sourceWordTable, word) |
|||
-- Step 9: remove duplicates from list |
|||
local bHit = false |
|||
local iR, iK -- iR = reader, iK = killer |
|||
for i, v in ipairs(sourceWordTable) do |
|||
local hit = false |
|||
if v == word then |
|||
iR = 1 |
|||
while iR < #wordTable do |
|||
break |
|||
iK = #wordTable -- will be counting downwards |
|||
while iK > iR do |
|||
if wordTable[iK] == wordTable[iR] then |
|||
hit = true |
|||
sMsg = sMsg .. '=syn=' .. wordTable[iK] |
|||
table.remove( wordTable, iK ) |
|||
tTools.compressSparseArray( wordTable ) |
|||
end |
|||
iK = iK - 1 |
|||
end |
end |
||
tTools.compressSparseArray( wordTable ) |
|||
iR = iR + 1 |
|||
end |
end |
||
end |
|||
if bHit then |
|||
return word |
|||
return wordTable |
|||
else |
|||
return nil |
|||
end |
|||
end |
end |
||
-- AND-logic with |
-- AND-logic with ANDwords words: ALL words must be found |
||
-- returns {T/F, hittable} |
-- returns {T/F, hittable} |
||
-- T when *all* AND words are found |
-- T when *all* AND words are found |
||
-- hittable with all hit words |
-- hittable with all hit words |
||
-- note 1: when F, the hittable still contains the words that were found |
-- note 1: when F, the hittable still contains the words that were found |
||
-- note 2: empty AND-wordlist => True by logic (because: not falsified) |
-- note 2: empty AND-wordlist => True by logic (because: not falsified) |
||
local function checkANDwords( |
local function checkANDwords( tWorkf ) |
||
local bANDchk = true -- main conclusion |
|||
local result1 |
|||
local result1 = nil -- per word hit |
|||
local bAND |
|||
local tHits |
local tHits = {} -- hit table |
||
---local iPos = -1 -- helper info just to keep in order |
|||
if #tWorkf.ANDwords > 0 then |
|||
bAND = true |
|||
bANDchk = true |
|||
tHits = {} |
|||
for i, word in ipairs( tWorkf.ANDwords ) do |
|||
result1 = nil |
|||
result1 = findWordInTable( tWorkf.SOURCEwords, word ) or nil |
|||
if #andWordTable > 0 then |
|||
for i, word in ipairs(andWordTable) do |
|||
result1 = findWordInTable(sourceWordTable, word) or nil |
|||
if result1 == nil then |
if result1 == nil then |
||
bANDchk = false -- Falsified! |
|||
-- could break |
-- We could break now logically, but we continue to complete the hit table (feature) |
||
-- |
-- bAND remains false till & at end of loop |
||
else |
else |
||
table.insert(tHits, result1) |
table.insert( tHits, result1 ) |
||
end |
end |
||
end |
end |
||
else |
else |
||
bANDchk = true -- not falsified |
|||
end |
end |
||
tTools.compressSparseArray( tHits ) |
|||
return |
return bANDchk, tHits |
||
end |
end |
||
-- OR-logic with |
-- OR-logic with tORwords words: at least one word must be found |
||
-- returns {T/F, hittable} |
-- returns {T/F, hittable} |
||
-- True when at least one OR word is found |
-- True when at least one OR word is found |
||
-- hittable has all hit words |
-- hittable has all hit words |
||
-- note 1: empty OR-wordlist => True by logic (because: not falsified) |
-- note 1: empty OR-wordlist => True by logic (because: not falsified) |
||
-- note 2: while just one hitword is a True result, the hittable contains all words found |
-- note 2: while just one hitword is a True result, the hittable contains all words found |
||
local function checkORwords( |
local function checkORwords( tWork ) |
||
local result1 |
local result1 |
||
local |
local bORchk |
||
local tHits |
local tHits |
||
bORchk = false |
|||
tHits = {} |
tHits = {} |
||
result1 = nil |
result1 = nil |
||
if # |
if #tWork.ORwords > 0 then |
||
for i, word in ipairs( |
for i, word in ipairs( tWork.ORwords ) do |
||
result1 = findWordInTable( |
result1 = findWordInTable( tWork.SOURCEwords, word ) or nil |
||
if result1 == nil then |
if result1 == nil then |
||
-- this one is false; bOR unchanged; do next |
-- this one is false; bOR unchanged; do next |
||
else |
else |
||
bORchk = true -- Confirmed! |
|||
table.insert(tHits, result1) |
table.insert( tHits, result1 ) |
||
-- could break here logically, but complete the check |
-- could break here logically, but complete the check; bOR will not be set to False |
||
end |
end |
||
end |
end |
||
else |
else |
||
bORchk = true |
|||
end |
end |
||
tTools.compressSparseArray( tHits ) |
|||
return |
return bORchk, tHits |
||
end |
end |
||
-- Determine the requested return value (string) |
-- Determine the requested return value (a string) |
||
-- |
-- sRESULTstring is the _main return value (logically defined value) |
||
-- this function applies tArgs. |
-- this function applies tArgs.out_true / tArgs.out_false return value |
||
-- note: |
-- note: out_true='' implies: blank return value |
||
-- note: no parameter |
-- note: no parameter out_true= (that is, out_true=nil) implies: by default, return the sRESULTstring |
||
--- todo add pref, suff |
|||
local function yesnoReturnstring(tArgs, sYeslist) |
|||
local function yesnoReturnstring( tResults ) |
|||
if sYeslist == '' then -- False |
|||
if tResults.resultALL == false then -- result False |
|||
return tArgs.no or '' |
|||
return tArgs.out_false or '' |
|||
else -- True |
|||
else -- result True |
|||
if tArgs.yes == nil then |
|||
if tArgs.out_true == nil then |
|||
return sYeslist |
|||
return table.concat( tResults.tTRUE, tArgs.out_sep ) |
|||
else -- some |yes= value is entered, could be '' |
|||
else -- some |out-true= value is entered, could be '' |
|||
return tArgs.yes |
|||
return '_out-true' .. tArgs.out_true |
|||
end |
end |
||
end |
end |
||
end |
end |
||
local function |
local function tCombinedSourceorderedTRUEtables( tResult ) |
||
local tOut = {} |
|||
local ifPreview = require('Module:If preview') |
|||
if tResult.tANDhits == nil then |
|||
return not (ifPreview._warning( {'is_preview'} ) == '') |
|||
tOut = tResult.tORhits |
|||
elseif tResult.tORhits == nil then |
|||
tOut = tResult.tANDhits |
|||
else |
|||
tOut = tResult.tANDhits |
|||
for i, v in ipairs( tResult.tORhits ) do |
|||
table.insert( tOut, i, v ) |
|||
end |
|||
end |
|||
if tOut == nil then |
|||
report.xpMessage( 'ERR921 BUG tOut is nil??? - tCombinedSourceorderedTRUEtables' ) |
|||
end |
|||
return tOut -- unsorted; never nil |
|||
end |
end |
||
local function concatAndLists( s1, s2 ) |
|||
-- Explain options (=report info), interprets parameter explain= |
|||
local tLists = {} -- args in: both s1 and s2 to concat |
|||
-- returns true/false/'testcases' |
|||
table.insert( tLists, s1 ) |
|||
-- explain=true => show report in Preview |
|||
table.insert( tLists, s2 ) |
|||
-- explain=testcases => WHEN in ns: template: or user: AND subpage = '/testcases' THEN show permanently |
|||
return table.concat( tLists, tArgs.sep ) |
|||
local function checkExplain(tArgs) |
|||
return false -- never. 22Mar2023 checkExplain(newArgs) |
|||
end |
end |
||
-- ===== ===== ===== ===== ===== ===== ===== ===== ===== |
-- ===== ===== ===== ===== ===== ===== ===== ===== ===== |
||
-- PARSE arguments |
|||
-- _main function: check for presence of words in source string |
|||
local function parseArgs( origArgs ) |
|||
-- Checks and returns: |
|||
local tNewArgs = {} |
|||
-- when T: the string of all hitwords (default), or the |yes=... input |
|||
local tDefault = {} |
|||
-- when F: empty string '' (default), or the |no=... input |
|||
tDefault['sep'] = ',' |
|||
-- steps: |
|||
tDefault['case'] = false |
|||
-- 1. input word strings are prepared (parsed into an array of words) |
|||
tDefault['booleans'] = false |
|||
-- 2. words checks are made (applying AND-logic, OR-logic) |
|||
tDefault['out_sep'] = ', ' |
|||
-- 3. final conclusion drawn (T/F) |
|||
-- 4. optionally, the preview report is prepared (debug, feedback) |
|||
-- 5. based on T or F status, the return value (string) is established and returned |
|||
-- note 1: each return value (yes=.., no=..) can be '' (nulstring) |
|||
function p._main(tArgs) |
|||
local sourceWordTable = {} |
|||
local andWordTable = {} |
|||
local orWordTable = {} |
|||
local tANDhits |
|||
local tORhits |
|||
-- logical finding: |
|||
local bANDresult = false |
|||
local bORresult = false |
|||
local resultALL = false |
|||
local sYeslist = '' |
|||
tNewArgs.sep = setSepIn( origArgs['sep'], tDefault['sep'] ) |
|||
sourceWordTable = buildWordTable(tArgs, tArgs.source) |
|||
tNewArgs.sep_pattern = escape_word( tNewArgs.sep ) |
|||
andWordTable = buildWordTable(tArgs, tArgs.andString) |
|||
tNewArgs.out_sep = setSepOut( origArgs['out-sep'] or origArgs['sep'], tDefault['out_sep'] ) |
|||
orWordTable = buildWordTable(tArgs, tArgs.orString) |
|||
tNewArgs.case = yesno( origArgs['case'] or origArgs['casesensitive'] ) or tDefault['case'] |
|||
tNewArgs.booleans = yesno( origArgs['bool'] or origArgs['booleans'] ) or tDefault['booleans'] |
|||
tNewArgs.out_true = sDecodeTrim( origArgs.out_true ) or nil -- nil =default so return sRESULTstring; keep '' as legal input & return value |
|||
tNewArgs.out_false = sDecodeTrim( origArgs.out_false ) or '' |
|||
tNewArgs.prefix = sDecodeTrim( origArgs.prefix or origArgs.p ) or '' |
|||
tNewArgs.suffix = sDecodeTrim( origArgs.suffix or origArgs.s ) or '' |
|||
tNewArgs.out_format = 'default' -- todo: table, default, htmllisttype, flatlidt , first, |
|||
tNewArgs.explain = false -- TEST17Apr origArgs.explain |
|||
tNewArgs.explain_type = parseReportType( tNewArgs ) or nil |
|||
tNewArgs.test = origArgs.test |
|||
-- the wordlists: |
|||
if (#sourceWordTable == 0) or (#andWordTable + #orWordTable == 0) then |
|||
tNewArgs['source'] = origArgs['source'] or origArgs['s'] or '' |
|||
-- No words to check |
|||
tNewArgs['sANDlist'] = concatAndLists( |
|||
resultALL = false |
|||
origArgs['word'] or origArgs['w'] or nil, |
|||
if yesno(tArgs.explain, false) then |
|||
origArgs['andwords'] or origArgs['andw'] or nil ) |
|||
report.xpNoWords(tArgs, sourceWordTable, andWordTable, orWordTable) |
|||
tNewArgs['sORlist'] = origArgs['orwords'] or origArgs['orw'] or '' |
|||
tNewArgs['synonyms'] = {} |
|||
tNewArgs['synonymsTables'] = {} -- to be populated later |
|||
for k, v in pairs( origArgs ) do |
|||
if str._match( k, '^_%S', 1, 1, false, false ) then |
|||
local syn1 |
|||
syn1 = mw.ustring.gsub( k, '^_', '', 1 ) |
|||
table.insert( tNewArgs['synonyms'], syn1 ) |
|||
tNewArgs['synonyms'][syn1] = v |
|||
end |
end |
||
else |
|||
bANDresult, tANDhits = checkANDwords(sourceWordTable, andWordTable) |
|||
bORresult, tORhits = checkORwords(sourceWordTable, orWordTable) |
|||
resultALL = (bANDresult) and (bORresult) |
|||
end |
end |
||
if tNewArgs.explain == true then |
|||
sYeslist = '' |
|||
initReport( tNewArgs.explain ) |
|||
if resultALL then |
|||
report.xpMessage( 'EXPLAIN: ' .. origArgs.explain .. '=>' .. tNewArgs.explain_type or 'unk') |
|||
-- concat the sYeslist (= all hit words; from 2 tables) |
|||
report.xpReportSynonyms( tNewArgs ) |
|||
if bANDresult then |
|||
sYeslist = sYeslist .. table.concat(tANDhits, tArgs.sep) |
|||
end |
|||
if #tORhits > 0 then |
|||
if #tANDhits > 0 then |
|||
sYeslist = sYeslist .. tArgs.sep |
|||
end |
|||
sYeslist = sYeslist .. table.concat(tORhits, tArgs.sep) |
|||
end |
|||
end |
end |
||
if yesno(tArgs.explain, false) then |
|||
if tArgs.yes ~= nil then |
|||
if (tArgs.yes == '') and (tArgs.no == '') then |
|||
report.xpYesNoBothBlank() |
|||
end |
|||
end |
|||
if warningIMaxWordsReached ~= nil then |
|||
report.xpMessage(warningIMaxWordsReached) |
|||
end |
|||
report.xpBuildReport(tArgs, sourceWordTable, |
|||
bANDresult, andWordTable, tANDhits, |
|||
bORresult, orWordTable, tORhits, |
|||
sYeslist, xpLitWordCount) |
|||
end |
|||
return yesnoReturnstring(tArgs, sYeslist) |
|||
end |
|||
if false then |
|||
-- set wordt separator |
|||
for aka1, sAkalist in pairs ( tNewArgs['synonyms'] ) do |
|||
local function setSep(sSep) |
|||
report.xpMessage( 'SYNONYMS: ' .. aka1 .. '=' .. sAkalist ) |
|||
if sSep == nil then return defaultSep end |
|||
local msg = '' |
|||
-- todo what with {{!}} |
|||
local newSep = defaultSep |
|||
newSep = sSep |
|||
sSep = decodeUnicode(sSep) |
|||
if string.match(sSep, '[%s%w%d]') ~= nil then -- not ok |
|||
msg = 'Irregular characters in sep: ' .. sSep |
|||
newSep = defaultSep |
|||
end |
|||
newSep = string.sub(sSep, 1, 1) |
|||
if newSep == '' then --- ??? |
|||
newSep = defaultSep |
|||
end |
end |
||
return newSep |
|||
end |
end |
||
return tNewArgs |
|||
local function concatAndLists(s1, s2, newSep) |
|||
local tLists = {} -- working table: both s1 and s2 to concat |
|||
table.insert(tLists, s1) |
|||
table.insert(tLists, s2) |
|||
return table.concat(tLists, newSep) |
|||
end |
end |
||
-- ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== |
|||
local function parseArgs(origArgs) |
|||
-- _main function: check for presence of words in source string |
|||
local newArgs = {} |
|||
-- Checks and returns: |
|||
newArgs['sep'] = setSep(origArgs['sep']) -- do first, needed below |
|||
-- when T: the string of all hitwords ( default ), or the |yes=... input |
|||
newArgs['source'] = decodeUnicode(origArgs['s'] or origArgs['source'] or '') |
|||
-- when F: empty string '' ( default ), or the |no=... input |
|||
newArgs['andString'] = decodeUnicode(concatAndLists( |
|||
-- steps: |
|||
origArgs['w'] or origArgs['word'] or nil, |
|||
-- 1. input word strings are prepared ( parsed into an array of words ) |
|||
origArgs['andw'] or origArgs['andwords'] or nil, |
|||
-- 2. words checks are made ( applying AND-logic, OR-logic ) |
|||
newArgs.sSep) |
|||
-- 3. final conclusion drawn ( T/F ) |
|||
) |
|||
-- 4. optionally, the preview report is prepared ( debug, feedback ) |
|||
newArgs['orString'] = decodeUnicode(origArgs['orw'] or origArgs['orwords'] or '') |
|||
-- 5. based on T or F status, the return value ( string ) is established and returned |
|||
-- boolean options: catch both parameters, also handle nil & nonsense input values: |
|||
-- note 1: each return value ( yes=.., no=.. ) can be '' ( nullstring ) |
|||
newArgs['case'] = yesno(origArgs['case'] or origArgs['casesensitive'] or true, true) -- defaults to True |
|||
function p._main( origArgs ) |
|||
newArgs['booleans'] = yesno(origArgs['bool'] or origArgs['booleans'] or false, false) -- defaults to False |
|||
local tWork = {} |
|||
newArgs['literals'] = yesno(origArgs['literals'] or origArgs['lit'] or true, true) -- defaults to True |
|||
local tResults = {} |
|||
newArgs['yes'] = origArgs['yes'] or nil -- nil; default so return sYeslist; keep '' as legal input & return value |
|||
newArgs['no'] = origArgs['no'] or '' |
|||
newArgs['explain'] = false -- never. 22Mar2023 checkExplain(newArgs) |
|||
tArgs = parseArgs( origArgs ) |
|||
newArgs.explain = false -- never. 22Mar2023 checkExplain(newArgs) |
|||
-- make synonyms into tables |
|||
return newArgs |
|||
-- 'aka1' = target synonym (= the synonym that remains) |
|||
end |
|||
for aka1, sAkalist in pairs( tArgs['synonyms'] ) do |
|||
tArgs['synonymsTables'][aka1] = buildWordTable( tArgs['synonyms'][aka1] ) |
|||
end |
|||
-- build the worktables |
|||
function p.main(frame) |
|||
tWork['SOURCEwords'] = buildWordTable( tArgs.source ) |
|||
local origArgs = getArgs(frame) |
|||
tWork['ANDwords'] = buildWordTable( tArgs.sANDlist ) |
|||
local sReturn = '' |
|||
tWork['ORwords'] = buildWordTable( tArgs.sORlist ) |
|||
local tArgs = {} |
|||
-- apply logic & conclude |
|||
tArgs = parseArgs(origArgs) |
|||
tResults.resultALL = nil -- best be set explicitly |
|||
if yesno(tArgs.explain, false) then |
|||
if ( #tWork.SOURCEwords == 0 ) or ( #tWork.ANDwords + #tWork.ORwords == 0 ) then |
|||
initReport() |
|||
-- No words to check |
|||
report.xpListArguments(origArgs) |
|||
tResults.resultALL = false |
|||
if yesno( tArgs.explain, true ) then |
|||
report.xpMessage( 'ERR201 No words to check' ) |
|||
end |
|||
else |
|||
tResults['bAND'], tResults['tANDhits'] = checkANDwords( tWork ) |
|||
tResults['bOR'], tResults['tORhits'] = checkORwords( tWork ) |
|||
tResults.resultALL = ( tResults.bAND ) and ( tResults.bOR ) |
|||
end |
end |
||
tResults.sRESULTstring = 'notinit' |
|||
sReturn = p._main(tArgs) |
|||
if tResults.resultALL == true then |
|||
tResults.tTRUE = tCombinedSourceorderedTRUEtables( tResults ) or {} |
|||
if warningIMaxWordsReached ~=nil then |
|||
local preview = require('Module:If preview') |
|||
sReturn = sReturn .. preview._warning({warningIMaxWordsReached}) |
|||
end |
end |
||
tResults.sRESULTstring = yesnoReturnstring( tResults ) |
|||
local sReport = '' |
|||
if yesno(tArgs.explain, false) then |
|||
if tArgs.explain then |
|||
sReport = 'xp endfinal Report here L485' |
|||
else |
|||
--sReport = report.xpPresent( tArgs, tWork, tResults ) |
|||
return sReturn |
|||
end |
end |
||
local test = 'Tunk' |
|||
test = tArgs.test or '_unk' |
|||
if tArgs.explain then |
|||
test = tostring(tArgs.explain) |
|||
else |
|||
test = 'not' |
|||
end |
|||
return string.upper( tostring( tResults.resultALL ) ) .. tResults.sRESULTstring |
|||
end |
|||
function p.main( frame ) |
|||
local origArgs = mArgs.getArgs( frame ) |
|||
return p._main( origArgs ) |
|||
end |
end |
||