Редкая профессия | страница 13
Опуская технические детали, следует сказать, что сейчас мы в целом недовольны тем, как спроектированы семантические таблицы. В свое оправдание отметим, что все "навороты" в них — вещи вполне объективные, которые так или иначе должны присутствовать в компиляторе. Наша неудовлетворенность имеет, скорее, эстетическую природу: таблицы не выглядят стройной системой, где каждый компонент точно подогнан к тому месту, которое для него предназначалось.
С деревом программы ситуация была обратной. Будучи один раз спроектированными, принципы организации дерева далее практически не изменялись. В противоположность таблицам, структура которых создавалась, чтобы непосредственно отражать контекстные отношения языка Си++, дерево оказалось практически полностью языко-независимым. Иными словами, используя основной строительный элемент дерева — терминальный узел — можно конструировать произвольные конфигурации, отображающие конструкции любых языков программирования. Все узлы дерева имеют идентичную структуру, различаясь лишь значениями своих (немногочисленных) атрибутов. Каждый узел имеет четыре ссылки (вверх, вниз, влево и вправо), с помощью которых легко формировать "плоские" конфигурации, соответствующие тем или иным конструкциям входного языка. Как правило, горизонтальные ссылки отражают верхний уровень структуры некоторой конструкции, а вертикальные используются для поддеревьев, соответствующих элементам этой конструкции, или вложенным конструкциям.
Несомненными достоинствами такой схемы являются высокая регулярность, простота и универсальность. Дерево для любой языковой конструкции строится по единым правилам, и все разнообразие выразительных свойств Си++ приводится к строгой единообразной регулярной конфигурации, для которой очень удобно строить всевозможные рекурсивные алгоритмы анализа, трансформации и генерации.
Однако у этих достоинств есть и оборотная сторона, которую можно определить как низкий уровень структуры дерева. В чистом виде оно не несет в себе никакой семантической информации — это лишь определенная структура и ничего более. Иными словами, для дерева как такового можно определить только достаточно примитивные операции, например, "связать два узла горизонтальными ссылками", "построить из данных узлов бинарное дерево" и т.п. Любое же мало-мальски серьезное действие, учитывающее семантику того или иного поддерева, например, его перестроение в процессе оптимизации или при генерации кода, приходится программировать специально для каждого вида конфигураций. На практике это приводит к тому, что операции над деревом не располагаются в одном или нескольких модулях, а рассредоточены по всему тексту компилятора.