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:
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.
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.
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;
}
}
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.
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));
}
}
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.
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;
}
}
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.
/*
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";
}
}
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:
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;
}
}