我目前正在做一项作业,创建一个带有 8 个关键字(不区分大小写)和 4 个算术运算符的基本解释器。这种语言的程序看起来像这样(实际上类似于 BASIC 的语法):
# (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);
}
}
最佳答案
因此,如果我这样做,我会使用更多面向对象的方法。
如果您为所有实现相同接口(interface)的每个命令创建一个“类”怎么办?该接口(interface)(我们称之为 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 (上面的variableHash)传递。
这种方法的好处是添加新的“命令”非常简单并且大部分是独立的。
编辑(回复评论):
您要做的第一件事是创建一个 HashMap 并“安装”每个命令。例如:(仍然是伪代码,我假设您更喜欢自己完成作业)
map = new HashMap<String, CommandObject>
然后将每个类的实例添加到 map 中:
map.put("LET", new LetCommand());
map.put("INTEGER", new Integercommand());
其中右侧类实现“CommandObject”接口(interface)。
请注意,由于每个 CommandObject 都是一个实例,每次找到该关键字时都会重复使用,因此您可能不应该存储任何状态(不要有任何实例变量),这意味着您的 CommandObject 将只需要一个方法,类似:
execute(String commandLine, HashMap variables);
这可能是最简单的方法(我根据最初的建议编辑了上面的文本以反射(reflect)这一点)。
如果这个解析器变得更复杂,那么向“CommandObject”添加更多功能是完全有效的,只要您有一个 Reset() 方法,您就可以保留状态变量(我最初的建议,但对于什么来说似乎过于复杂)你正在做的事)
请注意,关键字到命令对象的映射可以用反射代替,但不要尝试在学校作业中这样做,反射的复杂性使其不值得您花时间,并且您可能会被降级,因为老师不明白。我实现了一个这样的系统,其中每个关键字都链接到一个测试(允许您链接测试、循环测试,甚至定义和携带传递到这些测试并由这些测试操作的变量——在这种情况下,反射是值得的,因为添加一个新测试不需要更新缓存)
关于java - 使用多个数组列表或带有 stringtokenizer 的数组在 java 中编写解释器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8807778/