ROSE 编译器框架/AST 匹配
AST 匹配机制
- rose/src/midend/astMatching
以下文档位于以下文件(但尚未完成,并且不会显示在 doxygen 中)
- rose/src/midend/astMatching/AstMatching.docs
这些示例很好地概述了您可以使用匹配器执行的操作。请注意,它使用自己的解析器并实现了自己的规范语言来指定匹配表达式(具有许多不同的运算符)。匹配器是在 AST 迭代器之上实现的 - 因此,匹配器是迭代器的一个用例。
AstMatching 机制允许指定任意大的模式,以便在 AST 中的任何子树上进行匹配。这些模式以字符串形式指定,并且可以使用 AST 节点的类型名称来指定 AST 模式。此外,还提供变量和一些运算符,以允许指定复杂的模式。也可以使用 '_' 忽略匹配中的子树。二元运算符 '|' 允许将不同的匹配子表达式组合成一个表达式。变量用于指定存储在匹配结果中的已匹配子树的指针,以便用户进一步处理。
在下面的示例中,我们匹配两边都有变量的赋值,例如 x=y,并将结果分配给变量 $R。
#include "rose.h" #include "AstTerm.h" #include "AstMatching.h" AstMatching m; MatchResult res=m.performMatching("$R=SgAssignOp(SgVarRef,SgVarRef)",astRoot);
其中 'astRoot' 是指向 AST 中某个节点的指针。AssignOp 和 SgVarRef 是 ROSE AST 节点的名称,$R 是匹配器变量的名称。
在上面的示例中,将匹配所有表示具有两个变量作为操作数的赋值操作的子树。美元符号表示变量。在上面的示例中,指向已匹配子树的指针被分配给变量 $R。所有已匹配赋值的结果存储在类型为 AstMatchingResult 的变量 res 中。匹配结果是一组映射,其中每个映射代表一次成功匹配的结果,并包含变量名称和指向相应 AST 子树的指针对。
变量用作键来存储/检索结果中已匹配的子树。可以随意使用多个变量(键),并且支持两种使用形式。变量以引导美元符号和任意数量的字母和下划线(单个下划线用作通配符)表示。可以使用变量赋值符号将指定模式的指针分配给变量。
例如,$R=SgAssignOp(SgVarRef,_,_) 与所有在左侧具有变量并在右侧具有某些表达式的赋值匹配。
或者,我们也可以使用 $R=SgAssignOp($X=SgVarRef,$Y=_) - 在这种情况下,我们还将指向已匹配变量节点的指针和指向 rhs 上表达式的指针存储在匹配结果中。
对于表达式 $Y=_,我们也可以简单地将其写为 $Y 作为简写,因此我们也可以使用 $R=SgAssignOp($X=SgVarRef,$Y) 代替。不允许将变量分配给变量,例如 $Z=$Y。
也可以使用匹配表达式中的 '_' 指定忽略子树进行匹配。例如,如果我们使用 SgAssignOp(_,_),我们可以匹配 AST 中的所有赋值节点,但忽略表示 rhs 和 lhs 的 AST 的结构。
可以通过在匹配表达式中使用“null”显式匹配空值。例如,$X=SgForStatement(_,_,_,_,null) 将匹配所有第五个参数为 0 的 SgForStatement 项。
在匹配表达式中放置运算符 '#' 允许从后续匹配中排除任意子树,以便对匹配操作进行应用。即,标记的子树不会被遍历。例如,如果我们只想匹配最外层的 for 语句,而不是嵌套的 for 语句,我们可以使用
$FOR=SgForStatement(_,_,_,#_,..)
这仅匹配外部 for 语句,因为主体(第四个参数)被排除在应用匹配运算符之外。如果没有 '#',我们也会匹配内部循环。
此运算符可以在匹配表达式中使用,以指定可以跟随任意数量的参数。例如,我们可以使用 SgBlock($First,..) 来匹配 SgBlock 中的第一条语句。由于 SgBlock 可以具有任意元数,因此在这方面非常有用。运算符 '..' 在指定节点的元数时最多只能使用一次,但在匹配模式中可以任意多次使用,例如 SgBlock(SgForStatement($Cond,..),..) 是可以的,但 SgBlock(_,..,_,..) 是不行的。
此运算符允许组合多个匹配表达式。例如,“SgAddOp($L,$R)|SgSubOp($L,$R)”将匹配 SgAddOp 并将指向其两个子节点的指针绑定到 $L 和 $R,或者它将匹配 SgSubOp。运算符 '|' 执行短路计算,因此,匹配是从左到右执行的,并且只要其中一个模式可以成功匹配,匹配就会停止。
- performMatching("$R=AssignOp(_,_)",astRoot);
Match all assignment operators in an AST.
- performMatching("$R=SgAssignOp(SgVarRefExp,SgIntVal)",astRoot);
Match all assignment operators with a variable on the lhs and an integer value on the rhs.
- performMatching("$FORROOT=SgForStatement(_,_,_,#_)",astRoot);
Match all outer most for loops, but no nested for-loops. The operator '#' ensures that the match expression is not applied on the AST representing the body of the for-statement (4th argument). The pointer to the root of the AST representing the for-loop is bound to $FORROOT.
- performMatching("$N=_(null)",astRoot);
Match all nodes with arity 1 and a single null value. The main purpose for such match-expressions is to perform consistency checks in the AST.
- performMatching("$N=SgInitializedName(null)",astRoot); // 默认ROSE AST中存在许多这样的节点
Specifically match all SgInitializedName nodes with a null pointer.
变量可以嵌套在模式内部。如果不需要,顶级模式不需要变量。
- performMatching("SgForStatement($LoopCond,_,_,_)|SgWhile($LoopCond,_)|SgDoWhile(_,$LoopCond)",astRoot);
Match different Loop constructs and bind variable $LoopCond to the respective loop condition.
- performMatching("SgAssignOp(SgVarRef,SgAddOp($X,$Y))",astRoot)
Match assignments with a variable on the rhs and an add-operator on the rhs(root). The pointers to the sub-ASTs representing the lhs and rhs of the add-operator are bound to variables $X and $Y for each match in the AST:
- performMatching("$Func=SgFunctionCallExp($FuncRef,$Params)",astRoot)
Match all function calls and bind variable $Func to the root of each such expression, bind $FuncRef to the SgFunctionRefExp (which can be used to obtain the name) and $Params to the AST representing the parameters:
#include "AstTerm.h" #include "AstMatching.h" AstMatching m; // match i++ MatchResult res=m.performMatching("SgForStatement(_,_,SgPlusPlusOp($I=SgVarRefExp),..)",forloop); // match i++ or i== // MatchResult res=m.performMatching("SgForStatement(_,_,SgPlusPlusOp($I=SgVarRefExp)|SgMinusMinusOp($I=SgVarRefExp),..)",forloop); // cout<<res.size()<<endl; for(MatchResult::iterator i=res.begin();i!=res.end();++i) { // obtain the result:each variable is a map SgVarRefExp* ivar = isSgVarRefExp( (*i)["$I"]); cout<<"var:"<< ivar->unparseToString()<<endl; }
通常,对于给定的子树,不清楚应该使用哪种模式字符串。在这种情况下,您可以先找到其根节点,并使用AstTerm::astTermWithNullValuesToString() 打印出一个模式字符串。
#include "AstTerm.h" // check if this is within auto kernel = ...; SgStatement* stmt = SageInterface::getEnclosingStatement(exp); AstMatching m; MatchResult r =m.performMatching ("$L=SgVariableDeclaration", stmt); for(MatchResult::iterator i=r.begin();i!=r.end();++i) { SgVariableDeclaration* i1 = isSgVariableDeclaration((*i)["$L"]); cout<< AstTerm::astTermWithNullValuesToString(i1)<<endl; }
一些示例输出可能如下所示
"SgAggregateInitializer(SgExprListExp(SgDesignatedInitializer(SgExprListExp(SgAdaOthersExp:others),SgAssignInitializer(SgAggregateInitializer(SgExprListExp(SgDesignatedInitializer(SgExprListExp(SgAdaOthersExp:others),SgAssignInitializer(SgLongDoubleVal:2.0))))))))"
请注意,您不应该直接复制粘贴输出字符串作为匹配模式。您需要删除表达式中的:value 部分。
例如:(SgLongDoubleVal:2.0) 应该变成 (SgLongDoubleVal)
有一个专门的翻译器来转储输入文件的 AST 项
- exampleTranslators/defaultTranslator/astTermGenerator.C
结果收集在 std::list of std::maps 中。每个 map 代表 AST 中一个位置的一次成功匹配,并包含所有绑定变量。可以使用名称和随机访问运算符访问变量。列表中的元素数量(= maps)对应于 AST 中匹配模式的数量。
typedef std::map<std::string,SgNode*> SingleMatchVarBindings; typedef std::list<SingleMatchVarBindings> MatchResult;
可以按如下方式访问 AST 中匹配模式的指针
/* 1 */ AstMatching m; /* 2 */ MatchResult res=m.performMatching("$R=SgInitializedName($X)",root); /* 3 */ std::cout << "Number of matched patterns: " << r.size() << std::endl; /* 4 */ for(MatchResult::iterator i=r.begin();i!=r.end();++i) { /* 5 */ cout<<"Match expression variable $R:"<<isSgLocatedNode((*i)["R"])->unparseToString()<<endl; /* 6 */ cout<<"Match expression variable $X:"<<isSgLocatedNode((*i)["R"])->unparseToString()<<endl; /* 7 */ }
在第 1 行创建了 AstMatching 对象。在第 2 行,将匹配表达式和 AST 的根节点提供给匹配机制,并计算结果。匹配可以在任何感兴趣的 AST 子树上执行,方法是在开始匹配操作时让 'root' 指向相应的 AST 子树。在第 3 行打印匹配模式的数量。在第 4-7 行访问变量 $R 变量 $X,并使用 unparseToString 函数解析相应的匹配 AST 子树。匹配变量 $R 和 $X 的指针值引用成功匹配 AST 子树的根节点。
以下是一个更详细的代码示例,用于在整个 ROSE AST 上执行一次匹配操作,并使用迭代器打印所有匹配结果中的 map
#include "AstTerm.h" #include "AstMatching.h" // Fragment from the matcher_demo program AstMatching m; MatchResult r=m.performMatching("$R=SgInitializedName(_)",root); // print result in readable form for demo purposes std::cout << "Number of matched patterns: " << r.size() << std::endl; // for each matched instance for(MatchResult::iterator i=r.begin();i!=r.end();++i) { std::cout << "MATCH: \n"; // obtain the map SingleMatchVarBindings& dict=(*i); SgNode* matched_op = dict["$R"] ; // retrieve the node by its key/variable // Alternatively, iterate through all entries in the map. // for each variable in the matched instance for(SingleMatchVarBindings::iterator vars_iter=(*i).begin();vars_iter!=(*i).end();++vars_iter) { SgNode* matchedTerm=(*vars_iter).second; std::cout << " VAR: " << (*vars_iter).first << "=" << AstTerm::astTermWithNullValuesToString(matchedTerm) << " @" << matchedTerm << std::endl; } std::cout << std::endl; }
变量 matchedTerm 被赋予指向与变量绑定的相应 ROSE AST 节点的指针。(*vars_iter).first 是调用 performMatching 时匹配表达式中使用的变量的名称。在本例中,它们是 $R、$X 和 $Y。generateAstTerm 函数是一个辅助函数,用于以可读的形式在 stdout 上打印 AST。它使用与匹配机制相同的 Ast::iterator_with_null 实现。
示例输出
MATCH: VAR: $R=SgInitializedName(null) @0x7f1f8914da00 MATCH: VAR: $R=SgInitializedName(null) @0x7f1f8914db28 MATCH: VAR: $R=SgInitializedName(SgAssignInitializer(SgIntVal)) @0x7f1f8914dc50 MATCH: VAR: $R=SgInitializedName(null) @0x7f1f8914dd78 MATCH: VAR: $R=SgInitializedName(null) @0x7f1f8914dea0 MATCH: VAR: $R=SgInitializedName(SgAssignInitializer(SgIntVal)) @0x7f1f8914dfc8