編譯原理--02 自頂向下、自底向上的LR分析複習(清華大學出版社第3版)

前言

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

第4章 自頂向下的語法分析方法

確定的自頂向下分析思想

開始符號集首符號集:設\(G=(V_T,V_N,P,S)\)是上下文無關文法。
\(FIRST(\alpha)=\{a \mid\alpha \stackrel{*}{\Rightarrow} a\beta, a\in V_T, \alpha,\beta\in V^*\}\)

\(\alpha \stackrel{*}{\Rightarrow} \varepsilon\),則規定\(\varepsilon \in FIRST(\alpha)\),稱\(\varepsilon \in FIRST(\alpha)\)\(\alpha\)開始符號集首符號集

簡單來說,就是查看該句型推導出的所有句子的首字母集合。

例如文法\(G[S]:\)
\(S\rightarrow Ap\)
\(S\rightarrow Bq\)
\(A\rightarrow aA\)
\(A\rightarrow \varepsilon\)
\(B\rightarrow Bb\)
\(B\rightarrow b\)

那麼\(FIRST(S)=\{a,b,p\}\)\(FIRST(A)=\{a,\varepsilon\}\)\(FIRST(B)=\{b\}\)

FOLLOW集:設\(G=(V_T,V_N,P,S)\)是上下文無關文法,\(A\in V_N\),S是開始符號
\(FOLLOW(A)=\{a\mid S\stackrel{*}{\Rightarrow}\mu A\beta且a\in V_T,a\in FIRST(\beta), \mu\in {V_T}^*, \beta\in V^+\)
\(S \stackrel{*}{\Rightarrow}\mu A\beta\),且\(\beta \stackrel{*}{\Rightarrow}\varepsilon\),則 \(\# \in FOLLOW(A)\)

簡單來說,就是查看該句型在被推導前後面跟隨的所有可能的第一個字母的集合。需要將出現在推導式左邊的非終結符往

例如之前的文法\(G[S]\)\(FOLLOW(S)=\{\#\}\)\(FOLLOW(A)=\{p\}\)\(FOLLOW(B)=\{b,q\}\)

選擇符號集:一個產生式的選擇符號集SELECT。給定上下文無關文法的產生式\(A\rightarrow \alpha, A\in V_N, \alpha\in V^*\),若\(\alpha\stackrel{*}{\nRightarrow}\varepsilon\),則\(SELECT(A\rightarrow\alpha)=FIRST(\alpha)\)

而如果\(\alpha\stackrel{*}{\Rightarrow}\varepsilon\),則\(SELECT(A\rightarrow\alpha)=(FIRST(\alpha)-\{\epsilon\})\cup FOLLOW(A)\)

求出選擇符號集,是爲了找到哪些符號應該使用該推導。那麼,如果\(A\rightarrow \alpha\)不能推出空串,顯然 從該推導得到的所有句子的首字母構成的集合 來反向看出 哪些字母應該使用該推導。而如果\(A\rightarrow \alpha\)能推出空串,則還要考慮該非終結符的後跟字符。

一個上下文無關文法是LL(1)文法的充要條件,是對每個非終結符A的兩個不同產生式,\(A\rightarrow\alpha, A\rightarrow\beta\),滿足
\[SELECT(A\rightarrow\alpha)\cap SELECT(A\rightarrow\beta)=\emptyset\]

例如文法\(G[S]:\)
\(S\rightarrow aA\)
\(S\rightarrow d\)
\(A\rightarrow bAS\)
\(A\rightarrow \varepsilon\)

有:
\(SELECT(S\rightarrow aA)=\{a\}\)
\(SELECT(S\rightarrow d)=\{d\}\)
\(SELECT(A\rightarrow bAS)=\{b\}\)
\(SELECT(A\rightarrow \varepsilon)=\{a,d,\#\}\)

所以:
\(SELECT(S\rightarrow aA)\cap SELECT(S\rightarrow d)=\{a\}\cap\{d\}=\emptyset\)
\(SELECT(A\rightarrow bAS)\cap SELECT(A\rightarrow \varepsilon)=\{b\}\cap\{a,d,\#\}=\emptyset\)

LL(1)文法的判別

LL(1)的含義:第1個L 表明 從左向右掃描輸入串,第2個L 表明 分析過程中將使用最左推導,1表明只需向右看一個符號就知道該選擇哪個產生式推導。

  1. 找出能推出\(\varepsilon\)的非終結符
  2. 計算FIRST集
  3. 計算FOLLOR集
  4. 計算SELECT集
  5. 進行判斷

某些非LL(1)文法到LL(1)文法的等價變換

LL(1)文法的充分條件爲不含左公共因子

提取左公共因子

例如\(A\rightarrow \alpha\beta\mid \alpha\gamma\),可以寫成:
\(A\rightarrow \alpha B\)
\(B\rightarrow \beta\)
\(B\rightarrow \gamma\)

一般情況如\(A\rightarrow \alpha_1\alpha_2...\alpha_n(\beta_1\mid\beta_2\mid...\mid\beta_n)\),可以寫成:
\(A\rightarrow\alpha_1 A_1\)
\(A_1\rightarrow\alpha_2 A_2\)
\(...\)
\(A_{n-1}\rightarrow\alpha_n B\)

\(B\rightarrow\beta_1\)
\(B\rightarrow\beta_2\)
\(...\)
\(B\rightarrow\beta_n\)

此外,還需要檢查文法是否含有隱式的左公共因子,如:
\((1)A\rightarrow ad\)
\((2)A\rightarrow Bc\)
\((3)B\rightarrow aA\)

將(3)帶入(2),可暴露出左公共因子:
\((1)A\rightarrow ad\)
\((2)A\rightarrow aAc\)
\((3)B\rightarrow aA\)

可以看到此時(3)爲多餘規則,可以刪去,最後整理得到:
\((1)A\rightarrow aB\)
\((2)B\rightarrow Ac\)
\((2)B\rightarrow d\)

改寫後的文法不含空產生式,且無左遞歸時,則改寫後的文法是LL(1)文法

若還有空產生式,則還需要用LL(1)文法的判別方式進行判斷

消除左遞歸

對於包含直接左遞歸的文法,如\(G[A]:A\rightarrow Ab,A\rightarrow c, A\rightarrow d\)

可以直接改寫成右遞歸:\(A\rightarrow cA', A\rightarrow dA', A'\rightarrow bA',A'\rightarrow \varepsilon\)

一般情況下,如\(A\rightarrow A(\beta_1\mid\beta_2\mid...\mid\beta_n), A\rightarrow \alpha_1\mid\alpha_2\mid...\mid\alpha_n\),分爲了包含左遞歸和不含左遞歸的兩部分,可以變形爲:
\(A\rightarrow \alpha_1 A'\)
\(A\rightarrow \alpha_2 A'\)
\(...\)
\(A\rightarrow \alpha_n A'\)

\(A'\rightarrow \beta_1 A'\)
\(A'\rightarrow \beta_2 A'\)
\(...\)
\(A'\rightarrow \beta_n A'\)

對於包含間接左遞歸的文法,如\(G[S]:\)
\((1)A\rightarrow aB\)
\((2)A\rightarrow Bb\)
\((3)B\rightarrow Ac\)
\((4)B\rightarrow d\)

可以考慮把(1)和(2)帶入(3):
\((1)B\rightarrow aBc\)
\((2)B\rightarrow Bbc\)
\((3)B\rightarrow d\)

不會引起左遞歸的式子爲(1)和(3),故可以寫成:
\((1)B\rightarrow aBcB'\)
\((2)B\rightarrow dB'\)
\((3)B'\rightarrow bcB'\)
\((4)B'\rightarrow \varepsilon\)

消除一切左遞歸

在這裏插入圖片描述

LL(1)文法的實現

程序實現

  1. 求出文法G[S]各個產生式的SELECT集合
  2. 然後根據集合內的符號來選擇所屬產生式

寫程序時,用getsym來讀入下一個符號,如果有不合法的符號,應當有錯誤處理。

如文法\(L(G[A]): A\rightarrow aBd \mid b, B\rightarrow\varepsilon\mid c\)

\(SELECT(A\rightarrow aBd)=\{a\}\)
\(SELECT(A\rightarrow b)=\{b\}\)
\(SELECT(B\rightarrow \varepsilon)=\{d\}\)
\(SELECT(B\rightarrow c)=\{c\}\)

void PraseA()
{
    if (sym == 'a')
    {
        getsym();
        PraseB();
        if (sym == 'd')
            getsym();
    }
    else if (sym == 'b')
    {
        getsym();
    }
    else
    {
        error();
    }
}

void PraseB()
{
    if (sym == 'c')
    {
        getsym();
    }
    else if (sym == 'd')
    {
    }
    else
    {
        error();
    }
}

預測分析表

求出所有規則的SELECT集後,根據集合填入預測分析表。如上面的文法:

a b c d #
A \(\rightarrow aBd\) \(\rightarrow b\)
B \(\rightarrow c\) \(\rightarrow \varepsilon\)

下表是對對\(acd\)的分析過程:

步驟 分析棧 剩餘輸入串 推導所用產生式或匹配
1 #A acd# A→aBd
2 #dBa acd# a匹配
3 #dB cd# B→c
4 #dc cd# c匹配
5 #d d# d匹配
6 # # 接受

一開始把起始非終結符放入分析棧,然後會有2種情況:

  1. 若分析棧棧頂爲非終結符,根據剩餘輸入串的首字符以及預測分析表來選擇產生式。如果找到合適的產生式,此時需要把分析棧棧頂的非終結符彈出,然後根據產生式右邊的句型從右往左依次入棧。如果沒找到,則出現異常。
  2. 若分析棧棧頂爲終結符,此時如果和剩餘輸入串的首字符相匹配,則彈出分析棧棧頂終結符,並查看輸入串的下一個字符。如果不匹配,則出現異常。

這一章的可能考點

  1. 判別文法是否爲LL(1)
  2. 已知LL(1)文法,構造預測分析表
  3. 給定LL(1)文法和輸入串,寫出分析過程表

第5章 自底向上的移進-歸約分析

自底向上的移進-規約分析要求對輸入符號串自左向右掃描,按句柄進行歸約。
移進:將輸入串的下一個字符移入符號棧
歸約:符號棧中的頂部幾個符號如果能匹配某條推導式的右邊,則用該推導式的左邊替換

自底向上的移進-歸約法是每次對最左邊的內容進行歸約,它的逆過程爲自頂向下的規範(最右)推導。

設文法 \(G[S]\)
\(S\rightarrow aAcBe\)
\(A\rightarrow b\)
\(A\rightarrow Ab\)
\(B\rightarrow d\)

對輸入串\(abbcde\)使用自頂向下的最右推導:
\[S\Rightarrow aAcBe \Rightarrow aAcde \Rightarrow aAbcde \Rightarrow abbcde\]

對應的,我們可以得到它的逆過程,即規約過程。

步驟 符號棧 輸入符號串 動作
(1) # abbcde# 移進
(2) #a bbcde# 移進
(3) #ab bcde# 歸約\((A\rightarrow b)\)
(4) #aA bcde# 移進
(5) #aAb cde# 歸約\((A\rightarrow Ab)\)
(6) #aA cde# 移進
(7) #aAc de# 移進
(8) #aAcd e# 歸約\((B\rightarrow d)\)
(9) #aAcB e# 移進
(10) #aAcBe # 歸約\((S\rightarrow aAcBe)\)
(11) #S # 接受(acc)

第6章 LR分析

LR(K)分析使用自底向上分析法,從左到右掃描符號,只需要根據分析棧中的符號棧和向右順序查看輸入串的K(K>=0)個符號來確定分析器接下來是移進還是規約,因而也能唯一地確定句柄。

LR分析器

在這裏插入圖片描述
總控程序負責LR分析過程

分析棧分爲狀態棧和文法符號棧。它們均是後進先出。

分析表分爲動作(ACTION)表和狀態轉換(GOTO)表兩個部分。

SP爲棧指針,指向狀態棧和文法符號棧,即狀態棧和符號棧元素數目始終保持一致。

狀態轉換表內容按關係\(GOTO[S_i, X]=S_j\)確定,即當棧頂狀態爲\(S_i\)遇到棧頂符號\(X\)時應當轉向狀態\(S_j\)

動作表\(ACTION[S_i,a]\)確定了棧頂狀態爲\(S_i\)時遇到輸入符號\(a\)應執行的動作。


動作按優先程度排列:

規約:如果棧頂形成了句柄\(\beta\)(它的長度爲\(r\)),且有原來的推導\(A\rightarrow\beta\)來進行規約,則從狀態棧和文法符號棧中自頂向下去掉\(r\)個符號,即對SP減去\(r\)。接着把A移入文法符號棧內,再根據此時修改SP後的棧頂狀態,把滿足\(S_j=GOTO[S_i, A]\)的狀態移進狀態棧。

移進:如果棧頂沒有形成句柄,且\(S_j=GOTO[S_i,a]\)成立,則把\(S_j\)移入到狀態棧,把\(a\)移入到文法符號棧。其中\(i\)\(j\)表示狀態號。

接受acc:當歸約到文法符號棧中只剩下文法的開始符號S,並且輸入符號串只剩下#(表示已經結束),則爲分析成功。

報錯:如果狀態棧頂的當前狀態遇到了不該出現的文法符號時則報錯,說明輸入串不是該文法能接受的句子。

LR(0)分析

已知文法\(G[S]:\)
\((1)S\rightarrow aAcBe\)
\((2)A\rightarrow b\)
\((3)A\rightarrow Ab\)
\((4)B\rightarrow d\)

對輸入串\(abbcde\#\)用自底向上歸約的方法來分析,由於到第5步棧中的符號串爲\(\#aAb\),此時狀態棧的棧頂狀態\(0236\)決定了應該用(3)式而不是(2)式來歸約。

分析表和分析過程如下

可歸前綴和活前綴

爲了更清楚地表示最右推導與最左歸約的關係,可以在推導過程中加入一些附加信息。對上面的文法用\([i]\)編號:

\(S\rightarrow aAcBe[1]\)
\(A\rightarrow b[2]\)
\(A\rightarrow Ab[3]\)
\(B\rightarrow d[4]\)

對輸入串abbcde進行最右推導,把序號也帶入:
\(S\Rightarrow aAcBe[1]\Rightarrow aAcd[4]e[1]\Rightarrow aAb[3]cd[4]e[1]\Rightarrow ab[2]b[3]cd[4]e[1]\)

對應的逆過程——最左歸約(規範歸約,即從左到右歸約)爲:
\(ab[2]b[3]cd[4]e[1]\) 用產生式(2)歸約
\(\Leftarrow aAb[3]cd[4]e[1]\) 用產生式(3)歸約
\(\Leftarrow aAcd[4]e[1]\) 用產生式(4)歸約
\(\Leftarrow aAcBe[1]\) 用產生式(1)歸約
\(S\)

這裏用\(\Leftarrow\)表示歸約。

每次歸約前,句型的前部依次爲:
\(ab[2]\),它的前綴爲\(\varepsilon, a, ab\)
\(aAb[3]\),它的前綴爲\(\varepsilon, a, aA, aAb\)
\(aAcd[4]\),它的前綴爲\(\varepsilon, a, aA, aAc, aAcd\)
\(aAcBe[1]\),它的前綴爲\(\varepsilon, a, aA, aAc, aAcB, aAcBe\)

這些規範句型的前部我們稱之爲可歸前綴。而a, aA, aAc等這些出現在一個或多個可歸前綴的部分,可稱之爲活前綴,它的長度不能超過當前句型句柄的末端。

拓廣文法是指對原文法G增加產生式\(S'\rightarrow S\),其中\(S\)爲原文法G的開始符號。這樣確保新的開始符號\(S'\)只會在推導式的左邊出現(而\(S\)不一定)

使用拓廣文法可以將上面的文法表示成:
\(S'\rightarrow S[0]\)
\(S\rightarrow aAcBe[1]\)
\(A\rightarrow b[2]\)
\(A\rightarrow Ab[3]\)
\(B\rightarrow d[4]\)

對句子\(abbcde\)列出可歸前綴:
\(S[0]\)
\(ab[2]\)
\(aAb[3]\)
\(aAcd[4]\)
\(aAcBe[1]\)

構造識別其活前綴及可歸前綴的有限自動機如下圖所示:

帶*的終態既是句柄識別態,也是句子識別態(僅有唯一一個)。

用一個額外的開始狀態X,並用\(\varepsilon\)弧連接每個可歸前綴的有限狀態機,並進行確定化:

活前綴及可歸前綴的一般計算方法

\(G=(V_N, V_T, P, S)\)是一個上下文無關文法,對於\(A\in V_N\),有
\[LC(A)=\{\alpha\mid S'\mathop{\Rightarrow}\limits_{R}^{*}aA\omega,\alpha\in V^*, \omega\in V^T_*\}\]
其中S'是G的拓廣文法G'的開始符號

\(LC(A)\)表明了在規範推導(最右推導)中在非終結符A左邊出現的符號串集合。

推論:若文法G中有產生式\(B\rightarrow \gamma A\delta\),則有
\[LC(A) \supseteq LC(B) \cdot\{\gamma\}\]

因爲對任一形爲\(\alpha B\omega\)的句型,必然有規範推導:
\[S'\mathop{\Rightarrow}\limits_{R}^{*}\alpha B\omega'\mathop{\Rightarrow}\limits_{R}^{*}\alpha\gamma A\delta\omega\]

因此對任一\(\alpha \in LC(B)\),必有\(\alpha \gamma \in LC(A)\),即\(LC(B) \cdot\{\gamma\} \subseteq LC(A)\)

對於文法\(G[S]\)
\(S'\rightarrow S\)
\(S\rightarrow aAcBe\)
\(A\rightarrow b\)
\(A\rightarrow Ab\)
\(B\rightarrow d\)

需列出方程組求解(此處爲正規式):
\[\begin{cases} LC(S') = \varepsilon \\ LC(S) = LC(S') \cdot\varepsilon=\varepsilon \\ LC(A) = LC(S) \cdot a \mid LC(A) \cdot\varepsilon = a \\ LC(B) = LC(S) \cdot aAc = aAc \end{cases}\]

用正規式表示求解結果:
\[\begin{cases} LC(S') = \varepsilon \\ LC(S) = \varepsilon \\ LC(A) = a \\ LC(B) = aAc \end{cases}\]

規定\(LR(0)C(A\rightarrow\beta)=LC(A)\cdot\beta\),這樣包含句柄的活前綴有:

\[\begin{cases} LR(0)C(S'\rightarrow S) = S \\ LR(0)C(S\rightarrow aAcBe) = aAcBe \\ LR(0)C(A\rightarrow b) = ab \\ LR(0)C(A\rightarrow Ab) = aAb \\ LR(0)C(B\rightarrow d) = aAcd \end{cases}\]

包含句柄的活前綴也就是可歸前綴,將它們展開也就得到了所有的活前綴

對於遞歸型:
\[\begin{cases} LC(E)=\varepsilon \\ LC(A) = LC(E)\cdot a\mid LC(A) \cdot c \end{cases}\]

一直展開可以看到:
\(LC(A)\)
\(= a\mid LC(A)\cdot c\)
\(=a\mid (a\mid LC(A)\cdot c)\cdot c\)
\(=a\mid ac\mid LC(A)\cdot cc\)
\(=a\mid ac\mid acc\mid LC(A)\cdot ccc\)
\(=...\)

\(LC(A)=ac^*\)

LR(0)項目規範族的構造

1. LR(0)項目

在文法G'中爲每個產生式的右部的適當位置添加一個圓點構成項目
例如\(A\rightarrow Ab\)有3個項目:
\([0] A\rightarrow \cdot Ab\)
\([1] A\rightarrow A\cdot b\)
\([2] A\rightarrow Ab\cdot\)

而空產生式\(A\rightarrow\varepsilon\)只有一個項目\(A\rightarrow\cdot\)

\(\cdot\)左邊的符號表示已經被掃描過的部分,右邊如果還有符號,則它的第一個符號則是下一個將會被掃描的符號。

2. 構造識別活前綴的NFA

列出所有項目後,這些項目標上編號用於構造NFA,確定\(S'\rightarrow \cdot S\)爲初態,有2種情況:

  1. 即將從\(S'\rightarrow \cdot S\)過渡到\(S\rightarrow \cdot aA\)。這兩個項目用\(\rightarrow\)連接,並在上面標記\(\varepsilon\)
  2. 即將從\(S\rightarrow\cdot aA\)過渡到\(S\rightarrow a\cdot A\)。這兩個項目用\(\rightarrow\)連接,並在上面標記\(a\)

根據圓點所在位置和圓點後的符號狀況,可以分爲4類:

  1. 移進項目,形如\(A\rightarrow a\cdot aB\),圓點後面是終結符
  2. 待約項目,形如\(A\rightarrow aa\cdot B\),圓點後面是非終結符,需要移進完非終結符內的所有符號才能歸約符號B
  3. 歸約項目,形如\(A\rightarrow aaB\cdot\),圓點後面沒有符號,此時可以進行歸約
  4. 接受項目,形如\(S'\rightarrow A\),S'是所有產生式唯一的左部

3. LR(0)項目集規範族的構造

從NFA構造成DFA的關鍵點僅在於,使用閉包函數,將當前項目(它前面不是由\(\varepsilon\)推出)以及後面用\(\varepsilon\)弧連接的所有項目,構成一個新的項目集。

但一個項目集中不能同時存在:

  1. 移進項目和歸約項目,形如\(A\rightarrow a\cdot a\beta\)\(B\rightarrow \gamma\cdot\)
  2. 歸約項目和歸約項目,形如和\(A\rightarrow \beta\cdot\)\(B\rightarrow \gamma\cdot\)

在這裏插入圖片描述

4. LR(0)分析表的構造

現有LR(0)項目集規範族:
\[C=\{I_0, I_1, ..., I_n\}\]

\(I_k\)爲項目集的名,k爲狀態名,令包含\(S'\rightarrow \cdot S\)項目的集合\(I_k\)的下標k作爲分析器的初始狀態。分析表的ACTION表和GOTO表構造步驟如下:

  1. 若項目\(A\rightarrow \alpha\cdot a\beta\)屬於\(I_k\),且\(I_k\stackrel{a}{\rightarrow}I_j\),則置\(ACTION[k,a]=S_j\)
  2. 若項目\(A\rightarrow \alpha\cdot\)屬於\(I_k\),則對任何終結符和#號都置\(ACTION[k,*]=r_j\)
  3. \(I_k\stackrel{A}{\rightarrow}I_j\),則置\(GOTO[k,A]=j\)
  4. 若項目\(S'\rightarrow S\)屬於\(I_k\),則置\(ACTION[k,\#]=acc\),表示接受

SLR(1)分析

假定一個LR(0)規範族中含有如下的項目集(舉例):
\[I_5=\begin{cases} X\rightarrow\alpha\cdot b\beta \\ A\rightarrow \gamma \cdot \\ B\rightarrow \delta \cdot \end{cases}\]

也就是該項目集中出現了移進—歸約衝突歸約—歸約衝突

根據LR(0)分析,此時無法確定是移進,還是歸約,即產生了衝突。但我們可以向前查看一個符號(即查看當前剩餘輸入串最前面一個符號),來確定接下來的動作,這就是SLR(1)分析

對於上面的例子,要求三個項目中 \(·\) 後面的符號各不相同,即要求它們的FOLLOR集互不相交:
\(FOLLOW(A)\cap\{b\}=\emptyset\)
\(FOLLOW(B)\cap\{b\}=\emptyset\)
\(FOLLOW(A)\cap FOLLOW(B)=\emptyset\)

即說明在狀態5時,如果面臨某輸入符號爲\(a\),則可以按以下規定決策:

  1. \(a=b\),則移進
  2. \(a\in FOLLOW(A)\),則用產生式\(A\rightarrow \gamma\)歸約
  3. \(b\in FOLLOW(B)\),則用產生式\(B\rightarrow \delta\)歸約
  4. 此外,則報錯

接下來是分析表的修改,比如說產生式2爲\(A\rightarrow \gamma\),產生式3爲\(B\rightarrow \delta\)\(FOLLOW(A)=c\)\(FOLLOW(B)=d\),原本LR(0)時候分析表對應:

狀態 a b c d #
5 \(r_2,r_3\) \(r_2,r_3,S_6\) \(r_2,r_3\) \(r_2,r_3\) \(r_2,r_3\)

可見衝突非常嚴重,經過修改後,變成了:

狀態 a b c d #
5 \(S_6\) \(r_2\) \(r_3\)

此時的分析表滿足SLR(1)分析。

這一章可能的考點

  1. 已知文法,構造LR(0)的活前綴DFA,可能會要構造分析表
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章