Changes

Module:Buffer

15,223 bytes added, 10 years ago
more changes than can fit in this summary; held back update for months to avoid flooding job queue
--[[=============================
This Module was written by English wikipedia Alexander Zhikun He, also known as, User:Codehydroon the English Wikipedia 
All methods were developed independently and any resemblance to other string buffer libraries would be coincidental.
Furthermore, many methods will not work when compiled by standard Lua libraries as they depend on behaviors unique to
the MediaMiki Scribunto mod, which, for example, has a getmetatable() method that always returns nil on non-tables.
https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual
 
Source code comments may be thin at some points because they are intended to be supplemented by the documentation page:
https://en.wikipedia.org/wiki/Module:Buffer/doc
Licensed under Creative Commons Attribution-ShareAlike 3.0 Unported License
https://en.wikipedia.org/wiki/User:Codehydro
=============================--]]
return setmetatable({[type] = local functionValid(v)--type validation if v and v~=true then--reject nil/boolean; faster than 2 type() comparisons local str = tostring(v)--functions not filtered since unlikely passed by accident (Scribunto does not have userdata/thread types) if str~=v and str=='table' then return rawget(v, 1) and table.concat(v) end--tostring(string-type) returns same ref; same refs compare faster than type() return if str~='' and then return str or nil end--numbers are coerced to string per table.concat op; appending in string form saves ops on repeat concat end, [select] endlocal noOp, MBpairs = function(n)end do local iMap, ...vMap, oMap, pIter, pOther, pFast, Next--Map local function init()--init = noOp after first run local sepsfunction Next(t) return next, meta = {t end--slightly faster to do this than to use select(2, ...)} if typefunction pIter(seps[#seps]t, k)k =='table' then meta = rawset(table.removeiMap[t] or MBpairs(sepst, true)and iMap[t])[not k and 1 or vMap[t][k]] return k, t[k] end--don'__index', t use rawget; accepting unmapped tables does not measurably affect performance. functionpOther(t, ik) return metak = (oMap[t] or MBpairs(t, true) and oMap[t])[nil==k and 1or vMap[t][k]] return k, t[k] end)--comparison to nil because false is a valid key setmetatable function pFast(sepst, metak) k = not k and 1 or k < (vMap[t] or #t) and k + 1 or nil return k, t[k] end--mapless iterator; almost as fast as native ipairs; slight performance penalty when length not cached --k and k < (vMap[t] or #t) and k + 1 or not k and 1 or nil return ...k, seps t[k] end,--mapless iterator; almost as fast as native ipairs; slight performance penalty when length not cached __index local mk = {__mode = 'k'}--use mode 'k'; found that mode 'kv' sometimes garbage collects maps mid-loop (may not error because iterators auto re-map, but that's expensive) _ init, iMap, vMap, oMap = functionnoOp, setmetatable(self{}, vmk), ...setmetatable({}, mk) local at, rawsetmetatable({}, mk)--iMap is numeric keys, oMap is non-numeric keys, and vMap points to next key if select end function MBpairs('#'t, ...)--pairs always iterates in order local iter, ex ==1 and ... iter =iter=not not... then raw = ...init()--nil else at, raw = ... end if raw then selfiter and not oMap[rawsett] and ex= math.huge else v = getmetatablenil and rawget(selft, 1)[type]~=nil and next(vt, #t) end==nil then--while possible to miss keys, more thorough check would negate the benefit of pFast if v or raw thenvMap[t] = #t return pFast, t, nil if at then if tostring elseif ... or not vMap[t] or select(at'#', ...)~==at 1 then at local ti, tn, to, n = {}, {}, {}, #self + at endt--reduces table lookups if at < 1 then self iMap[rawsett], vMap[t], oMap[t] = math.hugeti, tn, to elseif at > #self + for k = 1 then self, n do ti[rawsetk] = self[rawset] and math.max(at, selftn[rawsetk]) or at = k, k + 1 end--stage one avoids number type checking op in stage two for most numeric keys end for k in (ex or Next)(t) do self.last_concat = if not tn[k] then table.insert(self, selecttonumber(at k)~=k and 1 to or 2ti, at, v)k)end
end
return self end, _nil = function(self, at, v2) if v2#ti~=true and v2~=false n then--faster than type(v2) ~= 'boolean' selftable.last_concat = nilsort(ti) if atfor k =='0' or at==nil then self[1, #self] = v2 if v2 then selfti do tn[rawsetti[k]] = math.huge end else if tostring(at)==at then at = #self k + at 1 end if v2 or self[rawset] or at < 1 or at > #self + 1 then self[at]--somewhat wasteful, self[rawset] = v2, math.huge else table.remove(self, at) end endbut trying to avoid overwriting can be even more expensive
end
return selffor k = 1, #to do tn[to[k]] = k + 1 end end, _all = function(selfreturn iter and pIter or oMap[t] and pOther or noOp, t--noOp for mapless endendlocal parent, valKeyrawkey, spec do--new scope for variables not reused outside (reduces number of var names that need to checked outside of scope) for local mkv = {__mode='kv', __call=function(t,k, v in getmetatable)t[k]=v return k end}--shared meta for Buffer parent property, raw mode, and specialized functions parent, rawkey, spec = setmetatable(self{}, mkv).__pairs, setmetatable(t{}, mkv) do self:_mix, setmetatable(v{}, mkv) --shared meta less memoryend local MB, MBi, MBmix, buffHTML, gfuncs, noCache, Element do--minimize number of locals per scope to reduce time spent sifting through irrelevant variable names local _stream do local stream--keep stream near top of scope local function init(f)--init = noOp after first run if valKey thenlocal function each(self, ...) for k= 1, v in getmetatable(self).__pairsselect(t'#', false...) do if tonumberk = Valid(vselect(k, ...) then )--slightly faster than table.insert(self:_, (Valid(select(k, v...)))) if k then table.insert(self, k) end
end
return self
end
return self endinit,stream, _stream = noOp, { _mix __call = function(selft, v, a) return v = v and ((typeValid(v)~='return v and table' or getmetatable.insert(t, v)or t end,--last_concat cleared before entering stream mode __index = function(t, i) return i=='each' and self._ each or selfMB._all__index(t, i)and setmetatable(selft, v, aMB) or self [i] end,--internal helperno table look up minimizes resources to retrieve the only stream function __tostring = function(t) return setmetatable(t, do not list MB)() end } for k, v in docnext, MB do stream[k] = stream[k] or v end setmetatable(stream, getmetatable(MB)) end _str = function_stream(self, ...) return getmetatable(self).__tostringlast_concat = init() return setmetatable(self, stream):each(...) end, _in = end local function isMBfunc(selfBuffer, s, ...) return rawset--helper for :getParent(getmetatable)-like methods (selfincluding getBuffer which does not return a parent) return s and (select('#', ...)==0 and--eventually should figure out to make this work for :getHTML which is very similar (not rawkey[s] and tostring(s):match'^_.*' and MB.__index(Buffer, nexts) and MB.__index(Buffer, selfs) end(Buffer) or MBmix(Buffer,s))--unprefixed function names append as a string or assert(MB.__index(Buffer, s), ('" %s " does not match any available Module:Buffer objects use [next] rather than 'parentfunction' to avoid traversal by mw):format(s))(Buffer, .html.allDone.)--getParent is a one-way trip so one-time assert not expensive ) or Buffer end local function MBselect(n, ...)--helper for :_out and :_str _out local n, seps = functionn - 1, {select(self2, ...)} if type(seps[n])=='table' then if selfbuffHTML and rawget(seps[nextn] , buffHTML) thenreturn ... end local setmetatable(seps, {__index = setmetatable(seps[n], parent {__index = selectfunction('#'t) return rawget(t, 1) end})})[n] = nil end return ..., seps end local _inHTML do local lastBuffer, lastHTML local function init(...)--init replaced and new version called on return local create, self[next]mwFunc = mw.html.create do if n > 1 then local kmwHTMLmeta = getmetatable(create()) buffHTML, outsmwFunc, seps _inHTML = 1setmetatable(mw.clone(mwHTMLmeta), getmetatable(selfMB)[), mwHTMLmeta.__index--buffHTML declared near top of module; remove _inHTML from outer scope function init(nodes, ...) local name, args, tag = select](n... and type(...)=='table' and 1 or 2, nil, ...) while outs > 0 dotag = create(Valid(name), args) if nodes then table.insert(nodes, tag.parent:_and tag or rawset(self(sepstag, 'parent', parent[knodes]))end if args then selflocal a, parent, k b = args.selfClosing, args.parent args.selfClosing, args.parent[= nil if next](args) then Element._add(parent(tag.nodes, k + 1tag), args) end outs = args.selfClosing, args.parent and outs = a, b- 1 or 0-in case args is reused
end
return tag else end for k, v in next, {[mw] = mwHTMLmeta, __call = function(h, v) return parent:_(selfMBmix(spec[h.nodes] and h.nodes or spec(setmetatable(parent(h.nodes, h), MB) , Element), v) end, __concat = false,--false means take from MB __eq = false } do buffHTML[k] = v or MB[k] end
end
return self[next] or self endlocal nonSelf, _c BHi = function(self{tag=true, cleardone=true, copy) assert(type(clear)allDone=='table'true}, copy and "Buffer:_c(clear, copy) only accepts tables for arg 'clear'buffHTML." or "Two possible causes: 1) Called Buffer:_c(clear, copy) without colon, or 2) Type of 'clear' is not 'table'." ) for k in next, clear __index do clear[k] = nil end local meta, copyMeta, copyType = getmetatable(self) if copy theng if type(copy)g ={__index ='table' then for kfunction(t, v in next, copy do clear[k] = type(vi)=='table' and mw.clone(v) or v end clearif gfuncs and gfuncs[rawseti] then g.__index, gfuncs = cleargfuncs return g.__index[rawseti] or math.hugeend copyMeta = getmetatable(copy) if copyMeta then if not buffHTMLmeta then meta():_inHTML() end} if copyMeta==buffHTMLmeta[mw.html] then copyMeta = buffHTMLmeta setmetatable(clear.nodesnonSelf, metag) end end elseif meta[type]setmetatable(copyBHi, g) then clear[1] = copy end
end
setmetatable(clearfor k in next, copyMeta or meta)nonSelf do--any HTML objects returned by these funcs will be granted Module:Buffer enhancements return self local func = mwFunc[k] end, _parent BHi[k] = function(self, s, outst, ...) return self:_(outs and self[next]:_str(s, outs, ...) or self[next](s)) end, getParent local HTML = functionfunc(selft, ...) return (assert(self, ' (:function(), not .function())')parent[nextHTML] and HTML or rawsetsetmetatable(rawsetparent(selfHTML, next, getmetatable(selft)())[next], require, {self})):_(...buffHTML) end, _inHTML = end do local functionjoinNode(selfHTML, ...sep) local HTMLnodes, meta join = mwHTML.html.createnodes if noCache and rawkey[sep] or Valid(...sep)then join, getmetatableHTML.nodes = tostring(self) if not rawgetrawset(_GHTML, 'buffHTMLmetanodes') then local mwHTMLmeta = getmetatable(mw, {MB.html.create__call(nodes, sep)})), nodes end buffHTMLmeta, mwHTMLfunc = rawset return join or tostring(mw.clone(mwHTMLmetaHTML), mw.html, mwHTMLmeta), mwHTMLmeta.__index end
for k, v in next, {
[rawget] getParent = function(HTML, Buffer...) lastHTML = HTML return MBi.getParent(HTML:allDone(), ...)end,--return to Buffer that created the HTML tree buffHTMLmeta[require] getBuffer = function(HTML if , ... then ) lastHTML = HTML return selectisMBfunc('#'lastBuffer, ...)end,--return to last used killParent ==1 and Bufferfunction(HTML, ...) MBi.killParent(HTML:_allDone(), ...) or assertreturn HTML end, _out = function(Buffer[HTML, ...]) if ...==0 then MBi._out(HTML.nodes, ...) return HTML end lastHTML, HTML = HTML, HTML:allDone() local n, ops, seps = select('" %s " does not match any Module:Buffer function#', ...):format(tostring if n > 1 then local ops, seps = MBselect(n, ...) return parent[HTML]:_in(joinNode(HTML, rawget(seps, 0))):_out(Bufferops, selectrawset(2seps, ...buffHTML, true)) end return Bufferparent[HTML]:_(joinNode(HTML, ...))
end,
getParent _str = function(HTML, ...) --does not set lastHTML if ...==0 then return HTML[rawget]joinNode(HTML, HTML:allDoneselect()[next]2, ...)) end,--return to buffer that created itpassing 0 strings without calling allDone() getBuffer = function( local HTML, ...) return n = HTML[rawget]:allDone(HTML), buffHTMLmeta[next]select('#', ...) end,--return to last buffer used _out if n > 1 then local ops, seps = functionMBselect(HTMLn, ...) return parent[HTML]:getParent_in():_joinNode(HTML, rawget(seps, 1))):_out_str(ops, rawset(seps, buffHTML, true)) end return joinNode(HTML, ...) end, _inv _parent = function(HTML, s, outs, ...) return table.insert(HTML.nodes:_(outs and , parent[HTML:getParentallDone()]:_str(s, outs, ...) or HTML:getParent()(s)) return HTML end } do buffHTMLmeta.__indexBHi[k] = v end end do local htmlArg, skip, outFuncs = {parent=true,selfClosing=true,tagName=true}, {} if new_G thendo local out local function func(nodes, ...) return out(parent[nodes], ...) end for koutFuncs = setmetatable({ tag = function(nodes, v in pairs...) return parent(gfuncssetmetatable(init(nodes, ...) , buffHTML), parent[nodes]) end, done = function(b, ops) b = parent[b] while b.parent and ops~=0 do buffHTMLmetab, ops = b.parent, ops and ops - 1 or 0 end return b end }, {__index= function(nodes, i) if rawget(BHi, i) then out = BHi[ki] = v return func end--rawget to exclude globals gfuncs = nilend})
end
local mbpairs, outFuncs, htmlArg, nonSelf, spinach Element = meta.__pairs, {}, {parent = true, selfClosing = true, tagName = true}, {tag = true, done = true, allDone = true} spinach = {--spinach makes html.nodes buffer... get it?... [rawset] = math.huge, _out = function(nodes, ...) return nodes:getParent('_', nodes:getHTML()):_out(...) end, _add = function(nodes, t)--children are unenhanced local HTML, nodeFunc = nodes[next], buffHTMLmeta[require].nodes for k, v in mbpairsMBpairs(t) , t, skip[t] do buffHTMLmeta.__call(HTML, v~=true and MBmix or noOp)(nodes, v) end local HTML = parent[nodes] for k, v in mbpairsMBpairs(t, false) do
if htmlArg[k] then HTML[k] = v
elseif v and v~=true then
if nonSelf[k] then
if k=='tag' then
if tostringtype(v)==v 'table' then mwHTMLfunc.tag(HTML, v) elseif v skip[1] and v[1]~, k =true then local tag = mwHTMLfunc.tag(HTML1, rawset(create(Valid(v[1])), 'parent', HTML) setmetatable(nodeFuncElement._add(rawsetspec(setmetatableparent(tagk.nodes, meta)k, nexttable.insert(nodes, tagk)), Element), v) if k.selfClosing then k.nodes = nil else spec[k.nodes], parent[k.nodes] = nilend--free memory/reduce clutter; parent ref will auto-unset when k.nodes is nil if not k.tagName then k.styles, k.attributes = nil end else table.insert(nodes, create(v)) end elseif mwFunc[k] then if k=='done' and tonumber(v)~=v and v[1]and tonumber(v[1])==v[1] then skip[v] = 1 end MBmix(outFuncs[k](nodes, tagskip[v] and v[1]).nodes, v) elseif v[next1] or v[2] then k = falseMBi[k](nodes, unpack(v, 1, rawset(skip, v, k=='_B' and 1 or 2)[v])) else nodeFuncElement._add(HTML.getmetatable(k) and rawget(k, 'nodes') or k, v) end--if k is not a table, then v should not contain any extra keys or this may error. else buffHTMLmeta.__call(mwHTMLfuncMBi[k](HTML)nodes, v) end--k probably = done/allDone= '_G' or '_R' elseif mwHTMLfuncmwFunc[k] then if k=='node' or type(v)~='table' or rawget(v, 'nodes') then mwHTMLfuncmwFunc[k](HTML, v)
else
local css = k=='css' for _x, y in mbpairsMBpairs(v, true) do if (y and y~=true then mwHTMLfuncand mwFunc[k]or noOp)(HTML, css and x:gsub('_', '-') or x, y) end end--iterate non-numbers first for x_, y in mbpairsMBpairs(v, falsenil) do if (y and y~=true then mwHTMLfuncand mwFunc[k]or noOp)(HTML, x, y) end end--don't bother with gsub since text must be quoted anyhow
end
elseif rawget(Element, k) or rawget(MBi, k) then if tonumber(v)==v or v[1]==nil or getmetatable(v) then (Element[k] or MBi[k])(nodes, v)--v is probably string-able object, or a table to be handled by :_all else nodeFunc(Element[k]or MBi[k])(HTML.nodes, unpack(v, 1, table.maxn(v))) end--v is definately a table else mwFunc.css(HTML, k:gsub('_', '-', 1), tostring(v)) end--oddly enough, :_add clocked its fastest runtime after adding auto-gsub as a feature skip[v] = nil
end
end
end
}
local tempMeta = {mode='v', copy={styles=true,attributes=true}} function makeNodesBuffertempMeta.__index(HTMLt, i) local nodes = setmetatablereturn tempMeta.copy[i] and rawset(HTMLt, i, MBi.nodes_cc(false, meta0, t.orig[i])) nodes[nexti], nodesor t.parent orig[i] end rawkey[setmetatable(Element, {__index = HTMLoutFuncs, HTML for k__concat=function(Element, v in next, buffHTMLmeta.__index do ) return setmetatable({nodes[k] = rawsetspec(outFuncs{}, kElement), outFuncsorig=parent[kv] or }, tempMeta) end})] = math.huge end functionMBi:getHTML(nodes...) lastBuffer = self if ... then if select('#', ...) ==1 then return v(nodesnot rawkey[nexts], and tostring(...) end):match'^_' and BHi[k...] end for k, v in next, spinach do nodesand BHi[k...] = v end(lastHTML) or lastHTML(...) nodeselse return assert(BHi[._inHTML..], nodes('" %s " does not match any mw._build = nodeshtml or Buffer-mw.taghtml function'):format(tostring(...)))(lastHTML, select(2, ...)) end
end
for k in nextreturn lastHTML end function MBi:_html(...) return MBi._(self, nonSelf dolastHTML, select(spec[self]==Element and select('#', ...)==0 and 1 or 2, true, ...)) end return init(...) end function _inHTML(self, ...) local HTML = init(nil, ...) if HTML.selfClosing and spec[self]==Element then self.last_concat = table.insert(self, HTML) return self end lastBuffer, lastHTML = self, setmetatable(parent(HTML, self), buffHTML)--any HTML objects returned set after 'args' table processed by these funcs will be granted Module:Buffer enhancements_add return HTML end end local func _var, unbuild do local prev, rebuild local function init(...)--init replaced before return local function pick(b, v) return b and table.insert(b, v) or v end local function c(a, num) return rawset(a.a or a, 0, a[0] and a[0] + a.c or num and a[1] or a[1]:byte())[0] end local same, build, alt = mwHTMLfunc{__tostring = function(a, b) return a.a[k0]and pick(b, a.a.string and string.char(a.a[0]) or a.a.table and a.a[1][a.a[0]] or a.a[0]) end}, { buffHTMLmeta __index = {c = 1}, __tostring = function(t) return t:_build() end, table = function(a, b) local i = next(a[1], a[0]) or a[0]==#a[1] and next(a[1]) return pick(b, rawset(a.a or a, 0, i)[1][i]) end,--change rate (a.c) ignored since users control the table's contents number = function(a, b) return pick(b, c(a, true)) end, string = function(a, b) return pick(b, string.char(c(a))) end }, {__index= function(a, i) return a.a[ki] end, __tostring = function(a, b) return (rawget(a, 0) and a[0]==tostring(a[0]) and rawset(a, 0, a[0]:byte()) or a).a._build(a, b) end} local function shift(t, c) t[0] = t[0] and t[0] + c or t:_build() and t[0] - t.c + c if t.table then t[0] = (t[0] - 1) % #t[1] + 1 end end function rebuild(...) local HTML v, c = func... if v or select(t'#', ...)==0 then if v and not c then return prev end local meta, c = select(HTMLv and 1 or 3, alt, c, same, 0) return setmetatable({a = prev, _build = meta.selfClosing __tostring, c = c}, meta) elseif v==nil then--no-op elseif c then shift(prev, c)--v == false else prev:_build() end end init, noCache = function(v, c) prev = setmetatable({v, c = c, _build = build[type(v)] or HTMLv, [nexttype(v)]= true, alt = {}}, build) return prev end, true return init(...) end function unbuild(sep) for k, v in MBpairs(sep, nil) do k = getmetatable(v) if k and (k==build or k==alt) then makeNodesBuffershift(v.a or v, -v.c) end end end function _var(self, ...) local obj if ... and ...~=true then obj = init(...) elseif prev then if ...~=false then obj = rebuild(...) else rebuild(...) end end return obj and MBi._(self, obj, nil, true) or self end end local lib; MBi = setmetatable(rawset{stream = _stream, _inHTML = _inHTML, _var = _var, _ = function(HTMLself, nextv, t...) local at, buffHTMLmetaraw = select(select('#', ...)==1 and ...==true and 1 or 2, nil, ...) if raw then rawkey[self] = math.huge else v = Valid(v) end if v or raw then if at or rawkey[self] then raw = #self end--if length increases by more than one after table.insert, then set rawkey[self] = math.huge; rawkey[self] may be equal to a previous 'at' at, self.last_concat = at and (tonumber(at)~=at and raw + at or at) table.insert(self, select(at and 1 or 2, at, v)) if at and at < 0 or raw and #self - raw > 1 then rawkey[self] = math.huge elseif at and #self==raw then rawkey[self] = rawkey[self] and math.max(rawkey[self], at) or at end end--above line looks bizarre because one table.insert op may make length jump from 0 to 8: local wtf={[2]=2,[4]=4,[8]=8}mw.log(#wtf,table.insert(wtf,1),#wtf) return HTMLself end, _nil = function(self, at, ...) if ...~=true and ...~=false then--faster than type(...) ~= 'boolean' if not at or at=='0' then self[#self] = ... if ... then rawkey[self] = math.huge end else local n, v = tonumber(at), ... if n~=at then if n then n = #self + at elseif at~=true and select('#', ...)==0 then v, n = at, #self end end if n then if v==nil and n > 0 then table.remove(self, n) else self[math.floor(n)], rawkey[self] = v, math.huge end--floor position for consistency with Table library
end
end
for k, v in next, {--add new Buffer object functions getHTML = function(self, ...) buffHTMLmeta[next] last_concat = self if ... then if type(...)=='table' then return buffHTMLmeta[require](...) else return assert(buffHTMLmeta[require][...], ('" %s " does not match any mw.html function'):format(tostring(...)))(buffHTMLmeta[require], select(2, ...)) end end return buffHTMLmeta[require] end, _html = function(self, ...) return self:_(self:getHTML(), ...) end } do meta.__index[k] = v end buffHTMLmeta.__call = function(h, ...) return ... and ('table'==type(...) and spinach._add or h.nodes._)(h.nodes, ...) or h.nodes endnil
end
makeNodesBuffer(setmetatable(HTML, buffHTMLmeta)) rawset(setmetatable(HTML, rawset(buffHTMLmeta, require, HTML)), next, self) return self:getHTML()
end,
_build _all = function (self, nodes) return table.insert(nodest, self(valKey)) end-- for compatibility with regular mw.html objects }k, __call = functionv in MBpairs(t) do MBmix(self, ...v, valKey)end local t2 = t[rawset] for k, v in valKey and t[rawset] > #t and getmetatable(t)():_allMBpairs(t, false) or noOp, tdo if selecttonumber('#', ...v)==0 then return rawsetMBi._(tself, 'last_concat'k, t.last_concat or table.concat(t2v)).last_concat end--self not always a buffer return table.concat elseif rawget(t2MBi, ...) or '' end, __tostring = function(t, ...) if type(tk)and v and v~='table' true then return t end local n, r if v[1]= select('#', ...), =nil or getmetatable(tv) if n > 1 then local MBi[k, outs, seps = 2, r[select](nself, ...v) r = r(t(seps else MBi[1k])) while outs > 1 do r:_(t[next]self, unpack(seps[k])v, 1, table.maxn(v))) end t, k = t[next], k + 1 outs = t[next] and outs - 1 or 0end
end
return self end, _str = function(t, ...) local n = select('#', ...) if n > 1 then local k, ops, seps, r = 2, MBselect(n, ...) r = MB(t(seps[1])) while parent[t] and ops > 1 and r:_(parent[t](seps[k]), 1) do t, k, ops = parent[t], k + 1, ops - 1 end return table.concat(r, seps[k]or nil) end return MB.__call(t, ...) end, _in = function (self, ...) return parent(MB(...), self) end, _out = function(t, ...) if ...==0 then return parent(t, parent[t], MBi._cc(t, t, MB.__call(t, (select(2, ...))), getmetatable(t))) end--love how :_cc needed nothing new to implement this *self pat on back* local n = select('#', ...) if n > 1 then local k, ops, seps = 1, MBselect(n, ...) while parent[t] and ops > 0 do t, k, ops = parent[t]:_(t(seps[k])), k + 1, ops - 1 end elseif parent[t] then return parent[t]:_(t(...)) end return t end, _cc = function(self, clear, copy, meta) if clear then if rawequal(clear, copy) then return self, spec[MBi._cc] and setmetatable(spec[MBi._cc], MB)--rawequal to avoid re-string via __eq in case both are different Buffer objects elseif copy==true then copy = self end if clear~=0 then assert(type(clear)=='table', debug.traceback('Buffer:_cc can only "clear" tables. Did you forget to call with a colon?', 2))--errors can be hard to trace without this for k in self and next or noOp, clear do rawset(clear, k, nil) end else return MBi._cc(false, {unpack(copy)}, copy) end--copy length w/o empty strings; recursion to avoid self = false causing garbage collection (non-weak child may exist) if self==false or copy and type(copy)=='table'then--self==false means copy is a table (saves a type op for recursive calls) meta = meta or getmetatable(copy) if self and #copy > 1 then--preserves length with empty strings; developed from studying http://www.lua.org/source/5.1/ltable.c.html local n, null, i, e = #copy, {}, math.ldexp(2, select(2, math.frexp(#copy)) - 2) e, spec[MBi._cc], parent[null] = i - 1, null, clear for k = 1, e do table.insert(clear, false) end while i<=n do table.insert(clear, i, '') i, null[i] = i + math.ldexp(2, select(2, math.frexp(n - i)) - 2), '' end for k = 1, e do rawset(clear, k, nil) end end for k, v in next, copy do rawset(clear, k, type(v)=='table' and MBi._cc(false, 0, v) or v) end elseif copy then rawset(clear, 1, (Valid(copy))) end rawkey[setmetatable(clear, meta)], parent[clear] = rawkey[copy], parent[copy] end return self and rawset(self, 'last_concat', nil) or clear end, _parent = function(self, ...) return parent[self] and MBi._(self, parent[self]:_str(...)) or self end, getParent = function(self, ...) return isMBfunc(parent[self] or parent[parent(self, setmetatable({}, MB))], ...) end, killParent = function(self, ...) return parent[self] and isMBfunc(parent[self], ...) and parent(self) or self end, _build = function(self, t) table.insert(t, self()) end,--for compatibility with mw.html:node() last_concat = false--prevent library check }, {__index = function(t, i)--import string, mw.text, and mw.ustring libraries on an as-needed basis local func = string[i] or mw.text[i] or mw.ustring[i] or type(i)=='string' and mw.ustring[i:match'^u(.+)'] if func then lib = lib or function (s, f, ...) if parent[s] and next(s)==nil then return s:_((f(tostring(parent[Element and (spec[s]==Element and s:allDone() or spec[parent[s]]==Element and parent[s]) or s]), ...))) end return f(tostring(s), ...)--not using ternary/logical operators here to allow multiple return values end return rawset(t, i, i:match'^u?gsub' and function(self, p, r, ...)return lib(self, func, p, r or '', ...)end--Why are ugsub/gsub special? because empty strings are against my religion! or function(self, ...)return lib(self, func, ...)end)[i]
end
return t(... end}) end, __concat = functionMBmix(at, bv, Buffer...) if Buffer then return error end Buffer v and ((type(v)~= 'table' or getmetatable(av)) local us, them = pcall(Buffer and BufferMBi.__concat_(t, false, next, 1v) if not us or them~=error then Buffer = getmetatable(b) end return Bufferselect():_all{a'#', b}(...) end, __pairs = function=0 and spec[t] and spec[t]._add or MBi._all)(t, v, ...)) or t end--pairs :_all always iterates in orderpasses two args if not map_i then local mk = {__mode='k'} map_i_G, map_n, map_o new_G = setmetatable_G--localize _G for console testing ({}, mkconsole _G ~= module _G), return setmetatable({}, mk), setmetatable({}, mk)--reduces table lookups __index = function mBuffIter(t, i) i = map_ireturn spec[t][nil==i and 1 or map_nspec[t][i]] return i, tor MBi[i] end, __call = function mBuffOther(t, ...) local rawsep, sep, i, j, raw = noCache and rawkey[...] and ..., ... if i or j or rawsep or Valid(sep) then raw, sep, i , j = map_orawkey[spec[t]] or rawkey[nil=t], rawsep or Valid(sep), i and (i~=tonumber(i ) and 1 or map_o[i + #t][i]] return or i), j and (j~=tonumber(j) and j + #t[i] endor j) function mBuffMapif rawsep or raw and (raw>=(j or #t)or i < 1) then local tiraw, tni, to, n j = {}, {}, {}i and math.floor(i), #tj and math.floor(j)--reduces floor for consistency with table lookups.concat(t, sep, i, j), which ignores decimals map_i[t]raw.lc, map_n[t], map_o[.last_concat = t] = ti, tn, .last_concat--temporarily unset last_concat toprevent disqualification from mapless iteration for k = 1, n do table.insertv in MBpairs(ti, kt)do tnif raw[1] or not i or k] >= i then if j and k + 1 > j then break end if nraw.s then raw.s ==0 or nexttable.insert(traw, n)~=nil then local start = nexttostring(tsep)==1 ) end--if sep contains v and n or nil for kv is a Buffer-variable, sep must be strung before v in next, t, start do k = Valid(v) if tn[k] then--no op, since: t raw.s ={[2]=1,rawsep or sep and raw[1]=2}; #t => 2; next(t,#t) => 1,2 elseif tonumber(k)==k then and table.insert(tiraw, ksep) elseif k~=t and v~=t then table.insert(toraw, k) to[k] = #to + 1
end
end
if #ti~=n then
table.sort(ti)
for k, v in next, ti do tn[v] = k + 1 end
end
end
if rawsep and not raw.s then raw[#raw] = unbuild(sep) end--unbuild rawsep if final index in t was invalid
t.last_concat = raw.lc return table.concat(raw)
end
return table.concat(t, sep, i and math.max(i, 1), j and math.min(j, #t))
end
if select('#', return MB...)==0 or ...==true then mBuffMap__tostring(t) end return ...==nil and mBuffIter or mBuffOther, t
end,
__ipairs = pairs},{__tostring = function(t) if t.last_concat then return''t.last_concat end, __call local r = functionrawkey[spec[t]] or rawkey[t] r = table.concat(self, ..r and r>=#t and MBi._all({}, t) or t) if not rawgetreturn (_GnoCache or rawset(t, 'libFunclast_concat', r)) then--adds string and mw libraries; also add global functions if passed; rawget avoids error when reading nil globalr rawset(_G end, 'libFunc', {__call __concat = function(ta, self, ...b) return t[ if buffHTML then for k = 1](type(self)~, 2 do local v ='table' and self or self:_strselect()k, ...a, b) end}) --faster than for _k, library v in ipairspairs{mw.texta, string, mw.ustringb} do for k, func in pairs(library) do self.__indexif v and spec[library==mw.ustring v] and k .. '_' or kspec[v] = setmetatable({func}, libFunc) end=Element then end if parent[v].selfClosing then rawset if rawequal(_Ga, 'new_G', ... and typeb) then return (not noCache or parent[v]...tagName)=='table' and v:_str(...0)._G==... and ...~=_G and :rep(...2).mw and or v:_str(...0).mw.log==mw.log and ...v:_str(0) if new_G then rawset(_G, 'gfuncs', {end--stored as global for rawequal avoids premature tostring of Buffer:_inHTML_var objects; _G b, a = functionselect(selfk, nameb, var) if rawget(new_Gparent[v], namea) then else local metaG temp = getmetatableElement .. v --helper method; returns a mirror of parent[v] MBmix(MBmix(new_Gparent(temp.nodes, temp) if not metaG then metaG , a), k= {__index = {}} setmetatable(new_G, metaG) end metaG.__index1 and spec[nameb] = new_G=Element and parent[nameb] end new_G[name] = var or selfb) return self endbuffHTML.__tostring(setmetatable(temp, _R {__index= function(self, name, var) new_Gparent[namev] , __mode= var return self end,'v'}))--switch from tempMeta to avoid MBi._cc op of styles/attributes _B = function(self, var) return var or var~=false and self end }) for k, v in pairs(gfuncs) do self.__index[k] = v end
end
end
return table.concat(MBmix(MBmix({}, a), b)) end, __pairs = MBpairs, __ipairs = MBpairs, __eq = function(a, b) return tostring(a)==tostring(b) end--avoid a==b in this module; use rawequal(a,b) when they may be different Buffers (premature tostring waste ops and is bad for Buffer:_var)}, {__tostring = function()return''end, __call = function(self, ...) MB = MB or self if new_G then if ... and _G and ...==_G then new_G = ... end elseif ... and (...==_G or type(...)==new_G 'table' and (...)._G==...) then local Nil, mG = {}, (...):getmetatable() or (...):setmetatable{}:getmetatable() new_G, _G, gfuncs = ..., ..., {--avoid error when passing gfuncs stored for Buffer:_inHTML; new_G is a is a Module:Buffer local declared just before the final return statement. _G to = function(self, i, ...) local X, save = rawget(new_G, i), select('#', ...)==0 and self or ... if i and i~=true and not (X and save and rawequal(X, save)) and rawset(new_G, i, save) and (X~=nil or save==nil and new_G[i]~=nil) then--rawequal in case X is another buffer a second time local newmG = getmetatable(new_G) or {__call=mG.__call} if mG.__index then pcall(rawset, name mG.__index, i, X) else mG.__index = setmetatable(new_G, mG) and {[i] = X}end end return self, ...--avoiding __eq with rawequal(self,save)is overkill since buffers can self-save without being passed as save end, select _R = function(2self, i, v, m) if i~='new_G' then if i and i~=true then rawset(new_G, i , v) end elseif not v or v==true or v._G~=_G then new_G = setmetatable(v~=true and v or {}, {__call = mG.__call, __index = v~=true and m~=true and (m or new_G) or nil}) else new_G, (not m and (m~=nil or v==new_G) and Nil or getmetatable(v)).__index = v, m~=true and (m or new_G)or nil end--setting Nil.__index is noOp return self end, _2 = function(self, ...) if name and typenew_G[...]~=nil then return new_G[...] end--higher priority so Buffer:_G(name'new_G', ...)can prevent an overwrite if ...=='stringnew_G' then return rawset((select('#', ...)~=1 and MBi._R(new_G, ...) or new_G), '_G', _G) end return select(select('#', ...)==1 and 1 or 2, self:_G(...))--return only one value; 'return select(2, self:_G(...)) or self' doesn't work for returning nil end, _B = function(self, v) return v or v==nil and Nil end } for k, v in next, gfuncs do MBi[namek] = new v end setmetatable(Nil,{__concat=MB.__concat,__newindex=noOp,__call=noOp,__tostring=noOp,__metatable=MB,__index=setmetatable({_B=MBi._B,_=function()return newNil end,last_concat=''}, {__index=function(t,i)return (MBi[i] or i and not tonumber(i)) and t._ or nil end})}) function mG.__call(G, k, ...) return (k._G or G.type(k)=='table') and (G.select('#', ...)~=1 and G.rawset(k, ...) or G:rawset(..., k) and k) or G:rawset(k, (...)) and ... end
end
return local new = setmetatable({}, self) if ... and (...)==new_G then return select(2, ...) and MBmix(new:_G((select(2, ...))), select(3, ...)) or new end return ... and MBi._(new, ...)or new
end,
__index = function(t, i)
MB = MB or t return MBi[i] and function(...) return t.__indexMBi[i](tsetmetatable({}, t), select(...==t and 2or 1,...)) end
end
})
Anonymous user

Help improve this page!