--------------------------------------------------------- -- -- Module : Caclulator -- Copyright : Bartosz Wójcik (2012) -- License : BSD3 -- -- Maintainer : bartek@sudety.it -- Stability : Unstable -- Portability : portable -- -- Module is part of Wojcik Tool Kit package. -- | Simple calculator. Evaluates arithmetic formulas. --------------------------------------------------------- {-# LANGUAGE TypeSynonymInstances, FlexibleInstances, NoMonomorphismRestriction #-} module Text.Calculator ( evaluate -- Evaluates arithmetic formula. ,evaluateInt -- Evaluates arithmetic formula on @Integer@s. ) where import Control.Monad import Control.Monad.Trans.Error import Control.Monad.Trans.State import Control.Monad.Trans.Class import Data.Char import Data.Either import Text.WtkParser -- | State contains just operator type State1 a = (a -> a -> a) -- | Only limited operators are available, -- they are shown using some trick. instance (Num a, Eq a) => Show (State1 a) where show f | 3 `f` 2 == 6 = "*" | 3 `f` 2 == 5 = "+" | 3 `f` 2 == 1 = "-" | 3 `f` 2 == 8 = "^" | otherwise = "/" -- | Calculator parser is just simple wtk parser with added state. -- State keeps not yet consumed operator. type Parser1 a = StateT (State1 a) Parser a runParser1 :: (Num a) => State1 a -> Parser1 a -> String -> Either String (String, (a, (State1 a))) runParser1 st e = runParser $ runStateT e st -- | Evaluates arithmetic formula. In case formula cannot be parsed -- returns error message. evaluate :: (Floating a, RealFrac a, Ord a) => String -> Either String a evaluate x = case runParser1 (+) arithmetic x of Left err -> Left err Right (_,(res,_)) -> Right res -- | Integer version of @evaluate@. evaluateInt :: String -> Either String Integer evaluateInt x = case runParser1 (+) arithmeticInt x of Left err -> Left err Right (_,(res,_)) -> Right res char1 = lift . lexeme . char finito n = (char1 '=' >> lift eof) `mplus` lift eof >> return n arithmetic = lift skipSpaces >> expression1 >>= finito arithmeticInt = lift skipSpaces >> expressionInt >>= finito -- "runStateT p f" transforms "Parser1 a" to "Parser (a,s)" -- "liftM fst" changes it to "Parser a" which gives us proper type -- for manyF function. Then in turn we lift result of manyF from "Parser a" -- to "Parser1 a". manyF11 :: (Num a) => Parser1 a -> a -> Parser1 a manyF11 p n = do f <- get lift $ manyF f (liftM fst (runStateT p f)) n manyF1 :: (Num a) => Parser1 a -> a -> Parser1 a manyF1 p n = StateT many' where many' f = Parser many'' many'' x = case runParser1 (-) p x of Left err -> Right (x,(n,(-))) Right (x',(a,f)) -> do let Right (x'',(rest,_)) = many'' x' Right (x'',(rest `f` a,f)) myPut :: (Monad m) => s -> a -> StateT s m a myPut s a = StateT $ \_ -> return (a, s) expression1 :: (Floating a, RealFrac a, Ord a) => Parser1 a expression1 = term1 >>= manyF1 (tMinus1 `mplus` tPlus1) tMinus1 :: (Floating a, RealFrac a, Ord a) => Parser1 a tMinus1 = char1 '-' >> term1 >>= myPut (-) tPlus1 :: (Floating a, RealFrac a, Ord a) => Parser1 a tPlus1 = char1 '+' >> term1 >>= myPut (+) term1 :: (Floating a, RealFrac a, Ord a) => Parser1 a term1 = tPower1 >>= manyF1 (tMult1 `mplus` tDiv1) tMult1 :: (Floating a, RealFrac a, Ord a) => Parser1 a tMult1 = char1 '*' >> tPower1 >>= myPut (*) tDiv1 :: (Floating a, RealFrac a, Ord a) => Parser1 a tDiv1 = char1 '/' >> tPower1 >>= myPut (/) tPower1 :: (Floating a, RealFrac a, Ord a) => Parser1 a tPower1 = tFactorial1 >>= manyF1 tPow1 tPow1 :: (Floating a, RealFrac a, Ord a) => Parser1 a tPow1 = char1 '^' >> tFactorial1 >>= myPut (**) tFactorial1 :: (Floating a, RealFrac a, Ord a) => Parser1 a tFactorial1 = phrase1 >>= manyF1 tFact1 tFact1 :: (RealFrac a, Ord a) => Parser1 a tFact1 = char1 '!' >> return 1 >>= myPut (\a b -> fromIntegral (product [1 .. round a])) phrase1 :: (Floating a, RealFrac a, Ord a) => Parser1 a phrase1 = (do char1 '(' v <- expression1 char1 ')' return v) `mplus` lift (lexeme real) expressionInt = termInt >>= manyF1 (tMinusInt `mplus` tPlusInt) tMinusInt = char1 '-' >> termInt >>= myPut (-) tPlusInt = char1 '+' >> termInt >>= myPut (+) termInt = tPowerInt >>= manyF1 (tMultInt `mplus` tDivInt) tMultInt = char1 '*' >> tPowerInt >>= myPut (*) tDivInt = char1 '/' >> tPowerInt >>= myPut div tPowerInt = tFactorialInt >>= manyF1 tPowInt tPowInt = char1 '^' >> tFactorialInt >>= myPut (^) tFactorialInt = phraseInt >>= manyF1 tFactInt tFactInt = char1 '!' >> return 1 >>= myPut (\a b -> product [1 .. a]) phraseInt = (do char1 '(' v <- expressionInt char1 ')' return v) `mplus` lift (lexeme intSigned)