Modulo:loaddata-tbllingvoj

 MODULO
Memtesto disponeblas sur la paĝo Vikivortaro:Testo-tbllingvoj.
Ĉi tiu modulo estas multfoje bindita.
Se vi konas la eblajn sekvojn, tiam vi povas zorgeme ekredakti.
Se vi ne kuraĝas redakti tiam vi povas proponi la deziratan ŝanĝon en la diskutejo.

--[===[

"eo.wiktionary.org/wiki/Modulo:loaddata-tbllingvoj" <!--2024-Oct-09-->
"id.wiktionary.org/wiki/Modul:loaddata-tblbahasa"

Purpose: translate (the transcludable part of) wikitext of a template
         (name is hardcoded to "tblbahasa") to 6 LUA tables that can be
         used repeatedly via the infamous "mw.loadData" command

Utilo: traduki na (la transkluzivigebla parto de) vikiteksto de sxablono
       (nomo fiksita al "tbllingvoj") al 6 LUA-tabeloj kiuj povas esti
       uzataj ripete per la famacxa ordono "mw.loadData"

Manfaat: menerjemahkan (bagian yang bisa ditransklusikan) wikiteks templat
         (namanya tetap "tblbahasa") menjadi 6 tabel LUA yang bisa digunakan
         beberapa kali melalui perintah "mw.loadData" yang terkenal buruk

Syfte: oeversaetta (den transkluderingsbara delen av) wikitext fraan en mall
       (namn fastslaget till "tbllingvoj") till 6 LUA-tabeller som kan
       anvaendas upprepade gaanger medelst det oekaenda
       kommandot "mw.loadData"

Used by templates / Uzata far sxablonoj /
Digunakan oleh templat / Anvaent av mallar:
* "Vikivortaro:Testo-tbllingvoj" (NOT a template) serving as
  a low-level test and diagnostic tool for the system
* "bigtable-tbllingvoj" / "bigtable-tblbahasa" serving additionally as
  a high-level test and diagnostic tool for the system
* "bigtable-tbldialektoj" / "bigtable-tbldialek" serving additionally as
  a high-level test and diagnostic tool for the system
* "lawc"
* "lili"
* "tagg"
* "kat"

Required submodules / Bezonataj submoduloj / Submodul yang diperlukan:
* none

Required templates:
* "tblbahasa" or "tbllingvoj"

Incoming: * nothing (imported via "mw.loadData", no ordinary caller)

Returned: * LUA table containing up to 17 items of status data, and
            up to 6 inner LUA tables of main data:
            * status values (some can happen to be empty):
              * [2] (integer) status code (see below)
              * [3] (integer) full bloat of raw incoming block
              * [4] (integer) remaining bloat of table content area,
                    after removing both outer areas and outer EOL:s but
                    still keeping all possible excessive inner whitespace
              * [6] (integer) octet position of error (relative to table
                    content area, excessive inner whitespace does count,
                    two outer areas and outer EOL:s don't)
              * [8] (integer) number of sections found, or index of section
                    where an error occurred (empty lines do NOT count)
              * [9] (integer) number of lines found, or index of line
                    where an error occurred (empty lines do NOT count)
              * [10] minimal length of line before space reduction
              * [11] maximal length of line before space reduction
              * [12] minimal length of line after space reduction
              * [13] maximal length of line after space reduction
              * [14] minimal number of elements in a valid line
              * [15] maximal number of elements in a valid line
              * [16] (string, AA) error string on some errors,
                                  otherwise empty string
                     * always hardcoded settings
              * [17] (string, BB) error string on some errors,
                                  otherwise empty string
                     * if applicable raw complete or truncated faulty line
              * [18] (string, CC) error string on some errors,
                                  otherwise empty string
                     * if applicable report with length of the line before
                       and after space reduction, and earliest and last char
              * [21] (string, DD) error string on some errors,
                                  otherwise empty string
                     * on E07 number of bad char:s and code of earliest
                       offending char
                     * on E42 counted number of elements
                     * if applicable early participant in sorting crime
                     * if applicable duping string
                     * if applicable offending content that failed
                       extra validation
              * [22] (string, EE) error string on some errors,
                                  otherwise empty string
                     * if applicable latter participant in sorting crime
                     * if applicable conflicting keys of the duping string
            * main data (selectable, type "nil" if not active):
            * ["T75"] (table) enumeration table with key/index ZERO-based
            * ["T76"] (table) y-index -> element in left-most column
            * ["T77"] (table) table y-index -> complete line
            * ["T78"] (table) table y-index -> subtable
            * ["T80"] (table) left-most column -> y-index
            * ["T81-" n] (table) other column of interest -> y-index
              * ["T81-1"] (table) column ONE -> y-index

Note that this module is NOT generic and CANNOT be made generic due to the
principle that data received by "mw.loadData" must be static and thus it is
not possible to submit parameters (namely name of the template to be seized)
to this module.

Status codes:
* #E00: OK
* #E02: template not found (outside of scope of the specification)
* #E03 ... #E79: range reserved by the
                 specification (see "spec-picker-en.txt")

Structure:
* the useful content area is delimited:
  * begins with <pre> LF 60 x cross "#" LF
  * ends with LF 60 x cross "#" LF </pre>
* in the useful content area:
  * empty lines are ignored
  * there is only ONE type of lines:
    * language info lines containing:
      * langcode inside "[[" & "]]"
      * 11 comma-separated values (CSV)

]===]

------------------------------------------------------------------------

---- CONSTANTS [O] ----

------------------------------------------------------------------------

-- uncommentable constant strings source ie target template

local constrtarget = string.char(0xC5,0x9C) .. "ablono:tbllingvoj"  -- EO -- "SXablono"
-- local constrtarget = "Templat:tblbahasa"                            -- ID

-- constant table -- ban list -- add obviously invalid access codes (2-letter or 3-letter) only

-- length of the list is NOT stored anywhere, the processing stops
-- when type "nil" is encountered, used by "lfivalidatelnkoadv" only

-- controversial codes (sh sr hr), (zh cmn)
-- "en.wiktionary.org/wiki/Wiktionary:Language_treatment" excluded languages
-- "en.wikipedia.org/wiki/Spurious_languages"
-- "iso639-3.sil.org/code/art" only valid in ISO 639-2
-- "iso639-3.sil.org/code/gem" only valid in ISO 639-2 and 639-5, "collective"
-- "iso639-3.sil.org/code/zxx" "No linguistic content"

local contabisbanned = {}
contabisbanned = {'by','dc','ll','jp','art','deu','eng','epo','fra','gem','ger','ido','lat','por','rus','spa','swe','tup','zxx'} -- 1...19

-- semi-hardcoded parameters (see specification "spec-picker-en.txt")

local contabqc = {}
contabqc['Q02'] = true -- use of stripping
contabqc['Q03'] = 1000
contabqc['Q04'] = 1000000 -- 1M
contabqc['Q05'] = '<pre>\n############################################################\n' -- <pre> LF 60 x cross "#" LF
contabqc['Q06'] = '\n############################################################\n</pre>' -- LF 60 x cross "#" LF </pre>
contabqc['Q08'] = 1000
contabqc['Q09'] = 1000000 -- 1M
contabqc['Q13'] = 10 -- minimal length of line
contabqc['Q14'] = 1000 -- maximal length of line
contabqc['Q15'] = 2
contabqc['Q16'] = 10000 -- 10K number of lines
contabqc['Q17'] = 2 -- name in EO oblig and D2, Q-item oblig but only D1
contabqc['Q18'] = 11 -- number of elements in a line, y-index marker does NOT count
contabqc['Q20'] = 10 -- ASCII code of LF
contabqc['Q21'] = 44 -- CSV
contabqc['Q22'] = '=' -- not found
contabqc['Q25'] = 2
contabqc['Q26'] = 12 -- length of index markers
contabqc['Q28'] = false -- use section headers
contabqc['Q36'] = 'Y4' -- y-index markers Y4 obligatory and sorted
contabqc['Q37'] = 'S1' -- allow exception for top and bottom line
contabqc['Q38'] = 'T'
contabqc['Q39'] = 'langcode'
contabqc['Q40'] = '00' -- "zh-min-nan", digit in middle position
contabqc['Q55'] = 'D2' -- dupe prevention level in left-most column
contabqc['Q56'] = true -- reverse access in left-most column
contabqc['Q60'] = 1 -- number of other column of interest
contabqc['Q61'] = 'D1' -- dupe prevention level in other column of interest
contabqc['Q62'] = true -- reverse access in other column of interest
-- contabqc['Q66'] = {} -- extra validation of values in the table, triples
contabqc['Q67'] = {[0]=2,[1]='rllr',[2]='',[3]=9,[4]='rllr',[5]=''}
        -- pluprocessing, triples, affects YES T76 T78 T80 T81, NOT T75 T77
contabqc['Q75'] = true -- deliver enumeration table
contabqc['Q76'] = true -- deliver table y-index -> left-most column
contabqc['Q77'] = true -- deliver table y-index -> complete line DEPRECATED
contabqc['Q78'] = true -- deliver table y-index -> subtable
contabqc['Q80'] = true -- deliver table left-most column -> y-index
contabqc['Q81'] = true -- deliver table other column of interest -> y-index

------------------------------------------------------------------------

---- SPECIAL STUFF OUTSIDE MAIN [B] ----

------------------------------------------------------------------------

  -- SPECIAL VAR:S

local qstrtca = '' -- table content area for "lfigetlinespacered" and main

------------------------------------------------------------------------

---- MATH FUNCTIONS [E] ----

------------------------------------------------------------------------

local function mathisintrange (numzjinput, numzjmin, numzjmax)
  local booisclean = false -- preASSume guilt
  if (type(numzjinput)=='number') then -- no non-numbers, thanks
    if (numzjinput==math.floor(numzjinput)) then -- no transcendental
      booisclean = ((numzjinput>=numzjmin) and (numzjinput<=numzjmax)) -- rang
    end--if
  end--if
  return booisclean
end--function mathisintrange

local function mathdiv (xdividens, xdivisero)
  local resultdiv = 0 -- DIV operator lacks in LUA :-(
  resultdiv = math.floor (xdividens / xdivisero)
  return resultdiv
end--function mathdiv

local function mathmod (xdividendo, xdivisoro)
  local resultmod = 0 -- MOD operator is "%" and bitwise AND operator lack too
  resultmod = xdividendo % xdivisoro
  return resultmod
end--function mathmod

------------------------------------------------------------------------

---- NUMBER CONVERSION FUNCTIONS [N] ----

------------------------------------------------------------------------

-- Local function LFNUMTO2DIGIT

-- Convert integer 0...99 to decimal ASCII string always 2 digits "00"..."99".

-- Depends on functions :
-- [E] mathisintrange mathdiv mathmod

local function lfnumto2digit (numzerotoninetynine)
  local strtwodig = '??' -- always 2 digits
  if (mathisintrange(numzerotoninetynine,0,99)) then
    strtwodig = tostring(mathdiv(numzerotoninetynine,10)) .. tostring(mathmod(numzerotoninetynine,10))
  end--if
  return strtwodig
end--function lfnumto2digit

------------------------------------------------------------------------

---- LOW LEVEL STRING FUNCTIONS [G] ----

------------------------------------------------------------------------

local function lfgtestnum (numkaad)
  local boodigit = false
  boodigit = ((numkaad>=48) and (numkaad<=57))
  return boodigit
end--function lfgtestnum

local function lfgtestuc (numkode)
  local booupperc = false
  booupperc = ((numkode>=65) and (numkode<=90))
  return booupperc
end--function lfgtestuc

local function lfgtestlc (numcode)
  local boolowerc = false
  boolowerc = ((numcode>=97) and (numcode<=122))
  return boolowerc
end--function lfgtestlc

------------------------------------------------------------------------

-- Local function LFGTRIMWHITES

-- Trim leading and trailing whitespace in a single line
-- in a customizable manner.

-- Input  : * strriskofwhite -- string, empty or only white tolerable, but
--                              type other than "string" NOT permitted
--          * bootakelf
--          * bootakespc
--          * boopronelfatend -- provide exactly ONE LF at end on
--                               exit, even do add if missing, only
--                               together with "bootakelf"

local function lfgtrimwhites (strriskofwhite, bootakelf, bootakespc, boopronelfatend)
  local str52rezulto = ''
  local str52pluslf = ''
  local numpos52beg = 1 -- ONE-based
  local numpos52end = 0 -- ONE-based
  local num52char = 0
  if (bootakelf and boopronelfatend) then
    str52pluslf = string.char(10)
  end--if
  numpos52end = string.len(strriskofwhite)
  while true do -- analyze begin
    if (numpos52beg>numpos52end) then
      break -- consists of only whitespace or empty
    end--if
    num52char = string.byte(strriskofwhite,numpos52beg,numpos52beg)
    if (not (((num52char==10) and bootakelf) or ((num52char==32) and bootakespc))) then
      break
    end--if
    numpos52beg = numpos52beg + 1
  end--while -- analyze begin
  while true do -- analyze trailer
    if (numpos52end<numpos52beg) then
      break -- consists of only whitespace or empty
    end--if
    num52char = string.byte(strriskofwhite,numpos52end,numpos52end)
    if (not (((num52char==10) and bootakelf) or ((num52char==32) and bootakespc))) then
      break
    end--if
    numpos52end = numpos52end - 1
  end--while -- analyze trailer
  if (numpos52end>=numpos52beg) then
    str52rezulto = string.sub(strriskofwhite,numpos52beg,numpos52end) .. str52pluslf
  else
    str52rezulto = str52pluslf -- empty or only LF
  end--if
  return str52rezulto
end--function lfgtrimwhites

------------------------------------------------------------------------

-- Local function LFGFINDONE

-- Return found position only if exactly ONE hit, otherwise
-- (on either several hits or no hit) return -1.

local function lfgfindone (str92where, str92what)
  local varaa = 0
  local varbb = 0
  local num92posi = -1 -- preASSume NOT found
  varaa = string.find (str92where, str92what, 1, true) -- >=1 or "nil"
  if (varaa) then
    varbb = string.find (str92where, str92what, (varaa+1), true)
  end--if
  if (varaa and (not varbb)) then
    num92posi = varaa - 1 -- make ZERO-based
  end--if
  return num92posi
end--function lfgfindone

------------------------------------------------------------------------

---- HIGH LEVEL STRING FUNCTIONS [I] ----

------------------------------------------------------------------------

-- Local function LFIRLLRSTRIP

-- Strip "rl " ... " lr" if it is present and string
-- length is at least 8 octet:s.

local function lfirllrstrip (strorlinut)
  if (string.len(strorlinut)>=8) then -- at least 2 octet:s length after strip
    if ((string.sub(strorlinut,1,3)=='rl ') and (string.sub(strorlinut,-3,-1)==' lr')) then
      strorlinut = string.sub(strorlinut,4,-4) -- strip off 3+3 char:s
    end--if
  end--if
  return strorlinut
end--function lfirllrstrip

------------------------------------------------------------------------

-- Local function LFIVALIDATELNKOADV

-- Advanced test whether a string (intended to be a langcode) is valid
-- containing only 2 or 3 lowercase letters, or 2...10 char:s and with some
-- dashes, or maybe a digit in middle position or maybe instead equals to "-"
-- or "??" and maybe additionally is not included on the ban list.

-- Input  : * strqooq -- string (empty is useless and returns
--                       "true" ie "bad" but cannot cause any major harm)
--          * booyesdsh -- "true" to allow special code dash "-"
--          * booyesqst -- "true" to allow special code doublequest "??"
--          * booloonkg -- "true" to allow long codes such as "zh-min-nan"
--          * boodigit -- "true" to allow digit in middle position
--          * boonoban -- (inverted) "true" to skip test against ban table

-- Output : * booisvaladv -- true if string is valid

-- Depends on functions :
-- [G] lfgtestnum lfgtestlc

-- Depends on constants :
-- * table "contabisbanned"

-- Incoming empty string is safe but type "nil" is NOT.

-- Digit is tolerable only ("and" applies):
-- * if boodigit is "true"
-- * if length is 3 char:s
-- * in middle position

-- Dashes are tolerable (except in special code "-") only ("and" applies):
-- * if length is at least 4 char:s (if this is permitted at all)
-- * in inner positions
-- * NOT adjacent
-- * maximally TWO totally
-- There may be maximally 3 adjacent letters, this makes at least ONE dash
-- obligatory for length 4...7, and TWO dashes for length 8...10.

local function lfivalidatelnkoadv (strqooq, booyesdsh, booyesqst, booloonkg, boodigit, boonoban)

  local varomongkosong = 0 -- for check against the ban list
  local numchiiar = 0
  local numukurran = 0
  local numindeex = 0 -- ZERO-based -- two loops
  local numadjlet = 0 -- number of adjacent letters (max 3)
  local numadjdsh = 0 -- number of adjacent dashes (max 1)
  local numtotdsh = 0 -- total number of dashes (max 2)
  local booislclc = false
  local booisdigi = false
  local booisdash = false
  local booisvaladv = true -- preASSume innocence -- later final verdict here

  while true do -- fake (outer) loop

    if (strqooq=='-') then
      booisvaladv = booyesdsh
      break -- to join mark -- good or bad
    end--if
    if (strqooq=='??') then
      booisvaladv = booyesqst
      break -- to join mark -- good or bad
    end--if
    numukurran = string.len (strqooq)
    if ((numukurran<2) or (numukurran>10)) then
      booisvaladv = false
      break -- to join mark -- evil
    end--if
    if (not booloonkg and (numukurran>3)) then
      booisvaladv = false
      break -- to join mark -- evil
    end--if

    numindeex = 0
    while true do -- inner genuine loop over char:s
      if (numindeex>=numukurran) then
        break -- done -- good
      end--if
      numchiiar = string.byte (strqooq,(numindeex+1),(numindeex+1))
      booisdash = (numchiiar==45)
      booisdigi = lfgtestnum(numchiiar)
      booislclc = lfgtestlc(numchiiar)
      if (not (booislclc or booisdigi or booisdash)) then
        booisvaladv = false
        break -- to join mark -- inherently bad char
      end--if
      if (booislclc) then
        numadjlet = numadjlet + 1
      else
        numadjlet = 0
      end--if
      if (booisdigi and ((numukurran~=3) or (numindeex~=1) or (not boodigit))) then
        booisvaladv = false
        break -- to join mark -- illegal digit
      end--if
      if (booisdash) then
        if ((numukurran<4) or (numindeex==0) or ((numindeex+1)==numukurran)) then
          booisvaladv = false
          break -- to join mark -- illegal dash
        end--if
        numadjdsh = numadjdsh + 1
        numtotdsh = numtotdsh + 1 -- total
      else
        numadjdsh = 0 -- do NOT zeroize the total !!!
      end--if
      if ((numadjlet>3) or (numadjdsh>1) or (numtotdsh>2)) then
        booisvaladv = false
        break -- to join mark -- evil
      end--if
      numindeex = numindeex + 1 -- ZERO-based
    end--while -- inner genuine loop over char:s

    if (not boonoban) then -- if "yesban" then
      numindeex = 0
      while true do -- lower inner genuine loop
        varomongkosong = contabisbanned[numindeex+1] -- number of elem unknown
        if (type(varomongkosong)~='string') then
          break -- abort inner loop (then outer fake loop) due to end of table
        end--if
        numukurran = string.len (varomongkosong)
        if ((numukurran<2) or (numukurran>3)) then
          break -- abort inner loop (then outer fake loop) due to faulty table
        end--if
        if (strqooq==varomongkosong) then
          booisvaladv = false
          break -- abort inner loop (then outer fake loop) due to violation
        end--if
        numindeex = numindeex + 1 -- ZERO-based
      end--while -- lower inner genuine loop
    end--if (not boonoban) then

    break -- finally to join mark
  end--while -- fake loop -- join mark

  return booisvaladv

end--function lfivalidatelnkoadv

------------------------------------------------------------------------

-- Local function LFIVALIOADV2STR

-- Optional extra validation type "langcode" of markers.

-- Depends on functions :
-- [I] lfivalidatelnkoadv

-- This sub "lfivalioadv2str" as well as the preceding
-- "lfivalidatelnkoadv", and also "contabisbanned" can be
-- omitted if none of Q30 Q39 Q49 contain the value "langcode".

local function lfivalioadv2str (str83mark, str83twodigits)
  local boolng5option = false
  local boolng6option = false
  local boois83valid = false
  if (type(str83twodigits=='string')) then
    if (string.len(str83twodigits)==2) then
      boolng5option = (string.sub(str83twodigits,1,1)=='1') -- "zh-min-nan"
      boolng6option = (string.sub(str83twodigits,2,2)=='1') -- digit in middle
    end--if
  end--if
  boois83valid = lfivalidatelnkoadv(str83mark,false,false,boolng5option,boolng6option,false)
  return boois83valid
end--function lfivalioadv2str

------------------------------------------------------------------------

-- Local function LFIVALIDATEMARKER

-- Validate marker by content (as stipulated by specification), whereas
-- the length (restricted by Q25,Q26) was already checked earlier.

-- Depends on functions :
-- [G] lfgtestnum lfgtestuc lfgtestlc

local function lfivalidatemarker (strmarkerlensudahok)

  local numtypeofmarker = 0 -- preASSume guilt
  local numlen92marker = ''
  local numin92dex = 0
  local numch92ar = 0
  local booisboulderdash = false
  local booisnumber = false
  local booisletter = false
  local booisnumorlet = false

  numlen92marker = string.len(strmarkerlensudahok)
  if ((numlen92marker>=2) and (numlen92marker<=12)) then -- stipulated by spec
    numtypeofmarker = 2 -- preASSume innocence now -- 2 digit 1 text 0 invalid
    while true do
      if (numin92dex==numlen92marker) then
        break -- done good or bad
      end--if
      numch92ar = string.byte(strmarkerlensudahok,(numin92dex+1),(numin92dex+1))
      booisboulderdash = (numch92ar==45)
      booisnumber = lfgtestnum(numch92ar)
      booisletter = lfgtestuc(numch92ar) or lfgtestlc(numch92ar)
      booisnumorlet = booisnumberr or booisletter
      if ((not booisnumorlet) and (not booisboulderdash)) then
        numtypeofmarker = 0 -- fully illegal char
        break
      end--if
      if (numin92dex==0) then
        if (booisboulderdash) then
          numtypeofmarker = 0 -- must begin with number or letter
          break
        end--if
        if (not booisnumber) then
          numtypeofmarker = 1 -- type must be text
        end--if
      else
        if ((not booisnumber) and (numtypeofmarker==2)) then
          numtypeofmarker = 0 -- too late dude
          break
        end--if
      end--if
      numin92dex = numin92dex + 1
      if ((numin92dex==numlen92marker) and booisboulderdash) then
        numtypeofmarker = 0 -- must end with number or letter
      end--if
    end--while
  end--if ((numlen92marker>=2) and (numlen92marker<=12)) then

  return numtypeofmarker

end--function lfivalidatemarker

------------------------------------------------------------------------

-- Local function LFIGETLINESPACERED

-- Read single line from buffer (upvalue) and perform space-reduction.

-- Input  : * num24pos -- if not BOF ie previous line processed, then this
--                        must point AFTER the EOL belonging to that line,
--                        also this MUST NOT be called on EOF

--          * num24len -- must be valid

-- Output : * num24status -- ZERO success, possible bad values E09 white line
--                           -- E10 multi empty -- E11 no EOL -- E12 length
--          * numnew24pos -- new position, unchanged on error
--          * num24rawsiz -- length before redu, also on E12, but max
--                           20 char:s too many
--          * numlen24baris -- length after redu, also on E12, but max
--                             20 char:s too many
--          * str24line -- line, empty on error

-- Depends on upvalues :
-- * qstrtca -- table content area

-- We expect text free of codes <32 except dedicated EOL code, and
-- exactly ONE EOL after last line (not missing, nor multiple).

-- We run up to 20 char:s over length limit Q14 (but never beyond EOF)
-- in order to give useful report on E12.

local function lfigetlinespacered (numq20eol, numq13min, numq14max, num24pos, num24len)

  local str24line = ''
  local num24status = 0 -- preASSume innocence
  local numnew24pos = 0
  local num24rawsiz = 0 -- before
  local numlen24baris = 0 -- after
  local numch24ar = 0
  local numbadchar = 0
  local booempty24line = false
  local boopostpone = false

  numnew24pos = num24pos -- preASSume not to move pos (kept on error)

  while true do -- find a line
    if (num24pos>=num24len) then
      num24status = 11 -- E11 do NOT call this on EOF, now give E11 too
      break
    end--if
    numch24ar = string.byte(qstrtca,(num24pos+1),(num24pos+1)) -- pick char
    num24pos = num24pos + 1 -- do NOT increment "num24rawsiz" here
    if (numch24ar==numq20eol) then
      if (booempty24line) then
        num24status = 10 -- E10 multi empty
        break
      else
        booempty24line = true -- this one was good, next one will cross limit
      end--if
    end--if (numch24ar==numq20eol) then
    if (numch24ar~=numq20eol) then
      break -- found line, still risk of leading spaces
    end--if
  end--while -- find a line

  if (numch24ar==32) then
    num24status = 9 -- E09 leading spaces or white line
  end--if

  if (num24status==0) then

    boopostpone = false

    while true do -- seize a line
      if ((numch24ar==numq20eol) or (num24rawsiz>=(numq14max+20))) then
        break -- captured an EOL, do NOT store it anywhere, or oveflow
      end--if
      if (num24pos>=num24len) then
        num24status = 11 -- E11 EOF instead EOL
        break
      end--if
      if (numch24ar==32) then
        boopostpone = true
      else
        if (boopostpone) then
          str24line = str24line .. string.char(32,numch24ar) -- build the line
          numlen24baris = numlen24baris + 2
          boopostpone = false -- !!!CRUCIAL!!!
        else
          str24line = str24line .. string.char(numch24ar) -- build the line
          numlen24baris = numlen24baris + 1
        end--if
      end--if
      numch24ar = string.byte(qstrtca,(num24pos+1),(num24pos+1)) -- pick
      num24pos = num24pos + 1
      num24rawsiz = num24rawsiz + 1 -- YES do increment "num24rawsiz" here
    end--while -- seize a line

    if (boopostpone) then
      num24status = 9 -- E09 trailing spaces
    end--if

  end--if (num24status==0) then

  if (num24status==0) then -- also if E12 is coming
    if ((numlen24baris<numq13min) or (num24rawsiz>numq14max)) then
      num24status = 12 -- E12 bad length of line
    end--if
  end--if

  if (num24status==0) then
    numnew24pos = num24pos -- advance on success
  else
    str24line = '' -- empty on error
    if (num24status~=12) then
      num24rawsiz = 0 -- no result on error other than E12
      numlen24baris = 0 -- no result on error other than E12
    end--if
  end--if

  return num24status, numnew24pos, num24rawsiz, numlen24baris, str24line

end--function lfigetlinespacered

------------------------------------------------------------------------

local function lfireportonline (str7line, numlenbef, numlenaft)
  local str7report = ''
  local numchv = 0
  local numchw = 0
  str7report = 'lenbef=' .. tostring(numlenbef) .. ' lenaft=' .. tostring(numlenaft)
  if (str7line~='') then
    numchv = string.byte(str7line,1,1)
    numchw = string.byte(str7line,-1,-1)
    str7report = str7report .. ' beg=' .. tostring(numchv) .. ' end=' .. tostring(numchw) -- decimal codes
  end--if
  return str7report
end--function lfireportonline

------------------------------------------------------------------------

-- Local function LFISTRIPOFFZYMARKER

-- Input  : * strfullline -- prechecked "[[" at begin & nowhere else & exactly
--                           one "]]" later, or "### " at begin & nowhere else

-- Output : * num68status -- ZERO success, possible bad only E20 E25 (E18 checked
--                           earlier) (see specification "spec-picker-en.txt")

local function lfistripoffzymarker (strfullline, numq25min, numq26max, boois68section)
  local varmyfinder = 0
  local strreducedline = ''
  local str68ymarker = ''
  local num68length = 0
  local num68status = 0
  if (boois68section) then
    num68length = string.len(strfullline)
    if ((num68length>=(numq25min+4)) and (num68length<=(numq26max+4))) then
      str68ymarker = string.sub (strfullline,5,-1)
    else
      num68status = 20 -- E20 section header AKA z-dimension
    end--if
  else
    varmyfinder = string.find(strfullline,']]',1,true)
    if (varmyfinder) then -- false outcome hopefully impossible
      num68length = varmyfinder - 2 -- OFF-BY-ONE
      if ((num68length>=numq25min) and (num68length<=numq26max)) then
        str68ymarker = string.sub (strfullline,3,(varmyfinder-1))
        strreducedline = string.sub (strfullline,(varmyfinder+2),-1)
      else
        num68status = 25 -- E25 y-dimension
      end--if
    end--if
  end--if
  return num68status, strreducedline, str68ymarker
end--function lfistripoffzymarker

------------------------------------------------------------------------

---- HIGH LEVEL FUNCTIONS [H] ----

------------------------------------------------------------------------

-- Local function LFHSPLITTOTABLE

-- Convert one string with elements separated by dedicated separator (CSV
-- SSV WSV) char into separate non-empty strings stored in a LUA table.

-- Input  : * strdinding -- must NOT contain codes below 32
--          * numseparator -- 44 for comma "," (CSV) or 59 for semicolon ";"
--                            or 124 for wall "|"
--          * strreservph -- reserved other placeholder prohibited as
--                           element (empty here if nothing to be prohibited)
--          * num37min -- minimal number of elements (>= 1)
--          * num37max -- maximal number of elements (>= 1)

-- Output : * num37status -- ZERO success, possible errors E40 E41 E42 E43
--          * numsubstrinx -- number of elements found, also valid on E42,
--                            max 5 too many, ZERO on other errors
--          * tabyeahsplit -- ZERO-based indexes, placeholder "-" possible,
--                            empty string impossible, empty table {} on error

-- Depends on functions :
-- [G] lfgtrimwhites

-- * empty elements must use dash "-" as a placeholder (applies to both
--   the incoming string and the returned table, but there is other
--   sub "lfhnildashtoempty")
-- * excessive spaces are not a problem, here they are trimmed
--   away from single elements (before and after element, not inner)
-- * we run up to 5 elements over limit in order to give useful report on E42

-- Errors:
-- * E40 discovered an empty element
-- * E41 discovered an element literally equal the reserved other placeholder
-- * E42 number of elements in the input string out of range
-- * E43 other error

-- Valid (assuming reserved other placeholder is "=", separator char
-- is wall "|", minimum is ONE, maximum 6):
-- * "-"
-- * "-|-|-|      -|-"
-- * "a"
-- * "     a" (leading and trailing spaces are legal)
-- * "jamak sama|nomina jamak bentuk sama"
-- * "jamak sama|nomina jamak bentuk sama|-"
-- * "jamak sama|nomina jamak bentuk sama|-|-|-|hej  " (6 elements)

-- Invalid (assuming reserved other placeholder is "=", separator char
-- is wall "|", minimum is ONE, maximum 6):
-- * ""
-- * "proton|  |antiproton" (inner empty element)
-- * "jamak sama|nomina jamak bentuk sama|" (empty element at end)
-- * "kato|   =    |lemniskato" (reserved other placeholder used as element)
-- * "jamak sama|nomina jamak bentuk sama|-|-|-|hej|nej" (too much
--   with 6 walls and 7 elements)

local function lfhsplittotable (strdinding,numseparator,strreservph,num37min,num37max)

  local vartmmp = 0
  local tabyeahsplit = {}
  local stronesub = ''

  local numinlenin = 0
  local numchrindexx = 1 -- ONE-based
  local numsubstrinx = 0
  local num37status = 0 -- preASSume innocence

  numinlenin = string.len(strdinding)
  if (numinlenin==0) then
    num37status = 43 -- E43 hopefully impossible
  end--if
  if ((numseparator~=44) and (numseparator~=59) and (numseparator~=124)) then
    num37status = 43 -- E43 hopefully impossible
  end--if

  while true do
    if (num37status~=0) then -- hopefully impossible
      break
    end--if
    vartmmp = string.find (strdinding, string.char(numseparator), numchrindexx, true) -- plain text search
    if (vartmmp==numchrindexx) then -- nothing to pick before separator char
      num37status = 40 -- E40 discovered an empty element
      break
    end--if
    if (vartmmp==numinlenin) then -- nothing to pick after separator char
      num37status = 40 -- E40 discovered an empty element
      break
    end--if
    if (vartmmp==nil) then
      vartmmp = numinlenin -- ONE-based last inclusive posi is end of string
    else
      vartmmp = vartmmp - 1 -- ONE-based last inclusive posi
    end--if
    stronesub = string.sub (strdinding,numchrindexx,vartmmp) -- at least ONE
    stronesub = lfgtrimwhites (stronesub,false,true,false) -- trim space only, risk of empty output
    if (stronesub=='') then
      num37status = 40 -- E40 empty element
      break
    end--if
    if (stronesub==strreservph) then
      num37status = 41 -- E41 reserved other placeholder
      break
    end--if
    tabyeahsplit[numsubstrinx] = stronesub -- store it
    numsubstrinx = numsubstrinx + 1 -- ZERO-based
    if (vartmmp==numinlenin) then
      break -- all stored, done
    end--if
    if (numsubstrinx>=(num37max+5)) then
      break -- abort now, max count elements exceeded and no end yet, soon E42
    end--if
    numchrindexx = vartmmp + 2 -- at least 1 octet left !!!
  end--while

  if ((num37status==0) and ((numsubstrinx<num37min) or (numsubstrinx>num37max))) then
    num37status = 42 -- E42 wrong number of elem -- do NOT override other err
  end--if

  if (num37status~=0) then
    if (num37status~=42) then
      numsubstrinx = 0 -- on E42 keep the count but still return empty table
    end--if
    tabyeahsplit = {} -- F**K
  end--if

  return num37status, numsubstrinx, tabyeahsplit

end--function lfhsplittotable

------------------------------------------------------------------------

-- Local function LFHREPORTQCT

-- Input  : * none, based on "contabqc"

-- Output : * strhugereport

-- Depends on functions :
-- [N] lfnumto2digit
-- [E] mathisintrange mathdiv mathmod

-- Depends on constants :
-- * contabqc

local function lfhreportqct ()
  local var94fragment = ''
  local strhugereport = ''
  local str94tabindex = ''
  local num94index = 0 -- per specification the range is "Q00" to "Q99"
  while true do
    str94tabindex = 'Q' .. lfnumto2digit(num94index)
    var94fragment = contabqc[str94tabindex] -- can be any type !!!
    if (var94fragment) then
      var94fragment = tostring (var94fragment)
      var94fragment = '"' .. str94tabindex .. '" = "' .. var94fragment .. '"'
      if (strhugereport~='') then
        var94fragment = ' -- ' .. var94fragment -- add separator
      end--if
      strhugereport = strhugereport .. var94fragment
    end--if
    if (num94index==99) then
      break
    end--if
    num94index = num94index + 1
  end--while
  if (strhugereport=='') then
    strhugereport = 'implementation-specific parameters NOT defined'
  end--if
  return strhugereport
end--function lfhreportqct

------------------------------------------------------------------------

-- Local function LFHPLUPROCESS

-- Pluprocess values in table elements if desired.

-- Input  : * tabpairscoltyp -- control table,
--                              tripples (column <-> type <-> param)
--          * tabmysubtable -- victim table

-- Depends on functions :
-- [I] lfirllrstrip

-- The only known type is "rllr".

local function lfhpluprocess (tabpairscoltyp, tabmysubtable)
  local strtypeofgg = ''
  local strparaofgg = ''
  local strmy87value = ''
  local numin87dex = 0
  local numkoluofgg = 0
  if ((type(tabpairscoltyp)=='table') and (type(tabmysubtable)=='table')) then
    while true do
      numkoluofgg = tabpairscoltyp[numin87dex*3]
      strtypeofgg = tabpairscoltyp[numin87dex*3+1]
      strparaofgg = tabpairscoltyp[numin87dex*3+2]
      if ((type(numkoluofgg)~='number') or (type(strtypeofgg)~='string') or (type(strparaofgg)~='string')) then
        break -- end of table reached
      end--if
      if (strtypeofgg=='rllr') then
        strmy87value = tabmysubtable[numkoluofgg]
        if (type(strmy87value)=='string') then -- skip boring non-strings
          tabmysubtable[numkoluofgg] = lfirllrstrip(strmy87value)
        end--if
      end--if (strtypeofgg=='rllr') then
      numin87dex = numin87dex + 1 -- step ONE here, MUL by 3 elsewhere
    end--while
  end--if
  return tabmysubtable
end--function lfhpluprocess

------------------------------------------------------------------------

---- VARIABLES [R] ----

------------------------------------------------------------------------

  -- general unknown type and "args" AKA "arx"

  local vartimp = 0

  -- tab for caller

  local tabtld = {} -- outer table

  local tab75enum   = {}
  local tab76yleft  = {}
  local tab77ycomp  = {}
  local tab78ysubt  = {}
  local tab80lefty  = {} -- also for dupe prevention
  local tab81othery = {} -- also for dupe prevention

  -- tab for dupe prevention

  local tabnodupezz = {} -- internal, only possible value is "true"
  local tabnodupey = {} -- internal, only possible value is "true"

  -- general tab

  local tabsplitu = {}

  -- str returned

  local str16aa = ''
  local str17bb = ''
  local str18cc = ''
  local str21dd = ''
  local str22ee = ''

  -- str sorting enforcement

  local strprevz = ''
  local strprevy = ''
  local strprevx = ''

  -- general str

  local strtymp = ''       -- temp
  local strliine = ''      -- complete line spaces reduced and still marker
  local strlinenm = ''     -- spaces reduced and no more y-index marker
  local strmarker = ''
  local strsectionpref = ''
  local strhardcoded = ''
  local strotherkol = ''   -- from "numothercol" in turn from Q60

  -- num returned

  local numerr = 0      -- status 0: OK -- 2: template not found ...
  local numbiglen = 0   -- full
  local numtrmlen = 0   -- after trim 2 areas + outer blank lines but not more
  local numtrmpos = 0
  local numtypmrk = 0   -- 2 digit | 1 text | 0 invalid
  local numcntsek = 0   -- processed sections
  local numcntlin = 0   -- processed lines
  local numminbef = 999999 -- from "numlengthlraw"
  local nummaxbef = 0
  local numminaft = 999999 -- from "numlengthtred"
  local nummaxaft = 0
  local numminele = 999999 -- from "numelementoj" only on valid lines
  local nummaxele = 0

  -- general num

  local numtomp = 0
  local numtump = 0
  local numothercol = 0 -- from Q60, number of other column of interest
  local numlengthlraw = 0
  local numlengthtred = 0
  local numelementoj = 0

  -- general boo

  local boohave17bb18cc = false
  local boolineissection = false
  local boolineistable = false
  local booislast = false -- for sorting and exit
  local boonotearnorlas = false
  local boote2mp = false

------------------------------------------------------------------------

---- MAIN [Z] ----

------------------------------------------------------------------------

  ---- SEIZE ----

  boote2mp = false
  if (type(constrtarget)=='string') then
    vartimp = mw.title.new(constrtarget):getContent()
    if (type(vartimp)=='string') then
      qstrtca = vartimp -- may be empty for now
      boote2mp = true
    end--if
  else
    constrtarget = '*** NO TARGET TITLE ***'
  end--if
  if (not boote2mp) then
    numerr = 2 -- #E02 outside of specification
  end--if

  --- MINIMAL CHECK ----

  -- the control table Q00...Q99 is supposed to be valid and
  -- consistent, these checks are NOT complete and NOT required

  if (numerr==0) then
    if (contabqc['Q80']) then
      boote2mp = false
      if ((contabqc['Q55']=='D1') or (contabqc['Q55']=='D2')) then
        boote2mp = (contabqc['Q56']==true)
      end--if
      if (not boote2mp) then
        numerr = 79 -- E79 other or internal error
      end--if
    end--if
  end--if
  if (numerr==0) then
    if (contabqc['Q81']) then
      boote2mp = false
      if (type(contabqc['Q60'])=='number') then
        if (contabqc['Q60']>=1) then
          if ((contabqc['Q61']=='D1') or (contabqc['Q61']=='D2')) then
            boote2mp = (contabqc['Q62']==true)
          end--if
        end--if
      end--if
      if (not boote2mp) then
        numerr = 79 -- E79 other or internal error
      end--if
    end--if
  end--if

  ---- CHECK LENGTH OF RAW AND TRIM AWAY TWO AREAS AND BLANK LINES ----

  -- trimming empty lines is required for reliable detection
  -- of EOF needed for both exit and S1

  if ((numerr==0) and contabqc['Q02']) then
    numbiglen = string.len(qstrtca)
    if ((numbiglen<contabqc['Q03']) or (numbiglen>contabqc['Q04'])) then
      numerr = 4 -- E04 bad raw BEFORE trim
    end--if
  end--if
  if ((numerr==0) and contabqc['Q02']) then
    numtomp = lfgfindone (qstrtca,contabqc['Q05']) -- beg
    if (numtomp>=0) then
      numtomp = numtomp + string.len(contabqc['Q05']) -- jump over it
    end--if
    numtump = lfgfindone (qstrtca,contabqc['Q06']) -- termi
    if ((numtomp>=0) and (numtump>=0) and (numtump>numtomp)) then
      qstrtca = string.sub(qstrtca,(numtomp+1),numtump) -- at least ONE
      qstrtca = lfgtrimwhites (qstrtca,true,false,true) -- now can be empty
    else
      qstrtca = ''
      numerr = 5 -- E05 strip fail
    end--if
  end--if

  ---- CHECK LENGTH OF TABLE CONTENT AREA AND FOR SOME BAD CONTENT ----

  -- we pass "numtrmlen" to one section below

  numtrmlen = string.len(qstrtca) -- can be ZERO

  if (numerr==0) then
    if ((numtrmlen<contabqc['Q08']) or (numtrmlen>contabqc['Q09'])) then
      numerr = 5 -- E05 bad AFTER trim
    end--if
  end--if
  if (numerr==0) then
    if (string.find(qstrtca,'(((',1,true) or string.find(qstrtca,')))',1,true) or string.find(qstrtca,'[[[',1,true) or string.find(qstrtca,']]]',1,true)) then
      numerr = 6 -- E06 triple
    end--if
  end--if

  ---- CHECK FOR SOME MORE BAD CONTENT ----

  if (numerr==0) then

    do -- scope
      local num29index = 1
      local num29d20d = 0
      local num29char = 0
      local num29store = 0
      local numcountevil = 0
      num29d20d = contabqc['Q20']
      while true do
        if (num29index>numtrmlen) then
          break
        end--if
        num29char = string.byte(qstrtca,num29index,num29index)
        if ((num29char<32) and (num29char~=num29d20d)) then
          if (numcountevil==0) then
            num29store = num29char -- store code of earliest violator
          end--if
          numcountevil = numcountevil + 1 -- do NOT abort
        end--if
        num29index = num29index + 1
      end--while
      if (numcountevil~=0) then
        numerr = 7 -- E07 bad char
        str21dd = 'discovered ' .. tostring(numcountevil) .. ' faulty chars, earliest offending code: #' .. tostring(num29store)
      end--if
    end--do scope

  end--if

  ---- PREBREW ----

  strhardcoded = lfhreportqct () -- for all errors

  boote2mp = false
  numothercol = contabqc['Q60'] -- optional
  if (type(numothercol)=='number') then
    if (mathisintrange(numothercol,1,10000)) then
      strotherkol = 'T81-' .. tostring(numothercol)
      boote2mp = true
    end--if
  end--if
  if (not boote2mp) then
    numothercol = 0
  end--if

  ---- PARSE THE BIG TEXT TABLE AND FILL SOME LUA TABLES ----

  if (numerr==0) then

    strprevz = ''
    strprevy = '' -- reset for every section
    strprevx = '' -- reset for every line

    numtrmpos = 0 -- ZERO-based position in the text
    numcntsek = 0 -- ZERO-based number of processed sections
    numcntlin = 0 -- ZERO-based number of processed lines, index in "tab75enum"

    while true do -- loop over all lines

      boohave17bb18cc = false -- reset on every iteration

      numerr, numtrmpos, numlengthlraw, numlengthtred, strliine = lfigetlinespacered (contabqc['Q20'],contabqc['Q13'],contabqc['Q14'],numtrmpos,numtrmlen)
      if (numerr~=0) then -- possible E09 E10 E11 E12
        if (numerr==12) then -- E12 bad length of line (restricted by Q13 Q14)
          str18cc = lfireportonline ('', numlengthlraw, numlengthtred) -- no text available
        end--if
        break
      end--if
      boohave17bb18cc = true -- now we have a line in "strliine"
      numminbef = math.min(numminbef,numlengthlraw)
      nummaxbef = math.max(nummaxbef,numlengthlraw)
      numminaft = math.min(numminaft,numlengthtred)
      nummaxaft = math.max(nummaxaft,numlengthtred)

      booislast = (numtrmpos>=numtrmlen) -- for sorting and exit
      boonotearnorlas = (numcntlin>=2) and (not booislast) -- for sorting only

      boolineissection = (string.sub(strliine,1,4)=='### ') and (lfgfindone(strliine,'###')~=-1)
      boolineistable = (string.sub(strliine,1,2)=='[[') and (lfgfindone(strliine,'[[')~=-1) and (lfgfindone(strliine,']]')~=-1)
      if ((not boolineissection) and (not boolineistable)) then
        numerr = 18 -- E18 line not identifiable as a valid type
        break -- both false error, both true IMPOSSIBLE
      end--if
      if (boolineissection and (not contabqc['Q28'])) then
        numerr = 19 -- E19 not enabled
        break
      end--if
      if (contabqc['Q36']~='Y4') then
        numerr = 79 -- E79 other or internal error -- unsupported
        break
      end--if

      numerr, strlinenm, strmarker = lfistripoffzymarker (strliine,contabqc['Q25'],contabqc['Q26'],boolineissection)
      if (numerr~=0) then -- possible E20 E25
        break
      end--if
      numtypmrk = lfivalidatemarker (strmarker) -- 2 digit 1 text 0 invalid

      if (boolineissection) then
        if (numtypmrk~=1) then -- must be text, digit inherently impossible
          numerr = 20 -- E20 structurally faulty section header
          break
        end--if
        if (contabqc['Q30']=='langcode') then
          if (not lfivalioadv2str(strmarker,contabqc['Q31'])) then
            str21dd = strmarker -- offending code
            numerr = 23 -- E23 failed extra vali z-dimension
            break
          end--if
        end--if
      else
        if (numtypmrk==0) then -- 0 means faulty
          numerr = 25 -- E25 structurally faulty y-index marker
          break
        end--if
        if (numtypmrk==2) then -- 2 means digit
          numerr = 26 -- E26 unsupp
          break
        end--if
        if (contabqc['Q39']=='langcode') then
          if (not lfivalioadv2str(strmarker,contabqc['Q40'])) then
            str21dd = strmarker -- offending code
            numerr = 29 -- E29 failed extra vali y-dimension
            break
          end--if
        end--if
      end--if (boolineissection) else

      if (boolineissection) then
        if (tabnodupezz[strmarker]) then -- dupe markers prohibited per spec
          str21dd = strmarker -- duping string section, no conf key
          numerr = 21 -- E21 dupe section
          break
        end--if
        tabnodupezz[strmarker] = true
        if (contabqc['Q29']=='S2') then -- "S1" is prohibited for sections
          if (strmarker<strprevz) then
            numerr = 22 -- E22 sorting violation section header
            str21dd = strprevz
            str22ee = strmarker
            break
          end--if
          strprevz = strmarker
        end--if
      else
        if (tabnodupey[strmarker]) then -- dupe markers prohibited per spec
          str21dd = strmarker -- duping string y-index, no conf key
          numerr = 27 -- E27 dupe y-index
          break
        end--if
        tabnodupey[strmarker] = true
        if ((strmarker<strprevy) and (boonotearnorlas or (contabqc['Q37']=='S2'))) then
          numerr = 28 -- E28 sorting violation y-index marker
          str21dd = strprevy
          str22ee = strmarker
          break
        end--if
        strprevy = strmarker
      end--if (boolineissection) else

      if (boolineissection) then
        strsectionpref = strmarker -- store prefix for later use
        tabnodupey = {} -- reset dupe check for every section
        strprevy = '' -- reset sorting check for every section
        numcntsek = numcntsek + 1 -- last step
      else
        if (contabqc['Q28']) then
          strmarker = strsectionpref .. '-' .. strmarker -- attach prefix
        end--if
      end--if (boolineissection) else

      if (boolineistable) then -- !!!FIXME!!! enforce sorting use "strprevx" and other sub
        numerr, numelementoj, tabsplitu = lfhsplittotable (strlinenm,contabqc['Q21'],contabqc['Q22'],contabqc['Q17'],contabqc['Q18'])
        if (numerr~=0) then -- possible E40 E41 E42 E43
          if (numerr==42) then -- E42 wrong number of elements
            str21dd = 'counted ' .. tostring (numelementoj) .. ' elements'
          end--if
          break
        end--if
        numminele = math.min(numminele,numelementoj) -- & only on
        nummaxele = math.max(nummaxele,numelementoj) -- & valid lines
        tabsplitu = lfhpluprocess (contabqc['Q67'],tabsplitu) -- YES T76 T78 T80 T81, NOT T75 T77
        if ((contabqc['Q55']=='D1') or (contabqc['Q55']=='D2')) then
          strtymp = tabsplitu[0] -- left-most maybe dupecheck, no sort
          if ((contabqc['Q55']=='D2') and (strtymp=='-')) then
            numerr = 51 -- E51 illegal placeholder left-most
            break
          end--if
          if (strtymp~='-') then -- exclude placeholder from dupecheck and store
            vartimp = tab80lefty[strtymp]
            if (vartimp) then
              str21dd = strtymp -- duping string
              str22ee = vartimp .. ' vs ' .. strmarker -- conflicting keys
              numerr = 50 -- E50 dupe left-most
              break
            end--if
            tab80lefty[strtymp] = strmarker -- store for dual purpose
          end--if (strtymp~='-') then
        end--if
        if (numothercol~=0) then -- "Q60" >=1, cannot be left-most again
          if ((contabqc['Q61']=='D1') or (contabqc['Q61']=='D2')) then
            strtymp = tabsplitu[numothercol] -- other maybe dupecheck, no sort
            if ((contabqc['Q61']=='D2') and (strtymp=='-')) then
              numerr = 56 -- E56 illegal placeholder other column
              break
            end--if
            if (strtymp~='-') then -- exclude placeholder from dupecheck and store
              vartimp = tab81othery[strtymp]
              if (vartimp) then
                str21dd = strtymp -- duping string
                str22ee = vartimp .. ' vs ' .. strmarker -- conflicting keys
                numerr = 55 -- E55 dupe other column
                break
              end--if
              tab81othery[strtymp] = strmarker -- store for dual purpose
            end--if (strtymp~='-') then
          end--if
        end--if -- (numothercol~=0) then -- "Q60"
        if (contabqc['Q75']) then
          tab75enum [numcntlin] = strmarker -- store marker at key/index ZERO-based
        end--if
        if (contabqc['Q76']) then
          tab76yleft [strmarker] = tabsplitu[0] -- store element in left-most
        end--if
        if (contabqc['Q77']) then
          tab77ycomp [strmarker] = strliine -- store line at key/index marker
        end--if
        if (contabqc['Q78']) then
          tab78ysubt [strmarker] = tabsplitu -- store subtable
        end--if
        numcntlin = numcntlin + 1 -- last step -- NOT reset on new section
      end--if (boolineistable) then

      if (booislast) then
        break
      end--if

    end--while -- loop over all lines

  end--if (numerr==0) then

  ---- DEAL WITH ERRORS ----

  if (numerr==0) then
    if ((numcntlin<contabqc['Q15']) or (numcntlin>contabqc['Q16'])) then
      numerr = 46 -- E46 bad number of lines
    end--if
  end--if

  if ((numerr==0) and (contabqc['Q80']) and (tab80lefty=={})) then
    numerr = 52 -- E52 no prey
  end--if

  if ((numerr==0) and (contabqc['Q81']) and (tab81othery=={})) then
    numerr = 57 -- E57 no prey
  end--if

  if (numerr~=0) then -- whole line including possible y-index marker
    str16aa = '"title" = "' .. constrtarget .. '" -- ' .. strhardcoded -- for all errors
    if (boohave17bb18cc) then
      str17bb = strliine -- additionally for most errors
      str18cc = lfireportonline (strliine, numlengthlraw, numlengthtred)
    end--if
  end--if

  ---- RETURN THE JUNK ----

  tabtld[2] = numerr
  tabtld[3] = numbiglen
  tabtld[4] = numtrmlen
  tabtld[6] = numtrmpos -- either error posi, or equal "numtrmlen" on success
  tabtld[8] = numcntsek
  tabtld[9] = numcntlin
  tabtld[10] = numminbef
  tabtld[11] = nummaxbef
  tabtld[12] = numminaft
  tabtld[13] = nummaxaft
  tabtld[14] = numminele
  tabtld[15] = nummaxele
  tabtld[16] = str16aa -- empty or whining
  tabtld[17] = str17bb -- empty or whining
  tabtld[18] = str18cc -- empty or whining
  tabtld[21] = str21dd -- empty or whining
  tabtld[22] = str22ee -- empty or whining

  if (numerr==0) then
    if (contabqc['Q75']) then
      tabtld['T75'] = tab75enum
    end--if
    if (contabqc['Q76']) then
      tabtld['T76'] = tab76yleft
    end--if
    if (contabqc['Q77']) then
      tabtld['T77'] = tab77ycomp -- NOT pluprocessed, with y-index marker
    end--if
    if (contabqc['Q78']) then
      tabtld['T78'] = tab78ysubt
    end--if
    if (contabqc['Q80']) then
      tabtld['T80'] = tab80lefty
    end--if
    if (contabqc['Q81']) then
      tabtld[strotherkol] = tab81othery -- custom key here
    end--if
  end--if

  return tabtld -- per LUA only allowed data type is "table"