大學時期自學 C 語言,那時書上都說要從記事本開始寫,底子才會扎實。後來寫出興趣了,選修資工系的課,一個演算法改了八百遍,光一個區域變數的 rename 就耗了 10 分鐘 debug。直到上機考,別人早早就提交出去玩了,我還在用肉眼檢查到底哪個字拼錯導致編譯不過。雖然最後低空飛過,但也讓我對於記事本寫 code 產生深深的陰影。
比較以下這兩個段落,後者明顯好看多了吧
public void DoSomething(Request request) {
// do something
}
public void DoSomething(Request request) {
// do something
}
一開始使用 IDE,還以為它除了顏色比較鮮艷,本質跟記事本沒什麼不同 ── 只能靠全域搜尋、手動比對,一個變數名稱改掉,其他地方仍得逐一檢查。隨著工作經驗累積,才知道實際上 IDE 能做的事情可多了。
光有符號還不夠,IDE 還需要能理解程式的結構。這就是 AST 的工作。
舉例來說:
var x = a + b * c;
對應的 AST 節點可能長這樣:
Assignment
├─ Identifier: x
└─ BinaryExpression (+)
├─ Identifier: a
└─ BinaryExpression (*)
├─ Identifier: b
└─ Identifier: c
更複雜的 AST 示意圖可以參考:
IDE 在讀取原始碼後,會先把文字解析成樹狀結構。節點代表語法元素:例如 if、for、函式呼叫、運算子、變數宣告。AST 去除了無關的細節(如空白、縮排),只保留能夠表達語法邏輯的核心結構,能清楚知道:「這裡是一個變數宣告」、「這裡是一個函式呼叫」、「這個符號屬於什麼 scope」、「這些 statement 是誰的子節點」等。有了 AST,IDE 能夠更精準地理解程式碼的結構關係。進而提供像是語法檢查、重構等功能。當你按下 rename 時,它不會笨笨地把所有叫 request 的字都取代掉,而是透過去找到 AST 裡的正確節點,再重新產生程式碼。
舉例來說:
x = a + b
建立了 AST 之後,我們還需要知道
這就是符號分析在做的事情。IDE 會幫每一個識別字 (identifier) 建立對應的符號 (symbol),並記錄它到底指向哪一個程式元素:DoSomething 是 method 的名稱、大駝峰的 Reqeust 是類別、小駝峰的 request 是變數等。
有了符號分析,IDE 就能做到
[Wiki] (https://en.wikipedia.org/wiki/Symbol_table) 上面也有關於符號表的範例,可以知道一個符號大概會有哪些資訊。
有了符號分析和 AST,IDE 跟肉眼比起來已經十分強大。但想像一個有上千個檔案的 repo,如果每次查詢或重構都要全專案重新解析,光等結果就能讓人崩潰。
所以 IDE 背後還有一個祕密武器:索引。
就像寫 SQL 都會加索引來加速查詢,IDE 同樣也是。當你打開專案時,IDE 就會在背景持續掃描所有檔案:
有了索引,當你按下 Go to Definition 時,IDE 才能即時反應,也讓你可以瞬間看到某個方法在哪裡被呼叫、有多少參考。
IDE 具有以下特性
一個簡單的 rename,背後可能是:
對使用者來說,只是一秒鐘完成的事,實際上是 IDE 在背後做了黑魔法。這也是為什麼 IDE 的重構功能比手改安全太多。如果說為什麼要用 IDE 的重構工具?因為 IDE 比你更懂程式碼。