2012-01-10 36 views
0

我目前正在进行一项任务,以创建一个包含8个关键字(不区分大小写)和4个算术运算符的基本解释器。在这门语言的程序会是这个样子(类似于BASIC的语法真的):用java编写一个解释器,使用多重阵列表或字符串排序器

# (signals start of a comment line) 
LET 
INTEGER 
STRING 
PRINT 
END 

所以无论如何,我目前正试图来标记要分析的文本行。我已经将所有文本行解析为一个ArrayList并标记了字符串。我现在的问题是,StringTokenizer预先标记所有的字符串(我使用空格作为分隔符),当我需要的是它找到我的关键字,它始终是代码行开头的第一个字,并且某些问题使得这不合要求;我不认为使用String.split()也会有很大的帮助。

我打算这样做的方法是让解释器找到我的第一个标记,然后从那里通过HashMap到相应的类(参考我以前关于在这里为我的解释器使用switch语句的问题: Switch or if statements in writing an interpreter in java;其他成员建议我使用Map)删除关键字标记并执行。设置第二个临时ArrayList或数组来保存变量是个好主意吗?我不想或不需要它太复杂。

在此先感谢您的建议。

public static void main (String[]args) 
    { 
     try 
     { 
      ArrayList<String> demo= new ArrayList <String>(); 
      FileReader fr= new FileReader("hi.tpl"); 
      BufferedReader reader= new BufferedReader(fr); 
      String line; 
      while ((line=reader.readLine()) !=null)//read file line by line 
       { 
        //Add to ArrayList 
        demo.add(line); 
       } 

      reader.close(); 

      boolean checkEnd= demo.contains("END");//check if arraylist contains END statement 
        if(line=null && checkEnd== false) 
         { 
          System.out.println(" Unexpected end of file: no END statement"); 
          System.exit(0); 
         } 

      ListIterator<String>arrayListIt=demo.listIterator(); 
      while (arrayListIt.hasNext()) 
      for (String file: demo)// begin interpreting the program file here 
       {    
        StringTokenizer st=new StringTokenizer(file); 
        while(st.hasMoreTokens()) 
         { 

          int firstWord=file.indexOf(); 
          String command = file; 
          if (firstSpace > 0) 
          { 
           command= file.substring(0, firstSpace); 
          } 
          TokenHandler tokens= tokens.get(command.toUpperCase()); 
          if(tokens != null) 
          { 
           tokens.execute(file); 
          } 

         } 
+0

目前尚不清楚你的问题是什么。 – 2012-01-10 18:31:29

+0

@JBNizet我在问我是否应该考虑解析方法中的参数,或者解析所有的参数,并将其放入一个单独的ArrayList中,这个ArrayList包含了一个拥有脚本行的ArrayList。 – Luinithil 2012-01-10 18:49:39

回答

1

所以,如果我这样做,我会使用更多的OO方法。

如果您为所有实现相同接口的每个命令创建了一个“类”,该怎么办?接口 - 我们称之为CommandObject将有一个execute()方法。

然后,您可以使用预先加载的映射,将“Let”这样的命令映射到Let类的实例。

现在你的主循环变得像这样(伪):

for(line:lineList) 
    CommandObject commandObject=map.get(line.split()[0]) // do this more clearly 
    commandObject.execute(variableHash, line) // Parse and execute the line 

这些命令对象必须共享一组变量 - 制作一个单独的工作,但有些的反模式,我建议你将它们作为hashmap(上面的变量hash)来代替。

这种方法的好处是添加一个新的“命令”非常简单,而且大多是独立的。

编辑(再评论):

你会做的第一件事是创建一个HashMap和“安装”,您的每一个命令。例如:(仍然psudeo码,我想你更愿意做自己分配)

map = new HashMap<String, CommandObject> 

那么每个类的实例添加到地图:

map.put("LET", new LetCommand()); 
map.put("INTEGER", new Integercommand()); 

在右手动类实现“CommandObject”接口。

请注意,由于每个CommandObject都是一个实例,每次找到该关键字时都应该重新使用该实例,您应该不会存储任何状态(没有任何实例变量),这意味着您的CommandObject只需要一个方法是这样的:

execute(String commandLine, HashMap variables); 

这可能是最简单的方法(我编辑的文本从上面我原来的建议,反映了这一点)。

如果这个解析器变得更加复杂,那么为“CommandObject”添加更多功能将是完全有效的,只要您有reset()方法(我的原始建议,但似乎过于复杂你在做什么)

请注意,命令对象的关键字映射可以用反射代替,但不要试图为学校作业做这件事,反射的复杂性使得它不值得你花时间,这很可能是你因为老师不理解它会被降级。我实现了一个像这样的系统,每个关键字链接到一个测试(允许你链接测试,循环测试,甚至定义和携带传入并由这些测试操作的变量 - 在这种情况下,反射是值得的,因为添加新的测试不需要更新缓存)

+0

这是我刚刚尝试拍摄的内容:我想要添加新关键字的操作非常简单。虽然,你能否解释更多有关将命令作为散列表传递的信息?谢谢! – Luinithil 2012-01-10 19:46:37

+0

非常感谢@Bill K,这真棒。我已经开始使用HashMap了。 – Luinithil 2012-01-10 21:55:36

0

听起来像是正确的设计将延迟解析这些参数,直到后来。像“STRING”或“PRINT”这样的某些命令可能是空白不可知的,其中STRING S =“HELLO WORLD”实际上与STRING S =“HELLOWORLD”在功能上不同。你不想“过度设计”这样的事情 - 最好是做最简单的工作,编写一个或两个命令类,然后弄清楚这些命令类的共同之处。

如果您以后发现所有(或大部分)命令都希望以某种方式将这些参数解析到列表中,则可以将该列表解析代码重构为静态实用程序方法(或者可能是非易失性实用程序方法)如果你正在使用继承,那么父类Command类本身就有一个静态实用方法。)如果你随时随地创建一套自动化测试,那么这样做的风险就小得多,但这种任务可能会超出范围你的任务。

0

一个不错的方法是使用enum。我不会避免使用split,但限制为2项。

enum Command { 
    LET { 
     @Override 
     public void execute(Context context, String args) { 
     } 
    }, 
    INTEGER { ... }, 
    STRING { ... }, 
    PRINT { ... }, 
    END { ... }; 

    public abstract void execute(Context context, String args); 
} 

private void executeLine(String line) { 
    String[] commandAndArgs = line.split("\\s+", 2); 
    String command = ""; 
    String args = ""; 
    if (commandAndArgs.length > 0) 
     command = commandArgs[0].toUpperCase(); 
    if (commandAndArgs.length > 1) 
     args = commandArgs[1]; 
    Command cmd = Command.valueOf(command); 
    Context context = ...; 
    cmd.execute(context, args); 
} 
相关问题