博客 / 詳情

返回

編譯器設計: 我們為什麼需要 Tokennizer (分詞器) ?

太長不看(TL;DR), 你可能在以下情況下需要 Tokenizer:

  1. 需要複用已有框架, 而它恰好提供了 Tokenizer.
  2. 需要回溯上下文, 通常用於報錯和語法高亮.

我正在設計和編寫一門編程語言 Styio. 在項目之初, 我就有所疑惑: 一個解析器 (Parser) 一般由 Lexer 和 Tokenizer 兩部分組成, 其中 Lexer 的存在毋庸置疑, 總要先解析字符串才能從中提取語義信息, 這很好理解; 不過, Tokenizer 的存在價值卻並不高, 因為 Lexer 可以直接從字符串生成 AST (抽象語法樹, Abstract Syntax Tree), 進行一次額外的 Token 轉換反而會帶來更大的性能損耗. 所以, 我省略了 Tokenizer, 手寫遞歸下降解析器, 直接從 字符串 生成 AST.

隨着 Styio 的不斷完善, 我開始需要設計報錯信息. 而就在這時候, 我發現自己需要在解析出錯的位置做標記, 並且根據其前後的語義信息生成報錯信息. 在沒有 Tokenizer 的情況下, 我必須重新從字符串再進行一次解析, 才能獲得前後文的語義, 但如果存在 Tokenizer, 就可以直接從 Token 開始分析, 省略重複的字符串解析環節.

實際上, Tokenizer 是在字符串的基礎上進行的第一層抽象, 也是最基礎的抽象. 它的價值並不在於生成 AST, 而在於保存操作結果. 當我們需要回溯分析的時候, Token 的抽象層級就已經足夠, 而回到 String 的層級卻需要把已經解析過的字符串再解析一遍, 這是額外的性能損耗.

在經過衡量之後, 我依然選擇了 放棄 Tokenizer. 因為 Styio 的語法極為複雜和自由, Lexer 完全有能力在當前位置拋出有價值的報錯信息. 不過, Tokenizer 確實對於語法高亮等下游任務極為有用. 因此, 我選擇採取一種反方向的設計: Styio 的 Parser 將會在字符串生成 AST 之後, 保存當前的 Token, 而不是消耗當前的 Token, 作為歷史信息以供後續回溯.

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.