ROSE 编译器框架/如何创建翻译器
翻译器基本上将一个 AST 转换为另一个版本的 AST。翻译过程可能会添加、删除或修改存储在 AST 中的信息。
基于 ROSE 的翻译器通常具有以下步骤
- 搜索要翻译的 AST 节点。
- 对找到的 AST 节点执行翻译操作。此操作可以是以下两种主要变体的其中之一
- 更新现有的 AST 节点
- 创建新的 AST 节点来替换原始节点。这通常比修补现有的 AST 更干净的方法,并且受到 SageBuilder 和 SageInterface 函数的更好支持。
- 深度复制现有的 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。建议使用后序遍历,以避免修改遍历稍后将访问的内容(类似于 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),等等
其他提示
- 参考 ROSE doxygen 网站,获取每个 AST 节点的信息:http://rosecompiler.org/ROSE_HTML_Reference/index.html
- 如果你想创建新的 AST 节点,请使用 SageBuilder 命名空间(http://rosecompiler.org/ROSE_HTML_Reference/namespaceSageBuilder.html)。更新 SageBuilder,你找不到你需要的那个。
- 在 SageInterface 命名空间(http://rosecompiler.org/ROSE_HTML_Reference/namespaceSageInterface.html)中查找你需要的翻译函数。如果没有,请编写你自己的函数。
- 除了从头开始构建之外,你还可以使用 SageInterface::deepCopy() 来复制 AST 子树。
- 更新信息,或创建你需要的新的 AST 节点。
- 用你更新的或新的 AST 节点替换现有的 AST 节点。
- 你可能需要处理一些细节,例如删除符号、更新父级和符号表。
- 小心使用 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)
提供了一个示例测试翻译器来测试此函数
它有一个测试输入文件