編譯原理第六到七章(知識點學習/期末復習/筆試/面試)

第六章?句法制導翻譯概述

句法制導翻譯概述

什么是句法制導翻譯

編譯的階段:詞法分析→句法分析→語義分析→中間代碼生成→代碼優化→目標代碼生成

語義翻譯:語義分析和中間代碼生成

句法制導翻譯 :句法分析和語義分析和中間代碼生成

句法制導翻譯使用CFG來引導對語言的翻譯, 是一種面向文法的翻譯技術

句法制導翻譯的基本思想

如何表示語義信息?

  • 為CFG中的文法符號設置語義屬性,用來表示語法成分對應的語義信息

如何計算語義屬性?

  • 文法符號的語義屬性值是用與文法符號所在產生式(句法規則)相關聯的語義規則來計算的
  • 對于給定的輸入串x ,構建x的句法分析樹,并利用與產生式(句法規則)相關聯的語義規則來計算分析樹中各結點對應的語義屬性值

兩個概念

將語義規則同句法規則(產生式)聯系起來要涉及兩個概念

  • 句法制導定義(Syntax-Directed Definitions, SDD)
  • 句法制導翻譯方案 (Syntax-Directed Translation Scheme , SDT )

句法制導定義(SDD)

SDD是對CFG的推廣

  • 將每個文法符號和一個語義屬性集合相關聯
  • 將每個產生式和一組語義規則相關聯,這些規則用于計算該產生式中各文法符號的屬性值

如果X是一個文法符號,a是X的一個屬性,則用X.a表示屬性a在某個標號為X的分析樹結點上的值

句法制導翻譯方案(SDT)

SDT是在產生式右部嵌入了程序片段的CFG,這些程序片段稱為語義動作。按照慣例,語義動作放在花括號內

例子

D → T { L.inh = T.type } L

T → int { T.type = int }

T → real { T.type = real }

L → { L1.inh = L.inh }L1, id ?? ? ?

?…

一個語義動作在產生式中的位置決定了這個動作的執行時間

SDD與SDT

SDD

  • 是關于語言翻譯的高層次規格說明
  • 隱蔽了許多具體實現細節,使用戶不必顯式地說明翻譯發生的順序

SDT

  • 可以看作是對SDD的一種補充,是SDD的具體實施方案
  • 顯式地指明了語義規則的計算順序,以便說明某些實現細節

句法制導定義SDD

句法制導定義SDD是對CFG的推廣

  • 將每個文法符號和一個語義屬性集合相關聯
  • 將每個產生式和一組語義規則相關聯,用來計算該產生式中各文法符號的屬性值

文法符號的屬性

  • 綜合屬性 (synthesized attribute)
  • 繼承屬性 (inherited attribute)

綜合屬性(synthesized attribute)

在分析樹結點 N上的非終結符A的綜合屬性只能通過 N的子結點或 N本身的屬性值來定義

例子:

產生式 ? ? ? ? ? ? ? ? ?語義規則

E → E1 + T ? ? ? E.val = E1.val + T.val

終結符可以具有綜合屬性。終結符的綜合屬性值是由詞法分析器提供的詞法值,因此在SDD中沒有計算終結符屬性值的語義規則。

繼承屬性(inherited attribute)

在分析樹結點 N上的非終結符A的繼承屬性只能通過 N的父結點、N的兄弟結點或 N本身的屬性值來定義

例子:

產生式 ? ? ? ? ? ? 語義規則

D → T L ? ? ? L. inh = T. type

終結符沒有繼承屬性。終結符從詞法分析器處獲得的屬性值被歸為綜合屬性值?

例子:

帶有綜合屬性的SDD

SDD:

輸入: 3*5+4n

注釋分析樹 ( Annotated parse tree ) : 每個節點都帶有屬性值的分析樹

帶有繼承屬性L.in的SDD

SDD:

?輸入: real a , b , c

D → T L ? ? ? ? ? ?? (使用規則 1)
T → real? ? ? ? ? ? ?(使用規則 3)
L → L1 , id ? ? ? ? ?(使用規則 4)
L1 → L2 , id ? ? ? ?(再次使用規則 4)
L2 → id ? ? ? ? ? ? ? (使用規則 5)

屬性傳遞過程(也就是“語義規則”的作用)

1?? T → real:

  • T.type = real

2?? D → T L:

  • L.inh = T.type = real

3?? L → L1 , id(c):

  • L1.inh = L.inh = real

  • addtype("c", real)

4?? L1 → L2 , id(b):

  • L2.inh = L1.inh = real

  • addtype("b", real)

5?? L2 → id(a):

  • addtype("a", real)

最終效果:構建符號表

通過這些繼承屬性 + 語義動作,我們完成了:

符號表:
a : real
b : real
c : real

在這個例子中,T.type 是一個綜合屬性(自己決定),L.inh 是繼承屬性(從左邊的 T 傳來的類型信息),所有變量都通過 L.inh 拿到自己的類型,最后用 addtype(...) 加進符號表。

屬性文法 (Attribute Grammar)

一個沒有副作用的SDD有時也稱為屬性文法 (屬性文法的規則僅僅通過其它屬性值和常量來定義一個屬性值)

例子:

副作用指的是:一個操作除了計算返回值之外,還改變了外部狀態(比如內存、符號表、輸出等)。

常見的副作用包括:

  • 改變一個變量的值(寫入內存)

  • 修改符號表

  • 打印輸出(如 print(...)

  • 執行文件或網絡操作

在 SDD(語法制導定義)中,副作用表現在哪?

在 SDD 中:如果一個語義規則只是用來計算屬性值,不對外部狀態造成影響,那就是無副作用的 SDD,也叫屬性文法(Attribute Grammar)

有副作用的 SDD 舉例:

L → id
語義規則:addToSymbolTable(id.lexeme, type)

這個規則調用了 addToSymbolTable(...)修改了符號表 ? 就是有副作用的。

SDD的求值順序

SDD為CFG中的文法符號設置語義屬性。對于給定的輸入串x,應用語義規則計算分析樹中各結點對應的屬性值

按照什么順序計算屬性值? 語義規則建立了屬性之間的依賴關系,在對句法分析樹節點的一個屬性求值之前,必須首先求出這個屬性值所依賴的所有屬性值

依賴圖(Dependency Graph)

依賴圖是一個描述了分析樹中結點屬性間依賴關系的有向圖

分析樹中每個標號為X的結點的每個屬性a都對應著依賴圖中的一個結點

如果屬性X.a的值依賴于屬性Y.b的值,則依賴圖中有一條從Y.b的結點指向X.a的結點的有向邊

例子

?如果一條語義規則的作用和求值無關,如打印一個值或向符號表中添加記錄,則成為生成式左側非終結符的虛擬綜合屬性

常見的虛擬綜合屬性: ? ? ? ?

1)print(any) 打印any; ? ? ? ?

2)addtype(id.entry, type) 在符號表中為符號id添加符號類型(變量類型)type id.entry指明符號id在符號表中的入口

屬性值的計算順序?

可行的求值順序是滿足下列條件的結點序列N1, N2, … , Nk :如果依賴圖中有一條從結點Ni到 Nj 的邊(Ni→Nj), ?那么i < j(即:在節點序列中,Ni 排在Nj 前面

這樣的排序將一個有向圖變成了一個線性排序,這個排序稱為這個圖的拓撲排序(topological sort)

例子

對于只具有綜合屬性的SDD ,可以按照任何自底向上的順序計算它們的值

對于同時具有繼承屬性和綜合屬性的SDD,不能保證存在一個順序來對各個節點上的屬性進行求值?

例子

產生式? ? ? 語義規則 ?

A→ B?? ? ? ? A.s = B.i

? ? ? ? ? ? ? ? ? ?B.i = A.s +1

如果圖中沒有環,那么至少存在一個拓撲排序

S-屬性定義與L-屬性定義

S-屬性定義

僅僅使用綜合屬性的SDD稱為S屬性的SDD,或S-屬性定義、S-SDD

例:

如果一個SDD是S屬性的,可以按照句法分析樹節點的任何自底向上順序來計算它的各個屬性值

S-屬性定義可以在自底向上的句法分析過程中實現

L-屬性定義

L-屬性定義(也稱為L屬性的SDD或L-SDD)的直觀含義:在一個產生式所關聯的各屬性之間,依賴圖的邊可以從左到右,但不能從右到左(因此稱為L屬性的,L是Left的首字母)

L-SDD的正式定義

一個SDD是L-屬性定義,當且僅當它的每個屬性要么是一個綜合屬性,要么是滿足如下條件的繼承屬性:假設存在一個產生式A→X1X2…Xn,其右部符號Xi (1<=?i <=?n)的繼承屬性僅依賴于下列屬性:

  • A的繼承屬性
  • 產生式中Xi左邊的符號 X1, X2, … , Xi-1 的屬性
  • Xi本身的屬性,但Xi 的全部屬性不能在依賴圖中形成環路

每個S-屬性定義都是L-屬性定義

例子:L-SDD

L型 SDD:每個屬性的求值從左往右按順序就能完成,只依賴左邊的兄弟或父節點的信息。

什么是綜合屬性(synthesized attributes

->從子節點計算并“傳回”父節點的屬性值。下往上傳(由子決定父)

什么是繼承屬性(inherited attributes

->是從父節點或左兄弟“傳遞給”當前節點的屬性值。從左往右傳(父兄決定子)

綜合屬性有:T.val、T′.syn、F.val

繼承屬性有:T′.inh、T1′.inh

非L屬性的SDD

L-屬性(L-attributed)SDD 是指:每個繼承屬性的值,只依賴于 父節點左邊的兄弟節點的綜合屬性(syn)

而如果一個 SDD 的屬性依賴了“右邊的兄弟”,那它就 不是 L 屬性的,叫做 非 L 屬性的 SDD,這類在自頂向下翻譯中不適用。

例子:

綜合屬性有:A.s

繼承屬性有:L.i、M.i、R.i、Q.i

句法制導翻譯方案SDT

句法制導翻譯方案(SDT )是在產生式右部中嵌入了程序片段(稱為語義動作)的CFG

SDT可以看作是SDD的具體實施方案 本節主要關注如何使用SDT來實現兩類重要的SDD,因為在這兩種情況下,SDT可在句法分析過程中實現

  • 基本文法可以使用LR分析技術,且SDD是S屬性的
  • 基本文法可以使用LL分析技術,且SDD是L屬性的
分析方式能用的語法文法類型配合的 SDD 類型使用的 SDT 風格
LL 分析器(自頂向下)LL 文法L-屬性 SDD把語義動作插入在產生式右部的中間或前面
LR 分析器(自底向上)LR 文法S-屬性 SDD把語義動作插入在產生式右部的末尾
  • S 屬性(Synthesized)SDD:只有綜合屬性,從子節點傳上來的(適合 LR)

  • L 屬性(Left-attributed)SDD:屬性可以從左兄弟或父節點傳來(適合 LL)

將S-SDD轉換為SDT

將一個S-SDD轉換為SDT的方法:將每個語義動作都放在產生式的最后

例子:

S-屬性定義的SDT 實現

如果一個S-SDD的基本文法可以使用LR分析技術,那么它的SDT可以在LR句法分析過程中實現

歸約發生時執行相應的語義動作

?擴展的LR句法分析棧

在分析棧中使用一個附加的域來存放綜合屬性值

若支持多個屬性:使棧記錄變得足夠大;在棧記錄中存放指針

將語義動作中的抽象定義式改寫成具體可執行的棧操作

例子:

在自底向上句法分析棧中實現桌面計算器

SLR自動機:

輸入: 3*5+4

將L-SDD轉換為SDT

將L-SDD轉換為SDT的規則

  • 將計算某個非終結符號A的繼承屬性的動作插入到產生式右部中緊靠在A的本次出現之前的位置上
  • 將計算一個產生式左部符號的綜合屬性的動作放置在這個產生式右部的最右端

例子

L-SDD

SDT

1) T → F { T′.inh = F.val } T′ { T.val = T′.syn }

2) T′ → *F { T1′.inh = T′.inh×F.val } T1′ { T′.syn = T1′.syn }

3) T′ → ε { T′.syn = T′.inh }

4) F → digit { F.val = digit.lexval }

L-屬性定義的SDT 實現

如果一個L-SDD的基本文法可以使用LL分析技術,那么它的SDT可以在LL或LR句法分析過程中實現

  • 在非遞歸的預測分析過程中進行語義翻譯
  • 在遞歸的預測分析過程中進行語義翻譯
  • 在LR分析過程中進行語義翻譯

在非遞歸的預測分析過程中進行翻譯

擴展句法分析棧

例子

1) T → F { T′.inh = F.val } T′ { T.val = T′.syn }

2) T′ → *F { T1′.inh = T′.inh×F.val } T1′ { T′.syn = T1′.syn }

3) T′ → ε { T′.syn = T′.inh }

4) F → digit { F.val = digit.lexval }

轉為

分析棧中的每一個記錄都對應著一段執行代碼

  • 綜合記錄出棧時,要將綜合屬性值復制給后面特定的語義動作(如果有需要)
  • 變量展開時(即變量本身的記錄出棧時),如果其含有繼承屬性,則要將繼承屬性值復制給后面特定的語義動作(位于即將擴展的產生式右部)

例子

1) T → F {a1:T′.inh=F.val} T′ {a2:T.val=T′.syn}

符號

屬性

執行代碼

F

Fsyn

val

stack[top-1].Fval?=?stack[top].valtop=top-1;

a1

Fval

stack[top-1].inh?=?stack[top].Fvaltop=top-1;

T

inh

根據當前輸入符號選擇產生式進行推導

若選?2):?stack[top+3].Tinh?=stack[top].inh;??top=top+6;

若選?3):?stack[top].Tinh?=stack[top].inh;????

Tsyn

syn

stack[top-1].Tsyn?=?stack[top].syntop=top-1;

a2

Tsyn

stack[top-1].val?=?stack[top].Tsyntop=top-1;

?2) T′ → *F{a3:T1′.inh=T′.inh×F.val}T1′{a4:T′.syn=T1′.syn}

符號

屬性

執行代碼

*

F

Fsyn

val

stack[top-1].Fval?=?stack[top].valtop=top-1;

a3

T?inh;?Fval

stack[top-1].inh?=?stack[top].Tinh×?stack[top].Fvaltop=top-1;

T1

inh

根據當前輸入符號選擇產生式進行推導

若選2):?stack[top+3].Tinh?=?stack[top].inh;??top=top+6;

若選3):?stack[top].Tinh?=?stack[top].inh;????

T1syn

syn

stack[top-1].T1syn?=?stack[top].syntop=top-1;

a4

T1syn

stack[top-1].syn?=?stack[top].T1syntop=top-1;

?3) T′ → ε {a5:T′.syn = T′.inh }

符號

屬性

執行代碼

a5

Tinh

stack[top-1].syn?=?stack[top].Tinh

top=top-1;

?4) F → digit {a6:F.val = digit.lexval}

符號

屬性

執行代碼

digit

lexval

stack[top-1].digitlexval?=?stack[top].lexval;

top=top-1;

a6

digitlexval

stack[top-1].val?=?stack[top].digitlexval;

top=top-1;

輸入: 3 * 5?

在遞歸的預測分析過程中進行翻譯

算法

  • 為每個非終結符A構造一個函數,A的每個繼承屬性對應該函數的一個形參,函數的返回值是A的綜合屬性值。對出現在A產生式中的每個文法符號的每個屬性都設置一個局部變量 ?
  • 非終結符A的代碼根據當前的輸入決定使用哪個產生式

與每個產生式有關的代碼執行如下動作:

從左到右考慮產生式右部的詞法單元、非終結符及語義動作 ?

  • 對于帶有綜合屬性x的詞法單元 X,把x的值保存在局部變量X.x中;然后產生一個匹配 X的調用,并繼續輸入
  • 對于非終結符B,產生一個右部帶有函數調用的賦值語句c := B(b1 , b2 , …, bk),其中, b1 , b2 , …, bk是代表B的繼承屬性的變量,c是代表B的綜合屬性的變量
  • 對于每個動作,將其代碼復制到句法分析器,并把對屬性的引用改為對相應變量的引用

?例子:

SDT

1) T → F { T′.inh = F.val } T′ ?? ?{ T.val = T′.syn }

2) T′ → *F { T1′.inh = T′.inh×F.val } T1′ ?{ T′.syn = T1′.syn }

3) T′ → ε { T′.syn = T′.inh }

4) F → digit { F.val = digit.lexval }

T′syn T′ (token, T′inh)

//為每個非終結符A構造一個函數,A的每個繼承屬性對應該函數的一個形參,函數的返回值是A的綜合屬性值

{ ?

? ? D: Fval, T1′inh, T1′syn

//對出現在A產生式右部中的每個文法符號的每個屬性都設置一個局部變量

? ? if token=“*” ?then

? ? { ?Getnext(token);

?? ? ? Fval=F(token);

?? ? ? T1′inh= T′inh×Fval

?? ? ? Getnext(token);

?? ? ? T1′syn=T1′(token, T1′inh);

?? ? ? T?syn=T1′syn

?? ? ? return T′syn;

? ? ?}

?? ?else if token= “ $” then

? ? ?{ ?

? ? ? T′syn= T′inh

?? ? ? return T′syn;

? ? ?}

//

? ? ?else Error;

}

L-屬性定義的自底向上翻譯

給定一個以LL文法為基礎的L-SDD,可以修改這個文法,并在LR句法分析過程中計算這個新文法之上的SDD

( 原本設計為 LL 分析 + L-屬性 SDD 的程序,只要重寫文法 + 插入中間動作(@pass)
就可以變成適用于 LR 分析的 SDT 實現方式。)

概念解釋
LL 文法左到右,自頂向下,預測式分析(用棧+遞歸下降)
LR 文法左到右,自底向上,歸約式分析(更強大但更復雜)
L-屬性 SDD屬性可以依賴于父親和左邊兄弟的信息(適合 LL 分析)
S-屬性 SDD屬性只能從孩子傳上來(適合 LR 分析)

?例子

1) T → F { T′.inh = F.val } T′ { T.val = T′.syn }

2) T′ → *F { T1′.inh = T′.inh×F.val } T1′ { T′.syn = T1′.syn }

3) T′ → ε { T′.syn = T′.inh }

4) F → digit { F.val = digit .lexval }

這是一個典型的 LL 分析 + L 屬性 SDD 的寫法:

繼承屬性T′.inh、T1′.inh
綜合屬性F.val、T′.syn、T.val 等

重點問題是:

  • 繼承屬性 T′.inh = F.val要在處理 T′ 之前就先計算的

  • 所以只能在 LL 分析中執行

LR 分析器是歸約式,不能在中間執行動作,所以我們引入中間產生式來提前執行語義動作

變成:修改后的SDT,所有語義動作都位于產生式末尾

1) T → F M T′ ? ? ? ? ? ? { T.val = T′.syn }

2) M → ε ? ? ? ? ? ? ? ? ?{ M.i = F.val; M.s = M.i }
3) T′ → * F N T1′ ? ? ? ? { T′.syn = T1′.syn }
4) N → ε ? ? ? ? ? ? ? ? ?{ N.i1 = T′.inh; N.i2 = F.val; N.s = N.i1 × N.i2 }
5) T′ → ε ? ? ? ? ? ? ? ? { T′.syn = T′.inh }
6) F → digit ? ? ? ? ? ? ?{ F.val = digit.lexval }

T → F M T′ ? ? ? ? ? ? ? { T.val = T′.syn }
M → ε ? ? ? ? ? ? ? ? ? { M.i = F.val; M.s = M.i }

  • 引入了中間符號 M,讓 M → ε 的動作在 F 之后、T′ 之前執行

  • 利用 M 計算 F.val,保存起來供 T′ 使用

  • 在 LR 歸約中,我們可以先歸約 F、然后歸約 M,就能保證 F.valT′ 用之前被計算

T′ → * F N T1′ ? ? ? ? ?{ T′.syn = T1′.syn }
N → ε ? ? ? ? ? ? ? ? ? { N.i1 = T′.inh; N.i2 = F.val; N.s = N.i1 × N.i2 }

  • 插入了 N → ε,把中間繼承計算提早執行

  • N.s 實際上就是 T1′.inh

  • 如果你愿意,后續還可以寫 T1′.inh = N.s,或者直接把 N.s傳給 T1′

T′ → ε ? ? ? ? ? ? ? ? ?{ T′.syn = T′.inh }
F → digit ? ? ? ? ? ? ? { F.val = digit.lexval }

原來現在為什么要改
T′.inh = F.valM → ε { M.i = F.val }提前在 LR 中執行語義動作
T1′.inh = T′.inh × F.valN → ε { 計算 N.s }提前傳值給 T1′
繼承屬性(inh)臨時變量中間保存LR 無法中途插入動作

我們要把SDT 中的語義動作 {} 替換成文法中的“標記非終結符”(dummy symbol),這樣就可以用 LR 分析器來處理這些語義動作了

把這種形式A → α {a} β

變成這種形式:

A → α M β
M → ε ? // 把原來的動作 a 放到 M 這個產生式里去

  • 首先構造SDT,在各個非終結符之前放置語義動作來計算它的繼承屬性, 并在產生式后端放置語義動作計算綜合屬性
  • 每當想計算一個繼承屬性,就把語義動作 {} 放在它前面

  • 每當想計算一個綜合屬性,就把語義動作 {} 放在它后面

  • 對每個內嵌的語義動作,向文法中引入一個標記非終結符來替換它。每個這樣的位置都有一個不同的標記,并且對于任意一個標記M都有一個產生式M→ε

例如這條產生式:T → F { T′.inh = F.val } T′ { T.val = T′.syn }

我們就加兩個標記符號 M1M2,變成:T → F M1 T′ M2

然后加兩個新產生式:

M1 → ε ? ? ? // 執行 { T′.inh = F.val }
M2 → ε ? ? ? // 執行 { T.val = T′.syn }

  • 每個 {} 都變成不同的 M1, M2, M3...

  • 每個 Mi 都是一個產生式 Mi → ε,但它背后執行的是原來的動作

  • 如果標記非終結符M在某個產生式A→α{a}β中替換了語義動作a,對a進行修改得到a' ,并且將a'關聯到M→ε 上。動作a'
  • {a} 拆出去,扔到 M → ε 里去執行

  • a' 其實和 a 做的是一回事,只是你要明確它用到哪些繼承/綜合屬性

  • (a) 將動作a需要的A或α中符號的任何屬性作為M的繼承屬性進行復制
  • 假如原來寫的是:T′.inh = F.val

  • 那現在 M1 → ε 這個動作,就要通過 M1.i = F.val 來模擬繼承傳值

  • 換句話說:把 F.val 傳給 M1 的繼承屬性

  • (b) 按照a中的方法計算各個屬性,但是將計算得到的這些屬性作為M的綜合屬性
  • 原來 { T′.inh = F.val } 是在產生式中直接執行

  • 現在你要在 M1 → ε 這個動作中執行它

  • 所以結果比如 M1.s = F.val(綜合屬性),后續由 M1.s 傳給 T′.inh

將語義動作改寫為可執行的棧操作

例子:

1) T → F M T′ ? ? ? ? ? ? { T.val = T′.syn }

2) M → ε ? ? ? ? ? ? ? ? ?{ M.i = F.val; M.s = M.i }

3) T′ → * F N T1′ ? ? ? ? { T′.syn = T1′.syn }

4) N → ε ? ? ? ? ? ? ? ? ?{ N.i1 = T′.inh; N.i2 = F.val; N.s = N.i1 × N.i2 }

5) T′ → ε ? ? ? ? ? ? ? ? { T′.syn = T′.inh }

6) F → digit ? ? ? ? ? ? ?{ F.val = digit.lexval }

1)

T→F M T′ {stack[top-2]. val = stack[top].syn; top = top-2;} ? ? ?

M→ ε {stack[top+1]. s = stack[top].val; top = top+1;} ?? ?

2)

T′→*F N T1′ {stack[top-3]. syn = stack[top].syn; top = top-3;}

N → ε {stack[top+1]. s = stack[top-2]. s × stack[top].val; top = top+1;}

3)

T′→ε{stack[top+1].syn = stack[top]. s; top = top+1;}

4)

F →digit {stack[top].val = stack[top]. lexval;}

例子

第七章 中間代碼生成

類型表達式

基本類型是類型表達式

  • integer
  • real
  • char
  • boolean
  • type_error (出錯類型)
  • void (無類型)

可以為類型表達式命名類型名也是類型表達式

類型構造符(type constructor)作用于類型表達式可以構成新的類型表達式

例如:

(1)數組構造符array

若T是類型表達式,則array ( I, T )是類型表達式( I是一個整數)

類型

類型表達式

int?[3]

array?(3,?int?)

int?[2][3]

array?(2,?array(3,int)?)?

?(2)指針構造符pointer

若T 是類型表達式,則 pointer ( T ) 是類型表達式,它表示一個指針類型

(3)笛卡爾乘積構造符×

若T1 和T2是類型表達式,則笛卡爾乘積T1 × T2 是類型表達式

(4)函數構造符

若T1、T2、…、Tn 和R是類型表達式,則T1×T2 ×…×Tn→ R是類型表達式

(5)記錄構造符record

若有標識符N1 、N2 、…、Nn 與類型表達式T1 、T2 、…、Tn , 則 ? ? record ( ( N1 ?× T1 ) × ( N2 × T2 )× …× ( Nn × Tn )) 是一個類型表達式

例子

設有C程序片段

struct ? stype ?? ??? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

{ ? char[8] name;

? ??int ?score; ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

}; ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

stype[50] table; ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

stype* p;

  • 和stype綁定的類型表達式:record ( (name× array(8, char)) × (score × integer) )
  • 和table綁定的類型表達式 :array (50, stype)
  • 和p綁定的類型表達式 :pointer (stype)

聲明語句的翻譯

局部變量的存儲分配

對于聲明語句,語義分析的主要任務就是收集標識符的類型等屬性信息,并為每一個名字分配一個相對地址

  • 從類型表達式可以知道該類型在運行時刻所需的存儲單元數量稱為類型的寬度(width)
  • 在編譯時刻,可以使用類型的寬度為每一個名字分配一個相對地址

名字的類型相對地址信息保存在相應的符號表記錄中

變量聲明語句的SDT

enter( name, type, offset ):在符號表中為名字name創建記錄,將name的類型設置為type,相對地址設置為offset

例子

① P →{ offset = 0 } D

② D → T id;{ enter( id.lexeme, T.type, offset ); offset = offset + T.width; }D

③ D → ε

④ T → B ? { t =B.type; w=B.width;}? C ? { T.type = C.type; T.width = C.width; }

⑤ T → ↑T1{ T.type = pointer( T1.type); T.width = 4; }

⑥ B → int { B.type = int; B.width = 4; }

⑦ B → real{ B.type = real; B.width = 8; }

⑧ C → ε ?{ C.type=t; C.width=w; }

⑨ C → [num]C1 { C.type = array( num.val, C1.type);? ?C.width = num.val * C1.width; }

符號

綜合屬性

B?

type,?width

C

type,?width

T

type,?width

變量

作用

offset

下一個可用的相對地址

t,?w

將類型和寬度信息從語法分析樹中的B結點傳遞到對應于產生式C?ε的結點

?“real x; int i;”的語法制導翻譯

?類型表達式“int[2][3]”的語法制導翻譯

中間代碼的形式

四元式是一種“三地址語句”的等價表示。它的一般形式為:(op,arg1,arg2,result)

其中,op為一個二元(也可是一元或零元)運算符; arg1,arg2分別為它的兩個運算對象,它們可以是變量、常數或系統定義的臨時變量名;運算的結果將放入result中。

四元式還可寫為類似于PASCAL語言的賦值語句的形式:result := arg1 op arg2

四元式的格式

每個四元式只能有一個運算符,所以,一個復雜的表達式只能由多個四元式構成的序列表示。

例如,表達式A+B*C可寫為序列 ?? ?

T1:=B*C ??

T2:=A+T1

當op為一元、零元運算(如無條件轉移),arg2甚至arg1應缺省,即result:=op arg1或 op result ;對應的一般形式為:? (op,arg1,-,result) 或? (op,-,-,result) 。

三元式的格式

為了節省臨時變量的開銷,有時也可采用一種三元式結構來作為中間代碼,其一般形式為

(i) (op,arg1,arg2) ?? ?

其中,(i)為三元式的編號,也代表了該式的運算結果;op,arg1,arg2的含義與四元式類似,區別在于arg可以是某三元式的序號,表示用該三元式的結果作為運算對象

例如,對于賦值語句a:=-b*(c+d),若用三元式表示,則可寫成 ?? ???

?①?? ??? ?(U_minus, ?b, ? - ?)? ? ? ?

?②?? ??? ?( ?+ ? , ? ? c, ? ?d ?) ?? ??? ?

③?? ??? ?( ?* ? ?, ? ? ①, ?② ) ?? ??? ?

④?? ??? ?( ?:= ?,?? ? ?③,?? ?a ?)

式①中的運算符U_minus表示一元減運算。

逆波蘭表示

每一運算符都置于其運算對象之后,故稱為后綴表示

特點:表達式中各個運算是按運算符出現的順序進行的,無須使用括號來指示運算順序,因而又稱為無括號式

例子

中綴表示?? ??? ??? ? ??后綴表示

A+B?? ??? ??? ??? ? ? ? ? AB+

A+B*C?? ??? ??? ? ? ? ? ABC*+

(A+B)*(C+D)?? ??? ? ?AB+CD+

* x/y^z-d*e?? ??? ? ? ? ?xyz^/de*-

運算符含義優先級結合性
^最高右結合
* /乘除中等左結合
+ -加減最低左結合

?例子:

中綴:x / y ^ z - d * e

最終后綴表達式為:x y z ^ / d e * -

當前位置操作棧(top→bottom)輸出結果
x輸出x
/壓棧/x
y輸出/x y
^壓棧(比 / 高)^ /x y
z輸出^ /x y z
-比較棧頂 ^ → 彈出 ^/x y z ^
比較 - vs / → 彈出 /x y z ^ /
壓入 --x y z ^ /
d輸出-x y z ^ / d
*優先級比 - 高,壓棧* -x y z ^ / d
e輸出* -x y z ^ / d e
END彈出 *, -x y z ^ / d e * -

簡單賦值語句的翻譯

賦值語句翻譯的任務

賦值語句的基本文法

① S → id = E;

② E → E1 + E2 ?

③ E → E1 * E2 ?

④ E →?-E1 ?? ? ?

⑤ E → (E1) ?

⑥ E → id

賦值語句翻譯的主要任務:生成對表達式求值的三地址碼

例子:

源程序片段 x = ( a + b ) * c ;

三地址碼 t1 = a + b;t2 = t1 * c;x ?= t2

賦值語句的SDT

lookup(name):查詢符號表返回name 對應的記錄

gen(code):生成三地址指令code

newtemp( ):生成一個新的臨時變量t,返回t的地址

例子:a = b + c * d;

生成:

t1 = c * d
t2 = b + t1
a = t2

S → id = E;

{ p = lookup(id.lexeme); if p == nil then error ;? S.code = E.code ||? ?gen( p ‘=’ E.addr ); }

這里的 || 不是邏輯或運算符,而是表示“代碼的連接/拼接”,意思是把兩段中間代碼拼成一段完整的中間代碼。(把表達式 E 的代碼,接在前面,再加上一句 p = E.addr,合成 S.code

  • id = E 是賦值語句,例如 a = b + c;

  • lookup(id.lexeme) 查找變量 a 是否已聲明

  • 如果 a 沒有聲明 → 報錯

  • E.code 是表達式右邊的代碼(可能是好幾條)

  • E.addr 是表達式的最終結果(臨時變量或變量地址)

  • gen(p = E.addr) → 生成最終賦值語句:a = t2

E → E1 + E2

{ E.addr = newtemp( );? ? E.code = E1.code || E2.code||? ?gen(E.addr ‘=’ E1.addr ‘+’ E2.addr); }

  • 生成新臨時變量 t

  • 拼接左右兩邊的中間代碼 E1.code || E2.code

  • 然后再生成一條新代碼:t = E1 + E2

  • 最后把結果地址保存在 E.addr

b + t1
→ t2 = b + t1

E → E1 * E2

{ E.addr = newtemp( );? ? ?E.code = E1.code || E2.code ||? gen(E.addr ‘=’ E1.addr ‘*’ E2.addr); }

c * d
→ t1 = c * d

E → -E1 ??

?{ E.addr = newtemp( );? E.code = E1.code||? ?gen(E.addr ‘=’ ‘uminus’ E1.addr); }

E → (E1)

{ E.addr = E1.addr;? E.code= E1.code;}

E → id ? ?? ?

{ E.addr = lookup(id.lexeme); if E.addr == nil then error ;? ? E.code = ‘’;}

增量翻譯 (Incremental Translation)

在增量方法中,gen( )不僅要構造出一個新的三地址指令,還要將它添加到至今為止已生成的指令序列之后

例子

例:x = ( a + b ) * c ;?

數組引用的翻譯

賦值語句的基本文法 ?? ?

S id = E; | L = E; ?? ?

E E1 + E2 | -E1 ? | (E1) | id | L ??

?L id [E] | L1 [E]

數組引用翻譯成三地址碼時要解決的主要問題是確定數組元素的存放地址,也就是數組元素的尋址

數組元素尋址 (Addressing Array Elements )

一維數組

假設每個數組元素的寬度是w,則數組元素a[i]的相對地址是: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

base+i×w ? ?

其中,base是數組的基地址,i×w是偏移地址

二維數組

假設一行的寬度是w1,同一行中每個數組元素的寬度是w2,則數組元素a[i1] [i2]的相對地址是: ?

base+i1×w1+i2×w2

k維數組

數組元素a[i1] [i2] …[ik]的相對地址是: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

?base+i1×w1+i2×w2+…+ik×wk

w1 →a[i1] 的寬度;w2 →a[i1] [i2] 的寬度;… wk →a[i1] [i2] …[ik]的寬度

例子

假設type(a)= array(3, array(5, array(8, int) ) ), 一個整型變量占用4個字節, ? ?

則addr(a[i1][i2][i3]) = base + i1 * w1 ?+ i2*w2 + i3*w3? = base + i1*160 + i2*32 + i3*4

a[i1]的寬度:160;a[i1][i2]的寬度:32;a[i1][i2][i3]的寬度:4;

帶有數組引用的賦值語句的翻譯

例子1

假設 type(a)=array(n, int)

?源程序片段 ?c = a[i];

三地址碼

?t1 ?= i * 4 ? ?

?t2 ?= a [ t1 ] ?

c ? = t2

addr(a[i]) = base + i*4

例子2

假設 type(a)= array(3, array(5, int)),

源程序片段 ?c =a[i1][i2];

三地址碼

t1 = i1 * 20 ? ? ? ?

t2 = i2 * 4 ? ? ? ?

t3 = t1 + t2 ? ? ? ?

t4 ?= a [ t3 ] ? ? ? ?

c ? = ?t4

addr(a[i1][i2])= base + i1*20 + i2*4

數組引用的SDT

賦值語句的基本文法 ?? ?

S → id = E; | L = E; ?? ?

E → E1 + E2 | -E1 | (E1) | id | L ?? ?

L → id [E] | L1 [E]

?base+i1×w1+i2×w2+…+ik×wk

假設 type(a)= array(3, array(5, array(8, int) ) ),翻譯語句片段“a[i1][i2][i3]”

L的綜合屬性:

  • L.type:L生成的數組元素的類型
  • L.offset:指示一個臨時變量,該臨時變量用于累加公式中的ij × wj項,從而計算數組引用的偏移量
  • L.array:數組名在符號表的入口地址

三地址碼 :t1 = i1*160 ;t2 = i2*32; t3 = t1+t2; t4 = i3*4; t5 = t3+t4 ;t6 = a[t5]

控制流語句及其SDT

控制流語句的基本文法

P → S

S →S1 S2

S →id = E ; | L = E ;?? ? ?

S → if B then S1

?? ??? ?| if B then S1 else S2

?? ??? ?| while B do S1

控制流語句的代碼結構

例子:S → if B then S1 else S2

布爾表達式B被翻譯成由跳轉指令構成的跳轉代碼

繼承屬性

  • S.next:是一個地址,該地址中存放了緊跟在S代碼之后的指令(S的后繼指令)的標號
  • B.true:是一個地址,該地址中存放了當B為真時控制流轉向的指令的標號
  • B.false:是一個地址,該地址中存放了當B為假時控制流轉向的指令的標號

用指令的標號標識一條三地址指令

控制流語句的SDT

newlabel( ): 生成一個用于存放標號的新的臨時變量L,返回變量地址

label(L): 將下一條三地址指令的標號賦給L

P → { S.next = newlabel(); }S{ label(S.next);}

S →{ S1.next = newlabel(); }S1 { label(S1.next); S2.next= S.next ; }S2

S →id = E ; | L = E ;?? ? ?

S → if B then S1

?? ??? ?| if B then S1 else S2

?? ??? ?| while B do S1

if-then-else語句的SDT

S → if B then S1 else S2

S → if ?{ B.true = newlabel(); B.false = newlabel(); }?B

? ? ? ? then { label(B.true); S1.next = S.next; }?S1?{ gen( ‘goto’ ?S.next ) }

? ? ? ? else { label(B.false); S2.next = S.next; }S2

if-then語句的SDT

S → if B then S1

S → if { B.true = newlabel(); B.false = S.next; } B

? ? ? ? then { label(B.true); S1.next = S.next; } S1

?while-do語句的SDT

S →while B do S1

S →while ?{S.begin = newlabel(); ?label(S.begin); ?B.true = newlabel(); ??B.false = S.next;}B

? ? ? ?do { label(B.true); S1.next = S.begin;} S1? { gen(‘goto’ S.begin); }

布爾表達式及其SDT

?? ?B → B or B

?? ??? ? ? | B and B

?? ??? ? ? | not B

?? ??? ? ? | (B)

?? ??? ? ? | E relop E

?? ??? ? ? | true

?? ??? ? ? | false

優先級:not > and > or

E relop E 是關系表達式

relop(關系運算符): <, ?<=, >, ?>=,==, ?! =

跳轉代碼中,邏輯運算符&&、|| 和 ! 被翻譯成跳轉指令。運算符本身不出現在代碼中,布爾表達式的值是通過代碼序列中的位置來表示的

例子:

語句 : if ( x<100 || x>200 && x!=y ) ?? x=0;

三地址代碼:

? ? ? ? ? ?if x<100 goto L2

? ? ? ? ? ?goto L3

L3 : ?? ?if x>200 goto L4

? ? ? ? ? ?goto L1

L4 : ?? ?if x!=y goto L2 ??

? ? ? ? ? ? goto L1 ? ?

L2 :?? ?x=0 ? ? ?

L1 :

布爾表達式的SDT

B → E1 relop E2{ gen(‘if ’ E1.addr relop E2.addr ‘goto’ ?B.true);gen(‘goto’ B.false); }? ??

B → true ?{ gen(‘goto’ B.true); } ?

B → false ?{ gen(‘goto’ B.false); } ? ? ?

B → ( {B1.true =B. true; ?B1.false =B.false; } B1 )

B → not { B1.true = B.false; B1.false = B.true; } B1 ? ?

B → B1 or B2 ?的SDT

B → ?{ B1.true = B.true; ?B1.false = newlabel(); } B1

? ? ? ?or { label(B1.false); ?B2.true = B.true; ?B2.false = B.false; }B2 ?

B → B1 or B2 ?

B → B1 and B2 ?的SDT

B → B1 and B2 ?

B → { B1.true = newlabel(); B1.false = B.false; }? B1

? ? ? ? ?and { label(B1.true); B2.true = B.true; B2.false = B.false; }B2?

控制流翻譯的例子

控制流語句的SDT

P → {a}S{a}

S → {a}S1{a}S2

S → id=E;{a} | L=E;{a}?? ? ?

E → E1+E2{a} | -E1{a} | (E1){a}| id{a}| L{a}

L → id[E]{a} | L1[E]{a}

S → if {a}B then {a}S1

? ? ? ? ? | if {a}B then {a}S1 else {a}S2

? ? ? ? ? | while {a}B do {a}S1{a}

B → {a}B or {a}B | {a}B and {a}B | not {a}B | ({a}B)

? ? ? ? ? | E relop E{a} | true{a} | false{a}

SDT的通用實現方法

任何SDT都可以通過下面的方法實現

首先建立一棵語法分析樹,然后按照從左到右的深度優先順序來執行這些動作

例子

?語句“while a<b do if c<d then x=y+z ?else x=y-z”的三地址代碼

1: if a < b ?goto 3? ? ? ? ? ? ? ? ? 1: ( j<, ?a, ?b , ?3 )

2: goto 11? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2: ( ?j , ?- , ?- , 11 )

3: if c < d ?goto 5? ? ? ? ? ? ? ? ? 3: ( j<, ?c, ?d , ?5 )

4: goto 8? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?4: ( ?j , ?- , ?- , ?8 )

5: t1 = y + z? ? ? ? ? ? ? ? ? ? ? ? ? 5: ( + , ?y , z , ?t1 ?)?

6: x = t1? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 6: ( = , ?t1, ?- , ?x )

7: goto 1? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?7: ( ?j , ?- , ?- , ?1 )

8: t2 = y - z? ? ? ? ? ? ? ? ? ? ? ? ? ?8: ( ?- , ?y, ?z , ?t2 )

9: x = t2? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?9: ( = , ?t2, ?- , ?x )

10: goto 1? ? ? ? ? ? ? ? ? ? ? ? ? ? ?10: ( ?j , ?- , ?- , ?1 )

11:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 11:?

布爾表達式的回填

回填

基本思想:生成一個跳轉指令時,暫時不指定該跳轉指令的目標標號。這樣的指令都被放入由跳轉指令組成的列表中。同一個列表中的所有跳轉指令具有相同的目標標號。等到能夠確定正確的目標標號時,才去填充這些指令的目標標號。

非終結符B的綜合屬性

B.truelist:指向一個包含跳轉指令的列表,這些指令最終獲得的目標標號就是當B為真時控制流應該轉向的指令的標號

B.falselist:指向一個包含跳轉指令的列表,這些指令最終獲得的目標標號就是當B為假時控制流應該轉向的指令的標號

函數

  • makelist( i ) 創建一個只包含i的列表,i是跳轉指令的標號,函數返回指向新創建的列表的指針
  • merge( p1, p2 ) 將 p1 和 p2 指向的列表進行合并,返回指向合并后的列表的指針
  • backpatch( p, i ) 將 i 作為目標標號插入到 p所指列表中的各指令中

布爾表達式的回填

B E1 relop E2

B→ E1 relop E2 {

??? ?B.truelist = makelist(nextquad);

? ? ?B.falselist = makelist(nextquad+1);

??? ?gen(‘if ’ E1.addr relop E2.addr ‘goto _’);

??? ?gen(‘goto _’);

}

這段代碼是為了給布爾表達式生成“如果為真/為假跳到哪里”的中間代碼,它用的是延遲填充跳轉地址(回填技術)。你可以先生成跳轉語句,把地址留空,后面再統一填入真實目標。

表達式是:B -> E1 relop E2

表示布爾表達式 B 是兩個表達式 E1 和 E2 之間的關系運算(例如 <, ==, >= 等)。

B.truelist = makelist(nextquad);

  • B.truelist:布爾表達式為真的跳轉目標位置列表。
  • makelist(nextquad):創建一個列表,里面是當前要生成的下一條指令的編號(因為這條指令就是當表達式為真時跳轉的地方)。
  • 作用:先占個位置,等確定真正跳轉到哪時再回填。

B.falselist = makelist(nextquad+1);

  • 同理,這里是創建布爾表達式為假時的跳轉列表,是下一條指令之后的那條(即下一條是if,再下一條是goto)。
  • 也是先占位,等以后知道“跳哪里”再填進去。

gen(‘if ’ E1.addr relop E2.addr ‘goto _’);

生成條件跳轉語句,例如:if a < b goto _?

下劃線 _ 表示“回填位置”,現在不知道跳到哪里,就先留空。

gen(‘goto _’);

如果條件不滿足,就執行這個跳轉,也要回填:goto _

整體例子:

假設我們要處理:a < b

生成如下偽代碼:

(100) if a < b goto _
(101) goto _

此時:

B.truelist = [100]

B.falselist = [101]

等你知道應該跳到哪兒時,比如:如果為真跳到 120 行,為假跳到 130 行,你就把 _ 回填成:

(100) if a < b goto 120

(101) goto 130

?B → true

B true { ?? ?

? ? ?B.truelist = makelist(nextquad); ??? ?

? ? ?gen(‘goto _’);

}

?B → false

?B false {

?? ?B.falselist = makelist(nextquad);

??? ?gen(‘goto _’);

}

?B →(B1)

B (B1) {

?? ?B.truelist = B1.truelist ;

??? B.falselist = B1.falselist ;

}

B→ not B1?

?B not B1 {

?? ?B.truelist = B1.falselist;

??? B.falselist = B1.truelist;

}

B → B1 or B2

?B B1 or M B2 {

??? ?backpatch(B1.falselist, M.quad ); ?

?? ?B.truelist= merge(B1.truelist,B2.truelist );

??? ?B.falselist = B2.falselist ;

}

M ε {?

? ?M.quad = nextquad ;

}

?B→ B1 and B2

B B1 and M B2 {

??? ?backpatch( B1.truelist, M.quad ); ?

?? ?B.truelist = B2.truelist;

??? ?B.falselist = merge( B1.falselist, B2.falselist );

}

?例子

B ?E1 relop E2 {

?? ?B.truelist = makelist(nextquad);

?? ?B.falselist = makelist(nextquad+1); ?

?? ?gen(‘if ’ E1.addr relop E2.addr ‘goto _’); ??

? ?gen(‘goto _’);

}

?100: if a<b goto _

101: goto _

?B B1 or M B2 {

??? ?backpatch(B1.falselist, M.quad ); ?

?? ?B.truelist= merge(B1.truelist,B2.truelist );

??? ?B.falselist = B2.falselist ;

}

M ε {?

? ?M.quad = nextquad ;

}

100: if a<b goto _

101: goto _

102: if c<d goto _

103: goto _

?B B1 and M B2 {

??? ?backpatch( B1.truelist, M.quad ); ?

?? ?B.truelist = B2.truelist;

??? ?B.falselist = merge( B1.falselist, B2.falselist );

}

100: if a<b goto _

101: goto _

102: if c<d goto _

103: goto _

104: if e<f goto _

105: goto _

?B B1 or M B2 {

??? ?backpatch(B1.falselist, M.quad ); ?

?? ?B.truelist= merge(B1.truelist,B2.truelist );

??? ?B.falselist = B2.falselist ;

}

控制流語句的回填

文法:

S S1 S2

S id = E ; | L = E ;?? ? ?

S if B then S1

? ? ? | if B then S1 else S2

? ? ? | while B do S1

綜合屬性

S.next1ist:指向一個包含跳轉指令的列表,這些指令最終獲得的目標標號就是按照運行順序緊跟在S代碼之后的指令的標號

S → if B then S1

S if B then M S1 {

??? ?backpatch(B.truelist, M.quad); ?

? ? S.nextlist=merge(B.falselist, S1.nextlist);

}

?S → if B then S1 else S2

S if B then M1 S1 N else M2 S2 {

?? ?backpatch(B.truelist, M1.quad);

??? ?backpatch(B.falselist, M2.quad); ?

?? ?S.nextlist = merge( merge(S1.nextlist,? N.nextlist), S2.nextlist );

}

N ε ? {

? ?N.nextlist = makelist(nextquad);

? ?gen(‘goto _’);

}

S → while B do S1

S →?while M1 B do M2 S1 {

?? ?backpatch( S1.nextlist, M1.quad );

??? backpatch( B.truelist, M2.quad );

??? S.nextlist = B.falselist;

??? ?gen(‘goto’ M1.quad);

}

?S → S1 S2

S S1 M S2 {

?? ?backpatch( S1.nextlist, ?M.quad );

??? S.nextlist = S2.nextlist ;

}

?S → id = E ; | L = E ;

S→ id = E ; | L = E ;{ S.nextlist = null; }?

例子

while ?a < b ?do

?? ???if ?c < 5 ?then

?? ??? ? ?while x > y do z = x + 1;

? ? ? else

? ? ? ? ? ?x = y;

語句“while a<b do if c<5 then while x>y do z=x+1; else x=y;”的注釋分析樹

?while a<b do if c<5 then while x>y do z=x+1; else x=y;

100: if a < b ?goto 102? ? ? ? ? ? ? ? ? ? ??100:?? ?( j<, a ,b , 102 )

101: goto _? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?101: ?? ?( j ?, - , ?- , ?- ? ? )

102: if c < 5 ?goto 104? ? ? ? ? ? ? ? ? ? ? ?102: ?? ?( j<, c , 5 , 104 )

103: goto 110? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??103: ?? ?( j ?, - , ?- , 110 )

104: if x > y ?goto 106? ? ? ? ? ? ? ? ? ? ? ?104: ?? ?( j>, x , y , 106 )

105: goto 100? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 105: ?? ?( j ?, - , ?- , 100 )

106: t1 = x + 1? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?106: ?? ?( + , x , 1 , ? t1 ?)

107: z = t1? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?107: ?? ?( = , t1 , - , ? z ? )

108: goto 104? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??108: ?? ?( j ?, - , ?- , 104 )

109: goto 100? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 109: ?? ?( j ?, - , ?- , 100 )

110: x = y? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 110: ?? ?( = , y , ?- , ? x ?)

111: goto 100? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 111: ?? ?( j ?, - , ?- , 100 )

112:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?112:

switch語句的翻譯

switch E

?? ?begin

?? ??? ?case V1: S1

?? ??? ?case V2: S2

?? ??? ?. . .

?? ??? ?case Vn – 1: Sn – 1

?? ??? ?default: Sn

?? ?end

switch E { t = newtemp(); gen( t ‘=’ E.addr ); }

case V1:{ L1= newlabel(); gen(‘if’ t ‘!=’ V1 ‘goto’ L1 ); } S1{ next = newlabel();gen(‘goto’ next); }

case V2:{ label(L1); L2=newlabel();? gen(‘if’ t ‘!= ’ V2 ‘goto’ L2 ); } S2{ gen(‘goto’ next); }

?? ??? ?. . .

case Vn -1:{ label(Ln-2); Ln-1=newlabel(); gen(‘if’ t ‘!=’ Vn-1 ‘goto’ Ln-1 );} Sn – 1{ gen(‘goto’ next);}

default:{ label(Ln-1);} Sn{label(next);}?? ?

switch語句的另一種翻譯

在代碼生成階段,根據分支的個數以及這些值是否在一個較小的范圍內,這種條件跳轉指令序列可以被翻譯成最高效的n路分支

switch E { t = newtemp(); gen(t ‘=’E.addr); test = newlabel(); gen(‘goto’ test); }

case V1 : { L1= newlabel(); label(L1); map(V1, L1); }? S1 ? ?{ next = newlabel(); ?gen(‘goto’ next); }

case V2 : { L2 = newlabel(); label(L2); map(V2, L2); } ? S2 ? ?{ gen(‘goto’ next); } ?

? ??? ?. . .

case Vn-1:{ Ln-1= newlabel(); label(Ln-1); map(Vn-1, Ln-1); }? ?Sn-1 ?{ gen(‘goto’ next); }

default : ?{ Ln= newlabel(); label(Ln); }?

? ? ? ? Sn??{ gen(‘goto’ next);label(test);

? ? ? ? ? ? ? ? gen(‘if ’ t ‘=’ V1 ‘goto’ L1);

? ? ? ? ? ? ? ? gen(‘if ’ t ‘=’ V2 ‘goto’ L2);

? ? ? ? ? ? ? ? . . .

? ? ? ? ? ? ? ? gen(‘if ’ t ‘=’ Vn-1 ‘goto’ Ln-1);??

? ? ? ? ? ? ? ? gen(‘goto’ Ln);

? ? ? ? ? ? ? ?label(next);? ?}

增加一種case指令

test : ?? if t = V1 goto L1? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??test :?? ?case t ?V1 ? ? ?L1

? ? ? ? ? ?if t = V2 goto L2? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? case t ?V2 ? ?L2

? ? ? ? ? ?. . .? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?. . .

? ? ? ? ? ?if t = Vn-1 goto Ln-1? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??case t ?Vn-1 ?Ln-1

? ? ? ? ? ?goto Ln? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??case t ? t ? ? ?Ln

next :? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?next :

指令 case t Vi ?Li ?和 if t = Vi ?goto Li 的含義相同,但是case指令更加容易被最終的代碼生成器探測到,從而對這些指令進行特殊處理

過程調用語句的翻譯

文法

S call id (Elist) ??

Elist Elist, E ?? ?

Elist E

過程調用語句的代碼結構

id( E1, E2, … , En )

?需要一個隊列q存放E1.addr 、E2.addr、…、 En.addr,以生成

param E1.addr

param E2.addr …

param En.addr

call id.addr ?n

過程調用語句的SDD

S → call id ( Elist )

?? ?{?? ?n=0;??// 參數個數計數

?? ? ??? ?for q中的每個t ?do

?? ??? ?{?? ?gen(‘param’ t );??// 生成 param 指令

?? ??? ??? ?n = n+1;

?? ? ??? ?}

?? ??? ?gen(‘call’ id.addr ‘,’ n);???// 生成函數調用指令

?? ?}

Elist → E?

?{ ??? ?將q初始化為只包含E.addr; ?? ?}??// 一個參數時,隊列中只有一個元素

Elist →Elist1, E ?

??{ ?? ?將E.addr添加到q的隊尾; ?? ?}? ??// 多個參數就一個個拼進去

S → call id (Elist)

這就是一個函數調用,比如 call f(...)Elist 是參數列表。

在翻譯時我們:

  1. 先翻譯每個實參 E,把結果臨時變量加入到 q(參數隊列)里;

  2. 然后一條條地生成 param 參數 的中間代碼;

  3. 最后調用函數:call f, 參數個數

例子

翻譯以下語句f ( b*c-1, x+y, x, y )

t1 =b*c

t2 =t1 - 1

t3 =x+y

param t2

param t3

param x

param y

call f, 4

源代碼是:f(b*c-1, x+y, x, y)

這是一個調用函數 f,傳入了 4 個實參:

  • b * c - 1

  • x + y

  • x

  • y

第一個參數:b*c - 1

  • t1 = b * c

  • t2 = t1 - 1

  • 加入隊列:q = [t2]

第二個參數:x + y

  • t3 = x + y

  • 加入隊列:q = [t2, t3]

第三個參數:x(變量)

  • 加入隊列:q = [t2, t3, x]

第四個參數:y(變量)

  • 加入隊列:q = [t2, t3, x, y]

最終生成中間代碼:

t1 = b * c ? ? ? ? // 第一個參數的第一步
t2 = t1 - 1 ? ? ? ?// 第一個參數的第二步
t3 = x + y ? ? ? ? // 第二個參數
param t2 ? ? ? ? ? // 參數從左到右一個個入棧
param t3
param x
param y
call f, 4 ? ? ? ? ?// 調用 f,傳 4 個參數

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/914841.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/914841.shtml
英文地址,請注明出處:http://en.pswp.cn/news/914841.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Pytorch深度學習框架實戰教程02:開發環境部署

相關章節 《Pytorch深度學習框架實戰教程09&#xff1a;模型的保存和加載》 《Pytorch深度學習框架實戰教程01&#xff1a;深度學習框架簡介》 本文詳細介紹了PyTorch CPU/GPU雙版本的完整安裝流程&#xff0c;從環境準備到部署驗證&#xff0c;助你快速搭建高效深度學習開發…

初試Spring AI實現聊天功能

文章目錄 1. 實戰概述 2. 實現步驟 2.1 申請API Key 2.2 創建Spring Boot項目 2.3 添加兩個項目相關依賴 2.4 應用屬性文件里配置Spring AI 2.5 修改啟動類注解屬性 2.6 創建第一個聊天控制器 2.7 創建聊天結果頁面 2.8 測試第一個聊天控制器 2.9 創建第二個聊天控制器 2.10 創…

【圖像處理基石】如何入門色彩評估?

什么是色彩評估&#xff1f; 色彩評估是對色彩的屬性、表現、一致性及適用性進行科學分析和主觀/客觀判斷的過程&#xff0c;核心是通過系統方法判斷色彩是否符合預期標準&#xff08;如設計要求、行業規范、視覺效果等&#xff09;&#xff0c;廣泛應用于印刷、紡織、涂料、產…

6、docker network

docker網絡驅動Docker 網絡驅動是 Docker 容器網絡通信的核心機制&#xff0c;負責管理容器之間的連接、隔離和跨主機通信。Docker 網絡驅動的作用網絡隔離通過網絡命名空間&#xff08;Network Namespace&#xff09;為每個容器提供獨立的網絡環境&#xff0c;確保容器之間的網…

Qt Quick 粒子系統詳解

Qt Quick 粒子系統詳解Qt Quick 粒子系統詳解一、核心組件二、粒子運動數學模型三、基本粒子系統結構四、完整示例1、火焰效果2、雪花飄落效果3、煙花爆炸效果五、性能優化技巧六、實例展示Qt Quick 粒子系統詳解 Qt Quick 粒子系統是用于創建動態視覺特效&#xff08;如爆炸、…

AI問答-供應鏈管理:各種交通運輸方式貨運成本分析

一、各種交通運輸方式貨運成本分析運輸方式主要成本構成成本特點適用場景成本優勢分析成本劣勢分析參考費用&#xff08;示例&#xff09;里程/價格公路運輸燃料費用、人工成本&#xff08;司機工資、維修工人工資等&#xff09;、維修費用、保險費用、道路通行費、折舊費、稅費…

redis速記

1.什么是緩存穿透&#xff1f;怎么解決&#xff1f;答&#xff1a;緩存穿透是指用戶請求的數據在緩存&#xff08;如 Redis&#xff09;和數據庫&#xff08;如 MySQL&#xff09;中都不存在&#xff0c;導致每次請求都必須繞過緩存直接查詢數據庫&#xff0c;最終大量無效請求…

aspnetcore Mvc配置選項中的ModelMetadataDetailsProviders

在ASP.NET Core 中&#xff0c;ModelMetadataDetailsProviders 是用于配置模型元數據提供程序的核心組件&#xff0c;它決定了如何解析和提供模型屬性的元數據&#xff08;如數據類型、驗證規則、顯示名稱等&#xff09;。以下是其詳細解析&#xff1a; 一、核心概念與作用 模…

分區表設計:歷史數據歸檔與查詢加速

以下為分區表設計的核心實現方案與技術要點&#xff0c;綜合最新技術實踐整理&#xff1a;一、分區表核心機制與價值?物理存儲與邏輯分離?分區表通過預定義規則&#xff08;如時間戳、ID范圍&#xff09;將大表物理拆分為多個子表&#xff08;分區&#xff09;&#xff0c;對…

下班倒計時

下班倒計時#include <stdio.h> #include <time.h> #include <unistd.h>void print_remaining_time(time_t now, time_t tar_time) {double diff difftime(tar_time, now);int hours (int)diff / 3600;int minutes ((int)diff % 3600) / 60;int seconds (…

Vue配置特性(ref、props、混入、插件與作用域樣式)

前言Vue提供了許多高級特性來增強組件開發的能力。本文將深入解析Vue中的ref屬性、props配置、混入(mixin)、插件開發以及scoped樣式等核心特性&#xff0c;通過實例演示它們的用法&#xff0c;并給出最佳實踐建議。一、ref屬性詳解1. ref基本用法ref用于給元素或子組件注冊引用…

解析力和清晰度區別

在視覺成像、光學設備或數字信號處理領域&#xff0c;清晰度和解析力是兩個相關但側重點不同的概念。它們都與“細節呈現”有關&#xff0c;但核心定義、影響因素和應用場景存在顯著區別。以下從定義、核心差異、聯系三個方面詳細說明&#xff1a; 一、核心定義清晰度&#xff…

Java網絡通信:UDP和TCP

一、UDP特點&#xff1a; 無連接不可靠&#xff1a;通信雙方不事先建立連接&#xff0c;直接發送數據。數據封裝&#xff1a;將數據封裝在64KB的數據包中&#xff0c;包含接收端的IP和端口。UDP通信模型&#xff1a; 模型比喻&#xff1a;以拋韭菜為例&#xff0c;發送端像拋韭…

Java行為型模式(狀態模式)實現方式與測試方法

一、狀態模式實現方式 核心結構 狀態接口&#xff08;State&#xff09;&#xff1a;定義狀態相關的行為方法。具體狀態類&#xff08;ConcreteState&#xff09;&#xff1a;實現狀態接口&#xff0c;封裝特定狀態下的邏輯。上下文類&#xff08;Context&#xff09;&#xff…

MISRA C-2012準則之標準C環境準則

目錄 1.標準C環境準則 錯誤示例1&#xff1a;未定義行為&#xff08;整數溢出&#xff09; 錯誤示例2&#xff1a;未指定行為&#xff08;函數調用順序&#xff09; 錯誤示例3&#xff1a;語言擴展&#xff08;GCC內置函數&#xff09; 錯誤示例4&#xff1a;關鍵未指定行…

26、鴻蒙Harmony Next開發:ArkTS并發(Promise和async/await和多線程并發TaskPool和Worker的使用)

目錄 異步并發 (Promise和async/await) Promise async/await 多線程并發 多線程并發模型 內存共享模型 Actor模型 TaskPool TaskPool運作機制 TaskPool注意事項 Concurrent裝飾器 裝飾器說明 裝飾器使用示例 TaskPool擴縮容機制 擴容機制 縮容機制 Worker Wo…

[IRF/Stack]華為/新華三交換機堆疊配置

堆疊的三大優勢 提高資源利用率&#xff0c;獲得更高的轉發性能、鏈路帶寬降低網絡規劃的復雜度、方便網絡的管理降低故障對業務的影響時間 堆疊的兩個需求 設備型號必須統一系統版本必須統一 華三堆疊案例&#xff1a;#### S6850_1 <H3C>sy [H3C]undo in en [H3C]sy SW…

融智興科技: RFID超高頻洗滌標簽解析

在紡織品租賃與管理領域&#xff0c;布草、工服、醫護織物等物品的流轉追蹤一直是運營管理的核心挑戰。傳統管理方式依賴人工計數與條碼掃描&#xff0c;存在效率低下、差錯率高、損耗嚴重等問題&#xff0c;尤其在工業洗滌環境下&#xff0c;紙質標簽易損壞、識別率低。融智興…

從平面到時空:地圖故事的時空敘事與沉浸式閱讀

朋友們&#xff0c;在工作中你是否也遇到過這些令人頭疼的挑戰&#xff1f;當項目匯報時總覺得表達不夠精彩&#xff0c;方案講解時聽眾總是一頭霧水&#xff0c;制作應急預案時更是無從下手&#xff1f;別擔心&#xff01;今天我要向大家介紹一個超級實用的解決方案——地圖故…

自動控制原理知識地圖:舵輪、路徑與導航圖

掌握自控原理的關鍵&#xff0c;在于看清那棵枝繁葉茂的“知識樹”——從根部的數學模型&#xff0c;到主干的分析方法&#xff0c;直至頂端的系統設計。作為一名自動化專業學生&#xff0c;你是否曾在深夜里面對勞斯判據和奈奎斯特圖感到深深的恐懼&#xff1f;作為初入行的工…