從零開始開發JVM語言(二)詞法分析

目錄戳這裏java

詞法分析的工做是,將輸入的字符串轉化爲結構清晰的Tokengit

一般來講,這個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串中。

匹配串很簡單,直接使用javaindexOf(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

相關文章
相關標籤/搜索