?? 123.txt
字號:
k := k + 1;
a[k] := ch;
end;
getch (* 讀下一個字符 *)
until not (ch in ['a'..'z','0'..'9']); (* 直到讀出的不是字母或數字,由此可知PL/0的標識符構成規則是:
以字母開頭,后面跟若干個字母或數字 *)
if k >= kk then (* 如果當前獲得的標識符長度大于等于kk *)
kk := k (* 令kk為當前標識符長度 *)
else
repeat (* 這個循環用于把標識符緩沖后部沒有填入相應字母或空格的空間用空格補足 *)
a[kk] := ' ';
kk := kk - 1
until kk = k;
(* 在第一次運行這個過程時,kk的值為al,即最大標識符長度,如果讀到的標識符長度小于kk,
就把a數組的后部沒有字母的空間用空格補足。
這時,kk的值就成為a數組前部非空格字符的個數。以后再運行getsym時,如果讀到的標識符長度大于等于kk,
就把kk的值變成當前標識符的長度。
這時就不必在后面填空格了,因為它的后面肯定全是空格。反之如果最近讀到的標識符長度小于kk,那就需要從kk位置向前,
把超過當前標識長度的空間填滿空格。
以上的這樣一個邏輯,完全是出于程序性能的上考慮。其實完全可以簡單的把a數組中a[k]元素以后的空間不管三七二十一全填空格。
*)
(* 下面開始二分法查找看讀出的標識符是不是保留字之一 *)
id := a; (* 最后讀出標識符等于a *)
i := 1; (* i指向第一個保留字 *)
j := norw; (* j指向最后一個保留字 *)
repeat
k := (i + j) div 2; (* k指向中間一個保留字 *)
if id <= word[k] then (* 如果當前的標識符小于k所指的保留字 *)
j := k - 1; (* 移動j指針 *)
if id >= word[k] then (* 如果當前的標識符大于k所指的保留字 *)
i := k + 1 (* 移動i指針 *)
until i > j; (* 循環直到找完保留字表 *)
if i - 1 > j then (* 如果i - 1 > j表明在保留字表中找到相應的項,id中存的是保留字 *)
sym := wsym[k] (* 找到保留字,把sym置為相應的保留字值 *)
else
sym := ident (* 未找到保留字,把sym置為ident類型,表示是標識符 *)
end(* 至此讀出字符為字母即對保留字或標識符的處理結束 *)
else (* 如果讀出字符不是字母 *)
if ch in ['0'..'9'] then (* 如果讀出字符是數字 *)
begin (* number *) (* 開始對數字進行處理 *)
k := 0; (* 數字位數 *)
num := 0; (* 數字置為0 *)
sym := number; (* 置sym為number,表示這一次讀到的是數字 *)
repeat (* 這個循環依次從源文件中讀出字符,組成數字 *)
num := 10 * num + (ord(ch) - ord('0')); (* num * 10加上最近讀出的字符ASCII減'0'的ASCII得到相應的數值 *)
k := k + 1; (* 數字位數加一 *)
getch
until not (ch in ['0'..'9']); (* 直到讀出的字符不是數字為止 *)
if k > nmax then (* 如果組成的數字位數大于最大允許的數字位數 *)
error(30) (* 發出30號錯 *)
end(* 至此對數字的識別處理結束 *)
else
if ch = ':' then (* 如果讀出的不字母也不是數字而是冒號 *)
begin
getch; (* 再讀一個字符 *)
if ch = '=' then (* 如果讀到的是等號,正好可以與冒號構成賦值號 *)
begin
sym := becomes; (* sym的類型設為賦值號becomes *)
getch (* 再讀出下一個字 *)
end
else
sym := nul; (* 如果不是讀到等號,那單獨的一個冒號就什么也不是 *)
end(* 以上完成對賦值號的處理 *)
else (* 如果讀到不是字母也不是數字也不是冒號 *)
if ch = '<' then (* 如果讀到小于號 *)
begin
getch; (* 再讀一個字符 *)
if ch = '=' then (* 如果讀到等號 *)
begin
sym := leq; (* 購成一個小于等于號 *)
getch (* 讀一個字符 *)
end
else (* 如果小于號后不是跟的等號 *)
sym := lss (* 那就是一個單獨的小于號 *)
end
else (* 如果讀到不是字母也不是數字也不是冒號也不是小于號 *)
if ch = '>' then (* 如果讀到大于號,處理過程類似于處理小于號 *)
begin
getch; (* 再讀一個字符 *)
if ch = '=' then (* 如果讀到等號 *)
begin
sym := geq; (* 購成一個大于等于號 *)
getch (* 讀一個字符 *)
end
else (* 如果大于號后不是跟的等號 *)
sym := gtr (* 那就是一個單獨的大于號 *)
end
else(* 如果讀到不是字母也不是數字也不是冒號也不是小于號也不是大于號 *)
begin (* 那就說明它不是標識符/保留字,也不是復雜的雙字節操作符,應該是一個普通的符號 *)
sym := ssym[ch]; (* 直接成符號表中查到它的類型,賦給sym *)
getch (* 讀下一個字符 *)
end
(* 整個if語句判斷結束 *)
end (* getsym *);
(* 詞法分析過程getsym總結:從源文件中讀出若干有效字符,組成一個token串,識別它的類型
為保留字/標識符/數字或是其它符號。如果是保留字,把sym置成相應的保留字類型,如果是
標識符,把sym置成ident表示是標識符,于此同時,id變量中存放的即為保留字字符串或標識
符名字。如果是數字,把sym置為number,同時num變量中存放該數字的值。如果是其它的操作符,
則直接把sym置成相應類型。經過本過程后ch變量中存放的是下一個即將被識別的字符 *)
(* 目標代碼生成過程gen *)
(* 參數:x:要生成的一行代碼的助記符 *)
(* y, z:代碼的兩個操作數 *)
(* 本過程用于把生成的目標代碼寫入目標代碼數組,供后面的解釋器解釋執行 *)
procedure gen(x: fct; y, z: integer);
begin
if cx > cxmax then (* 如果cx>cxmax表示當前生成的代碼行號大于允許的最大代碼行數 *)
begin
write('program too long'); (* 輸出"程序太長",退出 *)
close(fa);
close(fa1);
close(fin);
halt(0)
{ goto 99 }
(* 我修改的代碼,由于Turbo Pascal 7.0中不允許跨過程的goto,就只能用上面的方法退出程序了。 *)
end;
with code[cx] do (* 把代碼寫入目標代碼數組的當前cx所指位置 *)
begin
f := x;
l := y;
a := z;
end;
cx := cx + 1 (* 移動cx指針指向下一個空位 *)
end (* gen *);
(* 測試當前單詞是否合法過程test *)
(* 參數:s1:當語法分析進入或退出某一語法單元時當前單詞符合應屬于的集合 *)
(* s2:在某一出錯狀態下,可恢復語法分析正常工作的補充單詞集合 *)
(* n:出錯信息編號,當當前符號不屬于合法的s1集合時發出的出錯信息 *)
procedure test(s1, s2: symset; n: integer);
begin
if not (sym in s1) then (* 如果當前符號不在s1中 *)
begin
error(n); (* 發出n號錯誤 *)
s1 := s1 + s2; (* 把s2集合補充進s1集合 *)
while not (sym in s1) do (* 通過循環找到下一個合法的符號,以恢復語法分析工作 *)
getsym
end
end (* test *);
(* 語法分析過程block *)
(* 參數:lev:這一次語法分析所在的層次 *)
(* tx:符號表指針 *)
(* fsys:用于出錯恢復的單詞集合 *)
procedure block(lev, tx: integer; fsys: symset);
var
dx: integer; (* data allocation index *) (* 數據段內存分配指針,指向下一個被分配空間在數據段中的偏移位置 *)
tx0: integer; (* initial table index *) (* 記錄本層開始時符號表位置 *)
cx0: integer; (* initial code index *) (* 記錄本層開始時代碼段分配位置 *)
(* 登陸符號表過程enter *)
(* 參數:k:欲登陸到符號表的符號類型 *)
procedure enter(k: object1);
begin (* enter object into table *)
tx := tx + 1; (* 符號表指針指向一個新的空位 *)
with table[tx] do (* 開始登錄 *)
begin
name := id; (* name是符號的名字,對于標識符,這里就是標識符的名字 *)
kind := k; (* 符號類型,可能是常量、變量或過程名 *)
case k of (* 根據不同的類型進行不同的操作 *)
constant: (* 如果是常量名 *)
begin
if num > amax then (* 在常量的數值大于允許的最大值的情況下 *)
begin
error(31); (* 拋出31號錯誤 *)
num := 0; (* 實際登陸的數字以0代替 *)
end;
val := num (* 如是合法的數值,就登陸到符號表 *)
end;
variable: (* 如果是變量名 *)
begin
level := lev; (* 記下它所屬的層次號 *)
adr := dx; (* 記下它在當前層中的偏移量 *)
dx := dx+1; (* 偏移量自增一,為下一次做好準備 *)
end;
procedur: (* 如果要登陸的是過程名 *)
level := lev (* 記錄下這個過程所在層次 *)
end
end
end (* enter *);
(* 登錄符號過程沒有考慮到重復的定義的問題。如果出現重復定義,則以最后一次的定義為準。 *)
(* 在符號表中查找指定符號所在位置的函數position *)
(* 參數:id:要找的符號 *)
(* 返回值:要找的符號在符號表中的位置,如果找不到就返回0 *)
function position (id: alfa): integer;
var
i: integer;
begin (* find identifier in table *)
table[0].name := id; (* 先把id放入符號表0號位置 *)
i := tx; (* 從符號表中當前位置也即最后一個符號開始找 *)
while table.name <> id do (* 如果當前的符號與要找的不一致 *)
i := i - 1; (* 找前面一個 *)
position := i (* 返回找到的位置號,如果沒找到則一定正好為0 *)
end(* position *);
(* 常量聲明處理過程constdeclaration *)
procedure constdeclaration;
begin
if sym = ident then (* 常量聲明過程開始遇到的第一個符號必然應為標識符 *)
begin
getsym; (* 獲取下一個token *)
if sym in [eql, becomes] then (* 如果是等號或賦值號 *)
begin
if sym = becomes then (* 如果是賦值號(常量生明中應該是等號) *)
error(1); (* 拋出1號錯誤 *)
(* 這里其實自動進行了錯誤糾正使編譯繼續進行,把賦值號當作等號處理 *)
getsym; (* 獲取下一個token,等號或賦值號后應接上數字 *)
if sym = number then (* 如果的確是數字 *)
begin
enter(constant); (* 把這個常量登陸到符號表 *)
getsym (* 獲取下一個token,為后面作準備 *)
end
else
error(2) (* 如果等號后接的不是數字,拋出2號錯誤 *)
end
else
error(3) (* 如果常量標識符后接的不是等號或賦值號,拋出3號錯誤 *)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -