-- | Calculator module for evaluating mathematical expressions containing SI units

module Math.Haskellator (calculate) where

import Control.Monad

import Math.Haskellator.Internal.AstProcessingSteps.Evaluate
import Math.Haskellator.Internal.AstProcessingSteps.Normalize
import Math.Haskellator.Internal.Expr
import Math.Haskellator.Internal.Lexer
import Math.Haskellator.Internal.Parser
import Math.Haskellator.Internal.Utils.Error

getAst :: String -> Either Error Expr
getAst :: String -> Either Error Expr
getAst = String -> Either Error Tokens
scan (String -> Either Error Tokens)
-> (Tokens -> Either Error Expr) -> String -> Either Error Expr
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Tokens -> Either Error Expr
parse (Tokens -> Either Error Expr)
-> (Expr -> Either Error Expr) -> Tokens -> Either Error Expr
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> Expr -> Either Error Expr
normalize

-- | Determine the result of an expression
calculate :: String                 -- ^ The expression to evaluate
          -> Either Error EvalValue -- ^ The result of the expression
calculate :: String -> Either Error EvalValue
calculate String
input = do
    Expr
ast <- String -> Either Error Expr
getAst String
input
    Expr -> Either Error EvalValue
evaluateWithConv Expr
ast

evaluateWithConv :: Expr -> Either Error EvalValue
evaluateWithConv :: Expr -> Either Error EvalValue
evaluateWithConv = Expr -> Either Error EvalValue
ev where
    ev :: Expr -> Either Error EvalValue
ev (Conversion Expr
expr Dimension
target) = do
        EvalValue
r <- Expr -> Either Error EvalValue
execute Expr
expr
        let baseV :: EvalValue
baseV = EvalValue -> EvalValue
convertDimensionToBase EvalValue
r
        case EvalValue -> Dimension -> Maybe EvalValue
tryConvertDimensionTo EvalValue
baseV Dimension
target of
            Just EvalValue
v  -> EvalValue -> Either Error EvalValue
forall a. a -> Either Error a
forall (m :: * -> *) a. Monad m => a -> m a
return EvalValue
v
            Maybe EvalValue
Nothing -> Error -> Either Error EvalValue
forall a b. a -> Either a b
Left (Error -> Either Error EvalValue)
-> Error -> Either Error EvalValue
forall a b. (a -> b) -> a -> b
$ Kind -> String -> Error
Error Kind
RuntimeError (String -> Error) -> String -> Error
forall a b. (a -> b) -> a -> b
$ String
"Cannot convert " String -> String -> String
forall a. [a] -> [a] -> [a]
++ EvalValue -> String
forall a. Show a => a -> String
show EvalValue
baseV String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" to " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Dimension -> String
forall a. Show a => a -> String
show Dimension
target
    ev Expr
expr                     = Expr -> Either Error EvalValue
execute Expr
expr