ちなみに

火曜日の空は僕を押しつぶした。

Haskell勉強会#9 その後

うじひささん主催のHaskell勉強会#9に行ってきました。
今回はふつケルのp.318からp.332まで読み進めました。Parsec(sayではぱーせく)のあたりです。

最後にやった演習で結局宿題になった”逆ポーランド記法のパーサを書こう”を頑張ってやってみました。

(02/25:追記)
うじひささんの指摘を受けて先頭のスペースを無視する様に修正しました。

import Text.ParserCombinators.Parsec

data RPNExpr = Error
             | Expr Int Int Char

main = do expr <- getContents
          mapM_ (print . calc . compileRPN) (lines expr)

calc :: RPNExpr -> Int
calc Error          = -99
calc (Expr x y '+') = x + y
calc (Expr x y '-') = x - y
calc (Expr x y '*') = x * y
calc (Expr x y '/') = case y of
                        0 -> 0
                        y -> x `div` y 

compileRPN :: String -> RPNExpr
compileRPN cs = case parse rpn "" cs of
                Right expr -> expr
                Left err   -> Error

rpn :: Parser RPNExpr
rpn = do many space
         n1  <- try sector <|> digit'
         n2  <- try sector <|> digit'
         opt <- operator
         return $ Expr  n1 n2 opt
  where
    sector :: Parser Int
    sector = do char '('
                expr <- rpn
                char ')'
                many space
                return $ calc expr
    digit' :: Parser Int
    digit' = do n <- many1 digit
                many1 space
                return $ read n
    operator :: Parser Char
    operator = do opt <- oneOf "+-*/"
                  many space
                  return opt

main関数はうじひささんのもののパクリ。
左結合での再帰は無限ループになって書けないみたいなのでずるいですが、式を()でくくるように強制しています。
全体的に手抜きなので、パースに失敗した場合は-99を返す仕様。-99に意味はありません。

結果

4 5 +
9
(2 3 +) 2 *
10
(1 3 +) (8 3 -) *
20

とりあえず結果は出てるのでよしとしましょう。
うじひささんの”もうちょっとまともなver.”に期待しつつも僕もいづれちゃんと書き直そうとちいさな決心。