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)
我希望这些信息有所帮助.谢谢.
如果您可以更改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)