Я работаю над перезаписью IR компилятора, в котором и классы IR, и алгоритмы постоянно меняются.Текущий компилятор имеет как минимум 2 текущих IR, которые используются на разных этапах, которые я хочу объединить.
Во-первых, у нас есть иерархия AST, основанная на абстрактном базовом классе Node и шаблоне посетителя, связанном с ним.Далее, у нас есть отдельная семантическая иерархия, которая использует множество классов (которые я, вероятно, могу перебазировать, чтобы Node был классом самого низкого уровня для всех них).Семантическая иерархия, вероятно, будет расти по мере того, как мы узнаем больше специализаций.Для этих классов есть отдельный шаблон Visitor.Существует 2 «исполняемых» IR, которые создаются для выполнения результирующей программы.
Моя цель - объединить AST и семантическую иерархию и объединить исполняемые формы, которые они генерируют.Это уменьшит количество ошибок из-за несоответствий в двух формах.
Однако, как я заметил, в семантическую иерархию могут быть добавлены новые классы.Более того, мы также, вероятно, добавим новые алгоритмы для посетителей.
Итак, я хотел бы сделать что-то вроде этого:
class VisitorBase;
class Node;
template Visitable<T> {
virtual preVisitAccept(VisitorBase *v) {
v->preVisit(static_cast<T *>this); }
virtual inVisitAccept(VisitorBase *v) {
v->inVisit(static_cast<T *>this); }
virtual postVisitAccept(VisitorBase *v) {
v->postVisit(static_cast<T *>this); }
};
template Visitor<T> {
virtual preVisit(Node *n) { /* do nothing by default */ }
virtual inVisit(Node *n) { /* do nothing by default */ }
virtual postVisit(Node *n) { /* do nothing by default */ }
};
class VisitorBase : Visitor<VistorBase> {
};
class Node : Visitable<Node> {
// Code that walks the tree will be probably here
// invoking pre, in, and post visit functions as appropriate
}
class ArithOp: node {
// I don't mind repeating this in each class
// Some visitor may be specialized on this function
preVisitAccept(VisitorBase *v) override {
v->preVisit(static_cast<arithOp *>this); }
...
}
class PrettyPrintVisitor : VisitorBase {
// Here is a specialized visitor
preVisit(ArithOp *n) override { /* specialized code /* }
}
Я не против повторять некоторый код в каждомпроизводный класс узла или каждый класс посетителя.
От чего я хочу избавиться - это хрупкий статический список (который мы должны обновить) всех типов, унаследованных от Node, но при этом возможность двойной отправки по ним,Было бы особенно плохо, если бы нам пришлось повторять такой список более одного раза.По сути, я не хочу, чтобы узлы знали о посетителях (за исключением, возможно, когда есть посетители, настроенные для этого узла), а также о других классах узлов.Я также не хочу, чтобы посетители знали об узлах (за исключением типов узлов, для которых настроен посетитель).Более того, я не хочу никаких центральных хранилищ такой информации, так как это будет заголовок, который всегда вызывает перекомпиляцию мира ....
Какие-нибудь мысли по поводу экспериментов с кодом, которые я должен попробовать?Мы можем скомпилировать это с помощью G ++ или CLang.