Parsecにちょっと触ってみる(9): S式っぽい計算機

練習にS式っぽい計算機を作ってみました。

import Text.ParserCombinators.Parsec
import Data.List

-- sexpは'('と')'に囲まれていて、"*/+-"のどれかの次にスペースがあって、
-- スペースで区切られたexprがある。
sexp :: Parser Double
sexp = do
    char '('
    op <- oneOf "*/+-"
    spaces
    s  <- sepBy (many1 expr) spaces
    char ')'
    case op of
      '*' -> return $ foldl1' (*) $ concat s
      '/' -> return $ foldl1' (/) $ concat s
      '+' -> return $ foldl1' (+) $ concat s
      '-' -> return $ foldl1' (-) $ concat s

-- exprは数字かsexp
expr :: Parser Double
expr    = do{n <-try number;return n} <|> do{ n <- sexp; return n } 

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

{-
> parseTest sexp "(- 100 56)"                           -- > 44.0
> parseTest sexp "(+ 1 2 3 4 5 6 7 8 9 10)"             -- > 55.0
> parseTest sexp "(+ 345 (+ 1 2) (* 34 78 9))"          -- > 24216.0
> parseTest sexp "(/ (+ 345 (+ 1 2) (* 34 78 9)) 3)"    -- > 8072.0
> parseTest sexp "(+ 345 (+ 1 2 (- 10 3)) (* 34 78 9))" -- > 24223.0
> parseTest sexp "(a 345 5)"
 -- >
parse error at (line 1, column 2):
unexpected "a"
-}
  • noneOf "'( )" ・・・指定Char 以外に一致
> parseTest (do{s<-many1 (noneOf "'( )");return(map toUpper s)}) "abc" -- > "ABC"