目錄戳這裏java
詞法分析的工做是,將輸入的字符串轉化爲結構清晰的Token
git
一般來講,這個Token
須要包含兩樣東西,詞 和 類型github
##詞編程
什麼是詞呢?例如,輸入串以下:編程語言
val num = 3 * 7
從直覺上(語法高亮也幫助了咱們)能夠看出,它由以下幾部分組成性能
val
num
=
3
*
7
.net
咱們不會把val
拆成v
al
或者其餘什麼形式,由於它是一個「詞」設計
「詞」表示用於解析的最小單元。詞的規定很寬泛,像emoji語言,它的一個詞極可能就是一個emoji圖像(雖然我沒研究過不過大概如此吧。。)而對於主流的編程語言,詞一般爲一串字母,數字,某些特殊符號(運算符)code
注:以下方式的原理和自動機一致,可是性能沒有自動機高。好處在於手寫實現幾乎不可能出錯。blog
在Latte
中,詞有以下規定 Scanner.java#L95
以下的符號
// SPLIT ".", ":", "::", "=", "+=", "-=", "*=", "/=", "%=", "<<", ">>", ">>>", "&", "^", "|", "~", "^^", "!", "&&", "||", "!=", "==", "!==", "===", "<", ">", "<=", ">=", "+", "-", "*", "/", "%", "++", "--", "@", "=:=", "!:=", "..", ".:", "..." // LAYER "->" // STRING "\"", "'", "`" // NO_RECORD " " // ENDING "," // COMMENT ";" // PAIR "{", "}", "[", "]", "(", ")"
被視爲「只要遇到便須要分割的」符號,我把它們稱作「分隔符」。什麼意思呢?
觀察
val num = 3 * 7
它寫成這些形式都應當是同一種含義
val num=3*7 val num = 3* 7 val num = 3 *7
雖而後兩個看上去不太好看,但其含義不該與第一種相異
它爲什麼會被分割爲那幾個小塊?很明顯,空格是一個「分隔符」,在空格以前的視爲一個Token,在空格以後的視爲一個Token。而空格自己,比較特殊,它不可是「分隔符」,仍是「不須要被記錄的分隔符」,將會被忽略。先後token將直接串聯起來
而 *
也是一個「分隔符」,遇到*
時將自動分隔先後,而且*
自己也是一個詞,將被記錄在Token串中。
匹配串很簡單,直接使用java
的indexOf(str)
便可。可是咱們想要的並非簡單的匹配,而是「最優匹配」
例如
3<<2
首先匹配的應當爲<<
而不是<
。由於<<
長度爲2,<
長度爲1。它們下標在同一位置,長度更長的將更加優先
3<(2<<1)
將優先匹配<
,由於它的位置相比<<
更靠前。
因此得出一個比較通用的結論: 最優匹配項爲 1.下標最靠前 2.下標相同時長度最長
在java中也很是容易實現。首先將全部符號放到一個集合中,而後進行長度從大到小的排序。在匹配時按排序後的次序進行匹配,並記錄下標。當下標更靠前時更換爲匹配項爲新的項
能夠在這裏看出來:排序 Scanner.java#L183 最優匹配 Scanner.java#L631
這樣作法的擴展性極強,在修改時,只須要改動一下要修改的詞便可。例如一開始設計的lambda爲=>
,後來通過調查(其實就是在一個java的羣裏問了問你們更喜歡那種= =)修改成->
,僅僅改動一個字符串就完成了全部的修改。使用詞法分析器生成器也能夠方便的修改,可是手寫自動機再修改就不太容易了。
不過這個方法也是一種DFA,和明確的狀態機編程方式並無本質區別
不過要注意的是,按上述方式分隔將產生一個對於小數的莫名其妙的結果
1.2
匹配.
,分隔爲1
,.
,而後,遇到行末,便記錄2
因此token串爲1
.
2
而不是1.2
。幸運的是咱們只會在小數上遇到這個問題(更準確的描述爲 .
在不向前讀時將產生歧義,這個歧義僅會在小數上出現)。在生成後作一個final check
,轉換一下就好了。或者每當遇到.
都向前讀一個字符,若爲數字則,在生成時就生成爲1.2
##類型 Token也有其自身的類型。
val num = 3 * 7
每一個token的類型爲
val : 修飾符 MODIFIER num : 合法的名稱 VALID_NAME = : 符號 SYMBOL 3 : 數字 NUMBER_LITERAL * : 符號 SYMBOL 7 : 數字 NUMBER_LITERAL
實際上,記錄了token的「內容」也就至關於記錄了token的類型。好比val
,再怎麼變它也是一個修飾符。因此,標記類型僅僅是出於性能和擴展性考慮(固然,因爲詞法分析器須要檢查token是否合法,因此在檢查的時候順帶加上類型也是很天然的事)。
但願看官能關注一下個人編程語言哦~ Latte