Normalização dentro dos representadores


Para que soluções com diferenças não essenciais tenham a mesma representação, o Representer deve aplicar normalizações. Em geral, o processo de criar uma representação normalizada é assim:

  • Converter o código da solução em uma Árvore de Sintaxe Abstrata (AST)
  • Aplicar normalizações à AST
  • Converter a AST normalizada em uma string
  • Gravar a string da AST normalizada em um arquivo chamado representation.txt (veja a interface)

Note, porém, que a representação não precisa ser uma AST: ela pode ser código comum (normalizado), o que funcionar melhor para a sua trilha.

Para ajudar você a começar, vamos apresentar agora algumas estratégias de normalização comuns.

Observação 1: os exemplos de normalização não se acumulam; cada um mostra apenas uma normalização específica. Um Representer de verdade aplicaria todas em sequência.

Observação 2: o código destas diretrizes estará em C#, mas as diretrizes não dependem da linguagem.

Normalizar identificadores

Para que as representações não dependam dos nomes usados, nomes definidos por quem escreve o código (como variáveis, funções etc.) podem ser substituídos por placeholders. Nesse caso, você deve gerar um mapping.json (veja a interface).

É importante notar que todos os nomes idênticos devem ser substituídos pelo mesmo placeholder, independentemente do escopo.

Código-fonte

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

Representação

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

Normalizar espaços em branco

Espaços em branco inconsistentes são tão frequentes que normalizá-los é uma etapa de normalização comum. Os finais de linha também devem ser normalizados.

Código-fonte

using   System;

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

Representação

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

Normalizar blocos

Em muitas linguagens, quem escreve o código tem certa liberdade para definir um bloco (ou escopo). Por exemplo, na maioria das linguagens parecidas com C, o escopo é declarado entre chaves. Normalmente não faz diferença colocar as chaves na mesma linha ou na linha seguinte, então dá para normalizar isso.

Código-fonte

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

        return 1;
    }
}

Representação

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

        return 1;
    }
}

Remover código insignificante

Nem todo trecho de código é significativo para o Representer, e esses trechos podem ser removidos. Por exemplo, na maioria das linguagens os comentários não são significativos e podem ser removidos sem problema.

Código-fonte

/*
   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
    }
}

Representação

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

Normalizar a ordem quando ela for insignificante

Em alguns casos, a ordem do código não importa. Para evitar que o mesmo código em ordens diferentes crie representações diferentes, pode ser útil ordená-lo. Normalmente os itens a ordenar são funções ou declarações. Pode ser complicado encontrar a(s) métrica(s) de ordenação e também complicado implementar. Uma métrica poderia ser quantos nós filhos da AST um nó contém; outra, o nome do tipo do primeiro nó filho.

Este exemplo ordena por algum tipo de tamanho de função:

Código-fonte

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

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

Representação

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

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