|
|
|
@@ -96,6 +96,23 @@ func main() {
|
|
|
|
|
flag.Usage()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// Collect global modifiers from special entries and filter them out
|
|
|
|
|
vars := map[string]interface{}{}
|
|
|
|
|
filtered := make([]utils.ModifyCommand, 0, len(commands))
|
|
|
|
|
for _, c := range commands {
|
|
|
|
|
if len(c.Modifiers) > 0 && c.Name == "" && c.Regex == "" && len(c.Regexes) == 0 && c.Lua == "" && len(c.Files) == 0 {
|
|
|
|
|
for k, v := range c.Modifiers {
|
|
|
|
|
vars[k] = v
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
filtered = append(filtered, c)
|
|
|
|
|
}
|
|
|
|
|
if len(vars) > 0 {
|
|
|
|
|
mainLogger.Info("Loaded %d global modifiers", len(vars))
|
|
|
|
|
processor.SetVariables(vars)
|
|
|
|
|
}
|
|
|
|
|
commands = filtered
|
|
|
|
|
mainLogger.Info("Loaded %d commands", len(commands))
|
|
|
|
|
|
|
|
|
|
if *utils.Filter != "" {
|
|
|
|
@@ -111,7 +128,11 @@ func main() {
|
|
|
|
|
|
|
|
|
|
for _, command := range commands {
|
|
|
|
|
mainLogger.Trace("Command: %s", command.Name)
|
|
|
|
|
mainLogger.Trace("Regex: %s", command.Regex)
|
|
|
|
|
if len(command.Regexes) > 0 {
|
|
|
|
|
mainLogger.Trace("Regexes: %v", command.Regexes)
|
|
|
|
|
} else {
|
|
|
|
|
mainLogger.Trace("Regex: %s", command.Regex)
|
|
|
|
|
}
|
|
|
|
|
mainLogger.Trace("Files: %v", command.Files)
|
|
|
|
|
mainLogger.Trace("Lua: %s", command.Lua)
|
|
|
|
|
mainLogger.Trace("Reset: %t", command.Reset)
|
|
|
|
@@ -370,29 +391,96 @@ func CreateExampleConfig() {
|
|
|
|
|
createExampleConfigLogger := logger.Default.WithPrefix("CreateExampleConfig")
|
|
|
|
|
createExampleConfigLogger.Debug("Creating example configuration file")
|
|
|
|
|
commands := []utils.ModifyCommand{
|
|
|
|
|
// Global modifiers only entry (no name/regex/lua/files)
|
|
|
|
|
{
|
|
|
|
|
Name: "DoubleNumericValues",
|
|
|
|
|
Regex: "<value>(\\d+)<\\/value>",
|
|
|
|
|
Lua: "v1 * 2",
|
|
|
|
|
Files: []string{"data/*.xml"},
|
|
|
|
|
LogLevel: "INFO",
|
|
|
|
|
Modifiers: map[string]interface{}{
|
|
|
|
|
"foobar": 4,
|
|
|
|
|
"multiply": 1.5,
|
|
|
|
|
"prefix": "NEW_",
|
|
|
|
|
"enabled": true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
// Multi-regex example using $variable in Lua
|
|
|
|
|
{
|
|
|
|
|
Name: "UpdatePrices",
|
|
|
|
|
Regex: "price=\"(\\d+)\"",
|
|
|
|
|
Lua: "if num(v1) < 100 then return v1 * 1.5 else return v1 end",
|
|
|
|
|
Files: []string{"items/*.xml", "shop/*.xml"},
|
|
|
|
|
Name: "RFToolsMultiply",
|
|
|
|
|
Regexes: []string{"generatePerTick = !num", "ticksPer\\w+ = !num", "generatorRFPerTick = !num"},
|
|
|
|
|
Lua: "* $foobar",
|
|
|
|
|
Files: []string{"polymc/instances/**/rftools*.toml", `polymc\\instances\\**\\rftools*.toml`},
|
|
|
|
|
Reset: true,
|
|
|
|
|
// LogLevel defaults to INFO
|
|
|
|
|
},
|
|
|
|
|
// Named capture groups with arithmetic and string ops
|
|
|
|
|
{
|
|
|
|
|
Name: "UpdateAmountsAndItems",
|
|
|
|
|
Regex: `(?P<amount>!num)\s+units\s+of\s+(?P<item>[A-Za-z_\-]+)`,
|
|
|
|
|
Lua: `amount = amount * $multiply; item = upper(item); return true`,
|
|
|
|
|
Files: []string{"data/**/*.txt"},
|
|
|
|
|
// INFO log level
|
|
|
|
|
},
|
|
|
|
|
// Full replacement via Lua 'replacement' variable
|
|
|
|
|
{
|
|
|
|
|
Name: "BumpMinorVersion",
|
|
|
|
|
Regex: `version\s*=\s*"(?P<major>!num)\.(?P<minor>!num)\.(?P<patch>!num)"`,
|
|
|
|
|
Lua: `replacement = format("version=\"%s.%s.%s\"", major, num(minor)+1, 0); return true`,
|
|
|
|
|
Files: []string{"config/*.ini", "config/*.cfg"},
|
|
|
|
|
},
|
|
|
|
|
// Multiline regex example (DOTALL is auto-enabled). Captures numeric in nested XML.
|
|
|
|
|
{
|
|
|
|
|
Name: "XMLNestedValueMultiply",
|
|
|
|
|
Regex: `<item>\s*\s*<name>!any<\/name>\s*\s*<value>(!num)<\/value>\s*\s*<\/item>`,
|
|
|
|
|
Lua: `* $multiply`,
|
|
|
|
|
Files: []string{"data/**/*.xml"},
|
|
|
|
|
// Demonstrates multiline regex in YAML
|
|
|
|
|
},
|
|
|
|
|
// Multiline regexES array, with different patterns handled by same Lua
|
|
|
|
|
{
|
|
|
|
|
Name: "MultiLinePatterns",
|
|
|
|
|
Regexes: []string{
|
|
|
|
|
`<entry>\s*\n\s*<id>(?P<id>!num)</id>\s*\n\s*<score>(?P<score>!num)</score>\s*\n\s*</entry>`,
|
|
|
|
|
`\[block\]\nkey=(?P<key>[A-Za-z_]+)\nvalue=(?P<val>!num)`,
|
|
|
|
|
},
|
|
|
|
|
Lua: `if is_number(score) then score = score * 2 end; if is_number(val) then val = val * 3 end; return true`,
|
|
|
|
|
Files: []string{"examples/**/*.*"},
|
|
|
|
|
LogLevel: "DEBUG",
|
|
|
|
|
},
|
|
|
|
|
// Use equals operator shorthand and boolean variable
|
|
|
|
|
{
|
|
|
|
|
Name: "IsolatedTagUpdate",
|
|
|
|
|
Regex: "<tag>(.*?)<\\/tag>",
|
|
|
|
|
Lua: "string.upper(s1)",
|
|
|
|
|
Files: []string{"config.xml"},
|
|
|
|
|
Isolate: true,
|
|
|
|
|
NoDedup: true,
|
|
|
|
|
Name: "EnableFlags",
|
|
|
|
|
Regex: `enabled\s*=\s*(true|false)`,
|
|
|
|
|
Lua: `= $enabled`,
|
|
|
|
|
Files: []string{"**/*.toml"},
|
|
|
|
|
},
|
|
|
|
|
// Demonstrate NoDedup to allow overlapping replacements
|
|
|
|
|
{
|
|
|
|
|
Name: "OverlappingGroups",
|
|
|
|
|
Regex: `(?P<a>!num)(?P<b>!num)`,
|
|
|
|
|
Lua: `a = num(a) + 1; b = num(b) + 1; return true`,
|
|
|
|
|
Files: []string{"overlap/**/*.txt"},
|
|
|
|
|
NoDedup: true,
|
|
|
|
|
},
|
|
|
|
|
// Isolate command example operating on entire matched block
|
|
|
|
|
{
|
|
|
|
|
Name: "IsolateUppercaseBlock",
|
|
|
|
|
Regex: `BEGIN\n(?P<block>!any)\nEND`,
|
|
|
|
|
Lua: `block = upper(block); return true`,
|
|
|
|
|
Files: []string{"logs/**/*.log"},
|
|
|
|
|
Isolate: true,
|
|
|
|
|
LogLevel: "TRACE",
|
|
|
|
|
},
|
|
|
|
|
// Using !rep placeholder and arrays of files
|
|
|
|
|
{
|
|
|
|
|
Name: "RepeatPlaceholderExample",
|
|
|
|
|
Regex: `name: (.*) !rep(, .* , 2)`,
|
|
|
|
|
Lua: `-- no-op, just demonstrate placeholder; return false`,
|
|
|
|
|
Files: []string{"lists/**/*.yml", "lists/**/*.yaml"},
|
|
|
|
|
},
|
|
|
|
|
// Using string variable in Lua expression
|
|
|
|
|
{
|
|
|
|
|
Name: "PrefixKeys",
|
|
|
|
|
Regex: `(?P<key>[A-Za-z0-9_]+)\s*=`,
|
|
|
|
|
Lua: `key = $prefix .. key; return true`,
|
|
|
|
|
Files: []string{"**/*.properties"},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data, err := yaml.Marshal(commands)
|
|
|
|
@@ -428,31 +516,36 @@ func RunOtherCommands(file string, fileDataStr string, association utils.FileCom
|
|
|
|
|
cmdLogger = cmdLog
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cmdLogger.Debug("Begin processing file with command %q", command.Regex)
|
|
|
|
|
numCommandsConsidered++
|
|
|
|
|
newModifications, err := processor.ProcessRegex(fileDataStr, command, file)
|
|
|
|
|
if err != nil {
|
|
|
|
|
runOtherCommandsLogger.Error("Failed to process file with command %q: %v", command.Regex, err)
|
|
|
|
|
continue
|
|
|
|
|
patterns := command.Regexes
|
|
|
|
|
if len(patterns) == 0 {
|
|
|
|
|
patterns = []string{command.Regex}
|
|
|
|
|
}
|
|
|
|
|
modifications = append(modifications, newModifications...)
|
|
|
|
|
// It is not guranteed that all the commands will be executed...
|
|
|
|
|
// TODO: Make this better
|
|
|
|
|
// We'd have to pass the map to executemodifications or something...
|
|
|
|
|
count, ok := stats.ModificationsPerCommand.Load(command.Name)
|
|
|
|
|
if !ok {
|
|
|
|
|
count = 0
|
|
|
|
|
}
|
|
|
|
|
stats.ModificationsPerCommand.Store(command.Name, count.(int)+len(newModifications))
|
|
|
|
|
for idx, pattern := range patterns {
|
|
|
|
|
tmpCmd := command
|
|
|
|
|
tmpCmd.Regex = pattern
|
|
|
|
|
cmdLogger.Debug("Begin processing file with command %q (pattern %d/%d)", command.Name, idx+1, len(patterns))
|
|
|
|
|
numCommandsConsidered++
|
|
|
|
|
newModifications, err := processor.ProcessRegex(fileDataStr, tmpCmd, file)
|
|
|
|
|
if err != nil {
|
|
|
|
|
runOtherCommandsLogger.Error("Failed to process file with command %q: %v", command.Name, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
modifications = append(modifications, newModifications...)
|
|
|
|
|
count, ok := stats.ModificationsPerCommand.Load(command.Name)
|
|
|
|
|
if !ok {
|
|
|
|
|
count = 0
|
|
|
|
|
}
|
|
|
|
|
stats.ModificationsPerCommand.Store(command.Name, count.(int)+len(newModifications))
|
|
|
|
|
|
|
|
|
|
cmdLogger.Debug("Command %q generated %d modifications", command.Name, len(newModifications))
|
|
|
|
|
cmdLogger.Trace("Modifications generated by command %q: %v", command.Name, newModifications)
|
|
|
|
|
if len(newModifications) == 0 {
|
|
|
|
|
cmdLogger.Debug("No modifications yielded by command %q", command.Name)
|
|
|
|
|
cmdLogger.Debug("Command %q generated %d modifications (pattern %d/%d)", command.Name, len(newModifications), idx+1, len(patterns))
|
|
|
|
|
cmdLogger.Trace("Modifications generated by command %q: %v", command.Name, newModifications)
|
|
|
|
|
if len(newModifications) == 0 {
|
|
|
|
|
cmdLogger.Debug("No modifications yielded by command %q (pattern %d/%d)", command.Name, idx+1, len(patterns))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
runOtherCommandsLogger.Debug("Aggregated %d modifications from %d commands", len(modifications), numCommandsConsidered)
|
|
|
|
|
runOtherCommandsLogger.Debug("Aggregated %d modifications from %d command-pattern runs", len(modifications), numCommandsConsidered)
|
|
|
|
|
runOtherCommandsLogger.Trace("All aggregated modifications: %v", modifications)
|
|
|
|
|
|
|
|
|
|
if len(modifications) == 0 {
|
|
|
|
@@ -480,27 +573,35 @@ func RunIsolateCommands(association utils.FileCommandAssociation, file string, f
|
|
|
|
|
anythingDone := false
|
|
|
|
|
for _, isolateCommand := range association.IsolateCommands {
|
|
|
|
|
runIsolateCommandsLogger.Debug("Begin processing file with isolate command %q", isolateCommand.Regex)
|
|
|
|
|
modifications, err := processor.ProcessRegex(fileDataStr, isolateCommand, file)
|
|
|
|
|
if err != nil {
|
|
|
|
|
runIsolateCommandsLogger.Error("Failed to process file with isolate command %q: %v", isolateCommand.Regex, err)
|
|
|
|
|
continue
|
|
|
|
|
patterns := isolateCommand.Regexes
|
|
|
|
|
if len(patterns) == 0 {
|
|
|
|
|
patterns = []string{isolateCommand.Regex}
|
|
|
|
|
}
|
|
|
|
|
for idx, pattern := range patterns {
|
|
|
|
|
tmpCmd := isolateCommand
|
|
|
|
|
tmpCmd.Regex = pattern
|
|
|
|
|
modifications, err := processor.ProcessRegex(fileDataStr, tmpCmd, file)
|
|
|
|
|
if err != nil {
|
|
|
|
|
runIsolateCommandsLogger.Error("Failed to process file with isolate command %q (pattern %d/%d): %v", isolateCommand.Name, idx+1, len(patterns), err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(modifications) == 0 {
|
|
|
|
|
runIsolateCommandsLogger.Debug("Isolate command %q produced no modifications", isolateCommand.Name)
|
|
|
|
|
continue
|
|
|
|
|
if len(modifications) == 0 {
|
|
|
|
|
runIsolateCommandsLogger.Debug("Isolate command %q produced no modifications (pattern %d/%d)", isolateCommand.Name, idx+1, len(patterns))
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
anythingDone = true
|
|
|
|
|
|
|
|
|
|
runIsolateCommandsLogger.Debug("Executing %d isolate modifications for file", len(modifications))
|
|
|
|
|
runIsolateCommandsLogger.Trace("Isolate modifications: %v", modifications)
|
|
|
|
|
var count int
|
|
|
|
|
fileDataStr, count = utils.ExecuteModifications(modifications, fileDataStr)
|
|
|
|
|
runIsolateCommandsLogger.Trace("File data after isolate modifications: %s", utils.LimitString(fileDataStr, 200))
|
|
|
|
|
|
|
|
|
|
atomic.AddInt64(&stats.TotalModifications, int64(count))
|
|
|
|
|
|
|
|
|
|
runIsolateCommandsLogger.Info("Executed %d isolate modifications for file", count)
|
|
|
|
|
}
|
|
|
|
|
anythingDone = true
|
|
|
|
|
|
|
|
|
|
runIsolateCommandsLogger.Debug("Executing %d isolate modifications for file", len(modifications))
|
|
|
|
|
runIsolateCommandsLogger.Trace("Isolate modifications: %v", modifications)
|
|
|
|
|
var count int
|
|
|
|
|
fileDataStr, count = utils.ExecuteModifications(modifications, fileDataStr)
|
|
|
|
|
runIsolateCommandsLogger.Trace("File data after isolate modifications: %s", utils.LimitString(fileDataStr, 200))
|
|
|
|
|
|
|
|
|
|
atomic.AddInt64(&stats.TotalModifications, int64(count))
|
|
|
|
|
|
|
|
|
|
runIsolateCommandsLogger.Info("Executed %d isolate modifications for file", count)
|
|
|
|
|
}
|
|
|
|
|
if !anythingDone {
|
|
|
|
|
runIsolateCommandsLogger.Debug("No isolate modifications were made for file")
|
|
|
|
|