[英]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.