Skip to content

Commit

Permalink
語法的歧義
Browse files Browse the repository at this point in the history
  • Loading branch information
MROS committed Sep 28, 2024
1 parent c1fec8d commit 302849a
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 4 deletions.
Binary file added book/image/算式展開同結果.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
63 changes: 59 additions & 4 deletions book/零.一版/剖析(語法分析).md
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,69 @@
變數宣告式 = "元"・"・"・變數・"="・算式
```

算式則較為複雜,敏銳的道友可能已經注意到,算式也蘊含了遞回
算式則較為複雜,敏銳的道友可能已經注意到,算式也蘊含了遞回。

```語法
算式 = 運算元・運算子・運算元 | "("・算式・")"
運算元 = 變數 | 數字 | 算式
算式 = 變數
| 數字
| "("・算式・")"
| 算式・運算子・算式
運算子 = "+" | "−" | "*" | "/"
```
`算式 = 運算元・運算子・運算元 | "("・算式・")"``算式`定義為`運算元・運算子・運算元`,而在算式兩側加上括號後,依然是合法括號,也就是說,`1+2``算式`,而`(1+2)``((1+2))``(((1+2)))`...也都是合法算式。
`算式`可以只是一個變數或數字,`算式・運算子・算式`表明`算式`也可以是加減乘除的結果,`"("・算式・")"`,而在算式兩側加上括號後,依然是合法括號,也就是說,`1+2``算式`,而`(1+2)``((1+2))``(((1+2)))`...也都是合法算式```(0)``((0))`也都合法


思考題:有沒有辦法定義上下文無關語法,把同一層級的括號限制在一對,禁止`((1+2))``(((1+2)))`之無意義括號?

為方便觀看,以下將音界咒零・一版全部語法定義寫在一起,並將其縮排:

```語法
音界咒 = 句
| 句・音界咒
句 = 變數宣告式
| 算式
變數宣告式 = "元"・"・"・變數・"="・算式
算式 = 變數
| 數字
| "("・算式・")"
| 算式・運算子・算式
運算子 = "+"
| "−"
| "*"
| "/"
```

## 語法歧義
前文寫出的語法定義,定義的是如何**生成**合乎語法的字串,而非如何將字串的語法**剖析**出來。

這意思是說,當吾人想生成出所有(長度小於 n)的`算式`時,可以遍歷`算式`的兩個分支得到`變數宣告式``算式`兩種語法,這兩種語法又可以繼續分支下去,如此遞迴,便能得出所有(長度小於 n)的`算式`

但是在遞迴遍歷的過程中,不同路徑很可能會造出重複的句子。

`1+2*3`為例,其生成方式可能是`算式` => `算式+算式` => `算式+算式*算式`,先以``展開,接著展開後的第二個再以``展開,也可能是`算式` => `算式*算式` => `算式+算式*算式`,初始算式先以``展開,展開後的第二個算式再以``展開,如圖:

![算式展開同結果](../image/算式展開同結果.png)

一種語法出現不同展開過程但同結果這種情況,該語法就是「有歧義的」,亦有人稱「模糊」、「模稜兩可」、「二義性」。

歧義是一項不良性質,若對上圖中得到的兩棵語法樹做後序運算求值,所得將會不相同,一個是先乘除後加減,另個則是先加減後乘除。

即使語法存在歧義,還是有辦法剖析的,舉個例子,透過回溯來得到所有可能的語法樹,再依照某種方式挑選,如此還是能用一套算法總是從一套源碼中得到相同的語法樹。

遇到歧義與法時,也可以嘗試直接修原語法定義,寫出一套無歧義的語法。`算式`的例子可以透過額外增加`乘除式``原子式`兩層級來迫使先乘除後加減:

```
算式 = 乘除式 | 乘除式・加減・乘除式
乘除式 = 原子式 | 原子式・乘除・原子式
原子式 = 數字
| 變數
| "("・算式・")"
乘除 = "*"
| "/"
加減 = "+"
| "−"
```

0 comments on commit 302849a

Please sign in to comment.