如何使用JFlex、JavaCUP(詳細代碼模版)
編譯原理的實驗要求我們用JFlex和JavaCUP來對語言進行分析處理,JavaCUP有一個User's Manual教你怎樣做,上面還有一個簡單的計算器作為例子,但一試之下,卻發現那個例子有不少錯誤,結果改了我n久才完成~當然馬上就決定寫一篇博客告訴大家怎樣做才是正確的,以免像我那樣白走許多彎路。我也不知是不是我找的那份不好,如果你有更好的manual,記得告訴我。
撰寫本文的目的:
給出使用JFlex、JavaCUP來為一個計算器建立分析器的示例的完整代碼,使讀者能充分領會JavaCUP的使用方法。雖然本文僅僅給出了計算器的代碼,但只要你會寫你的語言的翻譯模式,則只要照抄這個模版,并改改相應動作就可以了。
引用到的資料:
- 《CUP User's Manual》,作者:Scott E. Hudson地址為李老師那里下載下來的JavaCUP-11a.rar\CUP-develop.tar.gz\develop\manual.html,有詳細的英文說明和示例代碼,但有很多錯。本文中簡稱為《手冊》。
- 《使用CUP進行語法分析》,摘自Apollo的博客,貎似是轉載的(竟然不注明[轉]和真實出處?!,BS之~),作者待考。有詳盡的解釋,但缺乏示例代碼。本文中簡稱為《語法分析》。
詳細步驟:
1、準備工作。
JavaCUP和JFlex一樣,壓縮包里邊有許多的文件夾和文件,我不知道正統的做法是否要求使用javaCUP也像Jflex一樣要設置一堆path啊、classpath啊、jflex_home 之類的環境變量,但如果你像我一樣只打算用它幾次,你只要執行下述的兩個簡單步驟就可以了,它并不需要你設置任何的環境變量(以下假設你的工作目錄是 work\):
1) 將JavaCUP壓縮包里的java-cup-11a.jar解壓到work\下。
2) 將JavaCUP壓縮包里的CUP-develop.tar.gz\develop\src下的java_cup文件夾整個解壓到work\下。
現在你可以使用JavaCUP了。
2、為這個計算器寫一個詞法分析器。或者用JFlex生成一個詞法分析器
// Simple Example Scanner Class // scanner.java import java_cup.runtime.; import java.io.; //import sym;public class scanner implements java_cup.runtime.Scanner { / single lookahead character / protected static int next_char; // since cup v11 we use SymbolFactories rather than Symbols private SymbolFactory sf = new DefaultSymbolFactory(); private static FileReader fileReader; public scanner(FileReader fr){ this.fileReader=fr; } / advance input by one character / protected static void advance() throws java.io.IOException { next_char = fileReader.read(); }
/ initialize the scanner / public static void init() throws java.io.IOException { advance(); }
/ recognize and return the next complete token / public Symbol next_token() throws java.io.IOException { for (;;) switch (next_char) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': / parse a decimal integer / int i_val = 0; do { i_val = i_val * 10 + (next_char - '0'); advance(); } while (next_char >= '0' && next_char <= '9'); return sf.newSymbol("NUMBER",sym.NUMBER, new Integer(i_val));
case ';': advance(); return sf.newSymbol("SEMI",sym.SEMI); case '+': advance(); return sf.newSymbol("PLUS",sym.PLUS); case '-': advance(); return sf.newSymbol("MINUS",sym.MINUS); case '*': advance(); return sf.newSymbol("TIMES",sym.TIMES); case '/': advance(); return sf.newSymbol("DIVIDE",sym.DIVIDE); case '%': advance(); return sf.newSymbol("MOD",sym.MOD); case '(': advance(); return sf.newSymbol("LPAREN",sym.LPAREN); case ')': advance(); return sf.newSymbol("RPAREN",sym.RPAREN); case -1: return sf.newSymbol("EOF",sym.EOF); default: /* in this simple scanner we just ignore everything else */ advance(); break; } }
};</pre>