簡單的說,parser的工做便是將代碼片斷轉換成計算機可讀的數據結構的過程。這個「計算機可讀的數據結構」更專業的說法是「抽象語法樹(abstract syntax tree)」,簡稱AST。AST是代碼片斷具體語義的抽象表達,它不包含該段代碼的全部細節,好比縮進、換行這些細節,因此,咱們可使用parser轉換出AST,卻不能使用AST還原出「原」代碼,固然,能夠還原出語義一致的代碼,就如同將ES6語法的js代碼轉換成ES5的代碼。javascript
通常來講,一個parser會由兩部分組成:html
在解釋某段代碼的時候,先由詞法解釋器將代碼段轉化成一個一個的詞組流(token),再交由解釋器對詞組流進行語法解釋,轉化爲對應語法的抽象解釋,便是AST了。java
爲了讓你們更清楚的理解parser兩部分的工做順序,咱們經過一個例子來進行說明:git
437 + 734
在parser解析如上的計算表達式時,詞法解析器首先依次掃描到「4」、「3」、「7」直到一個空白符,這時,詞法解析器便將以前掃描到的數字組成一個類型爲「NUM」的詞組(token);接下來,詞法解析器繼續向下掃描,掃描到了一個「+」,對應輸出一個類型爲「PLUS」的詞組(token);最後,掃描「7」、「3」、「4」輸出另外一個類型爲「NUM」的詞組(token)。
語法解釋器在拿到詞法解析器輸出的詞組流後,根據詞組流的「NUM」,「PLUS」,「NUM」的排列順序,解析成爲加法表達式。express
由上的例子咱們能夠看出,詞法解析器根據必定的規則對字符串進行解析並輸出爲詞組(token),具體表現爲接二連三的數字組合(「4」、「3」、「7」和「7」、「3」、「4」)即表明了數字類型的詞組;語法解釋器一樣根據必定的規則對詞組的組合進行解析,並輸出對應的表達式或語句。在這裏,詞法解析器應用的規則即爲詞彙語法(Lexical Grammar)的定義,語法解釋器應用的規則即爲表達式(Expressions)、語句(Statements)、聲明(Declarations)和函數(Functions)等的定義。json
看到這裏你們可能會感受到奇怪,爲何講parser講的好好的,又跑到ECMAScript標準上來了呢?由於以上提到的詞彙語法(Lexical Grammar)、表達式(Expressions)、語句(Statements)、聲明(Declarations)和函數(Functions)等都是ECMAScript標準中的所定義的,這其實也是ECMAScript標準的做用之一,即定義JavaScript的標準語法。segmentfault
ECMAScript的詞彙詞法規定了JavaScript中的基礎語法,好比哪些字符表明了空白(White Space),哪些字符表明了一行終止(Line Terminators),哪些字符的組合表明了註釋(Comments)等。具體的規定說明,能夠在ECMAScript標準11章中找到。babel
這裏咱們不仔細研讀每一個語法的定義,只需知道詞法解析器(lexer)判讀詞組(token)的依據來源於此便可,爲了讓你們有必定的瞭解,這裏,咱們拿上面例子中的數字字面量(Numeric Literals)來進行說明:
ECMAScript標準中,對數字字面量的定義以下:
該定義須要自上向下解讀:數據結構
首先,規則定義了數字字面量(Numeric Literal)能夠是十進制字面量(Decimal Literal)、二進制整數字面量(Binary Integer Literal)、八進制整數字面量(Octal Integer Literal)、十六進制整數字面量(Hex Integer Literal);ecmascript
在咱們的例子中,咱們只關心十進制的字面量,因此,接下來,規則定義十進制字面量(Decimal Literal)能夠是包含小數點與不包含小數點的組合,這裏咱們只需關注不包含小數點的定義,即十進制整數字面量(Decimal Integer Literal) + 可選的指數部分(Exponent Part);
最後,規則定義十進制整數字母量由非零數字(Non Zero Digit)+ 十進制數字(Decimal Digit)或十進制數字組(Decimal Digits)組成,非零數字是由1~9的數字組成,十進制數字是由0~9組成。
將上面的定義從新整合後,就能獲得咱們須要的數字字面量的定義規則:
非零數字(1~9)+十進制數字組(0~9)
須要注意的是,這是簡化版的數字字面量定義,完整版的須要加上以上規則中的全部分支條件。
ECMAScript標準12~13章包含了表達式和語句的相關定義,以前由詞法解析器(lexer)處理後生成的詞組流(token)交由語法解釋器(parser)處理的主要內容,便是處理詞組流構成的表達式與語句。在這裏,咱們須要稍加明確一下表達式與語句之間的不一樣與關係:
首先,語句包含表達式,大部分語句是由關鍵字+表達式或語句組成,而表達式則是由字面量(Literal)、標識符(Identifier)、符號(Punctuators)等低一級的詞組組成;
其次,表達式通常來說會產生一個值,而語句不總有值。
理解第一點對於咱們寫語法解釋器很重要,因爲語句是由表達式組成的,而表達式是有詞組組成的,詞組是有詞法解析器進行解析生成的,因此,在語法解釋器中,將以表達式爲切入點,由表達式解析再深刻到語句解析中。
瞭解一個parser的結構,以及parser解析語法所依賴的規則後,接下來,咱們須要瞭解一下一個parser所生產出來的結果——抽象語法樹。在文章的開頭,我有簡單的解釋抽象語法樹便是具體代碼片斷的抽象表達,那它具體是長什麼樣的呢?
function sum (a , b) { return a+b; }
以上的代碼片斷,AST樹的描述以下(使用babylon7-7.0.0-beta.44,結果進行了簡化):
{ "type": "Program", "body": [ { "type": "FunctionDeclaration", "id": { "type": "Identifier", "name": "sum" }, "params": [ { "type": "Identifier", "name": "a" }, { "type": "Identifier", "name": "b" } ], "body": { "type": "BlockStatement", "body": [ { "type": "ReturnStatement", "argument": { "type": "BinaryExpression", "left": { "type": "Identifier", "name": "a" }, "operator": "+", "right": { "type": "Identifier", "name": "b" } } } ] } } ] }
對該AST仔細觀察一番,便會明白,AST其實便是咱們在已經ECMAScript標準對代碼進行解析後,將標識符(identifier)、聲明(declaration)、表達式(expression)、語句(statement)等按代碼表述的邏輯整理成爲樹狀結構。就拿上面的例子來講,當語法解析器識別了一個二元表達式(Binary Expression),便將這個二元表達式所攜帶的信息——左值,右值,操做符按照固定的計算機可讀的數據格式保存下來,便是咱們看到的AST樹了。
固然,AST也須要具有固定的格式,這樣計算機才能依照該格式閱讀AST並進行接下來的編譯工做,固然,有一些AST也被用來轉義(如babel)。關於AST定義的規則,咱們能夠參考babel的定義,這也是後面咱們實現parser時,所參考的標準。
理解完以上相關的知識,咱們便具有編寫一個parser的先決條件了,那在下一章,咱們將實際操做一番,編寫一個簡易版本的JavaScript語言parser。