https://en.wikipedia.org/w/index.php?action=history&feed=atom&title=Module%3ABuffer%2Fsandbox Module:Buffer/sandbox - Revision history 2025-05-30T06:55:26Z Revision history for this page on the wiki MediaWiki 1.45.0-wmf.3 https://en.wikipedia.org/w/index.php?title=Module:Buffer/sandbox&diff=897782699&oldid=prev Neils51: sp 2019-05-19T10:30:00Z <p>sp</p> <table style="background-color: #fff; color: #202122;" data-mw="interface"> <col class="diff-marker" /> <col class="diff-content" /> <col class="diff-marker" /> <col class="diff-content" /> <tr class="diff-title" lang="en"> <td colspan="2" style="background-color: #fff; color: #202122; text-align: center;">← Previous revision</td> <td colspan="2" style="background-color: #fff; color: #202122; text-align: center;">Revision as of 10:30, 19 May 2019</td> </tr><tr> <td colspan="2" class="diff-lineno">Line 380:</td> <td colspan="2" class="diff-lineno">Line 380:</td> </tr> <tr> <td class="diff-marker"></td> <td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div> (Element[k] or MBi[k])(nodes, v) -- v is probably string-able object, or a table to be handled by :_all</div></td> <td class="diff-marker"></td> <td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div> (Element[k] or MBi[k])(nodes, v) -- v is probably string-able object, or a table to be handled by :_all</div></td> </tr> <tr> <td class="diff-marker"></td> <td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div> else (Element[k] or MBi[k])(nodes, unpack(v, 1, table.maxn(v)))</div></td> <td class="diff-marker"></td> <td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div> else (Element[k] or MBi[k])(nodes, unpack(v, 1, table.maxn(v)))</div></td> </tr> <tr> <td class="diff-marker" data-marker="−"></td> <td style="color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #ffe49c; vertical-align: top; white-space: pre-wrap;"><div> end -- v is <del style="font-weight: bold; text-decoration: none;">definately</del> a table</div></td> <td class="diff-marker" data-marker="+"></td> <td style="color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;"><div> end -- v is <ins style="font-weight: bold; text-decoration: none;">definitely</ins> a table</div></td> </tr> <tr> <td class="diff-marker"></td> <td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div> else mwFunc.css(HTML, k:gsub('_', '-', 1), tostring(v))</div></td> <td class="diff-marker"></td> <td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div> else mwFunc.css(HTML, k:gsub('_', '-', 1), tostring(v))</div></td> </tr> <tr> <td class="diff-marker"></td> <td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div> end -- oddly enough, :_add clocked its fastest runtime after adding auto-gsub as a feature</div></td> <td class="diff-marker"></td> <td style="background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;"><div> end -- oddly enough, :_add clocked its fastest runtime after adding auto-gsub as a feature</div></td> </tr> </table> Neils51 https://en.wikipedia.org/w/index.php?title=Module:Buffer/sandbox&diff=700922547&oldid=prev Mr. Stradivarius: see how far we can get using Vim regex and auto-indent 2016-01-21T12:23:13Z <p>see how far we can get using Vim regex and auto-indent</p> <a href="//en.wikipedia.org/w/index.php?title=Module:Buffer/sandbox&amp;diff=700922547&amp;oldid=700699429">Show changes</a> Mr. Stradivarius https://en.wikipedia.org/w/index.php?title=Module:Buffer/sandbox&diff=700699429&oldid=prev Mr. Stradivarius: start making the indentation slightly more sane 2016-01-20T02:27:48Z <p>start making the indentation slightly more sane</p> <a href="//en.wikipedia.org/w/index.php?title=Module:Buffer/sandbox&amp;diff=700699429&amp;oldid=700695172">Show changes</a> Mr. Stradivarius https://en.wikipedia.org/w/index.php?title=Module:Buffer/sandbox&diff=700695172&oldid=prev Mr. Stradivarius: create sandbox version of Module:Buffer 2016-01-20T01:51:03Z <p>create sandbox version of <a href="/wiki/Module:Buffer" title="Module:Buffer">Module:Buffer</a></p> <p><b>New page</b></p><div>--[[=============================<br /> This Module was written by Alexander Zhikun He, also known as, User:Codehydro on the English Wikipedia<br /> <br /> All methods were developed independently and any resemblance to other string buffer libraries would be coincidental.<br /> Furthermore, many methods will not work when compiled by standard Lua libraries as they depend on behaviors unique to<br /> the MediaMiki Scribunto mod, which, for example, has a getmetatable() method that always returns nil on non-tables.<br /> https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual<br /> <br /> Source code comments may be thin at some points because they are intended to be supplemented by the documentation page:<br /> https://en.wikipedia.org/wiki/Module:Buffer/doc<br /> <br /> Licensed under Creative Commons Attribution-ShareAlike 3.0 Unported License<br /> https://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License<br /> <br /> https://en.wikipedia.org/wiki/Module:Buffer<br /> https://en.wikipedia.org/wiki/User:Codehydro<br /> =============================--]]<br /> local function Valid(v)--type validation<br /> if v and v~=true then--reject nil/boolean; faster than 2 type() comparisons<br /> local str = tostring(v)--functions not filtered since unlikely passed by accident (Scribunto does not have userdata/thread types)<br /> if str~=v and str==&#039;table&#039; then return rawget(v, 1) and table.concat(v) end--tostring(string-type) returns same ref; same refs compare faster than type()<br /> if str~=&#039;&#039; then return str end--numbers are coerced to string per table.concat op; appending in string form saves ops on repeat concat<br /> end<br /> end<br /> local noOp, MBpairs = function()end do local iMap, vMap, oMap, pIter, pOther, pFast, Next--Map<br /> local function init()--init = noOp after first run<br /> function Next(t) return next, t end--slightly faster to do this than to use select()<br /> 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&#039;t use rawget; accepting unmapped tables does not measurably affect performance.<br /> 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<br /> function pFast(t, k) k = not k and 1 or k &lt; (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<br /> --k and k &lt; (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<br /> local mk = {__mode = &#039;k&#039;}--use mode &#039;k&#039;; found that mode &#039;kv&#039; sometimes garbage collects maps mid-loop (may not error because iterators auto re-map, but that&#039;s expensive)<br /> 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<br /> end<br /> function MBpairs(t, ...)--pairs always iterates in order<br /> local iter, ex = ...<br /> iter = iter==init()--nil<br /> 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<br /> vMap[t] = #t return pFast, t, nil<br /> elseif ... or not vMap[t] or select(&#039;#&#039;, ...)~=1 then<br /> local ti, tn, to, n = {}, {}, {}, #t--reduces table lookups<br /> iMap[t], vMap[t], oMap[t] = ti, tn, to<br /> 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<br /> for k in (ex or Next)(t) do<br /> if not tn[k] then table.insert(tonumber(k)~=k and to or ti, k) end<br /> end<br /> if #ti~=n then<br /> table.sort(ti)<br /> for k = 1, #ti do tn[ti[k]] = k + 1 end--somewhat wasteful, but trying to avoid overwriting can be even more expensive<br /> end<br /> for k = 1, #to do tn[to[k]] = k + 1 end<br /> end<br /> return iter and pIter or oMap[t] and pOther or noOp, t--noOp for mapless<br /> end<br /> end<br /> local parent, rawkey, spec do--new scope for variables not reused outside (reduces number of var names that need to checked outside of scope)<br /> local mkv = {__mode=&#039;kv&#039;, __call=function(t,k,v)t[k]=v return k end}--shared meta for Buffer parent property, raw mode, and specialized functions<br /> parent, rawkey, spec = setmetatable({}, mkv), setmetatable({}, mkv), setmetatable({}, mkv)--shared meta less memory<br /> end<br /> <br /> local MB, MBi, MBmix, buffHTML, gfuncs, noCache, Element do--minimize number of locals per scope to reduce time spent sifting through irrelevant variable names<br /> local _stream do local stream--keep stream near top of scope<br /> local function init(f)--init = noOp after first run<br /> local function each(self, ...)<br /> for k = 1, select(&#039;#&#039;, ...) do<br /> k = Valid(select(k, ...))--slightly faster than table.insert(self, (Valid(select(k, ...))))<br /> if k then table.insert(self, k) end<br /> end<br /> return self<br /> end<br /> init, stream, _stream = noOp, {<br /> __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<br /> __index = function(t, i) return i==&#039;each&#039; 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<br /> __tostring = function(t) return setmetatable(t, MB)() end<br /> } for k, v in next, MB do stream[k] = stream[k] or v end<br /> setmetatable(stream, getmetatable(MB))<br /> end<br /> function _stream(self, ...) self.last_concat = init() return setmetatable(self, stream):each(...) end<br /> end<br /> local function isMBfunc(Buffer, s, ...)--helper for :getParent()-like methods (including getBuffer which does not return a parent)<br /> return s and (select(&#039;#&#039;, ...)==0 and--eventually should figure out to make this work for :getHTML which is very similar<br /> (not rawkey[s] and tostring(s):match&#039;^_.*&#039; and MB.__index(Buffer, s) and MB.__index(Buffer, s)(Buffer) or MBmix(Buffer, s))--unprefixed function names append as a string<br /> or assert(MB.__index(Buffer, s), (&#039;&quot; %s &quot; does not match any available Module:Buffer function&#039;):format(s))(Buffer, ...)--getParent is a one-way trip so one-time assert not expensive<br /> ) or Buffer<br /> end<br /> local function MBselect(n, ...)--helper for :_out and :_str<br /> local n, seps = n - 1, {select(2, ...)}<br /> if type(seps[n])==&#039;table&#039; then <br /> if buffHTML and rawget(seps[n], buffHTML) then return ... end<br /> setmetatable(seps, {__index = setmetatable(seps[n], {__index = function(t) return rawget(t, 1) end})})[n] = nil<br /> end<br /> return ..., seps<br /> end<br /> local _inHTML do local lastBuffer, lastHTML<br /> local function init(...)--init replaced and new version called on return<br /> local create, mwFunc = mw.html.create do<br /> local mwHTMLmeta = getmetatable(create())<br /> buffHTML, mwFunc, _inHTML = setmetatable(mw.clone(mwHTMLmeta), getmetatable(MB)), mwHTMLmeta.__index--buffHTML declared near top of module; remove _inHTML from outer scope<br /> function init(nodes, ...)<br /> local name, args, tag = select(... and type(...)==&#039;table&#039; and 1 or 2, nil, ...)<br /> tag = create(Valid(name), args)<br /> if nodes then table.insert(nodes, tag.parent and tag or rawset(tag, &#039;parent&#039;, parent[nodes])) end<br /> if args then<br /> local a, b = args.selfClosing, args.parent<br /> args.selfClosing, args.parent = nil<br /> if next(args) then Element._add(parent(tag.nodes, tag), args) end<br /> args.selfClosing, args.parent = a, b--in case args is reused<br /> end<br /> return tag<br /> end<br /> for k, v in next, {[mw] = mwHTMLmeta,<br /> __call = function(h, v) return MBmix(spec[h.nodes] and h.nodes or spec(setmetatable(parent(h.nodes, h), MB), Element), v) end,<br /> __concat = false,--false means take from MB<br /> __eq = false<br /> } do buffHTML[k] = v or MB[k] end<br /> end<br /> local nonSelf, BHi = {tag=true,done=true,allDone=true}, buffHTML.__index do local g<br /> g = {__index = function(t, i)<br /> if gfuncs and gfuncs[i] then g.__index, gfuncs = gfuncs return g.__index[i] end<br /> end}<br /> setmetatable(nonSelf, g)<br /> setmetatable(BHi, g)<br /> end<br /> for k in next, nonSelf do--any HTML objects returned by these funcs will be granted Module:Buffer enhancements<br /> local func = mwFunc[k]<br /> BHi[k] = function(t, ...) local HTML = func(t, ...) return parent[HTML] and HTML or setmetatable(parent(HTML, t), buffHTML) end<br /> end<br /> do local function joinNode(HTML, sep)<br /> local nodes, join = HTML.nodes<br /> if noCache and rawkey[sep] or Valid(sep) then join, HTML.nodes = tostring(rawset(HTML, &#039;nodes&#039;, {MB.__call(nodes, sep)})), nodes end<br /> return join or tostring(HTML)<br /> end<br /> for k, v in next, {<br /> getParent = function(HTML, ...) lastHTML = HTML return MBi.getParent(HTML:allDone(), ...) end,--return to Buffer that created the HTML tree<br /> getBuffer = function(HTML, ...) lastHTML = HTML return isMBfunc(lastBuffer, ...) end,--return to last used<br /> killParent = function(HTML, ...) MBi.killParent(HTML:allDone(), ...) return HTML end,<br /> _out = function(HTML, ...)<br /> if ...==0 then MBi._out(HTML.nodes, ...) return HTML end<br /> lastHTML, HTML = HTML, HTML:allDone()<br /> local n, ops, seps = select(&#039;#&#039;, ...)<br /> if n &gt; 1 then<br /> local ops, seps = MBselect(n, ...)<br /> return parent[HTML]:_in(joinNode(HTML, rawget(seps, 0))):_out(ops, rawset(seps, buffHTML, true))<br /> end<br /> return parent[HTML]:_(joinNode(HTML, ...))<br /> end,<br /> _str = function(HTML, ...)--does not set lastHTML<br /> if ...==0 then return joinNode(HTML, select(2, ...)) end--passing 0 strings without calling allDone()<br /> local HTML, n = HTML:allDone(), select(&#039;#&#039;, ...)<br /> if n &gt; 1 then<br /> local ops, seps = MBselect(n, ...)<br /> return parent[HTML]:_in(joinNode(HTML, rawget(seps, 1))):_str(ops, rawset(seps, buffHTML, true))<br /> end<br /> return joinNode(HTML, ...)<br /> end,<br /> _parent = function(HTML, ...) table.insert(HTML.nodes, parent[HTML:allDone()]:_str(...)) return HTML end<br /> } do BHi[k] = v end<br /> end<br /> do local htmlArg, skip, outFuncs = {parent=true,selfClosing=true,tagName=true}, {}<br /> do local out local function func(nodes, ...) return out(parent[nodes], ...) end<br /> outFuncs = setmetatable({<br /> tag = function(nodes, ...) return parent(setmetatable(init(nodes, ...), buffHTML), parent[nodes]) end,<br /> done = function(b, ops)<br /> b = parent[b] <br /> while b.parent and ops~=0 do b, ops = b.parent, ops and ops - 1 or 0 end<br /> return b<br /> end<br /> }, {__index = function(nodes, i)<br /> if rawget(BHi, i) then out = BHi[i] return func end--rawget to exclude globals<br /> end})<br /> end<br /> Element = {<br /> _add = function(nodes, t)<br /> for k, v in MBpairs(t), t, skip[t] do (v~=true and MBmix or noOp)(nodes, v) end<br /> local HTML = parent[nodes] for k, v in MBpairs(t, false) do<br /> if htmlArg[k] then HTML[k] = v<br /> elseif v and v~=true then<br /> if nonSelf[k] then<br /> if k==&#039;tag&#039; then<br /> if type(v)==&#039;table&#039; then<br /> skip[v], k = 1, rawset(create(Valid(v[1])), &#039;parent&#039;, HTML)<br /> Element._add(spec(parent(k.nodes, k, table.insert(nodes, k)), Element), v)<br /> 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<br /> if not k.tagName then k.styles, k.attributes = nil end<br /> else table.insert(nodes, create(v)) end<br /> elseif mwFunc[k] then<br /> if k==&#039;done&#039; and tonumber(v)~=v and v[1] and tonumber(v[1])==v[1] then skip[v] = 1 end<br /> MBmix(outFuncs[k](nodes, skip[v] and v[1]).nodes, v)<br /> elseif v[1] or v[2] then<br /> k = MBi[k](nodes, unpack(v, 1, rawset(skip, v, k==&#039;_B&#039; and 1 or 2)[v]))<br /> Element._add(getmetatable(k) and rawget(k, &#039;nodes&#039;) or k, v)--if k is not a table, then v should not contain any extra keys or this may error.<br /> else MBi[k](nodes, v) end--k probably == &#039;_G&#039; or &#039;_R&#039;<br /> elseif mwFunc[k] then<br /> if type(v)~=&#039;table&#039; or rawget(v, &#039;nodes&#039;) then mwFunc[k](HTML, v)<br /> else<br /> local css = k==&#039;css&#039;<br /> for x, y in MBpairs(v, true) do (y and y~=true and mwFunc[k] or noOp)(HTML, css and x:gsub(&#039;_&#039;, &#039;-&#039;) or x, y) end--iterate non-numbers first<br /> for _, y in MBpairs(v, nil) do (y and y~=true and mwFunc[k] or noOp)(HTML, y) end--don&#039;t bother with gsub since text must be quoted anyhow<br /> end<br /> elseif rawget(Element, k) or rawget(MBi, k) then<br /> 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<br /> else (Element[k] or MBi[k])(nodes, unpack(v, 1, table.maxn(v))) end--v is definately a table<br /> else mwFunc.css(HTML, k:gsub(&#039;_&#039;, &#039;-&#039;, 1), tostring(v)) end--oddly enough, :_add clocked its fastest runtime after adding auto-gsub as a feature<br /> skip[v] = nil<br /> end<br /> end<br /> return nodes<br /> end<br /> }<br /> local tempMeta = {mode=&#039;v&#039;, copy={styles=true,attributes=true}}<br /> 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<br /> rawkey[setmetatable(Element, {__index = outFuncs, __concat=function(Element, v) return setmetatable({nodes=spec({}, Element),orig=parent[v]}, tempMeta) end})] = math.huge<br /> end<br /> function MBi:getHTML(...)<br /> lastBuffer = self<br /> if ... then<br /> if select(&#039;#&#039;, ...)==1 then return not rawkey[s] and tostring(...):match&#039;^_&#039; and BHi[...] and BHi[...](lastHTML) or lastHTML(...)<br /> else return assert(BHi[...], (&#039;&quot; %s &quot; does not match any mw.html or Buffer-mw.html function&#039;):format(tostring(...)))(lastHTML, select(2, ...)) end<br /> end<br /> return lastHTML<br /> end<br /> function MBi:_html(...) return MBi._(self, lastHTML, select(spec[self]==Element and select(&#039;#&#039;, ...)==0 and 1 or 2, true, ...)) end<br /> return init(...)<br /> end<br /> function _inHTML(self, ...)<br /> local HTML = init(nil, ...)<br /> if HTML.selfClosing and spec[self]==Element then self.last_concat = table.insert(self, HTML) return self end<br /> lastBuffer, lastHTML = self, setmetatable(parent(HTML, self), buffHTML)--set after &#039;args&#039; table processed by :_add<br /> return HTML<br /> end<br /> end<br /> local _var, unbuild do local prev, rebuild<br /> local function init(...)--init replaced before return<br /> local function pick(b, v) return b and table.insert(b, v) or v end<br /> 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<br /> 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}, {<br /> __index = {c = 1},<br /> __tostring = function(t) return t:_build() end,<br /> 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&#039;s contents<br /> number = function(a, b) return pick(b, c(a, true)) end,<br /> string = function(a, b) return pick(b, string.char(c(a))) end<br /> }, {__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}<br /> local function shift(t, c)<br /> t[0] = t[0] and t[0] + c or t:_build() and t[0] - t.c + c<br /> if t.table then t[0] = (t[0] - 1) % #t[1] + 1 end<br /> end<br /> function rebuild(...)<br /> local v, c = ...<br /> if v or select(&#039;#&#039;, ...)==0 then<br /> if v and not c then return prev end<br /> local meta, c = select(v and 1 or 3, alt, c, same, 0)<br /> return setmetatable({a = prev, _build = meta.__tostring, c = c}, meta)<br /> elseif v==nil then--no-op<br /> elseif c then shift(prev, c)--v == false<br /> else prev:_build() end<br /> end<br /> 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<br /> return init(...)<br /> end<br /> function unbuild(sep)<br /> for k, v in MBpairs(sep, nil) do<br /> k = getmetatable(v) if k and (k==build or k==alt) then shift(v.a or v, -v.c) end<br /> end<br /> end<br /> function _var(self, ...)<br /> local obj if ... and ...~=true then obj = init(...)<br /> elseif prev then<br /> if ...~=false then obj = rebuild(...)<br /> else rebuild(...) end<br /> end<br /> return obj and MBi._(self, obj, nil, true) or self<br /> end<br /> end<br /> local lib; MBi = setmetatable({stream = _stream,<br /> _inHTML = _inHTML,<br /> _var = _var,<br /> _ = function(self, v, ...)<br /> local at, raw = select(select(&#039;#&#039;, ...)==1 and ...==true and 1 or 2, nil, ...)<br /> if raw then rawkey[self] = math.huge else v = Valid(v) end<br /> if v or raw then<br /> 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 &#039;at&#039;<br /> at, self.last_concat = at and (tonumber(at)~=at and raw + at or at)<br /> table.insert(self, select(at and 1 or 2, at, v))<br /> if at and at &lt; 0 or raw and #self - raw &gt; 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<br /> 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)<br /> return self<br /> end,<br /> _nil = function(self, at, ...)<br /> if ...~=true and ...~=false then--faster than type(...) ~= &#039;boolean&#039;<br /> if not at or at==&#039;0&#039; then<br /> self[#self] = ... if ... then rawkey[self] = math.huge end<br /> else<br /> local n, v = tonumber(at), ...<br /> if n~=at then <br /> if n then n = #self + at<br /> elseif at~=true and select(&#039;#&#039;, ...)==0 then v, n = at, #self end<br /> end<br /> if n then <br /> if v==nil and n &gt; 0 then table.remove(self, n)<br /> else self[math.floor(n)], rawkey[self] = v, math.huge end--floor position for consistency with Table library<br /> end<br /> end<br /> self.last_concat = nil<br /> end<br /> return self<br /> end,<br /> _all = function(self, t, valKey)<br /> for k, v in MBpairs(t) do MBmix(self, v, valKey) end<br /> for k, v in valKey and MBpairs(t, false) or noOp, t do<br /> if tonumber(v) then MBi._(self, k, v)--self not always a buffer<br /> elseif rawget(MBi, k) and v and v~=true then<br /> if v[1]==nil or getmetatable(v) then MBi[k](self, v)<br /> else MBi[k](self, unpack(v, 1, table.maxn(v))) end<br /> end<br /> end<br /> return self<br /> end,<br /> _str = function(t, ...)<br /> local n = select(&#039;#&#039;, ...)<br /> if n &gt; 1 then<br /> local k, ops, seps, r = 2, MBselect(n, ...)<br /> r = MB(t(seps[1]))<br /> while parent[t] and ops &gt; 1 and r:_(parent[t](seps[k]), 1) do t, k, ops = parent[t], k + 1, ops - 1 end<br /> return table.concat(r, seps[k] or nil)<br /> end<br /> return MB.__call(t, ...)<br /> end,<br /> _in = function (self, ...) return parent(MB(...), self) end,<br /> _out = function(t, ...)<br /> 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*<br /> local n = select(&#039;#&#039;, ...)<br /> if n &gt; 1 then<br /> local k, ops, seps = 1, MBselect(n, ...)<br /> while parent[t] and ops &gt; 0 do t, k, ops = parent[t]:_(t(seps[k])), k + 1, ops - 1 end<br /> elseif parent[t] then return parent[t]:_(t(...)) end<br /> return t<br /> end,<br /> _cc = function(self, clear, copy, meta)<br /> if clear then<br /> 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<br /> elseif copy==true then copy = self end<br /> if clear~=0 then<br /> assert(type(clear)==&#039;table&#039;, debug.traceback(&#039;Buffer:_cc can only &quot;clear&quot; tables. Did you forget to call with a colon?&#039;, 2))--errors can be hard to trace without this<br /> for k in self and next or noOp, clear do rawset(clear, k, nil) end<br /> 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)<br /> if self==false or copy and type(copy)==&#039;table&#039; then--self==false means copy is a table (saves a type op for recursive calls)<br /> meta = meta or getmetatable(copy)<br /> if self and #copy &gt; 1 then--preserves length with empty strings; developed from studying http://www.lua.org/source/5.1/ltable.c.html <br /> local n, null, i, e = #copy, {}, math.ldexp(2, select(2, math.frexp(#copy)) - 2)<br /> e, spec[MBi._cc], parent[null] = i - 1, null, clear<br /> for k = 1, e do table.insert(clear, false) end<br /> while i&lt;=n do table.insert(clear, i, &#039;&#039;) i, null[i] = i + math.ldexp(2, select(2, math.frexp(n - i)) - 2), &#039;&#039; end<br /> for k = 1, e do rawset(clear, k, nil) end<br /> end<br /> for k, v in next, copy do rawset(clear, k, type(v)==&#039;table&#039; and MBi._cc(false, 0, v) or v) end<br /> elseif copy then rawset(clear, 1, (Valid(copy))) end<br /> rawkey[setmetatable(clear, meta)], parent[clear] = rawkey[copy], parent[copy]<br /> end<br /> return self and rawset(self, &#039;last_concat&#039;, nil) or clear<br /> end,<br /> _parent = function(self, ...) return parent[self] and MBi._(self, parent[self]:_str(...)) or self end,<br /> getParent = function(self, ...) return isMBfunc(parent[self] or parent[parent(self, setmetatable({}, MB))], ...) end,<br /> killParent = function(self, ...) return parent[self] and isMBfunc(parent[self], ...) and parent(self) or self end,<br /> _build = function(self, t) table.insert(t, self()) end,--for compatibility with mw.html:node()<br /> last_concat = false--prevent library check<br /> }, {__index = function(t, i)--import string, mw.text, and mw.ustring libraries on an as-needed basis<br /> local func = string[i] or mw.text[i] or mw.ustring[i] or type(i)==&#039;string&#039; and mw.ustring[i:match&#039;^u(.+)&#039;] if func then<br /> lib = lib or function (s, f, ...)<br /> 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<br /> return f(tostring(s), ...)--not using ternary/logical operators here to allow multiple return values<br /> end<br /> return rawset(t, i, i:match&#039;^u?gsub&#039; and function(self, p, r, ...)return lib(self, func, p, r or &#039;&#039;, ...)end--Why are ugsub/gsub special? because empty strings are against my religion!<br /> or function(self, ...)return lib(self, func, ...)end)[i]<br /> end<br /> end})<br /> end<br /> <br /> function MBmix(t, v, ...) return v and ((type(v)~=&#039;table&#039; or getmetatable(v)) and MBi._(t, v) or (select(&#039;#&#039;, ...)==0 and spec[t] and spec[t]._add or MBi._all)(t, v, ...)) or t end--:_all always passes two args<br /> <br /> local _G, new_G = _G--localize _G for console testing (console _G ~= module _G)<br /> return setmetatable({__index = function(t, i) return spec[t] and spec[t][i] or MBi[i] end,<br /> __call = function(t, ...)<br /> local rawsep, sep, i, j, raw = noCache and rawkey[...] and ..., ...<br /> if i or j or rawsep or Valid(sep) then<br /> 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)<br /> if rawsep or raw and (raw&gt;=(j or #t) or i &lt; 1) then<br /> 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<br /> raw.lc, t.last_concat = t.last_concat--temporarily unset last_concat to prevent disqualification from mapless iteration<br /> for k, v in MBpairs(t) do<br /> if raw[1] or not i or k&gt;=i then if j and k &gt; j then break end<br /> 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<br /> k = Valid(v) if k then<br /> raw.s = rawsep or sep and raw[1] and table.insert(raw, sep)<br /> table.insert(raw, k)<br /> end<br /> end<br /> end<br /> if rawsep and not raw.s then raw[#raw] = unbuild(sep) end--unbuild rawsep if final index in t was invalid<br /> t.last_concat = raw.lc return table.concat(raw)<br /> end<br /> return table.concat(t, sep, i and math.max(i, 1), j and math.min(j, #t))<br /> end<br /> return MB.__tostring(t)<br /> end,<br /> __tostring = function(t)<br /> if t.last_concat then return t.last_concat end<br /> local r = rawkey[spec[t]] or rawkey[t]<br /> r = table.concat(r and r&gt;=#t and MBi._all({}, t) or t)<br /> return (noCache or rawset(t, &#039;last_concat&#039;, r)) and r<br /> end,<br /> __concat = function(a, b)<br /> if buffHTML then<br /> for k = 1, 2 do local v = select(k, a, b)--faster than for k, v in pairs{a, b} do<br /> if v and spec[v] and spec[v]==Element then<br /> if parent[v].selfClosing then<br /> 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;<br /> b, a = select(k, b, parent[v], a)<br /> else local temp = Element .. v --helper method; returns a mirror of parent[v]<br /> MBmix(MBmix(parent(temp.nodes, temp), a), k==1 and spec[b]==Element and parent[b] or b)<br /> return buffHTML.__tostring(setmetatable(temp, {__index=parent[v], __mode=&#039;v&#039;}))--switch from tempMeta to avoid MBi._cc op of styles/attributes<br /> end<br /> end<br /> end<br /> end<br /> return table.concat(MBmix(MBmix({}, a), b))<br /> end,<br /> __pairs = MBpairs,<br /> __ipairs = MBpairs,<br /> __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)<br /> }, {__tostring = function()return&#039;&#039;end,<br /> __call = function(self, ...) MB = MB or self<br /> if new_G then if ... and _G and ...==_G then new_G = ... end<br /> elseif ... and (...==_G or type(...)==&#039;table&#039; and (...)._G==...) then<br /> local Nil, mG = {}, (...):getmetatable() or (...):setmetatable{}:getmetatable()<br /> 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.<br /> _G = function(self, i, ...)<br /> local X, save = rawget(new_G, i), select(&#039;#&#039;, ...)==0 and self or ...<br /> 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<br /> local mG = getmetatable(new_G) or {__call=mG.__call}<br /> if mG.__index then pcall(rawset, mG.__index, i, X)<br /> else mG.__index = setmetatable(new_G, mG) and {[i] = X} end<br /> end<br /> return self, ...--avoiding __eq with rawequal(self,save) is overkill since buffers can self-save without being passed as save<br /> end,<br /> _R = function(self, i, v, m)<br /> if i~=&#039;new_G&#039; then if i and i~=true then rawset(new_G, i , v) end<br /> 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})<br /> 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<br /> return self<br /> end,<br /> _2 = function(self, ...)<br /> if new_G[...]~=nil then return new_G[...] end--higher priority so Buffer:_G(&#039;new_G&#039;, ...) can prevent an overwrite<br /> if ...==&#039;new_G&#039; then return rawset((select(&#039;#&#039;, ...)~=1 and MBi._R(new_G, ...) or new_G), &#039;_G&#039;, _G) end<br /> return select(select(&#039;#&#039;, ...)==1 and 1 or 2, self:_G(...))--return only one value; &#039;return select(2, self:_G(...)) or self&#039; doesn&#039;t work for returning nil<br /> end,<br /> _B = function(self, v) return v or v==nil and Nil end<br /> } for k, v in next, gfuncs do MBi[k] = v end <br /> setmetatable(Nil,{__concat=MB.__concat,__newindex=noOp,__call=noOp,__tostring=noOp,__metatable=MB,__index=setmetatable({_B=MBi._B,_=function()return Nil end,last_concat=&#039;&#039;},<br /> {__index=function(t,i)return (MBi[i] or i and not tonumber(i)) and t._ or nil end})})<br /> function mG.__call(G, k, ...) return (k._G or G.type(k)==&#039;table&#039;) and (G.select(&#039;#&#039;, ...)~=1 and G.rawset(k, ...) or G:rawset(..., k) and k) or G:rawset(k, (...)) and ... end<br /> end<br /> local new = setmetatable({}, self)<br /> if ... and (...)==new_G then return select(2, ...) and MBmix(new:_G((select(2, ...))), select(3, ...)) or new end<br /> return ... and MBi._(new, ...) or new<br /> end,<br /> __index = function(t, i)<br /> MB = MB or t return MBi[i] and function(...) return MBi[i](setmetatable({}, t), select(...==t and 2 or 1,...)) end<br /> end<br /> })</div> Mr. Stradivarius