重构

时间:2016-11-11 21:42:12

标签: rascal

我在尝试重构if/else statement中的switch statement时遇到问题。使用的语法是Java 8,根据以下语法:

如果是其他声明

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

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

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

切换声明

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

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

syntax SwitchBlockStatementGroup = SwitchLabels BlockStatements ;

syntax SwitchLabels = SwitchLabel+ ; 

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

我使用以下代码执行重构:

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> }`
};

但是,运行重构代码时,会显示以下错误:

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

首先,重构很简单,如下面的代码块所示。随后,我打算增加其复杂性,以满足其他情况。

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");
}

2 个答案:

答案 0 :(得分:2)

在Rascal中,访问语句只能用同一个非终端替换非终端。

  • 原因是为了使visit语句的rank-2多态语义正确地进行类型检查,对于它访问的所有节点,它只能用其他类型替换它在那里找到的类型的值这是匹配类型的子类型
  • 此示例中的
  • SwitchBlockStatementGroup不是触发错误消息的StatementNoShortIf的子类型,IfThenElseStatementSwitchBlockStatementGroup
  • 的子类型相同
  • 特定错误消息令人困惑
  • 如果类型检查器忽略它,你现在编写的代码可能不会生成正确的Java代码。
  • 如果你使用访问来转换Rascal中的解析树,所有中间结果都保证语法正确Java

所以这意味着必须修复代码以跳过类型安全和语法安全箍。选项:

  1. 更改语法以减少非终端;这可以做到,但要做到这一点并不容易。它将使您的生活更轻松。 Rascal语法使用较少的非终端(即只有一个Statement和一个Expression),通常由明确的消歧构造支持(如优先级>并遵循限制!>>)。此外,我们避免使用syntax A = B之类的使用链规则(注入),以避免必须记住其他类型并避免编写过于特定的模式。
  2. 缩小到Statement级别并重写您的模式,将Statement替换为另一个Statement。在这种情况下,这意味着匹配左侧的周围if-then-else并在右侧重建它,并用右括号围绕SwitchBlockStatementGroup以使其成为声明等。
  3. 我有时会编写可重复使用的函数来在不同的子终端之间进行转换。
  4. 最后的解决方法是扩展带有扩展名的语法(在单独的模块中)以允许&#34;错误的&#34;解析树,然后将它们转换掉或者相信它们与正确的Java重合。我见过的例子是&#34; BlockExpressions&#34;其中一个块可以在源到源编译器中暂时表达,只能在稍后阶段转换为纯Java。在这种情况下,我不建议使用此选项,因为扩展语法必然是不明确的。

答案 1 :(得分:2)

我使用(在顶层)访问者+基于Rascal的switch-case语句(不使用其他访问者表达式)的递归函数来修复问题。请注意,此代码仍然是实验性的,我只使用一些测试用例进行了测试。

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>` ;
  }
};

我不得不稍微改变语法,引入如下定义:

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

虽然我不确定这种变化是否真的有必要。