PL/0编译器(java version)–Praser.java
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: * 测试当前符号是否合法 本过程有三个参数,s1、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: }