Parsecにちょっと触ってみる(6):Parsec で掛け算

  • しばらくやっていなかったのですっかり忘れました。Parsec, 高速なコンビネータパーサ を参考にして簡単な掛け算。
  • パースは何をするものかと言うと、数字文字列から本物の数字、S式の文字列から本物のS式、配列の文字列から本物の配列を作ります。文字列から意味のある値を生成します。
import Text.ParserCombinators.Parsec

multiply :: Parser Integer
multiply = do{ x <- number 
             ; char '*'
             ; y <- number 
             -- 記号'*'から本当の掛け算を行う。
             ; return (x * y)
             }

number :: Parser Integer
number  = do{ spaces
            ; ds <- many1 digit
            ; spaces
            -- ここで文字列から Integer に変換され意味が生まれる
            ; return (read ds)
            }

run :: Show a => Parser a -> String -> IO ()
run p input
        = case (parse p "" input) of
            Left err -> do{ putStr "parse error at "
                          ; print err
                          }
            Right x  -> print x
> run multiply "1234 * 10000"    -- > 12340000
> run multiply "1234g * 10000"
-- >
parse error at (line 1, column 5):
unexpected "g"
expecting digit, white space or "*"

> parse multiply  "Error" "1234 * 10000"  -- > Right 12340000

> parse multiply  "Error" "g1234 * 10000"
-- >
Left "Error" (line 1, column 1):
unexpected "g"
expecting white space or digit

> parse (do{x<-char '1';return ((read [x])::Integer)})  "Error" "1234"
-- > Right 1
  • operator = oneOf "+-*/"
import Text.ParserCombinators.Parsec

calc :: Parser Integer
calc = do x  <- number 
          op <- operator
          y  <- number 
          case op of
                 '+' -> return (x + y)
                 '-' -> return (x - y)
                 '*' -> return (x * y)
                 '/' -> return (x `div` y)

number :: Parser Integer
number  = do spaces
             ds <- many1 digit
             spaces
             return (read ds)

operator :: Parser Char
operator = oneOf "+-*/"

run :: Show a => Parser a -> String -> IO ()
run p input
        = case (parse p "" input) of
            Left err -> do{ putStr "parse error at "
                          ; print err
                          }
            Right x  -> print x
ghci> run calc "12300 / 234" -- > 52
ghci> run calc "123 + 234"   -- > 357
ghci> run calc "123 - 234"   -- > -111
ghci> run calc "123 * 234"   -- > 28782
ghci> run calc "234 / 12"    -- > 19
  • sepBy は指定された区切りで文字列を分割し、リストにして返す。
> parseTest (sepBy (many digit) (char ',')) "123,456,789"
-- > ["123","456","789"]
  • sepBy を使って小数にも対応させる
import Text.ParserCombinators.Parsec

calc :: Parser Double
calc = do x  <- number
          op <- operat
          y  <- number 
          case op of
                 '+' -> return (x + y)
                 '-' -> return (x - y)
                 '*' -> return (x * y)
                 '/' -> return (x / y)

number :: Parser Double
number  = do spaces
             ds <- sepBy1 (many1 digit) (char '.')
             spaces
             case ds of
               [int,decimal] -> return (read (int++"."++decimal))
               [int]         -> return (read int)
               _             -> return 0


operat :: Parser Char
operat = oneOf "+-*/"

run :: Show a => Parser a -> String -> IO ()
run p input
        = case (parse p "" input) of
            Left err -> putStr "parse error at " >> print err
            Right x  -> print x
> run calc "123.4 + 34.67" -- > 158.07
> run calc "123.4 - 34.67" -- > 88.73
> run calc "123.4 * 34.67" -- > 4278.278
> run calc "123.4 / 34.67" -- > 3.5592731468128065
> run calc "123 *  3"      -- > 369.0