Салат «Руководство Microsoft под SymbolTable с нежным привкусом Antlr4»

Всегда ли нужно использовать то, что предлагают? Разобравшись с посетителем и слушателем, пришло время попробовать «Велосипед». Изучая способы построения внешних DSL я столкнулся с проблемой получения практики. Не буду далеко ходить книга Фаулера про то, как построить язык мечты «DSL», к сожалению, оказалась совсем не обезболивающим, а скорее на оборот тропой в кустах крапивы.

Глава 12 Symbol Table

Место для хранения во время синтаксического анализа всехидентифицируемых объектов для разрешения ссылок.

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

В книге представлена задача по сбору на работу go_to_work → drink_coffee dress drink_coffee → make_coffee wash dress → wash И вот магия DSL, то для чего он создается, а именно код не нужно комментировать, достаточно просто прочитать, что бы определить зависимости между задачами.Но я кофе не пью, и подвернулась книга «руководство microsoft по проектированию архитектуры приложений». Отличная задача — Разработать приложение под винду, чем сейчас и займемся.И так MS уверяетДанное руководство поможет: • Понять базовые принципы и шаблоны построения архитектуры и дизайна для разработки успешных решений на платформе Microsoft.• Правильно выбрать стратегии и шаблоны проектирования, которые помогут при проектировании слоев, компонентов и сервисов решения.• Определить и реализовать ключевые технические решения.• Определить и реализовать основные показатели качества и сквозные функции для решения.• Правильно выбрать технологии для реализации решения.• Создать возможный вариант базовой архитектуры решения.• Правильно выбрать предлагаемые группой patterns & practices решения и руководства, которые помогут в реализации решения.

Отлично. У нас есть бесплатный список задач, который после реорганизации имеет вид1. Понять_базовые_принципы2. Выбрать_стратегии_и_шаблоны3. Реализовать_ключевые_технические_решения4. Реализовать_основные_показатели_качества5. Выбрать_технологии6. Создать_возможный_вариант_базовой_архитектуры7. Выбрать_предлагаемые_решения_и_руководства

Ну и зависимости между ними6 → 3 4 52 → 1 75 → 77 → 13 → 2 54 → 3

Пока все идет хорошо.

Грамматика этого DSL исключительно проста.

grammar file… network: SEP? dependency (SEP dependency)* SEP?; dependency : lhs=ID '→' rhs+=ID+ {helper.recognizedDependency ($lhs, $rhs);} ;

И это все? Что такое SEP, что такое ID? Не ну действительно я только недавно начал разбираться с грамматикой, да вообще с внешним DSL отличным от простого XML. За этим и пришел сюда — яма. Через 60 страниц листания, я узрел SEP: ('-' | ' '); Через еще 50 fragment LETTER: ('a'…'z' | 'A'…'Z' | '_'); fragment DIGIT: ('0'…'9'); ID: LETTER (LETTER | DIGIT)* ; Задачи описаны на русском, эх, а пусть и DSL будет на нем же! Собрав все вместе и изрядно поплясав вокруг кодировки для поддержки кириллицы grammar WorkGrammar;

network: SEP? dependency (SEP dependency)* SEP?;

dependency : lhs=ID '→' rhs+=ID+;

ID: LETTER (LETTER | DIGIT)* ;

fragment LETTER: ('а'…'я' | 'А'…'Я' | '_'); fragment DIGIT: ('0'…'9');

SEP: ('-' | ' ');

WS: [\t\r]±>skip; Я конечно не писатель (не разработчик), а читатель (пользователь), и мне как пользователю это доставило не самые приятные проблемы (и в тех поддержку не позвонишь, в запросах к Google раздавались гудки, ни кто не брал).С грамматикой видимо все! F5 породил Lexer и Parser! Task что то называть класс ну очень не очень, поэтому просто Work class Work { List _dep;

public Work (string name) { Name = name; _dep = new List(); }

public string Name { get; private set; }

public IEnumerable Dependencies { get { return _dep; } }

public bool Completed { get; set; }

public void AddDependency (Work dep) { _dep.Add (dep); }

} Как выяснилось в Битва «Слушатель vs Посетитель» на стадионе antlr4 посетитель и слушатель довольно не малы и тяжеловесны (хотя можно и их использовать), а так же в целях самообразования, пробуем обойти дерево на «Велосипеде». class WorksLoader { public Dictionary Load (WorkGrammarParser.NetworkContext context) { Dictionary result = new Dictionary();

foreach (var dep in context.dependency ()) { var work = GetWork (dep.lhs.Text, result);

foreach (var right in dep._rhs) { var depWork = GetWork (right.Text, result); work.AddDependency (depWork); } }

return result; }

Work GetWork (string name, Dictionary works) { if (! works.ContainsKey (name)) works[name] = new Work (name);

return works[name]; } } Возможности Antlr4 по генерации контекстов радуют, сильно упрощает обход.Класс с методом Load возвращающий таблицу символов (словарь работ) с уже определенными зависимостями.Пришло время самому интересному, исходнику Создать_возможный_вариант_базовой_архитектуры → Реализовать_ключевые_технические_решения Реализовать_основные_показатели_качества Выбрать_технологии Выбрать_стратегии_и_шаблоны → Понять_базовые_принципы Выбрать_предлагаемые_решения_и_руководства Выбрать_технологии → Выбрать_предлагаемые_решения_и_руководства Выбрать_предлагаемые_решения_и_руководства → Понять_базовые_принципы Реализовать_ключевые_технические_решения → Выбрать_стратегии_и_шаблоны Выбрать_технологии Реализовать_основные_показатели_качества → Реализовать_ключевые_технические_решения Не очень читаемые разделители (если укоротить названия работ, то вполне) ну да ладно, это во много раз лучше A → B CОпределив текст нашего языка в ресурсы, можно получить список работ class Program { static void Main (string[] args) {

AntlrInputStream input = new AntlrInputStream (Resource.Input); WorkGrammarLexer lexer = new WorkGrammarLexer (input); CommonTokenStream tokens = new CommonTokenStream (lexer); WorkGrammarParser parser = new WorkGrammarParser (tokens); var works = new WorksLoader ().Load (parser.network ());

var ready = GetReadyWorks (works); }

static Dictionary GetReadyWorks (Dictionary works) { Dictionary ready = new Dictionary();

foreach (var currentWork in works.Select (e=>e.Value)) { if (currentWork.Dependencies.Count () == 0 || currentWork.Dependencies.All (e => e.Completed)) ready[currentWork.Name] = currentWork; }

return ready; } } F5! Работает! Если бы… Результат выдается не тот который ожидался. Потратив изрядное время на выявление проблемы → засада расположилась там где ее ни кто не ожидал → грамматика. Вообще не реально, мало того что она дается не полная и нет ни каких исходных материалов, так она еще и с ошибками. Возмутительно! Рабочая грамматика grammar WorkGrammar;

network: SEP? dependency ('\n' dependency)*;

dependency : lhs=ID '→' rhs+=ID+;

ID: LETTER (LETTER | DIGIT)* ;

fragment LETTER: ('а'…'я' | 'А'…'Я' | '_'); fragment DIGIT: ('0'…'9');

SEP: ('-' | ' ');

WS: [\t\r]±>skip; При разработке DSL на Antlr4 по книге Фаулера чувства двухбокие, вкус с горчинкой, с одного бока описание, текст на высоте, с другого же по мимо того что примеров на C# не найти, так еще изучение затрудняется проблемами поиска частей описания по сем разделам, и багами которые разработчик заботливо раскидал то тут то там.

P.S Всегда ли нужно использовать то, что предлагают? Мое мнение, что если позволяет время, то лучше написать свои классы по обходу деревьев! Хотя многое зависит от самой задачи.

© Habrahabr.ru