Чи повторно використовуються синтаксичні вузли Roslyn?


124

Я дивився на CTP Roslyn і, хоча це вирішує аналогічну проблему з API дерева Expression , обидва незмінні, але Рослін робить це зовсім по-іншому:

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

  • SyntaxNodeЗ іншого боку, Roslyn's має посилання на свого батьківського, тому всі вузли фактично стають блоком, який неможливо повторно використовувати. Такі методи , як Update, ReplaceNodeі т.д., призначені для внесення змін.

Де це закінчується? Document? Project?ISolution? API просуває поетапну зміну дерева (замість кнопки вгору), але чи кожен крок робить повну копію?

Чому вони зробили такий вибір? Чи є якийсь цікавий трюк, якого я пропускаю?

Відповіді:


181

ОНОВЛЕННЯ: Це питання було предметом мого блогу 8 червня 2012 року . Дякую за чудове запитання!


Чудове запитання. Ми обговорювали проблеми, які ви порушуєте, дуже довго.

Ми хотіли б мати структуру даних, яка має такі характеристики:

  • Незмінний.
  • Форма дерева.
  • Недорогий доступ до батьківських вузлів із дочірніх вузлів.
  • Можливо зіставити з вузла на дереві зміщення символів у тексті.
  • Наполегливі .

Під наполегливістю я маю на увазі можливість повторно використовувати більшість існуючих вузлів на дереві, коли редагується текстовий буфер. Оскільки вузли незмінні, немає ніякого бар'єру для їх повторного використання. Це нам потрібно для виконання; ми не можемо повторно розбирати величезні уривки файлу кожного разу, коли натискаєте клавішу. Нам потрібно повторно лексикувати та переаналізувати лише ті частини дерева, які вплинули на редагування.

Тепер, коли ви намагаєтесь об'єднати всі п'ять цих речей в одну структуру даних, ви негайно стикаєтеся з проблемами:

  • Як в першу чергу побудувати вузол? Батько і дитина обоє посилаються один на одного, і вони незмінні, тож хто з них будується першим?
  • Припустимо, вам вдається вирішити цю проблему: як зробити її стійкою? Ви не можете повторно використовувати дочірній вузол у іншого батька, оскільки це означатиме сказати дитині, що у нього є новий батько. Але дитина незмінна.
  • Припустимо, вам вдасться вирішити цю проблему: коли ви вставляєте новий символ у буфер редагування, змінюється абсолютне положення кожного вузла, який відображається у позицію після цієї точки . Це ускладнює створення стійкої структури даних, оскільки будь-яка редакція може змінити проміжки більшості вузлів!

Але в команді Рослін ми звичайно робимо неможливі речі. Ми фактично робимо неможливе, зберігаючи два дерева розбору. «Зелене» дерево незмінне, стійке, не має батьківських посилань, будується «знизу вгору», і кожен вузол відстежує його ширину, але не абсолютне положення . Коли редагування трапляється, ми відновлюємо лише ті частини зеленого дерева, на які вплинуло редагування, що, як правило, стосується 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, коли ви дивитесь на операції, які зазвичай виконуються на дереві виразів (наприклад, набравши кілька символів у вихідний файл - значно гірше побудувати зовсім нове дерево виразів. Так вони будують лише половину.
Тімбо

1
@lukas Моя здогадка: враховуючи, що Рослін повинен працювати на фонових потоках, незмінність дозволяє декільком потокам одночасно аналізувати один і той же вихідний код, не переживаючи, що він буде змінений, коли користувач натисне клавішу. У відповідь на введення користувача, непорушні дерева можна оновлювати, не зупиняючи запущені завдання аналізу. Тому я думаю, що головна мета незмінюваності - зробити Рослін легше писати (а можливо, і простішим для клієнтів у користуванні).
Qwertie

3
@lukas Стійкі структури даних є більш ефективними, ніж копіювання, коли структура даних, як правило, значно більша, ніж зміни до неї. Ваша думка, якщо у вас є, втрачена на мені.
Qwertie
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.