import Maybe
import Lang

-- vyhodnoceni vyrazu

eval::Expr->Integer
eval (Plus e1 e2) = eval e1 + eval e2
eval (Minus e1 e2) = eval e1 - eval e2
eval (Mul e1 e2) = eval e1 * eval e2
eval (Div e1 e2) = eval e1 `div` eval e2
eval (Mod e1 e2) = eval e1 `mod` eval e2
eval (Negate e) = negate (eval e)
eval (Num n) = n

-- ??? jak pridat osetrovani chyb (deleni nulou) a ohodnoceni promennych
--     a nezblaznit se z toho?

-- jeste jeden pokus, chytre kombinatory

data Result x = Chyba String | Hodnota x deriving (Show)

bind :: Result a -> (a->Result b) -> Result b
bind (Chyba s) _ = Chyba s
bind (Hodnota a) f = f a

ret :: x -> Result x
ret x = Hodnota x

err :: String -> Result x
err ch = Chyba ch

eval1::Expr->Result Integer
eval1 (Plus e1 e2) = 
   eval1 e1 `bind` \r1 ->
   eval1 e2 `bind` \r2 ->
   ret (r1 + r2)
eval1 (Minus e1 e2) =
   eval1 e1 `bind` \r1 ->
   eval1 e2 `bind` \r2 ->
   ret (r1 - r2)
eval1 (Mul e1 e2) = 
   eval1 e1 `bind` \r1 ->
   eval1 e2 `bind` \r2 ->
   ret (r1 * r2)
eval1 (Div e1 e2) =
   eval1 e1 `bind` \r1 ->
   eval1 e2 `bind` \r2 ->
   if r2 == 0 then err "Deleni nulou" else ret (r1 `div` r2)
eval1 (Mod e1 e2) =
   eval1 e1 `bind` \r1 ->
   eval1 e2 `bind` \r2 ->
   if r2 == 0 then err "Deleni nulou" else ret (r1 `mod` r2)
eval1 (Negate e) =
   eval1 e `bind` \r ->
   ret (negate r)
eval1 (Num n) = ret n

-- typ m :: *->* spolu s operacemi bind a ret typu
-- bind :: m a -> (a -> m b) -> m b
-- ret :: a -> m a
-- tvori monadu, splnuje-li nasledujici axiomy:
--   (ret x) `bind` f = f x
--   v `bind` ret = v
--   v `bind` (\a -> f a `bind` g) = (v `bind` (\a -> f a)) `bind` g
