本文是 VMBC / D# 項目 的 系列文章,html
有關 VMBC / D# , 見 《我發起並創立了一個 VMBC 的 子項目 D#》(如下簡稱 《D#》) http://www.javashuo.com/article/p-zziqptgy-s.html 。java
VMBC 須要一個 內置 的 C 編譯器, 想來想去, 以爲仍是本身寫一個,數組
計劃用 C 語言 寫, 由於 VMBC 的 C 編譯器 要求是一個 本地庫, 若是不要求是 本地庫, 我就用 C# 寫了, 呵呵呵 。函數
爲何 是 庫 呢 ? 由於這是一個 內置編譯器, 是由 ILBC 運行時 來 調用的 (ILBC 見 《D#》), 因此 是一個 庫 。spa
這個 庫 最好 能 儘量的 小 。操作系統
C 語言 寫的 代碼 是 最貼近 底層(彙編) 的, 因此 C 語言 寫的 庫 應該是 最緊湊 的, 因此用 C 語言 來寫 。指針
還有一個 緣由 是, 我會的 語言 很少, C 算是 相對 更熟一點的, So 。htm
有 網友 說 C 語言 不適合 寫 編譯器, C 的抽象過低了, 建議用 函數式 語言寫,對象
又舉例 Rust 最先是用 OCaml 寫的, 而後又用 Rust 寫了一遍 。blog
好吧, 但 Rust 、OCaml 這些語言 的 名字 我都 沒怎麼聽過, 仍是用 C 吧 。
另外用 C 的話, 應該不用擔憂 操做系統 的 支持 的 問題 。
這個 項目 我只 實現 語法分析 和 類型檢查 的 部分, 語法分析 包含了 語法檢查 。
生成目標代碼 連接(連接外部庫) 這 2 個 部分 你們 若是有興趣, 對 彙編 和 操做系統 瞭解 的話, 能夠來補充 。
InnerC 是 ansi C 的 子集 + 擴展, 只支持 ansi C 的 部分特性, 同時還會加入一些 新特性 。
總的來講, InnerC 會 比 ansi C 簡單 。
好比, InnerC 不支持 結構體(Struct), 由於 InnerC 是 做爲 中間語言, 只須要是一種 「高級彙編語言」 就能夠 。
不用 Struct, 那用什麼 ?
用 數組, 包括 靜態數組 和 從 堆 裏 分配 的 數組 。
根據 偏移量 向 數組 的 相應位置 寫入 字段 的 值, 這就是 Struct, 也是 對象 。
去掉 Struct 能夠 省掉 很多 語法分析 的 開銷 和 人力上的 研發成本 。
但 C 語言 裏好像沒有 按值 傳遞 數組 的 特性, 因此 InnerC 須要 加入 按值傳遞數組(拷貝傳遞數組) 的 特性 。
好比, InnerC 應該 增長 T [ n ] 類型, 用於 參數 和 返回值,
T [ n ] 類型 表示 按值傳遞數組(拷貝傳遞數組),
假設 A() 方法 調用 B() 方法, B() 方法有一個 T [ n ] arr 參數, 那麼 A() 方法 傳給 T [ n ] arr 參數 的 是一個 數組的 首地址 arr, 編譯器會處理成 把 A() 裏的 arr 數組 以 長度 n 拷貝到 B() 的 arr 裏, 因此 B() 的 arr 也是 數組 的 首地址, 可是是 拷貝到 B() 的 堆棧 裏的 數組 的 首地址 。
T [ n ] arr 表示 arr 參數 是 長度 爲 n 的 數組, 編譯器 會爲 arr 在 B 的 堆棧 裏 分配 長度爲 n * sizeof(T) 的 內存空間 。 這個空間是 編譯器 分配的, 是 靜態分配 的, 等價於 聲明一個 T arr[ n ] 這樣的 靜態數組 。
同理, 假設 B() 的 返回值 是 T [ n ] 類型, B() 實際返回的是一個 數組 的 首地址 arr, A() 裏 用來 接收 B() 的 返回值 的 是一個 T arr[ n ] arr ; 靜態數組 變量, 編譯器會處理成 把 B() 裏的 arr 數組 以 長度 n 拷貝到 A() 的 arr 裏 。
InnerC 也不支持 對 函數指針 進行 類型檢查,
不對 函數指針 類型檢查 是指 函數指針 能夠調用 任意 的 參數列表, 固然, 出了錯 是 調用者 本身 負責 。^^
不過 對於 中間語言 來講, 基本上 不用擔憂 這個問題 。
InnerC 的 語法分析 能夠 生成一個 表達式對象樹, 把 表達式對象樹 序列化 獲得一個 byte [] (byte 數組),
這個 byte[] 就至關於 .Net 的 Op Code, 或者 java 的 Byte Code, 咱們能夠把 這個 byte[] 稱爲 ILBC Byte Code (簡稱 Byte Code) 。
這樣一來, 問題就明朗了,
若是 開發期 編譯 生成的 目標代碼 就是 ILBC Byte Code, 那 JIT 速度 較慢 的 問題 就 解決了 。
這就是說, 能夠把 C 語言 做爲 第一級 中間代碼, Byte Code 做爲 第二級 中間代碼 。
這樣, InnerC 就能夠由 2 個模塊 組成:
1 InnerC to Byte Code
2 Byte Code to Native Code
固然, 能夠在 開發期 編譯 直接 生成 Native Code (本地代碼), 這是 AOT 。