|
1 | 1 | 欲再做分詞,需先細數零.二版加入了哪些新詞。 |
2 | 2 |
|
| 3 | +## 新詞列表 |
| 4 | + |
3 | 5 | 運算子方面,有: |
4 | 6 | - % |
5 | 7 | - == |
|
12 | 14 | 特殊符號有: |
13 | 15 | - 【 |
14 | 16 | - 】 |
| 17 | +- 、 |
15 | 18 |
|
16 | 19 | 關鍵字則有: |
17 | 20 | - 術 |
|
30 | 33 |
|
31 | 34 | 以此為基礎繪製零.二版的分詞狀態機,貧道略去 1 字特殊符號的狀態,而 1 字關鍵字僅以`元`為代表,並以`不然`為 2 字關鍵字之代表,`>=`與`>`為前綴問題之代表。 |
32 | 35 |
|
33 | | -此外,除變數名之外,術名也允許非特殊符字符任意組合,今統一稱此二者為`識別子`。 |
| 36 | +此外,除變數名之外,術名也允許非特殊符號、非數字任意組合,今統一稱此二者為`識別子`。 |
34 | 37 |
|
35 | 38 |  |
36 | 39 |
|
37 | | -新圖中`x`的含義與零.一版並不相同,其意義改變為「其他出邊字符之外的所有非特殊字符」。 |
38 | | -TODO: 修改零.一版分詞狀態圖,使 x 之意義相同。 |
| 40 | +提醒:`x`的含義是「非數字、非特殊字符、非其他出邊字元的所有字元集合」。 |
| 41 | + |
| 42 | +把狀態機完整畫完,再在零.一版的分詞器源碼的基礎上依狀態機畫葫蘆即可寫出分詞器。 |
| 43 | + |
| 44 | +## 先將關鍵字當識別子 |
| 45 | + |
| 46 | +但若真的把整套狀態機畫出來,會發現頗為繁雜,關鍵字製造出太多狀態,其每個狀態有都類似,只要沒接收到預期後傳,馬上就轉換成識別子。 |
| 47 | + |
| 48 | +這其實昭示關鍵字與識別子的關係十分接近,那吾人不妨先將關鍵字當識別子,等識別子被斷詞後,再去判定該識別子的內容是否是某個關鍵字。 |
| 49 | + |
| 50 | +如此狀態機會簡單得多: |
| 51 | + |
| 52 | + |
| 53 | + |
| 54 | +## 實作 |
| 55 | + |
| 56 | +零.二版開始有 2 字的特殊符號,但這不難處理,只要往前多看一個字元就能判斷: |
| 57 | + |
| 58 | +``` rust |
| 59 | +fn 起點態(&mut self) -> Option<O詞> { |
| 60 | + let 字 = self.字流.pop_front()?; |
| 61 | + match 字 { |
| 62 | + '+' => Some(O詞::運算子(O運算子::加)), |
| 63 | + '-' => Some(O詞::運算子(O運算子::減)), |
| 64 | + '*' => Some(O詞::運算子(O運算子::乘)), |
| 65 | + '/' => Some(O詞::運算子(O運算子::除)), |
| 66 | + '%' => Some(O詞::運算子(O運算子::餘)), |
| 67 | + '=' => match self.字流.front() { |
| 68 | + Some('=') => { |
| 69 | + self.字流.pop_front()?; |
| 70 | + Some(O詞::運算子(O運算子::等於)) |
| 71 | + } |
| 72 | + _ => Some(O詞::賦值), |
| 73 | + }, |
| 74 | + '!' => match self.字流.front() { |
| 75 | + Some('=') => { |
| 76 | + self.字流.pop_front()?; |
| 77 | + Some(O詞::運算子(O運算子::異於)) |
| 78 | + } |
| 79 | + _ => panic!("!後必接="), |
| 80 | + }, |
| 81 | + '<' => match self.字流.front() { |
| 82 | + Some('=') => { |
| 83 | + self.字流.pop_front()?; |
| 84 | + Some(O詞::運算子(O運算子::小於等於)) |
| 85 | + } |
| 86 | + _ => Some(O詞::運算子(O運算子::小於)), |
| 87 | + }, |
| 88 | + '>' => match self.字流.front() { |
| 89 | + Some('=') => { |
| 90 | + self.字流.pop_front()?; |
| 91 | + Some(O詞::運算子(O運算子::大於等於)) |
| 92 | + } |
| 93 | + _ => Some(O詞::運算子(O運算子::大於)), |
| 94 | + }, |
| 95 | + '(' => Some(O詞::左圓括號), |
| 96 | + ')' => Some(O詞::右圓括號), |
| 97 | + '【' => Some(O詞::左基括號), |
| 98 | + '】' => Some(O詞::右基括號), |
| 99 | + '.' => Some(O詞::音界), |
| 100 | + '、' => Some(O詞::頓號), |
| 101 | + '\n' => Some(O詞::換行), |
| 102 | + '\t' | ' ' | ' ' => Some(O詞::空白), |
| 103 | + '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0' => { |
| 104 | + self.數字態(字.to_string()) |
| 105 | + } |
| 106 | + _ => self.識別子態(字.to_string()), |
| 107 | + } |
| 108 | +} |
| 109 | +``` |
| 110 | + |
| 111 | +數字態實作維持不變,識別子態會在斷詞完成時,判定其內容是否恰為一關鍵字: |
| 112 | + |
| 113 | +```rust |
| 114 | +fn 識別子態(&mut self, mut 前綴: String) -> Option<O詞> { |
| 115 | + let 字 = self.字流.front()?; |
| 116 | + match 字類(字) { |
| 117 | + O字類::數字 | O字類::其他 => { |
| 118 | + 前綴.push(self.字流.pop_front()?); |
| 119 | + self.識別子態(前綴) |
| 120 | + } |
| 121 | + O字類::特殊符號 => { |
| 122 | + // 遇到特殊符號,識別子截止 |
| 123 | + // 判定是否是關鍵字 |
| 124 | + match 前綴.as_str() { |
| 125 | + "元" => Some(O詞::元), |
| 126 | + "術" => Some(O詞::術), |
| 127 | + "歸" => Some(O詞::歸), |
| 128 | + "若" => Some(O詞::若), |
| 129 | + "或若" => Some(O詞::或若), |
| 130 | + "不然" => Some(O詞::不然), |
| 131 | + _ => Some(O詞::識別子(前綴)), |
| 132 | + } |
| 133 | + } |
| 134 | + } |
| 135 | +} |
| 136 | +``` |
39 | 137 |
|
40 | | -本次分詞就不再附上代碼,在零.一版的基礎上依狀態機畫葫蘆即可得。 |
|
0 commit comments