caasih.net

程式語言學習心得 7

infix operator 的 precendence 是寫死在文法中的。

才有書中 figure 1.3 這樣結構:

expr -> ...
     |  ...
     |  expr1

expr1 -> expr2 | expr1
      |  expr2
expr2 -> expr3 & expr2
      |  expr3
expr3 -> ...

直接寫起來會變成:

pExpr1 :: Parser CoreExpr
pExpr1
  = ((\e1 op e2 -> EAp (EAp (EVar op) e1) e2) `pFmap` pExpr2 `pAp` (pLit '|') `pAp` pExpr1) `pAlt`
    pExpr2

為了避免 expr1 不管成功或失敗,都各 parse expr2 一次,引進了新的資料結構: PartialExpr

data PartialExpr = NoOp | FoundOp Name CoreExpr

就可以寫成:

pExpr1 :: CoreExpr
pExpr1 = assembleOp `pFmap` pExpr2 `pAp` pExpr1c

pExpr1c :: PartialExpr
pExpr1c = (FoundOp `pFmap` (pLit "|") `pAp` pExpr1) `pAlt` (pEmpty NoOp)

pExpr2 :: CoreExpr
pExpr2 = assembleOp `pFmap` pExpr3 `pAp` pExpr2c

pExpr2c :: PartialExpr
pExpr2c = (FoundOp `pFmap` (pLit "&") `pAp` pExpr2) `pAlt` (pEmpty NoOp)

-- 略

而把 PartialExpr 組合成 CoreExpr ,則需要:

assembleOp :: CoreExpr -> PartialExpr -> CoreExpr
assembleOp e1 NoOp = e1
assembleOp e1 (FonudOp op e2) = EAp (EAp (EVar op) e1) e2

從現在的角度看,這讓我想起 Maybe ,但是 CoreExprPartialExpr 就算是 monoid ,也是兩種不同的 monoid ,如果真的要把它們接起來,那 assembleOp 對應的,會是現在的什麼函數呢?我還沒想清楚。

Isaac Huang 發佈於