原題 | Adding Actions to a PEG Grammarpython
做者 | Guido van Rossum(Python之父)git
譯者 | 豌豆花下貓(「Python貓」公衆號做者)github
聲明 | 本翻譯是出於交流學習的目的,基於 CC BY-NC-SA 4.0 受權協議。爲便於閱讀,內容略有改動。算法
若是你在語法規則中還能夠添加(某些)語義,那麼語法就會更好。特別是對於我正在構建的 Python 解析器,我須要控制每一個備選項返回的 AST 節點,由於 AST 的格式已經規定好。緩存
【這是個人 PEG 系列的第 6 部分。其他部分請參閱系列概述 】(譯註:本系列的譯文已在 Github 開源,項目地址:https://github.com/chinesehuazhou/guido_blog_translation)數據結構
許多語法都有支持給規則添加動做的約定,一般是 { 花括號 } 內的一段代碼塊。更確切地說,行動與備選項相關聯。動做塊中的代碼一般與編寫編譯器的語言相同,如 C 語言,增長一些工具,用於引用備選項中的條目。在 Python 原始的 pgen 中,我沒有添加此功能,但對於這個新項目,我但願使用它。函數
對於在這一系列博客文章中開發的簡化版解析器生成器,下面是咱們採用的作法。工具
通常而言,動做的語法以下:學習
rule: item item item { action 1 } | item item { action 2 }
由於它會使語法變得冗長,因此解析器生成器一般支持跨行分割規則,例如:ui
rule: item item item { action 1 } | item item { action 2}
它會使語法分析器變得複雜,但可讀性更重要,因此我會使用這種方式。
一個永恆的問題是什麼時候執行動做塊。在 Yacc / Bison 中,由於沒有回溯,一旦規則被解析器識別到,就會執行動做塊。每一個動做會當即執行,這意味着即便操做具備全局反作用,仍是會順利執行(例如更新符號表或其它編譯器數據結構)。
在 PEG 解析器中,由於有無限回溯,咱們有其它的選擇:
我要採用第三個選項——正好咱們用 packrat 算法緩存東西,因此咱們也能夠緩存動做的結果。
關於 {花括號} 裏面的內容,傳統上是使用 C 語言,它約定用 $
符號來引用已識別的備選項(例如,$1
引用第一個條目),並賦值給 $$
以指示動做的結果。
在我看來這太老古董了(我記得曾在 Algol-60 中使用對函數名的賦值,來指定返回值),因此我會用一些更 Pythonic 的方式:在括號內,你須要放置一個單一的表達式,它的值是動做的值,而條目的引用則是一些簡單的名稱,給出着條目的文本。
舉個例子,這是一個簡單的計算器,能夠做加減法:
start: expr NEWLINE { expr } expr: expr '+' term { expr + term } | expr '-' term { expr - term } | term { term } term: NUMBER { float(number.string) }
當咱們運行時,給定輸入 100+50-38-70
,它會識別出各部分並計算答案,計算成((100+50)-38)-70
,固然得出結果爲 42。
一個小細節:在term
的動做中,變量number
保存了一個TokenInfo
對象,所以該動做必須使用其.string
屬性來獲取字符串形式的標識符。
當一個備選項中屢次出現相同的規則名稱時,咱們該怎麼辦?對同一備選項中出現的規則,解析器生成器會給出惟一的名稱,即在隨後出現的規則上添加 一、2 等等。例如:
factor: atom '**' atom { atom ** atom1 } | atom { atom }
它的實現很無聊,因此我請大家 check out 代碼 ,本身看看。試試這個:
python3.8 -m story5.driver story5/calc.txt -g story5.calc.CalcParser
可視化功能如今支持使用左右箭頭鍵來回移動!
本文內容與示例代碼的受權協議:CC BY-NC-SA 4.0
公衆號【Python貓】, 本號連載優質的系列文章,有喵星哲學貓系列、Python進階系列、好書推薦系列、技術寫做、優質英文推薦與翻譯等等,歡迎關注哦。