ROSE 编译器框架/抽象语法树
ROSE 的主要中间表示形式是其抽象语法树 (AST)。要使用编程语言,您必须熟悉语言语法、语义等。要使用 ROSE,您必须熟悉其对输入代码的内部表示。
了解 AST 的最佳方法是使用最简单的代码示例对其进行可视化。
可视化 ROSE AST 需要三样东西
- 示例输入代码:您提供
- 从 AST 生成 dot 文件的 dot 图生成器:ROSE 提供 dot 图生成器
- 用于打开 dot 图的可视化工具:ROSE 开发人员使用 ZGRViewer 和 Graphviz
如果您不想从头开始安装 ROSE+ZGRview + Graphvis,您可以直接使用ROSE 虚拟机镜像,其中已安装和配置了您需要的所有内容,因此您只需可视化您的示例代码。
请准备最简单的输入代码,不要包含任何头文件,这样您就可以获得足够小的 AST 来消化。
我们提供 ROSE_INSTALLATION_TREE/bin/dotGeneratorWholeASTGraph(复杂图形)和 dotGenerator(一个更简单的版本)来生成输入代码详细 AST 的 dot 图。
用于以 dot 格式生成 AST 图的工具。有两个版本
- dotGenerator:简单的 AST 图生成器,显示基本节点和边
- dotGeneratorWholeASTGraph:完整的 AST 图,显示更多细节。它提供过滤选项以显示/隐藏某些 AST 信息。
命令行
dotGeneratorWholeASTGraph yourcode.c // it is best to avoid including any header into your input code to have a small enough tree to visualize.
跳过内置函数
- dotGeneratorWholeASTGraph -DSKIP_ROSE_BUILTIN_DECLARATIONS yourcode.c
dotGeneratorWholeASTGraph -rose:help -rose:help show this help message -rose:dotgraph:asmFileFormatFilter [0|1] Disable or enable asmFileFormat filter -rose:dotgraph:asmTypeFilter [0|1] Disable or enable asmType filter -rose:dotgraph:binaryExecutableFormatFilter [0|1] Disable or enable binaryExecutableFormat filter -rose:dotgraph:commentAndDirectiveFilter [0|1] Disable or enable commentAndDirective filter -rose:dotgraph:ctorInitializerListFilter [0|1] Disable or enable ctorInitializerList filter -rose:dotgraph:defaultFilter [0|1] Disable or enable default filter -rose:dotgraph:defaultColorFilter [0|1] Disable or enable defaultColor filter -rose:dotgraph:edgeFilter [0|1] Disable or enable edge filter -rose:dotgraph:expressionFilter [0|1] Disable or enable expression filter -rose:dotgraph:fileInfoFilter [0|1] Disable or enable fileInfo filter -rose:dotgraph:frontendCompatibilityFilter [0|1] Disable or enable frontendCompatibility filter -rose:dotgraph:symbolFilter [0|1] Disable or enable symbol filter -rose:dotgraph:emptySymbolTableFilter [0|1] Disable or enable emptySymbolTable filter -rose:dotgraph:typeFilter [0|1] Disable or enable type filter -rose:dotgraph:variableDeclarationFilter [0|1] Disable or enable variableDeclaration filter -rose:dotgraph:variableDefinitionFilter [0|1] Disable or enable variableDefinitionFilter filter -rose:dotgraph:noFilter [0|1] Disable or enable no filtering Current filter flags' values are: m_asmFileFormat = 0 m_asmType = 0 m_binaryExecutableFormat = 0 m_commentAndDirective = 1 m_ctorInitializer = 0 m_default = 1 m_defaultColor = 1 m_edge = 1 m_emptySymbolTable = 0 m_expression = 0 m_fileInfo = 1 m_frontendCompatibility = 0 m_symbol = 0 m_type = 0 m_variableDeclaration = 0 m_variableDefinition = 0 m_noFilter = 0
要可视化生成的 dot 图,您必须安装
- Graphviz:https://graphviz.cpp.org.cn/Download.php。
- ZGRViewer:http://zvtm.sourceforge.net/zgrviewer.html#download。(建议使用 0.8.x 版本,因为 0.9.x 存在一些错误,例如拖动图形时的反向(反转)方向。)
请注意,您必须配置 ZGRViewer 以使其拥有其使用的某些命令的正确路径。您可以从其配置/设置菜单项中执行此操作。或直接修改文本配置文件(.zgrviewer)。
下面显示了一个示例配置(cat .zgrviewer)
<?xml version="1.0" encoding="UTF-8"?> <zgrv:config xmlns:zgrv="http://zvtm.sourceforge.net/zgrviewer"> <zgrv:directories> <zgrv:tmpDir value="true">/tmp</zgrv:tmpDir> <zgrv:graphDir>/home/liao6/svnrepos</zgrv:graphDir> <zgrv:dot>/home/liao6/opt/graphviz-2.18/bin/dot</zgrv:dot> <zgrv:neato>/home/liao6/opt/graphviz-2.18/bin/neato</zgrv:neato> <zgrv:circo>/home/liao6/opt/graphviz-2.18/bin/circo</zgrv:circo> <zgrv:twopi>/home/liao6/opt/graphviz-2.18/bin/twopi</zgrv:twopi> <zgrv:graphvizFontDir>/home/liao6/opt/graphviz-2.18/bin</zgrv:graphvizFontDir> </zgrv:directories> <zgrv:webBrowser autoDetect="true" options="" path=""/> <zgrv:proxy enable="false" host="" port="80"/> <zgrv:preferences antialiasing="false" cmdL_options="" highlightColor="-65536" magFactor="2.0" saveWindowLayout="false" sdZoom="false" sdZoomFactor="2" silent="true"/> <zgrv:plugins/> <zgrv:commandLines/> </zgrv:config>
您还必须配置 run.sh 脚本以使其拥有正确的路径
cat run.sh
#!/bin/sh # If you want to be able to run ZGRViewer from any directory, # set ZGRV_HOME to the absolute path of ZGRViewer's main directory # e.g. ZGRV_HOME=/usr/local/zgrviewer ZGRV_HOME=/home/liao6/opt/zgrviewer-0.8.1 java -jar $ZGRV_HOME/target/zgrviewer-0.8.1.jar "$@"
一个完整的示例
# make sure the environment variables(PATH, LD_LIBRARY_PATH) for the installed rose are correctly set which dotGeneratorWholeASTGraph ~/workspace/masterClean/build64/install/bin/dotGeneratorWholeASTGraph # run the dot graph generator dotGeneratorWholeASTGraph -c ttt.c #see it which run.sh ~/64home/opt/zgrviewer-0.8.2/run.sh run.sh ttt.c_WholeAST.dot
我们将一些示例源文件及其 AST 转储文件放入:https://github.com/chunhualiao/rose-ast
- 例如:https://github.com/chunhualiao/rose-ast/blob/master/func1.c_WholeAST.dot.png
- https://github.com/chunhualiao/rose-ast/blob/master/parallelfor.c_WholeAST.dot.png
SageInterface 函数
// You can call the following functions with gdb //! Pretty print AST horizontally, output to std output void SageInterface::printAST (SgNode* node); //! Pretty print AST horizontally, output to a specified text file void SageInterface::printAST (SgNode* node, const char* filename); //! Pretty print AST horizontally, output to a specified text file. void SageInterface::printAST2TextFile (SgNode* node, const char* filename, bool printTypes=true);
一个翻译器 (textASTGenerator) 也可用,其源代码位于 exampleTranslators/defaultTranslator 下。
- make install-tools 将安装此工具
- textASTGenerator input.c 将生成整个 AST 的文本输出
- 将 AST 的一部分打印到屏幕上
- 将 AST 的一部分打印到文本文件中
(gdb) up #7 0x00007ffff418ab5d in Unparse_ExprStmt::unparseExprStmt (this=0x1a1bf950, stmt=0x7fffda63ce30, info=...) at ../../../sourcetree/src/backend/unparser/CxxCodeGeneration/unparseCxx_statements.C:9889 (gdb) p SageInterface::printAST(stmt) └──@0x7fffda63ce30 SgExprStatement transformation 0:0 └──@0x7fffd8488790 SgFunctionCallExp transformation 0:0 ├──@0x7fffe6211910 SgMemberFunctionRefExp transformation 0:0 └──@0x7fffd7f2c370 SgExprListExp transformation 0:0 └──@0x7fffd8488720 SgFunctionCallExp transformation 0:0 ├──@0x7fffe6211988 SgMemberFunctionRefExp transformation 0:0 └──@0x7fffd7f2c3d8 SgExprListExp transformation 0:0 $2 = void (gdb) up 10 #48 0x00007ffff40dce69 in Unparser::unparseFile (this=0x7fffffff8c60, file=0x7fffeb786010, info=..., unparseScope=0x0) at ../../../sourcetree/src/backend/unparser/unparser.C:945 (gdb) p SageInterface::printAST2TextFile(file,"test.txt")
示例命令行用法
textASTGenerator -c test_qualifiedName.cpp
cat test_qualifiedName.cpp.AST.txt
└──@0x7fe9f1916010 SgProject └──@0xb45730 SgFileList └──@0x7fe9f17be010 SgSourceFile ├──@0x7fe9fdf19120 SgGlobal test_qualifiedName.cpp 0:0 │ ├──@0x7fe9f159a010 SgTypedefDeclaration rose_edg_required_macros_and_functions.h 0:0 │ │ └── NULL │ ├──@0x7fe9f159a390 SgTypedefDeclaration rose_edg_required_macros_and_functions.h 0:0 │ │ └── NULL │ ├──@0x7fe9f0f59010 SgFunctionDeclaration rose_edg_required_macros_and_functions.h 0:0 "::feclearexcept" │ │ ├──@0x7fe9f1391010 SgFunctionParameterList rose_edg_required_macros_and_functions.h 0:0 │ │ │ └──@0x7fe9f1258010 SgInitializedName rose_edg_required_macros_and_functions.h 0:0 "::__excepts" │ │ │ └── NULL │ │ ├── NULL │ │ └── NULL │ ├──@0x7fe9f0f59540 SgFunctionDeclaration rose_edg_required_macros_and_functions.h 0:0 "::fegetexceptflag" │ │ ├──@0x7fe9f1391630 SgFunctionParameterList rose_edg_required_macros_and_functions.h 0:0 │ │ │ ├──@0x7fe9f1258420 SgInitializedName rose_edg_required_macros_and_functions.h 0:0 "::__flagp" │ │ │ │ └── NULL │ │ │ └──@0x7fe9f1258628 SgInitializedName rose_edg_required_macros_and_functions.h 0:0 "::__excepts" │ │ │ └── NULL │ │ ├── NULL │ │ └── NULL ... │ └──@0x7fe9eff218c0 SgFunctionDeclaration test_qualifiedName.cpp 14:1 "::foo" │ ├──@0x7fe9ef5e0320 SgFunctionParameterList test_qualifiedName.cpp 14:1 │ │ ├──@0x7fe9ef495278 SgInitializedName test_qualifiedName.cpp 14:13 "x" │ │ │ └── NULL │ │ └──@0x7fe9ef495480 SgInitializedName test_qualifiedName.cpp 14:20 "y" │ │ └── NULL │ ├── NULL │ └──@0x7fe9ee8f3010 SgFunctionDefinition test_qualifiedName.cpp 15:1 │ └──@0x7fe9ee988010 SgBasicBlock test_qualifiedName.cpp 15:1 │ ├──@0x7fe9eee1ba90 SgVariableDeclaration test_qualifiedName.cpp 16:3 │ │ ├── NULL │ │ └──@0x7fe9ef495688 SgInitializedName test_qualifiedName.cpp 16:3 "z" │ │ └── NULL │ ├──@0x7fe9ee7ad010 SgExprStatement test_qualifiedName.cpp 17:3 │ │ └──@0x7fe9ee7dc010 SgAssignOp test_qualifiedName.cpp 17:5 │ │ ├──@0x7fe9ee8c0010 SgVarRefExp test_qualifiedName.cpp 17:3 │ │ └──@0x7fe9ee813010 SgAddOp test_qualifiedName.cpp 17:9 │ │ ├──@0x7fe9ee8c0078 SgVarRefExp test_qualifiedName.cpp 17:7 │ │ └──@0x7fe9ee84a010 SgMultiplyOp test_qualifiedName.cpp 17:12 │ │ ├──@0x7fe9ee8c00e0 SgVarRefExp test_qualifiedName.cpp 17:11 │ │ └──@0x7fe9ee881010 SgIntVal test_qualifiedName.cpp 17:13 │ └──@0x7fe9ee77e010 SgReturnStmt test_qualifiedName.cpp 18:3 │ └──@0x7fe9ee8c0148 SgVarRefExp test_qualifiedName.cpp 18:10 ├── NULL ├── NULL └── NULL
存储库 errington1/ast-to-html
包含一个工具,用于将 Rose 抽象语法“图形”呈现为可折叠的 HTML,其中共享节点和循环由 HTML 链接表示。目前,它只能从命令行使用。计划是添加命令行选项以省略树的一部分,并将该工具作为库提供。目前,它有点任意地省略了源自文件 rose_edg_required_macros_and_functions.h
的树的部分。
命令
astToHTML file.C
将生成 file.C.html
,可以用浏览器查看
firefox file.C.html
我们提供了一套用于 AST 的健全性检查。我们使用它们来确保 AST 的一致性。强烈建议 ROSE 开发人员在完成 AST 转换后添加健全性检查。这比仅仅将代码正确地反解析为可编译代码具有更高的标准。AST 可能会正确地反解析,但在健全性检查中失败是很常见的。
推荐的健全性检查是
- 来自 src/midend/astDiagnostics 的 AstTests::runAllTests(project)。在内部,它调用以下检查
- TestAstForProperlyMangledNames
- TestAstCompilerGeneratedNodes
- AstTextAttributesHandling
- AstCycleTest
- TestAstTemplateProperties
- TestAstForProperlySetDefiningAndNondefiningDeclarations
- TestAstSymbolTables
- TestAstAccessToDeclarations
- TestExpressionTypes
- TestMangledNames::test()
- TestParentPointersInMemoryPool::test()
- TestChildPointersInMemoryPool::test()
- TestMappingOfDeclarationsInMemoryPoolToSymbols::test()
- TestLValueExpressions
- TestMultiFileConsistancy::test() //2009
- TestAstAccessToDeclarations::test(*i); // 命名类型测试
还有一些其他的函数散落在周围。但它们应该合并到 AstTests::runAllTests(project) 中。
- FixSgProject(*project); // 在 Qing 的 AST 接口中
- Utility::sanityCheck(SgProject*)
- Utility::consistencyCheck(SgProject*) // SgFile*
只需调用:SgNode::unparseToString()。您可以从 AST 中的任何 SgLocatedNode 调用它来转储部分 AST 的文本格式。
SageInterface 函数
//! Pretty print AST horizontally, output to std output void SageInterface::printAST (SgNode* node); //! Pretty print AST horizontally, output to a specified text file. void SageInterface::printAST2TextFile (SgNode* node, const char* filename);
一个翻译器 (textASTGenerator) 也可用,其源代码位于 exampleTranslators/defaultTranslator 下。
在 gdb 中的示例用法
- 将 AST 的一部分打印到屏幕上
- 将 AST 的一部分打印到文本文件中
(gdb) up #7 0x00007ffff418ab5d in Unparse_ExprStmt::unparseExprStmt (this=0x1a1bf950, stmt=0x7fffda63ce30, info=...) at ../../../sourcetree/src/backend/unparser/CxxCodeGeneration/unparseCxx_statements.C:9889 (gdb) p SageInterface::printAST(stmt) └──@0x7fffda63ce30 SgExprStatement transformation 0:0 └──@0x7fffd8488790 SgFunctionCallExp transformation 0:0 ├──@0x7fffe6211910 SgMemberFunctionRefExp transformation 0:0 └──@0x7fffd7f2c370 SgExprListExp transformation 0:0 └──@0x7fffd8488720 SgFunctionCallExp transformation 0:0 ├──@0x7fffe6211988 SgMemberFunctionRefExp transformation 0:0 └──@0x7fffd7f2c3d8 SgExprListExp transformation 0:0 $2 = void (gdb) up 10 #48 0x00007ffff40dce69 in Unparser::unparseFile (this=0x7fffffff8c60, file=0x7fffeb786010, info=..., unparseScope=0x0) at ../../../sourcetree/src/backend/unparser/unparser.C:945 (gdb) p SageInterface::printAST2TextFile(file,"test.txt")
示例命令行用法
textASTGenerator -c test_qualifiedName.cpp
cat test_qualifiedName.cpp.AST.txt
└──@0x7fe9f1916010 SgProject └──@0xb45730 SgFileList └──@0x7fe9f17be010 SgSourceFile ├──@0x7fe9fdf19120 SgGlobal test_qualifiedName.cpp 0:0 │ ├──@0x7fe9f159a010 SgTypedefDeclaration rose_edg_required_macros_and_functions.h 0:0 │ │ └── NULL │ ├──@0x7fe9f159a390 SgTypedefDeclaration rose_edg_required_macros_and_functions.h 0:0 │ │ └── NULL │ ├──@0x7fe9f0f59010 SgFunctionDeclaration rose_edg_required_macros_and_functions.h 0:0 "::feclearexcept" │ │ ├──@0x7fe9f1391010 SgFunctionParameterList rose_edg_required_macros_and_functions.h 0:0 │ │ │ └──@0x7fe9f1258010 SgInitializedName rose_edg_required_macros_and_functions.h 0:0 "::__excepts" │ │ │ └── NULL │ │ ├── NULL │ │ └── NULL │ ├──@0x7fe9f0f59540 SgFunctionDeclaration rose_edg_required_macros_and_functions.h 0:0 "::fegetexceptflag" │ │ ├──@0x7fe9f1391630 SgFunctionParameterList rose_edg_required_macros_and_functions.h 0:0 │ │ │ ├──@0x7fe9f1258420 SgInitializedName rose_edg_required_macros_and_functions.h 0:0 "::__flagp" │ │ │ │ └── NULL │ │ │ └──@0x7fe9f1258628 SgInitializedName rose_edg_required_macros_and_functions.h 0:0 "::__excepts" │ │ │ └── NULL │ │ ├── NULL │ │ └── NULL ... │ └──@0x7fe9eff218c0 SgFunctionDeclaration test_qualifiedName.cpp 14:1 "::foo" │ ├──@0x7fe9ef5e0320 SgFunctionParameterList test_qualifiedName.cpp 14:1 │ │ ├──@0x7fe9ef495278 SgInitializedName test_qualifiedName.cpp 14:13 "x" │ │ │ └── NULL │ │ └──@0x7fe9ef495480 SgInitializedName test_qualifiedName.cpp 14:20 "y" │ │ └── NULL │ ├── NULL │ └──@0x7fe9ee8f3010 SgFunctionDefinition test_qualifiedName.cpp 15:1 │ └──@0x7fe9ee988010 SgBasicBlock test_qualifiedName.cpp 15:1 │ ├──@0x7fe9eee1ba90 SgVariableDeclaration test_qualifiedName.cpp 16:3 │ │ ├── NULL │ │ └──@0x7fe9ef495688 SgInitializedName test_qualifiedName.cpp 16:3 "z" │ │ └── NULL │ ├──@0x7fe9ee7ad010 SgExprStatement test_qualifiedName.cpp 17:3 │ │ └──@0x7fe9ee7dc010 SgAssignOp test_qualifiedName.cpp 17:5 │ │ ├──@0x7fe9ee8c0010 SgVarRefExp test_qualifiedName.cpp 17:3 │ │ └──@0x7fe9ee813010 SgAddOp test_qualifiedName.cpp 17:9 │ │ ├──@0x7fe9ee8c0078 SgVarRefExp test_qualifiedName.cpp 17:7 │ │ └──@0x7fe9ee84a010 SgMultiplyOp test_qualifiedName.cpp 17:12 │ │ ├──@0x7fe9ee8c00e0 SgVarRefExp test_qualifiedName.cpp 17:11 │ │ └──@0x7fe9ee881010 SgIntVal test_qualifiedName.cpp 17:13 │ └──@0x7fe9ee77e010 SgReturnStmt test_qualifiedName.cpp 18:3 │ └──@0x7fe9ee8c0148 SgVarRefExp test_qualifiedName.cpp 18:10 ├── NULL ├── NULL └── NULL
1) 迭代器类:迭代器遵循 STL 迭代器模式,并实现为先序遍历,并维护自己的堆栈。迭代器执行与 ROSE 中的遍历类完全相同的遍历(它使用相同的基础信息)
#include "RoseAst.h" SgNode* node= .... // any subtree RoseAst ast(node); for(RoseAst::iterator i=ast.begin();i!=ast.end();++i) { cout<<"We are here:"<<(*i)->class_name()<<endl; }
更多功能
- 默认情况下,它不遍历空指针(您不会看到它们)。但是,如果您想查看和遍历所有空指针,您可以使用以下函数:ast.begin().withNullValues()
- 它还有一个功能,可以在遍历期间排除子树:您只需在*迭代器*上调用
- i.skipChildrenOnForward(); ++i; // 跳过当前节点的子节点,并转到遍历中所有这些子节点之后紧随其后的下一个节点
相关源文件
- https://github.com/rose-compiler/rose-develop/blob/master/src/midend/astMatching/RoseAst.h
- https://github.com/rose-compiler/rose-develop/blob/master/src/midend/astMatching/RoseAst.C
一些有用的成员函数
- get_base_type():某些从 SgType 派生的 IR 节点上的成员函数,并返回类型定义、引用、指针、数组、修饰符等下非递归剥离(直接)的类型。
- findBaseType() 递归地剥离所有
typedefs, SgTypedefType reference, SgReferenceType pointers, SgPointerType arrays, SgArrayType modifiers SgModifierType
- SgType * stripType (unsigned char bit_array=STRIP_MODIFIER_TYPE|STRIP_REFERENCE_TYPE|STRIP_POINTER_TYPE|STRIP_ARRAY_TYPE|STRIP_TYPEDEF_TYPE) const
返回类型定义、指针、引用、修饰符、数组表示等层级之下的隐藏类型。
- SgType * stripTypedefsAndModifiers () const
所有具有文件位置信息的 AST 节点都派生自 SgLocatedNode,它具有开始和结束 Sg_File_Info 来指示开始和结束位置信息。
您可以通过调用以下方法获取和打印位置信息对
locatedNode->get_startOfConstruct()->display() ; locatedNode->get_endOfConstruct()->display() ; // get beginning info only locatedNode->get_file_info()->display() ;
display() 的输出可能如下所示
Inside of Sg_File_Info::display(debug.......) isTransformation = false isCompilerGenerated = true (no position information) isOutputInCodeGeneration = false isShared = false isFrontendSpecific = true (part of ROSE support for gnu compatability) isSourcePositionUnavailableInFrontend = false isCommentOrDirective = false isToken = false file_id = 2 filename = /home/liao6/daily-test-rose/upcwork/install/include/gcc_HEADERS/rose_edg_required_macros_and_functions.h line = 167 column = 1 .... // transformation generated, will be outputted by the unparser upcr_pshared_ptr_t gsj; Inside of Sg_File_Info::display(debug.......) isTransformation = true (part of a transformation) isCompilerGenerated = false isOutputInCodeGeneration = true (output in code generator) isShared = false isFrontendSpecific = false isSourcePositionUnavailableInFrontend = false isCommentOrDirective = false isToken = false file_id = -3 filename = transformation line = 0 column = 0
如您所见,有一些 AST 节点是由 ROSE 的前端或转换器生成的。转换生成的位于节点可能没有行号或列号。
您可以获取文件名、行号、列号
SgLocatedNode* node = .... ; Sg_File_Info* info_start = node->get_startOfConstruct (); size_t a_start = (size_t)info_start->get_line (); string filename = node->get_file_info()->get_filename(); Sg_File_Info* info_end = node->get_endOfConstruct (); size_t a_end = (info_end == NULL) ? a_start : info_end->get_line ();
请参阅 ROSE 编译器框架/预处理信息 中的更多信息
除了节点和边之外,ROSE AST 还可以具有附加于节点和边的属性,这些属性附加用于预处理信息,例如 #include 或 #if .. #else。它们附加在附近 AST 节点之前、之后或内部(仅具有源位置信息的节点)。
一个示例转换器将遍历输入代码的 AST 并转储信息,其中可能包括预处理信息。
例如
exampleTranslators/defaultTranslator/preprocessingInfoDumper -c main.cxx ----------------------------------------------- Found an IR node with preprocessing Info attached: (memory address: 0x2b7e1852c7d0 Sage type: SgFunctionDeclaration) in file /export/tmp.liao6/workspace/userSupport/main.cxx (line 3 column 1) -------------PreprocessingInfo #0 ----------- : classification = CpreprocessorIncludeDeclaration: String format = #include "all_headers.h" relative position is = before
来源: http://www.rosecompiler.org/ROSE_Tutorial/ROSE-Tutorial.pdf(第 29 章 - 处理注释、预处理器指令以及向生成的代码中添加任意文本)
SageBuilder 和 SageInterface 命名空间提供用于创建 AST 并对其进行操作的函数。Doxygen 文档