Module:ScriptUtil

local Template = { self = { getTitle = function(self) return self._title end, getArgs = function(self) return self._args end, getOrderedArgs = function(self) local keys = self._orderedKeys local orderedArgs = {} for i=1,#keys do				orderedArgs[#orderedArgs+1] = {keys[i], self:getArg(keys[i])} end return orderedArgs end, getArg = function(self,arg) local param = mw.text.trim(tostring(arg or "")) return self._args[param] end, setArg = function(self,arg,value) local param = mw.text.trim(tostring(arg or "")) if self._args[param] then for i=1,#self._orderedKeys do					if self._orderedKeys[i] == param then table.remove(self._orderedKeys, i)						break end end end self._orderedKeys[#self._orderedKeys+1] = param self._args[param] = mw.text.trim(value or "") end, removeArg = function(self,arg) local param = mw.text.trim(tostring(arg or "")) self._args[param] = nil for i=1,#self._orderedKeys do				if self._orderedKeys[i] == param then table.remove(self._orderedKeys,i) break end end end, toWikitext = function(self,useBlockFormatting) local wikitext = "" return wikitext end },	mt = { __tostring = nil } }

Template.mt.__tostring = Template.self.toWikitext

Template.new = function(title, templateArgs) local newObj = { _title = mw.text.trim(title), _args = {}, _orderedKeys = {} }	for k,v in pairs(Template.self) do		newObj[k] = v	end setmetatable(newObj, Template.mt) for k,v in pairs(templateArgs or {}) do		newObj:setArg(k,v) end return newObj end

local function byteSequenceToString(t,i,j) -- Destroys the input table for i=(i or 1),(j or #t) do		t[i] = string.char(t[i]) end return table.concat(t,nil,i,j) end

Template.newFromWikitext = function(wikitext) local MALFORMED_WIKITEXT_ERROR_MSG = "Malformed wikitext provided" local OPENING_BRACE = string.byte("{") local ENDING_BRACE = string.byte("}") local PIPE = string.byte("|") local EQUALS = string.byte("=") local title local templateArgs local i = 1 local stringBytes = {string.byte(wikitext,nil,#wikitext)} local braceCounter = 0 while i <= #stringBytes do -- Advance the index past the opening braces if stringBytes[i] == OPENING_BRACE then braceCounter = braceCounter + 1 else braceCounter = 0 end i = i + 1 if braceCounter == 2 then break end end if braceCounter ~= 2 or i > #stringBytes then error(MALFORMED_WIKITEXT_ERROR_MSG) end local h = i	while i <= #stringBytes do -- Move i to the first pipe character or ending brace, everything in between is the title if stringBytes[i] == PIPE or stringBytes[i] == ENDING_BRACE then title = byteSequenceToString(stringBytes,h,i-1) if stringBytes[i] == ENDING_BRACE then return Template.new(title) end break end i = i + 1 end assert(title, MALFORMED_WIKITEXT_ERROR_MSG) local newTemplate = Template.new(title) i = i + 1 h = i	local lastNumericParameter = 0 local argToAdd = nil braceCounter = 0 while i <= #stringBytes do		local isEndOfTemplate = braceCounter <= 0 and stringBytes[i] == ENDING_BRACE and stringBytes[i+1] == ENDING_BRACE and stringBytes[i+2] ~= ENDING_BRACE if (braceCounter <= 1 and stringBytes[i] == PIPE) or isEndOfTemplate then if argToAdd then newTemplate:setArg(argToAdd,byteSequenceToString(stringBytes,h,i-1)) argToAdd = nil else lastNumericParameter = lastNumericParameter + 1 newTemplate:setArg(lastNumericParameter,byteSequenceToString(stringBytes,h,i-1)) end if isEndOfTemplate then return newTemplate end h = i + 1 elseif (braceCounter <= 1 and stringBytes[i] == EQUALS) then argToAdd = byteSequenceToString(stringBytes,h,i-1) h = i + 1 elseif stringBytes[i] == OPENING_BRACE then braceCounter = braceCounter + 1 elseif stringBytes[i] == ENDING_BRACE then braceCounter = braceCounter - 1 end i = i + 1 end return error(MALFORMED_WIKITEXT_ERROR_MSG) end

return {Template = Template}