{-  Exploring Languages with Interpreters and Functional Programming
    ELI Calculator Language; ProcessAST INCOMPLETE
    Copyright (C) 2017, 2018, H. Conrad Cunningham

1234567890123456789012345678901234567890123456789012345678901234567890

2017-09-19: Separated from EvalExpr as ProcessAST module.
2017-11-06: Made match current EvalExpr
2018-08-08: Updated for 2018 ELIFP textbook, Expression Language
            renamed ELI Calculator language, file EvalExpr renamed
            EvalCalc

TODO:
- Complete development, separate testing, decide how to better
  integrate with other modules

-}

module ProcessAST
    ( ValType, Name, Expr(..), Env, EvalErr, eval, lastVal,
      newEnviron, showEnviron, getNameBinding, hasNameBinding,
      newNameBinding, setNameBinding,
      simplify, deriv
    )  
where

-- Haskell libraries

-- ELI Calculator language modules
import EvalCalc
    (   ValType, Name, Expr(..), Env, EvalErr, eval, lastVal,
        newEnviron, showEnviron, getNameBinding, hasNameBinding,
        newNameBinding, setNameBinding
    )

-- Simplify an expression tree
simplify :: Expr -> Expr

simplify t@(Var _) = t

simplify t@(Val _) = t

simplify (Add l r) =
  case (simplify l, simplify r) of
    (Val 0, rr)    -> rr
    (ll, Val 0)    -> ll
    (Val x, Val y) -> Val (x+y)
    (ll, rr)       -> Add ll rr

simplify (Mul l r) =
  case (simplify l, simplify r) of
    (Val 0, rr)    -> Val 0
    (ll, Val 0)    -> Val 0
    (Val 1, rr)    -> rr
    (ll, Val 1)    -> ll
    (Val x, Val y) -> Val (x*y)
    (ll, rr)       -> Mul ll rr

simplify ex =
    error ("Simplification of " ++ (show ex) ++ " not supported.")

-- Symbolically differentiate an expression tree
-- Should change to Double or other Fractional type
deriv :: Expr -> Name -> Expr

deriv (Var n)   v
  | v == n        = Val 1

deriv (Add l r) v = Add (deriv l v) (deriv r v)

deriv (Mul l r) v = Add (Mul l (deriv r v)) (Mul r (deriv l v))

deriv ex        _ = Val 0


-- MOVE TO TESTING MODULE?

main = do
    let env = [("x",5), ("y",7),("z",1)]
    let exp1 = Val 3                  -- 3 
    let exp2 = Var "x"                -- x 
    let exp3 = Add (Val 1) (Val 2)    -- 1+2 
    let exp4 = Add (Var "x") (Val 3)  -- x + 3 
    let exp5 = Mul (Add (Var "x") (Var "y"))
                   (Add (Val 2) (Var "z")) -- (x + y) * (2 + z) 
    putStrLn ("Expression: " ++ show exp1) 
    putStrLn ("Evaluation with x=5, y=7, z=1:  "
              ++ show (eval exp1 env))
    putStrLn ("Simplification:  "
              ++ show (simplify exp1))
    putStrLn ("Derivative relative to x:\n  "
              ++ show (deriv exp1 "x"))
    putStrLn ("Derivative relative to y:\n  "
              ++ show (deriv exp1 "y"))
    putStrLn ""
             
    putStrLn ("Expression: " ++ show exp2) 
    putStrLn ("Evaluation with x=5, y=7, z=1:  "
              ++ show (eval exp2 env))
    putStrLn ("Simplification:  "
              ++ show (simplify exp2))
    putStrLn ("Derivative relative to x:\n  "
              ++ show (deriv exp2 "x"))
    putStrLn ("Derivative relative to y:\n  "
              ++ show (deriv exp2 "y"))
    putStrLn ""

    putStrLn ("Expression: " ++ show exp3) 
    putStrLn ("Evaluation with x=5, y=7, z=1:  " ++
              show (eval exp3 env))
    putStrLn ("Simplification:  "                ++
              show (simplify exp3))
    putStrLn ("Derivative relative to x:\n  "    ++
              show (deriv exp3 "x"))
    putStrLn ("Simplified Derivative relative to x:\n  "
              ++ show (simplify (deriv exp3 "x")))
    putStrLn ("Derivative relative to y:\n  "    ++
              show (deriv exp3 "y"))
    putStrLn ("Simplified Derivative relative to y:\n  "
              ++ show (simplify (deriv exp3 "y")))
    putStrLn ""     

    putStrLn ("Expression: " ++ show exp4) 
    putStrLn ("Evaluation with x=5, y=7, z=1:  " ++
              show (eval exp4 env))
    putStrLn ("Simplification:  "                ++
              show (simplify exp4))
    putStrLn ("Derivative relative to x:\n  "    ++
              show (deriv exp4 "x"))
    putStrLn ("Simplified Derivative relative to x:\n  "
              ++ show (simplify (deriv exp4 "x")))
    putStrLn ("Derivative relative to y:\n  "    ++
              show (deriv exp4 "y"))
    putStrLn ("Simplified Derivative relative to y:\n  "
              ++ show (simplify (deriv exp4 "y")))
    putStrLn ""     

    putStrLn ("Expression: " ++ show exp5) 
    putStrLn ("Evaluation with x=5, y=7, z=1:  " ++
              show (eval exp5 env))
    putStrLn ("Simplification:  "                ++
              show (simplify exp5))
    putStrLn ("Derivative relative to x:\n  "    ++
              show (deriv exp5 "x"))
    putStrLn ("Simplified Derivative relative to x:\n  "
              ++ show (simplify (deriv exp5 "x")))
    putStrLn ("Derivative relative to y:\n  "    ++
              show (deriv exp5 "y"))
    putStrLn ("Simplified Derivative relative to y:\n  "
              ++ show (simplify (deriv exp5 "y")))
    putStrLn ""
