2011-01-14 44 views
2

我想修改我的編譯器的代碼生成器以使用訪問者模式,因爲當前的方法必須使用多個條件語句在生成相應的代碼之前檢查孩子的實際類型。但是,在訪問後我有問題要獲得孩子的屬性。例如,在雙元表達我用這個:訪問者模式和編譯器代碼生成,如何獲取子代屬性?

LHSCode := GenerateExpressionCode(LHSNode); 
RHSCode := GenerateExpressionCode(RHSNode); 
CreateBinaryExpression(Self,LHS,RHS); 

在訪問者模式的訪問方法通常是無效的,所以我不能得到LHS和RHS表達式代碼。保留共享全局變量不是一種選擇,因爲表達式代碼生成是遞歸的,因此可以擦除保存在變量中的先前值。

我將只顯示二進制表示,因爲這是最複雜的部分(現在):

function TLLVMCodeGenerator.GenerateExpressionCode(
    Expr: TASTExpression): TLLVMValue; 
var 
    BinExpr: TASTBinaryExpression; 
    UnExpr: TASTUnaryExpression; 
    LHSCode, RHSCode, ExprCode: TLLVMValue; 
    VarExpr: TASTVariableExpression; 
begin 
    if Expr is TASTBinaryExpression then begin 
    BinExpr := Expr as TASTBinaryExpression; 
    LHSCode := GenerateExpressionCode(BinExpr.LHS); 
    RHSCode := GenerateExpressionCode(BinExpr.RHS); 
    case BinExpr.Op of 
     '<': Result := FBuilder.CreateICmp(ccSLT, LHSCode, RHSCode); 
     '<=': Result := FBuilder.CreateICmp(ccSLE, LHSCode, RHSCode); 
     '>': Result := FBuilder.CreateICmp(ccSGT, LHSCode, RHSCode); 
     '>=': Result := FBuilder.CreateICmp(ccSGE, LHSCode, RHSCode); 
     '==': Result := FBuilder.CreateICmp(ccEQ, LHSCode, RHSCode); 
     '<>': Result := FBuilder.CreateICmp(ccNE, LHSCode, RHSCode); 
     '/\': Result := FBuilder.CreateAnd(LHSCode, RHSCode); 
     '\/': Result := FBuilder.CreateOr(LHSCode, RHSCode); 
     '+': Result := FBuilder.CreateAdd(LHSCode, RHSCode); 
     '-': Result := FBuilder.CreateSub(LHSCode, RHSCode); 
     '*': Result := FBuilder.CreateMul(LHSCode, RHSCode); 
     '/': Result := FBuilder.CreateSDiv(LHSCode, RHSCode); 
    end; 
    end else if Expr is TASTPrimaryExpression then 
    if Expr is TASTBooleanConstant then 
     with Expr as TASTBooleanConstant do 
     Result := FBuilder.CreateConstant(Ord(Value), ltI1) 
    else if Expr is TASTIntegerConstant then 
     with Expr as TASTIntegerConstant do 
     Result := FBuilder.CreateConstant(Value, ltI32) 
    else if Expr is TASTUnaryExpression then begin 
     UnExpr := Expr as TASTUnaryExpression; 
     ExprCode := GenerateExpressionCode(UnExpr.Expr); 
     case UnExpr.Op of 
     '~': Result := FBuilder.CreateXor(
      FBuilder.CreateConstant(1, ltI1), ExprCode); 
     '-': Result := FBuilder.CreateSub(
      FBuilder.CreateConstant(0, ltI32), ExprCode); 
     end; 
    end else if Expr is TASTVariableExpression then begin 
     VarExpr := Expr as TASTVariableExpression; 
     with VarExpr.VarDecl do 
     Result := FBuilder.CreateVar(Ident, BaseTypeLLVMTypeMap[BaseType]); 
    end; 
end; 

希望你能理解吧:)

+0

問題不明確。你的意思是什麼兒童屬性?你的意思是GenerateExpressionCode扮演訪問方法的角色? – ssmir 2011-01-14 12:30:00

+0

@ssmir:如果我理解正確,則顯示的代碼是非訪客實現的片段。 – 2011-01-14 12:58:55

回答

3

在訪問者模式訪問方法通常無效,所以我無法從LHS和RHS獲取表達式代碼。保留共享全局變量不是一種選擇,因爲表達式代碼生成是遞歸的,因此可以擦除保存在變量中的先前值。

您需要獲取孩子的屬性,當他們被訪問,保持任何你需要的屬性,並確保你仍然有他們,當你需要他們。這可能會使訪問者的內部結構稍微複雜一點,但這確實是可行的。代碼生成絕對是訪問者模式的常見用法。

通常情況下,您不必拘泥於屬性,但您需要堅持中間結果,並將其結合到其他中間結果中以訪問其他對象。我認爲在這裏是這種情況,但是交互很複雜,有點令人困惑。我不是在Object Pascal上的專家,所以不是試圖寫實際的代碼,而是描述我將如何處理它。

在這種情況下,我可能會使用一個保存中間結果的堆棧。

遍歷順序可以在節點的接受方法或訪問者的訪問方法或外部迭代器中驅動。爲了簡單起見,我將假設它在接受方法中。

在簡單對象的接受方法中,您只需執行標準visitor.visit(this)(但是您在Object Pascal中說過)。

在簡單對象(如TASTBooleanConstant)的訪問方法中,您可以調用適當的方法,在此例中爲FBuilder.CreateConstant,其中包含從對象中提取的值並將該方法的結果推送到訪問者的堆棧中。

在更復雜對象的接受方法中,例如您的TASTBinaryExpression,您應先調用孩子的接受方法,然後執行標準visitor.visit(this),確保首先訪問子女。

然後,由於首先訪問了子級,因此在調用複雜對象的訪問方法時,應將結果放在堆棧中。在該訪問方法中,您將從堆棧中彈出適當的結果到本地變量中,根據您擁有的操作符調用適當的FBuilder.CreateXxx方法,將這些值作爲參數傳遞並將結果放入堆棧。

對於TASTUnaryExpression對象,它將是類似的,但只有一個孩子在accept方法中擔心,並且只有一箇中間結果彈出堆棧並在visit方法中使用。

在您的客戶端代碼中,創建訪問者並調用頂層節點的accept方法,將訪問者作爲參數傳遞給訪問者。在所有遞歸完成之後,棧應該只包含最終結果,訪問者類應該提供getResult方法,允許客戶端檢索它。

對不起,這是如此冗長 - 它可能會更清晰的代碼,但希望這給你一個如何處理這個問題的想法。

一個很好的資源來學習重構如何在現有的代碼中引入模式是Joshua Kerievsky的書Refactoring to Patterns