Введение
MPS — это среда разработки языков программирования на платформе Intellij Idea. Она предоставляет возможность моделировать языки программирования, описывать структуры, сосредотачиваясь конкретно на дизайне и бизнес-логике, не отвлекаясь на парсеры, лексеры и различные сторонние фичи языка. Что нужно сразу обозначить: язык, который разрабатывается с помощью MPS, не компилируется в рабочую программу. Чаще всего он генерируется в модели других языков либо в текстовую форму.
Почему я решил написать этот пост? Я считаю, что это очень недооцененная возможность, а недооценена она потому что порог вхождения очень высок, плюс это один из самых непопулярных продуктов JetBrains.
Документация очень подробная, описывает все возможности, но если нет осознания «что я делаю и зачем мне это делать», то она вряд ли будет полезна. Также есть серия уроков от JetBrains на YouTube, но опять же, я более-менее начал понимать, что происходит, только после двух просмотров всей серии и досканального изучения предлагаемых sample проектов.
Я планирую написать серию постов об этой замечательной среде, чтобы по окончанию прочтения у энтузиастов оставалось как можно меньше вопросов «как сделать эту штуку», и как можно больше понимания структуры, чтобы можно было эффективно пользоваться документацией. Познавать MPS мы будем во время создания языка для описания закономерностей погоды. Почему, собственно говоря, и нет?
Концепция
MPS (дальше — среда / MPS) предоставляет возможность создавать модули двух типов — Language и Solution. Первый является описанием языка и его аспектов, второй используется для разработки каких-либо проектов, тестирования языка / языков, расширений языков.
Я начну с Language.
Самое важное, что нужно понять с начала, — это процесс разработки языка и что с ним происходит потом.
- Мы описываем модель языка и необходимые аспекты этого языка, например, систему типов, поведение в редакторе.
- Мы описываем, как модель нашего языка компилируется в текст или в модель другого языка.
- Мы собираем наш язык в плагин для Intellij Idea и используем его. Все переходы из одной модели в другую делаются за нас, в итоге мы получаем готовый скомпилированный код.
Следует заметить, что написание кода в формате MPS немного отличается от написания текста программы, потому что мы на самом деле редактируем AST (Abstract Syntax Tree), а то, что мы видим в редакторе кода — проекция AST.
Итак, 1 статья — 1 кусок конечного проекта.
Создаем проект в MPS
На данном этапе мы выбираем название проекта и название языка, также можно создать Sandbox solution — модуль, в котором мы будем смотреть, как работает наш язык.
У нас есть пустой проект. Совсем пустой. Но в языке WeatherPrediction есть вложенные директивы — structure, editor… Это аспекты языка — в них мы описываем поведение языка в разных ситуациях. Например, structure содержит основные концепты языка, а editor — то, как они будут отображаться в редакторе кода. Это должно звучать очень абстрактно, особенно если Вы еще не знакомы с MPS. Понимаю. Так что сразу в бой.
Сначала нам нужно объявить root концепт языка. В переводе на русский — мы создаем некую структуру, которая будет обобщать все другие структуры. В Java это был бы
public class Weather{
}
Чтобы создать концепт, тыкаем ЛКМ по WeatherPrediction -> New -> Concept.
У концепта есть 3 типа данных, которые он может содержать:
- properties — здесь можно хранить любые примитивные данные, аля строки, числа и boolean Абстрактный пример — целочисленная переменная, у которой должно быть name: string, value: integer, final: boolean
- children — здесь хранятся элементы других концептов. Как абстрактный пример, для концепта Program туда можно было бы пихнуть массив Statement
- references — здесь хранятся ссылки на другие реализованные концепты в AST, которые есть в текущем scope. Абстрактный пример — переменная, у которой значение — ссылка на другую переменную по имени( в реальности это не совсем так, но это же абстрактный пример).
Назовем его PredictionList, определим его как root концепт и наследуем интерфейс INamedConcept.
Что здесь происходит: Мы определяем концепт, называем его PredictionList, говорим, что его можно реализовать как root концепт и наследуемся от INamedConcept. Если посмотреть на его definition (Ctrl + B)
то мы увидим, что это interface concept, у которого есть property name: string, что, собственно говоря, логично из названия
Обратите внимание, что синтаксис похож на язык программирования. Это так: этот код написан на языке jetbrains.mps.lang.structure, который описывает концепты языка.
Если мы сейчас соберем проект и захотим посмотреть, что получилось, то нам нужно будет создать модель в модуле WeatherPrediction.sandbox.
ЛКМ на sandbox(generation required) -> New -> PredictionList
Заменим no name на Saint Petersburg
Бум! У нас есть дефолтная визуализация концепта. Чтобы посмотреть AST, нажмите на любое место в редакторе и нажмите хоткей Alt + X
Здесь видна структура всего всего, это очень круто и удобно отслеживать состояние дерева. Сразу видим, что name у PredictionList = Saint Petersburg. Так, это очень здорово, но мы же хотим чтобы все было по красоте, поэтому мы открываем редактор концепта PredictionList и создаем ему editor aspect. Нажимаем на зеленый плюсик в нижнем списке, прямо под кодом нашего концепта, выбираем Editor -> Concept Editor
Здесь мы можем описать то, как будет отображаться наш PredictionList в редакторе кода.
Пока не будем вдаваться в подробности, как тут это все сделано, просто пишем [- и у нас создается массив ячеек. Все просто: в каждой ячейке — какой то константный текст / property / reference / children. И да, отображение описывается другим языком — jetbrains.mps.lang.editor.
Мы хотим, чтобы наш список предсказаний погоды выглядел следующим образом:
Weather prediction rules for %name%.
Так и пишем.
В первой ячейке — константный текст, во второй — {name}, обращение к property по ключу name.
Пересобираем наш язык (Ctrl + F9) и смотрим в Sandbox solution, где мы до этого создали пустой PredicitonList по имени Saint Petersburg.
Все работает, AST то же самое, что и до наших модификаций.
На этом я, наверное, остановлюсь пока что, чтобы получить фидбек. В следующем посте я планирую добавить еще парочку концептов, а так же генерацию кода на Java.
Спасибо за внимание! Пожалуйста, все пожелания, непонятки и вопросы и пишите в комменты. Если вопросы конкретные и простые, отвечу в комментариях, иначе добавлю в следующий пост.
Комментарии (28)
Conung_ViC
25.07.2017 18:10+6Добавьте пожалуйста, зачем мы разрабатываем язык Weather?
Какие «бизнес»-задачи будет решать этот язык?enchantinggg
26.07.2017 16:05Через пару дней я дополню пост, где подробно обосную зачем мы пишем Weather и в чем его преимущества.
xytop
25.07.2017 18:43+7Совсем непонятно и сумбурно.
Начните статью с того, чего мы хотим добиться.
Покажите язык, который мы хотим разработать, покажите как оно выглядит.
Потом уже можно расписывать как мы этого добиваемся, по пунктам.
Почему мы пишемroot: false
? Зачем мне знать про все эти properties и классы?
Объяснять надо отталкиваясь от проблемы, и только то, что нужно.
А сейчас выглядит как будто автор открыл новый проект, что-то там потыкал, расставил false/true и вот что-то уже получили, смотрите в конце..
apro
25.07.2017 20:47+1В каком-то подкасте слышал что какая-то часть YouTrack была написана на MPS,
а потом они переписали на других технологиях, никто не знает так ли это?deinlandel
26.07.2017 16:13У Youtrack был Workflow Editor (по сути скрипты для автоматизации работы с тикетами) на этой штуке. Тот еще ад был на нём что-то писать. Сейчас добавили поддержку обычного Javascript.
PerlPower
25.07.2017 21:25+2Проблема всех подобного рода решений, от кобола и до наших дней в том, что писать код все равно приходится в конечном итоге программистам, а не аналитикам. И программисты получают полный набор костылей, с которыми приходится бороться.
К тому же при создании таких DSL делается двойная работа — сначала под проект делается язык, а потом на языке делается проект. А потом нужно под этот языко-проект готовить программистов, потому что на рынке их не будет — у вас же свой язык. Создать язык для обработки графики или текста это одно, а держать свой доморощенный язык ради одного проекта это уже пустая трата сил.
Как вы сами видите применение своего детища?maxpsyhos
26.07.2017 04:06+1сначала под проект делается язык, а потом на языке делается проект
С этой точки зрения, DSL — это как фреймворк, только с более удобным интерфейсом. Ясное дело, под один проект фреймворк делать никто не будет, за исключением совсем уж монструозных случаев. А сделать его под конкретную предметную область — почему бы и нет?..
enchantinggg
27.07.2017 20:31+1Ну смотрите, практической пользы от языка Weather не будет, но на работе мы используем несколько языков, написанных в MPS для того, чтобы генерировать django сервер + всякий муторный код для мобильных приложений. Для стандартных вещей наша штучка очень хороша, позволяет концентрироваться на верстке разработчикам приложений и на кастомных endpoint'ах серверникам.Сначала мы использовали костыльный генератор на node.js, но MPS намного удобнее и практичнее, и, естественно, мощнее
beavis88
26.07.2017 09:51Для .net этот инструмент бесполезен?
scrivener
26.07.2017 10:26+1Сначала нужно сделать реализацию C# для MPS — потом можно будет генерировать C# код. На данный момент ни у кого руки не дошли вроде.
S_A
26.07.2017 10:04На самом деле пытался пощупать это чудо (без шуток), но без туториала не врубился, а туториала толкового не нашел сходу. Хотя когда-то сам на flex/bison языки делал. Вот бы время было бы… и кстати, с JS никак MPS не дружит? Это было бы огонь!
scrivener
26.07.2017 10:24+1Есть возможность на выходе генерировать любой язык если у вас есть его имплементация для MPS. Для JavaScript вроде была https://github.com/mar9000/ecmascript4mps
S_A
26.07.2017 10:38Спасибо! На выходе-то у MPS JS (выполняемый на java-машине?), а хотелось бы чтобы какой-то транспайлер из какого-то своего в JS. Ибо JS вездесущ.
scrivener
26.07.2017 10:50Я думаю что это возможно, я просто MPS давненько уже не занимался — сейчас более детально не могу сказать.
enchantinggg
26.07.2017 11:57+1Здравствуйте, логика моделей языков в MPS такова, что у языков есть аспекты TextGen — они отвечают за перевод AST в текстовую форму, а аспект Generator отвечает за Model 2 Model перевод. Так что ecmascript4mps транспайлерится в текст js кода ES5
NikitOS9
28.07.2017 10:50+1enchantinggg
28.07.2017 13:48+1Да, я знаю про racket, но в данном случае немного разные задачи: если с racket можно создать язык от 0 до парсера, лексера, интерпретатора и вообще весь, но его нужно писать руками, то MPS позволяет обойтись без парсинга текста. Конечный язык можно экспортировать как плагин на платформу Intellij Idea и писать в ней код, как в MPS.
symbix
Фидбэк: вы забыли объяснить, зачем это все. Почему для Weather prediction нужен свой язык? Почему не писать сразу на Java?
Jabher
потому что разработчики готовы пойти на многое, лишь бы не писать на Java?
symbix
Ха-ха. :) Да на чем угодно, на Scala, на Clojure...
На практике не встречал случаев, когда оправданно городить свой DSL, обычно все вполне решаемо средствами Domain Driven Design. Разве что декларативные языки, но это скорее конфигурация (из которой генерировать код может быть оправдано из соображений производительности, ога).
enchantinggg
Например, потому что можно будет не переписывать код на другой ЯП в зависимости от платформы (на JavaScript, например, а писать на 1 языке и писать для него языки -расширения, которые будут переводить AST Weather в AST другого языка. Также язык нужно писать с расчетом декларативность, лаконичность и расширяемость. Писать языки легко, а время сокращает.
symbix
В наше время, когда на JS и языках, компилирующихся в него, можно писать вообще все, это очень вымученный аргумент. Тем более, что DSL не решает проблемы инфраструктурных различий.