編譯原理--03 語法制導翻譯和中間代碼生成複習(清華大學出版社第3版)

前言

目錄
01 文法和語言、詞法分析複習
02 自頂向下、自底向上的LR分析複習
03 語法制導翻譯和中間代碼生成複習
04 符號表、運行時存儲組織和代碼優化複習

第7章 語法制導的語義計算

語義分析是上下文有關的,目前較爲常見的是用屬性文法來描述程序語言語義,並採用語法制導翻譯的方法完成對語法成分的翻譯工作。

屬性文法

屬性 描述文法符號的類型、值等有關的一些信息,它可以被計算或傳遞。

語義動作 指產生式相關聯的指定操作

條件謂詞 指產生式關聯的接受條件,或者根據該條件謂詞決定做什麼語義動作

語義規則集 通常是產生式關聯的一組語義規則,每個語義規則可以是一個語義動作或條件謂詞。

屬性\(att\)可以與某個文法符號\(a\)關聯,用\(a.att\)來表示這種關聯

現有一文法:
\(E\rightarrow T_1 + T_2\mid T_1 \&\& T_2\)
\(T\rightarrow num\mid true\mid false\)

將上面的文法描述爲類型檢查的屬性文法:
\(E\rightarrow T_1 + T_2 \quad \{T_1.type=int\quad\&\&\quad T_2.type=int\}\)
\(E\rightarrow T_1 \&\& T_2\quad\{T_1.type=bool\quad\&\&\quad T_2.type=bool\}\)
\(T\rightarrow num\quad\{T.type=int\}\)
\(T\rightarrow true\quad\{T.type=bool\}\)
\(T\rightarrow false\quad\{T.type=bool\}\)

綜合屬性和繼承屬性

對關聯於產生式\(A\rightarrow \alpha\)的語義動作\(b:=f(c_1, c_2, ..., c_k)\),如果\(b\)是A的某個屬性,則b是A的一個綜合屬性。綜合屬性是自底向上傳遞信息。

對關聯於產生式\(A\rightarrow \alpha\)的語義動作\(b:=f(c_1, c_2, ..., c_k)\),如果\(b\)是產生式右邊某個文法符號X的某個屬性,則b是A的一個繼承屬性。繼承屬性是自頂向下傳遞信息。

帶標註語法分析樹,即在語法樹的基礎上,將原來的非終結符結點修改爲綜合屬性的賦值。

下面是一個簡單表達式文法G[S]的一個僅含綜合屬性的屬性文法(開始符號爲S)
\(S\rightarrow E\quad\{print(E.val)\}\)
\(E\rightarrow E_1+T\quad\{E.val:=E_1.val+T.val\}\)
\(E\rightarrow T\quad\{E.val:=T.val\}\)
\(T\rightarrow T_1*F\quad\{T.val:=T_1.val\times F.val\}\)
\(T\rightarrow F\quad\{T.val:=F.val\}\)
\(F\rightarrow (E)\quad\{F.val:=E.val\}\)
\(F\rightarrow d\quad\{F.val:=d.lexval\}\)

其中\(d.lexval\)表示數值,\(E.val, T.val, F.val\)都爲綜合屬性
現在要給表達式\(3*(5+4)\)構造語法樹和帶標註語法分析樹:

下面則是一個包含綜合屬性、繼承屬性的屬性文法:
\(E\rightarrow TR\quad\{R.in:=T.val;\quad E.val:=R.val\}\)
\(R\rightarrow +TR_1\quad\{R_1.in:=R.in+T.val;\quad R.val:=R_1.val\}\)
\(R\rightarrow -TR_1\quad\{R_1.in:=R.in-T.val;\quad R.val:=R_1.val\}\)
\(R\rightarrow \varepsilon\quad\{R.val := R.in\}\)
\(T\rightarrow num\quad\{T.val := lexval(num)\}\)
其中\(lexval(num)\)表示從詞法分析程序得到的常數值。
可見\(E.val, T.val, R.val\)都爲綜合屬性,\(R.in\)爲繼承屬性
現在要給表達式\(3+4-5\)構造語法樹和帶標註語法分析樹:

這一章可能的考點

  1. 瞭解綜合屬性和繼承屬性。已知屬性文法和輸入符號串,構建語法樹和帶標註語法分析樹。

第8章 靜態語義分析和中間代碼生成

中間代碼生成

中間代碼 一種介於源語言目標語言的中間語言形式,有:

  1. 逆波蘭表示
  2. 三元式表示
  3. 四元式表示
  4. 樹形表示

逆波蘭表示

逆波蘭表示法即爲後綴表示法,而默認我們使用的表達式是中綴表示法

程序設計語言中的表示 逆波蘭表示
\(a+b\) \(ab+\)
\(-a\) \(a@\)
\(a+b*c\) \(abc*+\)
\((a+b)*c\) \(ab+c*\)
\(a:=b*c+b*d\) \(abc*bd+:=\)
\(a:=b*(c+b)*(-d)\) \(bcb+*d@*:=\)

逆波蘭式的使用:需使用額外的標識符棧。順序掃描逆波蘭表達式的時候,遇到標識符直接入棧。

遇到運算符時:

  1. 根據運算符目數,從棧頂取出相應數目的標識符做運算,並把運算結果壓棧
  2. 運算結束時,標識符棧應該只剩下一個元素,且爲運算結果

三元式表示

三元式\((op, A_1, A_2)\)

\(op\)爲運算符
\(A_1\)爲第一運算對象
\(A_2\)爲第二運算對象

例如\(a:=b*c+b*d\)表示爲:
\((1)\quad(*,b,c)\)
\((2)\quad(*,b,d)\)
\((3)\quad(+,(1),(2))\quad\) 這裏用(1)和(2)來表示中間計算結果的顯式引用
\((4)\quad(:=,(3),a)\quad\) 這裏相當於\(a:=(3)\)

而單目運算的\(-b\)可以表示成\((-,b,/)\)

樹形表示

樹形表示和三元式表示非常相似,如\(a:=b*c+b*d\)表示爲:

注意賦值表達式中被賦值對象在樹的左孩子節點位置

單目運算\(-T_1\)直接表示成:

四元式表示

四元式\((op, A_1, A_2,R)\)

\(op\)爲運算符
\(A_1\)爲第一運算對象
\(A_2\)爲第二運算對象
\(R\)爲運算結果

例如\(a:=b*c+b*d\)的四元式表示:
\((1)\quad(*,b,c,t_1)\)
\((2)\quad(*,b,d,t_2)\)
\((3)\quad(+,t_1,t_2,t_3)\)
\((4)\quad(:=,t_3,-,a)\)

和三元式的差別在於,四元式對中間結果的引用必須通過給定的名字(臨時變量)

翻譯

布爾表達式的翻譯

布爾表達式在程序設計語言中有兩個作用:

  1. 計算邏輯值
  2. 用於改變控制流語句中的條件表達式

控制流語句包含循環、分支兩大類。

通常我們只考慮如下文法生成的布爾表達式:
\(E\rightarrow E\; and\; E\mid E\; or\; E\mid not\; E\mid id\; rop\; id\mid id\mid true\mid false\)
其中\(rop\)是關係符,如\(<=, <, =, >, >=\)
布爾運算符的優先順序\(not>and>or\)
並且\(and\)\(not\)服從左結合

布爾表達式的計算有兩種方法:

  1. 計算各部分的真假值,最後計算出整個表達式的值
    \((1\;and\;0)\;and\;(0\;or\;1)=0\;and\;1=0\)
  2. 短路法:\(A\;and\;B\)如果\(A=0\)則直接得到\(0\)\(A\;or\;B\)如果\(A=1\)則直接得到1。這種方式若\(B\)爲一個帶返回值的過程調用會引發副作用

布爾表達式翻譯成四元式序列,如\(a\; or\; b\; and\; not\; c\)的翻譯結果爲:
\((1)\quad t_1 :=\;not\;c\)
\((2)\quad t_2 :=b\;and\;t_1\)
\((3)\quad t_3 :=a\;or\;t_2\)

條件語句中布爾表達式的翻譯

現在有文法:
\(S\rightarrow if\;E\;then\;S1\mid if\;E\;then\;S1\;else\;S2\mid while\;E\;do\;S1\)

翻譯這部分的題目主要是以給定四元式序列,然後填空。

對布爾表達式\(E = a\; rop\; b\),可以翻譯成:
\((1)\quad if\;a\;rop\;b\;goto\; E.true\)
\((2)\quad goto\;E.false\)

但此時\(E.true\)\(E.false\)的值仍不能被確定。例如:
\(S\rightarrow if\;E\;then\;S1\;else\;S2\)

\(E.true\)的值爲\(S1\)的第一條四元式的地址
\(E.false\)的值爲\(S2\)的第一條四元式的地址

\(if\;a<b\;or\;c<d\;and\;e>f\;then\;S1\;else\;S2\)的四元式序列:

\((1)\quad if\;a<b\;goto\; \underline{E.true}\)
\((2)\quad goto\; \underline{(3)}\)
\((3)\quad if\;c<d\;goto\; \underline{(5)}\)
\((4)\quad goto\; \underline{E.false}\)
\((5)\quad if\;e>f\;goto\; \underline{E.true}\)
\((6)\quad goto\;\underline{E.false}\)
\((7)\quad S1\;begin...\)
\(...\)
\((p-1)\quad ...S1\;end\)
\((p)\quad goto \;\underline{q}\)
\((p+1)\quad S2\;begin...\)
\(...\)
\((q-1)\quad ...S2\;end\)
\((q)\quad ...\)

在產生出S1和S2的狀態塊後,才能進行地址回填。上面的\(E.true\)應填\((7)\),而\(E.false\)應填\((p+1)\)

爲了解決地址回填的問題,需要採用拉鍊法,把需要回填\(E.true\)的所有四元式構造並維護一條真鏈的鏈表,把需要回填\(E.false\)的所有四元式構造一條假鏈的鏈表

對於上面的例子
真鏈:\((5)\rightarrow(1)\)
假鏈:\((6)\rightarrow(4)\)
一旦確定S1和S2的地址,就可以沿着鏈作地址回填

但還有3種情況會使得四元式序列變得十分複雜,這裏不討論:

  1. 連續的\(or\)或連續的\(and\)
  2. 連續的\(if-else\;if-else\;if...\)
  3. 嵌套條件語句

循環語句中布爾表達式翻譯

現需要翻譯語句:\(while\;A<B\;do\;if\;C<D\;then\;X:=Y+Z\)

\(100:\quad if\;a<b\;goto\; \underline{E.true}\)
\(101:\quad goto\; \underline{E.false}\)
\(102:\quad if\;c<d\;goto\; \underline{104}\)
\(103:\quad goto\; \underline{106}\)
\(104:\quad t1:=Y+Z\)
\(105:\quad X:=t1\)
\(106:\quad goto\;\underline{100}\)
\(107: \quad...\)

分析到狀態塊的開始就可以確認\(E.true=100\),而分析完狀態塊的結束之後就可以確認\(E.false=107\)

for循環語句翻譯

現需要翻譯語句:\(for\;I\;:=\;1\;step\;1\;until\;Y\;do\;X:=X+1\)
等價於C語言的:\(for(I=1;I<=Y;++I) X=X+1;\)

\(100:\quad I:=1\)
\(101:\quad goto\;\underline{103}\)
\(102:\quad I:=I+1\)
\(103:\quad if\;I<=Y\;goto\; \underline{105}\)
\(104:\quad goto\; \underline{108}\)
\(105:\quad T:=X+1\)
\(106:\quad X:=T\)
\(107:\quad goto\; \underline{102}\)
\(108: \quad...\)

數組的翻譯

對於一個靜態的n維數組,要訪問其中一個元素,可以使用下標法:
\[A[i_1,i_2,...,i_n]\]

由於內存是一維連續空間,對於行主數組,比如一個\(2\times3\)的二維數組,在內存中的佈局爲:
\[A[1,1]\;A[1,2]\;A[1,3]\;A[2,1]\;A[2,2]\;A[2,3]\]

現知道數組\(A\)的地址爲\(a\),那\(A[i,j]\)的地址爲:
\[a+(i-1)\times 3+(j-1)\]

\(B\)爲n維數組\(B[l_1:u_1,l_2:u_2,...,l_n:u_n]\)

顯然\(d_i=u_i-l_i+1\)。令\(b\)是數組首元素地址,那麼行主數組下\(B[i_1,i_2,...,i_n]\)的地址\(D\)爲:
\[D=b+(i_1-l_1)d_2d_3...d_n+(i_2-l_2)d_3...d_n+(i_{n-1}-l_{n-1})d_n+(i_n-l_n)\]

對式子展開可以提取出式子中的固定部分和變化部分:
\(D=constPart+varPart\)
\(constPart=b-C\)
\(C=l_1d_2d_3...d_n+l_2d_3...d_n+...+l_{n-1}d_{n-1}+l_nd_n\)
\(varPart=i_1d_2d_3...d_n+i_2d_3...d_n+...+i_{n-1}d_{n-1}+i_nd_n\)

訪問數組元素\(A[i_1,i_2,...,i_n]\)需要產生兩組計算數組的四元式:

  1. 一組計算\(varPart\),存放結果到臨時單元\(T\)
  2. 一組計算\(constPart\),存放結果到另一個臨時單元\(T1\)

\(T1[T]\)表示數組元素的地址

變址取數的四元式\((=[],T1[T],-,X)\),相當於\(X:=T1[T]\)
變址存數的四元式\(([]=,X1,-,T1[T])\),相當於\(T1[T]:=X\)

現在有一個10*20的數組A,即\(d1=10, d2=20\)。則\(X:=A[I,J]\)的四元式序列爲:
\((*,I,20,T1)\)
\((+,T1,J,T1)\)
\((-,A,21,T2)\)
\((=[], T2[T1], -, T3)\)
\((:=,T3,-,X)\)

對應:
\(varPart=20*I+J\)
\(constPart=A-(1*20+1)=A-21\)

這一章可能的考點

  1. 中間代碼的逆波蘭表示、樹形表示、三元式表示、四元式表示
  2. 翻譯相關,可能會給出代碼進行填空
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章