一.實驗目的
1、 學會針對DFA轉換圖實現相應的高級語言源程序。
2、 深刻領會狀態轉換圖的含義,逐步理解有限自動機。
3、 掌握手工生成詞法分析器的方法,了解詞法分析器的內部工作原理。
二.實驗內容
Java語言的編譯程序的詞法分析部分實現。
從左到右掃描每行該語言源程序的符號,拼成單詞,換成統一的內部表示送給語法分析程序。
具體的要求如下:
(1) 區分保留字、運算符、常數、界符和標識符
(2) 常數包含整型(正/負)、浮點型(正/負)、字符串和字符。
(3) 空白符是空格、回車符、制表符。
(4) 代碼是自由格式。
(5) 注釋包含單行注釋和多行注釋,並且不允許嵌套
程序的記號定義:
表2-1 Java語言記號
保留字 |
運算符 |
常數 |
界符 |
標識符 |
||
abstract |
assert |
+ |
整數 |
字符串 |
, |
以字母開頭, 由字母數字 和下划線組成 |
case |
catch |
- |
浮點數 |
字符 |
( |
|
continue |
default |
* |
{ |
|||
enum |
extends |
= |
[ |
|||
for |
goto |
< |
; |
|||
instanceof |
int |
> |
) |
|||
new |
package |
+= |
} |
|||
return |
strictfp |
-= |
] |
|||
switch |
synchronized |
*= |
||||
transient |
try |
== |
||||
boolean |
break |
>= |
||||
char |
class |
<= |
||||
do |
double |
? |
||||
final |
finally |
. |
||||
if |
implements |
: |
||||
interface |
long |
!= |
||||
private |
protected |
|||||
short |
static |
|||||
this |
throw |
|||||
void |
volatile |
|||||
byte |
native |
|||||
constant |
public |
|||||
else |
super |
|||||
float |
throws |
三.實驗要求
編譯器實現的功能:
(1) 按語法規則將字符識別、分類,並轉換成二元式形式打印
(2) 刪除注釋行(單行、多行)
(3) 刪除空白符(空格、回車符、制表符)
(4) 列表打印源程序,按照源程序的行打印,在每行的前面加上行號,並且打印出每行包含的記號的二元形式
(5) 能識別詞法錯誤並定位
Java詞法分析進行具體的要求:
(1) 詞法分析器每分析出一個完整的詞法成分,就將該詞法成分的行號和二元式添加到結果字符串info中:。
- 在所有代碼分析完畢后,將info中的內容寫入result.txt中;
- 若分析的過程中出現錯誤,則將錯誤信息和錯誤定位寫入result.txt。
(2) 單詞符號分種如下:
- 運算符:運算符分為由單個字符和兩個字符組成的運算符。所以對於有可能組成兩個運算符的字符,在分析完第一個字符后還要繼續分析第二個字符才能分析出完整的運算符。
- 常量:常量又分為數字、字符串、字符。
a) 數字:分為整數,浮點數,且都有正/負兩種情況。其中,“正”用數字開頭的“+”標識(可省略),“負”用數字開頭的“-”標識;
b) 字符串:字符串中允許有轉義字符;
c) 字符:合法的字符有:1.單個字符;2.“\”加“b”、“n”、“r”、“t”、“\”;3.“\”加1到3位數字。
- 標識符
- 保留字:標示符和保留字的詞法構成相同。識別出字符串后,再根據保留字數組判斷該字符串是否為保留字;
- 界符
(3) 詞法分析器的具體功能實現是用函數analyze()。每次都根據當前的狀態和當前的字符來判斷下一步操作,下一步操作有:
- 轉換系統狀態;
- 讀取下一個字符;
- 將當前字符存入字符串中,待該完成詞法成分都在字符串中時再生成對應的二元式,並清空字符串。
根據具體情況,下一步操作可同時執行以上三個動作或只執行其中一個或兩個。
DFA:
四.代碼
package newp2; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class Lexical { private static final String reserveWords[] = { "abstract", "boolean", "break", "byte", "case", "catch", "char", "class", "continue", "default", "do", "double", "else", "extends", "final", "finally", "float", "for", "if", "implements", "import", "instanceof", "int", "interface", "long", "native", "new", "package", "private", "protected", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "try", "void", "volatile", "while", "strictfp", "enum", "goto", "const", "assert" }; // 50 private FileReader fd; private int state; private char ch; private String info; // 結果串 private String temp; // 臨時存儲 int lineNum; public Lexical() { info = ""; temp = ""; lineNum = 1; getChar(); analyze(); write(info); } private void analyze() { if (ch == (char) -1 && temp.equals("")) return; // 已經讀取到最后一個字符,且沒有待處理字符 if (ch == '\n') lineNum++; switch (state) { case 0: temp = ""; if (ch == ' ' || ch == '\r' || ch == '\t' || ch == '\n') { toNextCharAndChangeState(0); } else if (ch == '/') { toNextCharAndStoreTempAndChangeState(1); } else if (isDigital(ch)) { toNextCharAndStoreTempAndChangeState(5); } else if (isOperator1(ch)) { toNextCharAndStoreTempAndChangeState(8); } else if (ch == '!') { toNextCharAndStoreTempAndChangeState(9); } else if (isOperator2(ch)) { writeInfo((ch + ""), "運算符"); getChar(); } else if (isBoundary(ch)) { writeInfo((ch + ""), "界符"); getChar(); } else if (ch == '"') { toNextCharAndStoreTempAndChangeState(10); } else if (isLetter(ch)) { toNextCharAndStoreTempAndChangeState(11); } else if (ch == '\'') { toNextCharAndStoreTempAndChangeState(14); } else if (ch == '-' || ch == '+') { toNextCharAndStoreTempAndChangeState(16); } else if (ch == '|') { toNextCharAndStoreTempAndChangeState(17); } else if (ch == '&') { toNextCharAndStoreTempAndChangeState(18); } else if (ch == (char) -1) { // 程序應該結束 } else { // 非法字符 error(1); return; } break; case 1: if (ch == '/') { toNextCharAndChangeState(2); } else if (ch == '*') { toNextCharAndChangeState(3); } else { state = 8; } break; case 2: // 處理注釋 if (ch == '\n') { state = 0; getChar(); } else { getChar(); } break; case 3: // 處理注釋 if (ch == '*') { toNextCharAndChangeState(4); } else { getChar(); } break; case 4: // 處理注釋 if (ch == '/') { toNextCharAndChangeState(0); } else { toNextCharAndChangeState(3); } break; case 5: if (isDigital(ch)) { temp += ch; getChar(); } else { state = 6; } break; case 6: if (ch == '.') { toNextCharAndStoreTempAndChangeState(7); } else { writeInfo(temp, "常數"); } break; case 7: if (isDigital(ch)) { toNextCharAndStoreTempAndChangeState(13); } else { error(4); return; } break; case 8: if (ch == '=') { temp += ch; writeInfo(temp, "運算符"); getChar(); } else { writeInfo(temp, "運算符"); } break; case 9: if (ch == '=') { temp += ch; writeInfo(temp, "運算符"); getChar(); } else { error(2); return; } break; case 10: if (ch == '"') { temp += ch; writeInfo(temp, "常量"); getChar(); } else if (ch == '\\') { for (int i = 0; i < 2; i++) { temp += ch; getChar(); } state = 10; } else { toNextCharAndStoreTempAndChangeState(10); } break; case 11: if (isDigital(ch) || isLetter(ch) || ch == '_') { toNextCharAndStoreTempAndChangeState(11); } else { state = 12; } break; case 12: if (isReserve(temp)) { writeInfo(temp, "保留字"); getChar(); } else { writeInfo(temp, "標識符"); getChar(); } break; case 13: if (isDigital(ch)) { toNextCharAndStoreTempAndChangeState(13); } else { writeInfo(temp, "常數"); } break; case 14: if (ch == '\'') { temp += ch; if (isLegalChar(temp)) { writeInfo(temp, "常量"); } else { error(9); return; } getChar(); } else if (ch == '\\') { for (int i = 0; i < 2; i++) { temp += ch; getChar(); } state = 14; } else { toNextCharAndStoreTempAndChangeState(14); } break; case 16: if (isDigital(ch)) { toNextCharAndStoreTempAndChangeState(5); } else { state = 8; } break; case 17: if (ch == '|') { temp += ch; writeInfo(temp, "運算符"); getChar(); } else { writeInfo(temp, "運算符"); } break; case 18: if (ch == '&') { temp += ch; writeInfo(temp, "運算符"); getChar(); } else { writeInfo(temp, "運算符"); } break; default: error(3); return; } analyze(); } private boolean isLegalChar(String temp) { char[] ch = temp.toCharArray(); int length = ch.length; boolean isLegalChar = false; /* * Char a = '';// error char b = ' ';// length = 3; char c = '\n';//length = 4; * b n r t " ' \ char d = '\122'; // length <= 6; */ if (length == 2) { // '' isLegalChar = false; } else if (length == 3) { isLegalChar = true; } else if (length == 4) { if ((ch[1] == '\\') && (ch[2] == 'b' || ch[2] == 'n' || ch[2] == 'r' || ch[2] == 't' || ch[2] == '\"' || ch[2] == '\'' || ch[2] == '\\' || isDigital(ch[2]))) { isLegalChar = true; } } else if (length <= 6) { if (ch[1] == '\\') { for (int i = 2; i < (length - 1); i++) { if (!isDigital(ch[i])) { isLegalChar = false; break; } isLegalChar = true; } } else { System.out.println('*'); isLegalChar = false; } } else { isLegalChar = false; } return isLegalChar; } private void toNextCharAndChangeState(int state) { this.state = state; getChar(); } private void toNextCharAndStoreTempAndChangeState(int state) { temp += ch; this.state = state; getChar(); } private boolean isReserve(String temp2) { for (int i = 0; i < 50; i++) { if (temp.equals(reserveWords[i])) { return true; } } return false; } private void writeInfo(String value, String type) { info += lineNum + ": < " + type + " , " + value + " >\r\n"; state = 0; } private boolean isLetter(char ch) { if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122)) return true; else return false; } private boolean isBoundary(char ch) { if (ch == ',' || ch == ';' || ch == '(' || ch == ')' || ch == '[' || ch == ']' || ch == '{' || ch == '}') return true; return false; } private boolean isOperator1(char ch) { // / * = < > if (ch == '/' || ch == '*' || ch == '=' || ch == '<' || ch == '>') return true; return false; } private boolean isOperator2(char ch) { // ? . : if (ch == '?' || ch == '.' || ch == ':') return true; return false; } private boolean isDigital(char ch) { if (ch >= 48 && ch <= 57) return true; else return false; } private void error(int i) { info = "詞法分析出錯\r\n錯誤定位:" + i; } private void getChar() { try { if (fd == null) { fd = new FileReader("D:/MyEclipse 10 Workspaces/Lexical analyzer/io file/test.txt"); } ch = (char) fd.read(); if (ch == -1) { // 當從一個文件中讀取數據時,在數據最后會返回一個int型-1來表示結束 fd.close(); } } catch (IOException e) { } } private void write(String info) { try { FileWriter fw = new FileWriter("D:/MyEclipse 10 Workspaces/Lexical analyzer/io file/result.txt"); fw.write(info); fw.flush(); // 刷新數據,將數據寫入文件中 fw.close(); } catch (IOException e) { } } public static void main(String[] args) throws IOException { new Lexical(); } }