用C#编写解释器:实现指令的最佳方法是什么?

Jos*_*ron 2 c# interpreter plc instruction-set

我正在使用C#编写PLC语言解释器.该PLC语言包含20多种数据类型和25种左右的指令.一旦我开始生成代码,我就会平衡两种不同的方式来编写指令:

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

}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我正在使用COMMAND设计模式(Exec())来编写指令.我认为第二种选择更好,因为避免了大转换,但这种选择涉及写入超过400条指令.

始终牢记在这种情况下,执行性能比翻译性能更重要.

所以,我的确切问题如下:是否有其他方法来分解指令和数据类型?我正在寻找写入较少量的指令而不会影响性能.

编辑:

此图显示了我的类型层次结构:

类型层次结构

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

}
Run Code Online (Sandbox Code Playgroud)

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

Operations 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;
}
Run Code Online (Sandbox Code Playgroud)

最后,在内存中进行读写操作.

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));
}
Run Code Online (Sandbox Code Playgroud)

我希望这些信息有所帮助.谢谢.

Pau*_*ell 7

如果您可以更改Context的实现以支持泛型类型(例如,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));
Run Code Online (Sandbox Code Playgroud)

减法:

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));
Run Code Online (Sandbox Code Playgroud)

师:

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));
Run Code Online (Sandbox Code Playgroud)

和类型之间的转换:

var addIntAndDouble = new MathInstruction<int, double, double>((a, b) => a + b));
Run Code Online (Sandbox Code Playgroud)

它会像这样实现:

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>
Run Code Online (Sandbox Code Playgroud)

我想象,你的情况下简单地拥有Stack<object>PopINT,PopBOOL等刚刚流行的说法和演员.在这种情况下,您可以使用:

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

public void Push<T>(T item)
{
    stack.Push(item);
} 
Run Code Online (Sandbox Code Playgroud)

请注意,这也可以处理您的逻辑运算符 - 例如:

var logicalAnd = new MathInstruction<bool>((a, b) => a && b);
var logicalOr = new MathInstruction<bool>((a, b) => a || b);
Run Code Online (Sandbox Code Playgroud)