Difference between revisions of "Module:Buffer"

From ChaldeanWiki
Jump to: navigation, search
m (1 revision imported: Maria Theresa Asmar)
(more changes than can fit in this summary; held back update for months to avoid flooding job queue)
Line 1: Line 1:
 
--[[=============================
 
--[[=============================
This Module was written by English wikipedia User:Codehydro
+
This Module was written by Alexander Zhikun He, also known as, User:Codehydro on the English Wikipedia
 +
 
 
All methods were developed independently and any resemblance to other string buffer libraries would be coincidental.
 
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
 
Licensed under Creative Commons Attribution-ShareAlike 3.0 Unported License
Line 9: Line 16:
 
https://en.wikipedia.org/wiki/User:Codehydro
 
https://en.wikipedia.org/wiki/User:Codehydro
 
=============================--]]
 
=============================--]]
return setmetatable({[type] = function(v)
+
local function Valid(v)--type validation
if v and v~=true then--reject nil/boolean; faster than 2 type() comparisons
+
if v and v~=true then--reject nil/boolean; faster than 2 type() comparisons
local str = tostring(v)
+
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 table.concat(v) end
+
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 str~='' and str or nil
+
if str~='' then return str end--numbers are coerced to string per table.concat op; appending in string form saves ops on repeat concat
end
+
end
end,
+
end
[select] = function(n, ...)
+
local noOp, MBpairs = function()end do local iMap, vMap, oMap, pIter, pOther, pFast, Next--Map
local seps, meta = {select(2, ...)}
+
local function init()--init = noOp after first run
if type(seps[#seps])=='table' then
+
function Next(t) return next, t end--slightly faster to do this than to use select()
meta = rawset(table.remove(seps), '__index', function(t, i) return meta[1] end)
+
function pIter(t, k) k = (iMap[t] or MBpairs(t, true) and iMap[t])[not k and 1 or vMap[t][k]] return k, t[k] end--don't use rawget; accepting unmapped tables does not measurably affect performance.
setmetatable(seps, meta)
+
function pOther(t, k) k = (oMap[t] or MBpairs(t, true) and oMap[t])[nil==k and 1 or vMap[t][k]] return k, t[k] end--comparison to nil because false is a valid key
end
+
function pFast(t, k) 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
return ..., seps
+
  --k and k < (vMap[t] or #t) and k + 1 or not k and 1 or nil return k, t[k] end--mapless iterator; almost as fast as native ipairs; slight performance penalty when length not cached
end,
+
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)
__index = {
+
init, iMap, vMap, oMap = noOp, setmetatable({}, mk), setmetatable({}, mk), setmetatable({}, mk)--iMap is numeric keys, oMap is non-numeric keys, and vMap points to next key
_ = function(self, v, ...)
+
end
local at, raw
+
function MBpairs(t, ...)--pairs always iterates in order
if select('#', ...)==1 and ...==not not... then raw = ...
+
local iter, ex = ...
else at, raw = ... end
+
iter = iter==init()--nil
if raw then self[rawset] = math.huge else v = getmetatable(self)[type](v) end
+
if iter and not oMap[t] and ex==nil and rawget(t, 1)~=nil and next(t, #t)==nil then--while possible to miss keys, more thorough check would negate the benefit of pFast
if v or raw then
+
vMap[t] = #t return pFast, t, nil
if at then
+
elseif ... or not vMap[t] or select('#', ...)~=1 then
if tostring(at)==at then at = #self + at end
+
local ti, tn, to, n = {}, {}, {}, #t--reduces table lookups
if at < 1 then self[rawset] = math.huge
+
iMap[t], vMap[t], oMap[t] = ti, tn, to
elseif at > #self + 1 then self[rawset] = self[rawset] and math.max(at, self[rawset]) or at end
+
for k = 1, n do ti[k], tn[k] = 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 = table.insert(self, select(at and 1 or 2, at, v))
+
if not tn[k] then table.insert(tonumber(k)~=k and to or ti, k) end
 
end
 
end
return self
+
if #ti~=n then
end,
+
table.sort(ti)
_nil = function(self, at, v2)
+
for k = 1, #ti do tn[ti[k]] = k + 1 end--somewhat wasteful, but trying to avoid overwriting can be even more expensive
if v2~=true and v2~=false then--faster than type(v2) ~= 'boolean'
+
self.last_concat = nil
+
if at=='0' or at==nil then
+
self[#self] = v2
+
if v2 then self[rawset] = math.huge end
+
else
+
if tostring(at)==at then at = #self + at end
+
if v2 or self[rawset] or at < 1 or at > #self + 1 then self[at], self[rawset] = v2, math.huge
+
else table.remove(self, at) end
+
end
+
 
end
 
end
return self
+
for k = 1, #to do tn[to[k]] = k + 1 end
end,
+
end
_all = function(self, t, valKey)
+
return iter and pIter or oMap[t] and pOther or noOp, t--noOp for mapless
for k, v in getmetatable(self).__pairs(t) do self:_mix(v) end
+
end
if valKey then
+
end
for k, v in getmetatable(self).__pairs(t, false) do
+
local parent, rawkey, spec do--new scope for variables not reused outside (reduces number of var names that need to checked outside of scope)
if tonumber(v) then self:_(k, v) end
+
local mkv = {__mode='kv', __call=function(t,k,v)t[k]=v return k end}--shared meta for Buffer parent property, raw mode, and specialized functions
 +
parent, rawkey, spec = setmetatable({}, mkv), setmetatable({}, mkv), setmetatable({}, mkv)--shared meta less memory
 +
end
 +
 
 +
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
 +
local function each(self, ...)
 +
for k = 1, select('#', ...) do
 +
k = Valid(select(k, ...))--slightly faster than table.insert(self, (Valid(select(k, ...))))
 +
if k then table.insert(self, k) end
 
end
 
end
 +
return self
 
end
 
end
return self
+
init, stream, _stream = noOp, {
end,
+
__call = function(t, v) v = v and Valid(v) return v and table.insert(t, v) or t end,--last_concat cleared before entering stream mode
_mix = function(self, v, a) return v and ((type(v)~='table' or getmetatable(v)) and self._ or self._all)(self, v, a) or self end,--internal helper, do not list in doc
+
__index = function(t, i) return i=='each' and each or MB.__index(t, i) and setmetatable(t, MB)[i] end,--no table look up minimizes resources to retrieve the only stream function
_str = function(self, ...) return getmetatable(self).__tostring(self, ...) end,
+
__tostring = function(t) return setmetatable(t, MB)() end
_in = function (self, ...) return rawset(getmetatable(self)(...), next, self) end,--Module:Buffer objects use [next] rather than 'parent' to avoid traversal by mw.html.allDone()
+
} for k, v in next, MB do stream[k] = stream[k] or v end
_out = function(self, ...)
+
setmetatable(stream, getmetatable(MB))
if self[next] then
+
end
local n, parent = select('#', ...), self[next]
+
function _stream(self, ...) self.last_concat = init() return setmetatable(self, stream):each(...) end
if n > 1 then
+
end
local k, outs, seps = 1, getmetatable(self)[select](n, ...)
+
local function isMBfunc(Buffer, s, ...)--helper for :getParent()-like methods (including getBuffer which does not return a parent)
while outs > 0 do
+
return s and (select('#', ...)==0 and--eventually should figure out to make this work for :getHTML which is very similar
parent:_(self(seps[k]))
+
(not rawkey[s] and tostring(s):match'^_.*' and MB.__index(Buffer, s) and MB.__index(Buffer, s)(Buffer) or MBmix(Buffer, s))--unprefixed function names append as a string
self, parent, k = parent, parent[next], k + 1
+
or assert(MB.__index(Buffer, s), ('" %s " does not match any available Module:Buffer function'):format(s))(Buffer, ...)--getParent is a one-way trip so one-time assert not expensive
outs = parent and outs - 1 or 0
+
) or Buffer
 +
end
 +
local function MBselect(n, ...)--helper for :_out and :_str
 +
local n, seps = n - 1, {select(2, ...)}
 +
if type(seps[n])=='table' then
 +
if buffHTML and rawget(seps[n], buffHTML) then return ... end
 +
setmetatable(seps, {__index = setmetatable(seps[n], {__index = function(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, mwFunc = mw.html.create do
 +
local mwHTMLmeta = getmetatable(create())
 +
buffHTML, mwFunc, _inHTML = setmetatable(mw.clone(mwHTMLmeta), getmetatable(MB)), mwHTMLmeta.__index--buffHTML declared near top of module; remove _inHTML from outer scope
 +
function init(nodes, ...)
 +
local name, args, tag = select(... and type(...)=='table' and 1 or 2, nil, ...)
 +
tag = create(Valid(name), args)
 +
if nodes then table.insert(nodes, tag.parent and tag or rawset(tag, 'parent', parent[nodes])) end
 +
if args then
 +
local a, b = args.selfClosing, args.parent
 +
args.selfClosing, args.parent = nil
 +
if next(args) then Element._add(parent(tag.nodes, tag), args) end
 +
args.selfClosing, args.parent = a, b--in case args is reused
 
end
 
end
else return parent:_(self(...)) end
+
return tag
 +
end
 +
for k, v in next, {[mw] = mwHTMLmeta,
 +
__call = function(h, v) return MBmix(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
 
end
return self[next] or self
+
local nonSelf, BHi = {tag=true,done=true,allDone=true}, buffHTML.__index do local g
end,
+
g = {__index = function(t, i)
_c = function(self, clear, copy)
+
if gfuncs and gfuncs[i] then g.__index, gfuncs = gfuncs return g.__index[i] end
assert(type(clear)=='table', copy and
+
end}
"Buffer:_c(clear, copy) only accepts tables for arg 'clear'."
+
setmetatable(nonSelf, g)
or "Two possible causes: 1) Called Buffer:_c(clear, copy) without colon, or 2) Type of 'clear' is not 'table'."
+
setmetatable(BHi, g)
)
+
for k in next, clear do clear[k] = nil end
+
local meta, copyMeta, copyType = getmetatable(self)
+
if copy then
+
if type(copy)=='table' then
+
for k, v in next, copy do clear[k] = type(v)=='table' and mw.clone(v) or v end
+
clear[rawset] = clear[rawset] or math.huge
+
copyMeta = getmetatable(copy)
+
if copyMeta then
+
if not buffHTMLmeta then meta():_inHTML() end
+
if copyMeta==buffHTMLmeta[mw.html] then
+
copyMeta = buffHTMLmeta
+
setmetatable(clear.nodes, meta)
+
end
+
end
+
elseif meta[type](copy) then clear[1] = copy end
+
 
end
 
end
setmetatable(clear, copyMeta or meta)
+
for k in next, nonSelf do--any HTML objects returned by these funcs will be granted Module:Buffer enhancements
return self
+
local func = mwFunc[k]
end,
+
BHi[k] = function(t, ...) local HTML = func(t, ...) return parent[HTML] and HTML or setmetatable(parent(HTML, t), buffHTML) end
_parent = function(self, s, outs, ...) return self:_(outs and self[next]:_str(s, outs, ...) or self[next](s)) end,
+
end
getParent = function(self, ...) return (assert(self, ' (:function(), not .function())')[next] or rawset(rawset(self, next, getmetatable(self)())[next], require, {self})):_(...) end,
+
do local function joinNode(HTML, sep)
_inHTML = function(self, ...)
+
local nodes, join = HTML.nodes
local HTML, meta = mw.html.create(...), getmetatable(self)
+
if noCache and rawkey[sep] or Valid(sep) then join, HTML.nodes = tostring(rawset(HTML, 'nodes', {MB.__call(nodes, sep)})), nodes end
if not rawget(_G, 'buffHTMLmeta') then
+
return join or tostring(HTML)
local mwHTMLmeta = getmetatable(mw.html.create())
+
end
buffHTMLmeta, mwHTMLfunc = rawset(mw.clone(mwHTMLmeta), mw.html, mwHTMLmeta), mwHTMLmeta.__index
+
 
for k, v in next, {
 
for k, v in next, {
[rawget] = function(HTML, Buffer, ...)
+
getParent = function(HTML, ...) lastHTML = HTML return MBi.getParent(HTML:allDone(), ...) end,--return to Buffer that created the HTML tree
buffHTMLmeta[require] = HTML
+
getBuffer = function(HTML, ...) lastHTML = HTML return isMBfunc(lastBuffer, ...) end,--return to last used
if ... then return select('#', ...)==1 and Buffer:_(...) or assert(Buffer[...], ('" %s " does not match any Module:Buffer function'):format(tostring(...)))(Buffer, select(2, ...)) end
+
killParent = function(HTML, ...) MBi.killParent(HTML:allDone(), ...) return HTML end,
return Buffer
+
_out = function(HTML, ...)
 +
if ...==0 then MBi._out(HTML.nodes, ...) return HTML end
 +
lastHTML, HTML = HTML, HTML:allDone()
 +
local n, ops, seps = select('#', ...)
 +
if n > 1 then
 +
local ops, seps = MBselect(n, ...)
 +
return parent[HTML]:_in(joinNode(HTML, rawget(seps, 0))):_out(ops, rawset(seps, buffHTML, true))
 +
end
 +
return parent[HTML]:_(joinNode(HTML, ...))
 
end,
 
end,
getParent = function(HTML, ...) return HTML[rawget](HTML, HTML:allDone()[next], ...) end,--return to buffer that created it
+
_str = function(HTML, ...)--does not set lastHTML
getBuffer = function(HTML, ...) return HTML[rawget](HTML, buffHTMLmeta[next], ...) end,--return to last buffer used
+
if ...==0 then return joinNode(HTML, select(2, ...)) end--passing 0 strings without calling allDone()
_out = function(HTML, ...) return HTML:getParent():_(HTML):_out(...) end,
+
local HTML, n = HTML:allDone(), select('#', ...)
_inv = function(HTML, s, outs, ...) return HTML.nodes:_(outs and HTML:getParent():_str(s, outs, ...) or HTML:getParent()(s)) end
+
if n > 1 then
} do buffHTMLmeta.__index[k] = v end
+
local ops, seps = MBselect(n, ...)
if new_G then
+
return parent[HTML]:_in(joinNode(HTML, rawget(seps, 1))):_str(ops, rawset(seps, buffHTML, true))
for k, v in pairs(gfuncs) do buffHTMLmeta.__index[k] = v end
+
end
gfuncs = nil
+
return joinNode(HTML, ...)
 +
end,
 +
_parent = function(HTML, ...) table.insert(HTML.nodes, parent[HTML:allDone()]:_str(...)) return HTML end
 +
} do BHi[k] = v end
 +
end
 +
do local htmlArg, skip, outFuncs = {parent=true,selfClosing=true,tagName=true}, {}
 +
do local out local function func(nodes, ...) return out(parent[nodes], ...) end
 +
outFuncs = setmetatable({
 +
tag = function(nodes, ...) return parent(setmetatable(init(nodes, ...), buffHTML), parent[nodes]) end,
 +
done = function(b, ops)
 +
b = parent[b]
 +
while b.parent and ops~=0 do b, ops = b.parent, ops and ops - 1 or 0 end
 +
return b
 +
end
 +
}, {__index = function(nodes, i)
 +
if rawget(BHi, i) then out = BHi[i] return func end--rawget to exclude globals
 +
end})
 
end
 
end
local mbpairs, outFuncs, htmlArg, nonSelf, spinach = meta.__pairs, {}, {parent = true, selfClosing = true, tagName = true}, {tag = true, done = true, allDone = true}
+
Element = {
spinach = {--spinach makes html.nodes buffer... get it?...
+
_add = function(nodes, t)
[rawset] = math.huge,
+
for k, v in MBpairs(t), t, skip[t] do (v~=true and MBmix or noOp)(nodes, v) end
_out = function(nodes, ...) return nodes:getParent('_', nodes:getHTML()):_out(...) end,
+
local HTML = parent[nodes] for k, v in MBpairs(t, false) do
_add = function(nodes, t)--children are unenhanced
+
local HTML, nodeFunc = nodes[next], buffHTMLmeta[require].nodes
+
for k, v in mbpairs(t) do buffHTMLmeta.__call(HTML, v~=true and v) end
+
for k, v in mbpairs(t, false) do
+
 
if htmlArg[k] then HTML[k] = v
 
if htmlArg[k] then HTML[k] = v
 
elseif v and v~=true then
 
elseif v and v~=true then
 
if nonSelf[k] then
 
if nonSelf[k] then
 
if k=='tag' then
 
if k=='tag' then
if tostring(v)==v then mwHTMLfunc.tag(HTML, v)
+
if type(v)=='table' then
elseif v[1] and v[1]~=true then
+
skip[v], k = 1, rawset(create(Valid(v[1])), 'parent', HTML)
local tag = mwHTMLfunc.tag(HTML, v[1])
+
Element._add(spec(parent(k.nodes, k, table.insert(nodes, k)), Element), v)
setmetatable(nodeFunc._add(rawset(setmetatable(tag.nodes, meta), next, tag), v), nil)[1], tag.nodes[next] = false
+
if k.selfClosing then k.nodes = nil else spec[k.nodes], parent[k.nodes] = nil end--free memory/reduce clutter; parent ref will auto-unset when k.nodes is nil
else nodeFunc._add(HTML.nodes, v) end
+
if not k.tagName then k.styles, k.attributes = nil end
else buffHTMLmeta.__call(mwHTMLfunc[k](HTML), v) end--k = done/allDone
+
else table.insert(nodes, create(v)) end
elseif mwHTMLfunc[k] then
+
elseif mwFunc[k] then
if k=='node' or type(v)~='table' then mwHTMLfunc[k](HTML, v)
+
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, skip[v] and v[1]).nodes, v)
 +
elseif v[1] or v[2] then
 +
k = MBi[k](nodes, unpack(v, 1, rawset(skip, v, k=='_B' and 1 or 2)[v]))
 +
Element._add(getmetatable(k) and rawget(k, 'nodes') or k, v)--if k is not a table, then v should not contain any extra keys or this may error.
 +
else MBi[k](nodes, v) end--k probably == '_G' or '_R'
 +
elseif mwFunc[k] then
 +
if type(v)~='table' or rawget(v, 'nodes') then mwFunc[k](HTML, v)
 
else
 
else
for _, y in mbpairs(v) do
+
local css = k=='css'
if y and y~=true then mwHTMLfunc[k](HTML, y) end
+
for x, y in MBpairs(v, true) do (y and y~=true and mwFunc[k] or noOp)(HTML, css and x:gsub('_', '-') or x, y) end--iterate non-numbers first
end
+
for _, y in MBpairs(v, nil) do (y and y~=true and mwFunc[k] or noOp)(HTML, y) end--don't bother with gsub since text must be quoted anyhow
for x, y in mbpairs(v, false) do
+
if y and y~=true then mwHTMLfunc[k](HTML, x, y) end
+
end
+
 
end
 
end
else nodeFunc[k](HTML.nodes, v) 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 (Element[k] or MBi[k])(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
 
end
Line 160: Line 208:
 
end
 
end
 
}
 
}
function makeNodesBuffer(HTML)
+
local tempMeta = {mode='v', copy={styles=true,attributes=true}}
local nodes = setmetatable(HTML.nodes, meta)
+
function tempMeta.__index(t, i) return tempMeta.copy[i] and rawset(t, i, MBi._cc(false, 0, t.orig[i]))[i] or t.orig[i] end
nodes[next], nodes.parent = HTML, HTML
+
rawkey[setmetatable(Element, {__index = outFuncs, __concat=function(Element, v) return setmetatable({nodes=spec({}, Element),orig=parent[v]}, tempMeta) end})] = math.huge
for k, v in next, buffHTMLmeta.__index do
+
end
nodes[k] = rawset(outFuncs, k, outFuncs[k] or function(nodes, ...) return v(nodes[next], ...) end)[k]
+
function MBi:getHTML(...)
end
+
lastBuffer = self
for k, v in next, spinach do nodes[k] = v end
+
if ... then
nodes._inHTML, nodes._build = nodes.tag
+
if select('#', ...)==1 then return not rawkey[s] and tostring(...):match'^_' and BHi[...] and BHi[...](lastHTML) or lastHTML(...)
 +
else return assert(BHi[...], ('" %s " does not match any mw.html or Buffer-mw.html function'):format(tostring(...)))(lastHTML, select(2, ...)) end
 
end
 
end
for k in next, nonSelf do--any HTML objects returned by these funcs will be granted Module:Buffer enhancements
+
return lastHTML
local func = mwHTMLfunc[k]
+
end
buffHTMLmeta.__index[k] = function(t, ...)
+
function MBi:_html(...) return MBi._(self, lastHTML, select(spec[self]==Element and select('#', ...)==0 and 1 or 2, true, ...)) end
local HTML = func(t, ...)
+
return init(...)
if not (HTML.selfClosing or HTML[next]) then makeNodesBuffer(setmetatable(rawset(HTML, next, t), buffHTMLmeta)) end
+
end
return HTML
+
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)--set after 'args' table processed by :_add
 +
return HTML
 +
end
 +
end
 +
local _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 = {__tostring = function(a, b) return a.a[0] 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}, {
 +
__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[i] 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 v, c = ...
 +
if v or select('#', ...)==0 then
 +
if v and not c then return prev end
 +
local meta, c = select(v and 1 or 3, alt, c, same, 0)
 +
return setmetatable({a = prev, _build = meta.__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 v, [type(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 shift(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({stream = _stream,
 +
_inHTML = _inHTML,
 +
_var = _var,
 +
_ = function(self, v, ...)
 +
local at, raw = 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 self
 +
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
 
end
 
end
for k, v in next, {--add new Buffer object functions
+
self.last_concat = nil
getHTML = function(self, ...)
+
buffHTMLmeta[next] = 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 end
+
 
end
 
end
makeNodesBuffer(setmetatable(HTML, buffHTMLmeta))
+
return self
rawset(setmetatable(HTML, rawset(buffHTMLmeta, require, HTML)), next, self)
+
return self:getHTML()
+
 
end,
 
end,
_build = function (self, nodes) return table.insert(nodes, self()) end--for compatibility with regular mw.html objects
+
_all = function(self, t, valKey)
},
+
for k, v in MBpairs(t) do MBmix(self, v, valKey) end
__call = function(t, ...)
+
for k, v in valKey and MBpairs(t, false) or noOp, t do
local t2 = t[rawset] and t[rawset] > #t and getmetatable(t)():_all(t) or t
+
if tonumber(v) then MBi._(self, k, v)--self not always a buffer
if select('#', ...)==0 then return rawset(t, 'last_concat', t.last_concat or table.concat(t2)).last_concat end
+
elseif rawget(MBi, k) and v and v~=true then
return table.concat(t2, ...) or ''
+
if v[1]==nil or getmetatable(v) then MBi[k](self, v)
end,
+
else MBi[k](self, unpack(v, 1, table.maxn(v))) end
__tostring = function(t, ...)
+
end
if type(t)~='table' then return t end
+
local n, r = select('#', ...), getmetatable(t)
+
if n > 1 then
+
local k, outs, seps = 2, r[select](n, ...)
+
r = r(t(seps[1]))
+
while outs > 1 do
+
r:_(t[next](seps[k]), 1)
+
t, k = t[next], k + 1
+
outs = t[next] and outs - 1 or 0
+
 
end
 
end
return table.concat(r, seps[k]) or ''
+
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
 
end
return t(...)
+
end})
end,
+
end
__concat = function(a, b, Buffer)
+
 
if Buffer then return error end
+
function MBmix(t, v, ...) return v and ((type(v)~='table' or getmetatable(v)) and MBi._(t, v) or (select('#', ...)==0 and spec[t] and spec[t]._add or MBi._all)(t, v, ...)) or t end--:_all always passes two args
Buffer = getmetatable(a)
+
 
local us, them = pcall(Buffer and Buffer.__concat, false, next, 1)
+
local _G, new_G = _G--localize _G for console testing (console _G ~= module _G)
if not us or them~=error then Buffer = getmetatable(b) end
+
return setmetatable({__index = function(t, i) return spec[t] and spec[t][i] or MBi[i] end,
return Buffer():_all{a, b}()
+
__call = function(t, ...)
end,
+
local rawsep, sep, i, j, raw = noCache and rawkey[...] and ..., ...
__pairs = function(t, ...)--pairs always iterates in order
+
if i or j or rawsep or Valid(sep) then
if not map_i then
+
raw, sep, i, j = rawkey[spec[t]] or rawkey[t], rawsep or Valid(sep), i and (i~=tonumber(i) and i + #t or i), j and (j~=tonumber(j) and j + #t or j)
local mk = {__mode='k'}
+
if rawsep or raw and (raw>=(j or #t) or i < 1) then
map_i, map_n, map_o = setmetatable({}, mk), setmetatable({}, mk), setmetatable({}, mk)--reduces table lookups
+
raw, i, j = {}, i and math.floor(i), j and math.floor(j)--floor for consistency with table.concat(t, sep, i, j), which ignores decimals
function mBuffIter(t, i) i = map_i[t][nil==i and 1 or map_n[t][i]] return i, t[i] end
+
raw.lc, t.last_concat = t.last_concat--temporarily unset last_concat to prevent disqualification from mapless iteration
function mBuffOther(t, i) i = map_o[t][nil==i and 1 or map_o[t][i]] return i, t[i] end
+
for k, v in MBpairs(t) do
function mBuffMap(t)
+
if raw[1] or not i or k>=i then if j and k > j then break end
local ti, tn, to, n = {}, {}, {}, #t--reduces table lookups
+
if raw.s then raw.s = table.insert(raw, tostring(sep)) end--if sep contains v and v is a Buffer-variable, sep must be strung before v
map_i[t], map_n[t], map_o[t] = ti, tn, to
+
k = Valid(v) if k then
for k = 1, n do
+
raw.s = rawsep or sep and raw[1] and table.insert(raw, sep)
table.insert(ti, k)
+
table.insert(raw, k)
tn[k] = k + 1
+
end
+
if n==0 or next(t, n)~=nil then
+
local start = next(t)==1 and n or nil
+
for k, v in next, t, start do
+
if tn[k] then--no op, since: t={[2]=1,[1]=2}; #t => 2; next(t,#t) => 1,2
+
elseif tonumber(k)==k then table.insert(ti, k)
+
elseif k~=t and v~=t then
+
table.insert(to, k)
+
to[k] = #to + 1
+
 
end
 
end
end
 
if #ti~=n then
 
table.sort(ti)
 
for k, v in next, ti do tn[v] = k + 1 end
 
 
end
 
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
 
end
 +
return table.concat(t, sep, i and math.max(i, 1), j and math.min(j, #t))
 
end
 
end
if select('#', ...)==0 or ...==true then mBuffMap(t) end
+
return MB.__tostring(t)
return ...==nil and mBuffIter or mBuffOther, t
+
 
end,
 
end,
__ipairs = pairs
+
__tostring = function(t)
},{__tostring = function()return''end,
+
if t.last_concat then return t.last_concat end
__call = function(self, ...)
+
local r = rawkey[spec[t]] or rawkey[t]
if not rawget(_G, 'libFunc') then--adds string and mw libraries; also add global functions if passed; rawget avoids error when reading nil global
+
r = table.concat(r and r>=#t and MBi._all({}, t) or t)
rawset(_G, 'libFunc', {__call = function(t, self, ...) return t[1](type(self)~='table' and self or self:_str(), ...) end})
+
return (noCache or rawset(t, 'last_concat', r)) and r
for _, library in ipairs{mw.text, string, mw.ustring} do
+
end,
for k, func in pairs(library) do self.__index[library==mw.ustring and k .. '_' or k] = setmetatable({func}, libFunc) end
+
__concat = function(a, b)
end
+
if buffHTML then
rawset(_G, 'new_G', ... and type(...)=='table' and (...)._G==... and ...~=_G and (...).mw and (...).mw.log==mw.log and ...)
+
for k = 1, 2 do local v = select(k, a, b)--faster than for k, v in pairs{a, b} do
if new_G then
+
if v and spec[v] and spec[v]==Element then
rawset(_G, 'gfuncs', {--stored as global for Buffer:_inHTML
+
if parent[v].selfClosing then
_G = function(self, name, var)
+
if rawequal(a, b) then return (not noCache or parent[v].tagName) and v:_str(0):rep(2) or v:_str(0)..v:_str(0) end--rawequal avoids premature tostring of Buffer:_var objects;
if rawget(new_G, name) then
+
b, a = select(k, b, parent[v], a)
local metaG = getmetatable(new_G)
+
else local temp = Element .. v --helper method; returns a mirror of parent[v]
if not metaG then
+
MBmix(MBmix(parent(temp.nodes, temp), a), k==1 and spec[b]==Element and parent[b] or b)
metaG = {__index = {}}
+
return buffHTML.__tostring(setmetatable(temp, {__index=parent[v], __mode='v'}))--switch from tempMeta to avoid MBi._cc op of styles/attributes
setmetatable(new_G, metaG)
+
end
end
+
end
metaG.__index[name] = new_G[name]
+
end
+
new_G[name] = var or self
+
return self
+
end,
+
_R = function(self, name, var) new_G[name] = var return self end,
+
_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
 
end
 
end
if new_G and ... and (...)==new_G then--avoid error when passing _G to buffer a second time
+
return table.concat(MBmix(MBmix({}, a), b))
local new, name = setmetatable({}, self), select(2, ...)
+
end,
if name and type(name)=='string' then new_G[name] = new end
+
__pairs = MBpairs,
return new
+
__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(...)=='table' and (...)._G==...) then
 +
local Nil, mG = {}, (...):getmetatable() or (...):setmetatable{}:getmetatable()
 +
new_G, _G, gfuncs = ..., ..., {--gfuncs stored for Buffer:_inHTML; new_G is a is a Module:Buffer local declared just before the final return statement.
 +
_G = 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
 +
local mG = getmetatable(new_G) or {__call=mG.__call}
 +
if mG.__index then pcall(rawset, 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,
 +
_R = function(self, 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 new_G[...]~=nil then return new_G[...] end--higher priority so Buffer:_G('new_G', ...) can prevent an overwrite
 +
if ...=='new_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[k] = v end  
 +
setmetatable(Nil,{__concat=MB.__concat,__newindex=noOp,__call=noOp,__tostring=noOp,__metatable=MB,__index=setmetatable({_B=MBi._B,_=function()return Nil 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
 
end
return setmetatable({}, self):_(...)
+
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,
 
end,
 
__index = function(t, i)
 
__index = function(t, i)
return function(...) return t.__index[i](t(), select(2,...)) end
+
MB = MB or t return MBi[i] and function(...) return MBi[i](setmetatable({}, t), select(...==t and 2 or 1,...)) end
 
end
 
end
 
})
 
})

Revision as of 16:58, 29 April 2015

Documentation for this module may be created at Module:Buffer/doc

--[[=============================
This Module was written by Alexander Zhikun He, also known as, User:Codehydro on 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/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License

https://en.wikipedia.org/wiki/Module:Buffer
https://en.wikipedia.org/wiki/User:Codehydro
=============================--]]
local function Valid(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()
		if str~='' then return str end--numbers are coerced to string per table.concat op; appending in string form saves ops on repeat concat
	end
end
local noOp, MBpairs = function()end do local iMap, vMap, oMap, pIter, pOther, pFast, Next--Map
	local function init()--init = noOp after first run
		function Next(t) return next, t end--slightly faster to do this than to use select()
		function pIter(t, k) k = (iMap[t] or MBpairs(t, true) and iMap[t])[not k and 1 or vMap[t][k]] return k, t[k] end--don't use rawget; accepting unmapped tables does not measurably affect performance.
		function pOther(t, k) k = (oMap[t] or MBpairs(t, true) and oMap[t])[nil==k and 1 or vMap[t][k]] return k, t[k] end--comparison to nil because false is a valid key
		function pFast(t, k) 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, t[k] end--mapless iterator; almost as fast as native ipairs; slight performance penalty when length not cached
		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 = noOp, setmetatable({}, mk), setmetatable({}, mk), setmetatable({}, mk)--iMap is numeric keys, oMap is non-numeric keys, and vMap points to next key
	end
	function MBpairs(t, ...)--pairs always iterates in order
		local iter, ex = ...
		iter = iter==init()--nil
		if iter and not oMap[t] and ex==nil and rawget(t, 1)~=nil and next(t, #t)==nil then--while possible to miss keys, more thorough check would negate the benefit of pFast
			vMap[t] = #t return pFast, t, nil
		elseif ... or not vMap[t] or select('#', ...)~=1 then
			local ti, tn, to, n = {}, {}, {}, #t--reduces table lookups
			iMap[t], vMap[t], oMap[t] = ti, tn, to
			for k = 1, n do ti[k], tn[k] = k, k + 1 end--stage one avoids number type checking op in stage two for most numeric keys
			for k in (ex or Next)(t) do
				if not tn[k] then table.insert(tonumber(k)~=k and to or ti, k) end
			end
			if #ti~=n then
				table.sort(ti)
				for k = 1, #ti do tn[ti[k]] = k + 1 end--somewhat wasteful, but trying to avoid overwriting can be even more expensive
			end
			for k = 1, #to do tn[to[k]] = k + 1 end
		end
		return iter and pIter or oMap[t] and pOther or noOp, t--noOp for mapless
	end
end
local parent, rawkey, spec do--new scope for variables not reused outside (reduces number of var names that need to checked outside of scope)
	local mkv = {__mode='kv', __call=function(t,k,v)t[k]=v return k end}--shared meta for Buffer parent property, raw mode, and specialized functions
	parent, rawkey, spec = setmetatable({}, mkv), setmetatable({}, mkv), setmetatable({}, mkv)--shared meta less memory
end

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
			local function each(self, ...)
				for k = 1, select('#', ...) do
					k = Valid(select(k, ...))--slightly faster than table.insert(self, (Valid(select(k, ...))))
					if k then table.insert(self, k) end
				end
				return self
			end
			init, stream, _stream = noOp, {
				__call = function(t, v) v = v and Valid(v) return v and table.insert(t, v) or t end,--last_concat cleared before entering stream mode
				__index = function(t, i) return i=='each' and each or MB.__index(t, i) and setmetatable(t, MB)[i] end,--no table look up minimizes resources to retrieve the only stream function
				__tostring = function(t) return setmetatable(t, MB)() end
			} for k, v in next, MB do stream[k] = stream[k] or v end
			setmetatable(stream, getmetatable(MB))
		end
		function _stream(self, ...) self.last_concat = init() return setmetatable(self, stream):each(...) end
	end
	local function isMBfunc(Buffer, s, ...)--helper for :getParent()-like methods (including 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, s) and MB.__index(Buffer, s)(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 function'):format(s))(Buffer, ...)--getParent is a one-way trip so one-time assert not expensive
			) or Buffer
	end
	local function MBselect(n, ...)--helper for :_out and :_str
		local n, seps = n - 1, {select(2, ...)}
		if type(seps[n])=='table' then 
			if buffHTML and rawget(seps[n], buffHTML) then return ... end
			setmetatable(seps, {__index = setmetatable(seps[n], {__index = function(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, mwFunc = mw.html.create do
				local mwHTMLmeta = getmetatable(create())
				buffHTML, mwFunc, _inHTML = setmetatable(mw.clone(mwHTMLmeta), getmetatable(MB)), mwHTMLmeta.__index--buffHTML declared near top of module; remove _inHTML from outer scope
				function init(nodes, ...)
					local name, args, tag = select(... and type(...)=='table' and 1 or 2, nil, ...)
					tag = create(Valid(name), args)
					if nodes then table.insert(nodes, tag.parent and tag or rawset(tag, 'parent', parent[nodes])) end
					if args then
						local a, b = args.selfClosing, args.parent
						args.selfClosing, args.parent = nil
						if next(args) then Element._add(parent(tag.nodes, tag), args) end
						args.selfClosing, args.parent = a, b--in case args is reused
					end
					return tag
				end
				for k, v in next, {[mw] = mwHTMLmeta,
					__call = function(h, v) return MBmix(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
			local nonSelf, BHi = {tag=true,done=true,allDone=true}, buffHTML.__index do local g
				g = {__index = function(t, i)
					if gfuncs and gfuncs[i] then g.__index, gfuncs = gfuncs return g.__index[i] end
				end}
				setmetatable(nonSelf, g)
				setmetatable(BHi, g)
			end
			for k in next, nonSelf do--any HTML objects returned by these funcs will be granted Module:Buffer enhancements
				local func = mwFunc[k]
				BHi[k] = function(t, ...) local HTML = func(t, ...) return parent[HTML] and HTML or setmetatable(parent(HTML, t), buffHTML) end
			end
			do local function joinNode(HTML, sep)
					local nodes, join = HTML.nodes
					if noCache and rawkey[sep] or Valid(sep) then join, HTML.nodes = tostring(rawset(HTML, 'nodes', {MB.__call(nodes, sep)})), nodes end
					return join or tostring(HTML)
				end
				for k, v in next, {
					getParent = function(HTML, ...) lastHTML = HTML return MBi.getParent(HTML:allDone(), ...) end,--return to Buffer that created the HTML tree
					getBuffer = function(HTML, ...) lastHTML = HTML return isMBfunc(lastBuffer, ...) end,--return to last used
					killParent = function(HTML, ...) MBi.killParent(HTML:allDone(), ...) return HTML end,
					_out = function(HTML, ...)
						if ...==0 then MBi._out(HTML.nodes, ...) return HTML end
						lastHTML, HTML = HTML, HTML:allDone()
						local n, ops, seps = select('#', ...)
						if n > 1 then
							local ops, seps = MBselect(n, ...)
							return parent[HTML]:_in(joinNode(HTML, rawget(seps, 0))):_out(ops, rawset(seps, buffHTML, true))
						end
						return parent[HTML]:_(joinNode(HTML, ...))
					end,
					_str = function(HTML, ...)--does not set lastHTML
						if ...==0 then return joinNode(HTML, select(2, ...)) end--passing 0 strings without calling allDone()
						local HTML, n = HTML:allDone(), select('#', ...)
						if n > 1 then
							local ops, seps = MBselect(n, ...)
							return parent[HTML]:_in(joinNode(HTML, rawget(seps, 1))):_str(ops, rawset(seps, buffHTML, true))
						end
						return joinNode(HTML, ...)
					end,
					_parent = function(HTML, ...) table.insert(HTML.nodes, parent[HTML:allDone()]:_str(...)) return HTML end
				} do BHi[k] = v end
			end
			do local htmlArg, skip, outFuncs = {parent=true,selfClosing=true,tagName=true}, {}
				do local out local function func(nodes, ...) return out(parent[nodes], ...) end
					outFuncs = setmetatable({
						tag = function(nodes, ...) return parent(setmetatable(init(nodes, ...), buffHTML), parent[nodes]) end,
						done = function(b, ops)
							b = parent[b] 
							while b.parent and ops~=0 do b, ops = b.parent, ops and ops - 1 or 0 end
							return b
						end
					}, {__index = function(nodes, i)
						if rawget(BHi, i) then out = BHi[i] return func end--rawget to exclude globals
					end})
				end
				Element = {
					_add = function(nodes, t)
						for k, v in MBpairs(t), t, skip[t] do (v~=true and MBmix or noOp)(nodes, v) end
						local HTML = parent[nodes] for k, v in MBpairs(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 type(v)=='table' then
											skip[v], k = 1, rawset(create(Valid(v[1])), 'parent', HTML)
											Element._add(spec(parent(k.nodes, k, table.insert(nodes, k)), Element), v)
											if k.selfClosing then k.nodes = nil else spec[k.nodes], parent[k.nodes] = nil end--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, skip[v] and v[1]).nodes, v)
									elseif v[1] or v[2] then
										k = MBi[k](nodes, unpack(v, 1, rawset(skip, v, k=='_B' and 1 or 2)[v]))
										Element._add(getmetatable(k) and rawget(k, 'nodes') or k, v)--if k is not a table, then v should not contain any extra keys or this may error.
									else MBi[k](nodes, v) end--k probably == '_G' or '_R'
								elseif mwFunc[k] then
									if type(v)~='table' or rawget(v, 'nodes') then mwFunc[k](HTML, v)
									else
										local css = k=='css'
										for x, y in MBpairs(v, true) do (y and y~=true and mwFunc[k] or noOp)(HTML, css and x:gsub('_', '-') or x, y) end--iterate non-numbers first
										for _, y in MBpairs(v, nil) do (y and y~=true and mwFunc[k] or noOp)(HTML, y) 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 (Element[k] or MBi[k])(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
						return nodes
					end
				}
				local tempMeta = {mode='v', copy={styles=true,attributes=true}}
				function tempMeta.__index(t, i) return tempMeta.copy[i] and rawset(t, i, MBi._cc(false, 0, t.orig[i]))[i] or t.orig[i] end
				rawkey[setmetatable(Element, {__index = outFuncs, __concat=function(Element, v) return setmetatable({nodes=spec({}, Element),orig=parent[v]}, tempMeta) end})] = math.huge
			end
			function MBi:getHTML(...)
				lastBuffer = self
				if ... then
					if select('#', ...)==1 then return not rawkey[s] and tostring(...):match'^_' and BHi[...] and BHi[...](lastHTML) or lastHTML(...)
					else return assert(BHi[...], ('" %s " does not match any mw.html or Buffer-mw.html function'):format(tostring(...)))(lastHTML, select(2, ...)) end
				end
				return lastHTML
			end
			function MBi:_html(...) return MBi._(self, lastHTML, 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)--set after 'args' table processed by :_add
			return HTML
		end
	end
	local _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 = {__tostring = function(a, b) return a.a[0] 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}, {
				__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[i] 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 v, c = ...
				if v or select('#', ...)==0 then
					if v and not c then return prev end
					local meta, c = select(v and 1 or 3, alt, c, same, 0)
					return setmetatable({a = prev, _build = meta.__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 v, [type(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 shift(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({stream = _stream,
		_inHTML = _inHTML,
		_var = _var,
		_ = function(self, v, ...)
			local at, raw = 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 self
		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
				self.last_concat = nil
			end
			return self
		end,
		_all = function(self, t, valKey)
			for k, v in MBpairs(t) do MBmix(self, v, valKey) end
			for k, v in valKey and MBpairs(t, false) or noOp, t do
				if tonumber(v) then MBi._(self, k, v)--self not always a buffer
				elseif rawget(MBi, k) and v and v~=true then
					if v[1]==nil or getmetatable(v) then MBi[k](self, v)
					else MBi[k](self, unpack(v, 1, table.maxn(v))) end
				end
			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
	end})
end

function MBmix(t, v, ...) return v and ((type(v)~='table' or getmetatable(v)) and MBi._(t, v) or (select('#', ...)==0 and spec[t] and spec[t]._add or MBi._all)(t, v, ...)) or t end--:_all always passes two args

local _G, new_G = _G--localize _G for console testing (console _G ~= module _G)
return setmetatable({__index = function(t, i) return spec[t] and spec[t][i] or MBi[i] end,
	__call = function(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 = rawkey[spec[t]] or rawkey[t], rawsep or Valid(sep), i and (i~=tonumber(i) and i + #t or i), j and (j~=tonumber(j) and j + #t or j)
			if rawsep or raw and (raw>=(j or #t) or i < 1) then
				raw, i, j = {}, i and math.floor(i), j and math.floor(j)--floor for consistency with table.concat(t, sep, i, j), which ignores decimals
				raw.lc, t.last_concat = t.last_concat--temporarily unset last_concat to prevent disqualification from mapless iteration
				for k, v in MBpairs(t) do
					if raw[1] or not i or k>=i then if j and k > j then break end
						if raw.s then raw.s = table.insert(raw, tostring(sep)) end--if sep contains v and v is a Buffer-variable, sep must be strung before v
						k = Valid(v) if k then
							raw.s = rawsep or sep and raw[1] and table.insert(raw, sep)
							table.insert(raw, k)
						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
		return MB.__tostring(t)
	end,
	__tostring = function(t)
		if t.last_concat then return t.last_concat end
		local r = rawkey[spec[t]] or rawkey[t]
		r = table.concat(r and r>=#t and MBi._all({}, t) or t)
		return (noCache or rawset(t, 'last_concat', r)) and r
	end,
	__concat = function(a, b)
		if buffHTML then
			for k = 1, 2 do local v = select(k, a, b)--faster than for k, v in pairs{a, b} do
				if v and spec[v] and spec[v]==Element then
					if parent[v].selfClosing then
						if rawequal(a, b) then return (not noCache or parent[v].tagName) and v:_str(0):rep(2) or v:_str(0)..v:_str(0) end--rawequal avoids premature tostring of Buffer:_var objects;
						b, a = select(k, b, parent[v], a)
					else local temp = Element .. v --helper method; returns a mirror of parent[v]
						MBmix(MBmix(parent(temp.nodes, temp), a), k==1 and spec[b]==Element and parent[b] or b)
						return buffHTML.__tostring(setmetatable(temp, {__index=parent[v], __mode='v'}))--switch from tempMeta to avoid MBi._cc op of styles/attributes
					end
				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(...)=='table' and (...)._G==...) then
			local Nil, mG = {}, (...):getmetatable() or (...):setmetatable{}:getmetatable()
			new_G, _G, gfuncs = ..., ..., {--gfuncs stored for Buffer:_inHTML; new_G is a is a Module:Buffer local declared just before the final return statement.
				_G = 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
						local mG = getmetatable(new_G) or {__call=mG.__call}
						if mG.__index then pcall(rawset, 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,
				_R = function(self, 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 new_G[...]~=nil then return new_G[...] end--higher priority so Buffer:_G('new_G', ...) can prevent an overwrite
					if ...=='new_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[k] = v end 
			setmetatable(Nil,{__concat=MB.__concat,__newindex=noOp,__call=noOp,__tostring=noOp,__metatable=MB,__index=setmetatable({_B=MBi._B,_=function()return Nil 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
		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 MBi[i](setmetatable({}, t), select(...==t and 2 or 1,...)) end
	end
})