我目前正在分配一个工作,以创建一个具有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;其他成员建议我使用地图)删除关键字令牌并执行。设置第二个临时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);
                            }

                        }

最佳答案

因此,如果我这样做了,我将使用更多面向对象的方法。

如果为所有都实现相同接口的命令创建一个“类”怎么办?接口-称之为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


这些命令对象必须共享一组变量-使单例可以工作,但是有点反模式,我建议您将其作为哈希映射(上面的variableHash)传递。

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

编辑(评论):

您要做的第一件事是创建一个哈希表并“安装”每个命令。例如:(仍然是伪代码,我想您希望自己进行分配)

map = new HashMap<String, CommandObject>


然后将每个类的实例添加到地图:

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


右侧的类在其中实现了“ CommandObject”接口。

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

execute(String commandLine, HashMap variables);


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

如果此解析器变得更加复杂,那么向“ CommandObject”添加更多功能将是完全有效的,只要您具有reset()方法即可保留状态变量(我的原始建议,但对于您正在做的事情来说似乎过于复杂)

请注意,关键字到命令对象的映射可以用反射代替,但不要在学校布置作业时这样做,反射的复杂性使其不值得您花费时间,并且很可能会因为老师没有将其降级而降级。不明白。我实现了这样的系统,其中每个链接到测试的关键字(允许您链接测试,循环测试,甚至定义和携带这些测试传递和操作的变量,在这种情况下,反射是值得的,因为添加一个新测试不需要更新缓存)

07-24 09:46
查看更多