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:
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.
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.
public static class Fake
{
public static int Test(int input)
{
var test = input + 2;
return test;
}
}
public static class PLACEHOLDER_1
{
public static int PLACEHOLDER_2(int PLACEHOLDER_3)
{
var PLACEHOLDER_4 = PLACEHOLDER_3 + 2;
return PLACEHOLDER_4;
}
}
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.
using System;
public static class Fake
{
public static DateTime Add (DateTime birthDate)
{
return birthDate.Add( TimeSpan.FromSeconds ( 10 ) ) ;
}
}
public static class Fake
{
public static DateTime Add(DateTime birthDate)
{
return birthDate.Add(TimeSpan.FromSeconds(10));
}
}
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.
public static class Fake {
public static int Test() {
if (1 > 2) {
return 0;
}
return 1;
}
}
public static class Fake
{
public static int Test()
{
if (1 > 2)
{
return 0;
}
return 1;
}
}
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.
/*
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
}
}
public static class Fake
{
public static string Test()
{
return "Test";
}
}
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:
public static class Fake
{
public static string Test2()
{
int a = "Test2";
return a;
}
public static string Test()
{
return "Test";
}
}
public static class Fake
{
public static string Test()
{
return "Test";
}
public static string Test2()
{
int a = "Test2";
return a;
}
}