把狀態機完整畫完,再在零.一版的分詞器源碼的基礎上依狀態機畫葫蘆即可寫出分詞器。
但若真的把整套狀態機畫出來,會發現頗為繁雜,關鍵字會製造出許多狀態,其每個狀態會都隨時會轉換成識別子。這昭示關鍵字與識別子的關係十分接近。不妨先將關鍵字當識別子,等識別子被斷詞後,再一次性判定該識別子的內容是否是某個關鍵字。
如此狀態機會簡單得多:
零.二版開始有 2 字的特殊符號,但這不難處理,只要往前多看一個字元就能判斷:
fn 起點態(&mut self) -> Option<O詞> {
let 字 = self.字流.pop_front()?;
match 字 {
'+' => Some(O詞::運算子(O運算子::加)),
'-' => Some(O詞::運算子(O運算子::減)),
'*' => Some(O詞::運算子(O運算子::乘)),
'/' => Some(O詞::運算子(O運算子::除)),
'%' => Some(O詞::運算子(O運算子::餘)),
'=' => match self.字流.front() {
Some('=') => {
self.字流.pop_front()?;
Some(O詞::運算子(O運算子::等於))
}
_ => Some(O詞::賦值),
},
'!' => match self.字流.front() {
Some('=') => {
self.字流.pop_front()?;
Some(O詞::運算子(O運算子::異於))
}
_ => panic!("!後必接="),
},
'<' => match self.字流.front() {
Some('=') => {
self.字流.pop_front()?;
Some(O詞::運算子(O運算子::小於等於))
}
_ => Some(O詞::運算子(O運算子::小於)),
},
'>' => match self.字流.front() {
Some('=') => {
self.字流.pop_front()?;
Some(O詞::運算子(O運算子::大於等於))
}
_ => Some(O詞::運算子(O運算子::大於)),
},
'(' => Some(O詞::左圓括號),
')' => Some(O詞::右圓括號),
'【' => Some(O詞::左基括號),
'】' => Some(O詞::右基括號),
'.' => Some(O詞::音界),
'、' => Some(O詞::頓號),
'\n' => Some(O詞::換行),
'\t' | ' ' | ' ' => Some(O詞::空白),
'1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0' => {
self.數字態(字.to_string())
}
_ => self.識別子態(字.to_string()),
}
}
數字態實作維持不變,識別子態會在斷詞完成時判定是否是關鍵字:
fn 識別子態(&mut self, mut 前綴: String) -> Option<O詞> {
let 字 = self.字流.front()?;
match 字類(字) {
O字類::數字 | O字類::其他 => {
前綴.push(self.字流.pop_front()?);
self.識別子態(前綴)
}
O字類::特殊符號 => {
// 遇到特殊符號,識別子截止
// 判定是否是關鍵字
match 前綴.as_str() {
"元" => Some(O詞::元),
"術" => Some(O詞::術),
"歸" => Some(O詞::歸),
"若" => Some(O詞::若),
"或若" => Some(O詞::或若),
"不然" => Some(O詞::不然),
_ => Some(O詞::識別子(前綴)),
}
}
}
}