Clang RecursiveASTVisitor - PullRequest
       9

Clang RecursiveASTVisitor

0 голосов
/ 13 июня 2019

Я работаю над переводчиком с ++ на бесклассовый с.

На основе примера Калейдоскопа в LLVM каждый узел дерева AST имеет функцию codeGen (), которая генерирует соответствующий код и возвращает его родительскому узлу.

Я пытаюсь реализовать подобное поведение, используя шаблон посетителя, но мне нужен посетитель, чтобы вернуть объекты.

Есть идеи о том, как это можно сделать с помощью RecursiveASTVisitor?

В качестве примера ввода для программы:

void DrawToLayout(std::string, double, double, double, double) {}

class PCellRect {

private:
  double bottomX, bottomY, topX, topY;

public:
  PCellRect(double bX, double bY, double tX, double ty)
      : bottomX(bX), bottomY(bX), topX(bX), topY(bX) {}

  void Draw() { DrawToLayout("Rect", bottomX, bottomY, topX, topY); }
};

void Test() {

  PCellRect rectangle(1.0, 1.0, 2.0, 2.0);

  rectangle.Draw();
}

выдаст такой вывод:

void DrawToLayout(std::string, double, double, double, double) {}

void PCellRect_Constructor(double &bottomX, double &bottomY, double &topX,
                           double &topY, double bX, double bY, double tX,
                           double tY) {
  bottomX = bX;
  bottomY = bY;
  topX = bY;
  topY = bY;
}

void PCellRect_Draw(double &bottomX, double &bottomY, double &topX,
                    double &topY) {

  DrawToLayout("Rect", bottomX, bottomY, topX, topY);
}

void Test() {

  double rectangle_PCellRect_bottomX;
  double rectangle_PCellRect_bottomY;
  double rectangle_PCellRect_topX;
  double rectangle_PCellRect_topY;

  PCellRect_Constructor(rectangle_PCellRect_bottomX,
                        rectangle_PCellRect_bottomY, rectangle_PCellRect_topX,
                        rectangle_PCellRect_topY, 1.0, 1.0, 2.0, 2.0);

  PCellRect_Draw(rectangle_PCellRect_bottomX, rectangle_PCellRect_bottomY,
                 rectangle_PCellRect_topX, rectangle_PCellRect_topY);
}

1 Ответ

0 голосов
/ 13 июня 2019

Как вы заметили, RecursiveASTVisitor функции посещения возвращают bool, и это нельзя изменить. Эти возвращаемые значения играют решающую роль в определяемом пользователем поведении обхода. Итак, в этом ограничении у меня есть два разных варианта для вас.

Раствор 1

Это решение основано на RecursiveASTVisitor. Вы можете сохранить std::stringstream (или любой другой контейнер для последовательного сбора результатов) в качестве объекта-члена посетителя и писать измененные операторы / объявления при обходе дерева.

class Translator : public RecursiveASTVisitor<Translator> {
public:
  bool VisitCXXConstructExpr(clang::CXXConstructExpr *ConstructorCall) {
    auto *Constructor = ConstructorCall->getConstructor();
    auto *ConstructedClass = Constructor->getParent();

    for (auto &Member : getMembers(ConstructedClass)) {
      SS << declareMember(Member) << "\n";
    }
    SS << "\n";
    SS << callPseudoConstructor(Constructor) << "\n";
  }

  // other visit functions

private:
  std::stringstream SS;
};

Решение 2

Это решение немного сложнее, но дает гораздо больше свободы. Он основан на идее реализации собственного RecursiveASTVisitor. Это можно сделать, используя StmtVisitor , DeclVisitor , TypeVisitor и TypeLocVisitor . Это только посетителей, поэтому они вызывают правильную функцию посещения для одного узла, но не для его дочерних элементов. Чтобы реализовать свой собственный обход, вам нужно вызвать Visit для всех дочерних узлов, которые вы хотите пройти.

В следующем фрагменте я использую не все Visitor классы (на самом деле const их версий):

/// Some custom object to be constructed for each AST node
class TranslatedNode {
  // ...
};

class Translator : public ConstStmtVisitor<Translator, TranslatedNode>,
                   public ConstDeclVisitor<Translator, TranslatedNode> {
public:
  TranslatedNode VisitCXXConstructExpr(clang::CXXConstructExpr *ConstructorCall) {
    TranslatedNode Result;

    auto *Constructor = Constructor->getConstructor();
    auto *ConstructedClass = Constructor->getParent();

    for (auto &Member : getMembers(ConstructedClass)) {
      Result.declareMember(Member);
    }

    Result.startCall(getPseudoConstructor(Constructor);
    for (auto &Member : getMembers(ConstructedClass)) {
      Result.addArgument(Member);
    }
    for (auto *Argument : ConstructorCall->arguments()) {
      Result.addArgument(Visit(Argument));
    }

    return Result;
  }

  // other visit functions
};

В каком-то смысле это псевдокод, но я надеюсь, что вы поняли.

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

Я надеюсь, что эта информация будет полезна для вашего проекта. Счастливого взлома с Clang!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...