Нормалізація в репрезентерах


Щоб рішення з несуттєвими відмінностями мали однакове представлення, Representer має застосовувати нормалізацію. Загалом процес створення нормалізованого представлення має такий вигляд:

  • Розібрати код рішення в абстрактне синтаксичне дерево (AST)
  • Застосувати нормалізацію до AST
  • Перетворити нормалізоване AST на рядок
  • Записати нормалізований рядок AST у файл з назвою representation.txt (див. інтерфейс)

Зауважте, що представлення не обовʼязково має бути AST; це може бути звичайний (нормалізований) код, залежно від того, що краще підходить для треку.

Щоб допомогти на початку, розгляньмо кілька поширених стратегій нормалізації.

Примітка 1: приклади нормалізації не надбудовуються один над одним, кожен показує лише одну конкретну нормалізацію. Справжній Representer застосовував би їх послідовно.

Примітка 2: код у цих рекомендаціях буде написано мовою C#, але самі рекомендації не залежать від конкретної мови.

Нормалізація ідентифікаторів

Щоб представлення не залежали від назв, визначені користувачем назви (змінних, функцій тощо) можна замінити на заповнювачі. У такому разі слід створити mapping.json (див. інтерфейс).

Важливо, що всі однакові назви мають бути замінені на той самий заповнювач, незалежно від області видимості.

Вихідний код

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

Нормалізація пробілів

Неоднакові пробіли трапляються так часто, що їхня нормалізація є звичним кроком. Закінчення рядків теж слід нормалізувати.

Вихідний код

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

Нормалізація блоків

У багатьох мовах користувач має певну свободу в тому, як визначати блок (або область видимості). Наприклад, у більшості C-подібних мов область видимості оголошують між фігурними дужками. Зазвичай не має значення, поставити їх в одному рядку чи в наступному, тож це можна нормалізувати.

Вихідний код

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

Вилучення несуттєвого коду

Не всі фрагменти коду є суттєвими для Representer, тож їх можна вилучити. Наприклад, у більшості мов коментарі несуттєві, і їх можна сміливо вилучити.

Вихідний код

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

Нормалізація порядку там, де він несуттєвий

У деяких випадках порядок коду не має значення. Щоб той самий код у різному порядку не створював різні представлення, його корисно відсортувати. Зазвичай сортувати доводиться функції або оголошення. Буває непросто знайти мірило для сортування, а також непросто це реалізувати. Одним мірилом може бути кількість дочірніх вузлів AST у вузлі, іншим - назва типу першого дочірнього вузла.

У цьому прикладі сортування відбувається за певною мірою довжини функції:

Вихідний код

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