簡單回顧下。本項目是爲了重現「木蘭」編程語言編譯器,剛開始原型搭建,與原始版本一樣,用 Python3 和 RPly 實現,照例使用中文命名標識符。
之前實現了加法和打印輸出。此文實現整數的減、乘、除,其中“除”的語義與 Python3 不同。
比如print(2+3*4/5)
在「木蘭」中輸出 4,而不是像 Python3 中輸出的 4.4。
設計分析
可以想見,「木蘭」的除法中,如果倆數是整數,除法結果就仍是整數,而小數部分直接捨去。
而 Python3 無論是否相除的倆數是否整數,都會返回小數部分(如果有的話)。
在進入實現細節之前,想先嚐試猜度一下這種設計的思路。剛搜了下 Python2 的除法語義和「木蘭」是一致的。而 Python3 新加了一個//
達到捨去小數的目的。
估計「木蘭」設計者也是參考了兩種設計,最後選擇了 Python2 的設計。從逆向工程看,「木蘭」實現用的是 Python3。從後面的實現細節可以看出,是額外添加了一點代碼才實現了 Python2 的除法。
相比之下,個人的理解是,Python3 添加一種運算符的一個好處是能讓語義更直觀,省得除法結果依賴於操作數是否是整數,因爲這不一定那麼好確定。而 Python2 的“整數除法得整數”這一層語義在實際應用中的用途感覺並不那麼廣泛。即使在需要實現這一功能時再自行實現也就是調用個floor
函數的事。
那麼爲何仍選擇用 Python2 的除法設計,而不使用//
運算符呢?使用手冊第一趴中可見,「木蘭」選擇了用//
和/* */
作爲註釋標誌。相信這是爲了與市面上佔大頭的 C 系列、Java、JS、PHP 等等編程語言的語法靠近。
語言設計時的各種取捨可見一斑。
具體實現
比較簡單,在之前的基礎上,加減乘部分與最上面的 RPly 入門示例大致相同這裏不重複。下面是除法運算的相關部分。
語法分析部分:
@分析器母機.production('二元表達式 : 表達式 除 表達式')
def 除法(片段):
return 語法樹.調用(
函數=語法樹.名稱(
標識='__除__',
上下文=(ast.Load()),
行號=0,
列號=0),
參數=[片段[0], 片段[2]],
行號=0,
列號=0)
行號列號還是全 0,比較刺眼,會盡快研究。下面是除法內置函數:
def __內置_除(a, b):
if isinstance(a, int):
if isinstance(b, int):
return math.floor(a / b)
return a / b
的確就是用了floor
進行了特殊處理。
完整代碼在此,語法分析部分已上升到 85 行,相比「木蘭」的 1400 行,還有相當距離。
下面打算對錯誤處理和行列號進行研究。
更多「木蘭」相關技術文章,歡迎關注木蘭編程語言知乎專欄。