2012-03-29 39 views
2

我在用C#写一个PLC语言解释器。该PLC语言包含20多种数据类型和25种左右的指令。只要我开始生成代码,我就平衡了两种不同的写指令的方式:用C#编写解释器:实现指令的最佳方式是什么?

1)为了选择数据类型,每种类型的指令都在一个类中表示,其中包含大的switch。示例:

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)每个类表示具有单一数据类型的单个指令。这样避免了大的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())来编写指令。我认为第二选择是更好的,因为避免了大开关,但是这个选择涉及写超过400条指令。

请务必记住,在这种情况下,执行性能比翻译中的性能更重要。

所以,我的确切问题如下:是否有任何其他方式来分解指令和数据类型?我正在寻找写入较少数量的指令而不惩罚性能。

编辑:

这张照片显示我的类型层次:

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

} 

一些类更复杂(结构,数组,。 ..)。

操作push和pop定义如下:

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

最后,操作读取和写入内存。

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 2012-03-29 11:31:41

+0

你完成了这个项目并且是你的代码开源吗? – 2014-08-24 05:15:26

回答

7

如果您可以更改上下文的实现以支持泛型(例如,Pop<int>而不是PopINT()),则可以使用委托来简化实现。

增加:

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

我真的很赞赏你的详细解释。感谢您快速回答。我正在等待回复你,因为我从未使用过代表,我收到了一些关于他们的信息。 – 2012-03-30 12:10:34

+0

关于你的答案,我不能使用通用数据类型。我写了我自己的类型层次结构和我自己的堆栈表示。那么,有没有什么办法可以将您以前的代码调整为用户创建的数据类型?再次感谢你。 – 2012-03-30 12:16:38

+0

你能发表一个你的类型层次的例子以及Pop和Push的实现吗? – 2012-03-30 15:14:20

2

你可以使用继承吗?我会看到有关数据类型的继承的巧妙组合,然后是将执行委托给适当对象的策略模式。

但是,我们真的需要看到一个类图来帮助你。

只记得编程到一个接口,而不是一个类型,而且组合比继承更强大。我希望这可以帮助你。

+0

谢谢你的回答。我有我自己的类型层次结构。所以,我不得不在内部指令类中使用组合(交换'OwnType类型'的字符串类型),然后在我的数据类型类中写入“具体策略方法”,最后编写方法来选择指令中的策略。对? – 2012-03-30 13:00:20

相关问题