假設我正在撰寫一個決議器來決議以下語法:
foo.bar().baz = 5;
語法規則如下所示:
program: one or more statement
statement: expression followed by ";"
expression: one of:
- identifier (\w )
- number (\d )
- func call: expression "(" ")"
- dot operator: expression "." identifier
兩個運算式有問題,func 呼叫和點運算子。這是因為運算式是遞回的,并在開始時尋找另一個運算式,導致堆疊溢位。我將專注于這個問題的點運算子。
加號運算子也面臨著類似的問題。但是,與其使用運算式,不如執行以下操作來解決它(尋找“術語”):
add operation: term " " term
term: one of:
- number (\d )
- "(" expression ")"
然后term包括除了添加操作本身之外的所有內容。為了確保多個加號運算子可以在不使用括號的情況下鏈接在一起,我們寧愿這樣做:
add operation: term, one or more of (" " followed by term)
我在想一個類似的解決方案可以用于點運算子或函式呼叫。
但是,點運算子的作業方式略有不同。我們總是從左到右進行評估,并且需要允許完整的運算式,以便您可以在兩者之間進行函式呼叫等。帶括號的例子可能是:
(foo.bar()).baz = 5;
不幸的是,我不想要求括號。如果遵循用于加號運算子的方法,最終會出現這種情況。
我該如何實施呢?
目前我的決議器從不往前看,但即使我往前看,完成起來似乎仍然很棘手。
uj5u.com熱心網友回復:
簡單的解決方案是使用自下而上的決議器,它不會在左遞回時陷入無底洞,但我想您已經拒絕了該解決方案。
不過,我不明白您反對使用回圈構造。諸如欄位查找和函式呼叫之類的后綴修飾符與諸如加法之類的二元運算子并沒有真正的不同(當然,除了它們不需要宣告最終的右運算元這一事實之外)。加號和減號自由混合,您可以通過以下重復決議:
additive: term ( ' ' term | '-' term )*
同樣,后綴修飾符可以很容易地決議為:
postfixed: atom ( '.' ID | '(' opt-expr-list `)` )*
我正在使用一種擴展 BNF 的形式:括號組;|分離備選方案并比串聯更嚴格地系結;并且*表示其左側原子的“零次或多次重復”。
另一個屬于同一類別的后綴運算子是陣列/映射下標 ( '[' expr ']'),盡管您可能還有其他后綴運算子。
請注意,與上面的附加語法一樣,選擇適當的替代項不需要超越下一個標記。如果不能窺探未來的一個令牌,就很難決議。幸運的是,這只是很少的開銷。
uj5u.com熱心網友回復:
一種方法是點運算子決議非點運算式,即與運算式相同但沒有點運算子的規則。這可以防止遞回。
然后,當非點運算式被決議后,檢查是否跟隨一個點和一個識別符號。如果不是這種情況,我們就完成了。如果是這種情況,請將當前節點包裹在一個點操作節點中。然后,跟蹤到目前為止已為此操作決議的整個字串文本。然后將所有內容恢復到決議操作之前的狀態,現在重新決議“自定義運算式”,其中第一個直接嵌套的運算式實際上會嘗試匹配之前決議的確切字串,而不是真正的運算式。重復直到不再有點識別符號對(這應該由新的“自定義運算式”自動發生)。
這很混亂、復雜并且可能很慢,我不完全確定它是否會起作用,但我會嘗試一下。我很感激替代解決方案。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/436617.html
標籤:解析
