2016-11-11 42 views
1

我有一個問題,而試圖在switch statement重構的if/else statement。所用的語法就是Java 8,根據下面的語法:UnexpectedType錯誤重構

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

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

syntax SwitchBlockStatementGroup = SwitchLabels BlockStatements ; 

syntax SwitchLabels = SwitchLabel+ ; 

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

我用下面的代碼來執行

如果else語句

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

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

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

switch語句重構:

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

您可以通過使用以下限制來簡化語法,即「」if「」(「Expression」()「Statement!>>」else「Statement」不需要StatementNoShortIf,並且您擁有更少的非終端/類型擔心。 – jurgenv

回答

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* ; 

雖然我不知道,如果這種變化確有必要。

2

在Rascal中,訪問聲明只能用相同的非終端替換非終端。

  • 的原因是爲了使visit聲明的秩爲2的多態語義正確輸入檢查,因爲訪問它只能替換類型的值是由其他類型的發現那裏所有的節點,其在該示例中SwitchBlockStatementGroup匹配類型
  • 的子類型不是和StatementNoShortIf亞型觸發該錯誤消息,與同爲IfThenElseStatementSwitchBlockStatementGroup
  • 特定錯誤消息是混淆
  • 的代碼作爲你現在寫的可能會如果類型檢查器忽略它,則不會生成正確的Java代碼。
  • 如果使用訪問變換流氓解析樹,所有的中間結果被語法正確的保證Java的

因此,這意味着代碼已經被固定通過類型安全和語法安全箍跳。選項:

  1. 將語法改爲擁有更少的非終端;這可以做到,但要正確做到並不容易。它會讓你的生活更輕鬆。流氓語法使用更少的非終端(即僅一個Statement和一個Expression),通常由明確的消歧構建支持(類似優先級>並按照限制!>>)。此外,我們避免使用鏈規則(注射)等syntax A = B以避免必須記住附加類型,並避免過度寫入特定模式。
  2. 縮小到Statement水平和重寫你的模式來取代另一個Statement一個Statement。在這種情況下,這意味着周邊配套IF-THEN-ELSE的左側和重建它的右側,以及圍繞SwitchBlockStatementGroup用正確的支架,使之聲明等。
  3. 我有時會編寫可重用的函數來在不同的子非終端之間進行轉換。
  4. 最後的解決方法是擴展帶擴展名的語法(在一個單獨的模塊中),以允許「不正確的」解析樹,並在稍後轉換它們或相信它們確實與正確的Java一致。我見過的例子是「BlockExpressions」,其中一個塊可以是源到源編譯器中的臨時表達式,僅在稍後階段才轉換爲純Java。在這種情況下,我不會推薦這個選項,因爲擴展語法必然是不明確的。