Pull to refresh

Comments 38

Ещё инструмент — www.jetbrains.com/mps/.

Вообще тема лично для меня интересная. Однако часто такие статьи рассказывают про какие-то общие вещи, абстрактные проекты. Хотелось бы видеть в статье разработку конкретного магазина на конкретном инструменте. Чтобы было сразу видно, что действительно всё можно просто расширить и изменить под новую бизнес логику.
У меня был опыт использования DSL в тестировании. Точнее, его маленького подмножества, — мы вынесли за кулисы повторяющийся код, добавили несколько объектов в виде ключевых слов, сделали своё расширение для кода и назвали всё это DSL :)
Пример: habrahabr.ru/post/221169/#comment_7544005
Да, MPS — это, так сказать, конкурент Eclipse Xtext. Я пробовал его тоже, но не пошло. Есть две проблемы. Первая — очень сложно писать кодогенератор, все совсем не очевидно и запутано. Второе — редактирование AST напрямую. Без среды это не сделать никак, а одна из фишек DSL, что файлы могут редактировать даже люди, далекие от программирования. И заставлять их ставить идею — это слишком.

По второму пункту. Я думаю сделать несколько практических постов на каком-нибудь интересном примере.
Да, будет крайне интересно почитать.
Имхо далёким от программирования людям как раз всё равно что ставить. Это гики любят поредактировать всё в каком-нибудь виме =)

То что программы хранятся не виде текста это конечно минус. Но зато у нового dsl-я практически без усилий сразу получается своя ИДЕ с подсветкой кода и комплишном, а приложив небольшие усилия можно дописать разные рефакторинги, типовые проверки, датафлоу. А ещё в MPS есть поддержка vcs, а также дебаггера и запуска сгенерённого из дсл-я кода.
Ещё один плюс представления кода в виде AST возможность компоновать dsl-и друг с другом.

Про генератор, имхо самое сложное это разобраться с порядком генерации когда есть несколько языков с неочевидными приоритетами (один язык непременно должен сгенериться раньше другого, а вот эти два обязательно нужно вместе сгенерить и т.п.) Бывало очень сложно разобраться что вообще происходит, что во что сгенерилось и почему всё не так как дожно быть :) Но может это уже и улучшили, в what's new версии 3.1 есть раздел «Generator performance and usability improvements», я не очень в курсе последних изменений.
а одна из фишек DSL, что файлы могут редактировать даже люди, далекие от программирования.

И тут мне вспоминается SQL.
В книге «Предметно-ориентированное проектирование (DDD). Структуризация сложных программных систем» www.ozon.ru/context/detail/id/5497184/ автор скептически высказывался по поводу DSL. Вкратце, он говорил о сложной поддержке и рефакторинге кода DSL. А какой у Вас опыт использования DSL? А то рассуждения выглядят правильно, но без практической реализации они мало что стоят.
DSL тоже подвержен ошибкам. Ошибки в коде проверяет компилятор, а кто будет проверять ошибки в DSL?
Э… А почему вопрос ко мне? :) Вообще, если Вы говорите про проверку на этапе компиляции — то сейчас это не большая проблема, выше указали на Eclipse Xtext и JetBrains MPS. А так, unit test'ы — наше всё, они и будут указывать на ошибки.
Это вообще просто. Синтаксические ошибки проверяет xtext. Кроме того есть возможность написания кастомных валидаторов, которые прописывают дополнительные ограничения, которые сложно выразить формальным определением языка. Этим свойством я активно пользуюсь. Например определена некая сущность у которой есть поле типа Enum (A, B) и второе типа список (чего-нибудь). Из предметной области мы знаем, что если в первом поле A, то в списке должно быть только один элемент. Такое сложно выразить в формальном определении языка, а добавить валидатор просто, да еще понятную подсказку в эклипс вывести. Ну и естественно тесты. Без них никуда.
«Вкратце, он говорил о сложной поддержке и рефакторинге кода DSL» — наверное не соглашусь. На DSL описывают высокоуровневые объекты из предметной области. Если предметная область не меняется, то DSL тоже сильно не меняется. Конечно, в процессе разработки добавляются некоторые атрибуты, но это скорее развитие, а не рефакторинг. Грубо говоря, мы увеличиваем (или уменьшаем) детализацию, до той степени которая нам нужна. Что касается кодогенераторов, то тут те же правила, что и для обычного кода — модульность и покрытие тестами — легко рефакторить, макароны — сложно.
«Если предметная область не меняется, то DSL тоже сильно не меняется.» — красной нитью через всю книгу проходит понятие «рефакторинга». В том числе рефакторинга модели предметной области. В как раз в большей степени в этом ключе и рассматривался DSL. И как раз и говорилось — модель предметной области меняем часто, DSL тоже должен меняться часто. Но часто менять его трудно. Ну это я книгу цитирую, сам то опыт использования DSL у меня не большой. :)
Да, про опыт. В первом проекте написал DSL для бизнес-настроек системы. Из DSL сгенерил схему хранения, сериализацию в сетевые объекты, всякие контейнеры, утилитные функции, таблички, редакторы на гуе. У нас в проекте куча разных языков, и от этого полезность генеренного кода повышается. Это был первый опыт. Потом я вошел во вкус и предложил расширить DSL до описания всех данных и всех потоков данных в системе, и соответственно сгенерить всю низкоуровневую ерунду, а рукописный код выкинуть. Не дали :) Но пока план остался, думаю в следующем году доделаем. Во втором проекте сразу пробил DSL. Написал синтаксис, потом взялся за генераторы. Так как предметная область немного другая, и язык охватывает больше аспектов, то пришлось попотеть в плане написания кодогенераторов. Первую версию выкинули :) и написали новую. Но сейчас просто сказка в плане удобства дальнейшей разработки, весь низкоуровневый код генерится и легко меняется. Третий проект — тут уже без вопросов. DSL, кодогенерация, и немного рукописного кода. Все три проекта живые и продолжаются.
А на каких типах проектов у Вас был опыт использования DSL? СЭД, бухгалтерское ПО, архивное ПО и т.п. И насколько они были большие?
P.S. А не могли бы вы пример кода на DSL предоставить?


  • Реальность сложнее абстракций.
  • Универсальных решений не существует.
  • Кодогенерация — круто, но только на начальном этапе.
  • Всё делать на DSL можно, если приложение простое.
Вы не правы:
1) Потому никогда и не стоит строить сверх детальный план. Сделали, проверили, отрефакторили, проверили, отрефакторили и т.д.
2) DSL — это не что-то данное от бога и неизменное. Чего-то не хватает в языке — рефакторим!
3) Кодогенерация очень помогает, когда на поздних этапах нужно везде добавить какой-то функционал, который изначально не предполагался. Вылизаная кодогенерация сохранаяет кучу времени на добавление типовых частей — ибо сгенерированый код уже 100 раз был протестирован, а потому более надежен чем свеженаписаный.
4) Нет смысла стремиться покрыть 100% случаев. Покрываем самые типовые случаи получая экономию максимальную экономию от решения, остальное реализуем другим способом.
5) Подход с DSL позволяет математикам делать ооочень непростые штуки. Попробуйте как ту же линейную алгебру объяснить не пользуюясь DSL.

Иными словами DSL позволяет разработчику решать задачу не углубляясь в микроменеджмент.
1) Нам не нужно описывать всю реальность, нам надо выделить существенное и отбросить несущественное. Поэтому нам нужны абстракции.
2) Я как раз про специальное решение для конкретного случая
3) «Кодогенерация — круто» — согласен, «но только на начальном этапе» — на любом
4) все делать и не обязательно. Задачи делать «все» не стоит.
Для себя полюбил связку Irony и t4 думаю надо статью на эту тему написать.
имхо удобнее ANTLR и Razor как «генератор». Хотя кому что…
ANTLR — дело вкуса. Ничего против него не имею, т.к. тоже очень неплохой инструмент.

А что вы имеете ввиду под Razor, я из него только представления умею готовить, а если я хочу куски бизнесс-логики, то КАК?
Disregard that. Обдумал как следует и понял — ОТЛИЧНАЯ ИДЕЯ! Пожалуй попробую Razor вместо T4.
nuget RazorEngine (как вариант RazorGenerator, если не хочется таскать за собой cshtml (хотя его можно впихнуть в ресурсы)) в помощь в генерации, ага… (правда VS сильно охреневает от него и пытается всё то скобочки поставить, то отформатировать)…

а вообще уже есть roslyn, но в нём как-то сложнее имхо, чем иметь готовый шаблон кода, по которому генить код, по которому генить exe…
Да уж. Пытаюсь всю эту тряхомудию завести.

1) roslyn — что-то как-то уже совсем хардкорно. На коленке что-то состряпать уже не соберешься, а учитывая что куча народа скептически настроена к DSL вообще — сложнее продать прототип.

2) Razor — с одной стороны синтаксис нравится гораздо больше, но как эту заразу RazorGenerator заставить кушать мой базовый шаблон, а не то, что у них там свое есть? Перемудрили с RazorGenerator IMHO. Ах если бы тот же t4, но с синтаксисом Razor (хоть сам бери и делай).

3) RazorEngine не хочет гад становиться CustomTool — полез уже смотреть как его завернуть в CustomTool самому, а иначе это ж надо будет добавлять в проект все ручками (кодом), да потом еще и заставлять VCS обновлять внутренние состояния для всего этого дела.

Недостаток всех способов — один файл выхлопа на шаблон. Жить с этим можно, но не удобно. Для t4 я нашел статью Oleg Synch как делать много файлов (но тут увы — никак не завести это дело из-под msbuild) www.olegsych.com/2008/03/how-to-generate-multiple-outputs-from-single-t4-template/

В общем счастье близко и счастье далеко.
Вам не нужен CustomTool, поверьте. Всё это от лукавого (получите огромный гемор при сборке не msbuild-ом например или при неустановленной студии).

Вам надо просто написать свою тулзу, которой параметрами коммандной строки и передавать: шаблон Razor, dsl файл и путь куда вывести результат. В многофайловости нет нужды, т.к. это один фиг генерированный код. Запуск этой тулзы в PreBuild Events не составит труда…
И соглашусь и поспорю. Генерировать все из PreBuild в один файл — выход. Дешево и сердито. Обычно так и делаю.

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

Сейчас поутру обдумываю вариант с тем, чтобы на этапе генерации добавлять файлы в csproj и тогда не принципиально лежат они в VCS или нет, если они добавятся в любом случае как на машинах разработчиков, так и на build сервере. Единственное — немного нервирует то, что надо в csproj лазить. А ну как тул поломает билд.
ну хочется несколько файлов — не проблема. Научите свою тулу: ведь вы в Razor двиг передаёте модель, которую вы заполняете так как хотите вы. Шаблон лишь средство представления вашей модели (грубо говоря — дергайте ваш шаблон столько раз, сколько вам надо файлов).

Насчёт поискать что-то в сгенерированном — нет нужды. Есть тесты ж на то что сгенерировано. У меня есть проекты где cs файлы размером в 20 мег нагенерированы — полёт нормальный (правда генерировалось в Ruby, но не суть). А посмотреть можно и не в студии (если уж очень хочется, хотя что там смотреть-то)…

Добавлять в csproj ничего не надо, можно просто добавить в компилед проперти динамически поискав в нужной директории (например), руками отредактировав csproj (это ж msbuild скрипт): Compile Include="**\WhatYouNeed.generated.cs"
> Научите свою тулу: ведь вы в Razor двиг передаёте модель, которую вы заполняете так как хотите вы.

Или я чего-то не понимаю или вы. Про модель и двиганье — это понятно.

Вот смотрите. У меня есть N моделей. Каждая из них производит по одному сгенерированому объекту.
Разработчик решил добавить N+1. Он нажимает на проекте, говорид Add New Item, создает файлик, заполняет содержимым.
Генератор берет шаблон, суммирует с моделью и производит сгенерированый объект.

Теперь хотелось бы чтобы сгенерированый объект стал частью проекта. Какие у меня варианты?
1) Заставить разработчика руками добавить сгенерированый объект (корявое решение — ведущее к ошибкам)
2) Добавить в проект работая с ним как XML файлом — уже лучше, но если используется TFS, то TFS клиент никогда не узнает о том что файл был изменен и разработчик может не перепроверить и закоммитить все без обновленного файла проекта.
3) Использовать Visual Studio Shell инструментарий и добавить через него, тогда плохо работает msbuild (это можно пережить используя devenv на билд сервере)
4) Пересоздавать файлы на билд сервере на pre-build событии, тогда в общем если есть модель в проекте и каталоге — все будет работать
5) Сделать как вы советуете — Compile Include=«gen\*.cs», но тогда весь сгенерированый код как бы не существует для разработчиков. Ни отладчик поставить на точке ни посмотреть что же там нагенерилось. Вариант, но плодящий невежество среди пользователей сгенерированого кода, опять же не очень чисто с точки зрения управлениями конфигурацией, не всякий аудит такое решение пройдет (был гемор. в одной конторе с этим — типа все что компилируется должно быть в VCS).
Знаете, а похоже есть вариант
  <ItemGroup>
    <Compile Include="gen\*.cs" />
  </ItemGroup>


а позже

  <Target Name="BeforeBuild">
    <ItemGroup>
      <Content Include="gen\*.cs" />
    </ItemGroup>
  </Target>

ну это я и говорил… в целом, конечно, понятно что мало кто ковыряется в msbuild скриптах и поэтому надо было развернуть свой ответ…
Короче вопрос надеюсь решен 8)

а tfs сжечь, благо он теперь git поддерживает.
Ну в общем удалось завести всё это чудо, сразу обнаружился драматический косяк razorengine и razor как такового.

Эта ботва предполагает что контент sgml базированый. Соответственно если я собираюсь генерировать C# код, то приходится делать секции

@foreach(var item in collection){<text>
...
</text>}


иначе оно не может найти '}'

А впридачу в razorengine баг, из за которого любой закрывающий тег например </param> не дает парсеру найти </text>

В общем понял, что счастья нет и надо делать свое.
UFO just landed and posted this here
antlr для парсинга протокола — эка затийники… обычно для такого ragel более уместен (если подходит).

Касательно использования XML — хм, ну где как. Я предпочитаю избегать XML иногда… Всё по-ситуации.
С xml можно и xslt использовать почти всегда для генерации и xsd для проверки правильности (и сразу генерации dto-шек например)… было бы желание…

ЗЫ: в antlr есть встроенные listener-ы для ошибок (да собственно в каких подобных инструментов их нет?).

Кстати, есть еще Modeling SDK for Visual Studio… я и его использовал, но как-то не срослось.
UFO just landed and posted this here
> Удобнее XML и обычный DOM парсер с XPath библиотекой.

Удобство — штука субъективная. Мне даже JSON кажется более читабельным чем XML.

И если DSL, такой чтобы не IT бизнесс пользователи могли на нем писать правила я могу легко (и это сделано и работает), то вот научить их всех колбасить хитрые XML-и условиями я даже браться не буду. Для XML надо будет писать спец. инструмент с пользовательским интерфейсом, а если так — то опять же не понятно зачем XML нужен.
UFO just landed and posted this here
Вы явно в теме :) Правда я не использую xml, а пишу обычно свой синтаксис, который более читаем, но по смыслу получаются похожие вещи. «чудовищные возможности по массовой модификации» — я рассматриваю это как плюс. Проблема тестирования конечно стоит. Есть соблазн сгенерировать тесты, но это бессмысленно, для автоматического кода нужны ручные тесты. Я обычно полагаюсь на качество тестирования генератора. Пишу тестовые файлы на DSL с максимальным покрытием граничных случаев. И пишу ручные тесты для них. Если эту часть хорошо протестировать, то дальше можно считать что правильный генератор выдает правильный код. Правда тут еще может быть зависимость от ручного кода.
Замечательная статья, всё по делу и достаточно просто. Если применить фразу из статьи, то сама статья следует принципу быть «наиболее простым текстом из всего возможного множества описаний» :)
Тема DSL очень интересная, и, вообще, DSL во многих случаях можно рассматривать как один из способов своевременного поднятия абстракции.
Лично я к такому подходу отношусь положительно, но термин «кодогенерация» мне не нравится, потому что он навевает генераторы всяких копипаст-болванок и «шаблонного» кода. Если языку нужно генерировать какой-то шаблонный код, то либо ему не хватает возможностей к абстракции, либо программисту не хватает знаний об этом языке.
А вот декларативность и доменный подход это, конечно, пушка. Не всегда даже нужно делать парсер или использовать спецефический язык, достаточно просто пересмотреть используемые концепции в ЯП общего назначения и он уже открывает свои новые грани, возможности тут безграничны.
Мне тоже не сильно нравится термин, но за неимением ничего лучше пользуемся. Это тупой перевод с code generation. DSL вообще лучше не переводить, а то получится какая-нибудь ПОЯ. По поводу шаблонного кода — я пожалуй не соглашусь. Язык может быть вполне ничего и позволять разные возможности абстракций, но заворачивание в абстракции может сильно усложнить код. А в шаблонный код прост.
Sign up to leave a comment.

Articles