Normalización en los representadores


Para que las soluciones con diferencias no esenciales tengan la misma representación, el Representer debe aplicar normalizaciones. En general, el proceso de crear una representación normalizada es el siguiente:

  • Analizar sintácticamente el código de la solución y obtener un árbol de sintaxis abstracta (AST)
  • Aplicar normalizaciones al AST
  • Convertir el AST normalizado a un string
  • Escribir el string del AST normalizado en un archivo llamado representation.txt (consulta la interfaz)

Ten en cuenta, no obstante, que la representación no tiene que ser un AST; puede ser código normal (normalizado), lo que mejor funcione para tu track.

Para ayudarte a empezar, vamos a presentar ahora algunas estrategias de normalización habituales.

Nota 1: los ejemplos de normalización no se construyen unos sobre otros, solo muestran una normalización concreta. Un Representer real querría aplicarlas de forma sucesiva.

Nota 2: el código de estas guías estará en C#, pero las guías son independientes del lenguaje.

Normalizar identificadores

Para que las representaciones no dependan de los nombres, los nombres definidos por el usuario (como variables, funciones, etc.) se pueden sustituir por marcadores de posición. En este caso, debería generarse un mapping.json (consulta la interfaz).

Es importante tener en cuenta que todos los nombres idénticos deben sustituirse por el mismo marcador de posición, independientemente del scope.

Código fuente

public static class Fake
{
    public static int Test(int input)
    {
        var test = input + 2;
        return test;
    }
}

Representación

public static class PLACEHOLDER_1
{
    public static int PLACEHOLDER_2(int PLACEHOLDER_3)
    {
        var PLACEHOLDER_4 = PLACEHOLDER_3 + 2;
        return PLACEHOLDER_4;
    }
}

Normalizar los espacios en blanco

Los espacios en blanco inconsistentes son tan frecuentes que normalizarlos es un paso de normalización habitual. También deberían normalizarse los finales de línea.

Código fuente

using   System;

     public static    class Fake
{
    public     static   DateTime Add    (DateTime    birthDate)
    {
        return birthDate.Add( TimeSpan.FromSeconds   (   10  ) )   ;
    }
}

Representación

public static class Fake
{
    public static DateTime Add(DateTime birthDate)
    {
        return birthDate.Add(TimeSpan.FromSeconds(10));
    }
}

Normalizar los bloques

En muchos lenguajes, el usuario tiene cierta libertad para definir un bloque (o scope). Por ejemplo, en la mayoría de los lenguajes de tipo C, el scope se declara entre llaves. Normalmente da igual si las pones en la misma línea o en la siguiente, así que esto se podría normalizar.

Código fuente

public static class Fake {
    public static int Test() {
        if (1 > 2) {
            return 0;
        }

        return 1;
    }
}

Representación

public static class Fake
{
    public static int Test()
    {
        if (1 > 2)
        {
            return 0;
        }

        return 1;
    }
}

Eliminar el código insignificante

No todas las partes del código son significativas para el Representer, por lo que se pueden eliminar. Por ejemplo, en la mayoría de los lenguajes los comentarios son insignificantes y se pueden eliminar sin problema.

Código fuente

/*
   These are some very nice
   comments spanning multiple lines
*/
public static class Fake
{
    // Nice method
    public static string Test()
    {
        return "Test"; // This is very nice
    }
}

Representación

public static class Fake
{
    public static string Test()
    {
        return "Test";
    }
}

Normalizar el orden cuando no es significativo

En algunos casos, el orden del código no importa. Para evitar que el mismo código en distinto orden genere representaciones diferentes, puede ser útil ordenarlo. Normalmente, los elementos que hay que ordenar son funciones o declaraciones. Puede ser complicado encontrar la métrica (o métricas) por la que ordenar y también complicado de implementar. Una métrica podría ser cuántos nodos hijos del AST contiene el nodo; otra, el nombre del tipo del primer nodo hijo.

Este ejemplo ordena por algún tipo de longitud de función:

Código fuente

public static class Fake
{
    public static string Test2()
    {
        int a = "Test2";
        return a;
    }

    public static string Test()
    {
        return "Test";
    }
}

Representación

public static class Fake
{
    public static string Test()
    {
        return "Test";
    }

    public static string Test2()
    {
        int a = "Test2";
        return a;
    }
}