{-# LANGUAGE DeriveAnyClass, DeriveGeneric, DeriveDataTypeable #-}
module Language.Floorplan.Syntax where

import Data.Maybe (maybeToList)

type LayerID  = String -- upper
type FormalID = String -- lower
type FlagID   = String -- upper
type FieldID  = String -- lower

data Primitive = Page | Word | Byte | Bit
  deriving (Eq, Ord, Show)

lexeme2prim "page"  = Just Page
lexeme2prim "pages" = Just Page
lexeme2prim "word"  = Just Word
lexeme2prim "words" = Just Word
lexeme2prim "byte"  = Just Byte
lexeme2prim "bytes" = Just Byte
lexeme2prim "bit"   = Just Bit
lexeme2prim "bits"  = Just Bit
lexeme2prim _       = Nothing

instance Read Primitive where
  readsPrec _ input = case lexeme2prim input of
    Just x  -> [(x,"")]
    Nothing -> []

data Demarc =
    Enum [FlagID]
  | Bits [(FieldID, SizeArith)]
  | Union [Demarc]
  | Seq   [Demarc]
  | PtrF  FieldID
  | PtrL  LayerID
  | Blob  SizeArith
  | Graft (LayerID, [Arg])
  | Field FieldID Demarc
  | Pound Demarc
  | Repetition FormalID Demarc
  | Layer
    { name      :: LayerID
    , formals   :: [FormalID]
    , magnitude :: Maybe SizeArith
    , alignment :: Maybe SizeArith
    , magAlign  :: Maybe SizeArith
    , contains  :: [LayerID]
    , rhs       :: Demarc
    }
  deriving (Eq, Ord, Show)

free_vars :: Demarc -> [FormalID]
free_vars d@(Enum{})         = []
free_vars d@(Bits{})         = []
free_vars d@(Union ds)       = concatMap free_vars ds
free_vars d@(Seq ds)         = concatMap free_vars ds
free_vars d@(PtrF{})         = []
free_vars d@(PtrL{})         = []
free_vars d@(Blob{})         = []
free_vars d@(Graft{})        = []
free_vars f@(Field _ d)      = free_vars d
free_vars p@(Pound d)        = free_vars d
free_vars r@(Repetition f d) = f : free_vars d
free_vars d@(Layer{})        =
  [ fv
  | fv <- free_vars (rhs d)
  , fv `notElem` formals d
  ]

accum :: (Demarc -> Maybe a) -> Demarc -> [a]
accum fn d@(Enum{})         = maybeToList (fn d)
accum fn d@(Bits{})         = maybeToList (fn d)
accum fn d@(Union ds)       = maybeToList (fn d) ++ concatMap (accum fn) ds
accum fn d@(Seq ds)         = maybeToList (fn d) ++ concatMap (accum fn) ds
accum fn d@(PtrF{})         = maybeToList (fn d)
accum fn d@(PtrL{})         = maybeToList (fn d)
accum fn d@(Blob{})         = maybeToList (fn d)
accum fn d@(Graft{})        = maybeToList (fn d)
accum fn f@(Field _ d)      = maybeToList (fn f) ++ accum fn d
accum fn p@(Pound d)        = maybeToList (fn p) ++ accum fn d
accum fn r@(Repetition _ d) = maybeToList (fn r) ++ accum fn d
accum fn d@(Layer{})        = maybeToList (fn d) ++ accum fn (rhs d)

countMatches :: (Demarc -> Bool) -> Demarc -> Int
countMatches fn demarc = length $ (flip accum) demarc $ \d -> if fn d then Just 1 else Nothing

-- | Bool is whether or not anything was changed (so no need to do Eq check in fixed-point transformations)
fmapD :: (Demarc -> (Demarc, Bool)) -> Demarc -> (Demarc, Bool)
fmapD fncn d@(Enum{}) = fncn d
fmapD fncn d@(Bits{}) = fncn d
fmapD fncn d@(Union ds) = let ds'     = map (fmapD fncn) ds
                              (d', b) = fncn (Union $ map fst ds')
                          in  (d', or (b : map snd ds'))
fmapD fncn d@(Seq ds) = let ds'   = map (fmapD fncn) ds
                            (d', b) = fncn (Seq $ map fst ds')
                        in  (d', or (b : map snd ds'))
fmapD fncn d@(PtrF{}) = fncn d
fmapD fncn d@(PtrL{}) = fncn d
fmapD fncn d@(Blob{}) = fncn d
fmapD fncn d@(Graft{}) = fncn d
fmapD fncn   (Field f d) = let (d',  b')   = fmapD fncn d
                               (d'', b'')  = fncn (Field f d')
                           in  (d'', b' || b'')
fmapD fncn   (Pound d) = let (d', b') = fmapD fncn d
                             (d'', b'') = fncn (Pound d')
                         in  (d'', b' || b'')
fmapD fncn   (Repetition f d) = let (d', b') = fmapD fncn d
                                    (d'', b'') = fncn (Repetition f d')
                                in  (d'', b' || b'')
fmapD fncn d@(Layer{}) = let (rhs', b') = fmapD fncn $ rhs d
                             (d'', b'') = fncn (d { rhs = rhs' })
                         in  (d'', b' || b'')

data Arg =
    ArgL Literal
  | ArgF FormalID
  deriving (Eq, Ord, Show)

type Literal = Int

bin2int :: String -> Int
bin2int ('0':'b':xs) = let
    b2i [] = 0
    b2i (b:bs) = (read [b]) + (2 * b2i bs)
  in b2i $ reverse xs

data LitArith =
    Plus      LitArith LitArith
  | Minus     LitArith LitArith
  | Times     LitArith LitArith
  | Div       LitArith LitArith
  | Exponent  LitArith LitArith
  | Lit       Literal
  deriving (Eq, Ord, Show)

data SizeArith =
    SizePlus  SizeArith SizeArith
  | SizeMinus SizeArith SizeArith
  | SizeLit   (Maybe LitArith) Primitive
  deriving (Eq, Ord, Show)