有沒有不適合使用flex/lex作為詞法分析器的語言?

O"REILLY 《flex和bison(中文版)》第24面問題5。求指點!我個人認為詞法分析既然是把輸入流分割成為一個個有意義的記號(參見龍書),而只要是程序設計語言肯定就能分割成為有意義的記號(不然人類就無法理解了),那就一定可以用詞法分析器生成器如flex生成,可是書上既然提出了這個問題那一定是有道理的!望指點!


Zeta的答案所提到的問題還是相對比較簡單的,只要你把bison當lex用就可以輕鬆解決,所有的那些狀態都embed在你的文法裡面了。主要標準就是,當你需要一個表達式而此時你看到的是/的時候,如果他不是注釋,那就肯定是正則表達式。

真正複雜的問題是bison搞不定的,譬如說C++需要語義分析和語法分析同時做,讓語義分析的結果來指導語法分析到底要選擇哪條grammar rule來resolve conflict。


Javascript

Javascript 正則表達式字面量和除法操作符的二義性, 很難用 lex 解決, 一般只用 lex 做很少很少的事情, 然後把真正含義的辨清延遲到 parse 階段.

有一個用 lex 中判斷 / 含義的方案, 主要是通過保存前一個 token 狀態來做判斷, 你感受一下它的複雜度:

`//` 總是辨認為行注釋開始

如果符號是 `/` 或者 `/=`, 並且前一個 token 是下列 token 之一, 那它就是除法運算符:

]
Identifier Number RegularExpression String
class false null private protected public super this true
get include set

如果前一個 token 是下列 token 之一, 那它就是正則表達式的開始:

! != !== # % %= = = ( * *= + += , - -= -&>
. .. ... / /= : :: ; &< &<&< &<&<= &<= = == === &> &>= &>&> &>&>= &>&>&> &>&>&>=
? @ [ ^ ^= ^^ ^^= { | |= || ||= ~
abstract break case catch const continue debugger default delete do else enum
export extends final finally for function goto if implements import in instanceof
interface is namespace native new package return static switch synchronized
throw throws transient try typeof use var volatile while with

但是它依然判斷不了 前一個 token 是 ) } 的情況...

if (true) /a/g ---&> 正則表達式
(x+y)/2 ---&> 除法
{}/a/g ---&> 正則表達式
+{}/a/g ---&> 除法

所以得添加一個狀態棧來判斷右括弧是控制結構 if / for / while 的括弧還是表達式的括弧.

再加一個狀態棧來判斷右大括弧是 block 結束還是 object literal 的結束.

但是它依然判斷不了前一個 token 是 ++ 或 -- 的情況

a++/a/g ---&> 除法

RegExp.prototype.foo = 3
++/a/g.foo ---&> 正則表達式

所以還得判斷前面的 ++ 到底是後綴運算符, 還是前綴運算符...

至此你的 lexer 充滿了一堆非常複雜的狀態... 你會思考人生的價值, 懷疑 lex 到底有啥意義, 為什麼不直接用一個 scannerless 的 parser 解決這個變態的語言?

參見 JavaScript 2.0 Syntax Rationale


因為不管怎麼樣.flex可以把鍋丟給語法分析和語義分析...所以答案是沒有.........


推薦閱讀:

編譯器後端優化有哪些經典的必讀論文?
繼續學習編譯原理的意義是什麼?
用c++寫正則引擎構造nfa的時候到底該用智能指針嗎?
設計應用的二進位存儲格式有什麼要點?
各個編程語言的發明者是否能夠隨便反編譯各自的已編譯的二進位文件?

TAG:軟體開發 | 編譯原理 | 詞法分析 |