一起來寫一個簡單的解釋器(3)

一起來寫一個簡單的解釋器(3)

1 人贊了文章

本系列文章參考國外編程高手魯斯蘭的博客文章《Let』s Build A Simple Interpreter》。

在之前的文章中,我們一起學習了如何實現對兩個數字加減法表達式的解釋。

這一篇文章,我們一起來學習對多個數字加減法表達式的解釋。

例如:12 + 8 – 6 – 10

在編寫代碼之前,我們先來看一張圖。

這張圖是語法圖。

語法圖是表示一種程序設計語言語法規則的示意圖。

實際上,語法圖體現了語法分析器(Parser)的運行規則。

一張語法圖能夠直觀地體現在我們的程序設計語言中,允許使用哪些語句和不允許使用哪些語句。

那麼,怎麼閱讀這張語法圖呢?

我們只需跟隨箭頭所指的路徑去了解程序的運行。

在語法圖中,有些路徑表示選擇,例如:加法表達式和減法表達式需要選擇不同的路徑。

另外,有的路徑表示循環,例如:多次進行加減運算的過程。

為了把這張圖看的更明白,我將不同的表達式對應語法圖中的路徑,做了幾張示意圖。

其中橙色路徑就是當前表達式運行的路徑。

例如:

>>>3

當我們只輸入一個數字,此時程序只找到了一個操作數,那就只需要把這個操作數傳遞出去。

例如:

>>>3+6

當我們輸入了一個完整的兩位數加法表達式,此時程序會先找到一個操作數,然後選擇加法操作符,再找到另外一個操作數,最終識別出一個完整的短語傳遞出去。

例如:

>>>3+5-4-2+6

當我們輸入了一個多個數字的加減法表達式,此時程序會先找到一個操作數,然後選擇加法操作符,再找到另外一個操作數;此時,程序發現還有新的記號,就開始循環的過程;再次選擇操作符,找到操作數,直到找不到新的記號,將完整的短語傳遞出去。

相信到這裡,大家都應該理解了這張語法圖的含義。

那麼,這裡我們看到了一個新的英文單詞「Term」,中文含義是「術語」,但是這很難理解。

不要搞那麼複雜,在這篇文章中,Term就是一個整數。

根據上面的語法圖,以下表達式都是合法的:

  • 3
  • 5+16
  • 33-15+7

實際上,因為在不同的程序設計語言中,四則運算表達式的語法規則十分相似。

所以,在Python的Shell中,我們運行這些表達式看到的結果也沒有什麼區別。

然而,對於我們的解釋器,表達式「3 + 」並不是一個有效的表達式。

因為,根據語法圖,加號後面必須跟著一個Term(整數),否則將會是一個語法錯誤。

在上一篇文章中,我們知道一連串Token(Token流)中識別出一個短語的過程叫做「Parsing」(語法分析)。一個解釋器或者編譯器用於語法分析的部分叫「Parser」(語法分析器)。

其實,「Parsing」也叫做「Syntax Analysis」,而「parser 」也叫做「Syntax Analyzer」

而且,我們也知道「Parser」和「Interpreter」位於「expr()」方法中。

「Parser」只負責識別結構並確保結構符合規範,當「Parser」成功識別(解析)出結構,Interpreter 會對表達式進行求值計算。

根據上面的語法圖,我們重寫「Parser」部分的代碼。

這裡我們添加一個新的方法「term()」,用於解析一個整數。

示例代碼:

def term(self): self.eat(INTEGER) # 驗證整數def expr(self): self.current_token = self.get_next_token() # 獲取記號 self.term() # 驗證第一個整數 while self.current_token.value_type in (PLUS, MINUS): # 循環 if self.current_token.value_type == PLUS: # 選擇加法運算符 self.eat(PLUS) # 驗證運算符 self.term() # 驗證下一個整數 if self.current_token.value_type == MINUS: # 選擇減法運算符 self.eat(MINUS) # 驗證運算符 self.term() # 驗證下一個整數

上方代碼就是根據語法圖重寫的代碼。

不過很顯然,「Parser」不會完成全部解釋工作,它正確的識別表達式之後沒有任何結果,只有在表達式識別失敗的時候會拋出異常。

提示:大家可以嘗試運行程序,輸入「1+2」和「1+」進行驗證。

因為,Interpreter需要表達式的值,所以,我們可以修改「term()」方法,讓它返回一個整數值。

並且,我們還要修改「expr()」方法,讓它在適當的位置執行加法和減法的計算,並且返回解釋的結果。

示例代碼:

def term(self): token = self.current_token # 獲取記號 self.eat(INTEGER) return token.value # 返回記號中的整數值def expr(self): self.current_token = self.get_next_token() result = self.term() # 獲取第一個整數(Term) while self.current_token.value_type in (PLUS, MINUS): if self.current_token.value_type == PLUS: self.eat(PLUS) result += self.term() # 已有結果加上新獲取的整數(Term) if self.current_token.value_type == MINUS: self.eat(MINUS) # 已有結果減去新獲取的整數(Term) result -= self.term() return result

到這裡,我們的解釋器就可以完成多個數字的加減法表達式運算了。

在這篇內容的基礎上,大家可以嘗試進行以下功能的擴展:

  • 畫一個只包含乘法和除法的算術表達式語法圖,例如「7 * 4 / 2 * 3」;(不要認為很簡單就不去做)
  • 重寫Interpreter類的所有源代碼,不要再參考寫過的代碼和文章,只靠自己的思考,讓它可以解釋只包含乘法和除法的算術表達式。

而且,當學習完這篇文章,請大家自我檢查一下,是否了解了以下內容:

  • 什麼是語法圖?
  • 什麼是語法分析?
  • 什麼是語法分析器?

在學習中遇到任何問題,可以留言關注一下喔!


推薦閱讀:

TensorFlow識別知乎驗證碼
求一本 Python 3 的好書,入門級別或中等級別,求推薦?
Python筆記--sklearn函數匯總
深度學習之BP神經網路(三)

TAG:編程語言 | 編程 | Python |