Этот пост – заметка самому себе на тему того, как работать с JetBrains MPS. Решил ее выложить публично т.к. насколько я знаю, в интернетах нет хорошего туториала по MPS 1.5 да и к тому же real-world примера я пока не видел, а у меня тут как раз нечто что является вполне себе работающей DSL. Но пока что я только экспериментирую – так что этот пост будет скорее в стиле Hello World.
Вступление
Ладно, раз уж я начал целый пост писать на эту тему, попробую сначала объяснить что такое MPS. Расшифровывается это как MetaProgramming System (там может еще где-то тире было после Meta), но по сути дела система эта не для метапрограммирования вовсе, а для кодогенерации. Но это не важно – MPS уникальна в том, что позволяет определять структурированные языки – структурированные значит что вы с помощью MPSного DSL определяете ваш особый DSL.
На вопрос «зачем» могу сказать что хоть создание DSL на F# и Ruby получается вполне неплохо, хочется попробовать нечто в чем можно писать интересную логику. Естественно, генерировать Java никакого желания нет, но к счастью идея о том что MPS только для Java – это заблуждение. На самом деле прелесть генераторов в том, что они умеют генерировать всё, что угодно (C#, Scala, you name it).
Ниже приведена последовательность шагов чтобы сделать минимальную DSL для формирования проектных планов. Внимание: в посте очень много скриншотов
Создание проекта
Создаем новый проект
и создаем новый язык
Далее идет вопрос о создании solution’а (по аналогии с Visual Studio это наверное скорее project чем solution, но не важно) в котором можно будет тестировать DSL. Заметьте что в отличии от DSL Tools, тестовый проект не вынесен в отдельный блок который мы изначально не видим. Ну и конечно в отличии от DSL Tools, весь наш DSL умещается в одном «проекте» (тут очень легко с терминологией запутаться) а не в двух.
Жмем Finish и получаем вот это:
Концепты
Концепция – это просто сущность DSLи. В нашем случае, например, есть корневая концепция «проект». Сначала добавляем ее:
С этого момента придется очень ловко работать клавиатурой. Четыре простых правила:
- Tab позволяет прыгать между элементами
- Enter добавляет что-то – например параметр или название чего-то
- Ctrl+Space это просто эпичный shortcut т.к. он показывает варианты ввода (это фактически вызов IntelliSense)
- Alt+Enter показывает подсказки в стиле контекстных подсказок ReSharper’а
Насчет концепта – это почти как Java-класс. Мы реализуем INamedConcept
(просто псевдодекоратор чтобы получить свойство name
), делаем концепт «корневым» и добавляем свойство startDate
– заметьте что для свойств доступны только 3 типа – string
, int
и boolean
.
С концепцией пока всё. Теперь нам нужно описать редактор.
Редактор
Переходим на таб Editor для нашего концепта (или концепции – как правильно?) и кликаем прямо в пустое пространство:
Далее начинается шаманство под заголовком node cell layout:
– тут нужно определить структуру нашей DSLи. В принципе, Ctrl+Space решает, но следует помнить хотя бы горизонтальные и вертикальные layout’ы – это [/
и [>
соответственно:
Теперь внутренности определяются либо через constant
(это фиксированный текст которые всегда будет в определении) либо через переменные в фигурных скобках – тут будет подсказка:
Для нашего DSL конечным результатом (пока) будет вот это:
Примерно на этом этапе можно нажать Ctrl+F9 и регенерировать проект.
Генератор
Генератор – это то, что создаст из DSL осмысленный код. Выбираем его из контекстного меню проекта:
Даем генератору гламурненькое имя
Теперь добавляем в него реализацию. Для начала, дабы не мудрить, спроецируем наш DSL на Java-класс:
baseLanguage
это Java, для тех кто не понял.
В плане ввода для шаблона определяем наш концепт (Project
). Дальше нужно добавить реализацию. Идем в секцию <<static methods>>
и добавляем static void Main()
. Внутри добавляем, например, выписку по проекту (пока что пример у нас простой):
Причина, по которой я так странно описал строку, в том, что я последствии могу нажать Alt+Enter на тех строках которые нужно подменить, и заменить их макросами. Макрос – это то, что превращает из нашего концепта в нечто осмысленное. Вы уже догадались какие intentions к чему применить? Мне кажется тут всё понятно.
Теперь последний штрих – нужно задать мэппинг с концепта на генератор. Для этого, редактируем нод main
:
Собственно, добавляем сюда мэппинг с нашей концепции Project
на класс MyProject
. По аналогии, можно наверное было бы замэпить концепцию на метод, поле, и т.д.
Проверка
Помните песочницу? Ну, это, проект EstimationDSL.sandbox
? Самое время ей воспользоваться. Сначала делаем Ctrl+F9 чтобы все перегенерировать. Теперь добавляем новую «модель» (тут с терминологией становится совсем плохо):
Задаем название, плюс у меня почему-то есть соблазн прописать стереотип как generator, хотя я понятия не имею что это значит:
Идем дальше и появляется вот такое окно – в этом окне можно настроить те фичи, которые будут использованы непосредственно для этого конкретного тестового скрипта:
В плане языков выбираем наш новый язык, а также добавляем generator
– он в принципе должен добавляться сам, но что-то в моей MPS «слетело» и теперь приходится его добавлять руками:
В табе Advanced выбираем наш язык:
Вот собственно и все. Теперь добавляем новый тестовый элемент (у нас это Project
):
И получаем – ура! – редактор для нашего DSL:
Чтобы быстренько протестировать DSL, вводим данные и выбираем в меню проекта Generate Text:
Результат не заставит себя ждать:
А это, собственно, именно то, что нам и хотелось получить.
Заключение
MPS – штука сложная, но судя по всему, зная азы, с ней как-то можно разобраться. Если интересно, в принципе для нее существует скринкаст – он весьма посредственный и у автора сильный бельгийский акцент (а я, как вы знаете, люблю придираться к акцентам), но пока я сам ничего не создал, альтернатив нет. Кстати, пока вы не спросили – да, соблазн есть, но пока что нужно разобраться как генерировать .Net-ный код в этой системе. Собственно, я уверен что как текст код генерировать можно, но было бы круто если бы все это дело встраивалось в Visual Studio, а то у нас в VS-экосистеме с configuration management итак очень плохо.
На самом деле, если серьезно – тема .Net не раскрыта. Ведь при создании шаблона для Java, редактор знает про структуру языка и умеет мэпить, например, концепт на класс. Понятное дело что для C# такой фичи пока нет, хотя и есть попытки реализации поддержки нашего любимого языка.
Еще посетила такая мысль: почему это не коммерческое ПО? Я бы сразу налепил на это ценник и записал бы 15 скринкастов для JetBrains TV. Ну да ладно, может это все впереди? :)
Оставить комментарий