使用LL算法構建AST —— 編程訓練

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同學們好,我是來自 《","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"技術銀河","attrs":{}},{"type":"text","text":"》的 💎 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"三鑽","attrs":{}},{"type":"text","text":" 。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在之前的 《","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"前端進階","attrs":{}},{"type":"text","text":"》系列的學習筆記中已經講到過不少跟字符串處理相關的內容。但是我們的主要學習的都是如何進行對字符串做一些初步的分析。我們這裏就來一起學一些邊緣裏面的稍微高級一點的字符串處理,就是使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"LL 算法構建 AST","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"AST","attrs":{}}],"attrs":{}},{"type":"text","text":" 叫做","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"抽象語法樹","attrs":{}}],"attrs":{}},{"type":"text","text":"。我們的代碼在計算機的分析過程中,首先就是把編程語言去分詞,在分詞之後就讓它構成這種層層相互嵌套的語法樹的樹形結構。最後纔是去解析代碼去執行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"構建抽象語法樹的過程又被稱爲","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"語法分析","attrs":{}}],"attrs":{}},{"type":"text","text":"。而最著名的語法分析算法的核心的思想有兩種:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LL 算法","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LR 算法","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏面的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"L","attrs":{}}],"attrs":{}},{"type":"text","text":" 是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Left","attrs":{}}],"attrs":{}},{"type":"text","text":" 的縮寫,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"LL","attrs":{}}],"attrs":{}},{"type":"text","text":" 算法就是從左到右掃描,然後從左到右規約的,也是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Left Left","attrs":{}}],"attrs":{}},{"type":"text","text":" 算法,第一個 Left 就是從左到右掃描,第二個 Left 就是從左到右規約。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼這個掃描規約具體是什麼呢?這個我們暫時可以不用特別細地去理解,後面當我們去實際執行到這部分代碼的時候大家就可以看到具體是怎麼去進行的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b3/b34c995f506fd74ab4d6e4adb20b4706.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"四則運算","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"詞法定義","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏我們要講解的案例是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"四則運算","attrs":{}}],"attrs":{}},{"type":"text","text":"的分析,四則運算包括了:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"TokenNumber","attrs":{}},{"type":"text","text":" —— 數字輸入","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1 2 3 4 5 6 7 8 9 0 的組合 (允許在數字中間插入小數點)","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"Operator","attrs":{}},{"type":"text","text":" (+、-、*、/ 之一) —— 加減乘除運算符","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"Whitespace","attrs":{}},{"type":"text","text":" () —— 空格","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"LineTerminator","attrs":{}},{"type":"text","text":" ( ) —— 換行","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這個四則運算的分析中,我們首先會有 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Number","attrs":{}}],"attrs":{}},{"type":"text","text":" 數字的輸入,然後還有加減兩種運算符,以及我們允許變成語言的使用者添加一些格式化的字符,比如說空格、換行等格式化字符我們就會把他們無視掉。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實在我們的四則運算中,真正有意義的輸入元素就是 Token,一種是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Number","attrs":{}}],"attrs":{}},{"type":"text","text":" 數字,一種是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Operator","attrs":{}}],"attrs":{}},{"type":"text","text":" 運算符。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"語法定義","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先加法和乘法是有一個優先級關係的,所以我們需要用 JavaScript 的產生式去定義它的加法和乘法運算。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏我們就把它的加減乘除做成一個嵌套的結構。我們可以認爲加法是由左右兩個乘法組成的,並且加法是可以進行連加的,所以說加法應該是一個重複自身的一個序列。(這裏也會有一個遞歸的產生式結構,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"這個也是我們在產生式當中,用來處理無限的列表的時候常用的手法","attrs":{}},{"type":"text","text":")","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 就是乘法運算,一個單獨的數字我們也可以認爲是一種特殊的乘法,就是隻有一項的乘法(其實也可以理解爲:任何數 x 1 = 自身,這種也屬於乘法之一)同樣我們把只有稱號認爲是一種特殊的加法,只有一項的加法。這樣就方便我們去遞歸的定義整個表達式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"乘法表達式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"::=","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" |","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" | ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":">","attrs":{}}],"attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先我們來看看最低層級的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" ,它的定義是一個用乘號或者除號相連接的 Number 的序列。我們來參考大家都比較熟悉的遞歸思想的話,我們就會規定它可以是一個單獨的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Number","attrs":{}}],"attrs":{}},{"type":"text","text":",它也可以是一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"乘法表達式","attrs":{}}],"attrs":{}},{"type":"text","text":",後面綴上一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"乘號","attrs":{}}],"attrs":{}},{"type":"text","text":"再加上一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Number","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏上面加入了背景的部分的,就是產生式定義裏面的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"終結符","attrs":{}}],"attrs":{}},{"type":"text","text":",也就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Terminal Symbol","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Terminal Symbol 就是我們直接從詞法裏面掃描出來的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而其他沒有標出來的部分就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"None Terminal Symbol","attrs":{}}],"attrs":{}},{"type":"text","text":",也就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"非終結符","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"非終結符就是我們拿終結符的組合定義出來的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以看到我們去定義這個乘法表達式的非終結符有:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"","attrs":{}},{"type":"text","text":" —— 一個單獨的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Number","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"* —— 自身加上一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"乘號","attrs":{}}],"attrs":{}},{"type":"text","text":" 再加上一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Number","attrs":{}}],"attrs":{}},{"type":"text","text":" ","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":">","attrs":{}},{"type":"text","text":" —— 自身加上一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"除號","attrs":{}}],"attrs":{}},{"type":"text","text":" 再加上一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Number","attrs":{}}],"attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以這就是乘法表達式的一個結構了,當我們遇到這樣一個結構的時候,我們就可以認爲它是一個乘法這類的表達式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"加法表達式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"加法表達式是和乘法的非常的類似的,只不過是基本的單元換成了一個非終結符的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MulplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"::=","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" |","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":"","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" |","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":"","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏的話我們可以擁有一個單獨的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 乘法表達式,或者是加法表達式自身加上 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"加號","attrs":{}}],"attrs":{}},{"type":"text","text":" 再加上 一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"乘法表達式","attrs":{}}],"attrs":{}},{"type":"text","text":"。當然也可以是自身加上 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"減號","attrs":{}}],"attrs":{}},{"type":"text","text":" 再加上一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"乘法表達式","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"整體來說就是數個乘法用加號或者減號連接在一起,那麼就是一個加法表達式的結構了。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"整體的表達式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"::=","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後我們認爲一個能處理的表達式 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Expression","attrs":{}}],"attrs":{}},{"type":"text","text":" ,他就是一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AdditiveExpress","attrs":{}}],"attrs":{}},{"type":"text","text":" 加法表達式。在最後我們引入了一個特殊的符號 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"EOF","attrs":{}}],"attrs":{}},{"type":"text","text":",EOF 它不是一個真實可見的字符。但是因爲","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"我們的語法需要一個終結","attrs":{}},{"type":"text","text":",在分析的過程中如果有一些結構是要求一定要到尾巴才結束的。所以這個 EOF 就是這樣的一個符號,它標識了我們源代碼的結束點。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"EOF","attrs":{}}],"attrs":{}},{"type":"text","text":" 其實就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"End of File","attrs":{}}],"attrs":{}},{"type":"text","text":" 的縮寫,也就是 \"文件底部\"。這個符號也常常被我們用在計算機各種表示中介的場景裏面。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"LL 語法分析","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面我們搞清楚了語法的定義之後,我們接下來就瞭解一下 LL 語法分析是怎麼去做的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"::=","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" |","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":"","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" |","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":"","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們可以用上面的這個加法的表達式爲例,因爲乘法比較簡單。我們應該從這個輸入的這樣的一個序列裏面,我們去看它當前能夠拿到的是什麼樣的東西。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們過一個策劃分析,我們在處理一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AddititiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" ,那麼這個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AdditiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 找到的第一個符號 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"symbol","attrs":{}}],"attrs":{}},{"type":"text","text":" 它會是什麼呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們在產生式裏面我們可以看到,我們可能會面臨着兩種情況。第一種就是開頭就是一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":",第二種就是一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AddititiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"。那是不是就只有這兩種情況呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然不是了,因爲一個乘法表達式,很有可能當前是一個還未解析的狀態。所以我們需要把這個乘法展開。這裏我們把遞歸中乘法的部分加入進來。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">::=","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">|","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">| ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":">","attrs":{}}],"attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">|","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":"","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">|","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":"","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 那麼乘法的表達式它可能是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Number","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AdditiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 等一系列的這樣的可能性。所以說加法表達式中的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 是有三種可能性的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼如果我們在加法表達式中遇到的是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Number","attrs":{}}],"attrs":{}},{"type":"text","text":" 或者是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":",我們時不時就應該把它直接當成乘法去處理呢?但是這裏我們只看一個字是不夠的,我們需要去看它第二個輸入的元素是乘號、除號、加號還是減號。因爲原來的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 還是在的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以說我們通過這個,就可以得出來一個從左到右的掃描,然後從左到右去歸併的一個語法分析的算法。那麼這就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"LL 語法分析","attrs":{}}],"attrs":{}},{"type":"text","text":"了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b3/b34c995f506fd74ab4d6e4adb20b4706.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":" 代碼實現","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來我們去看看我們的語法分析,在代碼上具體是怎麼樣去實現的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"實現正則表達式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"/([0-9\\.]+)|([ \\t]+)|([\\r\\n]+)|(\\*)|(\\/)|(\\+)|(\\-)/g","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼首先我們寫一個正則表達式來獲取我們需要的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"symbol","attrs":{}}],"attrs":{}},{"type":"text","text":",這個正則表達式它是以 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"或關係","attrs":{}}],"attrs":{}},{"type":"text","text":" 分開的。然後每一個裏面都是一個換括號的結構。那爲什麼要用圓括號呢?因爲在正則裏面圓括號表示","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"捕獲","attrs":{}}],"attrs":{}},{"type":"text","text":",那麼我們一旦對它進行了捕獲,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"除了這個正則表達式整體表示的字符串,圓括號裏面的內容也會直接被匹配出來。而這個是正則表達式的一個特性,這個特性就是專門爲詞法分析而準備的正則語法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲這裏我們用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"或","attrs":{}}],"attrs":{}},{"type":"text","text":"分割開了每一個正則規則,所以正則表達式整體匹配的內容每次只會匹配到一個或關係的分支裏面。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以這裏我們給每一個分支都取一個名字(也叫 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":" 名),我們把名字依次按順序放入一個數組裏面:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\n\n// 正則表達式\nlet regexp = /([0-9\\.]+)|([ \\t]+)|([\\r\\n]+)|(\\*)|(\\/)|(\\+)|(\\-)/g;\n// 正則分支名字\nlet dictionary = ['Number', 'Whitespace', 'LineTerminator', '*', '/', '+', '-'];","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏面 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"0-9","attrs":{}}],"attrs":{}},{"type":"text","text":" 包含了所有數字","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"\\t","attrs":{}}],"attrs":{}},{"type":"text","text":" 前面的是空格,而 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"\\t","attrs":{}}],"attrs":{}},{"type":"text","text":" 就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"tab","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"\\r","attrs":{}}],"attrs":{}},{"type":"text","text":" 就是回車,而 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"\\n","attrs":{}}],"attrs":{}},{"type":"text","text":" 就是新行","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後的就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"加減乘除","attrs":{}}],"attrs":{}},{"type":"text","text":" 了","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來我們來看看整體代碼的實現:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\n\n// 正則表達式\nlet regexp = /([0-9\\.]+)|([ \\t]+)|([\\r\\n]+)|(\\*)|(\\/)|(\\+)|(\\-)/g;\n// 正則分支名字\nlet dictionary = ['Number', 'Whitespace', 'LineTerminator', '*', '/', '+', '-'];\n\n// 接下來我們就可以去做 `tokenize` \nfunction tokenize(source) {\n let result = null;\n while (true) {\n // 使用正則表達式裏面的 `exec` 函數,去讓它不斷的掃描整個原字符裏面的內容。\n result = regexp.exec(source);\n \n // result 裏面沒有東西就直接退出\n if (!result) break;\n \n // 如果 result 裏面有東西\n // 那我們就根據 result 的位置\n // 從 1 到 7 的範圍裏面匹配到哪一種輸入元素\n //( 首先正則返回的第 0 個是整個結果,所以我們從 1 開始\n // 然後我們的匹配總數一個有 7 個所以是從 1 到 7 )\n for (let i = 1; i <= dictionary.length; i++) {\n if (result[i]) console.log(dictionary[i - 1]);\n }\n // 這裏我們把 token log 出來\n console.log(result)\n }\n}\n\ntokenize(\"1024 + 10 * 25\");","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":" 是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"1024 + 10 * 25","attrs":{}}],"attrs":{}},{"type":"text","text":",根據這個我們可以依次看到如下結果:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先出來的會是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"1024","attrs":{}}],"attrs":{}},{"type":"text","text":",這個是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Number","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來就是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"空格符","attrs":{}}],"attrs":{}},{"type":"text","text":",也就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Whitespace","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"+","attrs":{}}],"attrs":{}},{"type":"text","text":",對應的就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"+","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來還是一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"空格符","attrs":{}}],"attrs":{}},{"type":"text","text":",Token 是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Whitespace","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"10","attrs":{}}],"attrs":{}},{"type":"text","text":",Token 是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Number","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以此類推,我們發現這個代碼是會從左到右掃描我們的字符,找到所有對應的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣就是我們想要的一個詞法分析的結果了。這裏我們初步完成了詞法分析的正則。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b3/b34c995f506fd74ab4d6e4adb20b4706.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"LL 詞法分析","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"好我們接着來繼續學習 LL 語法分析。前面我們已經把詞法大概做完了,接下來我們把代碼稍微做一些整理。另外我們把代碼寫的更好看一點,還有設計一下使用的方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面的正則和字典都是不需要變。我們在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"tokenize","attrs":{}}],"attrs":{}},{"type":"text","text":" 的部分開始進行改造。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"tokenize","attrs":{}}],"attrs":{}},{"type":"text","text":" 這裏我們給它天上了一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lastIndex","attrs":{}}],"attrs":{}},{"type":"text","text":",因爲我們現在的代碼裏面還沒有去做判斷,比如說匹配出來的長度與我們前進的長度不一樣時怎麼辦。所以這裏我們要把這個邏輯補上。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\n\n// 正則表達式\nlet regexp = /([0-9\\.]+)|([ \\t]+)|([\\r\\n]+)|(\\*)|(\\/)|(\\+)|(\\-)/g;\n// 正則分支名字\nlet dictionary = ['Number', 'Whitespace', 'LineTerminator', '*', '/', '+', '-'];\n\n// 接下來我們就可以去做 `tokenize` \nfunction tokenize(source) {\n let result = null;\n let lastIndex = 0;\n while (true) {\n // 每次去取出 lastIndex\n lastIndex = regexp.lastIndex;\n \n // 使用正則表達式裏面的 `exec` 函數,去讓它不斷的掃描整個原字符裏面的內容。\n result = regexp.exec(source);\n \n // result 裏面沒有東西就直接退出\n if (!result) break;\n \n // 與新生成的 lastIndex 去做比較\n // 如果長度超了,那就說明,這裏面有我們不認識的字符或者格式\n if (regexp.lastIndex - lastIndex > result[0].length) break;\n \n // 如果 result 裏面有東西\n // 那我們就根據 result 的位置\n // 從 1 到 7 的範圍裏面匹配到哪一種輸入元素\n //( 首先正則返回的第 0 個是整個結果,所以我們從 1 開始\n // 然後我們的匹配總數一個有 7 個所以是從 1 到 7 )\n for (let i = 1; i <= dictionary.length; i++) {\n if (result[i]) console.log(dictionary[i - 1]);\n }\n // 這裏我們把 token log 出來\n console.log(result)\n }\n}\n\ntokenize(\"1024 + 10 * 25\");","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們輸出 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"break","attrs":{}}],"attrs":{}},{"type":"text","text":" 的地方其實更應該 throw 一個 error 出來的,但是這裏我們就不去做這種錯誤處理了。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"之前我們只是把 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":" 打印了出來,接下來我們試着把這個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":" 變成一個有效的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":",就是把 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":" 存儲起來。所以這裏我們定義一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":" 的對象。然後我們的對象裏面有 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"type","attrs":{}}],"attrs":{}},{"type":"text","text":" 類型和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"value","attrs":{}}],"attrs":{}},{"type":"text","text":" 值兩個屬性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來我們就可以在 while 循環裏面從我們 regex 匹配出來的數據存儲到我們的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":" 對象裏面。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當我們拿到一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":" 的類型和值之後,我們還需要加入一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"yield token","attrs":{}}],"attrs":{}},{"type":"text","text":"。因爲我們的這個函數是不斷地在找出多個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":" 。當然我們可以用回調函數的方式來吐出所有找到的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":",但是一個更好的做法就是使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"yeild","attrs":{}}],"attrs":{}},{"type":"text","text":"。這裏我們就利用一下新的 JavaScript 語法特性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當我們要返回一個序列的時候,我們就使用這個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"yield","attrs":{}}],"attrs":{}},{"type":"text","text":",這樣這個變量的值就會被輸出出來。其實大家應該都看出來了,使用了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"yield","attrs":{}}],"attrs":{}},{"type":"text","text":",那麼我們的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"tokenize","attrs":{}}],"attrs":{}},{"type":"text","text":" 函數就必須是一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"generator","attrs":{}}],"attrs":{}},{"type":"text","text":" 函數。所以我們函數的聲明就要改爲 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"fucntion* tokenize(source)","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個時候我們就可以使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"for of","attrs":{}}],"attrs":{}},{"type":"text","text":" 來把所有的 token 給打印出來了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最終的代碼如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\n\n// 正則表達式\nlet regexp = /([0-9\\.]+)|([ \\t]+)|([\\r\\n]+)|(\\*)|(\\/)|(\\+)|(\\-)/g;\n// 正則分支名字\nlet dictionary = ['Number', 'Whitespace', 'LineTerminator', '*', '/', '+', '-'];\n\n// 接下來我們就可以去做 `tokenize` \nfunction* tokenize(source) {\n let result = null;\n let lastIndex = 0;\n while (true) {\n // 每次去取出 lastIndex\n lastIndex = regexp.lastIndex;\n\n // 使用正則表達式裏面的 `exec` 函數,去讓它不斷的掃描整個原字符裏面的內容。\n result = regexp.exec(source);\n\n // result 裏面沒有東西就直接退出\n if (!result) break;\n\n // 與新生成的 lastIndex 去做比較\n // 如果長度超了,那就說明,這裏面有我們不認識的字符或者格式\n if (regexp.lastIndex - lastIndex > result[0].length) break;\n \n // 定義一個 token 變量\n let token = {\n type: null,\n value: null\n }\n \n // 如果 result 裏面有東西\n // 那我們就根據 result 的位置\n // 從 1 到 7 的範圍裏面匹配到哪一種輸入元素\n //( 首先正則返回的第 0 個是整個結果,所以我們從 1 開始\n // 然後我們的匹配總數一個有 7 個所以是從 1 到 7 )\n for (let i = 1; i <= dictionary.length; i++) {\n // result 中有值,就存儲當前類型\n if (result[i]) token.type = dictionary[i - 1];\n }\n // 這裏存儲值\n token.value = result[0];\n yield token;\n }\n \n yield {\n type: 'EOF',\n };\n}\n\nfor (let token of tokenize('1024 + 10 * 25')) {\n console.log(token);\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那爲什麼我們要這麼處理呢?因爲這樣處理我們的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"tokenize","attrs":{}}],"attrs":{}},{"type":"text","text":"函數會變成一個完全異步的形式。當我們需要一個新的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":" 的時候它就會回來去找一個新的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果還記得我們做語法分析的時候,我們最終的表達式的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"終結符","attrs":{}}],"attrs":{}},{"type":"text","text":"是一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"EOF","attrs":{}}],"attrs":{}},{"type":"text","text":"。所以我們在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"tokenize","attrs":{}}],"attrs":{}},{"type":"text","text":" 函數的結尾給它添加一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"yeild { type: 'EOF' }","attrs":{}}],"attrs":{}},{"type":"text","text":"。 這裏 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"EOF","attrs":{}}],"attrs":{}},{"type":"text","text":" 它是可以沒有 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"value","attrs":{}}],"attrs":{}},{"type":"text","text":" 的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"寫到這裏我們的詞法分析器就完成了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這部分使用到的技巧,都是我們在處理一些詞法分析的時候常常使用的一種辦法。就是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"使用正則表達式和正則表達式的捕獲關係直接去處理詞法","attrs":{}},{"type":"text","text":"。這個對大部分的語言的詞法分析來說都已經足夠了。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/b3/b34c995f506fd74ab4d6e4adb20b4706.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"LL 語法分析","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"完成了 LL 的詞法分析之後,我們就可以正式進入 LL 語法分析。也就是真實的去解讀這段段運算,並且找到語法的規則從而進行真實的運算了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LL 的語法分析的基本結構就是每一個產生式,對應着一個我們的函數。所以我們先把這幾個函數的名字先寫出來。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\n\n/**\n* 表達式\n* @param tokens\n*/\nfunction Expression(tokens) {}\n\n/**\n* 加法表達式\n* @param source\n*/\nfunction AdditiveExpression(source) {}\n\n/**\n* 乘法表達式\n* @param source\n*/\nfunction MultiplicativeExpression(source) {\n console.log(source);\n}\n\nMultiplicativeExpression(source);","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲這個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"四則運算","attrs":{}}],"attrs":{}},{"type":"text","text":"的語法結構還是非常的簡單的,所以說他在這裏我們只需要一個逐級的關係即可。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們先從最簡單,最底層的一級,最貼近終結符的開始編寫。那就是我們的乘法 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"。在這個方法裏面我們就直接加入了一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"console.log(source)","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"着這個之間我們使用我們之前寫好的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"tokenize","attrs":{}}],"attrs":{}},{"type":"text","text":" 把 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"token","attrs":{}}],"attrs":{}},{"type":"text","text":" 的內容推入我們的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"source","attrs":{}}],"attrs":{}},{"type":"text","text":" 裏面。這裏我們要注意的是,我們是需要忽略掉一些格式化的字符類型的:比如","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"空格","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"換行符","attrs":{}}],"attrs":{}},{"type":"text","text":"這兩種。所以在把 token 推入 source 之前我們需要先過濾一下這種類型的字符。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最終的代碼如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\n\n// 這裏 tokenize 的代碼忽略了,因爲沒有需要更改的\n// 就不重複在這裏編寫了\n\nlet source = [];\n\nfor (let token of tokenize('10 * 25')) {\n if (token.type !== 'Whitespace' && token.type !== 'LineTerminator')\n source.push(token);\n}\n\n/**\n* 表達式\n* @param tokens\n*/\nfunction Expression(tokens) {}\n\n/**\n* 加法表達式\n* @param source\n*/\nfunction AdditiveExpression(source) {}\n\n/**\n* 乘法表達式\n* @param source\n*/\nfunction MultiplicativeExpression(source) {\n console.log(source);\n}\n\nMultiplicativeExpression(source);","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這段代碼最後調用了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":",而 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"的方法裏面只是把 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"source","attrs":{}}],"attrs":{}},{"type":"text","text":" 原封不動的打印出來。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"MultipicativeExpression","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們首先來看如何實現 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"MultiplicativeExpression 的語法定義中的表達式,它開頭的第一個有可能是兩個輸入。第一個輸入有可能是一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Number","attrs":{}}],"attrs":{}},{"type":"text","text":",另一個是它自身一樣的類型,就是另外一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"。而它的第二個輸入","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 又有兩種可能,第一個就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"*","attrs":{}}],"attrs":{}},{"type":"text","text":"乘法,第二種就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"/","attrs":{}}],"attrs":{}},{"type":"text","text":" 也就是除法。如果遇到其他的呢,我們就不認識了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以我們在處理上我們就會拆分成三個邏輯分支:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一個邏輯分支就是處理 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Number","attrs":{}}],"attrs":{}},{"type":"text","text":" 這種情況的","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二個就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 後面跟着一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"*","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第三個就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 後面跟一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"/","attrs":{}}],"attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種情況下我們會進行合併,就是會把它最後形成一個新的語法結構,最終產生一個非終結符。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果說我們遇到一個我們不認識的情況的話,我們就可以直接 return,讓程序直接退出這個函數。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"好我們先來看第一種情況:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Number","attrs":{}}],"attrs":{}},{"type":"text","text":" 怎麼樣去處理","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們遇到的類型是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Number","attrs":{}}],"attrs":{}},{"type":"text","text":" 的話,因爲一個 Number 我們就可以把它形成一個 MultiplicativeExpression 的結構了,所以說我們就新建一個節點。這個節點是一個非終結符 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"NoneTerminalSymbol","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\n\nlet node = {\n type: \"MultiplicativeExpression\",\n children: [source[0]]\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個新的非終結符他會有一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"children","attrs":{}}],"attrs":{}},{"type":"text","text":" 屬性。因爲它是從 Number 構造起來的,所以它的 children 裏面我們就把 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"source[0]","attrs":{}}],"attrs":{}},{"type":"text","text":" 裏面的 Number 放入 children 裏面。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲我們產生式是一個遞歸的結構,我們的表達式也是一個遞歸的結構,所以當我們生成好了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 之後,後面還有可能是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"*","attrs":{}}],"attrs":{}},{"type":"text","text":" 或者是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"/","attrs":{}}],"attrs":{}},{"type":"text","text":"。所以我們要遞歸的去調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 函數。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最終我們第一節的完整代碼如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\n\n/**\n* 乘法表達式\n* @param source\n*/\nfunction MultiplicativeExpression(source) {\n // Number 類型\n if (source[0].type === 'Number') {\n let node = {\n type: 'MultiplicativeExpression',\n children: [source[0]]\n }\n source[0] = node;\n return MultiplicativeExpression(source);\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後我們來看第二種情況:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"後面出現了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"*","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實這個星號跟除號本來是可以寫在一起的,這裏爲了跟我們之前講到的產生式保持一致,一一對應起來,所以這裏我們就拆成兩個 if 判斷來實現了。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏也一樣我們先定義一個新的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"node","attrs":{}}],"attrs":{}},{"type":"text","text":",唯一的區別就是這個新節點裏面多了一個屬性叫 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"operator","attrs":{}}],"attrs":{}},{"type":"text","text":"。因爲這裏我們遇到了一個星號,所以我們就把","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"*","attrs":{}}],"attrs":{}},{"type":"text","text":"賦予這個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"operator","attrs":{}}],"attrs":{}},{"type":"text","text":"屬性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\n\nlet node = {\n type: 'MultiplicativeExpression',\n operator: '*',\n children: []\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後我們把 source 的前三項都用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":".shift()","attrs":{}}],"attrs":{}},{"type":"text","text":" 獲取出來放入 node 的 children 裏面,最後把新生成的結構放回 source 裏面。最後同樣我們需要去遞歸一次。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\n\n/**\n * 乘法表達式\n * @param source\n */\nfunction MultiplicativeExpression(source) {\n // Number 類型\n if (source[0].type === 'Number') {\n let node = {\n type: 'MultiplicativeExpression',\n children: [source[0]],\n };\n source[0] = node;\n return MultiplicativeExpression(source);\n }\n // 乘法\n if (source[0].type === 'MultiplicativeExpression' && source[1] && source[1].type === '*') {\n let node = {\n type: 'MultiplicativeExpression',\n operator: '*',\n children: [],\n };\n node.children.push(source.shift());\n node.children.push(source.shift());\n node.children.push(source.shift());\n source.unshift(node);\n return MultiplicativeExpression(source);\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除法與乘法的邏輯沒有任何的本質區別,就是抄寫一遍,把乘號變成了除號而已。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\n\n/**\n * 乘法表達式\n * @param source\n */\nfunction MultiplicativeExpression(source) {\n // Number 類型\n if (source[0].type === 'Number') {\n let node = {\n type: 'MultiplicativeExpression',\n children: [source[0]],\n };\n source[0] = node;\n return MultiplicativeExpression(source);\n }\n // 乘法\n if (source[0].type === 'MultiplicativeExpression' && source[1] && source[1].type === '*') {\n let node = {\n type: 'MultiplicativeExpression',\n operator: '*',\n children: [],\n };\n node.children.push(source.shift());\n node.children.push(source.shift());\n node.children.push(source.shift());\n source.unshift(node);\n return MultiplicativeExpression(source);\n }\n // 除法\n if (source[0].type === 'MultiplicativeExpression' && source[1] && source[1].type === '/') {\n let node = {\n type: 'MultiplicativeExpression',\n operator: '/',\n children: [],\n };\n node.children.push(source.shift());\n node.children.push(source.shift());\n node.children.push(source.shift());\n source.unshift(node);\n return MultiplicativeExpression(source);\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後我們這裏需要加入遞歸結束的條件,遞歸結束的條件就是 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"source[0] 的 type 是 MultiplicativeExpression 但是它的後面又不是乘號也不是除號","attrs":{}},{"type":"text","text":"。因爲前面兩個 if 都有 return 的分支, 所以這一段自然就是一個 else 分支。(也就是說如果能進入前面的 if 判斷都會被 return 的,所以如果沒有被退出這個函數,必然就會進入我們這段代碼。)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼這裏這段邏輯,肯定就是一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"return source[0]","attrs":{}}],"attrs":{}},{"type":"text","text":",這就說明我們已經把所有的乘法都處理完畢了。因爲除了乘法和除法,我們還有可能遇到不認識的情況,所以這裏最後我們加了一個默認遞歸自己,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"return MultiplicativeExpression(source);","attrs":{}}],"attrs":{}},{"type":"text","text":"。但是現實中是不應該有這種情況的,我們最後這裏加的這個 return 應該是永遠不會執行的。因爲如果遇到了不符合的字符,那肯定就已經在前面的步驟被退出了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後我們的代碼如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\n\n/**\n * 乘法表達式\n * @param source\n */\nfunction MultiplicativeExpression(source) {\n // Number 類型\n if (source[0].type === 'Number') {\n let node = {\n type: 'MultiplicativeExpression',\n children: [source[0]],\n };\n source[0] = node;\n return MultiplicativeExpression(source);\n }\n // 乘法\n if (source[0].type === 'MultiplicativeExpression' && source[1] && source[1].type === '*') {\n let node = {\n type: 'MultiplicativeExpression',\n operator: '*',\n children: [],\n };\n node.children.push(source.shift());\n node.children.push(source.shift());\n node.children.push(source.shift());\n source.unshift(node);\n return MultiplicativeExpression(source);\n }\n // 除法\n if (source[0].type === 'MultiplicativeExpression' && source[1] && source[1].type === '/') {\n let node = {\n type: 'MultiplicativeExpression',\n operator: '/',\n children: [],\n };\n node.children.push(source.shift());\n node.children.push(source.shift());\n node.children.push(source.shift());\n source.unshift(node);\n return MultiplicativeExpression(source);\n }\n \n if (source[0].type === 'MultiplicativeExpression') {\n return source[0];\n }\n \n return MultiplicativeExpression(source);\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們進入調試模式,走一篇代碼的話,首先剛進入 MultiplicativeExpression 的時候,我們的 source 就是通過 tokenize() 函數之後的結果。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們的 source 是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"10 * 25","attrs":{}}],"attrs":{}},{"type":"text","text":",那麼 MultiplicativeExpression 執行完畢之後,我們 source 數組最後只會有兩個元素,一個 type 是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":",另一個 type 是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"EOF","attrs":{}}],"attrs":{}},{"type":"text","text":"。因爲在我們處理 MultiplicativeExpression 的時候已經把所有屬於 MultiplicativeExpression 的節點合併到 MultiplicativeExpression 裏面了。所以如果我們的代碼無誤的話,就只會剩餘這兩個元素。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"AddictiveExpression","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"各位同學看到這裏應該都會有一些疲倦了,不過我們離完成已經不遠了。革命尚未成功,我們堅持一下!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接下來我們一起來完成 LL 語法分析的剩餘的部分,我們來實現一下比較複雜的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AddictiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":",加法語法分析。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"AddictiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"的三種情況是與我們的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"的邏輯是基本一樣的,只需要換一下名字和符號就有了基本的邏輯結構了。但是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AddictiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"是需要處理它的產生式的三種情況之外,還有","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"所有的邏輯。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏估計大家都要懵了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a6/a6b050f46c6b36fd26c6181d1b746e33.gif","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不要慌張,我們先回去看看我們 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AddictiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"的產生式就明白了:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">\\::=","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">\\","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">|\\","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":"\\","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">|\\","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":"\\","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過展開 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":"之後我們得到:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"::=","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"|","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"| ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":">","attrs":{}}],"attrs":{}},{"type":"text","text":" ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"|","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":"","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"|","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}},{"type":"text","text":"","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果還記得我們在講解四則運算的語法定義的時候,加法的表達式的第一部分是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":",所以上面的表達式當中的頭三個其實是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"對不對?所以在處理加法的這裏,我們也需要去調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression()","attrs":{}}],"attrs":{}},{"type":"text","text":" 函數,先處理掉 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":",然後再執行我們加法中的情況。(加法中的情況也就是上面表達式裏面的最後兩條)。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果還是蒙圈,那就","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"再讀多百遍,其義自見","attrs":{}},{"type":"text","text":"!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/12/12f4911557f68ddadee3f3f1e194609b.gif","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"簡單的說就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AdditiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 是包含了所有 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 的邏輯的,因爲 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AdditiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"的第一條就是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"。所以說我們在加法表達式函數中找到一個不認識的東西的時候,我們需要取調用一次","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"函數,然後再重新調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AdditiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"函數。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bd/bd95a0f527518180b8a254451db42abe.gif","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"懂了嗎?如果還是沒有,那就繼續再讀百遍,其義真的會自見的!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"迴歸正題,迴歸正題,迴歸正題啦!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以說 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AdditiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"的第一條分支邏輯,就是第一次進到","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AdditiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"的時候我們就會執行以下這個代碼先:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\nMultiplicativeExpression(source);\nreturn AddictiveExpression(source);","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實就是如果剛進來 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AdditiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 的時候發現還有加法的字符,那就要先去","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"中處理掉之後,才能再回來","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AdditiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"繼續處理。畢竟只會處理加減的人,不可能讓他去處理乘除法嘛。術業有專攻!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AdditiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 本身的邏輯也會比 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 有一個更復雜的地方,就是它的第三項是一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們再來看看","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AdditiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"的產生式:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"::=","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"|","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"|>","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"|","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"|","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏高亮的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":" 是一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"非終結符","attrs":{}}],"attrs":{}},{"type":"text","text":"。非終結符就意味着它本身也需要產生一次,所以我們在使用這個第三項之前我們還要而外的掉一次 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression()","attrs":{}}],"attrs":{}},{"type":"text","text":" 函數,去把 source 裏面的這個非終結符給它處理掉。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以在如要 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"+","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-","attrs":{}}],"attrs":{}},{"type":"text","text":" 符號的時候,就會比 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"MultiplicativeExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"多執行一行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"多說只會懵,我們直接來看看代碼是如何實現,可能不用再讀百變也會其義自見了!哈哈。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\n\n/**\n * 加法表達式\n * @param source\n */\nfunction AdditiveExpression(source) {\n // 第一個遇到乘法表達式時\n if (source[0].type === 'MultiplicativeExpression') {\n let node = {\n type: 'AdditiveExpression',\n children: [source[0]],\n };\n source[0] = node;\n return AdditiveExpression(source);\n }\n // 加法\n if (source[0].type === 'AdditiveExpression' && source[1] && source[1].type === '+') {\n let node = {\n type: 'AdditiveExpression',\n operator: '+',\n children: [],\n };\n node.children.push(source.shift());\n node.children.push(source.shift());\n MultiplicativeExpression(source);\n node.children.push(source.shift());\n source.unshift(node);\n return AdditiveExpression(source);\n }\n // 減法\n if (source[0].type === 'AdditiveExpression' && source[1] && source[1].type === '-') {\n let node = {\n type: 'AdditiveExpression',\n operator: '-',\n children: [],\n };\n node.children.push(source.shift());\n node.children.push(source.shift());\n MultiplicativeExpression(source);\n node.children.push(source.shift());\n source.unshift(node);\n return AdditiveExpression(source);\n }\n\n if (source[0].type === 'AdditiveExpression') {\n return source[0];\n }\n\n MultiplicativeExpression(source);\n return AdditiveExpression(source);\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"Expression","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼最後我們就是實現我們的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"整體的表達式","attrs":{}}],"attrs":{}},{"type":"text","text":" 。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實最後這個步驟就是把 Expression 的整體加上 EOF 的結構,然後給他產生一下。進入 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Expression()","attrs":{}}],"attrs":{}},{"type":"text","text":" 的時候我們就判斷一下 source 的第一個元素是不是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AdditiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"。因爲它同樣會包含了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AdditiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":"的所有邏輯,所以這裏判斷 source 第一個是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AdditiveExpression","attrs":{}}],"attrs":{}},{"type":"text","text":",然後第二個是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"EOF","attrs":{}}],"attrs":{}},{"type":"text","text":" 的話,Expression 就會總結,然後給我們的 source resolve 成一個最終節點。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們來看看 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Expression()","attrs":{}}],"attrs":{}},{"type":"text","text":"函數的 代碼:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\n\n/**\n * 表達式\n * @param tokens\n */\nfunction Expression(tokens) {\n if (source[0].type === 'AdditiveExpression' && source[1] && source[1].type === 'EOF') {\n let node = {\n type: 'Expression',\n children: [source.shift(), source.shift()],\n };\n source.unshift(node);\n return node;\n }\n AdditiveExpression(source);\n return Expression(source);\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後我們來看看整個 LL 語法分析的代碼:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"javascript"},"content":[{"type":"text","text":"/** Crafted by 三鑽 **/\n/** @website https://tridiamond.tech **/\n\n// 正則表達式\nlet regexp = /([0-9\\.]+)|([ \\t]+)|([\\r\\n]+)|(\\*)|(\\/)|(\\+)|(\\-)/g;\n// 正則分支名字\nlet dictionary = ['Number', 'Whitespace', 'LineTerminator', '*', '/', '+', '-'];\n\n// 接下來我們就可以去做 `tokenize`\nfunction* tokenize(source) {\n let result = null;\n let lastIndex = 0;\n while (true) {\n // 每次去取出 lastIndex\n lastIndex = regexp.lastIndex;\n\n // 使用正則表達式裏面的 `exec` 函數,去讓它不斷的掃描整個原字符裏面的內容。\n result = regexp.exec(source);\n\n // result 裏面沒有東西就直接退出\n if (!result) break;\n\n // 與新生成的 lastIndex 去做比較\n // 如果長度超了,那就說明,這裏面有我們不認識的字符或者格式\n if (regexp.lastIndex - lastIndex > result[0].length) break;\n\n // 定義一個 token 變量\n let token = {\n type: null,\n value: null,\n };\n\n // 如果 result 裏面有東西\n // 那我們就根據 result 的位置\n // 從 1 到 7 的範圍裏面匹配到哪一種輸入元素\n //( 首先正則返回的第 0 個是整個結果,所以我們從 1 開始\n // 然後我們的匹配總數一個有 7 個所以是從 1 到 7 )\n for (let i = 1; i <= dictionary.length; i++) {\n // result 中有值,就存儲當前類型\n if (result[i]) token.type = dictionary[i - 1];\n }\n // 這裏存儲值\n token.value = result[0];\n yield token;\n }\n\n yield {\n type: 'EOF',\n };\n}\n\nlet source = [];\n\nfor (let token of tokenize('10 + 20 + 30')) {\n if (token.type !== 'Whitespace' && token.type !== 'LineTerminator') source.push(token);\n}\n\n/**\n * 表達式\n * @param tokens\n */\nfunction Expression(tokens) {\n if (source[0].type === 'AdditiveExpression' && source[1] && source[1].type === 'EOF') {\n let node = {\n type: 'Expression',\n children: [source.shift(), source.shift()],\n };\n source.unshift(node);\n return node;\n }\n AdditiveExpression(source);\n return Expression(source);\n}\n\n/**\n * 加法表達式\n * @param source\n */\nfunction AdditiveExpression(source) {\n // 第一個遇到乘法表達式時\n if (source[0].type === 'MultiplicativeExpression') {\n let node = {\n type: 'AdditiveExpression',\n children: [source[0]],\n };\n source[0] = node;\n return AdditiveExpression(source);\n }\n // 加法\n if (source[0].type === 'AdditiveExpression' && source[1] && source[1].type === '+') {\n let node = {\n type: 'AdditiveExpression',\n operator: '+',\n children: [],\n };\n node.children.push(source.shift());\n node.children.push(source.shift());\n MultiplicativeExpression(source);\n node.children.push(source.shift());\n source.unshift(node);\n return AdditiveExpression(source);\n }\n // 減法\n if (source[0].type === 'AdditiveExpression' && source[1] && source[1].type === '-') {\n let node = {\n type: 'AdditiveExpression',\n operator: '-',\n children: [],\n };\n node.children.push(source.shift());\n node.children.push(source.shift());\n MultiplicativeExpression(source);\n node.children.push(source.shift());\n source.unshift(node);\n return AdditiveExpression(source);\n }\n\n if (source[0].type === 'AdditiveExpression') {\n return source[0];\n }\n\n MultiplicativeExpression(source);\n return AdditiveExpression(source);\n}\n\n/**\n * 乘法表達式\n * @param source\n */\nfunction MultiplicativeExpression(source) {\n // Number 類型\n if (source[0].type === 'Number') {\n let node = {\n type: 'MultiplicativeExpression',\n children: [source[0]],\n };\n source[0] = node;\n return MultiplicativeExpression(source);\n }\n // 乘法\n if (source[0].type === 'MultiplicativeExpression' && source[1] && source[1].type === '*') {\n let node = {\n type: 'MultiplicativeExpression',\n operator: '*',\n children: [],\n };\n node.children.push(source.shift());\n node.children.push(source.shift());\n node.children.push(source.shift());\n source.unshift(node);\n return MultiplicativeExpression(source);\n }\n // 除法\n if (source[0].type === 'MultiplicativeExpression' && source[1] && source[1].type === '/') {\n let node = {\n type: 'MultiplicativeExpression',\n operator: '/',\n children: [],\n };\n node.children.push(source.shift());\n node.children.push(source.shift());\n node.children.push(source.shift());\n source.unshift(node);\n return MultiplicativeExpression(source);\n }\n\n if (source[0].type === 'MultiplicativeExpression') {\n return source[0];\n }\n\n return MultiplicativeExpression(source);\n}\n\nconsole.log(Expression(source));\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"好,這個就是整個 LL 語法分析的主要過程了。我們到了這裏就成功的解析了一個四則運算的表達式。對,不容易但是我們終於完成了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ae/ae96ca354aebd9bd3378ac90d783cd67.gif","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我是來自《","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"技術銀河","attrs":{}},{"type":"text","text":"》的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"三鑽","attrs":{}},{"type":"text","text":":\"學習是爲了成長,成長是爲了不退步。堅持才能成功,失敗只是因爲沒有堅持。同學們加油哦!下期見!\"","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f7/f7db8645b6fdfa85351bc5f5aeb79aeb.gif","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/23/237db88ffb40799313e26780f79aad9e.gif","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"博主開始在B站直播學習,歡迎過來《","attrs":{}},{"type":"link","attrs":{"href":"https://live.bilibili.com/22619211","title":""},"content":[{"type":"text","text":"直播間","attrs":{}}]},{"type":"text","text":"》一起學習。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們在這裏互相監督,互相鼓勵,互相努力走上人生學習之路,讓學習改變我們生活!","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"學習的路上,很枯燥,很寂寞,但是希望這樣可以給我們彼此帶來多一點陪伴,多一點鼓勵。我們一起加油吧! (๑ •̀ㅂ•́)و","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我是來自微信公衆號《","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"技術銀河","attrs":{}},{"type":"text","text":"》的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"三鑽","attrs":{}},{"type":"text","text":",一位正在重塑知識的技術人。下期再見。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章