簡體   English   中英

使用多個arraylist或帶有stringtokenizer的數組在Java中編寫解釋器

[英]writing an interpreter in java using multiple arraylists or arrays with stringtokenizer

我目前正在分配一個工作,以創建一個具有8個關鍵字(不區分大小寫)和4個算術運算符的基本解釋器。 用這種語言編寫的程序看起來像這樣(實際上類似於BASIC的語法):

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

因此,無論如何,我目前正在嘗試標記要解析的文本行。 我已經將所有文本行解析為ArrayList並標記了字符串。 現在我的當前問題是,StringTokenizer預先對所有字符串進行標記化(我正在使用空格作為定界符),當我需要它來查找我的關鍵字時,該關鍵字始終是代碼行開頭的第一個單詞,並且某些問題使這種情況不受歡迎; 我認為使用String.split()也不會有太大幫助。

我打算這樣做的方法是讓解釋器找到我的第一個標記,然后通過HashMap從那里到達合適的類(請參見我之前關於在我的解釋器中使用switch語句的問題: Switch或if語句用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);
                            }

                        }

因此,如果我這樣做的話,我將使用更多的面向對象方法。

如果為所有都實現相同接口的命令創建一個“類”怎么辦? 接口-稱之為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()方法即可保留狀態變量(我的原始建議是,但對於您正在做的事情似乎過於復雜)

請注意,關鍵字到命令對象的映射可以用反射代替,但不要在學校布置作業時這樣做,反射的復雜性使其不值得您花費時間,並且很可能會因為老師沒有評級而被降級。不明白。 我實現了這樣一個系統,其中每個鏈接到測試的關鍵字(允許您鏈接測試,循環測試,甚至定義和攜帶那些測試傳遞和操作的變量,在這種情況下,反射是值得的,因為添加一個新測試不需要更新緩存)

聽起來正確的設計似乎是推遲解析這些參數,直到以后。 某些命令(例如“ STRING”或“ PRINT”)可能與空格無關,其中STRING S =“ HELLO WORLD”在功能上與STRING S =“ HELLOWORLD”確實沒有區別。 您不希望預先對這樣的東西進行“過度設計”-最好只是做一些最簡單的事情,然后編寫一個或兩個命令類,然后弄清楚這些命令類的共同點。

如果您以后發現所有(或大多數)命令都希望以某種方式將這些參數解析為列表,則可以將該“列表解析代碼”重構為靜態實用程序方法(或者可能是非靜態實用程序方法)。如果您正在使用繼承,則為父Command類本身。)如果您在進行過程中很聰明地創建一套自動化測試,則風險會大大降低,但是這種任務可能不在您的分配范圍之內。

一種不錯的方法是使用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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM