Reputation: 757
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");
}
Upvotes: 1
Views: 134
Reputation: 262
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.
Upvotes: 2
Reputation: 6696
In Rascal a visit statement can only replace a non-terminal by the same non-terminal.
visit
statement to type-check correctly, for all nodes it visits it can only replace the values of the type it finds there by another type which is a subtype of the matched typeSwitchBlockStatementGroup
is not a sub-type of StatementNoShortIf
which triggers the error message, and the same for IfThenElseStatement
and SwitchBlockStatementGroup
So this means the code has to be fixed to jump through the type-safety and syntax-safety hoop. Options:
Statement
and one Expression
), often supported by explicit disambiguation constructs (like priorities >
and follow restrictions !>>
). Also, we avoid the use chain rules (injections) like syntax A = B
to avoid having to remember additional types and to avoid writing overly specific patterns.Statement
level and rewrite your pattern to replace one Statement
by another Statement
. In this case, this means matching the surrounding if-then-else on the left-hand side and rebuilding it on the right-hand side, and surrounding the SwitchBlockStatementGroup
with the right brackets to make it a statement, etc.Upvotes: 2