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

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

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

上一篇文章中,我們一起實現了計算器的加法功能。

並且,在文末給大家留下了一些練習:

  • 讓解釋器支持減法運算;
  • 讓解釋器支持多位整數的運算,例如:12+36;
  • 添加一個方法,讓解釋器能夠處理用戶所輸入表達式中的空格。

我建議大家先獨自嘗試完成這些練習,再繼續本篇文章的學習。

當然,如果你如果已經儘力,但是沒能夠完成練習,那麼,這篇文章將會給你帶來很大的收穫。

在開始這篇文章的主體內容之前,我先引用原文中的一個小故事。

這個故事它所包含的哲理適用於各行各業。

《高效思考的5個要素》的作者 Burger 和 Starbird 在書中分享了一個關於他們觀看國際知名小號演奏家 Tony Plog 為多才多藝的小號演奏者舉辦大師課的故事。學生們首先演奏了複雜的音樂章節,他們表演的都很棒。但是,隨後他們被要求表演非常基礎、簡單的音符。當他們演奏那些音符時,比起之前演奏的複雜樂章顯得有些稚嫩。當他們完成演奏之後,大師也演奏了相同的音符。但大師演奏這些音符時聽起來並不稚嫩,差異非常明顯。Tony 解釋道:掌握簡單音符的演奏,讓演奏者可以更完美地掌握複雜的樂章。

這個故事的結論:要想成為真正的藝術家,你必須集中精力掌握簡單、基礎的概念。

再精密的儀器也都是由簡單的一個一個零件巧妙的組合而成。

在任何一個領域,你必須先掌握這些簡單基礎的概念,再通過靈活的運用才能夠成為這個領域的高手。

接下來,我們再繼續依靠我們所學過的Python3基礎知識,來為我們的計算器或者說四則運算表達式的解釋器添加更多的功能。

根據之前的練習要求,我們逐步來完成這些任務。

一、增加保存當前字元的變數

因為我們是逐個獲取字元進行驗證,一個多位數字需要多次獲取才能夠組成完整的數字生成記號,那麼在記號生成之前,我們需要先使用一個變數保存字元,進行驗證。

示例代碼:(class Interpreter)

def __init__(self, text): ...省略其它代碼... self.current_char = self.text[self.position] # 設置當前字元為指定位置的字元

二、增加獲取下一個字元的方法

因為新增加的功能中表達式中字元的數量不再是3個,而是無法固定的數量。

數字部分可能是多位數字,並且表達式中可能包含任意數量的空格。

這樣的話,我們需要對字元進行循環驗證,會經常用到獲取下一個字元的方法。

那麼,經常用到的重複的方法,我們把它抽象成一個獨立的方法。

示例代碼:

def advance(self): # 定義獲取下一個字元的方法 self.position += 1 # 獲取字元的位置自增 if self.position >= len(self.text): # 如果位置到達字元串的末尾 self.current_char = None # 設置當前字元為None值 else: # 否則 self.current_char = self.text[self.position] # 設置當前字元為指定位置的字元

三、增加跳過空格的方法

如果遇到空格,無論是單個還是連續多個,我們都應該將其跳過。

示例代碼:

def skip_whitespace(self): # 定義跳過空格的方法 while self.current_char is not None and self.current_char.isspace(): # 如果當前字元不是None值並且當前字元是空格 self.advance() # 獲取下一個字元

四、增加獲取多位數字的方法

如果獲取到的字元是數字,無論是單個還是連續多個,我們都將它們連接起來,轉為整數類型後返回。

示例代碼:

def long_integer(self): # 獲取多位數字 result = while self.current_char is not None and self.current_char.isdigit(): # 如果當前字元不是None值並且當前字元是數字 result += self.current_char # 連接數字 self.advance() # 獲取下一個字元 return int(result) # 返回數字

五、修改詞法分析器的代碼

在新版本的詞法分析器中,我們只需要根據當前字元的類型進行不同的處理。

示例代碼:

def get_next_token(self): while self.current_char is not None: # 如果當前字元不是None值 if self.current_char.isspace(): # 如果當前字元是空格 self.skip_whitespace() # 跳過所有空格 continue if self.current_char.isdigit(): # 如果當前字元是整數 return Token(INTEGER, self.long_integer()) # 獲取完整的數字創建記號對象並返回 if self.current_char == +: # 如果當前字元是加號 self.advance() # 跳到下一字元 return Token(PLUS, self.current_char) # 創建記號對象並返回 if self.current_char == -: # 如果當前字元是減號 self.advance() # 跳到下一字元 return Token(MINUS, self.current_char) # 創建記號對象並返回 self.error() # 如果以上都不是,則拋出異常。 return Token(EOF, None) # 遍歷結束返回結束標識創建的記號對象

這一次,詞法分析器的代碼更加清晰。

六、修改表達式計算方法

這裡我們添加減法的支持。

def expr(self): self.current_token = self.get_next_token() left = self.current_token self.eat(INTEGER) operator = self.current_token if operator.value_type == PLUS: # 如果運算符的類型是加法 self.eat(PLUS) # 驗證加法運算符 else: # 否則 self.eat(MINUS) # 驗證減法運算符 right = self.current_token self.eat(INTEGER) if operator.value_type == PLUS: # 如果運算符的類型是加法 result = left.value + right.value # 進行加法運算 else: # 否則 result = left.value - right.value # 進行減法運算 return result

上方代碼中,添加註釋的部分是更新的內容。

到這裡,我們就完成了所有目標功能。

最後,我們仍然需要掌握一些概念。

在上一篇文章中,我們了解了在兩個重要的概念: Token(記號) 和 Lexical Analyzer(詞法分析器)。

這一篇文章中,我們來簡單了解一下 lexeme(詞位)、parsing(語法分析)和 parser(語法分析器)。

1、 lexeme(詞位)

詞位的中文解釋是語言辭彙的基本單位。

在這裡,lexeme就是組成 token 的字元序列。

以當前的案例來說,詞位就是數字、加號和減號:

記號詞位INTEGER1,12,666,69,8,0,3568PLUS+MINUS–

2、parsing(語法分析)和 parser(語法分析器)。

我們所編寫的代碼中,「 expr()」方法是真正的對一個四則運算表達式進行解釋的地方。

但是,在對一個四則運算表達式進行解釋之前,我們需要先識別短語的類型,比如是加法表達式還是減法表達式。

「 expr()」方法本質上就是:先從「 get_next_token()」 方法獲取token流,找到token流中的特定的結構,或者說識別出特定的短語,然後,解釋識別出的短語,生成四則運算表達式的結果。

找到token流中特定結構的過程,或者說,識別token 流中特定短語的過程稱之為Parsing(語法分析)。

解釋器或編譯器中完成語法分析的部分,叫做Parser(語法分析器)。

現在我們知道「 expr()」方法是解釋器中既做了 Parsing(語法分析),又做了Interpreting(解釋)的部分。

「 expr()」方法首先嘗試識別(Parsing)token流中的[INTEGER>>PLUS>>INTEGER]結構或者[INTEGER>>MINUS>>INTEGER] 結構的短語,成功識別其中一種短語後,對短語進行解釋,並將兩整數相加或相減的結果返回。

以上就是《一起來寫一個簡單的解釋器》系列文章的第二篇內容。

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

  • 讓解釋器支持乘法運算;
  • 讓解釋器支持除法運算;
  • 讓解釋器支持任意多加法和減法運算,例如:3+7-4-2+8。

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

  • 什麼是 Lexeme(詞位)?
  • 找出Token流中特定結構的過程叫什麼?或者說,從 Token流中識別出特定短語的過程叫什麼?
  • 解釋器或編譯器中做 parsing(語法分析)部分的叫什麼?

歡迎在下面留言,完善本文內容。


推薦閱讀:

【中天智領人機交互】多點觸控表皮技術可將人體變成遙控裝置!
從CES看2018創新趨勢,那些你熟悉的黑科技全都成真了
史上最全!不同階段計算LTV的方法和模型!
有人說知乎米粉對屏幕一無所知?看完你們就對屏幕一清二楚了
Iphone X發售在即,蘋果Q4營收的亮眼與隱憂

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