diff --git a/book/.vitepress/config.mts b/book/.vitepress/config.mts index 2a06b8c..0995a80 100644 --- a/book/.vitepress/config.mts +++ b/book/.vitepress/config.mts @@ -83,6 +83,10 @@ export default defineConfig({ text: "再遇剖析(二)組合子剖析器", link: "/零.二版/再遇剖析(二)組合子剖析器.md", }, + { + text: "語義分析:類型檢查", + link: "/零.二版/語義分析:類型檢查.md", + }, ], }, { diff --git "a/book/\351\233\266\357\274\216\344\272\214\347\211\210/\345\206\215\351\201\207\345\211\226\346\236\220\357\274\210\344\272\214\357\274\211\347\265\204\345\220\210\345\255\220\345\211\226\346\236\220\345\231\250.md" "b/book/\351\233\266\357\274\216\344\272\214\347\211\210/\345\206\215\351\201\207\345\211\226\346\236\220\357\274\210\344\272\214\357\274\211\347\265\204\345\220\210\345\255\220\345\211\226\346\236\220\345\231\250.md" index 1d1429d..125d7cc 100644 --- "a/book/\351\233\266\357\274\216\344\272\214\347\211\210/\345\206\215\351\201\207\345\211\226\346\236\220\357\274\210\344\272\214\357\274\211\347\265\204\345\220\210\345\255\220\345\211\226\346\236\220\345\231\250.md" +++ "b/book/\351\233\266\357\274\216\344\272\214\347\211\210/\345\206\215\351\201\207\345\211\226\346\236\220\357\274\210\344\272\214\357\274\211\347\265\204\345\220\210\345\255\220\345\211\226\346\236\220\345\231\250.md" @@ -188,7 +188,23 @@ fn 剖析句(&self, 游標: usize) -> Option<(O句, usize)> { }) } ``` -好像沒什麼改善空間了,「或」語句不需要傳遞之前的剖析結果,而 `map` 中的類型轉換函數很難再省掉,就算省,也是在自動類型轉換上下功夫,與組合子關係不大了。 +好像沒什麼改善空間了,「或」結構不需要傳遞之前的剖析結果,比「且」結構要來得容易,而 `map` 中依然有重複出現的游標,那就照樣在`map`之外包裝一層。 + + +``` rust +fn 映射(self, f: F) -> Option<(U, usize)> +where + F: FnOnce(T) -> U, +{ + self.map(|(剖析結果, 游標)| (f(剖析結果), 游標)) +} + +fn 剖析句(&self, 游標: usize) -> Option<(O句, usize)> { + self.剖析變數宣告(游標) + .映射(|變數宣告| O句::變數宣告(變數宣告)) + .or_else(|| self.剖析算式(游標).映射(|(算式)| O句::算式(算式))) +} +``` ## 「重複」結構呢? @@ -199,3 +215,5 @@ fn 剖析句(&self, 游標: usize) -> Option<(O句, usize)> { 組合子剖析器無非就是種風格,若嚴格地只使用組合子,那結構會很單純,風格會很一致。但想要混用原本遞迴下降平鋪直敘的寫法,或是加上一些宏也沒什麼問題。例如要結合應用前一章所說的優先級決定算法,也許遞迴下降的寫法更好寫一些喔。 零.二版的新語法脫不出這幾項結構,就不再將新的剖析代碼貼出來了。 + +更新:貧道試著加上了更多組合子,可參考[音界咒源碼](https://github.com/MROS/yinjie-lang/blob/main/%E9%9B%B6%E8%99%9F%E7%B7%A8%E8%AD%AF%E5%99%A8/src/%E5%89%96%E6%9E%90/%E7%B5%84%E5%90%88%E5%AD%90.rs)。 diff --git "a/book/\351\233\266\357\274\216\344\272\214\347\211\210/\350\252\236\347\276\251\345\210\206\346\236\220\357\274\232\351\241\236\345\236\213\346\252\242\346\237\245.md" "b/book/\351\233\266\357\274\216\344\272\214\347\211\210/\350\252\236\347\276\251\345\210\206\346\236\220\357\274\232\351\241\236\345\236\213\346\252\242\346\237\245.md" new file mode 100644 index 0000000..a28b0f2 --- /dev/null +++ "b/book/\351\233\266\357\274\216\344\272\214\347\211\210/\350\252\236\347\276\251\345\210\206\346\236\220\357\274\232\351\241\236\345\236\213\346\252\242\346\237\245.md" @@ -0,0 +1,45 @@ +零.一版僅做了十分簡單的[語義分析](../零.一版/符號檢查.md),即檢查符號使用之前是否有宣告。 + +零.二版的情況沒這麼簡單了。 + +## 巢狀作用域 + +首先,「若」語句以及「術」宣告語句中的基括號`【】`將法咒切割成一個個區塊,深層的區塊能擷取外部區塊所宣告的變數,反之則不然。 + +```音界 +若(甲)【 + 元.鼠=1 + + 若(乙)【 + 元.牛=1 + // 可以截取「鼠」 + 】 + 若(丙)【 + 元.虎=1 + // 可以截取「鼠」 + 】 + + // 不能截取「牛」、「虎」 +】 +``` + +一個變數所能被擷取的範圍,被稱之為**作用域**,零.一版中,作用域是線性的,而零.二版有了區塊之後,作用域變為樹狀(或說巢狀的)。 + +零.一版中,由於作用域是線性的,變數一旦宣告,後續就一直可用,採用一個雜湊表來記錄目前作用域便已足夠。而在巢狀結構中,進入一個區塊時,會有某些變數被宣告,需將其移入雜湊表,二離開區塊時,又有某些變數的生命期耗盡,需要移出雜湊表。 + +仔細維護雜湊表確實是一種方法,但更簡單的寫法是採用[持久化資料結構](https://zh.wikipedia.org/zh-tw/%E5%8F%AF%E6%8C%81%E4%B9%85%E5%8C%96%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84),每進一個區塊就用一個新的持久化雜湊表來儲存變數,離開了區塊就直接丟掉,如此實作最為簡單。性能方面,這些持久化資料結構的時空間複雜度也都很優秀,不會是編譯的瓶頸。 + + + +## 類型檢查 + +雖然零.二版限制每個變數只能是整數,但別忘了現在有了「術」,識別子可能是變數,也可能是術,其形態終究是不一樣的。例如: + +``` +元.甲=1 +甲(2、3) +``` + +在施術時,應檢查欲施之術名是否確實為術,若是,還要進一步檢查它的形參與實參數長度是否相當。 + +類型檢查的實作可以參考[音界咒源碼](https://github.com/MROS/yinjie-lang/blob/1034459a443f44730b5ecec737062444738a7628/%E9%9B%B6%E8%99%9F%E7%B7%A8%E8%AD%AF%E5%99%A8/src/%E7%AC%A6%E8%99%9F%E6%AA%A2%E6%9F%A5.rs)。