textos texto sublime programa para online línea lineas gratis entre encontrar diferencias comparar codigo carpetas archivos parsing go compare abstract-syntax-tree

parsing - texto - programa para comparar archivos y carpetas



¿Cómo puedo comparar dos archivos de código fuente/árboles ast? (2)

Bueno, una forma sencilla de lograr esto es usar la biblioteca go/printer , que le da un mejor control del formato de salida y es básicamente como ejecutar gofmt en la fuente, normalizando ambos árboles:

package main import ( "fmt" "go/parser" "go/token" "go/printer" //"reflect" "bytes" ) func main() { stub1 := `package main func myfunc(s string) error { return nil }` stub2 := `package main func myfunc(s string) error { return nil }` fset1 := token.NewFileSet() r1, err := parser.ParseFile(fset1, "", stub1, parser.AllErrors) if err != nil { panic(err) } fset2 := token.NewFileSet() r2, err := parser.ParseFile(fset1, "", stub2, parser.AllErrors) if err != nil { panic(err) } // we create two output buffers for each source tree out1 := bytes.NewBuffer(nil) out2 := bytes.NewBuffer(nil) // we use the same printer config for both conf := &printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8} // print to both outputs if err := conf.Fprint(out1, fset1, r1); err != nil { panic(err) } if err := conf.Fprint(out2, fset2, r2); err != nil { panic(err) } // they should be identical! if string(out1.Bytes()) != string(out2.Bytes()) { panic(string(out1.Bytes()) +"/n" + string(out2.Bytes())) } else { fmt.Println("A-OKAY!") } }

Por supuesto, este código debe ser refaccionado para que no parezca tan estúpido. Otro enfoque es que en lugar de usar DeepEqual, cree una función de comparación de árbol, que omita nodos irrelevantes.

Estoy generando algo de código fuente usando el paquete de templates (¿hay un método mejor?) Y parte de las pruebas que necesito para verificar si la salida coincide con el código fuente esperado.

  • Intenté una comparación de cadenas pero falla debido a los espacios adicionales / nuevas líneas generadas por el paquete de plantillas. También he intentado format.Source . format.Source sin éxito. ( FALLAR)
  • Intenté analizar el ast de ambas fuentes (ver más abajo) pero el ast tampoco coincide, incluso si el código es básicamente el mismo, excepto las nuevas líneas / espacios. (FALLAR)

    paquete principal

    import ( "fmt" "go/parser" "go/token" "reflect" ) func main() { stub1 := `package main func myfunc(s string) error { return nil }` stub2 := `package main func myfunc(s string) error { return nil }` fset := token.NewFileSet() r1, err := parser.ParseFile(fset, "", stub1, parser.AllErrors) if err != nil { panic(err) } fset = token.NewFileSet() r2, err := parser.ParseFile(fset, "", stub2, parser.AllErrors) if err != nil { panic(err) } if !reflect.DeepEqual(r1, r2) { fmt.Printf("e %v, r %s, ", r1, r2) } }

Playground


Esto fue más fácil de lo que pensaba. Todo lo que tenía que hacer era eliminar las nuevas líneas vacías (después del formateo). A continuación se muestra el código.

package main import ( "fmt" "go/format" "strings" ) func main() { a, err := fmtSource(stub1) if err != nil { panic(err) } b, err := fmtSource(stub2) if err != nil { panic(err) } if a != b { fmt.Printf("a %v, /n b %v", a, b) } } func fmtSource(source string) (string, error) { if !strings.Contains(source, "package") { source = "package main/n" + source } b, err := format.Source([]byte(source)) if err != nil { return "", err } // cleanLine replaces double space with one space cleanLine := func(s string)string{ sa := strings.Fields(s) return strings.Join(sa, " ") } lines := strings.Split(string(b), "/n") n := 0 var startLn *int for _, line := range lines { if line != "" { line = cleanLine(line) lines[n] = line if startLn == nil { x := n startLn = &x } n++ } } lines = lines[*startLn:n] // Add final "" entry to get trailing newline from Join. if n > 0 && lines[n-1] != "" { lines = append(lines, "") } // Make it pretty b, err = format.Source([]byte(strings.Join(lines, "/n"))) if err != nil { return "", err } return string(b), nil }