mROA.CodegenTools

Как робин для RPC-бэтмена

Чтож, если говорить о кодгене в C# при помощи Roslyn, то тут все не так шикарно, как могло бы быть в других областях программировании. Ну по крайне мерее официально по примерам от Microsoft предлагается использовать просто интерполяцию строк.


// Build up the source code
var code = $@"// 

using System;
using System.Collections.Generic;

namespace {namespaceName};

partial class {className}
{{
    public IEnumerable<string> Report()
    {{
{string.Join("\n", methodBody)}
    }}
}}
";

// Add the source code to the compilation.
context.AddSource($"{className}.g.cs", SourceText.From(code, Encoding.UTF8));
    

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

Синтаксис mROA.CGT шаблонов основан на супер упрощенной, версии xml из параллельной вселенной и немного перекликается с шаблонами T4. Однако в отличие от T4, код исполняется по-нормальному в среде C# кода, а не как то там среди генерируемого кода.

Теги

Каждый тег в файле, который определяет так называемый "Документ", начинается с <! после чего идет тип тега, и заканчивается > . Например <!L someLinkName> . Этот тег - тег ссылки с идентификатором. При компиляции документа она попытается найти определение для своего идентификатора и вставить его содержащийся текст вместо себя.

Есть два типа тегов по окончанию: те, которые заканчиваются на своем же объявлении, например Link tag ( <!L> ), и те которые нужно закрыть таким же тегом. Таким тегом является тег определения (Define tag). Он объявляется таким же образом, как Link tag: <!D someLinkName> , но его нужно так же закрыть, используя <!D> . Между головой и хвостом тега заключается текст, который он определяет.

Например, тут тег <!D someLinkName> определяет текст "qwerty text", а <!L someLinkName> вставляет его.

<!L someLinkName><!L someLinkName><!L someLinkName>
<!D someLinkName>qwerty text<!D>

При компиляции такого документа шаблона на выходе получится qwerty textqwerty textqwerty text потому что были прописаны 3 ссылки на одно и то же определение

Последним компонентом этой библиотеки является Insert tag. Он обозначается как <!I> . Он работает похоже на Link tag, но он работает динамически, а не статически. То, что этот тег должен означать задается еще до компиляции документа. Задать содержание тега можно при помощи метода Insert у документа шаблона. В этот тег можно вставить строку или другой тег, например тег Define. Тег Insert может менять логику своей работы при помощи опций. Они указываются после названия через пробел.

На данный момент реализованно 2 опции: repeat и separate. Они связаны между собой и параметр separate не может быть использован без repeat. Опция repeat позволяет не просто заменять указанный Insert тег по его имени на строку или другой тег, а добавлять копию этого Insert тега после выбранного неограниченное количество раз и вставлять контент уже в него. Для использования этой опции нужно прописать после названия r

<!I someInsertName r sep nextLine><!D nextLine>
<!D>