mima-tools/src/Mima/Flag.hs

136 lines
3.6 KiB
Haskell

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Mima.Flag
( AddressRange
, lowerAddress
, upperAddress
, range
, rangeToAddresses
, rangeContains
, simplifyRanges
, AddressSpec
, rangesToSpec
, specToRanges
, specNull
, specContains
, Flag(..)
, allFlags
, flagChar
, Flags(..)
, rawFlags
, flagChecks
, impotentChecks
) where
import Data.List
import qualified Data.Map as Map
import qualified Data.Set as Set
import Mima.Word
data AddressRange = AddressRange
{ lowerAddress :: MimaAddress
, upperAddress :: MimaAddress
} deriving (Show, Eq, Ord)
range :: MimaAddress -> MimaAddress -> AddressRange
range a b
| a <= b = AddressRange a b
| otherwise = AddressRange b a
rangeToAddresses :: AddressRange -> [MimaAddress]
rangeToAddresses r = [lowerAddress r..upperAddress r]
rangeContains :: AddressRange -> MimaAddress -> Bool
rangeContains (AddressRange a b) c = a <= c && c <= b
simplifyRanges :: [AddressRange] -> [AddressRange]
simplifyRanges = helper . sort
where
helper :: [AddressRange] -> [AddressRange]
helper (r1:r2:rs)
| upperAddress r1 >= lowerAddress r2 = helper (merge r1 r2 : rs)
| otherwise = r1 : helper (r2:rs)
helper a = a
merge :: AddressRange -> AddressRange -> AddressRange
merge (AddressRange a1 b1) (AddressRange _ b2) = AddressRange a1 (max b1 b2)
newtype AddressSpec = AddressSpec [AddressRange]
deriving (Show)
rangesToSpec :: [AddressRange] -> AddressSpec
rangesToSpec = AddressSpec . simplifyRanges
specToRanges :: AddressSpec -> [AddressRange]
specToRanges (AddressSpec ranges) = ranges
specNull :: AddressSpec -> Bool
specNull = null . specToRanges
specContains :: AddressSpec -> MimaAddress -> Bool
specContains as addr = any (`rangeContains` addr) $ specToRanges as
{- Enough preamble, let's get to the flags -}
data Flag = Breakpoint | Executable | ReadOnly
deriving (Show, Eq, Ord)
allFlags :: [Flag]
allFlags = [Breakpoint, Executable, ReadOnly]
flagChar :: Flag -> Char
flagChar Breakpoint = 'b'
flagChar Executable = 'e'
flagChar ReadOnly = 'r'
data Flags a = Flags
{ flagBreakpoint :: a
, flagExecutable :: a
, flagReadOnly :: a
} deriving (Show)
instance Functor Flags where
fmap f Flags{..} = Flags
{ flagBreakpoint = f flagBreakpoint
, flagExecutable = f flagExecutable
, flagReadOnly = f flagReadOnly
}
instance Applicative Flags where
pure a = Flags a a a
f <*> a = Flags
{ flagBreakpoint = flagBreakpoint f $ flagBreakpoint a
, flagExecutable = flagExecutable f $ flagExecutable a
, flagReadOnly = flagReadOnly f $ flagReadOnly a
}
rawFlags :: Flags Flag
rawFlags = Flags
{ flagBreakpoint = Breakpoint
, flagExecutable = Executable
, flagReadOnly = ReadOnly
}
flagChecks :: Map.Map AddressRange (Set.Set Flag) -> Flags (MimaAddress -> Bool)
flagChecks m =
let getAddressSpec :: Flag -> AddressSpec
getAddressSpec f = rangesToSpec $ map fst $ filter (Set.member f . snd) $ Map.assocs m
conditions :: Flags (AddressSpec -> MimaAddress -> Bool)
conditions = Flags
{ flagBreakpoint = specContains
, flagExecutable = \as -> if specNull as then const True else specContains as
, flagReadOnly = specContains
}
in conditions <*> (getAddressSpec <$> rawFlags)
-- | Flag checks that should not alter the behaviour of the MiMa.
impotentChecks :: Flags (MimaAddress -> Bool)
impotentChecks = Flags
{ flagBreakpoint = const False
, flagExecutable = const True
, flagReadOnly = const False
}