Розповідь про SPM («спам») — процесор джерельного коду

В програмістській практиці часто доводиться використовувати такий метод програмування як copy-paste (копіювання коду з одного місця в інше). Цей метод має істотні недоліки, але часто від нього нікуди подітися і доводиться вручну копіювати а потім вручну правити один і той самий код в десятках чи навіть сотнях місць.

Уявімо що у нас з’явився додатковий помічник, який вміє сам копіювати текст, треба тільки вказати звідки і куди копіювати, а він вже сам буде копіювати текст по сотнях файлів («спамити»). Ми з помічником домовляємося, що області тексту, які потрібно редагувати, відмічаються коментарями. Чого ми точно хочемо уникнути, так це спеціальних файлів зі своїм спеціальним синтаксисом (шаблонів, templates), тому що наше середовище розробки не знайоме з їхнім форматом і відмовиться допомагати при редагуванні шаблонів.

Щоб уникнути двозначностей, ми домовляємося що формат коментарів наступний:

  • коментар має займати всю лінійку тексту;
  • початок блоку відмічається послідовністю "[spm:", будь який текст, "]";
  • кінець блоку відмічається послідовністю "[/spm]";
  • для команд приймемо синтаксис аналогічний командам оболонки (shell);
  • команда копіювання виглядає як "copy_to НАЗВАБУФЕРУ", а команда вставки виглядає як "paste_from НАЗВАБУФЕРУ";
  • інструкції не можуть бути вкладені, при вставлянні тексту у файл, інструкції, якщо вони були у вставленому тексті, мають бути видалені.

Тоді напр. файл foo.js виглядатиме так:

// [spm:copy_to test]
var test = 0;
// [/spm]

А файл bar.js виглядатиме так:

// [spm:paste_from test]
var test = 0;
// [/spm]

У такому випадку наш віртуальний помічник знатиме, що якщо у файл foo.js будуть внесені зміни між коментарями, то цими змінами також потрібно заспамити інші файли які мають посилання на буфер test.

Але часто код потрібно не тільки скопіювати але й підправити, напр. змінити синтаксис. Напр. у типовій аплікації яка реалізує CRUD (Create Retrieve Update Delete - Створити Отримати Обновити Видалити), назви полів, які редагуються, будуть проходити через всю аплікацію — у програмному коді, у SQL, у шаблонах інтерфейсу, і т.д. Напр. поле, яке відповідає за ім’я користувача, може фігурувати у коді як userName чи UserName, у SQL як user_name, у шаблонах інтерфейсу як user_name та "User name:".

Щоб допомогти програмісту, створимо набір обробників тексту, які вміють перетворювати код з одного синтаксису в інший, напр. з синтаксису Яви у синтаксис SQL. Так як ми узгодили що синтаксис команд буде аналогічний синтаксису оболонки, то формат команд буде таким:

Файл Foo.java

// [spm:extract_variables | copy_to test]
public class Foo {
  private String userName;
  private String password;
// [/spm]
  public String getUserName() {
    return userName;
  }
  ...
}

Файл create_database.sql

-- [spm:paste_from test | convert_variables_to_sql_create]
create table Foo (
     userName VARCHAR(100)
     password VARCHAR(100)
);

-- [/spm]

Висновок

Плюси:

  • при правильному використанні, може суттєво знизити об’єм ручного копіюванні тексту з місця на місце;
  • не потрібно створювати окремі шаблони для шматків тексту, зі своїм власним синтаксисом;
  • можна складати складні програми для типових випадків, які керуються з одного місця.

Мінуси:

  • програма прив’язується до процесора(-ів) тексту.
  • синтаксис команд процесора — це ще один синтаксис який потрібно тримати в голові, ще одне місце де можна помилитися.
  • у складних випадках потрібно писати свої процесори тексту, з усіма негативними наслідками.