jetbrains mps что это

JetBrains MPS — IDE для разработки проблемно-ориентированных языков программирования

Введение

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

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

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

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

Концепция

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

Я начну с Language.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Источник

Как система JetBrains MPS позволяет достичь более широкого использование DSL-ей (языков специфичных для предметной области)

DSL-и (domain specific languages или языки для специфичных областей) известны программистам давно. Несмотря на это, они редко используются в реальных системах. В этой статье будет рассмотрено, что такое DSL-и, и почему они не получили широкого распространения. Также будет описано, как система JetBrains MPS решает проблемы, препятствующие их широкому использованию.

Так что же такое DSL? DSL это язык, созданный для решения задач в определенной предметной области. DSL-ями являются большинство декларативных языков, которые решают задачки в узких предметных областях. Например, SQL, регулярные выражения, XPath, Prolog, формулы в Excel. К сожалению, на этом список широко используемых DSL-ей заканчивается. Основное достоинство таких языков в том, что благодаря близости их конструкций к предметной области, код на этих языках очень ясен и краток. Более того, чтобы редактировать код на таких языках, не обязательно быть программистом. Если человек разбирается в предметной области, то он легко может писать код на таких языках благодаря своим знаниям.

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

Давайте посмотрим на причины того, почему DSL-и не используются широко. Первая причина — авторы таких языков фокусируются на замкнутых языках только для одной конкретной проблемы, вместо того, чтобы расширять существующие языки программирования общего назначения, такие как Java, PHP, JavaScript. На это существует причина: возможность появления неоднозначностей при совмещении таких расширений друг с другом. Другая причина — сложность создания языковой инфраструктуры, необходимой для реализации языка, и комфортной работы с ним.

Читайте также:  что делать виагра с девушкой

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

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

Чтобы добиться высокой производительности разработчика, необходимы интеллектуальные средства разработки. C появлением интеллектуальных редакторов, таких как в IntelliJ IDEA или в Eclipse, разработчикам бывает трудно переключится на редактирование текста в обычных редакторах. Текстовые редакторы не подсвечивают ошибки, не предоставляют контекстную помощь, не показывают меню с доступными вариантами, в них нет поддержки рефакторингов. Существуют фреймворки для создания интеллектуальных редакторов, например, IntelliJ IDEA Language API, XText, Oslo, но ни один из этих фреймворков не поддерживает расширяемые языки на должном уровне. Даже если нам не нужна расширяемость, создание поддержки языка с использованием этих средств требует хороших знаний в области языков программирования и занимает очень много времени. Как видно, инструментальная поддержка очень важна, но реализовать ее непросто.

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

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

MPS решает проблему неоднозначности радикальным способом: если у нас нет текстовой грамматики, то у нас нет и неоднозначности. Такой подход, однако, не означает, что в MPS не используются грамматики. Вместо конкретного синтаксиса, в определении языка в MPS определяется абстрактный синтаксис (структура синтаксического дерева). Если вы знакомы с XML, то наверное знаете об XML Schema, которая напоминает способ описания синтаксиса, используемый в MPS.

Поскольку мы избавились от текстового представления, мы не можем использовать обычный текстовый редактор. Для работы с кодом мы используем специальный проекционный редактор. Для каждого узла синтаксического дерева, он создает проекцию — часть экрана с которой может взаимодействовать пользователь. При разработке MPS были приложены огромные усилия для того, чтобы такой редактор вел себя настолько близко к тестовому редактору, насколько это возможно. Например, если вы введете 1 + 2 + 3 в MPS, вы получите тоже синтаксическое дерево, которое было бы получено при разборе этой строки в Java. Конечно, проекционный редактор отличается от текстового, и существуют вещи, которые возможны в одном и невозможны в другом, и наоборот. Несмотря на это, к этим различиям можно привыкнуть, не теряя в производительности. По нашему опыту, требуется около 2-х недель чтобы стать продуктивным в проекционном редакторе.

Создание поддержки интеллектуального редактирования при работе с синтаксическим деревом напрямую сильно упрощается. Более того, во многих местах, интеллектуальные возможности предоставляются MPS IDE без каких либо усилий со стороны автора языка. Такие возможности, как автоматическое дополнение, поиск использований, переименование, работают автоматически. При разработке IntelliJ IDEA, была реализована поддержка интеллектуального редактирования для многих языков. Реализация такой поддержки потребовала больших усилий: несколько человеко месяцев на язык. С MPS аналогичные возможности могут быть реализованы в считанные дни. Это возможно, поскольку для разработки языков используются специальные языки, которые конфигурируют существующую языковую инфраструктуру. MPS это не просто редактор. Вы можете создать полноценную IDE с его помощью.

Внутри JetBrains мы используем MPS для разработки коммерческих проектов. Наша новая система учета ошибок, с кодовым именем Харизма, создана полностью на MPS, и это только начало.
Заключение

Широкому использованию DSL-ей мешают 2 проблемы: невозможность их повторного использования в системах на основе текстовых грамматик, и сложность создания интеллектуальных средств работы с ними. MPS решает обе эти проблемы путем работы с синтаксическим деревом напрямую, без промежуточного текстового представления, и предоставляя инфраструктуру для создания интеллектуальных средств работы с такими языками.

MPS 1.0 был выпущен в июле. Большая часть кода доступна под лицензией Apache 2.0 (за исключение JetBreains IDE Framework, лицензия которой позволяет использовать MPS в продуктах на основе MPS, не покупая каких бы то ни было лицензий у JetBrains).

Источник

JetBrains MPS для интересующихся #1

Введение

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

Зачем нам нужен язык Weather?

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

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

Читайте также:  какой патронус у седрика диггори

Синтаксис

Язык Weather, который мы хотим реализовать, должен выполнять следующую задачу: мы должны уметь лаконично выражать условия (погода сегодня, например) и следствия (погода завтра, послезавтра. ).
В языке Weather мы будем делать наши прогнозы отталкиваясь от 1 фактора: от температуры на сегодняшний день(массив объектов время + погодные условия).

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

Теперь нужно определить, что такое Temperature и Time.

Вот как я реализовал отображение в редакторе для Temperature:

Здесь у нас первая ячейка — double значение, величина температуры, а вторая — Read-Only model access. Здесь мы немного отдаляемся от практики и переходим к теории.

Теория

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

Можно было бы задать unit как переменную, а не писать абстрактный метод, но…
Есть аспект, называется Behavior. Все, что он может делать — добавлять новые методы к концепту. То есть добавить переменную мы не можем, поэтому будем использовать абстрактный метод.

И вот после этого мы можем у каждой реализации концепта Temperature вызывать этот метод. Но где же его вызывать? Как вообще кодить в этом MPS.

Снова практика

Мы остановились на том, что у нас есть непонятная ячейка в Editor аспекте — ReadOnly model access. Все очень просто — если нужно как-то логически обработать proeprty/children/reference перед тем, как его показывать, и на это не хватает встроенных приколов, то мы можем сами получить нужную строку из контекста редактора и реализации концепта. Если просто — нам дают текущий объект концепта, то есть реализованный, и мы можем из него получить все, что мы там понапихали. В данном случае мы хотим получить единицу измерения, поэтому мы нажимаем на ячейку R/O model access и пишем

Кстати, в любом месте кода вы можете тыкнуть на штучку, что Вас интересует и нажать Ctrl + Shift + T и получить информацию о типе этой штучки. Например, если мы нажмем на node в скрине выше и узнаем его тип, то мы получим

node = какая-то реализация концепта
concept = класс концепта
Так! Мы уже умеем составлять температуру по значению и единице измерения, но откуда мы возьмем, какая единица измерения нам нужна? Из дочерних реализаций, естественно.
Создаем пустой CelsiusTemperature концепт, расширяем Temperature и создаем для него behavior.

Остается только собрать все вместе в WeatherTimedData:

Собираем язык и смотрим на результат:

Вроде похоже на правду. Еще, конечно, нет самих предсказаний погоды, нет подсветки, к тому же часы у нас могут быть больше 24 и меньше нуля, минуты тоже не ограничены ничем, кроме размерности integer… В следующем посте ждите разъяснений по новому аспекту — constraints и еще чего-нибудь. А пока — пишите фидбек в комментариях, все как всегда, если вопрос простой — отвечаю там же, если он обширен и скорее как пожелание — то я постараюсь с каждым постом писать все качественнее. Спасибо за внимание!

//UPDATE\
Завел кривой репозиторий с проектом, где каждая ветка — новый туториал на Хабре. Это он.

Источник

Вышел MPS 2021.1

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

Редактор

Масштабирование

Размер шрифта теперь можно быстро настроить в открытом окне редактора — просто зажмите клавишу CTRL и прокрутите колесико мыши. Размер шрифта не запоминается — когда вы снова откроете файл, будет использовано значение по умолчанию. Вы можете включить/отключить эту опцию в разделе Preferences | Editor | General: Change font size with Ctrl/Cmd+Mouse Wheel.

Улучшение производительности форматирования с отступами

Для наиболее частых сценариев реализация форматирования с отступами (indent layout) стала инкрементальной — это ускорит отрисовку редакторов больших моделей, которые используют форматирование с отступами (например, BaseLanguage). Вы можете отключить эту опцию при помощи параметра VM mps.indent_layout.disable_incremental.

Настройки толщины шрифта

При длительной работе над кодом важно подобрать шрифт, который бы не сильно утомлял глаза. Мы добавили настройки типографики, чтобы вы могли подобрать идеальные размеры шрифта. Версия 2021.1 позволяет настраивать толщину обычных и жирных шрифтов через меню Preferences | Editor | Fonts.

Система контроля версий

Улучшения аннотирования кода

Использование дополнительной информации от системы контроля версий позволило нам улучшить аннотацию кода. Новый алгоритм вычисления изменений ревизии отслеживает перемещение узлов в модели. Цвет строк/ячеек с аннотациями определяется только на основе тех ревизий, где были фактически произведены изменения. Если столбец с аннотациями раскрыт, то в контекстном меню ячеек в основном редакторе появится новая группа опций, таких как Copy Revision Number, Show Diff и другие.

Окно Diff для коммитов слияния

Для коммитов, уже слитых с основной веткой, в Git-логе доступно окно Diff. Оно включает три панели: левая и правая — отображают ветки, которые подверглись слиянию, центральная — демонстрирует результат слияния.

Синхронизация прокрутки в редакторе Diff

Мы добавили кнопку, позволяющую выключить синхронизацию прокрутки в окне редактора Diff.

Использование графа ревизий в алгоритмах для аннотации и истории кода

Раньше алгоритмы для аннотации кода и истории кода или фрагмента брали за основу упорядоченный список ревизий файла и вычисляли изменения, сравнивая две соседние ревизии. Однако эти две ревизии могли принадлежать разным веткам — в таком случае найденные изменения становились бессмысленными. Аналогично ревизии слияния могли ошибочно отображать изменения более ранних ревизий. Использование графа ревизий при анализе истории изменений позволило исправить такие ошибки.

Читайте также:  робот пылесос ксиаоми с влажной уборкой какой лучше

Действие Save to Shelf

Новое действие Save to Shelf копирует изменения на вкладку Shelf, сохраняя их в локальной истории изменений. Чтобы запустить это действие, нажмите ⇧⌘A на macOS или Ctrl+Shift+A на Windows или Linux и введите Safe to Shelf.

Генератор

Теперь вы можете обратиться к входному узлу макроса LOOP при помощи выражения LOOP.inputNode — вам не нужно использовать макрос VAR. В том же пространстве имен можно получить доступ к индексу LOOP: используя LOOP.index вместо необходимой ранее контекстной переменной.

Обновленный оператор Transform в объявлении GenPlan

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

Улучшение скомпилированных шаблонов

В IDE в настройках генератора имеется опция Warn when child cannot be placed into role, которая проверяет, может ли создаваемый образец узла быть использован в указанной роли родительского узла. Эта опция также доступна в настройках сборки проектов. Генератор может показать предупреждение, которое обычно означает, что в шаблонах имеются несоответствия, например неправильно размещенные фрагменты шаблона или аннотация COPY-SRC.

Такая проверка отсутствовала при использовании скомпилированных шаблонов. В MPS 2021.1 при включении соответствующей опции скомпилированные шаблоны проверяют, правильно ли дочерние узлы размещены в родительских.

Разное

Java-компилятор для модулей MPS с фасетом Java

Мы существенно изменили подход к компиляции модулей MPS в Java. Это напрямую связано с задачей по поддержке обработки аннотаций, получившей большое количество голосов. Мы перевели инфраструктуру Java-компилятора для MPS на набор API javax.tools.JavaCompiler — теперь MPS может использовать любой компилятор, который реализует стандартный API javax.tools. На данный момент MPS применяет тот компилятор, который назначен стандартным для данного API. При желании вы можете выбрать Eclipse Compiler for Java (ECJ), если он доступен. Когда в classpath есть библиотеки ECJ, можно переключить MPS на использование ECJ, задав mps.compiler.java=ecj в качестве свойства системы (например, изменив mps.vmpoptions). Следует отметить, что если при использовании инфраструктуры нового компилятора возникнут неустраняемые проблемы, то можно задействовать устаревший механизм API прямой компиляции ECJ, изменив конфигурацию на mps.compiler.java=ecjlegacy в качестве свойства системы.

Исходная версия MPS для миграции проекта

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

Если для проекта отсутствует версия, при миграции версией по умолчанию будет 2018.1. Миграцию любого проекта можно запустить вручную из меню Migrations.

Улучшения рефакторинга Method signature

В окне рефакторинга Method Signature теперь можно указать значения по умолчанию для новых аргументов или аргументов, у которых поменялись типы. Если вы удалите параметр метода, но в коде останутся его использования, MPS введет новую локальную переменную и таким образом восстановит связь. Кроме того, теперь обрабатываются параметры типа vararg.

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

Новые возможности для комментариев BaseLanguage

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

Улучшенные выражения lang.smodel

Выражения model.roots(), model.rootsIncludingImported() и model.nodesIncludingImported() теперь, аналогично model.nodes(), поддерживают #expression для указания концептов.

Управление видимостью модулей плагина

Долгое время в MPS считалось, что конечному пользователю видны все модули из языковых плагинов. Из-за увеличения числа языков и усложнения плагинов бывают случаи, когда разработчики языков хотят скрыть определенные модули, чтобы не запутать и не перегрузить конечного пользователя огромным количеством невостребованных языков или функций. Плагины сообщают MPS о том, какие модули они затрагивают, при помощи расширения com.intellij.mps.LanguageLibrary. Теперь у расширений есть опциональный атрибут hide=«true», который разрешает фильтровать модули плагина на основе образца, указанного в com.intellij.mps.VisibleModuleMask. У большинства плагинов MPS появился флажок для удобства фильтрации собственных языков MPS, если разработчику языков необходимо их скрыть. MPS обеспечивает для своих модулей маски в составе плагина jetbrains.mps.ide.devkit, причем разработчики собственных IDE обычно не включают этот плагин в дистрибутив и полностью контролируют отображаемые модули своего продукта.

Другие улучшения

Предварительный просмотр HTML-файлов

Мы добавили встроенное превью для HTML-файлов. Чтобы его открыть, нажмите на логотип MPS в виджете в правом верхнем углу редактора. Превью обновляется в реальном времени, когда вы редактируете HTML-код или изменяете связанные CSS и JavaScript-файлы.

Поддержка Apple Silicon

Теперь вы можете пользоваться MPS на Mac-устройствах с чипом M1. Скачать установщик JetBrains MPS для Apple Silicon можно с нашего сайта либо из Toolbox App.

Автоматический импорт настроек

Раньше окно Import Settings появлялось каждый раз, когда вы устанавливали новую версию MPS. Теперь, если IDE найдет необходимые конфигурационные файлы, окно не появится.

Специальные возможности

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

Незначительные изменения

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

Ваша команда JetBrains MPS
The Drive to Develop

Источник

Сказочный портал