Parsecで簡単な電卓を作ってみた

LanguageDef と TokenParser と ExpressionParser を使ってみたかったので。。。

空白の扱いについて、ごちゃごちゃ書く必要がなくて楽でいいですね。

module Main (main) where

import Text.ParserCombinators.Parsec as Parsec
import qualified Text.ParserCombinators.Parsec.Token as Token
import qualified Text.ParserCombinators.Parsec.Language as Lang
import qualified Text.ParserCombinators.Parsec.Expr as Expr

languageDef :: Lang.LanguageDef ()
languageDef = Lang.emptyDef
              {
                Lang.opLetter = oneOf "+-*/"
              }

lexer :: Token.TokenParser ()
lexer = Token.makeTokenParser languageDef

term :: Parser Integer
term = parens expr <|> natural
    where parens = Token.parens lexer
          natural = Token.natural lexer

expr :: Parser Integer
expr = Expr.buildExpressionParser table term
    where table = [
                    [op "*" (*), op "/" div],
                    [op "+" (+), op "-" (-)]
                  ]
          op sym fn = Expr.Infix (Token.reservedOp lexer sym >> return fn) Expr.AssocLeft

program :: Parser Integer
program = Token.whiteSpace lexer >> expr >>= (\x -> eof >> return x)

main :: IO ()
main = rep >> main
    where rep = getLine >>= print . eval
          eval = either (error . show) id . parse program "input"