Normalização nos representadores


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

  • Fazer a análise sintática do código da solução, obtendo uma Árvore de Sintaxe Abstrata (AST)
  • Aplicar normalizações à AST
  • Converter a AST normalizada numa string
  • Escrever a string da AST normalizada num ficheiro chamado representation.txt (ver a interface)

Tem em atenção, no entanto, que a representação não tem de ser uma AST: pode ser código normal (normalizado), consoante o que funcione melhor para a tua track.

Para te ajudar a começar, apresentamos agora algumas estratégias de normalização comuns.

Nota 1: os exemplos de normalização não assentam uns nos outros, mostram apenas uma normalização específica. Um Representer real iria aplicá-las em sequência.

Nota 2: o código destas orientações será em C#, mas as orientações são independentes da linguagem.

Normaliza os identificadores

Para que as representações não dependam dos nomes, os nomes definidos pelo utilizador (variáveis, funções, etc.) podem ser substituídos por marcadores de posição. Nesse caso, deve ser produzido um mapping.json (ver a interface).

É importante notar que todos os nomes idênticos têm de ser substituídos pelo mesmo marcador de posição, independentemente do âmbito.

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;
    }
}

Normaliza os espaços em branco

Os espaços em branco inconsistentes são tão frequentes que normalizá-los é um passo de normalização comum. As terminações de linha também devem ser normalizadas.

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));
    }
}

Normaliza os blocos

Em muitas linguagens, o utilizador tem alguma liberdade na forma como define um bloco (ou âmbito). Por exemplo, na maioria das linguagens do tipo C, o âmbito é declarado entre chavetas. Normalmente, não importa se as pões na mesma linha ou na linha seguinte, por isso isto pode ser normalizado.

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;
    }
}

Remove código insignificante

Nem todos os pedaços de código são significativos para o Representer, pelo que podem ser removidos. Por exemplo, na maioria das linguagens os comentários são insignificantes 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";
    }
}

Normaliza a ordem quando não é significativa

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

Este exemplo ordena por uma espécie de comprimento da 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;
    }
}