上面的方法解決了左遞迴問題,但是要是有多個不同優先級的運算形式就會讓文法快速膨脹
這樣單調的修改在文法裡面確實很無聊,不過只要仔細觀察對應的 combinator,就會它們的型別發現這告訴了我們要怎麼利用 higher order function 來解決重複的問題!對 infix 運算形式(左結合)來說,通用的程式是
def infixL (op : Parsec (α → α → α)) (tm : Parsec α) : Parsec α := do let l ← tm match ← tryP op with | .some f => return f l (← tm) | .none => return l
這個 combinator 先解析左邊的 lower tm
,接著要是能夠解析 op
就回傳 op
combinator 中存放的函數,否則就回傳左半邊的 tm
結果。使用上就像這樣
mutual def factor : Parsec Expr def expr : Parsec Expr := factor |> infixL (parseString "*" *> return Expr.mul) |> infixL (parseString "+" *> return Expr.add) end
- 其中
parseString
是個假設會進行 whitespace 過濾並 match 目標字串的 combinator Expr.mul
跟Expr.add
的型別被假設為Expr → Expr → Expr
這裡真正發生的就是使用組合子編碼了文法的層級,其中每個規則都被記錄成匿名函數,我們無需再另外命名。