前言
最近遇到一個困難,就是有一包 C Code 的 #define 寫的相當漂亮,寫的太漂亮因此環環相扣,導致我無法直接的看出每個 #define 的數值到底是多少。你說這很重要嗎?相當重要,例如想要 debug 一個獨立的 MCU 微控制器之類,而當這些常數又不存在於任何變數裡的時候,你會突然發現無從得知這些數值到底是多少除非你很有耐心的一個一個算,尤其當 Project 很大的時候,數量之多手算會算到手酸又容易出錯。
Compiler 一定知道這些數值是多少,才能在 preprocessor 的時候就把這些資訊全部展開塞進程式碼裡面,我知道但人生就是這個但是,我想要知道的是每一個常數誰是多少誰是多少,而不是他們被展開後放在程式裡的樣子,難不成我還要一個個去展開後的程式裡面翻?
因此就有了這個 Python 寫的 Tool 誕生。
C-define Parser
這個 Tool 做的事情很簡單,就是盡可能的把所有 #define 的數值算出來。欸我用 VS Code 的 Intellisense 也會告訴我每個 Macro 展開後的式子啊,只要把那個式子貼進 Google Search 搜尋框裡也可以算數啊!但身為一名軟體工程師,隨身攜帶扳手順手將手動作業自動化(就是懶而已)也是很合理。
演算法
其實也沒那麼複雜,基本上就以下作業:
- 找出所有的 #define 的名字、所需參數及內容
- 根據所需的 #define 名字,將內容盡可能的展開
- 如果內容還存在 #define 的常數,先展開內容的 Macro
(為了處理內容又出現逗號的情形, 需要確保括號的數量是成對的) - 如果存在參數,將參數一個一個換成展開後的樣子
- 展開到不能再展開為止
- 如果內容還存在 #define 的常數,先展開內容的 Macro
- 用
eval
計算展開之後的數值
基本上就是用遞迴不斷的展開。
實作的時候遇到一些問題:
- 一開始學最簡單的 Syntax Highlight 方法只用了 Regex 正規表達式找出 Token (即#define 名稱及參數內容),發現小括號很多的時候容易出錯,所以最後還是乖乖的算括號直到結束。
遇到沒看過的 Token 時會無限遞迴使得程式崩潰 XD 所以先raise KeyError
來擋,並且允許手動輸入 Token。- 更新:加入
zero_defs
來判斷是否為沒看過的 Token,並以 0 帶入運算,以避免 Stack Overflow
- 更新:加入
- 有些 #define 是寫在 .c 檔案,此時要加入那些臨時的 define 數值去計算,並且在不需要之後移除
- 注意 #ifdef/#ifndef 的邏輯跟 #if/#elif 不同。
Example
例如輸入這樣子的一個 header 檔案:
#define BASE_Y (BASE_X + X_SIZE)
可以輸出成以下的樣子(輸出格式可以自訂):
#define BASE_Y (0x40C0)
Source Code
現在還很陽春,所以歡迎大家下載來玩、發發PR XD。
ukyouz/C-define-Parser