我正在为我自己的自定义程序集构建一个简单的汇编编译器,我有这样的东西作为编译的实际代码:
foreach (KeyValuePair<short, string> kvp in newCommandSet)
{
string fullCommandString = kvp.Value;
string instruction = fullCommandString.Split(new char[] { Convert.ToChar(" ") })[0];
string[] parameters = fullCommandString.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries);
// this is to remove the instruction part from the first parameter. Gonna have to ensure a well formed command at some point...
parameters[0] = parameters[0].Substring(instruction.Length + 1);
Command currentCommand = new Command();
switch (instruction)
{
case "load":
short value = Convert.ToInt16(instruction[0]);
byte register = Convert.ToByte(parameters[1]);
currentCommand = CommandFactory.CreateLoadCommand(register, value);
break;
case "input":
byte channel = Convert.ToByte(parameters[0]);
register = Convert.ToByte(parameters[1]);
currentCommand = CommandFactory.CreateInputCommand(register, channel);
break;
case "output":
channel = Convert.ToByte(parameters[0]);
register = Convert.ToByte(parameters[1]);
currentCommand = CommandFactory.CreateInputCommand(register, channel);
break;
...
}
...
}
感觉就像我在这里打破了大约6个设计规则(重复使用变量并期待良好的输入是我能发现的唯一但我敢打赌的更多),但不知道如何更好地构建它。想法?
答案 0 :(得分:5)
考虑将用于解释参数的逻辑推入CommandFactory。 switch语句如下所示:
switch(instruction)
{
case "load":
currentCommand = CommandFactory.CreateLoadCommand(parameters);
break;
case "input":
currentCommand = CommandFactory.CreateInputCommand(parameters);
break;
case "output":
currentCommand = CommandFactory.CreateOutputCommand(parameters);
break;
}
答案 1 :(得分:4)
你可能会考虑抛出一些像tokenizer一样的东西,它将你的程序作为一串标记返回(你的分裂器就是这样)。然后将其传递给解析器以创建解析树和符号表。为什么?因为不知道你的装配风味,在某些时候你会想要跳到我想要的标签(子程序)。或者你会希望你的跳转指令回到循环的开始等等......
如果您设置了解析树和符号表,那么您将拥有所有地址,以便轻松插入输出文件。自编写编译器以来已经很长时间了,所以请原谅我的小例子中的任何偏差......
答案 2 :(得分:0)
将指令信息移至类/属性包。为转换创建一些实用方法,让您的生活更轻松。然后使用字符串 - &gt;委托字典将指令名称映射到命令的创建。这只是一个开始,你可以重构这个更简单。
这些方面的一些东西,也许是:
public class InstructionData
{
public InstructionData(string fullCommandString)
{
string[] commandParts = fullCommandString.Split(new char[] {' ', ','}, StringSplitOptions.RemoveEmptyEntries);
this.InstructionName = commandParts[0];
this.parameters = commandParts.Skip(1).ToArray();
}
public string InstructionName { get; private set; }
public short InstructionInt { get { return Convert.ToInt16(InstructionName[0]); } }
private string[] parameters;
public string GetParameter(int paramNum)
{
return parameters[paramNum];
}
public byte GetParameterAsByte(int paramNum)
{
return Convert.ToByte(parameters[paramNum]);
}
}
public class SomeClass
{
// ...
private Dictionary<string, Func<InstructionData, Command>> commandTranslator = new Dictionary<string, Func<InstructionData, Command>>();
private static void InitializeCommandTranslator()
{
commandTranslator["load"] = ins => CommandFactory.CreateLoadCommand(ins);
commandTranslator["input"] = ins => CommandFactory.CreateInputCommand(ins);
commandTranslator["output"] = ins => CommandFactory.CreateOutputCommand(ins);
}
public void SomeMethod()
{
// ...
foreach (KeyValuePair<short, string> kvp in newCommandSet)
{
InstructionData currentInstruction = new InstructionData(kvp.Value);
if(commandTranslator.ContainsKey(currentInstruction.InstructionName))
{
currentCommand = commandTranslator[currentInstruction.InstructionName](currentInstruction);
}
}
}
// ...
}