import Maybe
import Lang

-- pridame ohodnoceni promennych

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

instance Monad Result where
  Chyba s >>=  _ = Chyba s
  Hodnota a >>= f = f a
  return x = Hodnota x
  fail s = Chyba s

{- type Vypocet x = V (Values->Result x)
   instance => Monad Vypocet where
     vyp1 >>= f = spoj
       where
         spoj hodnoty = case vyp1 hodnoty of
	                 Chyba s -> Chyba s
			 Hodnota a -> f a hodnoty
     return x = \_ -> Hodnota x
     fail s = \_ -> Chyba s

   obecneji:
-}

-- vypocet pouzivajici hodnoty typu s a vytvarejici hodnoty typu x v monade m
data Vypocet m s x = V (s->m x)
unV (V vyp) = vyp

instance Monad m => Monad (Vypocet m s) where
  V vyp1 >>= f = V spoj
    where
      spoj s = vyp1 s >>= \x -> unV (f x) s
  return x = V (\_ -> return x)
  fail ch = V (\_ -> fail ch)

class MonadRead m s | m -> s where
  get :: m s

instance Monad m => MonadRead (Vypocet m s) s where
  get = V (\s -> return s)

runVypocet :: Vypocet m s x -> s -> m x
runVypocet (V f) state = f state

eval::(Monad m, MonadRead m Values) => Expr->m Integer
eval (Plus e1 e2) = 
  do
   r1 <- eval e1
   r2 <- eval e2
   return (r1 + r2)
eval (Minus e1 e2) =
  do
   r1 <- eval e1
   r2 <- eval e2
   return (r1 - r2)
eval (Mul e1 e2) = 
  do
   r1 <- eval e1
   r2 <- eval e2
   return (r1 * r2)
eval (Div e1 e2) =
  do
   r1 <- eval e1
   r2 <- eval e2
   if r2 == 0 then fail "Deleni nulou" else return (r1 `div` r2)
eval (Mod e1 e2) =
  do
   r1 <- eval e1
   r2 <- eval e2 
   if r2 == 0 then fail "Deleni nulou" else return (r1 `mod` r2)
eval (Negate e) =
  do
   r <- eval e
   return (negate r)
eval (Num n) = return n
eval (Var s) =
  do
    ohodnoceni <- get
    case lookup s ohodnoceni of
      Just x -> return x
      Nothing -> fail ("Neznama promenna " ++ s)
