--- /dev/null
+
+import Text.ParserCombinators.Parsec
+import Text.ParserCombinators.Parsec.Expr
+import qualified Text.ParserCombinators.Parsec.Token as P
+import Text.ParserCombinators.Parsec.Language
+import System.IO
+
+lexer :: P.TokenParser ()
+lexer = P.makeTokenParser (haskellDef { reservedOpNames = ["*","/","+","-","**"] })
+
+naturalOrFloat = P.naturalOrFloat lexer
+parens = P.parens lexer
+reservedOp = P.reservedOp lexer
+
+expr :: Parser Double
+expr = buildExpressionParser table factor <?> "expression"
+ where
+ table = [
+ [unary "-" negate, unary "+" id],
+ [op "**" (**) AssocRight],
+ [op "*" (*) AssocLeft, op "/" (/) AssocLeft],
+ [op "+" (+) AssocLeft, op "-" (-) AssocLeft]
+ ]
+ op s f assoc = Infix (do{ reservedOp s; return f } <?> "operator") assoc
+ unary s f = Prefix (do{ reservedOp s; return f })
+
+factor :: Parser Double
+factor =
+ do {
+ parens expr;
+ } <|> do {
+ norf <- naturalOrFloat;
+ case norf of
+ Left i -> return $ fromInteger i
+ Right f -> return $ f
+ } <?>
+ "factor"
+
+repl :: String -> (String -> Bool) -> (String -> String) -> IO ()
+repl prompt bQuit eval = loop
+ where
+ loop = do
+ putStr prompt
+ s <- getLine
+ if bQuit s
+ then return ()
+ else putStrLn (eval s) >> loop
+
+calc :: IO ()
+calc = repl "> " (== ":q") (tostring . parse stmt "")
+ where
+ tostring (Right v) = show v
+ tostring (Left err) = show err
+ stmt = do
+ e <- expr
+ eof
+ return e
+
+main = hSetBuffering stdout NoBuffering >> putStrLn "type ':q' to quit." >> calc >> putStrLn "Bye"