1. 程式人生 > >侃一侃編譯原理的“文法”

侃一侃編譯原理的“文法”

das 目的 組成 spa 希望 英語 什麽 條件 sin

如果你翹累了代碼,想喝喝咖啡,順便看點兒可以當佐料的文章那本文應該比較適合現在的你。(•??•?)? ??

我們一天天都在和代碼打交道,但是你了解代碼的運行原理麽?為什麽你的一行代碼就能被執行出五花八門的效果嘞?

其實代碼這玩意兒就是一門語言。是的,你可以看成和中文、英文等語言平等的存在。是語言就得有語言的解析規則,不懂得規則自然無法理解語言的意思。就跟看沒字幕的美劇一樣,真是痛苦。╮(╯﹏╰)╭

中文有中文的語義、語法、句子、句法、文法,那麽編程語言也有自己的語言系統。

我們知道,我們寫的代碼被編譯器或者解釋器所執行,那它們是按照什麽文法來理解你的代碼呢?這就是文法。

本文也不會深入去解析文法,不然可以直接轉語言學了(笑~)。本文只是簡單介紹文法的一些概念。如果您喝著咖啡,看完之後,能有些許收獲,微微一笑,那本文的目的也就達到了。^_^

工欲善其事必先利其器。在談文法之前,我們先介紹幾個概念。

一.文法涉及的幾個簡單概念

假設Σ是一個有限的字母表集合,它的每一元素都是一個符號。Σ上的一個符號串就是指由Σ中的符號組成的一個有限序列。如果一個符號串不包含任何符號,就叫它空串,記為ε。現在再定義一個集合U和V的連接積的概念:

        UV = {αβ | α∈U,β∈V}

比如A = {a,b},B = {1,2},則AB={a1,a2,b1,b2}。很簡單的概念,是不是?

那麽相信你也能知道V1,V2等的的概念了。

還有幾個:

技術分享

ok,定義結束,現在來談談咱們本次的主角——文法。一個比較拗口的定義,

文法是描述語言的語法結構的形式規則(即語法規則)。

這啥意思啊?可能你一臉黑人問號……

技術分享

其實,就是指怎麽由一堆符號組成一個有含義的句子的規則和協議。

所謂的上下文無關文法就是文法的一種,它所定義的語法單位是完全上下文無關的。比如我們在程序語言 中,碰到一個算數表達式時,我們完全可以對它“就事論事”,不用去考慮它上下有啥東西

。當然,在自然語言(中文、英文等)中,一個語法單位(字、詞、句子)肯定和上下文環境有關,不然當年我們中文考試的閱讀理解題也就不會出現“根據上下文,解釋xx句子的含意”了。(ˇ?ˇ) 想~

所以說,上下文無關文法不能用來描述自然語言,但是對於當今的程序語言來說,上下文無關文法基本夠用了。下文中的“文法”,如果沒有特殊說明,都是之指“上下文無關文法”。

技術分享

下面類比自然語言的具體例子,談談我們今天要說的文法。

一個英文句子:

He gave me a book.

這個句子滿足英語的語法規則,是一個語法正確的句子。如果我們用“→”表示“由...組成”或者“定義為”,按照我們中學的語法,可以分解一下這個句子:

技術分享

這樣,通過這樣的一個個規則(又叫“產生式”),就把一個句子分解到了單詞的層次。或者這麽說,有了這些規則,我們可以這麽幹:

技術分享

我們可以畫一個更形象的圖(語法分析樹)來說明這種推導。

技術分享

上面定義英文句子的規則就可以說是一個上下文無關文法。其中,<句子>被稱為開始符號,<主語><謂語><代詞>之類的被稱為非終結符號,He、gave之類的被稱為終結符號

歸納起來,一個上下文無關文法G包括四個部分:終結符號,非終結符號,開始符號,產生式

終結符號就是一門語言中最基本的符號。在程序語言中,基本字、標識符、常數、運算符號等都算終結符號。

非終結符號更像一個抽象的集合,比如“算數表達式”、“賦值句”都可以看做非終結符號。

產生式就是推導規則。

下面上精確定義:

技術分享

二.遞歸定義的例子

有時候,只用一個產生式是不足以定義一個語法單位的,需要幾個產生式的相互配合。有時候會需要遞歸的形式。舉個栗子:

技術分享

假設要定義一類含有+、*的算術表達式,這個定義可以這麽說:

  • 變量是一個算術表達式;
  • 如果E1和E2是算術表達式,那麽E1+E2、E1*E2、(E1)也是算術表達式。

我們用產生式的形式描述它:

  • E→i
  • E→E+E
  • E→E*E
  • E→(E)

其中 E 代表算術表達式, i 代表變量。這四個產生式的全體才定義了什麽是“算術表達式”。後三個都是遞歸的形式。

還可以簡化為:E→i | E+E | E*E | (E)。其中的“|”代表“或”,是一種元語言符號。

三.文法與語言的推導

假設G是一個文法,S是開始符號,如果S經過零步或者若幹步推出α,那麽稱α是一個句型。只包含終結符號的句型是一個句子。文法G產生的所有句子構成一門語言,記為L(G)。

那麽怎麽從文法推導出它代表的語言嘞?

為了方便,我們引入一些符號。

技術分享

方法:把產生式看成替換規則,把當前符號串中的非終結符號用其產生式右邊的符號來替換。

技術分享

再看有文法G2->語言L(G2)例子。

技術分享

推導過程如下:

技術分享

語言L(G2)-> G2 的例子。

技術分享

由上面的兩個例子我們可以知道,一個文法可以唯一確定一個語言,但是一個語言不一定唯一對應一個文法

四.語法分析樹與二義性

我們發現從一個句型到另一個句型的推導過程不是唯一的。例如從E+E->i+i,存在兩個推導過程:

  • E+E->E+i->i+i 最推導,每個推導過程都是從最右邊的非終結符號的替換開始
  • E+E->i+E->i+i 最推導,每個推導過程都是從最左邊的非終結符號的替換開始

當然為了對句子的結構進行一個確定性的分析,我們一般只考慮最左推導或者最右推導。

前面我們提到過用一種樹形的圖示來表示這個句型的推導過程,這棵樹就被稱為”語法分析樹“,簡稱”語法樹“。

比如從E->(i+i) 的過程:

技術分享

對於一個文法,如果它的某些句子對應兩棵不同的語法樹,這個文法就屬於“二義性文法”。

技術分享

註意,文法的二義性和我們通常所說的語言的二義性不同,我們可能有兩個不同的文法G1,G2,一個是二義性,一個是非二義性,但是可能L(G1) = L(G2)。對於程序語言來說,我們常常希望它的文法是非二義性的,但是,只要我們能夠控制和駕馭文法的二義性,文法二義性的存在也不一定是壞事。

現在已經證明了,文法二義性是不可判定的。也就是說不存在一個算法,在有限步驟內算出一個文法是不是二義性的。我們能做的事兒,就是找一組充分條件來說明非二義性。比如,規定運算符號的優先級和結合性

對於我們上面使用的那個文法:E->E+E | (E) | E*E | i

如果限定*的優先級高於+,並且都是左結合的,那麽上述文法就變成了非二義性文法。讀者大大可以試試推導E->(i*i+i)。

侃一侃編譯原理的“文法”