Статьи

Круг вокруг Roslyn CTP: переписывание синтаксиса

Чтобы начать делать что-то полезное с Roslyn , мы собираемся осмотреть синтаксическое дерево, найти что-то интересное — и затем изменить его! Сложная структура синтаксического дерева программы на C # ( класс SyntaxTree ) раскрывается через довольно интуитивную объектную модель, включающую три типа сущностей:

Узлы являются основными элементами языка; например, IfStatementSyntax — это узел, представляющий оператор «if», а LiteralExpressionSyntax — это узел, представляющий буквальное выражение.

Токены являются вторичными элементами, которые, тем не менее, очень важны, такими как идентификаторы, строковые литералы и числовые литералы. Токены всегда привязаны к узлу. Например, узел IfStatementSyntax будет иметь узел ExpressionSyntax в качестве своего свойства Condition , и это может оказаться узлом BinaryExpressionSyntax со   свойствами Left и Right, описывающими больше узлов ExpressionSyntax, и токен OperationToken , описывающий операцию.

Пустяки — это все остальное — директивы препроцессора, пробелы, комментарии — на вершине токенов.

Синтаксическое дерево, конечно же, имеет родительско-дочерние отношения между узлами, и существует набор API для обхода этих отношений, например, DescendantNodes и FirstAncestor .

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

SyntaxTree tree = SyntaxTree.ParseCompilationUnit(@"
using System;
public class MyClass {
    public static void MyMethod() {
        Console.Write(""Hello There {0}"", 42);
        Console.Write(42);
    }
}
");
Console.WriteLine(tree.Root.GetFullText());

Проверка дерева в визуализаторе отладчика ( поставляется с Roslyn CTP в качестве примера ) показывает следующую структуру:

образ

Вы можете проверять и изменять синтаксические деревья напрямую, но более простым способом было бы использовать класс посетителей, производный от SyntaxWalker или SyntaxRewriter . Демонстрация быстрого кода лучше, чем тысяча слов, описывающих его, поэтому вот переписчик, который изменит числовые литералы со значения 42 на значение 43:

/// <summary>
/// Replaces the numeric literal 42 with the
/// numeric literal 43.
/// </summary>
class MyLiteralRewriter : SyntaxRewriter
{
    protected override SyntaxNode VisitLiteralExpression(
        LiteralExpressionSyntax node)
    {
        if (node.Kind ==
            SyntaxKind.NumericLiteralExpression)
        {
            SyntaxToken token = node.Token;
            if (token.Value is int &&
                (int)token.Value == 42)
            {
                return node.ReplaceToken(
                    token, Syntax.Literal(
                           token.LeadingTrivia,
                           "43", 43,
                           token.TrailingTrivia));
            }
        }
        return node;
    }
}

Обратите внимание, что метод VisitLiteralExpression не изменяет узел — он либо возвращает существующий узел, либо возвращает новый узел с новым литеральным токеном. Вся поверхность API Roslyn такая: все объекты неизменны, и вы создаете новые объекты на основе существующих.

Как этот посетитель применяется к синтаксическому дереву? Чтобы применить его, нам нужно дать ему корень дерева, и он вернет новый корень дерева. Этот новый корень дерева можно скомпилировать, проанализировать или просто … сериализовать в текст:

SyntaxNode newRoot =
    new MyLiteralRewriter().Visit(newRoot);           
tree = SyntaxTree.Create(
    tree.FileName, (CompilationUnitSyntax)newRoot);
Console.WriteLine(tree.Root.GetFullText());

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

В следующем посте мы рассмотрим несколько более сложного посетителя, переписывающего синтаксис, который потребует семантической модели кода (т. Е. Символов и их значений), а не только синтаксической информации.