File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 7171
7272思考題:有沒有辦法定義上下文無關語法,把同一層級的括號限制在一對,禁止` ((1+2)) ` 、` (((1+2))) ` 之無意義括號?
7373
74- 為方便觀看,以下將音界咒零・一版全部語法定義寫在一起,並將其縮排:
74+ 為方便觀看,以下將音界咒零・一版全部語法定義縮排後寫在一起,並加入一條 ` 音界咒檔 = 音界咒・檔案結尾 ` ,以生成檔案結尾(EOF)而完整描述音界咒檔案。
7575
7676``` 語法
77+ 音界咒檔 = 音界咒・檔案結尾
78+
7779音界咒 = 句
7880 | 句・音界咒
7981
113115遇到歧義與法時,也可以嘗試直接修原語法定義,寫出一套無歧義的語法。` 算式 ` 的例子可以透過額外增加` 乘除式 ` 、` 原子式 ` 兩層級來迫使先乘除後加減:
114116
115117```
116- 算式 = 乘除式 | 乘除式・加減・乘除式
117- 乘除式 = 原子式 | 原子式・乘除・原子式
118+ 算式 = 乘除式
119+ | 算式・+・乘除式
120+ | 算式・−・乘除式
121+ 乘除式 = 原子式
122+ | 乘除式・*・原子式
123+ | 乘除式・/・原子式
124+ 原子式 = 數字
125+ | 變數
126+ | "("・算式・")"
127+ ```
128+
129+ ## 回溯剖析
130+
131+ 再整理一次音界咒語法。
132+
133+ ``` 語法
134+ 音界咒檔 = 音界咒・檔案結尾
135+
136+ 音界咒 = 句
137+ | 句・音界咒
138+
139+ 句 = 變數宣告式
140+ | 算式
141+
142+ 變數宣告式 = "元"・"・"・變數・"="・算式
143+
144+ 算式 = 乘除式
145+ | 算式・+・乘除式
146+ | 算式・−・乘除式
147+
148+ 乘除式 = 原子式
149+ | 乘除式・*・原子式
150+ | 乘除式・/・原子式
151+
152+ 原子式 = 數字
153+ | 變數
154+ | "("・算式・")"
155+ ```
156+
157+ 前文提到,有了語法規則定義,就能透過遞迴展開生成符(生成符,即在語法規則左側出現,還能繼續展開的符號),最終獲得所有長度小於 n 的展開式。
158+
159+ 當吾人想要剖析時,也能利用這個想法,若一份文本長度為 n ,那遞迴生成出所有長度等於 n 的展開式,並一一與原始碼做比對,比到一種展開是一模一樣的,檢視當下的展開過程,就能得到語法樹了。
160+
161+ 一直展開到長度 n 才比對,太浪費時間了,一發現當下的展開式已經跟文本不一樣,就可以放棄目前展開,回溯到上個還沒失敗的狀態。
162+
163+ 寫成虛擬碼如下:
164+
165+ ``` 音界
166+ // 「剖析」函式嘗試以「展開式」來生成「文本」
167+
168+ 「文本」為一全域變數
169+
170+ 剖析(展開式)-> 成功|失敗:
171+ 匹配展開式與文本,若相等,回傳成功
172+
173+ 首生成符 = 展開式中的第一個生成符
174+
175+ 遍歷首生成符的「生成規則」 {
176+ 新展開式 = 在原展開式中,以「生成規則」展開首生成符
177+ 若「新展開式」的前綴已與文本不同,嘗試下個規則
178+
179+ 若「剖析(新展開式)」成功,回傳成功
180+ }
181+
182+ 走到這裡表示所有規則都不行,回傳失敗
183+
184+ // 初始展開式僅為「音界咒檔」
185+ 剖析(音界咒檔)
186+ ```
187+
188+ 以上虛擬碼為求簡單,省略了許多優化,例如說,首生成符的位置跟目前比對無誤的文本位置都應該紀錄起來,不用每次都從頭比對。另外,編譯器應用中,剖析應回傳語法樹,而非單單成功或失敗。
189+
190+ ## 消除左遞迴
191+
192+ 注意到` 剖析(展開式) ` 是遞迴函式,它會嘗試以各種規則展開首生成符,然後繼繻呼叫` 剖析(新展開式) ` 。
193+
194+ 觀察` 剖析(算式) ` ,只要第一個規則` 算式 = 乘除式 ` 配對失敗,就會嘗試匹配` 算式 = 算式・+・乘除式 ` ,也就是呼叫` 剖析(算式・+・乘除式) ` ,此一規則並沒有消耗任何文本,第一個規則剛剛不能生效,此刻一樣不能生效,於是會再套用一次` 算式 = 算式・+・乘除式 ` 得到` 剖析(算式・+・乘除式・+・乘除式) ` ......如此落入無窮遞迴。
195+
196+ 若保證每個展開都能消耗掉至少一個字符,就能避免落入遞迴,但文本卻完全不變的狀況。再次改寫算式:
197+
198+ 其中 e 代表空字串。
199+
200+ ```
201+ 算式 = 乘除式・重複乘除式
202+
203+ 重複乘除式 = +・重複乘除式
204+ | −・重複乘除式
205+ | e
206+
207+ 乘除式 = 原子式・重複原子式
208+
209+ 重複原子式 = *・重複乘除式
210+ | /・重複乘除式
211+ | e
212+
118213原子式 = 數字
119214 | 變數
120215 | "("・算式・")"
121- 乘除 = "*"
122- | "/"
123- 加減 = "+"
124- | "−"
125216```
217+
218+ 算式被改寫了真多次,由此可見寫出易於剖析的語法不是一件易事。所幸,這是零・一版最後一次重寫語法了。
You can’t perform that action at this time.
0 commit comments