1: package compiler;
2:
3: import java.io.IOException;
4: import java.util.BitSet;
5:
6: /**
7: * 語法分析器。 這是PL/0分析器中最重要的部分, 在語法分析的過程當中嵌入了語法錯誤檢查和目標代碼生成。
8: *
9: * @author jiangnan
10: *
11: */
12: public class Praser {
13:
14: /**
15: * 當前符號,由nextsym()讀入
16: *
17: * @see #nextsym()
18: */
19: private Symbol sym; //符號表
20: private Scanner lex; //詞法分析器
21: public SymbolTable table; //符號表
22: public Interpreter interp; //虛擬機指令
23: public Err myErr;
24:
25: /**
26: * 表示申明開始的符號集合:聲明的FIRST集合
27: */
28: private BitSet declbegsys;
29: /**
30: * 表示語句開始的符號集合:語句的FIRST集合
31: */
32: private BitSet statbegsys;
33: /**
34: * 表示因子開始的符號集合:因子的FIRST集合
35: */
36: private BitSet facbegsys;
37:
38: /**
39: * 當前做用域的堆棧幀大小,或者說數據大小(data size)
40: * 計算每一個變量在運行棧中相對本過程基地址的偏移量,
41: * 放在symbolTable中的address域,
42: * 生成目標代碼時再放在code中的a域
43: */
44: private int dx = 0;
45:
46: /**
47: * 構造並初始化語法分析器
48: *
49: * @param s 編譯器的詞法分析器
50: * @param t 編譯器的符號表
51: * @param i 編譯器的目標代碼生成器
52: */
53: public Praser(Scanner lex, SymbolTable table, Interpreter interp) {
54: this.lex = lex;
55: this.table = table;
56: this.interp = interp;
57: this.myErr=new Err();
58: /**
59: * 設置申明開始符號集
60: * <分程序> ::= [<常量說明部分>][<變量說明部分>]{<過程說明部分>}<語句>
61: * <常量說明部分> ::= const<常量定義>{,<常量定義>};
62: * <變量說明部分>::= var<標識符>{,<標識符>};
63: * <過程說明部分> ::= <過程首部>procedure<標識符>; <分程序>;
64: * FIRST(declaration)={const var procedure null };
65: */
66: declbegsys = new BitSet(Symbol.symnum);
67: declbegsys.set(Symbol.constsym); //將指定索引處的位設置爲 true。
68: declbegsys.set(Symbol.varsym);
69: declbegsys.set(Symbol.procsym);
70:
71: /**
72: * 設置語句開始符號集
73: * <語句> ::=<賦值語句>|<條件語句>|<當型循環語句>|<過程調用語句>|<讀語句>|<寫語句>|<複合語句>|<重複語句>|<空>
74: * <賦值語句> ::= <標識符>:=<表達式>
75: * <條件語句> ::= if<條件>then<語句>[else<語句>]
76: * <當型循環語句> ::= while<條件>do<語句>
77: * <重複語句> ::= repeat<語句>{;<語句>}until<條件>
78: * <過程調用語句> ::= call<標識符>
79: * <複合語句> ::= begin<語句>{;<語句>}end
80: * FIRST(statement)={begin call if while repeat null };
81: */
82: statbegsys = new BitSet(Symbol.symnum);
83: statbegsys.set(Symbol.beginsym);
84: statbegsys.set(Symbol.callsym);
85: statbegsys.set(Symbol.ifsym);
86: statbegsys.set(Symbol.whilesym);
87: statbegsys.set(Symbol.repeatsym);
88:
89: /**
90: * 設置因子開始符號集
91: * <因子> ::= <標識符>|<無符號整數>|'('<表達式>')'
92: * FIRST(factor)={ident,number,( };
93: */
94: facbegsys = new BitSet(Symbol.symnum);
95: facbegsys.set(Symbol.ident);
96: facbegsys.set(Symbol.number);
97: facbegsys.set(Symbol.lparen);
98: }
99:
100: //得到下一個語法符號,調用一下getsym()
101: public void nextsym() {
102: sym = lex.getsym();
103: }
104:
105: /**
106: * 測試當前符號是否合法 本過程有三個參數,s一、s2爲兩個符號集合,n爲出錯代碼。
107: * 本過程的功能是:測試當前符號(即sym變量中的值)是否在s1集合中, 若是不在,就經過調用出錯報告過程輸出出錯代碼n,
108: * 並放棄當前符號,經過詞法分析過程獲取一下單詞, 直到這個單詞出如今s1或s2集合中爲止。 這個過程在實際使用中很靈活,主要有兩個用法:
109: * 在進入某個語法單位時,調用本過程, 檢查當前符號是否屬於該語法單位的開始符號集合。 若不屬於,則濾去開始符號和後繼符號集合外的全部符號。
110: * 在語法單位分析結束時,調用本過程, 檢查當前符號是否屬於調用該語法單位時應有的後繼符號集合。 若不屬於,則濾去後繼符號和開始符號集合外的全部符號。
111: * 經過這樣的機制,能夠在源程序出現錯誤時, 及時跳過出錯的部分,保證語法分析能夠繼續下去。
112: *
113: * @param firstSet 須要的符號
114: * @param followSet 不須要的符號,添加一個補救集合
115: * @param errcode 錯誤號
116: */
117: void test(BitSet s1, BitSet s2, int errcode) {
118: /**
119: * 在某一部分(如一條語句,一個表達式)將要結束時,咱們但願下一個符號屬於某集合
120: * (該部分的FOLLOW集合),test負責這項檢測,而且負責當檢測不經過時的補救措施,程
121: * 序在須要檢測時指定當前須要的符號集合和補救用的集合(如以前未完成部分的後跟符 號),以及檢測不經過時的錯誤號。
122: */
123: if (!s1.get(sym.symtype)) {
124: myErr.report(errcode,lex.lineCnt);
125: //當檢測不經過時,不停地獲取符號,直到它屬於須要的集合
126: s1.or(s2); //把s2集合補充進s1集合
127: while (!s1.get(sym.symtype)) {
128: nextsym();
129: }
130: }
131: }
132:
133: /**
134: * <程序> ::= <分程序>. 啓動語法分析過程,此前必須先調用一次nextsym()
135: *
136: * @see #nextsym()
137: */
138: public void parse() {
139: BitSet nxtlev = new BitSet(Symbol.symnum);
140: nxtlev.or(declbegsys);
141: nxtlev.or(statbegsys);
142: nxtlev.set(Symbol.peroid);
143: /**
144: * <程序> ::= <分程序>. FOLLOW(block)={ . }
145: * <分程序> ::= [<常量說明部分>][變量說明部分>]{<過程說明部分>}<語句>
146: * FIRST(declaration)={const var procedure null };
147: * <語句> ::= <賦值語句>|<條件語句>|<當型循環語句>|<過程調用語句>|<讀語句>|<寫語句>|<複合語句>|<空>
148: * FIRST(statement)={begin call if while repeat null };
149: * FIRST(block)= {const var procedure begin call if while repeat . }
150: */
151: block(0, nxtlev); //解析<分程序>
152:
153: if (sym.symtype != Symbol.peroid) //缺乏句號
154: {
155: myErr.report(9,lex.lineCnt);
156: }
157:
158: //在程序分析結束以後,顯示符號表的信息
159: table.debugTable(0);
160: try {
161: interp.debugPcodeArray();
162: } catch (IOException ex) {
163: ex.printStackTrace();
164: System.out.println("***write pcode Array to file meet with error***");
165: }
166:
167: }
168:
169: /**
170: * 分析-->分程序
171: * <分程序>:=[<常數說明部分>][<變量說明部分>]{<過程說明部分>}<語句>
172: *
173: * @param lev 當前分程序所在層
174: * @param fsys 當前模塊的FOLLOW集合
175: */
176: public void block(int lev, BitSet fsys) {
177: BitSet nxtlev = new BitSet(Symbol.symnum);
178:
179: int dx0 = dx, //記錄本層以前的數據量,以便返回時恢復
180: tx0 = table.tablePtr, //記錄本層名字的初始位置
181: cx0;
182: //置初始值爲3的緣由是:
183: //每一層最開始的位置有三個空間用於存放靜態鏈SL、動態鏈DL和返回地址RA
184: dx = 3;
185: //當前pcode代碼的地址,傳給當前符號表的addr項
186: table.get(table.tablePtr).addr = interp.arrayPtr; //在符號表的當前位置記錄下這個jmp指令在代碼段中的位置
187: interp.gen(Pcode.JMP, 0, 0); //JMP 0 0
188:
189: if (lev > SymbolTable.levMax) //必須先判斷嵌套層層數
190: {
191: myErr.report(32,lex.lineCnt); //嵌套層數過大
192: }
193: //分析<說明部分>
194: do {
195: //<常量說明部分> ::= const<常量定義>{,<常量定義>};
196: if (sym.symtype == Symbol.constsym) { //例如const a=0,b=0,... ...,z=0;
197: nextsym();
198: constdeclaration(lev); //<常量定義>
199: while (sym.symtype == Symbol.comma) {
200: nextsym();
201: constdeclaration(lev);
202: }
203:
204: if (sym.symtype == Symbol.semicolon) //若是是分號,表示常量申明結束
205: {
206: nextsym();
207: } else {
208: myErr.report(5,lex.lineCnt); //漏了逗號或者分號
209: }
210: }
211:
212: //<變量說明部分>
213: //var<標識符>{,<標識符>};
214: if (sym.symtype == Symbol.varsym) { //讀入的數爲var
215: nextsym();
216: vardeclaration(lev); //識別<標識符>
217: while (sym.symtype == Symbol.comma) { //識別{,<標識符>}
218: nextsym();
219: vardeclaration(lev);
220: }
221: if (sym.symtype == Symbol.semicolon) //若是是分號,表示變量申明結束
222: {
223: nextsym();
224: } else {
225: myErr.report(5,lex.lineCnt); // 漏了逗號或者分號
226: }
227: }
228:
229: /**
230: * <過程說明部分> ::= procedure<標識符>; <分程序> ;
231: * FOLLOW(semicolon)={NULL<過程首部>},
232: * 須要進行test procedure a1; procedure 容許嵌套,故用while
233: */
234: while (sym.symtype == Symbol.procsym) { //若是是procedure
235: nextsym();
236: if (sym.symtype == Symbol.ident) { //填寫符號表
237: table.enter(sym, SymbolTable.Item.procedure, lev, dx); //當前做用域的大小
238: nextsym();
239: } else {
240: myErr.report(4,lex.lineCnt); //procedure後應爲標識符
241: }
242: if (sym.symtype == Symbol.semicolon) //分號,表示<過程首部>結束
243: {
244: nextsym();
245: } else {
246: myErr.report(5,lex.lineCnt); //漏了逗號或者分號
247: }
248: nxtlev = (BitSet) fsys.clone(); //當前模塊(block)的FOLLOW集合
249: //FOLLOW(block)={ ; }
250: nxtlev.set(Symbol.semicolon);
251: block(lev + 1, nxtlev); //嵌套層次+1,分析分程序
252:
253: if (sym.symtype == Symbol.semicolon) { //<過程說明部分> 識別成功
254:
255: nextsym();
256: //FIRST(statement)={begin call if while repeat null };
257: nxtlev = (BitSet) statbegsys.clone(); //語句的FIRST集合
258: //FOLLOW(嵌套分程序)={ ident , procedure }
259: nxtlev.set(Symbol.ident);
260: nxtlev.set(Symbol.procsym);
261: test(nxtlev, fsys, 6); // 測試symtype屬於FIRST(statement),
262: //6:過程說明後的符號不正確
263: } else {
264: myErr.report(5,lex.lineCnt); // 漏了逗號或者分號
265: }
266: }
267:
268: /**
269: * FIRST(statement)={begin call if while repeat null };
270: * FIRST(declaration)={const var procedure null };
271: * 一個分程序的說明部分識別結束後,下面多是語句statement或者嵌套的procedure(first(block)={各類聲明})
272: */
273: nxtlev = (BitSet) statbegsys.clone();
274: //FIRST(statement)={ ident }
275: nxtlev.set(Symbol.ident);
276: test(nxtlev, declbegsys, 7); //7:應爲語句
277: //FIRST(declaration)={const var procedure null };
278: } while (declbegsys.get(sym.symtype)); //直到沒有聲明符號
279:
280: //開始生成當前過程代碼
281: /**
282: * 分程序聲明部分完成後,即將進入語句的處理, 這時的代碼分配指針cx的值正好指向語句的開始位置,
283: * 這個位置正是前面的jmp指令須要跳轉到的位置
284: */
285: SymbolTable.Item item = table.get(tx0);
286: interp.pcodeArray[item.addr].a = interp.arrayPtr;//過程入口地址填寫在pcodeArray中的jmp 的第二個參數
287: item.addr = interp.arrayPtr; //當前過程代碼地址
288: item.size = dx;//dx:一個procedure中的變量數目+3 ,聲明部分中每增長一條聲明都會給dx+1
289: //聲明部分已經結束,dx就是當前過程的堆棧幀大小
290: /**
291: * 因而經過前面記錄下來的地址值,把這個jmp指令的跳轉位置改爲當前cx的位置。
292: * 並在符號表中記錄下當前的代碼段分配地址和局部數據段要分配的大小(dx的值)。 生成一條int指令,分配dx個空間,
293: * 做爲這個分程序段的第一條指令。 下面就調用語句處理過程statement分析語句。
294: */
295: cx0 = interp.arrayPtr;
296: //生成分配內存代碼,
297: interp.gen(Pcode.INT, 0, dx);
298:
299: //打印<說明部分>代碼
300: table.debugTable(tx0);
301:
302: //分析<語句>
303: nxtlev = (BitSet) fsys.clone(); //每一個FOLLOW集合都包含上層FOLLOW集合,以便補救
304: nxtlev.set(Symbol.semicolon); //語句後跟符號爲分號或者end
305: nxtlev.set(Symbol.endsym);
306: statement(nxtlev, lev);
307: /**
308: * 分析完成後,生成操做數爲0的opr指令, 用於從分程序返回(對於0層的主程序來講,就是程序運行完成,退出)。
309: */
310: interp.gen(Pcode.OPR, 0, 0); //每一個過程出口都要使用的釋放數據段指令
311:
312: nxtlev = new BitSet(Symbol.symnum); //分程序沒有補救集合
313: test(fsys, nxtlev, 8); //檢測後跟符號正確性
314:
315: interp.listcode(cx0);
316:
317: dx = dx0; //恢復堆棧幀計數器
318: table.tablePtr = tx0; //回覆名字表位置
319: }
320:
321: /**
322: * 分析<常量定義>
323: * <常量定義> ::= <標識符>=<無符號整數>
324: *
325: * @param lev 當前所在的層次
326: */
327: void constdeclaration(int lev) {
328: if (sym.symtype == Symbol.ident) { //識別符
329: String id = sym.id;//先保存起來
330: nextsym();
331: if (sym.symtype == Symbol.eql || sym.symtype == Symbol.becomes) { //等於或者賦值符號
332: if (sym.symtype == Symbol.becomes) {
333: myErr.report(1,lex.lineCnt); //把=寫成了:=
334: }
335: nextsym(); //自動進行了錯誤糾正使編譯繼續進行,把賦值號看成等號處理
336: if (sym.symtype == Symbol.number) {
337: sym.id = id;
338: table.enter(sym, SymbolTable.Item.constant, lev, dx); //將常量填入符號表
339: nextsym();
340: } else {
341: myErr.report(2,lex.lineCnt); //常量說明=後應是數字
342: }
343: } else {
344: myErr.report(3,lex.lineCnt); //常量說明標誌後應是=
345: }
346: } else {
347: myErr.report(4,lex.lineCnt); //const後應是標識符
348: }
349: }
350:
351: /**
352: * 分析<標識符>
353: * <變量說明部分>::= var <標識符> { , <標識符> } ;
354: *
355: * @param lev 當前所在的層次
356: */
357: void vardeclaration(int lev) {
358: if (sym.symtype == Symbol.ident) {
359: /**
360: * 填寫名字表並改變堆棧幀計數器 符號表中記錄下標識符的名字、它所在的層及它在所在層中的偏移地址
361: */
362: table.enter(sym, SymbolTable.Item.variable, lev, dx);
363: /**
364: * 變量定義過程當中,會用dx變量記錄下局部數據段分配的空間個數
365: */
366: dx++;
367: nextsym();
368: } else {
369: myErr.report(4,lex.lineCnt); //var後應是標識符
370: }
371: }
372:
373: /**
374: * 分析<語句>
375: *
376: * @param fsys FOLLOW集合
377: * @param lev 當前層次
378: */
379: void statement(BitSet fsys, int lev) {
380: // FIRST(statement)={ident,read,write,call,if, while}
381: switch (sym.symtype) {
382: case Symbol.ident:
383: praseAssignStatement(fsys, lev);
384: break;
385: case Symbol.readsym:
386: praseReadStatement(fsys, lev);
387: break;
388: case Symbol.writesym:
389: praseWriteStatement(fsys, lev);
390: break;
391: case Symbol.callsym:
392: praseCallStatement(fsys, lev);
393: break;
394: case Symbol.ifsym:
395: praseIfStatement(fsys, lev);
396: break;
397: case Symbol.beginsym:
398: praseBeginStatement(fsys, lev);
399: break;
400: case Symbol.whilesym:
401: praseWhileStatement(fsys, lev);
402: break;
403: case Symbol.repeatsym:
404: praseRepeatStatement(fsys, lev);
405: break;
406: default:
407: BitSet nxlev = new BitSet(Symbol.symnum);
408: test(fsys, nxlev, 19); //語句後的符號不正確
409: break;
410: }
411: }
412:
413: /**
414: * 解析<重複語句> ::= repeat<語句>{;<語句>}until<條件>
415: */
416: private void praseRepeatStatement(BitSet fsys, int lev) {
417: int cx1 = interp.arrayPtr;
418: nextsym();
419: BitSet nxtlev = (BitSet) fsys.clone();
420: nxtlev.set(Symbol.semicolon);
421: nxtlev.set(Symbol.untilsym);
422: statement(fsys, lev);
423:
424: while (statbegsys.get(sym.symtype) || sym.symtype == Symbol.semicolon) {
425: if (sym.symtype == Symbol.semicolon) {
426: nextsym();
427: } else {
428: myErr.report(34,lex.lineCnt);
429: }
430:
431: statement(nxtlev, lev);
432: }
433: if (sym.symtype == Symbol.untilsym) {
434: nextsym();
435: condition(fsys, lev);
436: interp.gen(Pcode.JPC, 0, cx1);
437: } else {
438: // myErr.report(dx);
439: }
440: }
441:
442: /**
443: * 分析<當型循環語句>
444: * <當型循環語句> ::= while<條件>do<語句>
445: * 首先用cx1變量記下當前代碼段分配位置, 做爲循環的開始位置。 而後處理while語句中的條件表達式生成相應代碼把結果放在數據棧頂,
446: * 再用cx2變量記下當前位置, 生成條件轉移指令, 轉移位置未知,填0。 經過遞歸調用語句分析過程分析do語句後的語句或語句塊並生成相應代碼。
447: * 最後生成一條無條件跳轉指令jmp,跳轉到cx1所指位置, 並把cx2所指的條件跳轉指令JPC的跳轉位置,改爲當前代碼段分配位置
448: *
449: * @param fsys FOLLOW符號集
450: * @param lev 當前層次
451: */
452: private void praseWhileStatement(BitSet fsys, int lev) {
453: int cx1 = interp.arrayPtr; //保存判斷條件操做的位置
454: nextsym();
455: BitSet nxtlev = (BitSet) fsys.clone();
456: //FOLLOW(條件)={ do }
457: nxtlev.set(Symbol.dosym); //後跟符號爲do
458: condition(nxtlev, lev); //分析<條件>
459: int cx2 = interp.arrayPtr; //保存循環體的結束下一個位置
460: interp.gen(Pcode.JPC, 0, 0);
461: if (sym.symtype == Symbol.dosym) {
462: nextsym();
463: } else {
464: myErr.report(18,lex.lineCnt); //缺乏do
465: }
466: statement(fsys, lev); //分析<語句>
467: interp.gen(Pcode.JMP, 0, cx1); //回頭從新判斷條件
468: interp.pcodeArray[cx2].a = interp.arrayPtr; //反填跳出循環的地址,與<條件語句>相似
469: }
470:
471: /**
472: * 分析<複合語句>
473: * <複合語句> ::= begin<語句>{;<語句>}end 經過循環遍歷begin/end語句塊中的每個語句,
474: * 經過遞歸調用語句分析過程分析並生成相應代碼。
475: *
476: * @param fsys FOLLOW集合
477: * @param lev當前層次
478: */
479: private void praseBeginStatement(BitSet fsys, int lev) {
480: nextsym();
481: BitSet nxtlev = (BitSet) fsys.clone();
482: //FOLLOW(statement)={ ; end }
483: nxtlev.set(Symbol.semicolon);
484: nxtlev.set(Symbol.endsym);
485: statement(nxtlev, lev);
486: //循環分析{;<語句>},直到下一個符號不是語句開始符號或者收到end
487: while (statbegsys.get(sym.symtype) || sym.symtype == Symbol.semicolon) {
488: if (sym.symtype == Symbol.semicolon) {
489: nextsym();
490: } else {
491: myErr.report(10,lex.lineCnt); //缺乏分號
492: }
493: statement(nxtlev, lev);
494: }
495: if (sym.symtype == Symbol.endsym) //若爲end ,statement解析成功
496: {
497: nextsym();
498: } else {
499: myErr.report(17,lex.lineCnt); //缺乏end 或者分號
500: }
501: }
502:
503: /**
504: * 分析<條件語句>
505: * <條件語句> ::= if <條件> then <語句>
506: * 按if語句的語法,首先調用邏輯表達式處理過程, 處理if語句的條件,把相應的真假值放到數據棧頂。
507: * 接下去記錄下代碼段分配位置(即下面生成的jpc指令的位置), 而後生成條件轉移jpc指令(遇0或遇假轉移), 轉移地址未知暫時填0。
508: * 而後調用語句處理過程處理then語句後面的語句或語句塊。 then後的語句處理完後, 當前代碼段分配指針的位置就應該是上面的jpc指令的轉移位置。
509: * 經過前面記錄下的jpc指令的位置, 把它的跳轉位置改爲當前的代碼段指針位置。
510: *
511: * @param fsys FOLLOW集合
512: * @param lev 當前層次
513: */
514: private void praseIfStatement(BitSet fsys, int lev) {
515: nextsym();
516: BitSet nxtlev = (BitSet) fsys.clone();
517:
518: //FOLLOW(condition)={ then do }
519: //註釋:<當型循環語句> ::= while<條件>do<語句>
520: nxtlev.set(Symbol.thensym);
521: nxtlev.set(Symbol.dosym);
522: condition(nxtlev, lev); //分析<條件>
523: if (sym.symtype == Symbol.thensym) {
524: nextsym();
525: } else {
526: myErr.report(16,lex.lineCnt); //缺乏then
527: }
528: int cx1 = interp.arrayPtr; //保存當前指令地址
529: interp.gen(Pcode.JPC, 0, 0); //生成條件跳轉指令,跳轉地址位置,暫時寫0
530: statement(fsys, lev); //處理then後的statement
531: interp.pcodeArray[cx1].a = interp.arrayPtr; //經statement處理後,cx爲then後語句執行
532: //完的位置,它正是前面未定的跳轉地址
533:
534: if (sym.symtype == Symbol.elsesym) {
535: interp.pcodeArray[cx1].a++;
536: nextsym();
537: int tmpPtr = interp.arrayPtr;
538: interp.gen(Pcode.JMP, 0, 0);
539: statement(fsys, lev);
540: interp.pcodeArray[tmpPtr].a = interp.arrayPtr;
541: }
542:
543: }
544:
545: /**
546: * 分析<標識符>
547: * <過程調用語句> ::= call<標識符>
548: * 從符號表中找到call語句右部的標識符, 得到其所在層次和偏移地址。 而後生成相應的cal指令。 至於調用子過程所需的保護現場等工做
549: * 是由類PCODE解釋程序在解釋執行cal指令時自動完成的
550: *
551: * @param fsys FOLLOW集合
552: * @param lev 當前層次
553: */
554: private void praseCallStatement(BitSet fsys, int lev) {
555: nextsym();
556: if (sym.symtype == Symbol.ident) { //檢查符號表中該標識符是否已聲明
557: int index = table.position(sym.id);
558: if (index != 0) { //若table中無此名字,返回0
559: SymbolTable.Item item = table.get(index); //得到名字表某一項的內容
560: if (item.type == SymbolTable.Item.procedure) //檢查該標識符的類型是否爲procedure
561: {
562: interp.gen(Pcode.CAL, lev - item.lev, item.addr);
563: } else {
564: myErr.report(15,lex.lineCnt); //call後標識符應爲過程
565: }
566: } else {
567: myErr.report(11,lex.lineCnt); //過程調用未找到
568: }
569: nextsym();
570: } else {
571: myErr.report(14,lex.lineCnt); //call後應爲標識符
572: }
573: }
574:
575: /**
576: * 分析'(' <表達式> { , <表達式> } ')'
577: * <寫語句> ::= write '(' <表達式> { , <表達式> } ')' 在語法正確的前提下,生成指令: 經過循環調用表達式處理過程
578: * 分析write語句括號中的每個表達式, 生成相應指令 保證把表達式的值算出並放到數據棧頂 並生成14號操做的opr指令, 輸出表達式的值。
579: * 最後生成15號操做的opr指令,輸出一個換行
580: *
581: * @param fsys FOLLOW集合
582: * @param lev 當前層次
583: */
584: private void praseWriteStatement(BitSet fsys, int lev) {
585: nextsym();
586: if (sym.symtype == Symbol.lparen) {
587: do {
588: nextsym();
589: BitSet nxtlev = (BitSet) fsys.clone();
590: //FOLLOW={ , ')' }
591: nxtlev.set(Symbol.rparen);
592: nxtlev.set(Symbol.comma);
593: expression(nxtlev, lev);
594: interp.gen(Pcode.OPR, 0, 14); //OPR 0 14:輸出棧頂的值
595: } while (sym.symtype == Symbol.comma);
596:
597: if (sym.symtype == Symbol.rparen) //解析成功
598: {
599: nextsym();
600: } else {
601: myErr.report(33,lex.lineCnt); //格式錯誤,應爲右括號
602: }
603: } else {
604: myErr.report(34,lex.lineCnt); //格式錯誤,應爲右括號
605: }
606: interp.gen(Pcode.OPR, 0, 15); //OPR 0 15:輸出換行
607: }
608:
609: /**
610: * 分析'(' <標識符> { , <標識符> } ')'
611: * <讀語句> ::= read '(' <標識符> { , <標識符> } ')' 肯定read語句語法合理的前提下(不然報錯), 生成相應的指令:
612: * 第一條是16號操做的opr指令, 實現從標準輸入設備上讀一個整數值,放在數據棧頂。 第二條是sto指令,
613: * 把棧頂的值存入read語句括號中的變量所在的單元
614: *
615: * @param fsys FOLLOW集合
616: * @param lev 當前層次
617: */
618: private void praseReadStatement(BitSet fsys, int lev) {
619: nextsym();
620: if (sym.symtype == Symbol.lparen) { //左括號
621: int index = 0;
622: do {
623: nextsym();
624: if (sym.symtype == Symbol.ident) //標識符
625: {
626: index = table.position(sym.id);
627: }
628: if (index == 0) {
629: myErr.report(35,lex.lineCnt); //read()中應是聲明過的變量名
630: } else {
631: SymbolTable.Item item = table.get(index);
632: if (item.type != SymbolTable.Item.variable) { //判斷符號表中的該符號類型是否爲變量
633: myErr.report(32,lex.lineCnt); //read()中的標識符不是變量
634: } else {
635: interp.gen(Pcode.OPR, 0, 16); //OPR 0 16:讀入一個數據
636: interp.gen(Pcode.STO, lev - item.lev, item.addr); //STO L A;存儲變量
637: }
638: }
639: nextsym();
640: } while (sym.symtype == Symbol.comma);
641: } else {
642: myErr.report(34,lex.lineCnt); //格式錯誤,應是左括號
643: }
644:
645: if (sym.symtype == Symbol.rparen) //匹配成功!
646: {
647: nextsym();
648: } else {
649: myErr.report(33,lex.lineCnt); //格式錯誤,應是右括號
650: while (!fsys.get(sym.symtype)) //sym.symtype!=NULL ???
651: {
652: nextsym();
653: }
654: }
655: }
656:
657: /**
658: * 分析:=<表達式>
659: * <賦值語句> ::= <標識符>:=<表達式>
660: * 首先獲取賦值號左邊的標識符, 從符號表中找到它的信息, 並確認這個標識符確爲變量名。 而後經過調用表達式處理過程 算得賦值號右部的表達式的值
661: * 並生成相應的指令 保證這個值放在運行期的數據棧頂。 最後經過前面查到的左部變量的位置信息, 生成相應的sto指令, 把棧頂值存入指定的變量的空間,
662: * 實現了賦值操做。
663: *
664: * @param fsys FOLLOW集合
665: * @param lev 當前層次
666: */
667: private void praseAssignStatement(BitSet fsys, int lev) {
668: //從符號表中找到該標識符的信息
669: int index = table.position(sym.id);
670: if (index > 0) {
671: SymbolTable.Item item = table.get(index);
672: if (item.type == SymbolTable.Item.variable) { //標識符
673: nextsym();
674: if (sym.symtype == Symbol.becomes) {
675: nextsym();
676: } else {
677: myErr.report(13,lex.lineCnt); //沒有檢測到賦值符號
678: }
679: BitSet nxtlev = (BitSet) fsys.clone();
680: expression(nxtlev, lev); //解析表達式
681: //expression將執行一系列指令,
682: //但最終結果將會保存在棧頂,
683: //執行sto命令完成賦值
684: interp.gen(Pcode.STO, lev - item.lev, item.addr);
685: } else {
686: myErr.report(12,lex.lineCnt); //不可向常量或過程名賦值
687: }
688: } else {
689: myErr.report(11,lex.lineCnt); //標識符未說明
690: }
691: }
692:
693: /**
694: * 分析<表達式>
695: * <表達式> ::= [+|-]<項>{<加法運算符><項>} 根據PL/0語法可知,
696: * 表達式應該是由正負號或無符號開頭、由若干個項以加減號鏈接而成。 而項是由若干個因子以乘除號鏈接而成, 因子則多是一個標識符或一個數字,
697: * 或是一個以括號括起來的子表達式。 根據這樣的結構,構造出相應的過程, 遞歸調用就完成了表達式的處理。
698: * 把項和因子獨立開處理解決了加減號與乘除號的優先級問題。 在這幾個過程的反覆調用中,始終傳遞fsys變量的值,
699: * 保證能夠在出錯的狀況下跳過出錯的符號,使分析過程得以進行下去
700: *
701: * @param fsys FOLLOW集合
702: * @param lev 當前層次
703: */
704: private void expression(BitSet fsys, int lev) {
705: if (sym.symtype == Symbol.plus || sym.symtype == Symbol.minus) { //分析[+|-]<項>
706: int addOperatorType = sym.symtype;
707: nextsym();
708: BitSet nxtlev = (BitSet) fsys.clone();
709: nxtlev.set(Symbol.plus);
710: nxtlev.set(Symbol.minus);
711: term(nxtlev, lev);
712: if (addOperatorType == Symbol.minus) //OPR 0 1::NEG取反
713: {
714: interp.gen(Pcode.OPR, 0, 1);
715: }
716: // 若是不是負號就是正號,不需生成相應的指令
717: } else {
718: BitSet nxtlev = (BitSet) fsys.clone();
719: nxtlev.set(Symbol.plus);
720: nxtlev.set(Symbol.minus);
721: term(nxtlev, lev);
722: }
723:
724: //分析{<加法運算符><項>}
725: while (sym.symtype == Symbol.plus || sym.symtype == Symbol.minus) {
726: int addOperatorType = sym.symtype;
727: nextsym();
728: BitSet nxtlev = (BitSet) fsys.clone();
729: //FOLLOW(term)={ +,- }
730: nxtlev.set(Symbol.plus);
731: nxtlev.set(Symbol.minus);
732: term(nxtlev, lev);
733: interp.gen(Pcode.OPR, 0, addOperatorType); //opr 0 2:執行加法,opr 0 3:執行減法
734: }
735: }
736:
737: /**
738: * 分析<項>
739: * <項> ::= <因子>{<乘法運算符><因子>}
740: *
741: * @param fsys FOLLOW集合
742: * @param lev 當前層次
743: */
744: private void term(BitSet fsys, int lev) {
745: //分析<因子>
746: BitSet nxtlev = (BitSet) fsys.clone();
747: //FOLLOW(factor)={ * /}
748: //一個因子後應當遇到乘號或除號
749: nxtlev.set(Symbol.mul);
750: nxtlev.set(Symbol.div);
751:
752: factor(nxtlev, lev); //先分析<因子>
753:
754: //分析{<乘法運算符><因子>}
755: while (sym.symtype == Symbol.mul || sym.symtype == Symbol.div) {
756: int mulOperatorType = sym.symtype; //4表示乘法 ,5表示除法
757: nextsym();
758: factor(nxtlev, lev);
759: interp.gen(Pcode.OPR, 0, mulOperatorType); //乘法:OPR 0 4 ,除法:OPR 0 5
760: }
761: }
762:
763: /**
764: * 分析<因子>
765: * <因子>=<標識符>|<無符號整數>|'('<表達式>')' 開始因子處理前,先檢查當前token是否在facbegsys集合中。
766: * 若是不是合法的token,拋24號錯誤,並經過fsys集恢復使語法處理能夠繼續進行
767: *
768: * @param fsys FOLLOW集合
769: * @param lev 當前層次
770: */
771: private void factor(BitSet fsys, int lev) {
772: test(facbegsys, fsys, 24);//!!!!!!!有問題 //檢測因子的開始符號
773:
774: if (facbegsys.get(sym.symtype)) {
775: if (sym.symtype == Symbol.ident) { //因子爲常量或變量或者過程名
776: int index = table.position(sym.id);
777: if (index > 0) { //大於0:找到,等於0:未找到
778: SymbolTable.Item item = table.get(index);
779: switch (item.type) {
780: //若是這個標識符對應的是常量,值爲val,生成lit指令,把val放到棧頂
781: case SymbolTable.Item.constant: //名字爲常量
782: interp.gen(Pcode.LIT, 0, item.value); //生成lit指令,把這個數值字面常量放到棧頂
783: break;
784: case SymbolTable.Item.variable: //名字爲常量
785: //把位於距離當前層level的層的偏移地址爲adr的變量放到棧頂
786: interp.gen(Pcode.LOD, lev - item.lev, item.addr);
787: break;
788: case SymbolTable.Item.procedure: //常量
789: myErr.report(21,lex.lineCnt); //表達式內不可有過程標識符
790: break;
791: }
792: } else {
793: myErr.report(11,lex.lineCnt); //標識符未聲明
794: }
795: nextsym();
796: } else if (sym.symtype == Symbol.number) { //因子爲數
797: int num = sym.num;
798: if (num > SymbolTable.addrMax) { //數越界
799: myErr.report(31,lex.lineCnt);
800: num = 0;
801: }
802: interp.gen(Pcode.LIT, 0, num); //生成lit指令,把這個數值字面常量放到棧頂
803: nextsym();
804: } else if (sym.symtype == Symbol.lparen) { //因子爲表達式:'('<表達式>')'
805: nextsym();
806: BitSet nxtlev = (BitSet) fsys.clone();
807: //FOLLOW(expression)={ ) }
808: nxtlev.set(Symbol.rparen);
809: expression(nxtlev, lev);
810: if (sym.symtype == Symbol.rparen) //匹配成功
811: {
812: nextsym();
813: } else {
814: myErr.report(22,lex.lineCnt); //缺乏右括號
815: }
816: } else //作補救措施
817: {
818: test(fsys, facbegsys, 23); //一個因子處理完畢,遇到的token應在fsys集合中
819: } //若是不是,拋23號錯,並找到下一個因子的開始,使語法分析能夠繼續運行下去
820: }
821: }
822:
823: /**
824: * 分析<條件>
825: * <表達式><關係運算符><表達式>|odd<表達式>
826: * 首先判斷是否爲一元邏輯表達式:判奇偶。 若是是,則經過調用表達式處理過程分析計算表達式的值, 而後生成判奇指令。
827: * 若是不是,則確定是二元邏輯運算符, 經過調用表達式處理過程依次分析運算符左右兩部分的值, 放在棧頂的兩個空間中,而後依不一樣的邏輯運算符,
828: * 生成相應的邏輯判斷指令,放入代碼段。
829: *
830: * @param fsys FOLLOW集合
831: * @param lev 當前層次
832: */
833: private void condition(BitSet fsys, int lev) {
834: if (sym.symtype == Symbol.oddsym) { //分析ODD<表達式>
835: nextsym();
836: expression(fsys, lev);
837: interp.gen(Pcode.OPR, 0, 6); //OPR 0 6:判斷棧頂元素是否爲奇數
838: } else { //分析<表達式><關係運算符><表達式>
839: BitSet nxtlev = (BitSet) fsys.clone();
840: //FOLLOW(expression)={ = != < <= > >= }
841: nxtlev.set(Symbol.eql);
842: nxtlev.set(Symbol.neq);
843: nxtlev.set(Symbol.lss);
844: nxtlev.set(Symbol.leq);
845: nxtlev.set(Symbol.gtr);
846: nxtlev.set(Symbol.geq);
847: expression(nxtlev, lev);
6: if (sym.symtype == Symbol.eql || sym.symtype == Symbol.neq
7: || sym.symtype == Symbol.lss || sym.symtype == Symbol.leq
8: || sym.symtype == Symbol.gtr || sym.symtype == Symbol.geq) {
9: int relationOperatorType = sym.symtype; //預先保存symtype的值
10: nextsym();
11: expression(fsys, lev);
12: interp.gen(Pcode.OPR, 0, relationOperatorType); //symtype=eql... leq與7... 13相對應
13: } else {
14: myErr.report(20,lex.lineCnt); //應爲關係運算符
15: }
16: }
17: }
18:
19: void debug(String msg) {
20: System.out.println("*** DEDUG : " + msg + " ***");
21: }
22: }