-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
84 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
```音界 | ||
元・甲=1 | ||
乙+1 | ||
``` | ||
|
||
這個範例的語法完全正確,但`乙`並沒有先宣告再使用,因此仍是非法的程式。 | ||
|
||
由此可見,一份源碼能被剖析成語法樹,吾人仍需對它進行更多檢查,以確認它是否任何意義不明之處。從「符合語法的程式」中過濾掉「不能編譯執行的程式」,這就是語義分析做的事情。 | ||
|
||
## 語義分析 | ||
|
||
有沒有可能設計一種上下文無關語法,得以強迫在`算式`中用到的變數全都是已經宣告的呢?這恐怕辦不到,上下文無關語法天生就記憶不了上下文。`音界咒 = 句 | 句・音界咒`一旦分離成多個`句`之後,句與句之間就再無關聯,無法互相影響。 | ||
|
||
或許更強的語法系統能夠做到這點,但以符號檢查來說,在語法樹遍歷一趟就能完成,大可不必大費周章,非得要設計出語法。 | ||
|
||
讓語法定義完成它擅長的任務就行,剩下的交由語義分析來做。畢竟語法規則寫起來也並不容易是吧! | ||
|
||
## 符號檢查 | ||
符號檢查很容易,遍歷語法樹的過程中,讀到`變數宣告式`時,將變數名稱加入一集合,後續任何`算式`中使用到變數時,檢查該變數名是否存在於集合中即可。 | ||
|
||
這種檢查也可以在遞迴下降法的剖析函式中順手做完,但貧道就先讓剖析過程純粹一些吧! | ||
|
||
### 實作 | ||
|
||
```rust | ||
pub fn 檢查語法樹(語法樹: O語法樹) -> bool { | ||
let mut 通過 = true; | ||
|
||
let mut 變數集 = HashSet::<String>::new(); | ||
|
||
for 句 in 語法樹.句 { | ||
通過 = match 句 { | ||
O句::變數宣告(宣告) => { | ||
let 通過 = 檢查算式(&變數集, &宣告.算式); | ||
變數集.insert(宣告.變數名.clone()); | ||
通過 | ||
} | ||
O句::算式(算式) => 檢查算式(&變數集, &算式), | ||
} && 通過 // 「通過」寫在 && 後面,避免短路 | ||
} | ||
|
||
通過 | ||
} | ||
|
||
|
||
fn 檢查算式(變數集: &HashSet<String>, 算式: &O算式) -> bool { | ||
match 算式 { | ||
O算式::變數(變數名) => { | ||
if 變數集.contains(變數名) { | ||
true | ||
} else { | ||
println!("{} 未宣告", 變數名); | ||
false | ||
} | ||
} | ||
O算式::數字(_) => true, | ||
O算式::二元運算(二元運算) => { | ||
let 左通過 = 檢查算式(變數集, 二元運算.左.as_ref()); | ||
let 右通過 = 檢查算式(變數集, 二元運算.右.as_ref()); | ||
左通過 && 右通過 | ||
} | ||
} | ||
} | ||
|
||
``` | ||
|
||
## 符號重定義 | ||
|
||
音界咒允許變數重新宣告,類似於 Rust,後面的宣告覆蓋前面宣告。 | ||
|
||
```音界 | ||
元・甲=1 | ||
// 此處甲都是1 | ||
元・甲=2 | ||
// 此處甲都是2 | ||
``` |