最近正在研究JS動態解析的基本結構,希望自己能夠將研究成果前前後後總結出來。
以方便自己複習,也希望能夠和大家分享這樣一套擁有悠久歷史的編譯技術實現。
按照編譯原理的運行特點,一套解釋系統最前面也是最簡單的就是詞法分析。
這裏首先研究的是V8引擎的詞法分析結構,頭文件定義在 src/scanner.h中,具體實現在對應的src/scanner.cc中。
所有的構件
- Scanner [v8::internal]
- UC16CharacterStream [v8::internal]
- UnicodeCache [v8::internal]
- LiteralBuffer [v8::internal]
- LiteralScope [v8::internal::Scanner]
在頭文件中共聲明有五個類,但是對外提供詞法分析服務的就是Scanner類。其他四個類爲詞法分析提供基本基礎服務:譬如字符流編碼、結果存取、輸入流緩衝等。
UC16CharacterStream [v8::internal]
對外主要提供三個接口:
- Advance [v8::internal::UC16CharacterStream] 向前掃描一個字符,並返回掃描到的字符,如果到文件尾部則返回一個負數。
- pos [v8::internal::UC16CharacterStream] 返回當前字符流掃描到的位置
- SeekForward [v8::internal::UC16CharacterStream] 向前N步步進
- PushBack [v8::internal::UC16CharacterStream] 回溯一個字符
其是一個抽象類,與其相關的子類如圖:
UnicodeCache [v8::internal]
用於對Unicode字符進行類型分析。其內部專門實現了用於支持Unicode的相關類包。主要是便於詞法分析器在掃描到當前字符時可以進行當前字符狀態的查詢。
其內部含有各種類型分辨數據單元:
- unibrow::Predicate<IdentifierStart, 128> kIsIdentifierStart;
- unibrow::Predicate<IdentifierPart, 128> kIsIdentifierPart;
- unibrow::Predicate<unibrow::LineTerminator, 128> kIsLineTerminator;
- unibrow::Predicate<unibrow::WhiteSpace, 128> kIsWhiteSpace;
例如IndentifierStart數據題用於判斷是否爲起始字符:
struct IdentifierStart {
static inline bool Is(uc32 c) {
switch (c) {
case '$': case '_': case '\\': return true;
default: return unibrow::Letter::Is(c);
}
}
};
LiteralBuffer [v8::internal]
當前分析結果的字符流存儲緩存結構。
內部用 Vector<byte> backing_store_ 保存掃描的字符流數據。
LiteralScope [v8::internal::Scanner]
用於記錄在完成詞法掃描後的詞法分析狀態
Scanner [v8::internal]
詞法分析主體類,原先感覺是否會使用lex等自動生成器,沒想到時完全手寫版本。
字符流的基本分析方法時DFA,如果有時間,我考慮把分析器的DFA逆向畫出來。
Scanner內部主要維護了current_,next_兩步的token,具體的維護策略需要看語法分析的維護。