Используются ли повторно узлы синтаксиса Roslyn?

124

Я изучал Roslyn CTP, и, хотя он решает ту же проблему, что и API дерева выражений , оба неизменяемы, но Roslyn делает это совершенно по-другому:

  • Expressionузлы не имеют ссылки на родительский узел, изменяются с помощью a ExpressionVisitor, поэтому большие части можно использовать повторно.

  • Roslyn SyntaxNode, с другой стороны, имеет ссылку на своего родителя, поэтому все узлы фактически становятся блоком, который невозможно использовать повторно. Для внесения изменений предусмотрены такие методы, как Update, ReplaceNodeи т.д.

Где это заканчивается? Document? Project? ISolution? API предлагает пошаговое изменение дерева (вместо кнопки вверх), но делает ли каждый шаг полную копию?

Почему они сделали такой выбор? Есть какой-нибудь интересный трюк, который мне не хватает?

Olmo
источник

Ответы:

181

UPDATE: Этот вопрос был предметом моего блога на 8 июня 2012 года . Спасибо за отличный вопрос!


Отличный вопрос. Мы долго-долго обсуждали поднимаемые вами вопросы.

Мы хотели бы иметь структуру данных со следующими характеристиками:

  • Неизменный.
  • Форма дерева.
  • Дешевый доступ к родительским узлам из дочерних узлов.
  • Возможно отображение узла в дереве на символьное смещение в тексте.
  • Стойкий .

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

Теперь, когда вы пытаетесь поместить все эти пять вещей в одну структуру данных, вы сразу же сталкиваетесь с проблемами:

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

Но в команде Roslyn мы постоянно делаем невозможные вещи. На самом деле мы делаем невозможное, сохраняя два дерева синтаксического анализа. «Зеленое» дерево является неизменным, постоянным, не имеет родительских ссылок, строится «снизу вверх», и каждый узел отслеживает его ширину, но не его абсолютное положение . Когда происходит редактирование, мы перестраиваем только те части зеленого дерева, которые были затронуты редактированием, что обычно составляет около O (log n) от общего числа узлов синтаксического анализа в дереве.

«Красное» дерево - это неизменный фасад , построенный вокруг зеленого дерева; он создается по запросу «сверху вниз» и выбрасывается при каждом редактировании. Он вычисляет родительские ссылки, производя их по запросу, когда вы спускаетесь по дереву сверху . Он определяет абсолютные позиции, вычисляя их по ширине, опять же, когда вы спускаетесь.

Вы, пользователь, видите только красное дерево; зеленое дерево - деталь реализации. Если вы заглянете во внутреннее состояние узла синтаксического анализа, вы фактически увидите, что там есть ссылка на другой узел синтаксического анализа другого типа; это зеленый узел дерева.

Между прочим, они называются «красными / зелеными деревьями», потому что это были цвета маркеров белой доски, которые мы использовали для рисования структуры данных на совещании по дизайну. У цветов нет другого значения.

Преимущество этой стратегии в том, что мы получаем все эти замечательные вещи: неизменяемость, постоянство, родительские ссылки и так далее. Цена состоит в том, что эта система сложна и может потреблять много памяти, если «красные» фасады становятся большими. В настоящее время мы проводим эксперименты, чтобы увидеть, сможем ли мы снизить некоторые затраты без потери выгод.

Эрик Липперт
источник
3
И чтобы ответить на часть вашего вопроса о IProjects и IDocuments: мы используем аналогичную модель на уровне услуг. Внутри есть типы DocumentState и ProjectState, которые морально эквивалентны зеленым узлам синтаксического дерева. Полученные вами объекты IProject / IDocument - это фасады красных узлов. Если вы посмотрите на реализацию Roslyn.Services.Project в декомпиляторе, вы увидите, что почти все вызовы направляются во внутренние объекты состояния.
Джейсон Малиновски
@Eric извините за замечание, но вы противоречите себе. The expense and difficulty of building a complex persistent data structure doesn't pay for itself.ref: stackoverflow.com/questions/6742923/… Если у вас были цели высокой производительности, почему вы вообще сделали его неизменным? Есть ли еще одна причина, кроме очевидных? например, проще сделать потокобезопасным, рассуждать об этом и т. д.
Лукаш Мадон
2
@lukas Вы вырываете эту цитату из контекста. Предыдущее предложение звучало так: «Потому что, когда вы смотрите на операции, которые обычно выполняются со строками в программах .NET, во всех соответствующих отношениях совсем не хуже просто создать совершенно новую строку». OTOH, когда вы смотрите на операции, которые обычно выполняются в дереве выражений - например, ввод нескольких символов в исходный файл - значительно хуже построить совершенно новое дерево выражений. Так что они строят только половину.
Timbo
1
@lukas Мое предположение: учитывая, что Roslyn должен работать с фоновыми потоками, неизменяемость позволяет нескольким потокам анализировать один и тот же исходный код одновременно, не беспокоясь о том, что он будет изменен, когда пользователь нажмет клавишу. В ответ на вводимые пользователем данные неизменяемые деревья могут обновляться без остановки выполняющихся задач анализа. Итак, я полагаю, что основная цель неизменяемости - упростить написание Roslyn (и, возможно, облегчить использование клиентами).
Qwertie
3
@lukas Постоянные структуры данных более эффективны, чем копирование, когда структура данных обычно намного больше, чем изменения в ней. Ваша точка зрения, если она у вас есть, для меня потеряна.
Qwertie