本身寫一個 C 語言 編譯器 InnerC

InnerC  用於 ILBC,   我如今把它獨立一個版本出來,   項目地址:html

 

https://github.com/kelin-xycs/InnerC              ,git

 

InnerC 是一個   C 語言 編譯器,  最初的 目的 是 做爲  ILBC  的  中間語言 編譯器  用於編譯 C 中間語言  。github

有關 ILBC ,   見 《ILBC 規範》   http://www.javashuo.com/article/p-hsmtjoox-s.html        。正則表達式

 

目前 InnerC 已實現的部分 只包含 語法分析 和 語法檢查, 不包含 生成目標代碼 和 連接  。數組

 

目前 InnerC 支持     全局變量 函數 結構體 數組 指針 函數指針, int float char ,     四則運算, 大於小於不等於 比較, 與或非邏輯運算,架構

if 語句, while 語句,  不支持 for 語句, 主要是 懶得寫了,煩  。  之後能夠加上  。函數

支持 return break continue  語句  。學習

支持 做用域,  好比 函數體 是一個 做用域, 函數形參 是一個 做用域,  if 子句 和 while 子句(循環體) 是 一個 做用域  。測試

不支持 ++  --  +=  -=,    也是 沒時間寫 。 之後能夠加上  。spa

不支持     三元運算符     ?   :    ,        三元運算符 的 規則 和 通常的 運算符 有所不一樣,   要 額外 的 一些 語法分析 邏輯  來 處理  。  之後能夠加上  。

 

語法檢查 的 部分  只 粗略 的 實現了 檢查 變量是否已聲明,  是否在 上級做用域 中聲明瞭同名的變量, 只寫了代碼,沒有測試  。

另外還實現了 函數名 和 結構體名 的 命名檢查,  就是 應該由 下劃線字母數字 組成 且 以 下劃線字母 開頭,以及 不能 和 關鍵字 相同  。

 

命名檢查 和 語法檢查 是 分開的,由於 在 語法檢查 裏 檢查 函數名 和 結構體名 是否存在,  因此 先 進行 命名檢查  。

目前 命名檢查 包含在 文本解析(Parse)過程 中 。

 

大部分 的 語法檢查 都在      I_C_Member.類型和語法檢查()     方法   裏  實現   。

只要 去 實現     I_C_Member   接口 的   類型和語法檢查()      方法     就好了  。

 

全部的 語法成員 都 繼承了    I_C_Member 接口 ,   包括    變量聲明  結構體  函數  做用域  各類語句  各類表達式  。

 

因此這個架構是 很清晰 的,   完善剩餘的部分 只是 工做量 的 問題  。

 

這裏把   類型和語法檢查   要作 的 工做 大概 列一下  :


檢查上級做用域中是否已定義了同名的變量

變量 參數 返回值 字段 的 類型 是否正確,好比 是不是 int float 等基礎類型或結構體

是否使用了 未定義 的 變量 參數 字段

變量不能在聲明前使用

運算符兩邊的表達式的類型是否匹配

Cast 是否合法

函數返回值的類型和聲明的返回類型是否一致

是否使用了 未定義 的 函數 和 結構體

數組聲明 的 維度長度 只能是 常量 或者 常量表達式,若是用 常量 初始化數組,能夠不用聲明維度長度,但這好像只適用於 一維數組

全局變量 初始化 只能用 常量 或者 常量表達式

由於 大部分 的 語法檢查 都和 類型 有關,因此歸到一塊兒稱爲 「類型和語法檢查」

 

這些內容 在 代碼 的 註釋 裏有寫 。

 

除了以上,還有 2 個 語法檢查 是 在   類型和語法檢查   以後 獨立 進行的,分別是 :

 

檢查函數內全部路徑都有返回值

檢查結構體不能循環包含

 

這個 流程 在 代碼 裏 能夠很清楚的 看到  。

 

能夠在 解決方案 中的 InnerC_Demo 項目 看到 Demo,  這是一個 WinForm 項目, 運行 InnerC_Demo.exe,   指定要編譯 的 C 源文件,  點擊 「測試」 按鈕, 若是沒有語法錯誤,  就會 把 C 源文件 編譯爲 語法成員樹,   並 將 語法成員樹 逆向 還原 爲 C 源代碼,   還原後 的 C 源代碼 保存在 另一個 文件裏,  這個文件的文件名 是 原文件名 加上  「.reverse.c」   ,   好比 源文件名 是 「a.c」,   還原後的 文件名 是 「a.c.reverse.c」   。

 

在 InnerC_Demo 的  Bin\Debug  目錄下,  有一個 Test.c ,    運行 InnerC_Demo.exe  能夠 編譯 Test.c  觀察 演示效果 。

 

此次對 C 語法 有一點 修改,就是 C 語言 是用  大括號 如  { 1, 2, 3, 4 }  表示 一個 數組常量,  可是這讓 InnerC  的 編譯器 變得複雜 。

由於 大括號 是用來表示 一個 代碼塊,好比 函數體, 結構體,  或者 if 子句, 或者 while 子句(循環體),

用  大括號 表示  數組常量  會讓   第一層解析 劃分 函數 和 結構體 的 大括號塊 變得 麻煩  。

爲了 維持 編譯器 的 簡單清晰,  我決定 作出一個 改革,

改用 中括號 來表示 數組常量,如   [ 1, 2, 3, 4 ]    ,    結果很爽      。  啊哈哈哈    。

我以爲  發明 C 語言  的 前輩  可能有 大括號  偏心癖好   ,      要不就是 可能 看到 當時 其它語言 裏 用 中括號 表示 數組 以爲 不爽 。

 

將來  D# 也會沿用 這個 作法,   D#  的 編譯器 能夠在  InnerC 的 基礎上 擴展而來 。

 

在  D#  中 有 Lambda 表達式, 這樣 是否是 仍然 要 增長 對 Lambda 表達式 的 判斷?

是的,  可是,  Lambda 表達式 是一個  明顯的 主要的     需求,     並且 能夠根據 大括號 前面 是否有   ()=>  操做符  來 明確的 判斷 大括號 是不是 Lambda 表達式,

在 劃分 方法  的 大括號塊 時 加入 是不是 Lambda 表達式 大括號 的 判斷 不會讓  編譯器 架構 的 關注點 分散,

而 數組常量 是一個 很弱 的 需求,  在 劃分 函數 結構體 大括號塊  時  加入 是不是 數組常量 大括號 的 判斷 會讓 編譯器 架構 的 關注點 分散 。

 

咦?   你們可能會問,   在 函數 和 結構體 外 哪裏來的 數組常量?   全局變量 啊,  全局變量 的 初始化 可能會用 這種 大括號數組常量  。 

若是 是 在 函數 和 結構體 內部,  其實 沒什麼問題  。

 

InnerC   還有另一個 意義,  就是   能夠做爲 編譯器 的 範例 和 內核,  讓後人能夠容易的   學習瞭解 編譯器 以及 在 這個基礎 上 改寫 和 開發 新的 編譯器  。

 

另外,  根據 InnerC 的原理, 其實 能夠寫一個 正則表達式 引擎  。  正則表達式 自己 就是一個 描述規則的文本,須要 文本解析, 解析獲得 規則,  把 規則 保存在 字典(Hash 表) 裏,  根據 規則 對 目標字符串 進行 匹配  。

這些用 InnerC 的 文本解析 方法 均可以實現  。

相關文章
相關標籤/搜索