Статьи

Руководство по генерации кода

Есть много применений для генерации кода:

  • Мы можем генерировать повторяющийся код из схем или источника информации, которая у нас есть. Например, мы можем генерировать объекты доступа к данным из файлов схемы базы данных.
  • Мы можем генерировать код из мастеров
  • Мы можем генерировать скелеты приложений из простых моделей. Мы написали о примере здесь: Генерация кода с Roslyn: класс скелета из UML
  • Мы можем создавать целые приложения из DSL высокого уровня
  • Мы можем генерировать код из информации, которую мы получили, обрабатывая существующие документы
  • Мы можем генерировать код из информации, которую мы получили путем обратного инжиниринга кода, написанного с использованием других языков или структур
  • Некоторые IDE имеют функциональность для генерации стандартного кода, например методы equals или hashCode в Java

Таким образом, генерация кода может использоваться для небольших частей кода или целых приложений. Он может использоваться с различными языками программирования и с использованием различных методов.

В этой статье мы рассмотрим все возможности, предоставляемые генерацией кода, чтобы дать вам полный обзор по этому вопросу.

Зачем использовать генерацию кода

Оснований для использования генерации кода в основном четыре: производительность , упрощение, переносимость и согласованность .

производительность

При генерации кода вы пишете генератор один раз, и его можно использовать столько раз, сколько вам нужно. Предоставление определенных входных данных для генератора и его вызов значительно быстрее, чем написание кода вручную, поэтому генерация кода позволяет сэкономить время.

упрощение

С генерацией кода вы генерируете свой код из некоторого абстрактного описания . Это означает, что ваш источник правды становится этим описанием, а не кодом. Это описание обычно легче анализировать и проверять по сравнению со всем сгенерированным кодом.

портативность

Как только у вас есть процесс для генерации кода для определенного языка или платформы, вы можете просто изменить генератор и выбрать другой язык или структуру . Вы также можете настроить таргетинг на несколько платформ одновременно. Например, с помощью генератора парсеров вы можете получить парсер в C #, Java и C ++. Другой пример: вы можете написать диаграмму UML и использовать генерацию кода для создания как каркасного класса в C #, так и кода SQL для создания базы данных для MySQL. Таким образом, одно и то же абстрактное описание может быть использовано для создания различных видов артефактов.

консистенция

С генерацией кода вы всегда получаете ожидаемый код . Сгенерированный код разработан в соответствии с теми же принципами, соответствие правил именования и т. Д. Код всегда работает так, как вы ожидаете, конечно, за исключением ошибок в генераторе. Качество кода соответствует. Вместо кода, написанного вручную, вы можете заставить разных разработчиков использовать разные стили и иногда вносить ошибки даже в самый повторяющийся код.

Почему бы не использовать генерацию кода

Поскольку все инструменты генерации кода не совершенны, в основном это две проблемы: обслуживание и сложность .

Обслуживание

Когда вы используете инструмент генератора кода, ваш код становится зависимым от него. Инструмент генератора кода должен быть сохранен. Если вы создали его, вы должны продолжать обновлять его, если вы просто используете существующий, вы должны надеяться, что кто-то продолжит поддерживать его, или вы должны взять на себя ответственность. Так что преимущества генерации кода не бесплатны. Это особенно рискованно, если у вас нет или вы не можете найти нужные навыки для работы на генераторе.

сложность

Код, сгенерированный автоматически, имеет тенденцию быть более сложным, чем код, написанный вручную. Иногда это связано с клейким кодом, необходимым для соединения разных частей, или с тем фактом, что генератор поддерживает больше вариантов использования, чем тот, который вам нужен. Во втором случае сгенерированный код может сделать больше, чем вы хотите, но это не обязательно является преимуществом. Сгенерированный код также, безусловно, менее оптимизирован, чем тот, который вы можете написать вручную. Иногда разница невелика и несущественна, но если вашему приложению нужно сжать каждый бит производительности, код генерации может оказаться не оптимальным для вас.

Как мы можем использовать генерацию кода?

В зависимости от контекста генерация кода может быть просто полезным повышением производительности или жизненно важным компонентом вашего процесса разработки. Пример полезного использования — во многих современных IDE: они позволяют создавать каркасный класс для реализации интерфейсов или подобных вещей одним нажатием кнопки. Вы можете определенно написать этот код самостоятельно, вы просто потратите некоторое время на выполнение маниакального задания.

Существует много возможных способов разработки конвейера генерации кода. В основном нам нужно определить два элемента:

  1. Вход: откуда берется информация, которая будет использоваться при генерации кода
  2. Вывод: как будет сгенерирован код

При желании вы можете иметь шаги преобразования между входом и выходом. Они могут быть полезны, чтобы упростить выходной слой и сделать входные и выходные данные более независимыми.

Возможные входы:

  • DSL: например, мы можем использовать ANTLR для описания грамматики языка. Из этого мы можем сгенерировать парсер
  • код в других форматах: схемы базы данных. Из схемы базы данных мы можем генерировать DAO
  • мастера: они позволяют запрашивать информацию для пользователя
  • обратный инжиниринг: информация может быть получена путем обработки сложных артефактов кода
  • источники данных, такие как БД, файл CSV или электронная таблица

Возможные выводы:

  • шаблонный движок ; большинство веб-программистов знают механизмы шаблонов, которые используются для заполнения HTML-интерфейса данными
  • API построения кода: например, Javaparser может использоваться для программного создания файлов Java

Давайте теперь рассмотрим некоторые конвейеры:

  • генерация парсера ; читатели этого сайта наверняка будут знакомы с ANTLR и другими подобными инструментами для автоматической генерации парсеров из формальной грамматики. В этом случае входные данные представляют собой DSL, а выходные данные создаются с использованием шаблонизатора.
  • модельно-ориентированный дизайн ; плагины для IDE, или автономные IDE, которые позволяют описать модель приложения, иногда с графическим интерфейсом, из которого можно создать целое приложение или только его скелет
  • код, связанный с базой данных ; это использование можно считать дочерним по отношению к модельно-ориентированному дизайну и движку шаблонов. Обычно программист определяет схему базы данных, из которой могут быть сгенерированы все приложения CRUD или только код для обработки базы данных. Существуют также инструменты, которые выполняют обратный процесс: из существующей базы данных они создают схему базы данных или код для ее обработки.
  • специальные приложения ; эта категория включает в себя все: от инструментов, предназначенных для обработки одной вещи, до специальных систем, используемых в корпоративной среде, которые могут генерировать целые приложения из формального пользовательского описания. Эти приложения обычно являются частью определенного рабочего процесса. Например, клиент использует графический интерфейс для описания приложения, и одна специальная система генерирует схему базы данных для поддержки этого приложения, другая — интерфейс CRUD и т. Д.
  • Код, сгенерированный IDE: многие статически типизированные языки требуют написания большого количества стандартного кода, и IDE обычно может генерировать некоторые из них:   классы с заглушками для реализуемых методов, стандартные методы equals, hashCode и toString, методы получения и установки для всех существующих свойств

Инструменты генерации кода

После этого краткого введения в генерацию кода мы увидим несколько инструментов генерации кода.

Шаблонный движок

Группа шаблонизаторов, пожалуй, самая известная и используемая. Шаблонный движок — это, по сути, мини-компилятор, который может понимать простой язык шаблонов. Файл шаблона содержит специальные обозначения, которые могут быть интерпретированы механизмом шаблонов. Самое простое, что он может сделать, это заменить эту специальную запись правильными данными, данными во время выполнения. Большинство шаблонизаторов также поддерживает простые команды управления потоком (например, for-loops , if-else-statements ), что позволяет описывать простые структуры.

Есть много примеров, давайте посмотрим на два из них, которые представляют, как ведет себя большинство из них.

Jinja2

Jinja2 — это шаблонизатор, написанный на чистом Python. Он обеспечивает не-XML-синтаксис, вдохновленный Django, но поддерживает встроенные выражения и необязательную изолированную среду.

Jinja2 — широко используемый шаблонизатор для Python. Он может делать то, что делают все движки шаблонов: создавать уникальные документы на основе предоставленных данных. Он поддерживает модульные шаблоны, поток управления, переменные и т. Д. Однако он также обладает мощными мерами безопасности: системой, обеспечивающей выход из HTML, и средой «песочницы», которая может контролировать доступ к рискованным атрибутам.

Так выглядит шаблон Jinja2.

1
2
3
4
5
6
<title>{% block title %}{% endblock %}</title>
<ul>
{% for user in users %}
  <li><a href="{{ user.url }}">{{ user.username }}</a></li>
{% endfor %}
</ul>

Jinja2 имеет специальную поддержку для создания HTML-страниц, и именно это наиболее часто используется. Однако его можно использовать для создания других типов файлов.

мопс

Pug — это высокопроизводительный шаблонизатор, находящийся под сильным влиянием Haml и реализованный с помощью JavaScript для Node.js и браузеров.

Во многих отношениях Pug похож на многие другие движки шаблонов: он поддерживает модульные шаблоны, поток управления и т. Д. Разница в том, что Pug похож на DSL и предназначен только для работы с HTML. В результате шаблон Pug выглядит очень чистым и простым.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
doctype html
html(lang="en")
  head
    title= pageTitle
    script(type='text/javascript').
      if (foo) bar(1 + 5)
  body
    h1 Pug - node template engine
    #container.col
      if youAreUsingPug
        p You are amazing
      else
        p Get on it!
      p.
        Pug is a terse and simple templating language with a
        strong focus on performance and powerful features.

Генерация парсера

Генерация парсера — это инструмент для автоматического и быстрого создания парсера для языка. Они очень успешны и продуктивны, потому что проблема синтаксического анализа языка была тщательно изучена. Таким образом, существуют решения, которые могут гарантировать синтаксический анализ большинства языков, которые нужно анализировать людям.

ANTLR

ANTLR , вероятно, наиболее используемый генератор парсеров. Это означает, что есть много примеров. Но настоящая добавленная ценность огромного сообщества — это большое количество доступных грамматик .

ANTLR принимает в качестве входных данных грамматику: формальное описание языка. Выход синтаксического анализатора представляет собой дерево синтаксического анализа: структура, содержащая исходный код, преобразованный таким образом, который легко использовать для остальной части программы. ANTLR также предоставляет два способа обхода дерева разбора: посетители и слушатели. Первый подходит, когда вам нужно манипулировать элементами дерева или взаимодействовать с ними, а второй полезен, когда вам просто нужно что-то сделать, когда правило соответствует.

Формат грамматики чистый и не зависит от языка.

Очень простая грамматика ANTLR

1
2
3
4
5
6
7
grammar simple;
 
basic   : NAME ':' NAME ;
 
NAME    : [a-zA-Z]* ;
 
COMMENT : '/*' .*? '*/' -> skip ;

Если вам интересно узнать, как использовать ANTLR, вы можете заглянуть в этот гигантский учебник ANTLR, который мы написали. Если вы готовы стать профессиональным разработчиком ANTLR, вы можете купить наш видеокурс, чтобы создавать профессиональные парсеры и языки с помощью ANTLR .

Модель управляемый дизайн

Обычно это плагины для IDE или автономные IDE, которые позволяют описать модель приложения с графическим интерфейсом, из которого можно создать каркас приложения. Это может произойти, потому что основой модельно-ориентированного проектирования является абстрактная модель, определенная с помощью диаграмм UML или DSL. И как только основные характеристики программы могут быть описаны в связи с моделью, можно автоматически генерировать представление этой программы. Это представление модели в коде будет иметь структуру, разработанную разработчиком, но обычно поведение должно быть реализовано непосредственно самим разработчиком.

Например, при проектировании на основе модели вы можете автоматически генерировать класс со списком полей и надлежащими методами, но вы не можете автоматически реализовать поведение метода.

Acceleo

Acceleo 3 — это генератор кода, реализующий спецификацию OMG «Модель в текст». Он поддерживает разработчика с большинством функций, которые можно ожидать от IDE генератора кода высшего качества: простой синтаксис, эффективная генерация кода, расширенные инструменты и функции наравне с JDT. Acceleo поможет разработчику справиться с жизненным циклом его генераторов кода. Благодаря подходу, основанному на прототипах, вы можете быстро и легко создать свой первый генератор из исходного кода существующего прототипа, а затем со всеми функциями инструмента Acceleo, такими как инструменты рефакторинга, вы легко улучшите свой генератор для реализации полноценного кода. генератор.

Это цитата с веб-сайта Acceleo, которая довольно хорошо описывает, что делает Acceleo: реализует принципы проектирования модели привода. Чего не хватает, так это описания опыта работы с Acceleo. По сути, это плагин Eclipse, который дает вам инструмент для создания Java-кода, начиная с модели EMF, в соответствии с указанным вами шаблоном. Модель EMF может быть определена различными способами: с помощью диаграммы UML или пользовательского DSL.

Это изображение показывает рабочий процесс проекта Acceleo.

Umple

Umple — это инструмент моделирования и семейства языков программирования, позволяющий то, что авторы называют модельно-ориентированным программированием. Он добавляет абстракции, такие как Ассоциации, Атрибуты и Машины состояний, производные от UML, к объектно-ориентированным языкам программирования, таким как Java, C ++, PHP и Ruby. Umple также можно использовать для создания диаграмм классов и состояний UML в текстовом формате.

Umple — это пример инструмента, который комбинирует режимы UML со стандартным языком программирования. Он был создан для того, чтобы упростить процесс модельно-ориентированной разработки, которая традиционно требует специальных и сложных инструментов. По сути, это язык программирования, который поддерживает функции диаграмм UML (класс и состояние) для определения моделей. Затем код Umple преобразуется его компилятором в традиционный язык, такой как Java или PHP.

Umple можно использовать более чем одним способом:

  • он может использоваться для описания диаграмм UML в текстовом виде
  • его можно использовать в сочетании с традиционным языком в качестве препроцессора или расширения для этого целевого языка. Компилятор Umple преобразует код Umple на целевой язык и оставляет существующий целевой язык без изменений
  • учитывая его широкую поддержку конечного автомата UML, он может работать как генератор конечного автомата, из описания конечного автомата Umple может быть сгенерирована реализация на многих целевых языках.

Следующий код Umple эквивалентен следующей диаграмме UML.

01
02
03
04
05
06
07
08
09
10
class Student {}
 
class CourseSection {}
 
class Registration
{
  String grade;
  * -- 1 Student;
  * -- 1 CourseSection;
}

Вместо этого следующий код Umple описывает конечный автомат.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
class GarageDoor 
   status { 
      Open { buttonOrObstacle -> Closing;  } 
      Closing { 
          buttonOrObstacle -> Opening; 
          reachBottom -> Closed; 
      
      Closed { buttonOrObstacle -> Opening; } 
      Opening { 
          buttonOrObstacle -> HalfOpen; 
          reachTop -> Open; 
      
      HalfOpen { buttonOrObstacle -> Opening; } 
  
}

Код, связанный с базой данных

Все это вращается вокруг схемы базы данных, из которой генерируется код, или из базы данных, из которой генерируется схема. Такие генераторы возможны по двум причинам:

  • реляционные базы данных поддерживают стандартный язык для взаимодействия с ними (SQL)
  • в языках программирования широко распространены шаблоны и библиотеки для взаимодействия с базами данных

Эти две причины гарантируют, что в общем случае возможно создание стандартного связующего кода между языком программирования и базой данных, содержащей данные, необходимые программе. На практике схема базы данных работает как простая модель, которую можно использовать для создания кода.

Многие фреймворки или IDE содержат базовые инструменты для генерации схемы базы данных из класса или, наоборот, генерации класса для взаимодействия с таблицей в базе данных. В этом разделе мы видим пример инструмента, который может сделать что-то большее.

Celerio

Celerio — это инструмент для генерации кода для ориентированных на данные приложений.

Celerio — это инструмент Java, который включает в себя экстрактор базы данных, чтобы получить схему базы данных из существующей базы данных. Затем он связывает эту сгенерированную схему с файлами конфигурации, а затем запускает шаблонизатор для создания целого приложения. Извлеченная схема базы данных в формате XML.

Например, есть пример приложения, которое генерирует приложение Angular + Spring из схемы базы данных.

Домен-специфический язык

DSL — отличный способ формализовать бизнес-логику. После этого это нужно как-то выполнить. Хотя иногда интерпретаторы и компиляторы создаются для выполнения DSL, очень часто используются генераторы кода. Таким образом, DSL может быть переведен на язык, для которого уже существует компилятор, такой как Java или C #.

В настоящее время DSL могут быть построены с использованием языковых рабочих мест, которые являются конкретными IDE, созданными для проектирования и реализации DSL. Языковые инструментальные средства полезны, потому что они позволяют также определять редакторов и другие вспомогательные инструменты для DSL по сниженной цене. Это очень важно, потому что DSL могут использоваться не разработчиками, которым требуются пользовательские редакторы, чтобы воспользоваться возможностями языка, или они просто не могут использовать обычный текстовый редактор. Среди других функций языковые рабочие места обычно интегрированы со средствами генерации кода. Давайте посмотрим пару примеров.

JetBrains MPS

JetBrains MPS — это языковая рабочая среда, основанная на проекционном редакторе. Вы можете использовать его для создания одного или нескольких DSL. Он также может быть использован для расширения существующего языка. Например, mbeddr является расширением для C для улучшения встроенного программирования и основано на JetBrains MPS.

Термин « проекционный редактор» означает, что MPS сохраняет основную структуру ваших данных и показывает ее вам в удобной для редактирования форме. Понятие может быть немного сложным, чтобы обернуть ваше услышанное вокруг. Подумайте о традиционном программировании: вы пишете исходный код, а затем компилятор преобразует исходный код в его логическое представление, дерево разбора, которое он использует для выполнения нескольких задач, таких как оптимизация или преобразование в исполняемый машинный код. С помощью проекционного редактора вы напрямую работаете с логическим представлением: деревом разбора. Однако вы можете изменить его только так, как позволяет редактор (MPS).

Основным последствием является то, что при создании DSL с JetBrains MPS вам нужна вся среда IDE со всей ее мощью и весомостью. Вы можете получить подсветку синтаксиса, завершение кода, управление проектом и т. Д.

Недостаток этого подхода заключается в том, что вы можете изменять код только определенными способами. Так, например, обычно вы не можете делать такие вещи, как копирование и вставка некоторого кода в вашу программу, то, что вы видите в своем редакторе, это просто представление кода, а не реальный код (т. Е. Дерево разбора).

Однако преимущество этого подхода заключается в том, что вы можете создать DSL, который использует любую форму ввода для изменения кода, поэтому вы можете создать графический редактор, табличный ввод или даже обычный текст. Это преимущество делает его особенно полезным для создания DSL, который также может использоваться непрограммистами.

Это видео показывает обзор JetBrains MPS.

Генерация кода с помощью JetBrains MPS

Рабочий процесс работы с JetBrains MPS осуществляется в основном внутри самого этого программного обеспечения: вы управляете всеми аспектами вашего языка там. Сделав все это, вы можете захотеть выполнить свой код, и наиболее распространенный способ сделать это — перевести его на язык, для которого у вас есть компилятор. Здесь идет генерация игрового кода.

Для этого необходимо сделать две вещи:

  1. Перевод понятий высокого уровня в понятия более низкого уровня (по сути, преобразование модели в модель)
  2. Имейте текстовый генератор, который берет понятия более низкого уровня и сопоставляет их непосредственно с текстом

Очевидно, что здесь мы не можем вдаваться в подробности, но мы можем показать вам пример шаблона, используемого для преобразования модели в модель: здесь мы хотим отобразить концепцию с именем Document в соответствующий код Java. Вы увидите, что у нас есть в основном класс Java с методом main. Внутри этого класса некоторые элементы являются фиксированными, в то время как другие зависят от значений Document (т. Е. От имени класса и содержимого контейнера).

Мы говорили, что это преобразование модели в модель, потому что этот шаблон будет генерировать модель Java. Однако MPS поставляется также с генератором текста, который может преобразовать модель Java в код, поэтому в конце вы получите файл Java, подобный этому:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
package jetbrains.mps.samples.generator_demo.test_models.test1;
 
/*Generated by MPS */
 
import javax.swing.JFrame;
import java.awt.Container;
import java.awt.FlowLayout;
import javax.swing.JButton;
 
public class Button {
  public static void main(String[] args) {
    JFrame frame = new JFrame("Demo");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    Container container = frame.getContentPane();
    container.setLayout(new FlowLayout());
    container.add(new JButton());
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}

Это самый простой способ генерации кода с использованием средств, предоставляемых платформой MPS. Однако ничто не запрещает вам создавать код самостоятельно. Поскольку вы можете использовать этот язык также для определения поведения компонентов, вы можете использовать его для непосредственного взаимодействия с файловой системой и написания кода самостоятельно. Например, вы можете написать C #, Python или любой другой язык в файл вручную.

По сути, вы могли бы создать новый файл и добавить к нему содержимое, например, это print("Python code here") . Конечно, это было бы обременительно, так как вы в основном пишете длинные строки текста в файле. Таким образом, этот подход полезен только для записи файлов конфигурации или форматов данных, а не кода на языке программирования.

XText

Xtext — это языковая рабочая среда , созданная на основе Eclipse и Eclipse Modeling Framework. Он может быть использован для разработки текстовых DSL и получения редакторов для них. Функционально Xtext — это комбинация различных инструментов (например, ANTLR для анализа, Eclipse для пользовательского интерфейса и т. Д.), Собранных для создания DSL.

Например, вы собираетесь определить грамматику для вашего DSL, используя формат ANTLR, как в этом примере.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
grammar org.example.domainmodel.Domainmodel with
                                      org.eclipse.xtext.common.Terminals
  
  
Domainmodel :
    (elements+=Type)*;
   
Type:
    DataType | Entity;
   
DataType:
    'datatype' name=ID;
  
Entity:
    'entity' name=ID ('extends' superType=[Entity])? '{'
        (features+=Feature)*
    '}';
  
Feature:
    (many?='many')? name=ID ':' type=[Type];

Сам Xtext сгенерирует синтаксический анализатор, который распознает код вашего языка и предоставит вам модель EMF. В этот момент вы можете использовать другую систему для выполнения преобразований модель-модель или генерации кода модели.

Преобразование модель-модель означает, что вы можете преобразовать модель, созданную с помощью Xtext, в другую модель. Это может быть промежуточным шагом на пути к созданию кода или конечного продукта вашего проекта. Это связано с тем, что Xtext использует преимущества уже доступных инструментов, поэтому вы можете использовать модель EMF с другими инструментами платформы EMF. Это одно из больших преимуществ использования платформы, такой как Xtext: ее можно использовать как часть большей цепочки инструментов.

Генерация кода с помощью Xtext

Существует также гибкость в создании кода для вашего языка. По сравнению с монолитом JetBrains MPS он имеет как преимущества, так и недостатки. Одним из важных преимуществ является тот факт, что процесс генерации проще.

По сути, Xtext предоставляет вам язык (Xtend), который является более мощным диалектом Java, с такими вещами, как поддержка макросов. Однако он не предоставляет особых полномочий для генерации кода.

Это пример файла, который управляет генерацией кода.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class StatemachineGenerator implements IGenerator {
     
    override void doGenerate(Resource resource, IFileSystemAccess fsa) {
        fsa.generateFile(resource.className+".java", toJavaCode(resource.contents.head as Statemachine))
    }
     
    [..]
     
    def generateCode(State state) '''
        if (currentState.equals("«state.name»")) {
            if (executeActions) {
                «FOR c : state.actions»
                    do«c.name.toFirstUpper»();
                «ENDFOR»
                executeActions = false;
            }
            System.out.println("Your are now in state '«state.name»'. Possible events are [«
                state.transitions.map(t | t.event.name).join(', ')»].");
            lastEvent = receiveEvent();
            «FOR t : state.transitions»
                if ("«t.event.name»".equals(lastEvent)) {
                    currentState = "«t.state.name»";
                    executeActions = true;
                }
            «ENDFOR»
        }
    '''
}

Как вы можете видеть, хотя вы заметили, что пишете длинную строку кода, вы довольно близки. Поддержка макросов уменьшает количество повторений, но это не гладкий опыт.

Этот код на языке Xtend затем преобразуется в код Java, где очень очевидно, насколько ручным является процесс.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[..]
 
public CharSequence generateCode(final State state) {
    StringConcatenation _builder = new StringConcatenation();
    _builder.append("if (currentState.equals(\"");
    String _name = state.getName();
    _builder.append(_name);
    _builder.append("\")) {");
    _builder.newLineIfNotEmpty();
    _builder.append("\t");
    _builder.append("if (executeActions) {");
    _builder.newLine();
    {
      EList<Command> _actions = state.getActions();
      for(final Command c : _actions) {
        _builder.append("\t\t");
        _builder.append("do");
        String _firstUpper = StringExtensions.toFirstUpper(c.getName());
        _builder.append(_firstUpper, "\t\t");
        _builder.append("();");
        _builder.newLineIfNotEmpty();
      }
    }
    _builder.append("\t\t");
 
    [..]
 
    return _builder;
  }

Это выглядит очень похоже на то, как если бы вы собирали генератор вручную.

В любом случае это также показывает преимущество специальной природы Xtext. Это просто код Java, с которым вы можете работать и работать так, как вам хочется.

JetBrains MPS и Xtext очень разные: у них есть свои сильные и слабые стороны. Один полностью интегрирован, что облегчает создание автономных DSL, другой представляет собой комбинацию инструментов, которые упрощают интеграцию ваших DSL с существующей кодовой базой или инфраструктурой.

В этой презентации объясняется, как разрабатывать DSL с помощью Xtext.

Специальные приложения

В эту категорию входит все: от инструментов, предназначенных для обработки одной вещи, до специальных систем, используемых в корпоративной среде, которые могут генерировать целые приложения из формального пользовательского описания. Эти приложения обычно являются частью определенного рабочего процесса. Например, клиент использует графический интерфейс для описания приложения, и одна специальная система генерирует схему базы данных для поддержки этого приложения, другая — интерфейс CRUD и т. Д.

Это не правильно определенная категория, а универсальная, которая включает в себя все, что не относится к определенной группе. Это означает, что нет стандартной структуры для этих групп программ. Это также свидетельство универсальности генерации кода: если вы можете создать модель или описание проблемы, вы можете решить ее с помощью генерации кода. Конечно, вы все равно должны понимать, имеет ли смысл решать общую проблему и создавать инструмент генерации кода, или просто решать вашу проблему напрямую.

В этом разделе мы поговорим о двух инструментах: CMake, инструмент разработки, и Yeoman, инструмент скаффолдинга. Первый по сути создает файлы конфигурации: программное обеспечение, которое питает другое программное обеспечение. Второй упрощает жизнь разработчиков, предоставляя способ создания готового к использованию проекта, оптимизированного для конкретной программной платформы, библиотеки или потребности.

CMake

CMake — это кроссплатформенное семейство инструментов с открытым исходным кодом, предназначенное для создания, тестирования и упаковки программного обеспечения.

CMake включает в себя три средства разработки, которые помогают разрабатывать на C и C ++. Основной инструмент предназначен для генерации файлов сборки (т.е. файлов makefile и файлов проекта) для разных платформ и наборов инструментов. Например, tt может генерировать make-файлы для файлов проектов Linux и Visual Studio.

CMake не является компилятором. Пользователь определяет структуру проекта в формате CMake, затем инструмент генерирует обычные файлы сборки, которые используются в традиционном процессе сборки.

Файл CMake выглядит как серия команд / макросов, которые устанавливают опции / флаги для компилятора, связывают библиотеки, выполняют пользовательские команды и т. Д.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
cmake_minimum_required(VERSION 2.8)
 
project("Antlr-cpp-tutorial")
 
 
[..]
 
if (NOT WIN32)
  set(CMAKE_CXX_FLAGS "-Wdeprecated -Wno-attributes" )
endif()
 
[..]
 
if(APPLE)
add_custom_command(TARGET antlr4-tutorial POST_BUILD
  COMMAND ${CMAKE_COMMAND} -E copy_if_different
  "${PROJECT_SOURCE_DIR}/libs/antlr4-runtime.dylib"
  $<TARGET_FILE_DIR:antlr4-tutorial>)
endif()

йомен

Yeoman — это универсальная система, позволяющая создавать приложения любого типа.

В настоящее время быть хорошим программистом означает гораздо больше, чем просто знать, как писать код . Вы должны знать лучшие практики для каждого инструмента, который вы используете, и не забывать применять его каждый раз. И это достаточно сложно при написании самого кода, но это еще более сложная работа, когда у него есть файлы конфигурации и правильная структура проекта. Вот где появляется такой инструмент, как Yeoman: это инструмент для создания леса, который с помощью одной команды генерирует новый проект, который мгновенно реализует все лучшие практики.

Ядром Yeoman является генераторная экосистема, поверх которой разработчики создают свои собственные шаблоны. Инструмент настолько популярен, что уже доступны тысячи шаблонов.

Yeoman — это приложение JavaScript, поэтому для написания генератора достаточно просто написать код JavaScript и использовать предоставленный API. Рабочий процесс также довольно прост: вы запрашиваете у пользователя информацию о проекте (например, имя), собираете информацию о конфигурации и затем генерируете проект.

В следующем коде показана часть примера генератора для создания шаблона Yeoman.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
function makeGeneratorName(name) {
  name = _.kebabCase(name);
  name = name.indexOf('generator-') === 0 ? name : 'generator-' + name;
  return name;
}
 
module.exports = class extends Generator {
  initializing() {
    this.props = {};
  }
 
  prompting() {
    return askName(
      {
        name: 'name',
        message: 'Your generator name',
        default: makeGeneratorName(path.basename(process.cwd())),
        filter: makeGeneratorName,
        validate: str => {
          return str.length > 'generator-'.length;
        }
      },
      this
    ).then(props => {
      this.props.name = props.name;
    });
  }
 
  [..]
 
  writing() {
    const pkg = this.fs.readJSON(this.destinationPath('package.json'), {});
    const generatorGeneratorPkg = require('../package.json');
 
    [..]
 
    this.fs.writeJSON(this.destinationPath('package.json'), pkg);
  }
 
  conflicts() {
    this.fs.append(this.destinationPath('.eslintignore'), '**/templates\n');
  }
 
  install() {
    this.installDependencies({ bower: false });
  }
};

Резюме

В этой статье мы увидели огромный мир генерации кода. Мы видели основные категории генераторов и несколько примеров конкретных генераторов. Некоторые из этих категорий были бы знакомы большинству разработчиков, некоторые — новинкам для большинства. Все они могут быть использованы для повышения производительности при условии, что вы используете их правильно.

О каждой конкретной категории можно сказать гораздо больше. На самом деле мы написали несколько статей о разборе генераторов для разных языков: Java , C # , Python и JavaScript . Однако мы хотели создать вводную статью, чтобы познакомить с этим миром, не перегружая читателя информацией. А вы, как вы планируете воспользоваться генерацией кода?

Смотреть оригинальную статью здесь: Руководство по генерации кода

Мнения, высказанные участниками Java Code Geeks, являются их собственными.