?? pio2.txt
字號:
getsym; (* 獲得下一個token,正常應為賦值號 *)
if sym = becomes then (* 如果的確為賦值號 *)
getsym (* 獲取下一個token,正常應為一個表達式 *)
else
error(13); (* 如果賦值語句的左部標識符號后所接不是賦值號,拋出13號錯誤 *)
expression(fsys); (* 處理表達式 *)
if i <> 0 then (* 如果不曾出錯,i將不為0,i所指為當前語名左部標識符在符號表中的位置 *)
with table[i] do
gen(sto, lev - level, adr) (* 產生一行把表達式值寫往指定內存的sto目標代碼 *)
end
else
if sym = readsym then (* 如果不是賦值語句,而是遇到了read語句 *)
begin
getsym; (* 獲得下一token,正常情況下應為左括號 *)
if sym <> lparen then (* 如果read語句后跟的不是左括號 *)
error(34) (* 拋出34號錯誤 *)
else
repeat (* 循環得到read語句括號中的參數表,依次產生相應的“從鍵盤讀入”目標代碼 *)
getsym; (* 獲得一個token,正常應是一個變量名 *)
if sym = ident then (* 如果確為一個標識符 *)
(* 這里略有問題,還應判斷一下這個標識符是不是變量名,如果是常量名或過程名應出錯 *)
i := position(id) (* 查符號表,找到它所在位置給i,找不到時i會為0 *)
else
i := 0; (* 不是標識符則有問題,i置0作為出錯標志 *)
if i = 0 then (* 如果有錯誤 *)
error(35) (* 拋出35號錯誤 *)
else (* 否則生成相應的目標代碼 *)
with table[i] do
begin
gen(opr, 0, 16); (* 生成16號操作指令:從鍵盤讀入數字 *)
gen(sto, lev - level, adr) (* 生成sto指令,把讀入的值存入指定變量所在的空間 *)
end;
getsym (* 獲取下一個token,如果是逗號,則read語還沒完,否則應當是右括號 *)
until sym <> comma; (* 不斷生成代碼直到read語句的參數表中的變量遍歷完為止,這里遇到不是逗號,應為右括號 *)
if sym <> rparen then (* 如果不是我們預想中的右括號 *)
begin
error(33); (* 拋出33號錯誤 *)
while not (sym in fsys) do (* 依靠fsys集,找到下一個合法的token,恢復語法分析 *)
getsym
end
else
getsym (* 如果read語句正常結束,得到下一個token,一般為分號或end *)
end
else
if sym = writesym then (* 如果遇到了write語句 *)
begin
getsym; (* 獲取下一token,應為左括號 *)
if sym = lparen then (* 如確為左括號 *)
begin
repeat (* 依次獲取括號中的每一個值,進行輸出 *)
getsym; (* 獲得一個token,這里應是一個標識符 *)
expression([rparen, comma] + fsys); (* 調用expression過程分析表達式,用于出錯恢復的集合中加上右括號和逗號 *)
gen(opr, 0, 14) (* 生成14號指令:向屏幕輸出 *)
until sym <> comma; (* 循環直到遇到的不再是逗號,這時應是右括號 *)
if sym <> rparen then (* 如果不是右括號 *)
error(33) (* 拋出33號錯誤 *)
else
getsym (* 正常情況下要獲取下一個token,為后面準備好 *)
end;
gen(opr, 0, 15) (* 生成一個15號操作的目標代碼,功能是輸出一個換行 *)
(* 由此可知PL/0中的write語句與Pascal中的writeln語句類似,是帶有輸出換行的 *)
end
else
if sym = callsym then (* 如果是call語句 *)
begin
getsym; (* 獲取token,應是過程名型標識符 *)
if sym <> ident then (* 如果call后跟的不是標識符 *)
error(14) (* 拋出14號錯誤 *)
else
begin
i := position(id); (* 從符號表中找出相應的標識符 *)
if i = 0 then (* 如果沒找到 *)
error(11) (* 拋出11號錯誤 *)
else
with table[i] do (* 如果找到標識符位于符號表第i位置 *)
if kind = procedur then (* 如果這個標識符為一個過程名 *)
gen(cal,lev-level,adr) (* 生成cal目標代碼,呼叫這個過程 *)
else
error(15); (* 如果call后跟的不是過程名,拋出15號錯誤 *)
getsym (* 獲取下一token,為后面作準備 *)
end
end
else
if sym = ifsym then (* 如果是if語句 *)
begin
getsym; (* 獲取一token應是一個邏輯表達式 *)
condition([thensym, dosym] + fsys); (* 對邏輯表達式進行分析計算,出錯恢復集中加入then和do語句 *)
if sym = thensym then (* 表達式后應遇到then語句 *)
getsym (* 獲取then后的token,應是一語句 *)
else
error(16); (* 如果if后沒有then,拋出16號錯誤 *)
cx1 := cx; (* 記下當前代碼分配指針位置 *)
gen(jpc, 0, 0); (* 生成條件跳轉指令,跳轉位置暫時填0,分析完語句后再填寫 *)
statement(fsys); (* 分析then后的語句 *)
code[cx1].a:=cx (* 上一行指令(cx1所指的)的跳轉位置應為當前cx所指位置 *)
end
else
if sym = beginsym then (* 如果遇到begin *)
begin
getsym; (* 獲取下一個token *)
statement([semicolon, endsym] + fsys);(* 對begin與end之間的語句進行分析處理 *)
while sym in [semicolon] + statbegsys do (* 如果分析完一句后遇到分號或語句開始符循環分析下一句語句 *)
begin
if sym = semicolon then (* 如果語句是分號(可能是空語句) *)
getsym (* 獲取下一token繼續分析 *)
else
error(10); (* 如果語句與語句間沒有分號,出10號錯 *)
statement([semicolon, endsym] + fsys) (* 分析一個語句 *)
end;
if sym = endsym then (* 如果語句全分析完了,應該遇到end *)
getsym (* 的確是end,讀下一token *)
else
error(17) (* 如果不是end,拋出17號錯 *)
end
else
if sym = whilesym then (* 如果遇到while語句 *)
begin
cx1 := cx; (* 記下當前代碼分配位置,這是while循環的開始位置 *)
getsym; (* 獲取下一token,應為一邏輯表達式 *)
condition([dosym] + fsys); (* 對這個邏輯表達式進行分析計算 *)
cx2 := cx; (* 記下當前代碼分配位置,這是while的do中的語句的開始位置 *)
gen(jpc, 0, 0); (* 生成條件跳轉指令,跳轉位置暫時填0 *)
if sym = dosym then (* 邏輯表達式后應為do語句 *)
getsym (* 獲取下一token *)
else
error(18); (* if后缺少then,拋出18號錯誤 *)
statement(fsys); (* 分析do后的語句塊 *)
gen(jmp, 0, cx1); (* 循環跳轉到cx1位置,即再次進行邏輯判斷 *)
code[cx2].a := cx (* 把剛才填0的跳轉位置改成當前位置,完成while語句的處理 *)
end;
test(fsys, [], 19) (* 至此一個語句處理完成,一定會遇到fsys集中的符號,如果沒有遇到,就拋19號錯 *)
end(* statement *);
begin (* block *)
dx := 3; (* 地址指示器給出每層局部量當前已分配到的相對位置。
置初始值為3的原因是:每一層最開始的位置有三個空間用于存放靜態鏈SL、動態鏈DL和返回地址RA *)
tx0 := tx; (* 初始符號表指針指向當前層的符號在符號表中的開始位置 *)
table[tx].adr := cx; (* 符號表當前位置記下當前層代碼的開始位置 *)
gen(jmp, 0, 0); (* 產生一行跳轉指令,跳轉位置暫時未知填0 *)
if lev > levmax then (* 如果當前過程嵌套層數大于最大允許的套層數 *)
error(32); (* 發出32號錯誤 *)
repeat (* 開始循環處理源程序中所有的聲明部分 *)
if sym = constsym then (* 如果當前token是const保留字,開始進行常量聲明 *)
begin
getsym; (* 獲取下一個token,正常應為用作常量名的標識符 *)
repeat (* 反復進行常量聲明 *)
constdeclaration; (* 聲明以當前token為標識符的常量 *)
while sym = comma do (* 如果遇到了逗號則反復聲明下一個常量 *)
begin
getsym; (* 獲取下一個token,這里正好應該是標識符 *)
constdeclaration (* 聲明以當前token為標識符的常量 *)
end;
if sym = semicolon then (* 如果常量聲明結束,應遇到分號 *)
getsym (* 獲取下一個token,為下一輪循環做好準備 *)
else
error(5) (* 如果常量聲明語句結束后沒有遇到分號則發出5號錯誤 *)
until sym <> ident (* 如果遇到非標識符,則常量聲明結束 *)
end;
(* 此處的常量聲明的語法與課本上的EBNF范式有不同之處:
它可以接受像下面的聲明方法,而根據課本上的EBNF范式不可得出下面的語法:
const a = 3, b = 3; c = 6; d = 7, e = 8;
即它可以接受分號或逗號隔開的常量聲明,而根據EBNF范式只可接受用逗號隔開的聲明 *)
if sym = varsym then (* 如果當前token是var保留字,開始進行變量聲明,與常量聲明類似 *)
begin
getsym; (* 獲取下一個token,此處正常應為用作變量名的一個標識符 *)
repeat (* 反復進行變量聲明 *)
vardeclaration; (* 以當前token為標識符聲明一個變量 *)
while sym = comma do (* 如果遇到了逗號則反復聲明下一個變量 *)
begin
getsym; (* 獲取下一個token,這里正好應該是標識符 *)
vardeclaration; (* 聲明以當前token為標識符的變量 *)
end;
if sym = semicolon then (* 如果變量聲明結束,應遇到分號 *)
getsym (* 獲取下一個token,為下一輪循環做好準備 *)
else
error(5) (* 如果變量聲明語句結束后沒有遇到分號則發出5號錯誤 *)
until sym <> ident; (* 如果遇到非標識符,則變量聲明結束 *)
(* 這里也存在與上面的常量聲明一樣的毛病:與PL/0的語法規范有沖突。 *)
end;
while sym = procsym do (* 循環聲明各子過程 *)
begin
getsym; (* 獲取下一個token,此處正常應為作為過程名的標識符 *)
if sym = ident then (* 如果token確為標識符 *)
begin
enter(procedur); (* 把這個過程登錄到名字表中 *)
getsym (* 獲取下一個token,正常情況應為分號 *)
end
else
error(4); (* 否則拋出4號錯誤 *)
if sym = semicolon then (* 如果當前token為分號 *)
getsym (* 獲取下一個token,準備進行語法分析的遞歸調用 *)
else
error(5); (* 否則拋出5號錯誤 *)
block(lev + 1, tx, [semicolon] + fsys); (* 遞歸調用語法分析過程,當前層次加一,同時傳遞表頭索引、合法單詞符 *)
if sym = semicolon then (* 遞歸返回后當前token應為遞歸調用時的最后一個end后的分號 *)
begin
getsym; (* 獲取下一個token *)
test(statbegsys + [ident, procsym], fsys, 6); (* 檢查當前token是否合法,不合法則用fsys恢復語法分析同時拋6號錯 *)
end
else
error(5) (* 如果過程聲明后的符號不是分號,拋出5號錯誤 *)
end;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -