Введение


MPS — это среда разработки языков программирования на платформе Intellij Idea. Она предоставляет возможность моделировать языки программирования, описывать структуры, сосредотачиваясь конкретно на дизайне и бизнес-логике, не отвлекаясь на парсеры, лексеры и различные сторонние фичи языка. Что нужно сразу обозначить: язык, который разрабатывается с помощью MPS, не компилируется в рабочую программу. Чаще всего он генерируется в модели других языков либо в текстовую форму.

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

Документация очень подробная, описывает все возможности, но если нет осознания «что я делаю и зачем мне это делать», то она вряд ли будет полезна. Также есть серия уроков от JetBrains на YouTube, но опять же, я более-менее начал понимать, что происходит, только после двух просмотров всей серии и досканального изучения предлагаемых sample проектов.

Я планирую написать серию постов об этой замечательной среде, чтобы по окончанию прочтения у энтузиастов оставалось как можно меньше вопросов «как сделать эту штуку», и как можно больше понимания структуры, чтобы можно было эффективно пользоваться документацией. Познавать MPS мы будем во время создания языка для описания закономерностей погоды. Почему, собственно говоря, и нет?

Концепция


MPS (дальше — среда / MPS) предоставляет возможность создавать модули двух типов — Language и Solution. Первый является описанием языка и его аспектов, второй используется для разработки каких-либо проектов, тестирования языка / языков, расширений языков.

Я начну с Language.

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

  1. Мы описываем модель языка и необходимые аспекты этого языка, например, систему типов, поведение в редакторе.
  2. Мы описываем, как модель нашего языка компилируется в текст или в модель другого языка.
  3. Мы собираем наш язык в плагин для Intellij Idea и используем его. Все переходы из одной модели в другую делаются за нас, в итоге мы получаем готовый скомпилированный код.

Следует заметить, что написание кода в формате MPS немного отличается от написания текста программы, потому что мы на самом деле редактируем AST (Abstract Syntax Tree), а то, что мы видим в редакторе кода — проекция AST.

Итак, 1 статья — 1 кусок конечного проекта.

Создаем проект в MPS


MPS_1_START

На данном этапе мы выбираем название проекта и название языка, также можно создать Sandbox solution — модуль, в котором мы будем смотреть, как работает наш язык.

image

У нас есть пустой проект. Совсем пустой. Но в языке WeatherPrediction есть вложенные директивы — structure, editor… Это аспекты языка — в них мы описываем поведение языка в разных ситуациях. Например, structure содержит основные концепты языка, а editor — то, как они будут отображаться в редакторе кода. Это должно звучать очень абстрактно, особенно если Вы еще не знакомы с MPS. Понимаю. Так что сразу в бой.

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

Пример root концепта на Java
public class Weather{

}

Чтобы создать концепт, тыкаем ЛКМ по WeatherPrediction -> New -> Concept.

image

У концепта есть 3 типа данных, которые он может содержать:

  1. properties — здесь можно хранить любые примитивные данные, аля строки, числа и boolean Абстрактный пример — целочисленная переменная, у которой должно быть name: string, value: integer, final: boolean

  2. children — здесь хранятся элементы других концептов. Как абстрактный пример, для концепта Program туда можно было бы пихнуть массив Statement

  3. references — здесь хранятся ссылки на другие реализованные концепты в AST, которые есть в текущем scope. Абстрактный пример — переменная, у которой значение — ссылка на другую переменную по имени( в реальности это не совсем так, но это же абстрактный пример).

Назовем его PredictionList, определим его как root концепт и наследуем интерфейс INamedConcept.

image

Что здесь происходит: Мы определяем концепт, называем его PredictionList, говорим, что его можно реализовать как root концепт и наследуемся от INamedConcept. Если посмотреть на его definition (Ctrl + B)

image

то мы увидим, что это interface concept, у которого есть property name: string, что, собственно говоря, логично из названия

Обратите внимание, что синтаксис похож на язык программирования. Это так: этот код написан на языке jetbrains.mps.lang.structure, который описывает концепты языка.

Если мы сейчас соберем проект и захотим посмотреть, что получилось, то нам нужно будет создать модель в модуле WeatherPrediction.sandbox.

image

ЛКМ на sandbox(generation required) -> New -> PredictionList

image

Заменим no name на Saint Petersburg

Бум! У нас есть дефолтная визуализация концепта. Чтобы посмотреть AST, нажмите на любое место в редакторе и нажмите хоткей Alt + X

image

Здесь видна структура всего всего, это очень круто и удобно отслеживать состояние дерева. Сразу видим, что name у PredictionList = Saint Petersburg. Так, это очень здорово, но мы же хотим чтобы все было по красоте, поэтому мы открываем редактор концепта PredictionList и создаем ему editor aspect. Нажимаем на зеленый плюсик в нижнем списке, прямо под кодом нашего концепта, выбираем Editor -> Concept Editor

image

Здесь мы можем описать то, как будет отображаться наш PredictionList в редакторе кода.
Пока не будем вдаваться в подробности, как тут это все сделано, просто пишем [- и у нас создается массив ячеек. Все просто: в каждой ячейке — какой то константный текст / property / reference / children. И да, отображение описывается другим языком — jetbrains.mps.lang.editor.
Мы хотим, чтобы наш список предсказаний погоды выглядел следующим образом:
Weather prediction rules for %name%.

Так и пишем.

image

В первой ячейке — константный текст, во второй — {name}, обращение к property по ключу name.

Пересобираем наш язык (Ctrl + F9) и смотрим в Sandbox solution, где мы до этого создали пустой PredicitonList по имени Saint Petersburg.

image

Все работает, AST то же самое, что и до наших модификаций.

На этом я, наверное, остановлюсь пока что, чтобы получить фидбек. В следующем посте я планирую добавить еще парочку концептов, а так же генерацию кода на Java.

Спасибо за внимание! Пожалуйста, все пожелания, непонятки и вопросы и пишите в комменты. Если вопросы конкретные и простые, отвечу в комментариях, иначе добавлю в следующий пост.
Поделиться с друзьями
-->

Комментарии (28)


  1. symbix
    25.07.2017 18:09
    +6

    Фидбэк: вы забыли объяснить, зачем это все. Почему для Weather prediction нужен свой язык? Почему не писать сразу на Java?


    1. Jabher
      26.07.2017 14:59
      +4

      потому что разработчики готовы пойти на многое, лишь бы не писать на Java?


      1. symbix
        26.07.2017 15:43

        Ха-ха. :) Да на чем угодно, на Scala, на Clojure...


        На практике не встречал случаев, когда оправданно городить свой DSL, обычно все вполне решаемо средствами Domain Driven Design. Разве что декларативные языки, но это скорее конфигурация (из которой генерировать код может быть оправдано из соображений производительности, ога).


    1. enchantinggg
      26.07.2017 16:13

      Например, потому что можно будет не переписывать код на другой ЯП в зависимости от платформы (на JavaScript, например, а писать на 1 языке и писать для него языки -расширения, которые будут переводить AST Weather в AST другого языка. Также язык нужно писать с расчетом декларативность, лаконичность и расширяемость. Писать языки легко, а время сокращает.


      1. symbix
        27.07.2017 15:24
        +1

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


  1. Conung_ViC
    25.07.2017 18:10
    +6

    Добавьте пожалуйста, зачем мы разрабатываем язык Weather?
    Какие «бизнес»-задачи будет решать этот язык?


    1. enchantinggg
      26.07.2017 16:05

      Через пару дней я дополню пост, где подробно обосную зачем мы пишем Weather и в чем его преимущества.


  1. xytop
    25.07.2017 18:43
    +7

    Совсем непонятно и сумбурно.
    Начните статью с того, чего мы хотим добиться.
    Покажите язык, который мы хотим разработать, покажите как оно выглядит.
    Потом уже можно расписывать как мы этого добиваемся, по пунктам.
    Почему мы пишем root: false? Зачем мне знать про все эти properties и классы?
    Объяснять надо отталкиваясь от проблемы, и только то, что нужно.
    А сейчас выглядит как будто автор открыл новый проект, что-то там потыкал, расставил false/true и вот что-то уже получили, смотрите в конце..


    1. enchantinggg
      26.07.2017 16:06

      Хорошо, в дополнении поста я постараюсь обосновать.


  1. apro
    25.07.2017 20:47
    +1

    В каком-то подкасте слышал что какая-то часть YouTrack была написана на MPS,
    а потом они переписали на других технологиях, никто не знает так ли это?


    1. deinlandel
      26.07.2017 16:13

      У Youtrack был Workflow Editor (по сути скрипты для автоматизации работы с тикетами) на этой штуке. Тот еще ад был на нём что-то писать. Сейчас добавили поддержку обычного Javascript.


  1. PerlPower
    25.07.2017 21:25
    +2

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

    К тому же при создании таких DSL делается двойная работа — сначала под проект делается язык, а потом на языке делается проект. А потом нужно под этот языко-проект готовить программистов, потому что на рынке их не будет — у вас же свой язык. Создать язык для обработки графики или текста это одно, а держать свой доморощенный язык ради одного проекта это уже пустая трата сил.

    Как вы сами видите применение своего детища?


    1. maxpsyhos
      26.07.2017 04:06
      +1

      сначала под проект делается язык, а потом на языке делается проект

      С этой точки зрения, DSL — это как фреймворк, только с более удобным интерфейсом. Ясное дело, под один проект фреймворк делать никто не будет, за исключением совсем уж монструозных случаев. А сделать его под конкретную предметную область — почему бы и нет?..


    1. enchantinggg
      27.07.2017 20:31
      +1

      Ну смотрите, практической пользы от языка Weather не будет, но на работе мы используем несколько языков, написанных в MPS для того, чтобы генерировать django сервер + всякий муторный код для мобильных приложений. Для стандартных вещей наша штучка очень хороша, позволяет концентрироваться на верстке разработчикам приложений и на кастомных endpoint'ах серверникам.Сначала мы использовали костыльный генератор на node.js, но MPS намного удобнее и практичнее, и, естественно, мощнее


  1. beavis88
    26.07.2017 09:51

    Для .net этот инструмент бесполезен?


    1. scrivener
      26.07.2017 10:26
      +1

      Сначала нужно сделать реализацию C# для MPS — потом можно будет генерировать C# код. На данный момент ни у кого руки не дошли вроде.


      1. MihMuh
        26.07.2017 16:42

        Не знаю, насколько полно получилось реализовать С#, но попытки точно были
        MPS C#


      1. MihMuh
        26.07.2017 16:55
        +2

        А еще, если не хочется писать реализацию C# — можно генерить прямо в текст на C#, без промежуточной модели на C#. Вот тут, например, генерят Java-код без использования промежуточной модели, прямо из DSL


    1. m08pvv
      26.07.2017 11:10

      Для .NET должен подойти другой их проект JetBrains Nitra


      1. MihMuh
        26.07.2017 16:48

        Nitra — тоже language workbench, но parser-based. Поэтому будет очень сложно, если не невозможно, сделать в своем языке фичи типа этой


  1. S_A
    26.07.2017 10:04

    На самом деле пытался пощупать это чудо (без шуток), но без туториала не врубился, а туториала толкового не нашел сходу. Хотя когда-то сам на flex/bison языки делал. Вот бы время было бы… и кстати, с JS никак MPS не дружит? Это было бы огонь!


    1. scrivener
      26.07.2017 10:24
      +1

      Есть возможность на выходе генерировать любой язык если у вас есть его имплементация для MPS. Для JavaScript вроде была https://github.com/mar9000/ecmascript4mps


      1. S_A
        26.07.2017 10:38

        Спасибо! На выходе-то у MPS JS (выполняемый на java-машине?), а хотелось бы чтобы какой-то транспайлер из какого-то своего в JS. Ибо JS вездесущ.


        1. scrivener
          26.07.2017 10:50

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


        1. enchantinggg
          26.07.2017 11:57
          +1

          Здравствуйте, логика моделей языков в MPS такова, что у языков есть аспекты TextGen — они отвечают за перевод AST в текстовую форму, а аспект Generator отвечает за Model 2 Model перевод. Так что ecmascript4mps транспайлерится в текст js кода ES5


          1. S_A
            26.07.2017 12:04

            Огонь! :)


  1. NikitOS9
    28.07.2017 10:50
    +1

    1. enchantinggg
      28.07.2017 13:48
      +1

      Да, я знаю про racket, но в данном случае немного разные задачи: если с racket можно создать язык от 0 до парсера, лексера, интерпретатора и вообще весь, но его нужно писать руками, то MPS позволяет обойтись без парсинга текста. Конечный язык можно экспортировать как плагин на платформу Intellij Idea и писать в ней код, как в MPS.