成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

使用 Python 開發一個 Python 解釋器

開發 后端
在本文中,我們將設計一個可以執行算術運算的解釋器。一起來看看吧。

 

計算機只能理解機器碼。歸根結底,編程語言只是一串文字,目的是為了讓人類更容易編寫他們想讓計算機做的事情。真正的魔法是由編譯器和解釋器完成,它們彌合了兩者之間的差距。解釋器逐行讀取代碼并將其轉換為機器碼。

在本文中,我們將設計一個可以執行算術運算的解釋器。

我們不會重新造輪子。文章將使用由 David M. Beazley 開發的詞法解析器 —— PLY(Python Lex-Yacc(https://github.com/dabeaz/ply))。

PLY 可以通過以下方式下載:

  1. $ pip install ply 

我們將粗略地瀏覽一下創建解釋器所需的基礎知識。欲了解更多,請參閱這個 GitHub 倉庫(https://github.com/dabeaz/ply)。

標記(Token)

標記是為解釋器提供有意義信息的最小字符單位。標記包含一對名稱和屬性值。

讓我們從創建標記名稱列表開始。這是一個必要的步驟。 

  1. tokens = (  
  2.     # 數據類型  
  3.     "NUM",  
  4.     "FLOAT",  
  5.     # 算術運算  
  6.     "PLUS",  
  7.     "MINUS",  
  8.     "MUL",  
  9.     "DIV",  
  10.     # 括號  
  11.     "LPAREN",  
  12.     "RPAREN",  

詞法分析器(Lexer)

將語句轉換為標記的過程稱為標記化或詞法分析。執行詞法分析的程序是詞法分析器。 

  1. # 標記的正則表達  
  2. t_PLUS   = r"\+"  
  3. t_MINUS  = r"\-"  
  4. t_MUL    = r"\*"  
  5. t_DIV    = r"/"  
  6. t_LPAREN = r"\("  
  7. t_RPAREN = r"\)"  
  8. t_POW    = r"\^"  
  9. # 忽略空格和制表符  
  10. t_ignore = " \t"  
  11. # 為每個規則添加動作  
  12. def t_FLOAT(t):  
  13.     r"""\d+\.\d+"""  
  14.     t.value = float(t.value)  
  15.     return t  
  16. def t_NUM(t):  
  17.     r"""\d+"""  
  18.     t.value = int(t.value)  
  19.     return t  
  20. # 未定義規則字符的錯誤處理  
  21. def t_error(t):  
  22.     # 此處的 t.value 包含未標記的其余輸入  
  23.     print(f"keyword not found: {t.value[0]}\nline {t.lineno}")  
  24.     t.lexer.skip(1)  
  25. # 如果遇到 \n 則將其設為新的一行  
  26. def t_newline(t):  
  27.     r"""\n+"""  
  28.     t.lexer.lineno += t.value.count("\n") 

為導入詞法分析器,我們將使用:

import ply.lex as lex

t_ 是一個特殊的前綴,表示定義標記的規則。每條詞法規則都是用正則表達式制作的,與 Python 中的 re 模塊兼容。正則表達式能夠根據規則掃描輸入并搜索符合的符號串。正則表達式定義的文法稱為正則文法。正則文法定義的語言則稱為正則語言。

定義好了規則,我們將構建詞法分析器。 

  1. data = 'a = 2 +(10 -8)/1.0'  
  2. lexlexer = lex.lex()  
  3. lexer.input(data)  
  4. while tok :lexer.token():  
  5.     print(tok) 

為了傳遞輸入字符串,我們使用 lexer.input(data)。lexer.token() 將返回下一個 LexToken 實例,最后返回 None。根據上述規則,代碼 2 + ( 10 -8)/1.0 的標記將是:

紫色字符代表的是標記的名稱,其后是標記的具體內容。

巴科斯-諾爾范式(Backus-Naur Form,BNF)

大多數編程語言都可以用上下文無關文法來編寫。它比常規語言更復雜。對于上下文無關文法,我們用上下文無關語法,它是描述語言中所有可能語法的規則集。BNF 是一種定義語法的方式,它描述了編程語言的語法。讓我們看看例子:

symbol : alternative1 | alternative2 …

根據產生式,: 的左側被替換為右側的其中一個值替換。右側的值由 | 分隔(可理解為 symbol 定義為 alternative1 或 alternative2或…… 等等)。對于我們的這個算術解釋器,語法規格如下: 

  1. expression : expression '+' expression  
  2.            | expression '-' expression  
  3.            | expression '/' expression  
  4.            | expression '*' expression  
  5.            | expression '^' expression  
  6.            | +expression  
  7.            | -expression  
  8.            | ( expression )  
  9.            | NUM  
  10.            | FLOAT 

輸入的標記是諸如 NUM、FLOAT、+、-、*、/ 之類的符號,稱作終端(無法繼續分解或產生其他符號的字符)。一個表達式由終端和規則集組成,例如 expression 則稱為非終端。

解析器(Parser)

我們將使用 YACC(Yet Another Compiler Compiler) 作為解析器生成器。導入模塊:import ply.yacc as yacc。 

  1. from operator import (add, sub, mul, truediv, pow)  
  2. # 我們的解釋器支持的運算符列表  
  3. ops = {  
  4.     "+": add,  
  5.     "-": sub,  
  6.     "*": mul,  
  7.     "/": truediv,  
  8.     "^": pow,  
  9. def p_expression(p):  
  10.     """expression : expression PLUS expression  
  11.                   | expression MINUS expression  
  12.                   | expression DIV expression  
  13.                   | expression MUL expression  
  14.                   | expression POW expression"""  
  15.     if (p[2], p[3]) == ("/", 0):  
  16.         # 如果除以 0,則將“INF”(無限)作為值  
  17.         p[0] = float("INF")  
  18.     else:  
  19.         p[0] = ops[p[2]](p[1], p[3])  
  20. def p_expression_uplus_or_expr(p):  
  21.     """expression : PLUS expression %prec UPLUS  
  22.                   | LPAREN expression RPAREN"""  
  23.     p[0] = p[2]  
  24. def p_expression_uminus(p):  
  25.     """expression : MINUS expression %prec UMINUS"""  
  26.     p[0] = -p[2]  
  27. def p_expression_num(p):  
  28.     """expression : NUM  
  29.                   | FLOAT"""  
  30.     p[0] = p[1]  
  31. # 語法錯誤時的規則  
  32. def p_error(p):  
  33.     print(f"Syntax error in {p.value}") 

在文檔字符串中,我們將添加適當的語法規范。p 列表中的的元素與語法符號一一對應,如下所示: 

  1. expression : expression PLUS expression  
  2. p[0]         p[1]       p[2] p[3] 

在上文中,%prec UPLUS 和 %prec UMINUS 是用來表示自定義運算的。%prec 即是 precedence 的縮寫。在符號中本來沒有 UPLUS 和 UMINUS 這個說法(在本文中這兩個自定義運算表示一元正號和符號,其實 UPLUS 和 UMINUS 只是個名字,想取什么就取什么)。之后,我們可以添加基于表達式的規則。YACC 允許為每個令牌分配優先級。我們可以使用以下方法設置它: 

  1. precedence = (  
  2.     ("left", "PLUS", "MINUS"),  
  3.     ("left", "MUL", "DIV"),  
  4.     ("left", "POW"),  
  5.     ("right", "UPLUS", "UMINUS")  

在優先級聲明中,標記按優先級從低到高的順序排列。PLUS 和 MINUS 優先級相同并且具有左結合性(運算從左至右執行)。MUL 和 DIV 的優先級高于 PLUS 和 MINUS,也具有左結合性。POW 亦是如此,不過優先級更高。UPLUS 和 UMINUS 則是具有右結合性(運算從右至左執行)。

要解析輸入我們將使用: 

  1. parser = yacc.yacc()  
  2. result = parser.parse(data)  
  3. print(result) 

完整代碼如下: 

  1. #####################################  
  2. # 引入模塊                           #  
  3. #####################################  
  4. from logging import (basicConfig, INFO, getLogger)  
  5. from operator import (add, sub, mul, truediv, pow)  
  6. import ply.lex as lex  
  7. import ply.yacc as yacc  
  8. # 我們的解釋器支持的運算符列表  
  9. ops = {  
  10.     "+": add,  
  11.     "-": sub,  
  12.     "*": mul,  
  13.     "/": truediv,  
  14.     "^": pow,  
  15.  
  16. #####################################  
  17. # 標記集                             #  
  18. #####################################  
  19. tokens = (  
  20.     # 數據類型  
  21.     "NUM",  
  22.     "FLOAT",  
  23.     # 算術運算  
  24.     "PLUS",  
  25.     "MINUS",  
  26.     "MUL",  
  27.     "DIV",  
  28.     "POW",  
  29.     # 括號  
  30.     "LPAREN",  
  31.     "RPAREN",  
  32.  
  33. #####################################  
  34. # 標記的正則表達式                    #  
  35. #####################################  
  36. t_PLUS   = r"\+"  
  37. t_MINUS  = r"\-"  
  38. t_MUL    = r"\*"  
  39. t_DIV    = r"/"  
  40. t_LPAREN = r"\("  
  41. t_RPAREN = r"\)"  
  42. t_POW    = r"\^"  
  43. # 忽略空格和制表符  
  44. t_ignore = " \t"  
  45. # 為每個規則添加動作  
  46. def t_FLOAT(t):  
  47.     r"""\d+\.\d+"""  
  48.     t.value = float(t.value)  
  49.     return t  
  50. def t_NUM(t):  
  51.     r"""\d+"""  
  52.     t.value = int(t.value)  
  53.     return t  
  54. # 未定義規則字符的錯誤處理  
  55. def t_error(t):  
  56.     # 此處的 t.value 包含未標記的其余輸入  
  57.     print(f"keyword not found: {t.value[0]}\nline {t.lineno}")  
  58.     t.lexer.skip(1)  
  59. # 如果看到 \n 則將其設為新的一行 
  60.  def t_newline(t):  
  61.     r"""\n+"""  
  62.     t.lexer.lineno += t.value.count("\n")  
  63. #####################################  
  64. # 設置符號優先級                      #  
  65. #####################################  
  66. precedence = (  
  67.     ("left", "PLUS", "MINUS"),  
  68.     ("left", "MUL", "DIV"),  
  69.     ("left", "POW"),  
  70.     ("right", "UPLUS", "UMINUS")  
  71.  
  72. #####################################  
  73. # 書寫 BNF 規則                      #  
  74. #####################################  
  75. def p_expression(p):  
  76.     """expression : expression PLUS expression  
  77.                   | expression MINUS expression  
  78.                   | expression DIV expression  
  79.                   | expression MUL expression  
  80.                   | expression POW expression"""  
  81.     if (p[2], p[3]) == ("/", 0):  
  82.         # 如果除以 0,則將“INF”(無限)作為值  
  83.         p[0] = float("INF")  
  84.     else:  
  85.         p[0] = ops[p[2]](p[1], p[3])  
  86. def p_expression_uplus_or_expr(p):  
  87.     """expression : PLUS expression %prec UPLUS  
  88.                   | LPAREN expression RPAREN"""  
  89.     p[0] = p[2]  
  90. def p_expression_uminus(p):  
  91.     """expression : MINUS expression %prec UMINUS"""  
  92.     p[0] = -p[2]  
  93. def p_expression_num(p):  
  94.     """expression : NUM  
  95.                   | FLOAT"""  
  96.     p[0] = p[1]  
  97. # 語法錯誤時的規則  
  98. def p_error(p):  
  99.     print(f"Syntax error in {p.value}") 
  100.  #####################################  
  101. # 主程式                             #  
  102. #####################################  
  103. if __name__ == "__main__":  
  104.     basicConfig(level=INFOfilename="logs.txt"
  105.     lexlexer = lex.lex()  
  106.     parser = yacc.yacc()  
  107.     while True:  
  108.         try:  
  109.             result = parser.parse(  
  110.                 input(">>>"),  
  111.                 debug=getLogger())  
  112.             print(result)  
  113.         except AttributeError:  
  114.             print("invalid syntax") 

結論

由于這個話題的體積龐大,這篇文章并不能將事物完全的解釋清楚,但我希望你能很好地理解文中涵蓋的表層知識。 

 

責任編輯:龐桂玉 來源: 馬哥Linux運維
相關推薦

2024-01-31 08:16:38

IPythonPython解釋器

2024-05-15 10:07:11

Agents人工智能CSV

2022-11-02 21:00:21

Python解釋器智能手機

2010-02-01 13:55:12

Python 解釋器

2023-03-10 13:38:00

Python文檔掃描器

2012-08-14 10:44:52

解釋器編程

2024-01-25 11:41:00

Python開發前端

2016-09-12 14:05:27

PythonPython解釋器Web

2022-06-29 09:02:31

go腳本解釋器

2014-04-18 09:31:04

PystonDropboxPython

2019-07-24 13:42:34

Python編程語言代碼

2020-01-10 18:04:01

Python編程語言Windows

2018-08-26 05:38:44

路由器調制解調器網絡設備

2020-10-16 16:28:54

Python開發技術

2010-02-23 15:52:14

Python編輯器

2021-12-01 07:02:55

Python 記錄器按鍵

2021-09-03 12:33:36

語言并發下載器

2023-10-18 10:48:44

Python解釋器

2023-09-05 09:00:00

工具Python抄襲檢測系統

2010-02-24 15:41:53

Python解釋器
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 情侣酒店偷拍一区二区在线播放 | 免费国产一区二区 | 欧美男人天堂 | 精品日韩一区 | 午夜精品一区二区三区在线观看 | 香蕉婷婷 | 久草精品视频 | 久久久性色精品国产免费观看 | 亚洲国产精品成人久久久 | 国产成人av一区二区三区 | 久久久一区二区 | 国产日韩欧美 | 亚洲网在线| 免费在线一区二区 | 久久久久久久久国产精品 | 日韩性在线 | 色久在线 | 久久69精品久久久久久久电影好 | 久久伊人一区二区 | 色婷婷综合久久久中字幕精品久久 | 久久久精品 | 午夜免费视频 | 色免费视频 | 中文字幕精品一区二区三区精品 | 欧美爱爱视频网站 | 国产精品99久久久久久久久久久久 | 韩日一区二区三区 | 亚洲高清一区二区三区 | 久久精品国产亚洲夜色av网站 | 日本五月婷婷 | 久久99精品久久久久久国产越南 | 国产免费看 | 亚洲欧美在线一区 | 精品久久国产 | 中文字幕日韩欧美 | 精品国产一区二区三区成人影院 | 免费播放一级片 | 亚洲免费在线观看视频 | 一区二区三区四区不卡 | 一区二区三区免费 | 一级视频在线免费观看 |