Module:Script-MBTL: Difference between revisions

From Mizuumi Wiki
Jump to navigation Jump to search
(adding missing keywords)
(adding more error correction because people can't follow the notation written in the combo legend)
Line 109: Line 109:
if word=="dl" then
if word=="dl" then
return Token:new(Type.PREFIX,"dl.")
return Token:new(Type.PREFIX,"dl.")
elseif word=="j" then
elseif word=="j" or word=="J" then
return Token:new(Type.PREFIX,"j.")
return Token:new(Type.PREFIX,"j.")
elseif word=="dj" then
elseif word=="dj" or word=="DJ" then
return Token:new(Type.PREFIX,"dj.")
return Token:new(Type.PREFIX,"dj.")
elseif word=="w" then
elseif word=="w" or word=="W" then
return Token:new(Type.PREFIX,"w.")
return Token:new(Type.PREFIX,"w.")
elseif word=="tk" then
elseif word=="tk" or word=="TK" then
return Token:new(Type.PREFIX,"tk.")
return Token:new(Type.PREFIX,"tk.")
elseif word=="md" or word=="microdash" then
elseif word=="md" or word=="microdash" then

Revision as of 10:17, 14 December 2023

Documentation for this module may be created at Module:Script-MBTL/doc

local p = {}

--custom script for the IS wiki, feel free to make a copy of it or take any part of the code for other wikis. ohoho I DID!!!!! 
--MBTL wiki fork

--split multiple properties passed as a single param to a template call and return the converted property text


--local objects used by p.gameInput()
local Type = {WORD=0, CONNECTOR=1, PREFIX=2, SUFFIX=3, SYMBOL=4, BUTTON=5, DIRECTION=6, SYS=7}

local Token = {
	ttype = nil,
	targ = nil,
	new = function(self,typ,arg)
		t = {}
		self.ttype = typ
		self.targ = arg
		setmetatable(t,self)
		self.__index = self
		return t
	end
}

local lexer = {
	buffer = "",
	readb = "",
	peek = " ",
	backup_buffer = "",
	backup_readb = "",
	newBuffer = function(self,buf)
		self.buffer = buf
	end,
	readch = function(self)
		if self.buffer:len() >= 1 then
			local r = self.buffer:sub(1,1)
			self.readb = self.readb .. r
			self.buffer = self.buffer:sub(2,-1)
			self.peek = r
		else
			self.peek = ":EOF:"
		end
	end,
	scan = function(self)
		while self.peek==" " or self.peek=="\t" or self.peek=="\n" or self.peek=="\r" do
			self:readch()
		end
		return self:cswitch()
	end,
	cswitch = function(self)
		if self.peek == ">" then 
			self.peek = " "
			return Token:new(Type.CONNECTOR," > ")
		elseif self.peek == "," then
			self.peek = " "
			return Token:new(Type.CONNECTOR," , ")
		elseif self.peek == "(" then
			self.peek = " "
			return Token:new(Type.SYMBOL,"(")
		elseif self.peek == ")" then
			self.peek = " "
			return Token:new(Type.SYMBOL,")")
		elseif self.peek == "[" then
			self.peek = " "
			return Token:new(Type.SYMBOL,"[")
		elseif self.peek == "]" then
			self.peek = " "
			return Token:new(Type.SYMBOL,"]")
		elseif self.peek == "{" then
			self.peek = " "
			return Token:new(Type.SYMBOL,"{")
		elseif self.peek == "}" then
			self.peek = " "
			return Token:new(Type.SYMBOL,"}")
		elseif self.peek == "~" then
			self.peek = " "
			return Token:new(Type.CONNECTOR,"~")
		elseif self.peek == "+" then
			self.peek = " "
			return Token:new(Type.CONNECTOR,"+")
		elseif self.peek == "/" then
			self.peek = " "
			return Token:new(Type.CONNECTOR,"/")
		elseif self.peek == ":EOF:" then
			self.peek = " "
			return Token:new(Type.SYS,"EOF")
		else
			if tonumber(self.peek) == nil then
				w = ""
				self.backup_buffer = self.buffer
				self.backup_readb = self.readb
				repeat
					w = w .. self.peek
					self:readch()
				until self.peek:match("%a")==nil or self.peek:match(":EOF:") --peek is not a letter or reached EOF
				if self.peek=="." then self.peek=" " end
				return self:wswitch(w)
			else
				d = ""
				repeat
					d = d .. self.peek
					self:readch()
				until tonumber(self.peek) == nil
				return Token:new(Type.DIRECTION,d)
			end
		end
	end,
	wswitch = function(self,word)
		if word=="dl" then
			return Token:new(Type.PREFIX,"dl.")
		elseif word=="j" or word=="J" then
			return Token:new(Type.PREFIX,"j.")
		elseif word=="dj" or word=="DJ" then
			return Token:new(Type.PREFIX,"dj.")
		elseif word=="w" or word=="W" then
			return Token:new(Type.PREFIX,"w.")
		elseif word=="tk" or word=="TK" then
			return Token:new(Type.PREFIX,"tk.")
		elseif word=="md" or word=="microdash" then
			return Token:new(Type.PREFIX,"md.")
		elseif word=="A" then
			return Token:new(Type.BUTTON,"A")
		elseif word=="B" then
			return Token:new(Type.BUTTON,"B")
		elseif word=="C" then
			return Token:new(Type.BUTTON,"C")
		elseif word=="D" then
			return Token:new(Type.BUTTON,"D")
		elseif word=="X" then
			return Token:new(Type.BUTTON,"X")
		elseif word=="jc" then
			return Token:new(Type.WORD,"jc")
		elseif word=="sj" or word=="superjump" then
			return Token:new(Type.WORD,"sj")
		elseif word=="djc" then
			return Token:new(Type.WORD,"djc")
		elseif word=="IAD" or word=="iad" then
			return Token:new(Type.WORD,"IAD")
		elseif word=="AT" or word=="at" then
			return Token:new(Type.WORD,"AT")
		elseif word=="OR" or word=="or" then
			return Token:new(Type.WORD," OR ")
		elseif word=="CH" or word=="ch" then
			return Token:new(Type.WORD,"CH ")
		elseif word=="FC" or word=="fc" then
			return Token:new(Type.WORD,"FC ")
		elseif word=="OTG" or word=="otg" then
			return Token:new(Type.WORD,"OTG ")
		elseif word=="MD" then
			return Token:new(Type.WORD,"MD")
		elseif word=="AD" or word=="ad" then
			return Token:new(Type.WORD,"AD")
		elseif word=="LA" or word=="la" then
			return Token:new(Type.WORD,"LA")
		elseif word=="RB" or word=="rb" then
			if self.peek=="1" then
				self.peek = " "
				return Token:new(Type.WORD,"RB1")
			elseif self.peek=="2" then
				self.peek = " "
				return Token:new(Type.WORD,"RB2")
			else return nil
			end
		elseif word=="Heat" or word=="heat" then
			return Token:new(Type.WORD,"Heat")
		elseif word=="Dash" or word=="dash" then
			return Token:new(Type.WORD,"Dash")
		elseif word=="Airdash" or word=="airdash" then
			return Token:new(Type.WORD,"Airdash")
		elseif word=="Throw" or word=="throw" then
			return Token:new(Type.WORD,"Throw")
		elseif word=="Sideswap" or word=="sideswap" then
			return Token:new(Type.WORD,"Sideswap")
		elseif word=="Jump" or word=="jump" then
			return Token:new(Type.WORD,"Jump")
		elseif word=="Rebeat" or word=="rebeat" then
			return Token:new(Type.WORD,"Rebeat")
		elseif word=="Starter" or word=="starter" then
			return Token:new(Type.WORD,"Starter")
		elseif word=="Ender" or word=="ender" then
			return Token:new(Type.WORD,"Ender")
		else
			self.buffer = self.backup_buffer
			self.readb = self.backup_readb
			return self:wswitch(word:sub(1,1))
		end
	end,
}

local color = {
	["A"] = "#e2258a",
	["B"] = "#ff7606",
	["C"] = "#36b270",
	["D"] = "#1e8bcb",
	["X"] = "#686868",
}

local parser = {
	v = {COMBOLIST=0, COMBOLISTP=1, COMBO=2, INPUTLIST=3, INPUTLISTP=4, INPUTN=5, INPUT=6, BLOCKLIST=7, BLOCKLISTP=8, BLOCK=9, UCPREFIXLIST=10, UCPREFIX=11, PREFIXLIST=12, PREFIX=13, DIR=14, BTNSLIST=15, BTNSLISTP=16, BTNS=17, SUFFIXLIST=18, SUFFIX=19, BTN=20, BTNP=21},
	look = Token:new(Type.SYS,"START"),
	
	move = function(self)
		self.look = lexer:scan()
	end,
	
	formatElem = function(self,t,clr)
		if clr then
			return "<span style=\"color:" .. clr .. ";\">" .. (t.targ or t) .. "</span>" --works because the or is lazy so if t is a token it will not return t even if it's technically not nil because t.targ comes first
		else
			return t.targ or t
		end
	end,
	
	start = function(self)
		self:move()
		return (self:combolist()) --only return the line parameter
	end,
	
	combolist = function(self)
		if self:first(self.v.COMBOLIST) then
			local c_line = self:combo()
			local cp_line = self:combolistp()
			if self.look.targ~="EOF" then error("illegal token: " .. self.look.targ) end
			return c_line..cp_line
		else error("illegal starting token") end
	end,
	
	combolistp = function(self)
		if self:first(self.v.COMBOLISTP) then
			local c_line = self:combo()
			local cp_line = self:combolistp()
			return c_line..cp_line
		elseif not self:follow(self.v.COMBOLISTP) then return error("illegal token: " .. self.look.targ)
		else return "" end
	end,
	
	combo = function(self)
		if self:first(self.v.COMBO) then
			local ret_line
			if self.look.targ=="(" then
				ret_line = self:formatElem(self.look)
				self:move()
				ret_line = ret_line .. self:inputlist()
				ret_line = ret_line .. self:combolistp()
				if self.look.targ==")" then
					ret_line = ret_line .. self:formatElem(self.look)
					self:move()
				else error("illegal token: " .. self.look.targ) end
			else ret_line = self:inputlist() end
			return ret_line
		else error("illegal token: " .. self.look.targ) end
	end,
	
	inputlist = function(self)
		if self:first(self.v.INPUTLIST) then
			local ret_line
			ret_line = self:input()
			ret_line = ret_line .. self:inputlistp()
			return ret_line
		else error("illegal token: " .. self.look.targ) end
	end,
	
	inputlistp = function(self)
		if self:first(self.v.INPUTLISTP) then
			local ret_line
			ret_line = self:formatElem(self.look)
			self:move()
			ret_line = ret_line .. self:inputn()
			ret_line = ret_line .. self:inputlistp()
			return ret_line
		elseif not self:follow(self.v.INPUTLISTP) then return error("illegal token: " .. self.look.targ)
		else return "" end
	end,
	
	inputn = function(self)
		if self:first(self.v.INPUTN) then
			return (self:input())
		elseif not self:follow(self.v.INPUTN) then return error("illegal token: " .. self.look.targ)
		else return "" end
	end,
	
	input = function(self)
		if self:first(self.v.INPUT) then
			local ret_line
			if self.look.ttype==Type.WORD then
				ret_line = self:formatElem(self.look)
				self:move()
			else ret_line = self:blocklist() end
			return ret_line
		else error("illegal token: " .. self.look.targ) end
	end,
	
	blocklist = function(self)
		if self:first(self.v.BLOCKLIST) then
			local ret_line
			ret_line = self:block()
			ret_line = ret_line .. self:blocklistp()
			return ret_line
		else error("illegal token: " .. self.look.targ) end
	end,
	
	blocklistp = function(self)
		if self:first(self.v.BLOCKLISTP) then
			local ret_line
			ret_line = self:formatElem(self.look)
			self:move()
			ret_line = ret_line .. self:block()
			ret_line = ret_line .. self:blocklistp()
			return ret_line
		elseif not self:follow(self.v.BLOCKLISTP) then return error("illegal token: " .. self.look.targ)
		else return "" end
	end,
	
	block = function(self)
		if self:first(self.v.BLOCK) then
			local ret_line
			local up = self:ucprefixlist()
			local pf = self:prefixlist()
			local bl_line, bl_button = self:btnslist()
			ret_line = self:formatElem(up) .. self:formatElem(pf,bl_button) .. bl_line
			return ret_line
		else error("illegal token: " .. self.look.targ) end
	end,
	
	ucprefixlist = function(self)
		if self:first(self.v.UCPREFIXLIST) then
			local ret_line
			ret_line = self:ucprefix()
			ret_line = ret_line .. self:ucprefixlist()
			return ret_line
		else return "" end
	end,
	
	ucprefix = function(self)
		if self:first(self.v.UCPREFIX) then
			local u = self.look.targ
			self:move()
			return u
		else return "" end
	end,
	
	prefixlist = function(self)
		if self:first(self.v.PREFIXLIST) then
			local ret_line
			ret_line = self:prefix()
			ret_line = ret_line .. self:prefixlist()
			return ret_line
		elseif not self:follow(self.v.PREFIXLIST) then return error("illegal token: " .. self.look.targ)
		else return "" end
	end,
	
	prefix = function(self)
		if self:first(self.v.PREFIX) then
			local p = self.look.targ
			self:move()
			return p
		else error("illegal token: " .. self.look.targ) end
	end,
	
	dir = function(self)
		if self:first(self.v.DIR) then
			local d = self.look.targ
			self:move()
			return d
		elseif not self:follow(self.v.DIR) then return error("illegal token: " .. self.look.targ)
		else return "" end
	end,
	
	btnslist = function(self)
		if self:first(self.v.BTNSLIST) then
			local ret_line, ret_button
			ret_line, ret_button = self:btns()
			ret_line = ret_line .. self:btnslistp()
			return ret_line, ret_button
		else error("illegal token: " .. self.look.targ) end
	end,
	
	btnslistp = function(self)
		if self:first(self.v.BTNSLISTP) then
			local ret_line
			ret_line = self:formatElem(self.look)
			self:move()
			ret_line = ret_line .. self:btns()
			ret_line = ret_line .. self:btnslistp()
			return ret_line
		elseif not self:follow(self.v.BTNSLISTP) then return error("illegal token: " .. self.look.targ)
		else return "" end
	end,
	
	btns = function(self)
		if self:first(self.v.BTNS) then
			local dr = self:dir()
			local b_line, b_button = self:btn()
			b_line = self:formatElem(dr,b_button) .. b_line .. self:suffixlist(b_button)
			return b_line, b_button
		else error("illegal token: " .. self.look.targ) end
	end,
	
	suffixlist = function(self,mainb)
		if self:first(self.v.SUFFIXLIST) then
			local ret_line
			ret_line = self:suffix(mainb)
			ret_line = ret_line .. self:suffixlist(mainb)
			return ret_line
		elseif not self:follow(self.v.SUFFIXLIST) then return error("illegal token: " .. self.look.targ)
		else return "" end
	end,
	
	suffix = function(self,mainb)
		if self:first(self.v.SUFFIX) then
			local ret_line
			ret_line = self:formatElem(self.look, mainb)
			self:move()
			local sufbody = ""
			if self.look.targ=="w." then
				sufbody = self:formatElem("w",mainb)
				self:move()
			else 
				sufbody = self:formatElem(self:dir(),mainb)
			end
			ret_line = ret_line .. sufbody
			if self.look.targ==")" then
				ret_line = ret_line .. self:formatElem(self.look,mainb)
				self:move()
				return ret_line
			else error("illegal token: " .. self.look.targ) end
		else error("illegal token: " .. self.look.targ) end
	end,
	
	btn = function(self)
		if self:first(self.v.BTN) then
			local ret_line, ret_button
			if self.look.ttype==Type.BUTTON then
				ret_button = color[self.look.targ]
				ret_line = self:formatElem(self.look,ret_button)
				self:move()
				ret_line = ret_line .. self:btnp()
				return ret_line, ret_button
			elseif self.look.targ=="[" then
				local pre = self.look.targ
				self:move()
				if self.look.ttype==Type.BUTTON then
					ret_button = color[self.look.targ]
					ret_line = self:formatElem(pre,ret_button)
					ret_line = ret_line .. self:formatElem(self.look,ret_button)
					self:move()
					ret_line = ret_line .. self:btnp()
					if self.look.targ=="]" then
						ret_line = ret_line .. self:formatElem(self.look,ret_button)
						self:move()
						return ret_line, ret_button
					else error("illegal token: " .. self.look.targ) end
				else error("illegal token: " .. self.look.targ) end
			elseif self.look.targ=="]" then
				local pre = self.look.targ
				self:move()
				if self.look.ttype==Type.BUTTON then
					ret_button = color[self.look.targ]
					ret_line = self:formatElem(pre,ret_button)
					ret_line = ret_line .. self:formatElem(self.look,ret_button)
					self:move()
					ret_line = ret_line .. self:btnp()
					if self.look.targ=="[" then
						ret_line = ret_line .. self:formatElem(self.look,ret_button)
						self:move()
						return ret_line, ret_button
					else error("illegal token: " .. self.look.targ) end
				else error("illegal token: " .. self.look.targ) end
			elseif self.look.targ=="{" then
				local pre = self.look.targ
				self:move()
				if self.look.ttype==Type.BUTTON then
					ret_button = color[self.look.targ]
					ret_line = self:formatElem(pre,ret_button)
					ret_line = ret_line .. self:formatElem(self.look,ret_button)
					self:move()
					ret_line = ret_line .. self:btnp()
					if self.look.targ=="}" then
						ret_line = ret_line .. self:formatElem(self.look,ret_button)
						self:move()
						return ret_line, ret_button
					else error("illegal token: " .. self.look.targ) end
				else error("illegal token: " .. self.look.targ) end
			elseif self.look.targ=="AT" then
				ret_line = self:formatElem(self.look)
				self:move()
				return ret_line
			end
		else error("illegal token: " .. self.look.targ) end
	end,
	
	btnp = function(self)
		if self:first(self.v.BTNP) then
			local ret_line
			ret_line = self:formatElem(self.look)
			self:move()
			if self.look.ttype==Type.BUTTON then
				local subb = color[self.look.targ]
				ret_line = ret_line .. self:formatElem(self.look,subb)
				self:move()
				ret_line = ret_line .. self:btnp()
				return ret_line
			else error("illegal token: " .. self.look.targ) end
		elseif not self:follow(self.v.BTNP) then return error("illegal token: " .. self.look.targ)
		else return "" end
	end,
	
	first = function(self,var)
		if var==self.v.COMBOLIST then return self:first(self.v.COMBO)
		elseif var==self.v.COMBOLISTP then return self:first(self.v.COMBO)
		elseif var==self.v.COMBO then return self.look.targ=="(" or self:first(self.v.INPUTLIST)
		elseif var==self.v.INPUTLIST then return self:first(self.v.INPUT)
		elseif var==self.v.INPUTLISTP then return self.look.ttype==Type.CONNECTOR
		elseif var==self.v.INPUTN then return self:first(self.v.INPUT)
		elseif var==self.v.INPUT then return self.look.ttype==Type.WORD or self:first(self.v.BLOCKLIST)
		elseif var==self.v.BLOCKLIST then return self:first(self.v.BLOCK)
		elseif var==self.v.BLOCKLISTP then return self.look.targ=="/"
		elseif var==self.v.BLOCK then return self.look.ttype==Type.DIRECTION or self:first(self.v.PREFIXLIST) or self:first(self.v.BTNSLIST)
		elseif var==self.v.UCPREFIXLIST then return self:first(self.v.UCPREFIX)
		elseif var==self.v.COMBOLISTP then return self:first(self.v.COMBO)
		elseif var==self.v.UCPREFIX then return self.look.targ=="dl." or self.look.targ=="tk." or self.look.targ=="w."
		elseif var==self.v.PREFIXLIST then return self:first(self.v.PREFIX)
		elseif var==self.v.PREFIX then return self.look.ttype==Type.PREFIX
		elseif var==self.v.DIR then return self.look.ttype==Type.DIRECTION
		elseif var==self.v.BTNSLIST then return self:first(self.v.BTNS)
		elseif var==self.v.BTNSLISTP then return self.look.targ=="~"
		elseif var==self.v.BTNS then return self.look.ttype==Type.DIRECTION or self:first(self.v.BTN)
		elseif var==self.v.SUFFIXLIST then return self:first(self.v.SUFFIX)
		elseif var==self.v.SUFFIX then return self.look.targ=="("
		elseif var==self.v.BTN then return self.look.ttype==Type.BUTTON or self.look.targ=="AT" or self.look.targ=="[" or self.look.targ=="]" or self.look.targ=="{"
		elseif var==self.v.BTNP then return self.look.targ=="+"
		else return false end
	end,
	
	follow = function(self, var)
		if var==self.v.COMBOLISTP then return self.look.targ=="(" or self.look.targ==")" or self.look.targ=="EOF"
		elseif var==self.v.INPUTLISTP then return self.look.ttype==Type.WORD or self.look.ttype==Type.PREFIX or self.look.ttype==Type.BUTTON or self.look.ttype==Type.DIRECTION or self.look.targ==")" or self.look.targ=="(" or self.look.targ=="]" or self.look.targ=="[" or self.look.targ=="{" or self.look.targ=="EOF"
		elseif var==self.v.INPUTN then return self.look.ttype==Type.CONNECTOR or self.look.ttype==Type.WORD or self.look.ttype==Type.PREFIX or self.look.ttype==Type.BUTTON or self.look.ttype==Type.DIRECTION or self.look.targ==")" or self.look.targ=="(" or self.look.targ=="]" or self.look.targ=="[" or self.look.targ=="{" or self.look.targ=="EOF"
		elseif var==self.v.BLOCKLISTP then return self.look.ttype==Type.CONNECTOR or self.look.ttype==Type.WORD or self.look.ttype==Type.PREFIX or self.look.ttype==Type.BUTTON or self.look.ttype==Type.DIRECTION or self.look.targ==")" or self.look.targ=="(" or self.look.targ=="]" or self.look.targ=="[" or self.look.targ=="{" or self.look.targ=="EOF"
		elseif var==self.v.PREFIXLIST then return self.look.ttype==Type.BUTTON or self.look.ttype==Type.DIRECTION or self.look.targ=="]" or self.look.targ=="[" or self.look.targ=="]" or self.look.targ=="{"
		elseif var==self.v.DIR then return self.look.ttype==Type.BUTTON or self.look.targ=="]" or self.look.targ=="[" or self.look.targ=="]" or self.look.targ=="{"
		elseif var==self.v.BTNSLISTP then return self.look.ttype==Type.CONNECTOR or self.look.ttype==Type.WORD or self.look.ttype==Type.PREFIX or self.look.ttype==Type.BUTTON or self.look.ttype==Type.DIRECTION or self.look.targ=="/" or self.look.targ==")" or self.look.targ=="(" or self.look.targ=="]" or self.look.targ=="[" or self.look.targ=="{" or self.look.targ=="EOF"
		elseif var==self.v.SUFFIXLIST then return self.look.ttype==Type.CONNECTOR or self.look.ttype==Type.WORD or self.look.ttype==Type.PREFIX or self.look.ttype==Type.BUTTON or self.look.ttype==Type.DIRECTION or self.look.targ=="/" or self.look.targ=="~" or self.look.targ==")" or self.look.targ=="(" or self.look.targ=="]" or self.look.targ=="[" or self.look.targ=="{" or self.look.targ=="EOF"
		elseif var==self.v.BTNP then return self.look.ttype==Type.CONNECTOR or self.look.ttype==Type.WORD or self.look.ttype==Type.PREFIX or self.look.ttype==Type.BUTTON or self.look.ttype==Type.DIRECTION or self.look.targ=="/" or self.look.targ=="~" or self.look.targ==")" or self.look.targ=="(" or self.look.targ=="]" or self.look.targ=="[" or self.look.targ=="{" or self.look.targ=="}" or self.look.targ=="EOF"
		else return false end
	end,
}


--actual method functions
	
function p.multiprop(frame)
	
	local str = frame.args[1]
	local sep = ","
	local t={}
	--split properties
	for str in string.gmatch(str, "([^"..sep.."]+)") do
			table.insert(t, str)
	end
	--get the string done
	for index, prop in ipairs(t) do
		if prop:sub(1,1) == "%s" then
			prop = prop:sub(2,-1)
		end
		expanded = frame:expandTemplate{ title="Property-MBTL", args={prop,"IGNOREPROPERTYERROR"}}
		t[index] = expanded
	end
	
	ret = table.concat(t,", ")
	
	return ret
end

--color code and format game inputs.
--supports entire combos as arguments.
function p.gameInput(frame)
	
	local str = frame.args[1]
	lexer:newBuffer(str) --set the input string as the input buffer
	return parser:start()
	
end

return p