Files
BigChef/processor/regex.go

180 lines
4.8 KiB
Go

package processor
import (
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
lua "github.com/yuin/gopher-lua"
)
// RegexProcessor implements the Processor interface using regex patterns
type RegexProcessor struct{}
// Process implements the Processor interface for RegexProcessor
func (p *RegexProcessor) Process(filename string, pattern string, luaExpr string) (int, int, error) {
// Read file content
fullPath := filepath.Join(".", filename)
content, err := os.ReadFile(fullPath)
if err != nil {
return 0, 0, fmt.Errorf("error reading file: %v", err)
}
fileContent := string(content)
// Process the content
modifiedContent, modCount, matchCount, err := p.ProcessContent(fileContent, pattern, luaExpr)
if err != nil {
return 0, 0, err
}
// If we made modifications, save the file
if modCount > 0 {
err = os.WriteFile(fullPath, []byte(modifiedContent), 0644)
if err != nil {
return 0, 0, fmt.Errorf("error writing file: %v", err)
}
}
return modCount, matchCount, nil
}
// ToLua sets capture groups as Lua variables (v1, v2, etc. for numeric values and s1, s2, etc. for strings)
func (p *RegexProcessor) ToLua(L *lua.LState, data interface{}) error {
captures, ok := data.([]string)
if !ok {
return fmt.Errorf("expected []string for captures, got %T", data)
}
// Set variables for each capture group, starting from v1/s1 for the first capture
for i := 1; i < len(captures); i++ {
// Set string version (always available as s1, s2, etc.)
L.SetGlobal(fmt.Sprintf("s%d", i), lua.LString(captures[i]))
// Try to convert to number and set v1, v2, etc.
if val, err := strconv.ParseFloat(captures[i], 64); err == nil {
L.SetGlobal(fmt.Sprintf("v%d", i), lua.LNumber(val))
} else {
// For non-numeric values, set v to 0
L.SetGlobal(fmt.Sprintf("v%d", i), lua.LNumber(0))
}
}
return nil
}
// FromLua implements the Processor interface for RegexProcessor
func (p *RegexProcessor) FromLua(L *lua.LState) (interface{}, error) {
// Get the modified values after Lua execution
modifications := make(map[int]string)
// Check for modifications to v1-v12 and s1-s12
for i := 0; i < 12; i++ {
// Check both v and s variables to see if any were modified
vVarName := fmt.Sprintf("v%d", i+1)
sVarName := fmt.Sprintf("s%d", i+1)
vLuaVal := L.GetGlobal(vVarName)
sLuaVal := L.GetGlobal(sVarName)
// First check string variables (s1, s2, etc.) as they should have priority
if sLuaVal.Type() != lua.LTNil {
modifications[i] = sLuaVal.String()
}
// Then check numeric variables (v1, v2, etc.)
if vLuaVal.Type() != lua.LTNil {
modifications[i] = vLuaVal.String()
}
}
return modifications, nil
}
// ProcessContent applies regex replacement with Lua processing
func (p *RegexProcessor) ProcessContent(content string, pattern string, luaExpr string) (string, int, int, error) {
// Handle special pattern modifications
if !strings.HasPrefix(pattern, "(?s)") {
pattern = "(?s)" + pattern
}
compiledPattern, err := regexp.Compile(pattern)
if err != nil {
return "", 0, 0, fmt.Errorf("error compiling pattern: %v", err)
}
previous := luaExpr
luaExpr = BuildLuaScript(luaExpr)
fmt.Printf("Changing Lua expression from: %s to: %s\n", previous, luaExpr)
L := lua.NewState()
defer L.Close()
// Initialize Lua environment
modificationCount := 0
matchCount := 0
// Load math library
L.Push(L.GetGlobal("require"))
L.Push(lua.LString("math"))
if err := L.PCall(1, 1, nil); err != nil {
return content, 0, 0, fmt.Errorf("error loading Lua math library: %v", err)
}
// Initialize helper functions
if err := InitLuaHelpers(L); err != nil {
return content, 0, 0, err
}
// Process all regex matches
result := compiledPattern.ReplaceAllStringFunc(content, func(match string) string {
matchCount++
captures := compiledPattern.FindStringSubmatch(match)
if len(captures) <= 1 {
// No capture groups, return unchanged
fmt.Println("No capture groups for lua to chew on")
return match
}
if err := p.ToLua(L, captures); err != nil {
fmt.Println("Error setting Lua variables:", err)
return match
}
// Execute the user's Lua code
if err := L.DoString(luaExpr); err != nil {
fmt.Println("Error executing Lua code:", err)
return match // Return unchanged on error
}
// Get modifications from Lua
modResult, err := p.FromLua(L)
if err != nil {
fmt.Println("Error getting modifications:", err)
return match
}
// Apply modifications to the matched text
modsMap, ok := modResult.(map[int]string)
if !ok || len(modsMap) == 0 {
fmt.Println("No modifications to apply")
return match // No changes
}
// Apply the modifications to the original match
result := match
for i, newVal := range modsMap {
oldVal := captures[i+1]
result = strings.Replace(result, oldVal, newVal, 1)
}
modificationCount++
return result
})
return result, modificationCount, matchCount, nil
}