?? parser.java
字號:
package compiler.pl0;
/**
* 語法分析器。這是PL/0分析器中最重要的部分,在語法分析的過程中穿插著語法錯誤檢查和目標代碼生成。
*/
public class Parser {
private Scanner lex; // 對詞法分析器的引用
private Table table; // 對符號表的引用
private Interpreter interp; // 對目標代碼生成器的引用
String a = "";
private final int symnum = Symbol.values().length;
Err err = new Err();
String ss = "";
// 表示聲明開始的符號集合、表示語句開始的符號集合、表示因子開始的符號集合
// 實際上這就是聲明、語句和因子的FIRST集合
private SymSet declbegsys, statbegsys, facbegsys;
/**
* 當前符號,由nextsym()讀入
* @see #nextSym()
*/
private Symbol sym;
/**
* 當前作用域的堆棧幀大小,或者說數據大?。╠ata size)
*/
private int dx = 0;
/**
* 構造并初始化語法分析器,這里包含了C語言版本中init()函數的一部分代碼
* @param l 編譯器的詞法分析器
* @param t 編譯器的符號表
* @param i 編譯器的目標代碼生成器
*/
public Parser(Scanner l, Table t, Interpreter i) {
lex = l;
table = t;
interp = i;
// 設置聲明開始符號集
declbegsys = new SymSet(symnum);
declbegsys.set(Symbol.constsym);
declbegsys.set(Symbol.varsym);
declbegsys.set(Symbol.procsym);
// 設置語句開始符號集
statbegsys = new SymSet(symnum);
statbegsys.set(Symbol.beginsym);
statbegsys.set(Symbol.callsym);
statbegsys.set(Symbol.ifsym);
statbegsys.set(Symbol.whilesym);
// 設置因子開始符號集
facbegsys = new SymSet(symnum);
facbegsys.set(Symbol.ident);
facbegsys.set(Symbol.number);
facbegsys.set(Symbol.lparen);
}
/**
* 啟動語法分析過程,此前必須先調用一次nextsym()
* @see #nextSym()
*/
public void parse() {
SymSet nxtlev = new SymSet(symnum);
nxtlev.or(declbegsys);
nxtlev.or(statbegsys);
nxtlev.set(Symbol.period);
parseBlock(0, nxtlev);
if (sym != Symbol.period) {
String ss1 = err.report(9);
ss = ss + ss1 + "\n";
}
}
/**
* 獲得下一個語法符號,這里只是簡單調用一下getsym()
*/
public void nextSym() {
lex.getsym();
sym = lex.sym;
}
/**
* 測試當前符號是否合法
*
* @param s1 我們需要的符號
* @param s2 如果不是我們需要的,則需要一個補救用的集合
* @param errcode 錯誤號
*/
void test(SymSet s1, SymSet s2, int errcode) {
// 在某一部分(如一條語句,一個表達式)將要結束時時我們希望下一個符號屬于某集合
//(該部分的后跟符號),test負責這項檢測,并且負責當檢測不通過時的補救措施,程
// 序在需要檢測時指定當前需要的符號集合和補救用的集合(如之前未完成部分的后跟符
// 號),以及檢測不通過時的錯誤號。
if (!s1.get(sym)) {
//String ss1 = err.report(errcode);
//ss = ss + ss1 + "\n";
// 當檢測不通過時,不停獲取符號,直到它屬于需要的集合或補救的集合
while (!s1.get(sym) && !s2.get(sym)) {
nextSym();
}
}
}
/**
* 分析<分程序>
*
* @param lev 當前分程序所在層
* @param fsys 當前模塊后跟符號集
*/
public void parseBlock(int lev, SymSet fsys) {
// <分程序> := [<常量說明部分>][<變量說明部分>][<過程說明部分>]<語句>
int dx0, tx0, cx0; // 保留初始dx,tx和cx
SymSet nxtlev = new SymSet(symnum);
dx0 = dx; // 記錄本層之前的數據量(以便恢復)
dx = 3;
tx0 = table.tx; // 記錄本層名字的初始位置(以便恢復)
table.get(table.tx).adr = interp.cx;
interp.gen(Fct.JMP, 0, 0);
if (lev > PL0.levmax) {
String ss1 = err.report(32);
ss = ss + ss1 + "\n";
}
// 分析<說明部分>
do {
// <常量說明部分>
if (sym == Symbol.constsym) {
nextSym();
// the original do...while(sym == ident) is problematic, thanks to calculous
// do
parseConstDeclaration(lev);
while (sym == Symbol.comma) {
nextSym();
parseConstDeclaration(lev);
}
if (sym == Symbol.semicolon) {
nextSym();
}
else {
String ss1 = err.report(5); // 漏掉了逗號或者分號
ss = ss + ss1 + "\n";
}
// } while (sym == ident);
}
// <變量說明部分>
if (sym == Symbol.varsym) {
nextSym();
// the original do...while(sym == ident) is problematic, thanks to calculous
// do {
//TODO 變量和數組
parseVarDeclaration(lev);
while (sym == Symbol.comma) {
nextSym();
parseVarDeclaration(lev);
}
if (sym == Symbol.semicolon) {
nextSym();
}
else {
String ss1 = err.report(5); // 漏掉了逗號或者分號
ss = ss + ss1 + "\n";
}
// } while (sym == ident);
}
// <過程說明部分>
while (sym == Symbol.procsym) {
nextSym();
if (sym == Symbol.ident) {
table.enter(sym, Objekt.procedure, lev, dx);
nextSym();
}
else {
String ss1 = err.report(4); // procedure后應為標識符
ss = ss + ss1 + "\n";
}
if (sym == Symbol.semicolon) {
nextSym();
}
else {
String ss1 = err.report(5); // 漏掉了分號
ss = ss + ss1 + "\n";
}
nxtlev = (SymSet) fsys.clone();
nxtlev.set(Symbol.semicolon);
parseBlock(lev + 1, nxtlev);
if (sym == Symbol.semicolon) {
nextSym();
nxtlev = (SymSet) statbegsys.clone();
nxtlev.set(Symbol.ident);
nxtlev.set(Symbol.procsym);
test(nxtlev, fsys, 6);
}
else {
String ss1 = err.report(5); // 漏掉了分號
ss = ss + ss1 + "\n";
}
}
nxtlev = (SymSet) statbegsys.clone();
nxtlev.set(Symbol.ident);
test(nxtlev, declbegsys, 7);
}
while (declbegsys.get(sym)); // 直到沒有聲明符號
// 開始生成當前過程代碼
Table.Item item = table.get(tx0);
interp.code[item.adr].a = interp.cx;
item.adr = interp.cx; // 當前過程代碼地址
item.size = dx; // 聲明部分中每增加一條聲明都會給dx增加1,
// 聲明部分已經結束,dx就是當前過程的堆棧幀大小
cx0 = interp.cx;
interp.gen(Fct.INT, 0, dx); // 生成分配內存代碼
table.debugTable(tx0);
// 分析<語句>
nxtlev = (SymSet) fsys.clone(); // 每個后跟符號集和都包含上層后跟符號集和,以便補救
nxtlev.set(Symbol.semicolon); // 語句后跟符號為分號或end
nxtlev.set(Symbol.endsym);
parseStatement(nxtlev, lev);
interp.gen(Fct.OPR, 0, 0); // 每個過程出口都要使用的釋放數據段指令
nxtlev = new SymSet(symnum); // 分程序沒有補救集合
test(fsys, nxtlev, 8); // 檢測后跟符號正確性
interp.listcode(cx0);
dx = dx0; // 恢復堆棧幀計數器
table.tx = tx0; // 回復名字表位置
}
/**
* 分析<常量說明部分>
* @param lev 當前所在的層次
*/
void parseConstDeclaration(int lev) {
if (sym == Symbol.ident) {
nextSym();
if (sym == Symbol.eql || sym == Symbol.becomes) {
if (sym == Symbol.becomes) {
String ss1 = err.report(1); // 把 = 寫成了 :=
ss = ss + ss1 + "\n";
}
nextSym();
if (sym == Symbol.number) {
table.enter(sym, Objekt.constant, lev, dx);
nextSym();
}
else {
String ss1 = err.report(2); // 常量說明 = 后應是數字
ss = ss + ss1 + "\n";
}
}
else {
String ss1 = err.report(3); // 常量說明標識后應是 =
ss = ss + ss1 + "\n";
}
}
else {
String ss1 = err.report(4); // const 后應是標識符
ss = ss + ss1 + "\n";
}
}
/**
* 分析<變量說明部分>
* @param lev 當前層次
*/
void parseVarDeclaration(int lev) {
if (sym == Symbol.ident) {
// 填寫名字表并改變堆棧幀計數器
int startid = 0, endid = 0;
table.enter(sym, Objekt.variable, lev, dx);
dx++;
nextSym();
if (sym == Symbol.lparen) {
nextSym();
switch (sym) {
case ident:
startid = table.get(table.position(lex.id)).
val;
break;
case number:
startid = PL0.lex.num;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -