首页 > 解决方案 > 嵌套的后代模式匹配

问题描述

我正在尝试查找所有方法调用和包含它们的类。如果我理解正确,模式匹配会执行回溯以以所有可能的方式进行匹配。

采取以下java代码。

package main;
public class Main {
    public static void main(String[] args) {
        System.out.println("hello world");
        System.out.println("hello again");
    }
}

我正在使用 createAstsFromDirectory 加载代码。

rascal>ast = createAstsFromDirectory(|home:///multiple-method-calls|, false);

我试图找到对 println 的两个调用。以下代码匹配一次:

void findCalls(set[Declaration] ast)
{
  visit(ast)
  {
    case \class(_,_,_,/\methodCall(_,_,str methodName,_)):
      println("<methodName>");
  }
}
rascal>findCalls(ast);
println
ok

此代码匹配四次:

void findCalls(set[Declaration] ast)
{
  visit(ast)
  {
    case /\class(_,_,_,/\methodCall(_,_,str methodName,_)):
      println("<methodName>");
  }
}
rascal>findCalls(ast);
println
println
println
println
ok

模式必须如何精确匹配两次?

相关问题,如何访问类名?尝试访问类名时,我收到一条错误消息。

void findCalls(set[Declaration] ast)
{
  visit(ast)
  {
    case /\class(str className,_,_,/\methodCall(_,_,str methodName,_)):
      println("<className> <methodName>");
  }
}
findCalls(ast);
Main println
|project://personal-prof/src/Assignment13Rules.rsc|(3177,9,<141,16>,<141,25>): Undeclared variable: className

看起来第一个匹配项将 className 正确绑定到“Main”,但第二个匹配项没有。

标签: rascal

解决方案


我想我会这样写:

void findCalls(set[Declaration] ast) {
  for(/class(_, _, _, /methodCall(_,_,str methodName,_)) <- ast) {
      println("<methodName>");
  }
}

for 循环适用于它可以找到的每个类,通过它可以在内部找到的每个方法调用,因此对于您给出的示例来说是两次。

您的第二次尝试出错并且匹配太频繁:如果您将 a 嵌套/在访问案例的顶部,则您访问树中的每个位置一次,然后再次遍历整个子树,包括根节点。所以你会接到两次电话。

您的第一次尝试出错了,因为访问的案例模式的顶层不会自行回溯,它会找到整个模式的第一个匹配项然后停止。所以嵌套/只匹配一次,然后执行主体。

在 for 循环解决方案中,for 循环(与访问不同)将尝试所有可能的匹配,直到它停止,所以这就是要走的路。还有另一种更接近您的原始计划的解决方案:

void findCalls(set[Declaration] ast) {
      visit (ast) {
        case class(_, _, _, body) : 
          for (/methodCall(_,_,str methodName,_) <- body) {
            println("<methodName>");
          }
      }
}   

这也像 for 循环一样工作,它首先通过访问一一找到所有类,然后通过 for 循环遍历所有嵌套匹配。

最后,您还可以嵌套访问本身以获得正确答案:

void findCalls(set[Declaration] ast) {
      visit (ast) {
        case class(_, _, _, body) : 
          visit(body) {
            case methodCall(_,_,str methodName,_): {
            println("<methodName>");
          }
        }
      }
}

WRT 到 className 的东西,似乎visit和顶级深度匹配的组合在这样的情况下:case /<pattern>Rasca 解释器中有一个错误,深度模式中变量绑定丢失。所以请避免这种模式(我认为您不需要它),如果您愿意,请在 github 上提交问题报告?

在 for 循环的情况下,这将按预期工作:

for(/class(str className, _, _, /methodCall(_,_,str methodName,_)) <- ast) {
   println("<className>::<methodName>");
}

推荐阅读