如何將 token 流轉(zhuǎn)換成抽象語法樹(上)

前言:之前我們不是太艱難地將字符流轉(zhuǎn)換成了 token 流梯嗽,今天我們將嘗試將 token 流轉(zhuǎn)換成「抽象語法樹」阐污,本系列博客大部分內(nèi)容來自 http://www.craftinginterpreters.com/获诈,以下只是我的學(xué)習(xí)筆記。

1+2*3

0X00 基礎(chǔ)理論

這次實(shí)現(xiàn)的抽象語法樹只包括基礎(chǔ)表達(dá)式肾胯,比如:

1+2*3 轉(zhuǎn)換成如下的抽象語法樹:

1+2*3

(非常建議和我一樣在實(shí)現(xiàn)這個解釋器的小伙伴闲勺,先去學(xué)習(xí)理論(華保健老師 編譯原理前 8 章)再動手實(shí)現(xiàn)這個解釋器)

基礎(chǔ)理論——上下文無關(guān)文法

首先我們來感性地認(rèn)識什么是「上下文無關(guān)文法」:

上下文無關(guān)文法沒啥特別的意思,無非就是一種文法規(guī)則灯变。比如我們定義了這樣幾條規(guī)則:

句子 -> 名詞 動詞 名詞
名詞 -> 羊
            | 草
            | 老虎
動詞 -> 吃
            | 喝

這就是上下文無關(guān)文法殴玛。用這個文法我們可以構(gòu)造出來:

  • 羊(名詞)吃(動詞)草(名詞)

  • 老虎(名詞)吃(動詞)羊(名詞)

  • 羊(名詞)吃(動詞)老虎(名詞)

  • ...

接著我們用數(shù)學(xué)的手段描述一下上下文無關(guān)文法:

上下文無關(guān)文法是一個四元組:G(T, N, P, S)

  • T 是終結(jié)符
  • N 是非終結(jié)符
  • P 是產(chǎn)生式規(guī)則
  • S 是唯一的開始符號

套用上面的中文的例子:

T 是「羊 草 老虎 吃 喝」這樣不能被替換的詞

N 是「動詞 名詞」這樣能夠被替換的詞

P 是「句子 -> 名詞 動詞 名詞」這樣描述替換的規(guī)則

S 是 一切的開始

基礎(chǔ)理論——優(yōu)先級和符號關(guān)聯(lián)性

這樣我們就能寫一些有關(guān)本章的「上下文無關(guān)文法」:

expression     → equality ;
equality       → comparison ( ( "!=" | "==" ) comparison )* ;
comparison     → addition ( ( ">" | ">=" | "<" | "<=" ) addition )* ;
addition       → multiplication ( ( "-" | "+" ) multiplication )* ;
multiplication → unary ( ( "/" | "*" ) unary )* ;
unary          → ( "!" | "-" ) unary
               | primary ;
primary        → NUMBER | STRING | "false" | "true" | "nil"
               | "(" expression ")" ;

這是實(shí)現(xiàn)本章 token 流與抽象語法樹轉(zhuǎn)換的關(guān)鍵,其中 * 是可重復(fù)的意思添祸。比如 1 * 2 * 3 * 4

完全理解之前滚粟,我們得先弄懂兩個基本概念:「優(yōu)先級」和「符號關(guān)聯(lián)性」

  • 優(yōu)先級

這一點(diǎn)不用說,* 比 + 高刃泌。這里的符號優(yōu)先級與 C 語言類似凡壤,且在上面那個「上下文無關(guān)文法」中署尤,越在上面的符號,優(yōu)先級越低也就是

( "!=" | "==" ) < ( ">" | ">=" | "<" | "<=") < ( "-" | "+" ) < ( "/" | "*" ) < ( "!" | "-" )

  • 符號關(guān)聯(lián)性

除了 "!" "-" 這兩個符號亚侠,其他符號都是左關(guān)聯(lián)曹体。

-1 !0 右關(guān)聯(lián)

1 + 2 + 3 左關(guān)聯(lián)

0X01 代碼實(shí)現(xiàn)

接著我們要憑借著:

expression     → equality ;
equality       → comparison ( ( "!=" | "==" ) comparison )* ;
comparison     → addition ( ( ">" | ">=" | "<" | "<=" ) addition )* ;
addition       → multiplication ( ( "-" | "+" ) multiplication )* ;
multiplication → unary ( ( "/" | "*" ) unary )* ;
unary          → ( "!" | "-" ) unary
               | primary ;
primary        → NUMBER | STRING | "false" | "true" | "nil"
               | "(" expression ")" ;

實(shí)現(xiàn)抽象語法樹了!注意盖奈,按照這個實(shí)現(xiàn)語法樹混坞,就會完成的符號的優(yōu)先級了,而對于符號的關(guān)聯(lián)性只需要在代碼中具體實(shí)現(xiàn)就好了钢坦!

用的方法很簡單究孕,掃描 token,自頂向下建立抽象語法樹

基本框架

先寫出基本框架:從 parse() 解析 expression 開始:

public class Parser {
    private final List<Token> tokens;
    private int current = 0;

    Parser(List<Token> tokens) {
        this.tokens = tokens;
    }
    Expr parse() {
        return expression();
    }
}

根據(jù)文法寫出替換

我們看到前兩個替換:

expression     → equality ;
equality       → comparison ( ( "!=" | "==" ) comparison )* ;

所以代碼寫成:

    private Expr equality() {
        // equality → comparison ( ( "!=" | "==" ) comparison )* ;
        Expr expr = comparison();

        if (match(BANG_EQUAL, EQUAL_EQUAL)) {
            Token operator = previous();
            Expr right = comparison();
            expr = new Expr.Binary(expr, operator, right);
            return expr;
        }

        return expr;
    }

    private Expr expression() {
        // expression → equality ;
        return equality();
    }

一路順下來就可以寫出最后的代碼見:https://github.com/TensShinet/toy_compiler/blob/master/code/MyLox/src/app/Parser.java爹凹。

最后解釋一下 Expr.Binary 這個是什么

由于現(xiàn)在是簡單的表達(dá)式:1+2*3 這樣的厨诸,所以表達(dá)式的種類并不多。

只有四種:

  • Unary 一元表達(dá)式禾酱。-1
  • Binary 二元表達(dá)式 1 + 2
  • Group 組 (expression)
  • Literal 值 現(xiàn)在只有 數(shù)字 字符串 true false null
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末微酬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子颤陶,更是在濱河造成了極大的恐慌颗管,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滓走,死亡現(xiàn)場離奇詭異垦江,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)搅方,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進(jìn)店門比吭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人姨涡,你說我怎么就攤上這事衩藤。” “怎么了涛漂?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵赏表,是天一觀的道長。 經(jīng)常有香客問我匈仗,道長底哗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任锚沸,我火速辦了婚禮,結(jié)果婚禮上涕癣,老公的妹妹穿的比我還像新娘哗蜈。我一直安慰自己前标,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布距潘。 她就那樣靜靜地躺著炼列,像睡著了一般。 火紅的嫁衣襯著肌膚如雪音比。 梳的紋絲不亂的頭發(fā)上俭尖,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天,我揣著相機(jī)與錄音洞翩,去河邊找鬼稽犁。 笑死,一個胖子當(dāng)著我的面吹牛骚亿,可吹牛的內(nèi)容都是我干的已亥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼来屠,長吁一口氣:“原來是場噩夢啊……” “哼虑椎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起俱笛,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤捆姜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后迎膜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泥技,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年星虹,在試婚紗的時候發(fā)現(xiàn)自己被綠了零抬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡宽涌,死狀恐怖平夜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情卸亮,我是刑警寧澤忽妒,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站兼贸,受9級特大地震影響段直,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜溶诞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一鸯檬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧螺垢,春花似錦喧务、人聲如沸赖歌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽庐冯。三九已至,卻和暖如春坎穿,著一層夾襖步出監(jiān)牢的瞬間展父,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工玲昧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留栖茉,地道東北人。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓酌呆,卻偏偏與公主長得像衡载,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子隙袁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內(nèi)容