« Home

擴展到 operator 優先序問題 [cs-000D]

上面的方法解決了左遞迴問題,但是要是有多個不同優先級的運算形式就會讓文法快速膨脹

ExprTerm+TermTermFactorFactorFactorIntExprExpr \to Term + Term \\ Term \to Factor * Factor \\ Factor \to Int \mid Expr

這樣單調的修改在文法裡面確實很無聊,不過只要仔細觀察對應的 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
  1. 其中 parseString 是個假設會進行 whitespace 過濾並 match 目標字串的 combinator
  2. Expr.mulExpr.add 的型別被假設為 Expr → Expr → Expr

這裡真正發生的就是使用組合子編碼了文法的層級,其中每個規則都被記錄成匿名函數,我們無需再另外命名。