mirror of
https://github.com/brues-code/SuperCleveRoidMacros.git
synced 2026-09-16 03:38:00 +00:00
improve checker accuracy
This commit is contained in:
+136
-84
@@ -2,7 +2,7 @@
|
||||
Macro Syntax Error Checker
|
||||
Author: Mewtiny
|
||||
License: MIT License
|
||||
|
||||
|
||||
Validates macro syntax and reports errors to help users debug their macros
|
||||
]]
|
||||
local _G = _G or getfenv(0)
|
||||
@@ -47,7 +47,7 @@ local VALID_CONDITIONALS = {
|
||||
swimming = true, noswimming = true,
|
||||
talent = true, notalent = true,
|
||||
zone = true, nozone = true,
|
||||
|
||||
|
||||
-- Unit based
|
||||
alive = true, noalive = true,
|
||||
buff = true, nobuff = true,
|
||||
@@ -79,13 +79,22 @@ local VALID_COMMANDS = {
|
||||
["/cast"] = true,
|
||||
["/castpet"] = true,
|
||||
["/cancelaura"] = true,
|
||||
["/cancelform"] = true,
|
||||
["/castsequence"] = true,
|
||||
["/castrandom"] = true,
|
||||
["/changeactionbar"] = true,
|
||||
["/clearfocus"] = true,
|
||||
["/cleartarget"] = true,
|
||||
["/click"] = true,
|
||||
["/dismount"] = true,
|
||||
["/equip"] = true,
|
||||
["/equipmh"] = true,
|
||||
["/equipoh"] = true,
|
||||
["/equipslot"] = true,
|
||||
["/focus"] = true,
|
||||
["/petattack"] = true,
|
||||
["/petfollow"] = true,
|
||||
["/petstay"] = true,
|
||||
["/petwait"] = true,
|
||||
["/petpassive"] = true,
|
||||
["/petaggressive"] = true,
|
||||
@@ -98,9 +107,19 @@ local VALID_COMMANDS = {
|
||||
["/stopattack"] = true,
|
||||
["/stopcasting"] = true,
|
||||
["/stopmacro"] = true,
|
||||
["/stopspelltarget"] = true,
|
||||
["/swapactionbar"] = true,
|
||||
["/target"] = true,
|
||||
["/targetenemy"] = true,
|
||||
["/targetfriend"] = true,
|
||||
["/targetlastenemy"] = true,
|
||||
["/targetlastfriend"] = true,
|
||||
["/targetlasttarget"] = true,
|
||||
["/targetparty"] = true,
|
||||
["/targetraid"] = true,
|
||||
["/unqueue"] = true,
|
||||
["/use"] = true,
|
||||
["/userandom"] = true,
|
||||
["/unbuff"] = true,
|
||||
["/unshift"] = true,
|
||||
["/retarget"] = true,
|
||||
@@ -126,10 +145,10 @@ CleveRoids.MacroErrors = {}
|
||||
local function checkBrackets(text)
|
||||
local openCount = 0
|
||||
local inQuotes = false
|
||||
|
||||
|
||||
for i = 1, string.len(text) do
|
||||
local char = string.sub(text, i, i)
|
||||
|
||||
|
||||
if char == '"' then
|
||||
inQuotes = not inQuotes
|
||||
elseif not inQuotes then
|
||||
@@ -143,13 +162,13 @@ local function checkBrackets(text)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if openCount > 0 then
|
||||
return false, "Missing closing bracket"
|
||||
elseif openCount < 0 then
|
||||
return false, "Extra closing bracket"
|
||||
end
|
||||
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -157,10 +176,10 @@ end
|
||||
local function checkQuotes(text)
|
||||
local quoteCount = 0
|
||||
local escaped = false
|
||||
|
||||
|
||||
for i = 1, string.len(text) do
|
||||
local char = string.sub(text, i, i)
|
||||
|
||||
|
||||
if escaped then
|
||||
escaped = false
|
||||
elseif char == "\\" then
|
||||
@@ -169,19 +188,19 @@ local function checkQuotes(text)
|
||||
quoteCount = quoteCount + 1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local quotient = math.floor(quoteCount / 2)
|
||||
if (quoteCount - (quotient * 2)) ~= 0 then
|
||||
return false, "Unmatched quotes"
|
||||
end
|
||||
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- Validate conditional syntax
|
||||
local function validateConditional(conditional, args, action)
|
||||
local errors = {}
|
||||
|
||||
|
||||
-- Check if conditional is valid
|
||||
local baseCond = string.lower(conditional)
|
||||
if not VALID_CONDITIONALS[baseCond] then
|
||||
@@ -191,7 +210,7 @@ local function validateConditional(conditional, args, action)
|
||||
message = "Unknown conditional: " .. conditional
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
-- Check for required arguments
|
||||
local needsArgs = {
|
||||
combo = true,
|
||||
@@ -205,7 +224,7 @@ local function validateConditional(conditional, args, action)
|
||||
button = true,
|
||||
form = true, stance = true,
|
||||
}
|
||||
|
||||
|
||||
if needsArgs[baseCond] and (not args or args == "") and (not action or action == "") then
|
||||
table.insert(errors, {
|
||||
type = ERROR_TYPES.MISSING_ARGUMENT,
|
||||
@@ -213,9 +232,9 @@ local function validateConditional(conditional, args, action)
|
||||
message = conditional .. " requires an argument"
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
-- Check operator syntax for numeric comparisons
|
||||
if args and string.find(baseCond, "hp") or string.find(baseCond, "power") or
|
||||
if args and string.find(baseCond, "hp") or string.find(baseCond, "power") or
|
||||
string.find(baseCond, "combo") or baseCond == "stat" then
|
||||
local hasOperator = string.find(args, "[<>=~]+")
|
||||
if args ~= "" and not hasOperator and not string.find(args, "^%d+$") then
|
||||
@@ -229,20 +248,20 @@ local function validateConditional(conditional, args, action)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return errors
|
||||
end
|
||||
|
||||
-- Parse and validate a single line
|
||||
local function validateLine(line, lineNum)
|
||||
local errors = {}
|
||||
|
||||
|
||||
-- Skip comments and empty lines
|
||||
line = CleveRoids.Trim(line)
|
||||
if line == "" or string.sub(line, 1, 2) == "--" then
|
||||
if line == "" or string.sub(line, 1, 2) == "--" or string.sub(line, 1, 1) == "#" then
|
||||
return errors
|
||||
end
|
||||
|
||||
|
||||
-- Check for valid command
|
||||
local _, _, cmd = string.find(line, "^(/[a-z]+)")
|
||||
if cmd then
|
||||
@@ -256,7 +275,7 @@ local function validateLine(line, lineNum)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Check brackets
|
||||
local bracketsOk, bracketError = checkBrackets(line)
|
||||
if not bracketsOk then
|
||||
@@ -266,7 +285,7 @@ local function validateLine(line, lineNum)
|
||||
message = bracketError
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
-- Check quotes
|
||||
local quotesOk, quoteError = checkQuotes(line)
|
||||
if not quotesOk then
|
||||
@@ -276,61 +295,94 @@ local function validateLine(line, lineNum)
|
||||
message = quoteError
|
||||
})
|
||||
end
|
||||
|
||||
-- Parse conditionals if present
|
||||
local _, cbEnd, conditionBlock = string.find(line, "%[(.+)%]")
|
||||
if conditionBlock then
|
||||
if CleveRoids.Trim(conditionBlock) == "" then
|
||||
table.insert(errors, {
|
||||
type = ERROR_TYPES.EMPTY_CONDITIONAL,
|
||||
line = lineNum,
|
||||
message = "Empty conditional block []"
|
||||
})
|
||||
else
|
||||
-- Check for invalid @ target syntax
|
||||
local _, _, target = string.find(conditionBlock, "(@[^%s,]+)")
|
||||
if target and not string.find(target, "^@[a-z]+") then
|
||||
table.insert(errors, {
|
||||
type = ERROR_TYPES.INVALID_TARGET,
|
||||
line = lineNum,
|
||||
message = "Invalid target: " .. target
|
||||
})
|
||||
end
|
||||
|
||||
-- Parse individual conditionals
|
||||
local seenConditionals = {}
|
||||
for _, condGroup in CleveRoids.splitStringIgnoringQuotes(conditionBlock, {",", " "}) do
|
||||
if condGroup ~= "" and condGroup ~= target then
|
||||
local parts = CleveRoids.splitStringIgnoringQuotes(condGroup, ":")
|
||||
local cond = string.lower(CleveRoids.Trim(parts[1] or ""))
|
||||
local args = CleveRoids.Trim(parts[2] or "")
|
||||
|
||||
if cond ~= "" then
|
||||
seenConditionals[cond] = true
|
||||
|
||||
-- Validate the conditional
|
||||
local condErrors = validateConditional(cond, args, nil)
|
||||
for _, err in condErrors do
|
||||
err.line = lineNum
|
||||
table.insert(errors, err)
|
||||
|
||||
-- Split by semicolons to handle multiple actions per line
|
||||
local actions = CleveRoids.splitStringIgnoringQuotes(line, ";")
|
||||
|
||||
for _, actionPart in ipairs(actions) do
|
||||
actionPart = CleveRoids.Trim(actionPart)
|
||||
if actionPart ~= "" and string.sub(actionPart, 1, 1) ~= "/" then
|
||||
actionPart = "/" .. actionPart -- Add leading slash if missing after split
|
||||
end
|
||||
|
||||
-- Parse conditionals if present - use non-greedy match
|
||||
local condStart = string.find(actionPart, "%[")
|
||||
local condEnd = nil
|
||||
local conditionBlock = nil
|
||||
|
||||
if condStart then
|
||||
-- Find matching closing bracket
|
||||
local depth = 0
|
||||
local inQuotes = false
|
||||
for i = condStart, string.len(actionPart) do
|
||||
local char = string.sub(actionPart, i, i)
|
||||
if char == '"' then
|
||||
inQuotes = not inQuotes
|
||||
elseif not inQuotes then
|
||||
if char == "[" then
|
||||
depth = depth + 1
|
||||
elseif char == "]" then
|
||||
depth = depth - 1
|
||||
if depth == 0 then
|
||||
condEnd = i
|
||||
conditionBlock = string.sub(actionPart, condStart + 1, i - 1)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check for action after conditionals
|
||||
local actionPart = string.sub(line, (cbEnd or 0) + 1)
|
||||
local _, _, action = string.find(actionPart, "^%s*[!~]?(.+)")
|
||||
if not action or CleveRoids.Trim(action) == "" then
|
||||
table.insert(errors, {
|
||||
type = ERROR_TYPES.EMPTY_ACTION,
|
||||
line = lineNum,
|
||||
message = "Conditional has no action"
|
||||
})
|
||||
|
||||
if conditionBlock then
|
||||
if CleveRoids.Trim(conditionBlock) == "" then
|
||||
table.insert(errors, {
|
||||
type = ERROR_TYPES.EMPTY_CONDITIONAL,
|
||||
line = lineNum,
|
||||
message = "Empty conditional block []"
|
||||
})
|
||||
else
|
||||
-- Check for invalid @ target syntax
|
||||
local _, _, target = string.find(conditionBlock, "(@[^%s,]+)")
|
||||
if target and not string.find(target, "^@[a-z]+%d*") then
|
||||
table.insert(errors, {
|
||||
type = ERROR_TYPES.INVALID_TARGET,
|
||||
line = lineNum,
|
||||
message = "Invalid target: " .. target
|
||||
})
|
||||
end
|
||||
|
||||
-- Parse individual conditionals
|
||||
for _, condGroup in CleveRoids.splitStringIgnoringQuotes(conditionBlock, {",", " "}) do
|
||||
if condGroup ~= "" and condGroup ~= target then
|
||||
local parts = CleveRoids.splitStringIgnoringQuotes(condGroup, ":")
|
||||
local cond = string.lower(CleveRoids.Trim(parts[1] or ""))
|
||||
local args = CleveRoids.Trim(parts[2] or "")
|
||||
|
||||
if cond ~= "" then
|
||||
-- Validate the conditional
|
||||
local condErrors = validateConditional(cond, args, nil)
|
||||
for _, err in condErrors do
|
||||
err.line = lineNum
|
||||
table.insert(errors, err)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check for action after conditionals
|
||||
local afterCond = string.sub(actionPart, (condEnd or 0) + 1)
|
||||
local _, _, action = string.find(afterCond, "^%s*[!~?]?(.+)")
|
||||
if not action or CleveRoids.Trim(action) == "" then
|
||||
table.insert(errors, {
|
||||
type = ERROR_TYPES.EMPTY_ACTION,
|
||||
line = lineNum,
|
||||
message = "Conditional has no action"
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return errors
|
||||
end
|
||||
|
||||
@@ -338,14 +390,14 @@ end
|
||||
function CleveRoids.ValidateMacro(macroName)
|
||||
local errors = {}
|
||||
local macroID = GetMacroIndexByName(macroName)
|
||||
|
||||
|
||||
if not macroID or macroID == 0 then
|
||||
return {{
|
||||
type = "ERROR",
|
||||
message = "Macro not found: " .. tostring(macroName)
|
||||
}}
|
||||
end
|
||||
|
||||
|
||||
local name, texture, body = GetMacroInfo(macroID)
|
||||
if not body or body == "" then
|
||||
return {{
|
||||
@@ -353,17 +405,17 @@ function CleveRoids.ValidateMacro(macroName)
|
||||
message = "Macro is empty"
|
||||
}}
|
||||
end
|
||||
|
||||
|
||||
-- Split into lines
|
||||
local lines = CleveRoids.splitString(body, "\n")
|
||||
|
||||
|
||||
for lineNum, line in ipairs(lines) do
|
||||
local lineErrors = validateLine(line, lineNum)
|
||||
for _, err in lineErrors do
|
||||
table.insert(errors, err)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return errors
|
||||
end
|
||||
|
||||
@@ -371,7 +423,7 @@ end
|
||||
function CleveRoids.ValidateAllMacros()
|
||||
local results = {}
|
||||
local totalErrors = 0
|
||||
|
||||
|
||||
local numMacros = GetNumMacros()
|
||||
for i = 1, numMacros do
|
||||
local name = GetMacroInfo(i)
|
||||
@@ -383,21 +435,21 @@ function CleveRoids.ValidateAllMacros()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return results, totalErrors
|
||||
end
|
||||
|
||||
-- Print errors for a macro
|
||||
function CleveRoids.PrintMacroErrors(macroName)
|
||||
local errors = CleveRoids.ValidateMacro(macroName)
|
||||
|
||||
|
||||
if not errors or table.getn(errors) == 0 then
|
||||
CleveRoids.Print("|cff00ff00✓|r Macro '" .. macroName .. "' has no syntax errors")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
CleveRoids.Print("|cffff0000✗|r Macro '" .. macroName .. "' has " .. table.getn(errors) .. " error(s):")
|
||||
|
||||
|
||||
for _, err in errors do
|
||||
local line = err.line and ("Line " .. err.line .. ": ") or ""
|
||||
local msg = "|cffffaa00" .. line .. "|r" .. err.message
|
||||
@@ -408,18 +460,18 @@ end
|
||||
-- Print all macro errors
|
||||
function CleveRoids.PrintAllMacroErrors()
|
||||
local results, totalErrors = CleveRoids.ValidateAllMacros()
|
||||
|
||||
|
||||
if totalErrors == 0 then
|
||||
CleveRoids.Print("|cff00ff00✓|r All macros are error-free!")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
CleveRoids.Print("|cffff0000Found " .. totalErrors .. " error(s) in " .. table.getn(results) .. " macro(s):|r")
|
||||
|
||||
|
||||
for macroName, errors in pairs(results) do
|
||||
DEFAULT_CHAT_FRAME:AddMessage(" ")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffff8800" .. macroName .. "|r (" .. table.getn(errors) .. " error(s)):")
|
||||
|
||||
|
||||
for _, err in errors do
|
||||
local line = err.line and ("Line " .. err.line .. ": ") or ""
|
||||
local msg = " |cffffaa00" .. line .. "|r" .. err.message
|
||||
@@ -432,7 +484,7 @@ end
|
||||
SLASH_MACROCHECK1 = "/macrocheck"
|
||||
SlashCmdList.MACROCHECK = function(msg)
|
||||
msg = CleveRoids.Trim(msg or "")
|
||||
|
||||
|
||||
if msg == "" or msg == "all" then
|
||||
CleveRoids.PrintAllMacroErrors()
|
||||
else
|
||||
|
||||
Reference in New Issue
Block a user