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)
提供了一個示例測試翻譯器來測試此函式
它有一個測試輸入檔案