2011-01-14 6 views
2

現在のアプローチでは、対応するコードを生成する前に、子の実際の型をチェックするために複数の条件文を使用する必要があるため、コンパイラのコードジェネレータをvisitorパターンを使用して変更したいと考えています。しかし、私は訪問した後に子供の属性を取得するのに問題があります。例えば、私はこれを使用したバイナリ表現で:ビジターパターンでビジターパターンとコンパイラコード生成、どのように子供の属性を取得するには?

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

+0

@ssmir:正しく理解していれば、表示されているコードは訪問者以外の実装のスニペットです。 –

+0

@ssmir:私が上に示したスニペットから、現在の(自己)ノードは左と右のオペランド(Selfの子である)から生成されたコードを必要とします。それは私が実際に "属性"を意味するものです。 – LeleDumbo

答えて

3

訪問の方法があります通常は無効ですので、LHSとRHSから式コードを取得できません。式のコード生成は再帰的であるため、変数に保存されている以前の値を消去する可能性があるため、共有グローバル変数を保持することはオプションではありません。

訪問時に子属性を取得し、必要な属性を保持し、必要なときに属性を保持する必要があります。それは訪問者の内部構造をもう少し複雑にするかもしれませんが、確かに実現可能です。コード生成は間違いなくVisitorパターンの一般的な使用です。

通常、属性を保持する必要はありませんが、中間結果を保持し、他のオブジェクトへの訪問時に他の中間結果に結合する必要があります。私はこれがここに当てはまると思いますが、やりとりはちょっと混乱するほど複雑です。

私はObject Pascalの専門家ではないため、実際のコードを記述するのではなく、どのように扱うか説明します。

この場合、おそらく中間結果を保持するスタックを使用します。

トラバーサルオーダーは、ノードの受け入れメソッド、訪問者の訪問メソッド、または外部イテレータで駆動できます。簡単にするために、私はacceptメソッドでそれを仮定します。

単純なオブジェクトのacceptメソッドでは、単に標準visitor.visit(this)を実行します(ただし、オブジェクト・パスカルではそう言います)。

TASTBooleanConstantのような単純なオブジェクトの訪問メソッドでは、適切なメソッド(この場合はFBuilder.CreateConstant)をオブジェクトから取得し、そのメソッドの結果を訪問者のスタックにプッシュする値で呼び出すことになります。

TASTBinaryExpressionのようなもっと複雑なオブジェクトのacceptメソッドでは、最初に子に対してacceptメソッドを呼び出し、標準visitor.visit(this)を実行して、子が最初に訪問することを確認します。

次に、子供たちが最初に訪問されたので、複雑なオブジェクトの訪問メソッドが呼び出されたときにその結果がスタックにあるはずです。その訪問メソッドでは、適切な結果をスタックからローカル変数にポップし、演算子を基にして適切なFBuilder.CreateXxxメソッドを呼び出し、それらの値をパラメータとして渡して結果をスタックに格納します。

TASTUnaryExpressionオブジェクトの場合、それは似ていますが、acceptメソッドでは1人の子供しか気にする必要がなく、スタックをポップオフしてvisitメソッドで使用する中間結果は1つのみです。

クライアントコードでは、訪問者を作成し、訪問者を引数として渡している最上位ノードのacceptメソッドを呼び出します。すべての再帰が完了した後、スタックには最終結果のみが含まれ、visitorクラスにはgetResultメソッドが用意されていなければなりません。

申し訳ありませんが、コードではっきりしているかもしれませんが、うまくいけば、これに対処する方法がわかります。

リファクタリングがこのような既存のコードにパターンを導入する方法を学ぶ良いリソースは、Joshua Kerievskyの本Refactoring to Patternsです。

+0

うーん...私はそれを得ました。スタックを使うことは解決策のようです(基本的に私の再帰的な解決法には暗黙的なスタックも含まれています。ありがとう。 – LeleDumbo

+0

喜んで助けてください。 Upvoteおよび/またはAnswer welcome welcome ;-) –

+0

コードジェネレータが少なくとも2種類のトラバースアルゴリズムを必要とする場合、たとえば、 "1より大きい場合は1 else" 0のように、ラベルとジャンプを生成する必要がある場合トップダウントラバースで生成することができますが、 "a> 1"の場合は児童のためのアプローチが必要です。 – Puchacz

関連する問題