1

I want to refactor some C code using Clair and Rascal. I search for a function with a certain name. If I find such a function, I want to replace it with another function. I need to choose between four functions. The function to choose depends on the argument of the function found. I need to match the root element of an expression.

I can match using the visit pattern. I tried

visit(body) {
  case \functionCall(func, args): {
    if ("myName" == func.name.\value) {
      visit(args[0]) {
        case \equals(_, _): println("Equals");
        case \notEquals(_, _): println("Not equals");
      }             
    }
  }
}

This does not guarantee I match the root element. In (A != B) == C I only want to match the ==

How can I match only the root element?

Matty
  • 134
  • 1
  • 7
  • First I tried to match using an if-statement. Like: if (args[0] == \equals(_, _) ..., but this does not work. I got it working with a switch-statement. – Matty Jul 08 '19 at 08:09

2 Answers2

1

You can nest patterns arbitrarily and use this to match a function call including the name of your function and the shape of the first argument you want to match.

For example:

case functionCall(someId("my name", [e:\equals(_, _), *_]) => newFunctionCall(e)
case functionCall(someId("my name", [e:\notEquals(_, _), *_]) => newFunctionCall(e)

Note the list pattern [..] which matches argument lists of arbitrary length here as long as the first argument is an equals or nonEquals expression.

So you could repeat a top-level case for each case of the first parameter, like above, or nest a switch and use "insert", like below:

  case functionCall(someId("my name", [arg, *_]) : 
               switch(arg) {
                  case equals(_, _) : insert newFunctionCall(arg);
                  ...
               }

I prefer the first case because it's more declarative. Rascal should factor the common stuff for efficiency sake internally. The fact that both patterns are very similar is not a code smell in Rascal IMHO, because that's the whole point of this code, you want to treat two similar patterns slightly differently and the first example documents that explicitly without nesting control flow. In other words: It's clearer to nest a pattern than to nest control flow

Jurgen Vinju
  • 6,393
  • 1
  • 15
  • 26
0

I use this field, because space is limited in the comment field.

I like this feature of nested patterns, but I do not get the simple case to match. I tried the following patterns:

    visit(body) {
        case \functionCall("methodA", [arg, *_]): println("match");
        case \functionCall(SomeId("methodA", [arg, *_])): println("match");
        case \functionCall(SomeId("methodA"), [arg, *_]): println("match");
        case \functionCall(IdExpresssion("methodA", [arg, *_])): println("match");
        case \functionCall(IdExpresssion("methodA"), [arg, *_]): println("match");
        case \functionCall(name("methodA", [arg, *_])): println("match");
        case \functionCall(name("methodA"), [arg, *_]): println("match");
    }

iprint(body) gives the following results:

compoundStatement(
  [
    compoundStatement(
      [
        expressionStatement(
.... some expressions and declarations
        for(
... for statement
          compoundStatement(
            [
... inside for loop
              expressionStatement(
                functionCall(
                  idExpression(
                    name(
                      "methodA",
                      src=|project://Instrumentation/example.c|(11195,10)),
                    src=|project://Instrumentation/example.c|(11195,10),
                    decl=|cpp+problem://Attempt%20to%20use%20symbol%20failed:%20methodA|,
                    typ=problemType("Failure to determine type of expression")),
                  [equals(
                      idExpression(
                        name(
                          "beforeTest",
                          src=|project://Instrumentation/example.c|(11207,20)),
                        src=|project://Instrumentation/example.c|(11207,20),
                        decl=|cpp+variable:///test1()/beforeTest|,
                        typ=problemType("Type depends on an unresolved name")),
                      idExpression(
                        name(
                          "afterTest",
                          src=|project://Instrumentation/example.c|(11231,19)),
                        src=|project://Instrumentation/example.c|(11231,19),
                        decl=|cpp+variable:///test1()/afterTest|,
                        typ=problemType("Type depends on an unresolved name")),
                      src=|project://Instrumentation/example.c|(11207,43),
                      typ=problemType("Type depends on an unresolved name"))],
                  src=|project://Instrumentation/example.c|(11195,57),
                  typ=problemBinding()),
                src=|project://Instrumentation/example.c|(11195,58)),
              expressionStatement(
... more expressions and declaratios in for loop              
  ],
  src=|project://Instrumentation/example.c|(10148,1349))ok

How can I make a pattern that does match MethodA?

Matty
  • 134
  • 1
  • 7
  • 1
    It's best to copy/paste a pattern from an example (printed-out tree). The expression syntax is the same as the pattern syntax. In the example I see `idExpression(...)` with a lowercase `i` and in the pattern I see `IdExpression`. That's the reason the pattern does not match. – Jurgen Vinju Jul 12 '19 at 10:25
  • 1
    Even better: the example has `idExpression(name("patternA")))` so there should also be a `name` constructor nested! – Jurgen Vinju Jul 12 '19 at 10:26
  • 1
    the `src` fields can be ignored and removed from the pattern (keyword fields in the values are ignored by pattern matching if they are not included in the match pattern explicitly) – Jurgen Vinju Jul 12 '19 at 10:27
  • Both idExpression and name are necessary. – Matty Jul 13 '19 at 06:10
  • Yep. The pattern should be exactly like the example you printed. Groetjes – Jurgen Vinju Jul 13 '19 at 08:24