當前位置:編程學習大全網 - 源碼下載 - Python怎樣使用解釋器

Python怎樣使用解釋器

大學裏計算機科學最吸引我的地方就是編譯器。最神奇的是,編譯器是如何讀出我寫的那些爛代碼,並且還能生成那麽復雜的程序。當我終於選了壹門編譯方面的課程時,我發現這個過程比我想的要簡單得多。

在本系列的文章中,我會試著通過為壹種基本命令語言IMP寫壹個解釋器,來展示這種簡易性。因為IMP是壹個簡單廣為人知的語言,所以打算用 Python寫這個解釋器。Python代碼看起來很像偽代碼,所以即使妳不認識 Python,妳也能理解它。解析可以通過壹套從頭開始實現的解析器組合完成(在本系列的下壹篇文章中會有解釋)。除了sys(用於I/O)、re(用於解析正則表達式)以及unittest(用於確保壹切工作正常)庫,沒有使用其他額外的庫。

IMP 語言

在開始寫之前,我們先來討論壹下將要解釋的語言。IMP是擁有下面結構的最小命令語言:

賦值語句(所有變量都是全局的,而且只能存儲整數):

Python

1

x := 1

條件語句:

Python

1

2

3

4

5

if x = 1 then

y := 2

else

y := 3

end

while循環:

Python

1

2

3

while x < 10 do

x := x + 1

end

復合語句(分號分隔):

Python

1

2

x := 1;

y := 2

OK,所以它只是壹門工具語言,但妳可以很容易就把它擴展成比Lua或python更有用的語言。我希望能把這份教程能保持盡量簡單。

下面這個例子是計算階乘的程序:

Python

1

2

3

4

5

6

n := 5;

p := 1;

while n > 0 do

p := p * n;

n := n - 1

end

IMP沒有讀取輸入的方式,所以初始狀態必須是在程序最開始寫壹系列的賦值語句。也沒有打印結果的方式,所以解釋器必須在程序的結尾打印所有變量的值。

解釋器的結構

解釋器的核心是「中間表示」(Intermediate representation,IR)。這就是如何在內存中表示IMP程序。因為IMP是壹個很簡單的語言,中間表示將直接對應於語言的語法;每壹種表達和語句都有對應的類。在壹種更復雜的語言中,妳不僅需要壹個「語法表示」,還需要壹個更容易分析或運行的「語義表示」。

解釋器將會執行三個階段:

將源碼中的字符分割成標記符(token)

將標記符組織成壹棵抽象語法樹(AST)。抽象語法樹就是中間表示。

評估這棵抽象語法樹,並在最後打印這棵樹的狀態

將字符串分割成標記符的過程叫做「詞法分析」,通過壹個詞法分析器完成。關鍵字是很短,易於理解的字符串,包含程序中最基本的部分,如數字、標識符、關鍵字和操作符。詞法分析器會除去空格和註釋,因為它們都會被解釋器忽略。

將標記符組織成抽象語法樹(AST)的過程稱為「解析過程」。解析器將程序的結構提取成壹張我們可以評估的表格。

實際執行這個解析過的抽象語法樹的過程稱為評估。這實際上是這個解析器中最簡單的部分了。

本文會把重點放在詞法分析器上。我們將編寫壹個通用的詞匯庫,然後用它來為IMP創建壹個詞法分析器。下壹篇文章將會重點打造壹個語法分析器和評估計算器。

詞匯庫

詞法分析器的操作相當簡單。它是基於正則表達式的,所以如果妳不熟悉它們,妳可能需要讀壹些資料。簡單來說,正則表達式就是壹種能描述其他字符串的特殊的格式化的字符串。妳可以使用它們去匹配電話號碼或是郵箱地址,或者是像我們遇到在這種情況,不同類型的標記符。

詞法分析器的輸入可能只是壹個字符串。簡單起見,我們將整個輸入文件都讀到內存中。輸出是壹個標記符列表。每個標記符包括壹個值(它代表的字符串)和壹個標記(表示它是壹個什麽類型的標記符)。語法分析器會使用這兩個數據來決定如何構建壹棵抽象語法樹。

由於不論何種語言的詞法分析器,其操作都大同小異,我們將創建壹個通用的詞法分析器,包括壹個正則表達式列表和對應的標簽(tag)。對每壹個表達式,它都會檢查是否和當前位置的輸入文本匹配。如果匹配,匹配文本就會作為壹個標記符被提取出來,並且被加上該正則表達式的標簽。如果該正則表達式沒有標簽,那麽這段文本將會被丟棄。這樣免得我們被諸如註釋和空格之類的垃圾字符幹擾。如果沒有匹配的正則表達式,程序就要報錯並終止。這個過程會不斷循環直到沒有字符可匹配。

下面是壹段來自詞匯庫的代碼:

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

import sys

import re

def lex(characters, token_exprs):

pos = 0

tokens = []

while pos < len(characters):

match = None

for token_expr in token_exprs:

pattern, tag = token_expr

regex = re.compile(pattern)

match = regex.match(characters, pos)

if match:

text = match.group(0)

if tag:

token = (text, tag)

tokens.append(token)

break

if not match:

sys.stderr.write('Illegal character: %sn' % characters[pos])

sys.exit(1)

else:

pos = match.end(0)

return tokens

註意,我們遍歷正則表達式的順序很重要。lex會遍歷所有的表達式,然後接受第壹個匹配成功的表達式。這也就意味著,當使用詞法分析器時,我們應當首先考慮最具體的表達式(像那些匹配算子(matching operator)和關鍵詞),其次才是比較壹般的表達式(像標識符和數字)。

詞法分析器

給定上面的lex函數,為IMP定義壹個詞法分析器就非常簡單了。首先我們要做的就是為標記符定義壹系列的標簽。IMP只需要三個標簽。RESERVED表示壹個保留字或操作符。INT表示壹個文字整數。ID代表標識符。

Python

1

2

3

4

5

import lexer

RESERVED = 'RESERVED'

INT?= 'INT'

ID? = 'ID'

接下來定義詞法分析器將會用到的標記符表達式。前兩個表達式匹配空格和註釋。它們沒有標簽,所以 lex 會丟棄它們匹配到的所有字符。

Python

1

2

3

token_exprs = [

(r'[ nt]+',?None),

(r'#[^n]*',? None),

然後,只剩下所有的操作符和保留字了。記住,每個正則表達式前面的“r”表示這個字符串是“raw”;Python不會處理任何轉義字符。這使我們可以在字符串中包含進反斜線,正則表達式正是利用這壹點來轉義操作符比如“+”和“*”。

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

(r':=',? RESERVED),

(r'(',RESERVED),

(r')',RESERVED),

(r';', RESERVED),

(r'+',RESERVED),

(r'-', RESERVED),

(r'*',RESERVED),

(r'/', RESERVED),

(r'<=',RESERVED),

(r'<', RESERVED),

(r'>=',RESERVED),

(r'>', RESERVED),

(r'=', RESERVED),

(r'!=',RESERVED),

(r'and',? RESERVED),

(r'or',RESERVED),

(r'not',? RESERVED),

(r'if',RESERVED),

(r'then',?RESERVED),

(r'else',?RESERVED),

(r'while', RESERVED),

(r'do',RESERVED),

(r'end',? RESERVED),

最後,輪到整數和標識符的表達式。要註意的是,標識符的正則表達式會匹配上面的所有的保留字,所以它壹定要留到最後。

Python

1

2

3

(r'[0-9]+',INT),

(r'[A-Za-z][A-Za-z0-9_]*', ID),

]

既然正則表達式已經定義好了,我們還需要創建壹個實際的lexer函數。

Python

1

2

def imp_lex(characters):

return lexer.lex(characters, token_exprs)

如果妳對這部分感興趣,這裏有壹些驅動代碼可以測試輸出:

Python

1

2

3

4

5

6

7

8

9

10

11

import sys

from imp_lexer import *

if __name__ == '__main__':

filename = sys.argv[1]

file = open(filename)

characters = file.read()

file.close()

tokens = imp_lex(characters)

for token in tokens:

print token

繼續……

在本系列的下壹篇文章中,我會討論解析器組合,然後描述如何使用他們從lexer中生成的標記符列表建立抽象語法樹。

如果妳對於實現IMP解釋器很感興趣,妳可以從這裏下載全部的源碼。

在源碼包含的示例文件中運行解釋器:

Python

1

python imp.py hello.imp

運行單元測試:

Python

1

python test.py

  • 上一篇:究極綠寶石瞬移金手指代碼
  • 下一篇:pathofexile帝王試煉迷宮怎麽玩的
  • copyright 2024編程學習大全網