Skip to content

Commit 8644fa9

Browse files
committed
零.二版類型檢查
1 parent 9b661b9 commit 8644fa9

File tree

3 files changed

+68
-1
lines changed

3 files changed

+68
-1
lines changed

book/.vitepress/config.mts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ export default defineConfig({
8383
text: "再遇剖析(二)組合子剖析器",
8484
link: "/零.二版/再遇剖析(二)組合子剖析器.md",
8585
},
86+
{
87+
text: "語義分析:類型檢查",
88+
link: "/零.二版/語義分析:類型檢查.md",
89+
},
8690
],
8791
},
8892
{

book/零.二版/再遇剖析(二)組合子剖析器.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,23 @@ fn 剖析句(&self, 游標: usize) -> Option<(O句, usize)> {
188188
})
189189
}
190190
```
191-
好像沒什麼改善空間了,「或」語句不需要傳遞之前的剖析結果,而 `map` 中的類型轉換函數很難再省掉,就算省,也是在自動類型轉換上下功夫,與組合子關係不大了。
191+
好像沒什麼改善空間了,「或」結構不需要傳遞之前的剖析結果,比「且」結構要來得容易,而 `map` 中依然有重複出現的游標,那就照樣在`map`之外包裝一層。
192+
193+
194+
``` rust
195+
fn 映射<F>(self, f: F) -> Option<(U, usize)>
196+
where
197+
F: FnOnce(T) -> U,
198+
{
199+
self.map(|(剖析結果, 游標)| (f(剖析結果), 游標))
200+
}
201+
202+
fn 剖析句(&self, 游標: usize) -> Option<(O句, usize)> {
203+
self.剖析變數宣告(游標)
204+
.映射(|變數宣告| O句::變數宣告(變數宣告))
205+
.or_else(|| self.剖析算式(游標).映射(|(算式)| O句::算式(算式)))
206+
}
207+
```
192208

193209
## 「重複」結構呢?
194210

@@ -199,3 +215,5 @@ fn 剖析句(&self, 游標: usize) -> Option<(O句, usize)> {
199215
組合子剖析器無非就是種風格,若嚴格地只使用組合子,那結構會很單純,風格會很一致。但想要混用原本遞迴下降平鋪直敘的寫法,或是加上一些宏也沒什麼問題。例如要結合應用前一章所說的優先級決定算法,也許遞迴下降的寫法更好寫一些喔。
200216

201217
零.二版的新語法脫不出這幾項結構,就不再將新的剖析代碼貼出來了。
218+
219+
更新:貧道試著加上了更多組合子,可參考[音界咒源碼](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)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
零.一版僅做了十分簡單的[語義分析](../零.一版/符號檢查.md),即檢查符號使用之前是否有宣告。
2+
3+
零.二版的情況沒這麼簡單了。
4+
5+
## 巢狀作用域
6+
7+
首先,「若」語句以及「術」宣告語句中的基括號`【】`將法咒切割成一個個區塊,深層的區塊能擷取外部區塊所宣告的變數,反之則不然。
8+
9+
```音界
10+
若(甲)【
11+
元.鼠=1
12+
13+
若(乙)【
14+
元.牛=1
15+
// 可以截取「鼠」
16+
17+
若(丙)【
18+
元.虎=1
19+
// 可以截取「鼠」
20+
21+
22+
// 不能截取「牛」、「虎」
23+
24+
```
25+
26+
一個變數所能被擷取的範圍,被稱之為**作用域**,零.一版中,作用域是線性的,而零.二版有了區塊之後,作用域變為樹狀(或說巢狀的)。
27+
28+
零.一版中,由於作用域是線性的,變數一旦宣告,後續就一直可用,採用一個雜湊表來記錄目前作用域便已足夠。而在巢狀結構中,進入一個區塊時,會有某些變數被宣告,需將其移入雜湊表,二離開區塊時,又有某些變數的生命期耗盡,需要移出雜湊表。
29+
30+
仔細維護雜湊表確實是一種方法,但更簡單的寫法是採用[持久化資料結構](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),每進一個區塊就用一個新的持久化雜湊表來儲存變數,離開了區塊就直接丟掉,如此實作最為簡單。性能方面,這些持久化資料結構的時空間複雜度也都很優秀,不會是編譯的瓶頸。
31+
32+
33+
34+
## 類型檢查
35+
36+
雖然零.二版限制每個變數只能是整數,但別忘了現在有了「術」,識別子可能是變數,也可能是術,其形態終究是不一樣的。例如:
37+
38+
```
39+
元.甲=1
40+
甲(2、3)
41+
```
42+
43+
在施術時,應檢查欲施之術名是否確實為術,若是,還要進一步檢查它的形參與實參數長度是否相當。
44+
45+
類型檢查的實作可以參考[音界咒源碼](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)

0 commit comments

Comments
 (0)