Normalizzazione nei Representer


Per fare in modo che soluzioni con differenze non essenziali abbiano la stessa rappresentazione, il Representer dovrebbe applicare delle normalizzazioni. In generale, il processo per creare una rappresentazione normalizzata si svolge così:

  • Analizzare il codice della soluzione e convertirlo in un albero sintattico astratto (AST)
  • Applicare le normalizzazioni all'AST
  • Convertire l'AST normalizzato in una stringa
  • Scrivere la stringa dell'AST normalizzato in un file chiamato representation.txt (vedi l'interfaccia)

Tieni però presente che la rappresentazione non deve per forza essere un AST: può anche essere codice normale (normalizzato), a seconda di cosa funziona meglio per il tuo track.

Per aiutarti a muovere i primi passi, vediamo ora alcune strategie di normalizzazione comuni.

Nota 1: gli esempi di normalizzazione non si basano l'uno sull'altro, mostrano solo una normalizzazione specifica. Un Representer reale vorrà applicarli in successione.

Nota 2: il codice di queste linee guida sarà in C#, ma le linee guida sono indipendenti dal linguaggio.

Normalizza gli identificatori

Per rendere le rappresentazioni indipendenti dai nomi, i nomi definiti dall'utente (come variabili, funzioni e così via) possono essere sostituiti con dei segnaposto. In questo caso, va prodotto un file mapping.json (vedi l'interfaccia).

È importante notare che tutti i nomi identici devono essere sostituiti con lo stesso segnaposto, qualunque sia lo scope.

Codice sorgente

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

Rappresentazione

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

Normalizza gli spazi bianchi

Gli spazi bianchi incoerenti sono così frequenti che normalizzarli è un passo di normalizzazione comune. Anche i fine riga vanno normalizzati.

Codice sorgente

using   System;

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

Rappresentazione

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

Normalizza i blocchi

In molti linguaggi, l'utente ha una certa libertà su come definire un blocco (o uno scope). Per esempio, nella maggior parte dei linguaggi simili al C, lo scope si dichiara tra parentesi graffe. Di solito non importa se le metti sulla stessa riga o su quella successiva, quindi questo aspetto si può normalizzare.

Codice sorgente

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

        return 1;
    }
}

Rappresentazione

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

        return 1;
    }
}

Rimuovi il codice non significativo

Non tutti i pezzi di codice sono significativi per il Representer, e quelli che non lo sono si possono rimuovere. Per esempio, nella maggior parte dei linguaggi i commenti non sono significativi e si possono rimuovere senza problemi.

Codice sorgente

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

Rappresentazione

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

Normalizza l'ordine dove non è significativo

In alcuni casi l'ordine del codice non ha importanza. Per evitare che lo stesso codice in un ordine diverso produca rappresentazioni diverse, può essere utile ordinarlo. Di solito gli elementi da ordinare sono funzioni o dichiarazioni. Può essere complicato trovare il criterio (o i criteri) in base a cui ordinare, ed altrettanto complicato implementarlo. Un criterio potrebbe essere quanti nodi figli contiene un nodo dell'AST, un altro il nome del tipo del primo nodo figlio.

Questo esempio ordina secondo una sorta di lunghezza della funzione:

Codice sorgente

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

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

Rappresentazione

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

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