Modul:Benutzer/Neuneinhalbtel/Vorlage:Play mit Abstand
Modulbeschreibung
Modul:Benutzer/Neuneinhalbtel/Vorlage:Play mit Abstand/Doku
Funktionen
interpret
Interprets a code like in
{{#invoke:Vorlage:Tabellendaten Skriptessa|interpret|code=
.:table t1= "Data:Fraction tasks abacdeP80max.tab":. .:var s1= "<hr>":. .:var id= 3:.
.:t1(content; id==id++, group==1):. .:s1:. jo}}
_parseEvalStr
Parses an eval string with 0, 1 or 2 operators out of +, -, * or one ++.
Alters vars={} i.a., reads global variable ginstr i.a.
p._parseEvalStr( "id + 2 * 5", {id="3"} )
_parseQueryStr
Parses a query string and returns (tablename, outcolnames={}, where={}, postopts={}).
Alters vars={} i.a.
p._parseQueryStr( "tab1(cont; id==id++, group>=2; limit=3)", vars )
_dumpRecords
Returns a string-dump of a table with given columns
- param outputFormat= "foo%d..", optional
p._dumpRecords( {{1, "first"}, {2, "second"}} )
_lookup
Queries rows by col==condition pairs, with other options, returns {string=, table=} to chose from.
Table has {data= {{1,"a"}, {2,"b"}}, schema= {fields= {{name="id", type="number"}, {name="cont", type="string"}}}}
format like the data table param and the Commons Data tables.
original source: w:en:Module:Tabular data by w:en:User:Mxn (permalink, changed)
- param where= {{searchColumnName=, searchValue=, or searchPattern=,
- compare=function(a,b)..}, {same again}, ..} (may be {})
- param opts= {orderBy=, descending=, limit=, or occurrence=} (may be {} or optional)
- param outputFormat= "foo%s.." (optional, for _dumpRecords, untested)
Reads global variable ginstr i.a.
example p._lookup(mw.ext.data.get("Data:Fraction tasks abacdeP80max.tab"),
{searchColumnName="group", searchValue=1, compare=function(a,b) return a==b end},
{"id", "content"}, nil, {limit=3}).string
- Diese Dokumentation wurde mithilfe von Modul:LuaDokumentation erstellt und befindet sich im Quelltext.
- Liste der Unterseiten
---{{:{{FULLPAGENAME}}/Doku}}
local p = {}
-- ''Tabular data script interpreter'' for the template [[Vorlage:Tabellendaten Skriptessa]] <br>or<br>
-- <code><nowiki>{{#invoke:Vorlage:Tabellendaten Skriptessa|interpret|code=abc.:var s1=bla:.~.:s1:.}}</nowiki></code><br>
-- {{#invoke:Vorlage:Tabellendaten Skriptessa|interpret|code=abc.:var s1=bla:.~.:s1:.}}<br>
--- Interprets a code like in<br>
-- <code><nowiki> {{#invoke:Vorlage:Tabellendaten Skriptessa|interpret|code=
-- .:table t1= "Data:Fraction tasks abacdeP80max.tab":. .:var s1= "&lt;hr>":. .:var id= 3:.
-- .:t1(content; id==id++, group==1):. .:s1:. jo}} </nowiki></code>
function p.interpret(frame)
code= frame.args.code
code= mw.ustring.gsub(code, "ᙾ", "x") --separator ᙾ
code= mw.ustring.gsub(code, ":%.%s*%.:", ":..:") --dont trim whitespace around wikisyntax strip markers
code= mw.ustring.gsub(code, ":%.", ":.ᙾ")
code= mw.ustring.gsub(code, "%.:", "ᙾ.:")
code= mw.ustring.gsub(code, "ᙾᙾ", "ᙾ")
instrs= {}
for match in mw.ustring.gmatch(code, "[^ᙾ]+") do
instrs[#instrs+1]= match
end
--mw.addWarning( "code "..code )
local tables, tabledata, queries, vars= {}, {}, {}, {}; local out= "";
ginstr= "" --like-global (readable from closure funcs)
for k, instr in ipairs(instrs) do
ginstr= instr
--mw.addWarning( "instr"..mw.dumpObject( instr ) )
--//commands
ms= {} --matches
--.:table tab1="dd.tab":.
if (function()
ms= {mw.ustring.match(instr, "^%.:%s*table%s+(%w+)%s*=%s*(.+)%s*:%.")}
return #ms~=0
end)() then
key, val= unpack(ms)
val= mw.ustring.match(val, "^Data%:(.+)$") or val
tables[key]= val
tabledata[key]= mw.ext.data.get(val)
--.:var id==1+1:. .:var s=="-+-":. var is [%a_][%w_]*
elseif (function()
ms= {mw.ustring.match(instr, "^%.:%s*var%s+([%a_][%w_]*)%s*=%s*(.+)%s*:%.")}
return #ms~=0
end)() then
key, val= unpack(ms)
if (function()
ms= {mw.ustring.match(instr, '^"(.*)"$')};
return #ms~=0
end)() then
val= ms[1]
elseif (function()
ms= {mw.ustring.match(instr, "^'(.*)'$")};
return #ms~=0
end)() then
val= ms[1]
else
val= p._parseEvalStr(val, vars)
end
vars[key]= val
--mw.addWarning( "vars "..mw.dumpObject( vars ) )
--.:query t1q= t1(col;id==1,g==2;l:3):.
elseif (function()
ms= {mw.ustring.match(instr, "^%.:%s*query%s+(%w+)%s*=%s*(.+)%s*:%.")}
return #ms~=0
end)() then
key, val= unpack(ms)
tab, where, outcols, opts= p._parseQueryStr(val, vars)
local source
if tabledata[tab] then source= tabledata[tab]
elseif queries[tab] then source= queries[tab]
end
queries[key]= p._lookup( source, where, outcols, nil, opts ).table
--mw.addWarning( "queries "..mw.dumpObject( queries ) )
--.:--comment:.
elseif mw.ustring.match(instr, "^%.:%s*%-%-.*:%.$") then
i= 1
--.:!--logcomment:.
elseif (function()
ms= {mw.ustring.match(instr, "^%.:%s*!%-%-(.+)%s*:%.$")}
return #ms~=0
end)() then
if ms[1] then mw.addWarning( "--"..ms[1] ) end
--//expressions
--.:tab1(content; id==id++, group==group; l:1):. .:id:. .:!id+1:.
elseif mw.ustring.match(instr, "^%.:.*:%.$") then
local outex= ""
local token= mw.ustring.gsub(instr, "^%.:!?%s*(%w+).*", "%1")
local tokenall= mw.ustring.gsub(instr, "^%.:!?%s*(.*)%s*:%.$", "%1")
if tables[token] ~= nil then
q= mw.ustring.gsub(instr, "^%.:!?%s*(.*)%s*:%.$", "%1")
tab, where, outcols, opts= p._parseQueryStr(q, vars);
--mw.addWarning( "table access "..mw.dumpObject( tabledata[tab].data ) )
outex= outex..p._lookup( tabledata[tab], where, outcols, nil, opts ).string
elseif queries[token] ~= nil then
q= mw.ustring.gsub(instr, "^%.:!?%s*(.*)%s*:%.$", "%1")
qry, where, outcols, opts= p._parseQueryStr(q, vars);
--mw.addWarning( "query access "..mw.dumpObject( queries[qry].data ) )
outex= outex..p._lookup( queries[qry], where, outcols, nil, opts ).string
else
--mw.addWarning( "eval expression "..mw..tokenall )
outex= outex..p._parseEvalStr( tokenall, vars )
end
--.:!debugOutput:. or .:normalOutput:.
if ({mw.ustring.gsub(instr, "^%.:!", "")})[2]>0 then
mw.addWarning(tokenall.."= "..outex)
else
out= out..outex
end
else --plain text
out= out..instr
end
end
htmlent= {["<"]="<", ["["]="[", ["="]= "=", ["+"]= "+", [":"]= ":",
["{"]= "{", ["|"]= "|", ["}"]= "}"}
for k, v in pairs(htmlent) do
out= mw.ustring.gsub(out, k, v)
end
pout= frame:preprocess(out)
--Lua-Fehler: .. or <span class="scribunto-error" ..?
if mw.ustring.match((pout), 'class="scribunto%-error"') then
for template in mw.ustring.gmatch(out, "{{[^}]*}}") do
ptout= frame:preprocess(template)
if mw.ustring.match(ptout, 'class="scribunto%-error"') then
mw.addWarning("<span style='color:#d33; font-size:120%'>"
.."Fehler im Templateaufruf <nowiki>"..template.."</nowiki></span>")
break
end
end
end
return pout
end
--- Parses an eval string with 0, 1 or 2 operators out of +, -, * or one ++.
-- Alters vars={} i.a., reads global variable ginstr i.a.<br>
-- example <code> p._parseEvalStr( "id + 2 * 5", {id="3"} ) </code>
function p._parseEvalStr( ev, vars )
ms= {} --matches
--1 element op like "id++" var is [%a_][%w_]*
if (function()
ms= {mw.ustring.match(ev, "^%s*([%a_][%w_]*)%+%+%s*$")}
return #ms~=0
end)() then
pt= ms[1]
assert(vars[pt], "Variable not found near "..ginstr)
out= vars[pt]
assert(tonumber(vars[pt]), "Conversion to number failed near "..ginstr..mw.dumpObject(vars))
vars[pt]= vars[pt]+1
--2 element op like "id+1" var or number is [%w_]*
elseif (function()
ms= {mw.ustring.match(ev, "^%s*([%w_]*)%s*([+-%*])%s*"
.."([%w_]*)%s*$")}
return #ms~=0
end)() then
pts= {ms[1], ms[3]}
op= ms[2]
for i, pt in ipairs(pts) do
if vars[pt] ~= nil then pts[i]= vars[pt] end
assert(tonumber(pts[i]), "Conversion to number failed near "..ginstr..mw.dumpObject(pts))
end
if op=="+" then
out= pts[1]+pts[2]
elseif op=="-" then
out= pts[1]-pts[2]
elseif op=="*" then
out= pts[1]*pts[2]
else
assert(false, "Unknown +*- operator near "..ginstr)
end
--3 element op like "1-2*3"
elseif (function()
ms= {mw.ustring.match(ev, "^%s*([%w_]*)%s*([+-%*])%s*"
.."([%w_]*)%s*([+-%*])%s*"
.."([%w_]*)%s*$")}
return #ms~=0
end)() then
if ms[4]=="*" then
calc2= p._parseEvalStr(ms[3].."*"..ms[5], vars)
out= p._parseEvalStr(ms[1]..ms[2]..calc2, vars)
else
calc1= p._parseEvalStr(ms[1]..ms[2]..ms[3], vars)
out= p._parseEvalStr(calc1..ms[4]..ms[5], vars)
end
--quoted text
elseif (function()
ms= {mw.ustring.match(ev, '^"(.*)"$')}
return #ms~=0
end)() then
out= ms[1]
--plain text
else
pt= mw.ustring.match(ev, "^%s*(%w+)%s*$")
if pt and vars[pt] ~= nil then
out= vars[pt]
else
out= ev
end
end
return out
end
--- Parses a query string and returns (tablename, outcolnames={}, where={}, postopts={}).
-- Alters vars={} i.a.<br>
-- example <code> p._parseQueryStr( "tab1(cont; id==id++, group>=2; limit=3)", vars ) </code>
function p._parseQueryStr( q, vars )
local tab, optstr= mw.ustring.match(q, "^%s*(%w+)%s*%((.*)%)%s*$")
local opts= {}; local o=""
for match in mw.text.gsplit(optstr, ";") do
i= #opts+1; opts[i]= {}
for mat in mw.text.gsplit(match, ",") do
opts[i][#opts[i]+1]= mw.text.trim(mat)
end
end
--mw.addWarning( "parsequery all query opts "..mw.dumpObject( opts ) )
local outcolname= opts[1][1];
local where= {};
opts2= {}
if opts[2] and opts[2][1] and opts[2][1]~="" then opts2= opts[2] end
for i, cond in ipairs(opts2) do --"id==id++"," group<>1"
lt, op, rt= mw.ustring.match(cond, "^%s*(%w+)%s*([=<>][=<>]?)%s*(.*)%s*$")
opf= function() end
if op=="==" then
opf= function(a, b) return a==b end
elseif op=="<=" then
opf= function(a, b) return a<=b end
elseif op==">=" then
opf= function(a, b) return a>=b end
elseif op=="<" then
opf= function(a, b) return a<b end
elseif op==">" then
opf= function(a, b) return a>b end
elseif op=="<>" then
opf= function(a, b) return a~=b end
else
assert(false, "Unknown <>= operator near "..ginstr)
end
rtp= p._parseEvalStr(rt, vars)
where[#where+1]= {searchColumnName= lt, searchValue= rtp, compare= opf}
end
--mw.addWarning( "parsequery where "..mw.dumpObject(where) )
local postopts= {};
opts3= {}
if opts[3] and opts[3][1] and opts[3][1]~="" then opts3= opts[3] end
for i, po in ipairs(opts3) do --"descending=1, limit=3" "occurrence=o++" "orderBy=id"
lt= mw.ustring.match(po, "^%s*(%w+).*$")
rt= mw.ustring.match(po, "^%s*%w+%s*=%s*(.*)%s*$")
if not rt then rt= "1" end
if lt=="occurrence" or lt=="occurrenceLooped" or lt=="limit" then
rt= p._parseEvalStr(rt, vars)
end
postopts[lt]= rt
end
--mw.addWarning( "parsequery postopts "..mw.dumpObject(postopts) )
return tab, where, {outcolname}, postopts
end
--- Returns a string-dump of a table with given columns<br>
-- * param ''outputFormat''= "foo%d..", optional<br>
-- example <code> p._dumpRecords( {{1, "first"}, {2, "second"}} ) </code>
function p._dumpRecords( records, outputFormat )
local outStr= "";
for i, record in ipairs(records) do
if outStr ~= "" then outStr= outStr.."; " end
if outputFormat then
outStr= outStr.. mw.ustring.format(outputFormat, unpack(record))
else
for j, col in ipairs(record) do
if j~=1 then outStr= outStr..", " end
outStr= outStr..tostring(col)
end
end
end
--mw.addWarning( "dumpRecords "..mw.dumpObject(record) ) --addWarning doesnt show same messages twice!
return outStr
end
function typecast(s, typ)
local v
if typ == "number" then
v= assert(tonumber(s), "Number expected near "..ginstr..mw.dumpObject(s))
elseif typ == "boolean" then
v= s ~= "false" and s ~= "0"
end
--mw.addWarning( "typecast "..s.." "..mw.dumpObject( v==false ) )
return v
end
--- Queries rows by col==condition pairs, with other options, returns {string=, table=} to chose from.
-- Table has {data= {{1,"a"}, {2,"b"}}, schema= {fields= {{name="id", type="number"}, {name="cont", type="string"}}}}
-- format like the data table param and the Commons Data tables.<br>
-- ''original source'': [[w:en:Module:Tabular data]] by [[w:en:User:Mxn]] ([[Special:PermaLink/979265259|permalink]], changed)<br>
-- * param ''where''= {{searchColumnName=, searchValue=, or searchPattern=,
-- : compare=function(a,b)..}, {same again}, ..} (may be {})<br>
-- * param ''opts''= {orderBy=, descending=, limit=, or occurrence=} (may be {} or optional)<br>
-- * param ''outputFormat''= "foo%s.." (optional, for _dumpRecords, untested)<br>
-- Reads global variable ginstr i.a.<br>
-- example <code> p._lookup(mw.ext.data.get("Data:Fraction tasks abacdeP80max.tab"),
-- {searchColumnName="group", searchValue=1, compare=function(a,b) return a==b end},
-- {"id", "content"}, nil, {limit=3}).string </code>
function p._lookup(data, where, outputColumnNames, outputFormat, opts)
assert(data, "Table or query not found near "..ginstr)
--//colIdxByName, colTypeByName, colNameByIdx
local colIdxByName, colTypeByName, colNameByIdx= {}, {}, {}
for i, field in ipairs(data.schema.fields) do
colIdxByName[field.name]= i
colTypeByName[field.name]= field.type
colNameByIdx[i]= field.name
end
--mw.addWarning( "colIdxByName "..mw.dumpObject( colIdxByName ) )
--//outputColumnNames column check
assert(outputColumnNames[1] ~= nil and outputColumnNames[1] ~= "", "Output col empty near "..ginstr)
if outputColumnNames[1] == "*" and #outputColumnNames == 1 then
outputColumnNames= colNameByIdx
end
for j, outputColumnName in ipairs(outputColumnNames) do
assert(colIdxByName[outputColumnName], "Output column not found near "..ginstr)
end
outColIdxByName= {}
for i, name in ipairs(outputColumnNames) do
outColIdxByName[name]= i
end
--mw.addWarning( "lookup outputColumnNames "..mw.dumpObject( outputColumnNames) )
--//where column check==
for j, condition in ipairs(where) do
assert(condition.searchColumnName ~= nil and condition.searchColumnName ~= "",
"Where.colname empty near "..ginstr)
typ= assert(colTypeByName[condition.searchColumnName],
"Where.colname not found near "..ginstr)
where[j].searchValue= typecast(where[j].searchValue, typ)
end
--mw.addWarning( "lookup where "..mw.dumpObject( where ) )
--//query
local matchdata= {data= {}, schema= {fields= {}}}
for j, outputColumnName in ipairs(outputColumnNames) do
matchdata.schema.fields[#matchdata.schema.fields+1]= {
name= outputColumnName,
type= colTypeByName[outputColumnName]
}
end
for i, record in ipairs(data.data) do --each record row in data
local ismatch= true
for j, condition in ipairs(where) do --each conditon in where
ismatch= ismatch and (
(condition.searchValue~=nil and
condition.compare( record[ colIdxByName[condition.searchColumnName] ],
condition.searchValue) ) or
(condition.searchPattern and
mw.ustring.match(tostring(record[ colIdxByName[condition.searchColumnName] ]),
condition.searchPattern)))
--mw.addWarning( "i j ismatch "..i..j..tostring(ismatch))
end
if ismatch then
matchingRecord= {}
for j, outputColumnName in ipairs(outputColumnNames) do
matchingRecord[#matchingRecord+1]= record[ colIdxByName[outputColumnName] ]
end
matchdata.data[#matchdata.data+1]= matchingRecord
end
end
--mw.addWarning( "lookup matchdata "..mw.dumpObject( matchdata ) )
--//post processing options
if opts.orderBy then
ocoli= assert(outColIdxByName[opts.orderBy], "OrderBy column not found near "..ginstr)
table.sort( matchdata.data, function(a,b) return a[ocoli]<b[ocoli] end )
--mw.addWarning( "lookup opts order "..mw.dumpObject( matchdata.data ) )
end
if opts.descending then
d2= {}
for i, record in ipairs(matchdata.data) do
d2[#matchdata.data-i+1]= record
end
matchdata.data= d2
--mw.addWarning( "lookup opts desc "..mw.dumpObject( matchdata.data ) )
end
if opts.limit then
l= assert(tonumber(opts.limit), "Limit nonumber error near "..ginstr)
l= math.abs(math.floor(l))
for i= l+1, #matchdata.data do
table.remove(matchdata.data)
end
--mw.addWarning( "lookup opts limit "..mw.dumpObject( matchdata.data ) )
end
if opts.occurrence or opts.occurrenceLooped then
if opts.occurrenceLooped then
opts.occurrence= opts.occurrenceLooped
end
o= assert(tonumber(opts.occurrence), "Occurrence nonumber error near "..ginstr)
o= math.abs(math.floor(o))
md= {matchdata.data[o]} or {}
if opts.occurrenceLooped then
md= {matchdata.data[(o-1)%#matchdata.data+1]}
end
matchdata.data= md
if #matchdata.data==0 then
mw.addWarning("<span style='color:#d33; font-size:120%'>"
.."Warning: No occurrence, near "..ginstr..", "..o..". Liste aus?</span>")
end
--mw.addWarning( "lookup opts occ "..mw.dumpObject( matchdata.data ))
end
return {["string"]= p._dumpRecords( matchdata.data, outputFormat ),
["table"]= matchdata}
end
return p