2009-10-07 44 views
2

我建立了装配自己的自定义的味道简单的组装编译器,我有这样的事情作为实际的代码,不会编译:更好的方法来设计这个循环?

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

感觉就像我打破约半这里有十几个设计规则(重复使用变量和期待合格的输入是唯一可以发现的,但我敢打赌还有更多),但不知道如何更好地构建它。想法?

回答

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

那么你是否也提供了可以解析字符串参数的CommandFactory函数的重载?我喜欢.... – RCIX 2009-10-07 10:02:40

4

你可能会考虑把一些东西扔在一个标记器中,它将你的程序作为一串标记返回(你的分离器是这样做的)。然后将其传递给解析器以创建解析树和符号表。为什么?因为如果不知道你的装配风格,在某些时候你会想要跳转到一个标签(子程序),我会假设。或者你会希望你的跳转指令回到开始的循环等...

如果你有你的分析树和符号表设置,你将有所有的地址在那里,方便插入到你的输出文件。我写了一个编译器已经很长时间了,所以请原谅我小示例中的任何偏差...

+0

其实我跟以前只是一个代码聪明一点,产生一个“跳图”来了,这样我就可以发出跳转地址到我的字节码,而不是当前的标签。 – RCIX 2009-10-07 09:09:54

+0

虽然如果你可以指向我的一些图书馆/资源的这些东西(tokenizer,解析器),这将是很好的... – RCIX 2009-10-07 09:14:46

+0

对不起,不太熟悉.NET。既然你在这里编写一个非常原始的程序,你没有理由不能自己创建。类似这样的: 将循环拉出到“Tokenizer”类中 将所有内部代码从您的case语句中拉出,然后返回一个常量作为“标记”,当然,请明确定义您的常量。 解析器: 一次带入1个令牌。跟踪你的各种状态(开始循环,在循环中,在子程序中启动子程序),你应该有一个简单的语法,即使是你的汇编语言,所以只需在分析器中写出每个案例和组。 – Zak 2009-10-07 09:24:12

0

将指令信息移动到类/属性包中。创建一些实用的转换方法,让您的生活更轻松。然后使用字符串 - >委托字典将指令名称映射到创建命令。这只是一个开始,你可以重构这个简单得多。

东西沿着这些路线,也许是:

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

    // ... 
} 
相关问题