2012-03-29 15 views
2

私はC言語を使ってPLC言語インタープリタを書いています。そのPLC言語には、20種類以上のデータ型と25種類の命令が含まれています。C#でインタプリタを書く:命令を実装する最良の方法は?

1)あらゆる種類の命令は、データ型を選択するために大きいswitchを含む1つのクラスで表されます。例:

public class ADD : Instruction 
{ 
    private string type; 

    public ADD(string type) 
    { 
     this.type = type; 
    } 

    public bool Exec(Context c) 
    { 
     switch (type) 
     { 
      case "INT": 
       short valor2 = c.PopINT(); 
       short valor = c.PopINT(); 
       short result = (short)(valor + valor2); 
       c.PushINT(result); 
       break; 
      case "DINT": 
       int valor4 = c.PopDINT(); 
       int valor3 = c.PopDINT(); 
       int result2 = (int)(valor4 + valor3); 
       c.PushDINT(result2); 
       break; 
      case "BOOL": 
       // Implement BOOL 
       break; 
      // Implement other types... 
      default: 
       break; 
     } 

     c.IP++; 
     return false; ; 
    } 

} 

2)各クラスは、1つのデータ型を持つ単一の命令を表します。この方法は大きなswitchを避けます。例:

public class ADDi : Instruction 
{ 
    public bool Exec(Context c) 
    { 
     short valor = c.PopINT(); 
     short valor2 = c.PopINT(); 
     short result = (short)(valor + valor2); 
     c.PushINT(result); 
     c.IP++; 
     return false; 
    } 
} 

私は命令を書くためにCOMMAND desingパターン(Exec())を使用しています。 2番目の選択肢は、大きなスイッチを避けるために優れていると思いますが、その選択肢には400以上の命令を書き込む必要があります。

この場合、実行パフォーマンスは翻訳のパフォーマンスよりも重要であることに常に注意してください。

私の正確な質問は次のとおりです。命令やデータ型を分解する他の方法はありますか?私はパフォーマンスを犠牲にすることなく、より少ない量の指示書を書くことを望んでいます。

EDIT:

この絵は私のタイプの階層を示しています

Type hierarchy

これは、INTクラスの実装です:

public class INT : ANY_INT 
{ 

    public override string DefaultInitValue() 
    { 
     return "0"; 
    } 

    public override int GetBytes() 
    { 
     return 2; 
    } 

    public override string GetLastType() 
    { 
     return this.ToString(); 
    } 

    public override string ToString() 
    { 
     return "INT"; 
    } 

} 

は、いくつかのクラスは、より複雑な(構造体、配列されています、。 ..)。

public void PushINT(short value) 
{ 
    //SP -> Stack Pointer 
    resMem.WriteINT(SP, value); 
    SP += 2; 
} 

public short PopINT() 
{ 
    SP -= 2; 
    short value = resMem.ReadINT(SP); 
    return value; 
} 

そして、最後に、操作を読み取ってメモリに書き込むために:

操作は次のように定義されているpushとpop。

public void WriteINT(int index, short entero) 
{ 
    SetCapacity(index + 2); // Memory grows up dinamically 
    memory[index] = (sbyte)((ushort)entero >> 8 & 0x00FF); 
    memory[index + 1] = (sbyte)((ushort)entero >> 0 & 0x00FF); 
} 

public short ReadINT(int index) 
{    
    return (short)(((short)(memory[index]) << 8 & 0xFF00) | 
     ((short)(memory[index + 1]) & 0x00FF)); 
} 

この情報が役立ちますようお願いいたします。ありがとうございました。

+0

私は言語で非終端記号ごとに一つのクラスで行くだろう、解決策に向かって私の道を分け合って征服してください。 – Alex

+0

このプロジェクトを終了し、あなたのコードオープンソースですか? –

答えて

7

汎用タイプ(たとえばPopINT()ではなくPop<int>)をサポートするようにContextの実装を変更できる場合は、代理人を使用して実装を簡単にすることができます。

追加:

var addInt = new MathInstruction<int>((a, b) => a + b)); 
var addDouble = new MathInstruction<double>((a, b) => a + b)); 
var addDecimal = new MathInstruction<decimal>((a, b) => a + b)); 

減算:

var subtractInt = new MathInstruction<int>((a, b) => a - b)); 
var subtractDouble = new MathInstruction<double>((a, b) => a - b)); 
var subtractDecimal = new MathInstruction<decimal>((a, b) => a - b)); 

部門:

var divideIntAsDouble = new MathInstruction<int, double>((a, b) => a/b)); 
var divideDouble = new MathInstruction<double>((a, b) => a/b)); 
var divideDecimal = new MathInstruction<decimal>((a, b) => a/b)); 

と型の間の変換:

var addIntAndDouble = new MathInstruction<int, double, double>((a, b) => a + b)); 

それはこのように実装されるだろう:

class MathInstruction<TA, TB, TResult> : Instruction 
{ 
    private Func<TA, TB, TResult> callback; 

    public MathInstruction(Func<TA, TB, TResult> callback) 
    { 
     this.callback = callback; 
    } 

    public bool Exec(Context c) 
    { 
     var a = c.Pop<TA>(); 
     var b = c.Pop<TB>(); 
     var result = callback(a, b); 
     c.Push<TResult>(result); 
     return false; 
    } 
} 

// Convenience 
class MathInstruction<T, TResult> : MathInstruction<T, T, TResult> 
class MathInstruction<T> : MathInstruction<T, T, T> 

私はあなたのコンテキストは単純に引数とキャストをポップStack<object>PopINTPopBOOLなどを持っていることを想像しています。その場合、あなたはおそらく使用することができます。

public T Pop<T>() 
{ 
    var o = stack.Pop(); 
    return Convert.ChangeType(o, typeof(T)); 
} 

public void Push<T>(T item) 
{ 
    stack.Push(item); 
} 

注これはまたあなたの論理演算子を扱うことができる - 例えば:

var logicalAnd = new MathInstruction<bool>((a, b) => a && b); 
var logicalOr = new MathInstruction<bool>((a, b) => a || b); 
+0

私はあなたの詳細な説明を本当に感謝します。すぐにお返事ありがとうございます。私は代議員を使ったことがなかったので、あなたに返信するのを待っていました。 –

+0

あなたの答えに関連して、私は一般的なデータ型を使用することはできません。私は自分自身の型階層と私自身のスタック表現を書いた。だから、以前のコードをユーザーが作成したデータ型に適合させる方法はありますか?ありがとうございました。 –

+0

タイプ階層の例とPopおよびPushの実装を投稿できますか? –

2

継承を使用できますか?私は、データ型に関する継承の賢明な組み合わせ、そして実行を適切なオブジェクトに委譲するための戦略パターンを見ていきます。

しかし、私たちは実際にあなたを助けるためにクラス図を見る必要があります。

インターフェイスではなくタイプにプログラムすることを覚えておいてください。また、コンポジションは継承より強力です。これがあなたを助けてくれることを願っています。

+0

ありがとうございました。私は自分のタイプ階層を持っています。だから、命令クラス内でコンポジションを使う必要があります( 'OwnType type'の 'string type'を入れ替えて)、データ型クラスの中に「具体的な戦略メソッド」を書いて、最後にInstructions内のストラテジを選択するメソッドを書かなければなりません。右? –

関連する問題