跳转到内容

ROSE 编译器框架/如何创建翻译器

来自维基教科书,开放世界中的开放书籍

翻译器基本上将一个 AST 转换为另一个版本的 AST。翻译过程可能会添加、删除或修改存储在 AST 中的信息。

基于 ROSE 的翻译器通常具有以下步骤

  1. 搜索要翻译的 AST 节点。
  2. 对找到的 AST 节点执行翻译操作。此操作可以是以下两种主要变体的其中之一
  • 更新现有的 AST 节点
  • 创建新的 AST 节点来替换原始节点。这通常比修补现有的 AST 更干净的方法,并且受到 SageBuilder 和 SageInterface 函数的更好支持。
  • 深度复制现有的 AST 子树以复制代码。可能表达式子树不应该共享。因此需要深度复制它们以获得正确的 AST。
  • 可选地更新翻译的其他相关信息。

第一步

[编辑 | 编辑源代码]

熟悉翻译前后的 AST。这样你就确切地知道你的代码将处理什么以及你的代码将生成什么 AST。

最好的方法是准备最简单的示例代码,并仔细检查它们的整个点图。

有关可视化 AST 的更多详细信息,请参阅 如何可视化 AST

设计考虑因素

[编辑 | 编辑源代码]

通常,以下方法是个好主意

  • 将搜索步骤与翻译步骤分开,以便所有类型的翻译都可以重复使用一次搜索(遍历)。
  • 在设计搜索和翻译的顺序时,请注意翻译是否会对搜索产生负面影响
    • 请避免使用先序遍历,因为你可能最终会修改 AST 节点以供稍后访问,类似于迭代器失效的效果。
    • 请使用后序遍历,或先序遍历的反向顺序,以便你的遍历与翻译挂钩

搜索 AST 节点

[编辑 | 编辑源代码]

有多种方法可以在 AST 中找到要翻译的内容。

AST 查询

[编辑 | 编辑源代码]
  • 通过 AST 查询:节点查询返回同一类型的一系列 AST 节点。这对于简单的翻译通常就足够了
Rose_STL_Container<SgNode*> ProgramHeaderStatementList = NodeQuery::querySubTree (project,V_SgProgramHeaderStatement);
for (Rose_STL_Container<SgNode*>::iterator i = ProgramHeaderStatementList.begin(); i != ProgramHeaderStatementList.end(); i++)
{
    SgProgramHeaderStatement* ProgramHeaderStatement = isSgProgramHeaderStatement(*i);
    ...
}


有关 AST 查询的更多信息,请参阅 ROSE 用户手册 pdf 中的“6 查询库”。

AST 遍历

[编辑 | 编辑源代码]
  • 通过 AST 遍历:使用不同的顺序(先序或后序)遍历整个 AST。建议使用后序遍历,以避免修改遍历稍后将访问的内容(类似于 C++ 中的迭代器失效问题)
    • AST 遍历提供了 visit() 函数,用于挂钩你的翻译函数。可以使用 switch 语句来处理不同类型的 AST 节点。
class f2cTraversal : public AstSimpleProcessing
{
  public:
    virtual void visit(SgNode* n);
};

void f2cTraversal::visit(SgNode* n)
{
  switch(n->variantT())
  {
    case V_SgSourceFile:
      {
        SgFile* fileNode = isSgFile(n);
        translateFileName(fileNode);
      }
      break;
    case V_SgProgramHeaderStatement:
      {
        ...
      }
      break;
    default:
      break;
  }
}

有关 AST 遍历的更多信息,请参阅在线 ROSE 用户手册 pdf 中的“7 AST 遍历”。

执行翻译

[编辑 | 编辑源代码]

在你编写翻译器之前,请阅读 ROSE 教程 pdf 文档中的第 32 章 AST 构造(http://rosecompiler.org/ROSE_Tutorial/ROSE-Tutorial.pdf)。它包含任何翻译编写者必不可少的信息。

你想要执行的翻译通常取决于你访问的 AST 节点的类型。例如,你可以在你的命名空间中定义一组翻译函数

  • void translateForLoop(SgForLoop* n)
  • void translateFileName(SgFile* n)
  • void translateReturnStatement(SgReturnStmt* n),等等

其他提示

更新树
[编辑 | 编辑源代码]
  • 你可能需要处理一些细节,例如删除符号、更新父级和符号表。
  • 小心使用 deepDelete() 和 deepCopy()。某些信息可能无法正确更新。例如,deepDelete 可能不会更新你的符号表。

验证正确性

[编辑 | 编辑源代码]

你可以使用整个 AST 图来验证你的翻译。

所有基于 ROSE 的翻译器都应该在所有转换完成后调用AstTests::runAllTests(project),以确保翻译后的 AST 正确。

这比仅仅正确地反解析为可编译代码具有更高的标准。AST 正确反解析,但无法通过健全性检查是常见现象。

更多信息请参阅 健全性检查

示例翻译器

[编辑 | 编辑源代码]

这里列出了几个示例翻译器,它们可以发展为你想要的更复杂的翻译器。

查找编译指示

[编辑 | 编辑源代码]
/*
toy code
by Liao, 12/14/2007
*/
#include "rose.h"
#include <iostream>
using namespace std;

class visitorTraversal : public AstSimpleProcessing
{
  protected:
    virtual void visit(SgNode* n);
};

void visitorTraversal::visit(SgNode* node)
{
  if (node->variantT() == V_SgPragmaDeclaration) {
      cout << "pragma!" << endl;
  }
}

int main(int argc, char * argv[])
{
  SgProject *project = frontend (argc, argv);
  visitorTraversal myvisitor;
  myvisitor.traverseInputFiles(project,preorder);

  return backend(project);
}


以下是一个执行编译指示解析并将结果保存到 AST 属性中的示例项目。

https://github.com/rose-compiler/rose-develop/tree/master/projects/pragmaParsing

循环转换

[编辑 | 编辑源代码]

SageInterface 命名空间(http://rosecompiler.org/ROSE_HTML_Reference/namespaceSageInterface.html)包含许多翻译函数,例如循环的函数。

例如,https://github.com/rose-compiler/rose/blob/master/src/frontend/SageIII/sageInterface/sageInterface.C 中定义了一个循环平铺函数

//     Tile the n-level (starting from 1) loop of a perfectly nested loop nest using tiling size s.
bool     loopTiling (SgForStatement *loopNest, size_t targetLevel, size_t tileSize)


提供了一个示例测试翻译器来测试此函数

它有一个测试输入文件

如何构建你的翻译器

[编辑 | 编辑源代码]

参见 如何为翻译器设置 makefile

华夏公益教科书