Search code examples
rascal

UnexpectedType error to refactoring


I'm having a problem while trying to refactor an if/else statement in a switch statement. The grammar used is Java 8, according to the syntax below:

If else statement

syntax IfThenStatement = "if" "(" Expression ")" Statement ;

syntax IfThenElseStatement = "if" "(" Expression ")" StatementNoShortIf "else" Statement ;

syntax IfThenElseStatementNoShortIf = "if" "(" Expression ")" StatementNoShortIf "else" StatementNoShortIf ;

Switch statement

syntax SwitchStatement = "switch" "(" Expression ")" SwitchBlock ; 

syntax SwitchBlock = "{" SwitchBlockStatementGroup* SwitchLabel* "}" ;

syntax SwitchBlockStatementGroup = SwitchLabels BlockStatements ;

syntax SwitchLabels = SwitchLabel+ ; 

syntax SwitchLabel = "case" ConstantExpression ":" 
                   | "default" ":" 
                   ;

I'm using the following code to perform the refactoring:

CompilationUnit refactorIfElseStatement(CompilationUnit unit) =  visit(unit) {
    case (IfThenElseStatement) `if (<Identifier idIf>.equals(<StringLiteral stringCompare>)) <StatementNoShortIf stmtIf> else <Statement stmtElse>` => 
        (SwitchStatement) `switch (<Identifier idIf>) {<SwitchBlockStatementGroup switchBlock>}`
        when switchBlock := generateCaseFromIfElseStatement(stmtElse, idIf)
};

SwitchBlockStatementGroup generateCaseFromIfElseStatement(Statement stmt, Identifier idIf) = visit(stmt){
    case (StatementNoShortIf) `<StatementNoShortIf stmt1>` => 
         (SwitchBlockStatementGroup) `default: <BlockStatements stmt1>`

    case (IfThenElseStatement) `if (idIf.equals(<StringLiteral stringCompare>)) <StatementNoShortIf stmtIf> else <Statement stmtElse>` =>
         (SwitchBlockStatementGroup) `case <ConstantExpression stringCompare> : { <BlockStatements stmtIf> }`
};

However, when you run the refactoring code, the following error is displayed:

Expected SwitchBlockStatementGroup, but got Statement Advice: |http://tutor.rascal-mpl.org/Errors/Static/UnexpectedType/UnexpectedType.html|

At first, the refactoring is simple as can be seen in the code block below. Subsequently, I intend to increase its complexity to meet other cases.

if (string.equals("boo")){
    System.out.println("is a boo");
}
else if (string.equals("blah")){
    System.out.println("is a blah");
}
else if (string.equals("foo")){
    System.out.println("is a foo");
}
else{
    System.out.print("is a default");
}

Solution

  • I fixed the problem using (at the top level) a visitor + a recursive function based on a switch-case statement of Rascal (without using another visitor expression). Note that this code is still experimental, and I have only tested it using a few test cases.

    CompilationUnit refactorToSwitchString(CompilationUnit unit) = top-down-break visit(unit) {
     case (Statement)`if(<Identifier id>.equals(<StringLiteral lit>)) {<Statement stmt1> } else <Statement stmt2>` => 
     (Statement)`switch(<Identifier id>) { case <StringLiteral lit> : { <Statement stmt1> }  <SwitchBlockStatementGroup* stmt3> }` 
     when stmt3 := buildSwitchGroups(stmt2, id)
    };
    
    
    SwitchBlockStatementGroups buildSwitchGroups(stmt, id) {
     switch(stmt) {
      case (Statement)`if(<Identifier id>.equals(<StringLiteral lit>)) { <Statement stmt1> } else <Statement stmt2>` : {
        stmt3 = buildSwitchGroups(stmt2, id) ;
        return (SwitchBlockStatementGroups)`case <StringLiteral lit> : { <Statement stmt1> break; } <SwitchBlockStatementGroup* stmt3>`;
      }
      case (Statement)`if(<Identifier id>.equals(<StringLiteral lit>)) <Statement stmt1>` : {
       return (SwitchBlockStatementGroups) `case <StringLiteral lit> : { <Statement stmt1> break;`;
      }
      case (Statement)`<Statement stmt>` : {
         return (SwitchBlockStatementGroups)`default : <Statement stmt>` ;
      }
    };
    

    I had to change the grammar a bit, introducing a definition like:

    syntax SwitchBlock = "{" SwitchBlockStatementGroups SwitchLabel* "}" ;
    syntax SwitchBlockStatementGroups = SwitchBlockStatementGroup* ;
    

    Though I am not sure if this change was really necessary.