Normalisation au sein des representers


Pour que des solutions qui ne diffèrent que par des détails non essentiels aient la même représentation, le representer doit appliquer des normalisations. De manière générale, le processus de création d'une représentation normalisée se déroule comme ceci :

  • Analyser le code de la solution pour en construire un arbre syntaxique abstrait (AST, Abstract Syntax Tree)
  • Appliquer des normalisations à l'AST
  • Convertir l'AST normalisé en une string
  • Écrire la string de l'AST normalisé dans un fichier nommé representation.txt (voir l'interface)

Note cependant que la représentation ne doit pas nécessairement être un AST : elle peut tout aussi bien être du code (normalisé) classique, selon ce qui marche le mieux pour ton parcours.

Pour t'aider à démarrer, on va maintenant présenter quelques stratégies de normalisation courantes.

Remarque 1 : les exemples de normalisation ne s'enchaînent pas, ils illustrent chacun une normalisation précise. Un vrai representer aura tout intérêt à les appliquer successivement.

Remarque 2 : le code de ces recommandations sera en C#, mais les recommandations sont indépendantes du langage.

Normaliser les identifiants

Pour que les représentations soient indépendantes des noms, les noms définis par l'utilisateur (variables, fonctions, etc.) peuvent être remplacés par des espaces réservés. Dans ce cas, il faut produire un mapping.json (voir l'interface).

Il est important de noter que tous les noms identiques doivent être remplacés par le même espace réservé, quelle que soit la portée.

Code source

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

Représentation

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

Normaliser les espaces

Les espaces incohérents sont si fréquents que les normaliser est une étape de normalisation courante. Les fins de ligne doivent elles aussi être normalisées.

Code source

using   System;

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

Représentation

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

Normaliser les blocs

Dans beaucoup de langages, l'utilisateur dispose d'une certaine liberté pour définir un bloc (ou une portée). Par exemple, dans la plupart des langages de type C, la portée se déclare entre accolades. En général, peu importe qu'on les place sur la même ligne ou sur la suivante, ce qui permet de normaliser ce point.

Code source

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

        return 1;
    }
}

Représentation

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

        return 1;
    }
}

Supprimer le code non significatif

Tous les morceaux de code n'ont pas d'importance pour le representer, et peuvent donc être supprimés. Par exemple, dans la plupart des langages, les commentaires sont non significatifs et peuvent être supprimés sans risque.

Code source

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

Représentation

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

Normaliser l'ordre quand il n'est pas significatif

Dans certains cas, l'ordre du code n'a pas d'importance. Pour éviter que le même code dans un ordre différent produise des représentations différentes, il peut être utile de le trier. En général, les éléments à trier sont des fonctions ou des déclarations. Trouver le ou les critères de tri peut être délicat, tout comme leur implémentation. Un critère pourrait être le nombre de nœuds enfants que contient un nœud de l'AST, un autre le nom du type du premier nœud enfant.

Cet exemple trie selon une sorte de longueur de fonction :

Code source

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

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

Représentation

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

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