import Text.Parsec import Text.Parsec.String import Control.Applicative hiding (many, (<|>)) data Calc = Number Float | OpPlus Calc Calc | OpMult Calc Calc | OpSubt Calc Calc | OpDivd Calc Calc instance Show Calc where show (Number a) = show a show (OpPlus c c') = "(" ++ show c ++ " + " ++ show c' ++ ")" show (OpSubt c c') = "(" ++ show c ++ " - " ++ show c' ++ ")" show (OpMult c c') = "(" ++ show c ++ " * " ++ show c' ++ ")" show (OpDivd c c') = "(" ++ show c ++ " / " ++ show c' ++ ")" -- INTERPRETING intr :: String -> Either ParseError Float intr str = eval <$> parse calc "(unknown)" str -- EVALUATING eval :: Calc -> Float eval (Number a) = a eval (OpPlus c c') = (eval c) + (eval c') eval (OpSubt c c') = (eval c) - (eval c') eval (OpMult c c') = (eval c) * (eval c') eval (OpDivd c c') = (eval c) / (eval c') -- divide by zero doesn't error - that's 'inifinity' -- PARSING -- ::= | -- ::= + | - -- ::= | -- ::= * | / -- ::= "(" ")" | number -- Parsers calc = expression expression = term `chainl1` plusSubt <||> term term = factor `chainl1` multDivd <||> factor factor = parens expression <||> number multDivd = withSpaces mult <||> withSpaces divd plusSubt = withSpaces plus <||> withSpaces subt -- minimal parsers mult = char '*' >> return OpMult plus = char '+' >> return OpPlus subt = char '-' >> return OpSubt divd = char '/' >> return OpDivd number = Number <$> (read <$> many1 digit) -- helpers (<||>) :: Parser a -> Parser a -> Parser a a <||> b = try a <|> try b parens :: Parser a -> Parser a parens = between (char '(') (char ')') withSpaces :: Parser a -> Parser a withSpaces = between spaces spaces