package processor import ( "fmt" "strconv" "strings" lua "github.com/yuin/gopher-lua" ) // Processor defines the interface for all file processors type Processor interface { // Process handles processing a file with the given pattern and Lua expression Process(filename string, pattern string, luaExpr string) (int, int, error) // ProcessContent handles processing a string content directly with the given pattern and Lua expression // Returns the modified content, modification count, match count, and any error ProcessContent(content string, pattern string, luaExpr string) (string, int, int, error) // ToLua converts processor-specific data to Lua variables ToLua(L *lua.LState, data interface{}) error // FromLua retrieves modified data from Lua FromLua(L *lua.LState) (interface{}, error) } // ModificationRecord tracks a single value modification type ModificationRecord struct { File string OldValue string NewValue string Operation string Context string } func NewLuaState() (*lua.LState, error) { L := lua.NewState() // defer L.Close() // Load math library L.Push(L.GetGlobal("require")) L.Push(lua.LString("math")) if err := L.PCall(1, 1, nil); err != nil { return nil, fmt.Errorf("error loading Lua math library: %v", err) } // Initialize helper functions if err := InitLuaHelpers(L); err != nil { return nil, err } return L, nil } // ToLua converts a struct or map to a Lua table recursively func ToLua(L *lua.LState, data interface{}) (lua.LValue, error) { switch v := data.(type) { case map[string]interface{}: luaTable := L.NewTable() for key, value := range v { luaValue, err := ToLua(L, value) if err != nil { return nil, err } luaTable.RawSetString(key, luaValue) } return luaTable, nil case []interface{}: luaTable := L.NewTable() for i, value := range v { luaValue, err := ToLua(L, value) if err != nil { return nil, err } luaTable.RawSetInt(i+1, luaValue) // Lua arrays are 1-indexed } return luaTable, nil case string: return lua.LString(v), nil case bool: return lua.LBool(v), nil case float64: return lua.LNumber(v), nil default: return nil, fmt.Errorf("unsupported data type: %T", data) } } // FromLua converts a Lua table to a struct or map recursively func FromLua(L *lua.LState, luaValue lua.LValue) (interface{}, error) { switch v := luaValue.(type) { case *lua.LTable: result := make(map[string]interface{}) v.ForEach(func(key lua.LValue, value lua.LValue) { result[key.String()], _ = FromLua(L, value) }) // This may be a bit wasteful... // Hopefully it won't run often enough to matter isArray := true for key := range result { _, err := strconv.Atoi(key) if err != nil { isArray = false break } } if isArray { list := make([]interface{}, 0, len(result)) for _, value := range result { list = append(list, value) } return list, nil } return result, nil case lua.LString: return string(v), nil case lua.LBool: return bool(v), nil case lua.LNumber: return float64(v), nil default: return nil, nil } } // InitLuaHelpers initializes common Lua helper functions func InitLuaHelpers(L *lua.LState) error { helperScript := ` -- Custom Lua helpers for math operations function min(a, b) return math.min(a, b) end function max(a, b) return math.max(a, b) end function round(x) return math.floor(x + 0.5) end function floor(x) return math.floor(x) end function ceil(x) return math.ceil(x) end function upper(s) return string.upper(s) end function lower(s) return string.lower(s) end -- String to number conversion helper function num(str) return tonumber(str) or 0 end -- Number to string conversion function str(num) return tostring(num) end -- Check if string is numeric function is_number(str) return tonumber(str) ~= nil end ` if err := L.DoString(helperScript); err != nil { return fmt.Errorf("error loading helper functions: %v", err) } return nil } // Helper utility functions // LimitString truncates a string to maxLen and adds "..." if truncated func LimitString(s string, maxLen int) string { s = strings.ReplaceAll(s, "\n", "\\n") if len(s) <= maxLen { return s } return s[:maxLen-3] + "..." } // BuildLuaScript prepares a Lua expression from shorthand notation func BuildLuaScript(luaExpr string) string { // Auto-prepend v1 for expressions starting with operators if strings.HasPrefix(luaExpr, "*") || strings.HasPrefix(luaExpr, "/") || strings.HasPrefix(luaExpr, "+") || strings.HasPrefix(luaExpr, "-") || strings.HasPrefix(luaExpr, "^") || strings.HasPrefix(luaExpr, "%") { luaExpr = "v1 = v1" + luaExpr } else if strings.HasPrefix(luaExpr, "=") { // Handle direct assignment with = operator luaExpr = "v1 " + luaExpr } // Add assignment if needed if !strings.Contains(luaExpr, "=") { luaExpr = "v1 = " + luaExpr } return luaExpr } // Max returns the maximum of two integers func Max(a, b int) int { if a > b { return a } return b } // Min returns the minimum of two integers func Min(a, b int) int { if a < b { return a } return b }