Skip to content

Commit

Permalink
零.二版類型檢查
Browse files Browse the repository at this point in the history
  • Loading branch information
MROS committed Oct 13, 2024
1 parent 9b661b9 commit 8644fa9
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 1 deletion.
4 changes: 4 additions & 0 deletions book/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ export default defineConfig({
text: "再遇剖析(二)組合子剖析器",
link: "/零.二版/再遇剖析(二)組合子剖析器.md",
},
{
text: "語義分析:類型檢查",
link: "/零.二版/語義分析:類型檢查.md",
},
],
},
{
Expand Down
20 changes: 19 additions & 1 deletion book/零.二版/再遇剖析(二)組合子剖析器.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,23 @@ fn 剖析句(&self, 游標: usize) -> Option<(O句, usize)> {
})
}
```
好像沒什麼改善空間了,「或」語句不需要傳遞之前的剖析結果,而 `map` 中的類型轉換函數很難再省掉,就算省,也是在自動類型轉換上下功夫,與組合子關係不大了。
好像沒什麼改善空間了,「或」結構不需要傳遞之前的剖析結果,比「且」結構要來得容易,而 `map` 中依然有重複出現的游標,那就照樣在`map`之外包裝一層。


``` rust
fn 映射<F>(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句::算式(算式)))
}
```

## 「重複」結構呢?

Expand All @@ -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)
45 changes: 45 additions & 0 deletions book/零.二版/語義分析:類型檢查.md
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit 8644fa9

Please sign in to comment.