Skip to content

欲再做分詞,需先細數零.二版加入了哪些新詞。

新詞列表

運算子方面,有:

  • ==
  • !=
  • >=
  • <=

特殊符號有:

關鍵字則有:

  • 或若
  • 不然

若不以功能性區分,以詞固有的性質來區分: 運算子與特殊符號用到的符號都不允許出現在識別子(變數名、術名),可以歸為一類討論。其中,長度為 1 的詞有可能恰為長度為 2 的詞的前綴,此狀況需要特殊判定。其餘 1 字詞判定與+-*/處理方式相同。

關鍵字中的 1 字詞處理方式與零.一版的相同,2 字詞與 1 字詞也類似,只是會增加一個狀態。

回憶零.一版的分詞狀態機: 零・一版分詞狀態機

以此為基礎繪製零.二版的分詞狀態機,貧道略去 1 字特殊符號的狀態,而 1 字關鍵字僅以為代表,並以不然為 2 字關鍵字之代表,>=為前綴問題之代表。

此外,除變數名之外,術名也允許非特殊符號、非數字任意組合,今統一稱此二者為識別子

零・二版分詞狀態機

提醒:x的含義是「非數字、非特殊字符、非其他出邊字元的所有字元集合」。

把狀態機完整畫完,再在零.一版的分詞器源碼的基礎上依狀態機畫葫蘆即可寫出分詞器。

先將關鍵字當識別子

但若真的把整套狀態機畫出來,會發現頗為繁雜,關鍵字製造出太多狀態,其每個狀態有都類似,只要沒接收到預期後傳,馬上就轉換成識別子。

這其實昭示關鍵字與識別子的關係十分接近,那吾人不妨先將關鍵字當識別子,等識別子被斷詞後,再去判定該識別子的內容是否是某個關鍵字。

如此狀態機會簡單得多:

零・二版分詞狀態機簡化

實作

零.二版開始有 2 字的特殊符號,但這不難處理,只要往前多看一個字元就能判斷:

rust
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()),
    }
}

數字態實作維持不變,識別子態會在斷詞完成時,判定其內容是否恰為一關鍵字:

rust
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詞::識別子(前綴)),
            }
        }
    }
}