module Calculator.Evaluator (eval) where import Calculator.Parser (parseExpr) import Calculator.Primitives import Control.Applicative ((<$>), (<*)) import Data.Function (on) import Text.ParserCombinators.Parsec (ParseError, eof, parse) evaluate :: Expr -> Expr evaluate e@(Constant _) = e evaluate e@(InvalidError _) = e evaluate (UnOp (UnaryOp op) e) = Constant . op . fromConst $ evaluate e evaluate (BinOp (expr, rest)) = process expr rest evaluate (Function "" e) = evaluate e evaluate (Function f e) = let func = lookup f dispatch :: Maybe (Number -> Number) val = fromConst $ evaluate e in case func of Nothing -> InvalidError $ "Unknown function " ++ show f Just g -> Constant $ g val evaluate _ = InvalidError evalFaliure process :: Expr -> [(Operator, Expr)] -> Expr process expr rest = evaluate $ foldl evalPart expr rest evalPart :: Expr -> (Operator, Expr) -> Expr evalPart e1 ((BinaryOp op), e2) = let val1 = evaluate e1 val2 = evaluate e2 in if isConst val1 && isConst val2 then Constant $ (op `on` fromConst) val1 val2 else case (val1, val2) of (e@(InvalidError _), _) -> e (_, e@(InvalidError _)) -> e (_, _) -> InvalidError evalFaliure evalPart _ _ = InvalidError evalFaliure eval :: String -> Either ParseError Expr eval s = evaluate <$> parse (parseExpr <* eof) s s