В библиотеке Ob51.Compiler.dll в пространстве имён Ob51.Compiler.InternalTree определены классы, производные от класса ProgramNode. Из их экземпляров строятся деревья – внутренние представления программ в компиляторе.
А в пространстве имён Ob51.Compiler.InternalTree.Processing есть 2 класса – TreeProcessor и TreeRebuilder. Они предназначены для обработки этих деревьев.
Класс TreeProcessor предназначен для восходящего обхода дерева. Он может менять дерево, но не приспособлен для удаления или замены узлов, не подходит он и для создания нового дерева на основе уже существующего.
TreeProcessor – это коллекция правил. Каждое правило – экземпляр класса, производного от TreeProcessor.Rule (их всех можно перечислить через IEnumerable<TreeProcessor.Rule>, то есть оператором foreach).
Правила содержат:
Выполнение метода Apply считается применением правила к узлу input. Результат выполнения – ссылка на некоторый другой узел дерева. Эта ссылка передаётся правилам, применяемым к вышележащим узлам дерева.
Т.к. метод Apply – абстрактный, то и Rule – абстрактный класс, а реально в TreeProcessor могут помещаться только экземпляры классов, производных от Rule. Некоторые такие классы определены в самом TreeProcessor:
По умолчанию после создания TreeProcessor содержит единственное правило – экземпляр IdentityRule. Это позволяет добавлять правила только для отдельных конкретных классов узлов, все остальные будут обрабатываться по умолчанию без всяких изменений. Новые правила добавляются методами AddRule.
Обработку дерева выполняет метод TreeProcessor.Process. Он принимает ссылку на корневой узел дерева и работает так:
Таким образом, можно считать, что TreeProcessor выполняет восходящий обход дерева, применяя к каждому из узлов подходящее правило, пока не окажется, что всё дерево обработано (успех) или что подходящих правил нет (ошибка).
Класс TreeRebuilder основан на TreeProcessor и тоже выполняет восходящий обход дерева, но он предназначен для существенной правки дерева или для создания нового дерева на основе старого.
TreeRebuilder обрабатывает 2 набора узлов в целом и для каждого правила: входные и выходные. Входные узлы – это все узлы входного дерева, и только они. Выходные узлы – узлы, возвращаемые правилами при применении. Из выходных узлов TreeRebuilder составляет выходное дерево.
Чтобы упростить при этом правила, TreeRebuilder запоминает отображение входных узлов в выходные, построенное применением правил. Кроме того, для каждого узла через reflection TreeRebuilder находит все поля и свойства, ссылающиеся на входные узлы, и заменяет эти ссылки ссылками на соответствующие выходные узлы.
TreeRebuilder – коллекция правил, аналогичная TreeProcessor. Правила TreeRebuilder обязательно наследуются от абстрактного класса TreeRebuilder.Rule и отличаются от правил TreeProcessor. Правила TreeProcessor нельзя использовать в TreeRebuilder, и наоборот.
Сразу после создания TreeRebuilder не содержит ни одного правила.
Правила содержат:
При применении правила сначала вызывается RebuildNode, который создаёт или как-то определяет выходной узел по входному input. По умолчанию TreeRebuilder.Rule.RebuildNode создаёт копию input вызовом ProgramNode.ShallowClone.
Метод Apply в параметре output принимает результат работы RebuildNode. Из узла output и результатов обработки поддеревьев в childApplyResults Apply должен составить поддерево выходного дерева, корень которого соответствует input. Корень этого выходного поддерева Apply должен вернуть как результат применения правила.
Обход дерева выполняется так же, как и в TreeProcessor, за исключением двух моментов.
Во-первых, Apply имеет несколько иные параметры, и для инициализации одного из них при применении правила перед Apply вызывается RebuildNode.
Во-вторых, соответствие между входными узлами и результатами применения к ним правил запоминается внутри TreeRebuilder во время работы метода Process. А в конце его работы ссылки в выходных узлах переправляются так, чтобы указывать в выходное дерево вместо входного. Это поведение управляется выходным параметром mapLinksFromOldToNew метода Apply. Если он устанавливается в true, в данном узле выполняется замена ссылок, если в false – нет.
Возможны 2 основных применения TreeRebuilder:
В TreeRebuilder реализованы следующие готовые к использованию классы правил:
На основе TreeRebuilder и CloneRule реализован метод ProgramNode.Clone.