Quantcast

Allow for multiple checkbox and list/item pairs in parameters.

Johnny C. Lam [07-13-14 - 11:32]
Allow for multiple checkbox and list/item pairs in parameters.

List/item pairs should now use the following syntax:

    list=name item=value   -->   listitem=name:value

Isolate code to handle deprecated parameters (checkboxon, checkboxoff,
list, item, mastery) into OvaleAST:FlattenParameters() so that other
modules no longer need logic to handle the deprecated features when using
parameters.

git-svn-id: svn://svn.curseforge.net/wow/ovale/mainline/trunk@1551 d5049fe3-3747-40f7-a4b5-f36d6801af5f
Filename
OvaleAST.lua
OvaleCompile.lua
diff --git a/OvaleAST.lua b/OvaleAST.lua
index 65b4a43..10e341d 100644
--- a/OvaleAST.lua
+++ b/OvaleAST.lua
@@ -99,15 +99,16 @@ local DECLARATION_KEYWORD = {

 local PARAMETER_KEYWORD = {
 	["checkbox"] = true,
-	["checkboxoff"] = true,
-	["checkboxon"] = true,
+	["checkboxoff"] = true,		-- deprecated
+	["checkboxon"] = true,		-- deprecated
 	["glyph"] = true,
 	["if_spell"] = true,
 	["if_stance"] = true,
-	["item"] = true,
+	["item"] = true,			-- deprecated
 	["itemcount"] = true,
 	["itemset"] = true,
-	["list"] = true,
+	["list"] = true,			-- deprecated
+	["listitem"] = true,
 	["specialization"] = true,
 	["stance"] = true,
 	["talent"] = true,
@@ -203,6 +204,7 @@ end
 local self_indent = 0
 local self_outputPool = OvalePool("OvaleAST_outputPool")

+local self_controlPool = OvalePool("OvaleAST_controlPool")
 local self_parametersPool = OvalePool("OvaleAST_parametersPool")
 local self_childrenPool = OvalePool("OvaleAST_childrenPool")
 local self_pool = OvalePool("OvaleAST_pool")
@@ -349,6 +351,30 @@ local function GetTokenIterator(s)
 	return OvaleLexer.scan(s, MATCHES, exclude)
 end

+-- "Flatten" a parameter value node into a string.
+local function FlattenParameterValue(parameterValue)
+	local value = parameterValue
+	if type(parameterValue) == "table" then
+		local node = parameterValue
+		local isBang = false
+		if node.type == "bang_value" then
+			isBang = true
+			node = node.child[1]
+		end
+		if node.type == "value" then
+			value = node.value
+		elseif node.type == "variable" then
+			value = node.name
+		elseif node.type == "string" then
+			value = node.value
+		end
+		if isBang then
+			value = "!" .. tostring(value)
+		end
+	end
+	return value
+end
+
 --[[------------------------
 	"Unparser" functions
 --]]------------------------
@@ -530,6 +556,14 @@ UnparseParameters = function(parameters)
 	for k, v in pairs(parameters) do
 		if type(k) == "number" and k <= N then
 			-- Already output in previous loop.
+		elseif k == "checkbox" then
+			for _, name in ipairs(v) do
+				output[#output + 1] = format("checkbox=%s", Unparse(name))
+			end
+		elseif k == "listitem" then
+			for list, item in pairs(v) do
+				output[#output + 1] = format("listitem=%s:%s", list, Unparse(item))
+			end
 		elseif type(v) == "table" then
 			output[#output + 1] = format("%s=%s", k, Unparse(v))
 		elseif k == "filter" or k == "target" then
@@ -1524,14 +1558,50 @@ ParseParameters = function(tokenStream, nodeList, annotation, isList)
 				if tokenType == "=" then
 					-- Consume the '=' token.
 					tokenStream:Consume()
-					-- Get the value.
-					ok, node = ParseParameterValue(tokenStream, nodeList, annotation)
-					if ok then
+					if name == "checkbox" or name == "listitem" then
+						local control = parameters[name] or self_controlPool:Get()
+						if name == "checkbox" then
+							-- Get the checkbox name.
+							ok, node = ParseParameterValue(tokenStream, nodeList, annotation)
+							if ok then
+								control[#control + 1] = node
+							end
+						else -- if name == "listitem" then
+							-- Consume the list name.
+							tokenType, token = tokenStream:Consume()
+							local list
+							if tokenType == "name" then
+								list = token
+							else
+								SyntaxError(tokenStream, "Syntax error: unexpected token '%s' when parsing PARAMETERS; name expected.", token)
+								ok = false
+							end
+							if ok then
+								-- Consume the ':' token.
+								tokenType, token = tokenStream:Consume()
+								if tokenType ~= ":" then
+									SyntaxError(tokenStream, "Syntax error: unexpected token '%s' when parsing PARAMETERS; ':' expected.", token)
+									ok = false
+								end
+							end
+							if ok then
+								-- Consume the list item.
+								ok, node = ParseParameterValue(tokenStream, nodeList, annotation)
+							end
+							if ok then
+								control[list] = node
+							end
+						end
+						if not parameters[name] then
+							parameters[name] = control
+							annotation.controlList = annotation.controlList or {}
+							annotation.controlList[#annotation.controlList + 1] = control
+						end
+					else
+						-- Get the value.
+						ok, node = ParseParameterValue(tokenStream, nodeList, annotation)
 						parameters[name] = node
 					end
-				elseif PARAMETER_KEYWORD[name] then
-					SyntaxError(tokenStream, "Syntax error: unexpected keyword '%s' when parsing PARAMETERS; simple expression expected.", name)
-					ok = false
 				else
 					parameters[#parameters + 1] = node
 				end
@@ -2021,6 +2091,7 @@ end
 function OvaleAST:Debug()
 	self_pool:Debug()
 	self_parametersPool:Debug()
+	self_controlPool:Debug()
 	self_childrenPool:Debug()
 	self_outputPool:Debug()
 end
@@ -2045,6 +2116,11 @@ function OvaleAST:NodeToString(node)
 end

 function OvaleAST:ReleaseAnnotation(annotation)
+	if annotation.controlList then
+		for _, control in ipairs(annotation.controlList) do
+			self_controlPool:Release(control)
+		end
+	end
 	if annotation.parametersList then
 		for _, parameters in ipairs(annotation.parametersList) do
 			self_parametersPool:Release(parameters)
@@ -2209,29 +2285,65 @@ function OvaleAST:FlattenParameters(ast)
 				local parameters = self_parametersPool:Get()
 				for key, value in pairs(node.rawParams) do
 					-- Lookup the key.
-					if type(key) ~= "number" and dictionary and dictionary[key] then
-						key = dictionary[key]
-					end
-					-- Evaluate the value.
-					if type(value) == "table" then
-						local node = value
-						local isBang = false
-						if node.type == "bang_value" then
-							isBang = true
-							node = node.child[1]
+					if key == "checkbox" or key == "listitem" then
+						local control = parameters[key] or self_controlPool:Get()
+						if key == "checkbox" then
+							for i, name in ipairs(value) do
+								control[i] = FlattenParameterValue(name)
+							end
+						else -- if key == "listitem" then
+							for list, item in pairs(value) do
+								control[list] = FlattenParameterValue(item)
+							end
+						end
+						if not parameters[key] then
+							parameters[key] = control
+							annotation.controlList = annotation.controlList or {}
+							annotation.controlList[#annotation.controlList + 1] = control
 						end
-						if node.type == "value" then
-							value = node.value
-						elseif node.type == "variable" then
-							value = node.name
-						elseif node.type == "string" then
-							value = node.value
+					elseif key == "checkboxon" or key == "checkboxoff" then
+						-- Deprecated: checkboxon
+						-- Deprecated: checkboxoff
+						local control = parameters.checkbox or self_controlPool:Get()
+						local name = FlattenParameterValue(value)
+						if key == "checkboxon" then
+							Ovale:OneTimeMessage("Warning: 'checkboxon=%s' is deprecated; use 'checkbox=%s' instead.", name, name)
+							control[#control + 1] = name
+						elseif key == "checkboxoff" then
+							Ovale:OneTimeMessage("Warning: 'checkboxoff=%s' is deprecated; use 'checkbox=!%s' instead.", name, name)
+							control[#control + 1] = "!" .. name
 						end
-						if isBang then
-							value = "!" .. tostring(value)
+						if not parameters.checkbox then
+							parameters.checkbox = control
+							annotation.controlList = annotation.controlList or {}
+							annotation.controlList[#annotation.controlList + 1] = control
 						end
+					elseif key == "list" or key == "item" then
+						-- Deprecated: list
+						-- Deprecated: item
+						if key == "list" and node.rawParams.item then
+							local control = parameters.listitem or self_controlPool:Get()
+							local list = FlattenParameterValue(value)
+							local item = FlattenParameterValue(node.rawParams.item)
+							Ovale:OneTimeMessage("Warning: 'list=%s item=%s' is deprecated; use 'listitem=%s:%s' instead.", list, item, list, item)
+							control[list] = item
+							if not parameters.listitem then
+								parameters.listitem = control
+								annotation.controlList = annotation.controlList or {}
+								annotation.controlList[#annotation.controlList + 1] = control
+							end
+						end
+					elseif key == "mastery" then
+						-- Deprecated: mastery -> specialization
+						local spec = FlattenParameterValue(value)
+						Ovale:OneTimeMessage("Warning: 'mastery=%s' is deprecated; use 'specialization=%s' instead.", spec, spec)
+						parameters.specialization = spec
+					else
+						if type(key) ~= "number" and dictionary and dictionary[key] then
+							key = dictionary[key]
+						end
+						parameters[key] = FlattenParameterValue(value)
 					end
-					parameters[key] = value
 				end
 				node.params = parameters
 				annotation.parametersList = annotation.parametersList or {}
@@ -2246,6 +2358,14 @@ function OvaleAST:FlattenParameters(ast)
 				for k, v in pairs(parameters) do
 					if type(k) == "number" and k <= N then
 						-- Already output in previous loop.
+					elseif k == "checkbox" then
+						for _, name in ipairs(v) do
+							output[#output + 1] = format("checkbox=%s", name)
+						end
+					elseif k == "listitem" then
+						for list, item in ipairs(v) do
+							output[#output + 1] = format("listitem=%s:%s", list, item)
+						end
 					else
 						output[#output + 1] = format("%s=%s", k, v)
 					end
diff --git a/OvaleCompile.lua b/OvaleCompile.lua
index 770a144..77a3625 100644
--- a/OvaleCompile.lua
+++ b/OvaleCompile.lua
@@ -106,13 +106,6 @@ local function TestConditions(parameters)
 		local isSpec = OvalePaperDoll:IsSpecialization(spec)
 		boolean = (required and isSpec) or (not required and not isSpec)
 	end
-	-- Deprecated: mastery -> specialization
-	if boolean and parameters.mastery then
-		Ovale:OneTimeMessage("Warning: 'mastery' is deprecated; use 'specialization' instead.")
-		local spec, required = RequireValue(parameters.mastery)
-		local isSpec = OvalePaperDoll:IsSpecialization(spec)
-		boolean = (required and isSpec) or (not required and not isSpec)
-	end
 	if boolean and parameters.if_stance then
 		self_compileOnStances = true
 		local stance, required = RequireValue(parameters.if_stance)
@@ -137,50 +130,28 @@ local function TestConditions(parameters)
 	do
 		local profile
 		if boolean and parameters.checkbox then
-			local name, required = RequireValue(parameters.checkbox)
-			local checkBox = Ovale.casesACocher[name] or {}
-			checkBox.compile = true
-			Ovale.casesACocher[name] = checkBox
-			-- Check the value of the checkbox.
-			profile = profile or OvaleOptions:GetProfile()
-			local isChecked = (profile.check[name] ~= nil)
-			boolean = (required and isChecked) or (not required and not isChecked)
-		end
-		-- Deprecated: checkboxon
-		if boolean and parameters.checkboxon then
-			Ovale:OneTimeMessage("Warning: 'checkboxon=name' is deprecated; use 'checkbox=name' instead.")
-			-- Flag this checkbox as triggering a script evaluation.
-			local name = parameters.checkboxon
-			local checkBox = Ovale.casesACocher[name] or {}
-			checkBox.compile = true
-			Ovale.casesACocher[name] = checkBox
-			-- Check the value of the checkbox.
-			profile = profile or OvaleOptions:GetProfile()
-			boolean = (profile.check[name] ~= nil)
-		end
-		-- Deprecated: checkboxoff
-		if boolean and parameters.checkboxoff then
-			Ovale:OneTimeMessage("Warning: 'checkboxoff=name' is deprecated; use 'checkbox=!name' instead.")
-			-- Flag this checkbox as triggering a script evaluation.
-			local name = parameters.checkboxon
-			local checkBox = Ovale.casesACocher[name] or {}
-			checkBox.compile = true
-			Ovale.casesACocher[name] = checkBox
-			-- Check the value of the checkbox.
-			profile = profile or OvaleOptions:GetProfile()
-			boolean = (profile.check[name] == nil)
+			for _, checkbox in ipairs(parameters.checkbox) do
+				local name, required = RequireValue(checkbox)
+				local control = Ovale.casesACocher[name] or {}
+				control.compile = true
+				Ovale.casesACocher[name] = control
+				-- Check the value of the checkbox.
+				profile = profile or OvaleOptions:GetProfile()
+				local isChecked = (profile.check[name] ~= nil)
+				boolean = (required and isChecked) or (not required and not isChecked)
+			end
 		end
-		if boolean and parameters.list and parameters.item then
-			-- Flag this list as triggering a script evaluation.
-			local name = parameters.list
-			local item, required = RequireValue(parameters.item)
-			local list = Ovale.listes[name] or { items = {}, default = nil }
-			list.compile = true
-			Ovale.listes[name] = list
-			-- Check the selected item in the list.
-			profile = profile or OvaleOptions:GetProfile()
-			local isSelected = (profile.list[name] == item)
-			boolean = (required and isSelected) or (not required and not isSelected)
+		if boolean and parameters.listitem then
+			for list, listitem in pairs(parameters.listitem) do
+				local item, required = RequireValue(listitem)
+				local control = Ovale.listes[list] or { items = {}, default = nil }
+				control.compile = true
+				Ovale.listes[list] = control
+				-- Check the selected item in the list.
+				profile = profile or OvaleOptions:GetProfile()
+				local isSelected = (profile.list[list] == item)
+				boolean = (required and isSelected) or (not required and not isSelected)
+			end
 		end
 	end
 	profiler.Stop("OvaleCompile_TestConditions")
@@ -323,8 +294,7 @@ local function EvaluateSpellAuraList(node)
 		local tbl = auraTable[filter] or {}
 		local count = 0
 		for k, v in pairs(parameters) do
-			-- Deprecated: "mastery" can't be a keyword since it's the same as the name of the raid buff.
-			if not OvaleAST.PARAMETER_KEYWORD[k] and k ~= "mastery" then
+			if not OvaleAST.PARAMETER_KEYWORD[k] then
 				tbl[k] = v
 				count = count + 1
 			end
@@ -365,11 +335,8 @@ local function EvaluateSpellInfo(node)
 				OvaleData.buffSpellList[v] = list
 			elseif k == "sharedcd" then
 				OvaleCooldown:AddSharedCooldown(v, spellId)
-			else
-				-- Deprecated: "mastery" can't be a keyword since it's the same as the name of the raid buff.
-				if not OvaleAST.PARAMETER_KEYWORD[k] and k ~= "mastery" then
-					si[k] = v
-				end
+			elseif not OvaleAST.PARAMETER_KEYWORD[k] then
+				si[k] = v
 			end
 		end
 	end