module Text.Parsec.Expr.Math (
Expr(..)
, parse
, P.ParseError
, evaluate
) where
import Control.Applicative ((<|>))
import Control.Monad (liftM, liftM2)
import qualified Data.Map as M
import qualified Text.ParserCombinators.Parsec.Expr as P
import qualified Text.ParserCombinators.Parsec as P
data Expr a = Num a
| Var String
| Neg (Expr a)
| Add (Expr a) (Expr a)
| Sub (Expr a) (Expr a)
| Mul (Expr a) (Expr a)
| Div (Expr a) (Expr a)
| Pow (Expr a) (Expr a)
| Sqrt (Expr a)
| Exp (Expr a)
| Log (Expr a)
| Abs (Expr a)
| Sin (Expr a)
| Cos (Expr a)
| Tan (Expr a)
| Sec (Expr a)
| Csc (Expr a)
| Cot (Expr a)
| Sinh (Expr a)
| Cosh (Expr a)
| Tanh (Expr a)
| Sech (Expr a)
| Csch (Expr a)
| Coth (Expr a)
| ArcSin (Expr a)
| ArcCos (Expr a)
| ArcTan (Expr a)
| ArcSec (Expr a)
| ArcCsc (Expr a)
| ArcCot (Expr a)
| ArcSinh (Expr a)
| ArcCosh (Expr a)
| ArcTanh (Expr a)
| ArcSech (Expr a)
| ArcCsch (Expr a)
| ArcCoth (Expr a) deriving Show
parse :: Floating a => String -> Either P.ParseError (Expr a)
parse = P.parse build "" . (:) '(' . flip (++) ")" . filter (/=' ')
build :: Floating a => P.Parser (Expr a)
build = P.buildExpressionParser table factor
table :: Floating a => [[P.Operator Char st (Expr a)]]
table =
[ [ prefix "arcsinh" ArcSinh, prefix "arcsin" ArcSin, prefix "sinh" Sinh, prefix "sin" Sin ]
, [ prefix "arccosh" ArcCosh, prefix "arccos" ArcCos, prefix "cosh" Cosh, prefix "cos" Cos ]
, [ prefix "arctanh" ArcTanh, prefix "arctan" ArcTan, prefix "tanh" Tanh, prefix "tan" Tan ]
, [ prefix "arcsech" ArcSech, prefix "arcsec" ArcSec, prefix "sech" Sech, prefix "sec" Sec ]
, [ prefix "arccsch" ArcCsch, prefix "arccsc" ArcCsc, prefix "csch" Csch, prefix "csc" Csc ]
, [ prefix "arccoth" ArcCoth, prefix "arccot" ArcCot, prefix "coth" Coth, prefix "cot" Cot ]
, [ prefix "abs" Abs ]
, [ prefix "exp" Exp ]
, [ prefix "sqrt" Sqrt ]
, [ prefix "log" Log ]
, [ binary "^" Pow P.AssocRight ]
, [ prefix "-" Neg ]
, [ binary "*" Mul P.AssocLeft, binary "/" Div P.AssocLeft ]
, [ binary "+" Add P.AssocLeft, binary "-" Sub P.AssocLeft ]
] where binary s f a = P.Infix ( P.string s >> return f) a
prefix s f = P.Prefix (P.try (P.string s) >> return f)
factor :: Floating a => P.Parser (Expr a)
factor = do
_ <- P.char '('
expr <- build
_ <- P.char ')'
return $! expr
<|> variable
variable :: Floating a => P.Parser (Expr a)
variable = do
var <- P.many1 P.letter
return $! Var var
<|> number
number :: Floating a => P.Parser (Expr a)
number = do
pr <- P.many1 P.digit
let n = foldl stl 0 pr
P.option (Num n) . P.try $ do
_ <- P.char '.'
su <- P.many1 P.digit
return $! Num $ n + foldr str 0 su
where stl a x = (ctn x ctn '0') + a * 10
str x a = (ctn x ctn '0' + a) / 10
ctn = realToFrac . fromEnum
evaluate
:: Floating a
=> M.Map String a
-> Maybe (Expr a)
-> Maybe a
evaluate def = \ case
Just (Num num) -> Just num
Just (Var var) -> M.lookup var def
Just (Neg e1) -> liftM (negate ) (evaluate def $ Just e1)
Just (Add e1 e2) -> liftM2 (+ ) (evaluate def $ Just e1) (evaluate def $ Just e2)
Just (Sub e1 e2) -> liftM2 ( ) (evaluate def $ Just e1) (evaluate def $ Just e2)
Just (Mul e1 e2) -> liftM2 (* ) (evaluate def $ Just e1) (evaluate def $ Just e2)
Just (Div e1 e2) -> liftM2 (/ ) (evaluate def $ Just e1) (evaluate def $ Just e2)
Just (Pow e1 e2) -> liftM2 (** ) (evaluate def $ Just e1) (evaluate def $ Just e2)
Just (Sqrt e1) -> liftM (** 0.5 ) (evaluate def $ Just e1)
Just (Exp e1) -> liftM (exp ) (evaluate def $ Just e1)
Just (Log e1) -> liftM (log ) (evaluate def $ Just e1)
Just (Abs e1) -> liftM (abs ) (evaluate def $ Just e1)
Just (Sin e1) -> liftM (sin ) (evaluate def $ Just e1)
Just (Cos e1) -> liftM (cos ) (evaluate def $ Just e1)
Just (Tan e1) -> liftM (tan ) (evaluate def $ Just e1)
Just (Sec e1) -> liftM (inv . sin ) (evaluate def $ Just e1)
Just (Csc e1) -> liftM (inv . cos ) (evaluate def $ Just e1)
Just (Cot e1) -> liftM (inv . tan ) (evaluate def $ Just e1)
Just (Sinh e1) -> liftM (sinh ) (evaluate def $ Just e1)
Just (Cosh e1) -> liftM (cosh ) (evaluate def $ Just e1)
Just (Tanh e1) -> liftM (tanh ) (evaluate def $ Just e1)
Just (Sech e1) -> liftM (inv . sinh ) (evaluate def $ Just e1)
Just (Csch e1) -> liftM (inv . cosh ) (evaluate def $ Just e1)
Just (Coth e1) -> liftM (inv . tanh ) (evaluate def $ Just e1)
Just (ArcSin e1) -> liftM (asin ) (evaluate def $ Just e1)
Just (ArcCos e1) -> liftM (acos ) (evaluate def $ Just e1)
Just (ArcTan e1) -> liftM (atan ) (evaluate def $ Just e1)
Just (ArcSec e1) -> liftM (inv . asin ) (evaluate def $ Just e1)
Just (ArcCsc e1) -> liftM (inv . acos ) (evaluate def $ Just e1)
Just (ArcCot e1) -> liftM (inv . atan ) (evaluate def $ Just e1)
Just (ArcSinh e1) -> liftM (asinh ) (evaluate def $ Just e1)
Just (ArcCosh e1) -> liftM (acosh ) (evaluate def $ Just e1)
Just (ArcTanh e1) -> liftM (atanh ) (evaluate def $ Just e1)
Just (ArcSech e1) -> liftM (inv . asinh) (evaluate def $ Just e1)
Just (ArcCsch e1) -> liftM (inv . acosh) (evaluate def $ Just e1)
Just (ArcCoth e1) -> liftM (inv . atanh) (evaluate def $ Just e1)
_ -> Nothing
where inv = (/) 1