超微編譯器

事物的特殊性往往也能反映事物的普遍性。-- 網摘

背景:在瞭解抽象語法樹的時候偶然瞭解到 the-super-tiny-compiler這個開源項目,一看之下似乎不復雜,原文全是英文,閱讀的時候就想到翻譯一下,一方面加深理解,一方面也作爲閱讀的成果。
本文主要是對源碼中註釋的翻譯。

大多數編譯器分爲三個主要階段

  1. 解析
  2. 轉換
  3. 生成代碼

一、解析
定義:將原始代碼轉成更抽象的表示。
詳細解釋:解析通常分爲詞法分析句法分析兩個階段。
詞法分析:由詞法分析器將原始代碼分解成不同的標記,這些標記包括數字、標籤、標點符號、運算符等等,這取決於你怎麼去定義詞法分析器。
句法分析:將詞法分析器產出的標記重新格式化爲一種表示形式,該表示形式描述了語法的每個部分以及它們之間的關係。 這被稱爲中間表示或抽象語法樹。抽象語法樹,簡稱AST,是一個深層嵌套的對象,以易於使用的方式表示代碼,並告訴我們很多信息。

舉例:
類LISP語法:(add 2 (subtract 4 2))
詞法解析後可能是這樣:

[
    { type: 'paren',  value: '('        },
    { type: 'name',   value: 'add'      },
    { type: 'number', value: '2'        },
    { type: 'paren',  value: '('        },
    { type: 'name',   value: 'subtract' },
    { type: 'number', value: '4'        },
    { type: 'number', value: '2'        },
    { type: 'paren',  value: ')'        },
    { type: 'paren',  value: ')'        },
]

句法解析後可能是這樣:

{
    type: 'Program',
    body: [{
      type: 'CallExpression',
      name: 'add',
      params: [{
        type: 'NumberLiteral',
        value: '2',
      }, 
	  {
        type: 'CallExpression',
        name: 'subtract',
        params: [{
          type: 'NumberLiteral',
          value: '4',
        }, {
           type: 'NumberLiteral',
          value: '2',
        }]
      }]
    }]
}

二、轉換
定義:利用代碼更抽象的表示法添加編譯器想做的任何操作。
詳細解釋:
1、變動:對拿到的AST做一些需要的改變,AST由很多語法節點組成,我們可以增加、刪除、替換、更新樹上的節點,當我們最終的目標語言是另一種語言時,也可以基於現有的語法樹創建新的語法樹。
2、遍歷:採用深度優先遍歷AST來獲取節點。
比如有如下AST:

{
    type: 'Program',
    body: [{
      type: 'CallExpression',
      name: 'add',
      params: [{
        type: 'NumberLiteral',
        value: '2'
      }, {
        type: 'CallExpression',
        name: 'subtract',
        params: [{
          type: 'NumberLiteral',
          value: '4'
        }, {
          type: 'NumberLiteral',
          value: '2'
        }]
      }]
    }]
}

遍歷順序:

  1. Program
  2. CallExpression (add)
  3. NumberLiteral (2)
  4. CallExpression (subtract)
  5. NumberLiteral (4)
  6. NumberLiteral (2)

3、訪問器:用於操作AST的節點
訪問器模型:

 /*  
  *   -> Program (enter)
 *     -> CallExpression (enter)
 *       -> Number Literal (enter)
 *       <- Number Literal (exit)
 *       -> Call Expression (enter)
 *          -> Number Literal (enter)
 *          <- Number Literal (exit)
 *          -> Number Literal (enter)
 *          <- Number Literal (exit)
 *       <- CallExpression (exit)
 *     <- CallExpression (exit)
 *   <- Program (exit)
 */
 
var visitor = {
  NumberLiteral: {
    enter(node, parent) {},
    exit(node, parent) {},
  }
};

三、生成代碼
定義:將轉換後的代碼表示轉化成新的源代碼。
詳細解釋:實際上,代碼生成器要知道怎麼去輸出AST中所有不同類型的節點,它要遞歸地調用自身去輸出嵌套的節點,直到所有節點都被輸出到一個長的存儲代碼的字符串中。

小結
以上就是一個編譯器不同的部分。當然,也不是說所有的編譯器就是上面描述的那樣,基於不同的目的,編譯器可能會有更多的步驟。

源碼

  1. 編譯器-詞法分析器
  2. 編譯器-解析器
  3. 編譯器-遍歷器
  4. 編譯器-轉換器
  5. 編譯器-代碼生成器
  6. 完整代碼
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章