module Language.ImProve.Core
  ( E (..)
  , V (..)
  , Name
  , AllE (..)
  , NumE
  , Statement (..)
  ) where

import Data.Ratio

type Name = String

data V a = V Bool [Name] a

class AllE a where
  showConst :: a -> String
  showType  :: a -> String
  zero      :: (Name -> a -> m (V a)) -> a

instance AllE Bool where
  showConst a = case a of
    True  -> "1"
    False -> "0"
  showType _ = "int"
  zero = const False

instance AllE Int where
  showConst = show
  showType _ = "int"
  zero = const 0
  
instance AllE Float where
  showConst = show
  showType _ = "float"
  zero = const 0

class    AllE a => NumE a
instance NumE Int
instance NumE Float

data E a where
  Ref   :: AllE a => V a -> E a
  Const :: AllE a => a -> E a
  Add   :: NumE a => E a -> E a -> E a
  Sub   :: NumE a => E a -> E a -> E a
  Mul   :: NumE a => E a -> a -> E a
  Div   :: NumE a => E a -> a -> E a
  Mod   :: E Int -> Int -> E Int
  Not   :: E Bool -> E Bool
  And   :: E Bool -> E Bool -> E Bool
  Or    :: E Bool -> E Bool -> E Bool
  Eq    :: AllE a => E a -> E a -> E Bool
  Lt    :: NumE a => E a -> E a -> E Bool
  Gt    :: NumE a => E a -> E a -> E Bool
  Le    :: NumE a => E a -> E a -> E Bool
  Ge    :: NumE a => E a -> E a -> E Bool
  Mux   :: AllE a => E Bool -> E a -> E a -> E a

instance Show (E a) where show = undefined 
instance Eq   (E a) where (==) = undefined

instance (Num a, AllE a, NumE a) => Num (E a) where
  (+) = Add
  (-) = Sub
  (*) = error "general multiplication not supported, use (*.)"
  negate a = 0 - a
  abs a = Mux (Lt a 0) (negate a) a
  signum a = Mux (Eq a 0) 0 $ Mux (Lt a 0) (-1) 1
  fromInteger = Const . fromInteger

instance Fractional (E Float) where
  (/) = error "general division not supported, use (/.)"
  recip a = 1 / a
  fromRational r = Const $ fromInteger (numerator r) / fromInteger (denominator r)

data Statement
  = AssignBool  (V Bool ) (E Bool )
  | AssignInt   (V Int  ) (E Int  )
  | AssignFloat (V Float) (E Float)
  | Branch      [Name] (E Bool) Statement Statement
  | Sequence    Statement Statement
  | Assert      [Name] (E Bool)
  | Assume      [Name] (E Bool)
  | Null