Spice up Metafile format

This commit is contained in:
I-Al-Istannen 2020-03-26 00:25:14 +01:00
parent 737c04a741
commit a0dcb37e12

View file

@ -3,76 +3,81 @@
module Mima.Vm.MetaFileParser module Mima.Vm.MetaFileParser
( Metadata(..) ( Metadata(..)
, GlobalData , AddressRange(..)
, LocalData , Range(..)
) where ) where
import Control.Applicative import Control.Applicative
import Control.Monad
import Data.Aeson import Data.Aeson
import Data.Aeson.Types import Data.Aeson.Types
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import qualified Data.Map.Strict as Map import qualified Data.Map.Strict as Map
import qualified Data.Text as T import qualified Data.Text as T
import Data.Traversable
import Numeric
import Text.ParserCombinators.ReadP
import Mima.Format import Mima.Format
import Mima.Vm.Word import Mima.Vm.Word
data Metadata = Metadata data Metadata = Metadata
{ global :: GlobalData { global :: Map.Map T.Text Value
, local :: LocalData , local :: [Range]
} deriving Show } deriving Show
type GlobalData = Map.Map T.Text Value data AddressRange
type LocalData = Map.Map MimaAddress (Map.Map T.Text Value) = RangeFromTo MimaAddress MimaAddress
| RangeAt MimaAddress
deriving Show
data Range = Range AddressRange (Map.Map T.Text Value)
deriving Show
instance FromJSON AddressRange where
parseJSON = withObject "address range" $ \obj -> at obj <|> range obj
where
at obj = RangeAt <$> ((obj .: "at") >>= boundedAddress)
range obj = do
start <- obj .: "start" >>= boundedAddress
stop <- obj .: "stop" >>= boundedAddress
when (start > stop) (fail "start must be <= stop")
return $ RangeFromTo start stop
instance ToJSON AddressRange where
toJSON = Object . addressRangeToMap
addressRangeToMap :: AddressRange -> Object
addressRangeToMap (RangeAt addr) = HM.fromList ["at" .= T.unpack (toHex addr)]
addressRangeToMap (RangeFromTo start stop) = HM.fromList
[ "start" .= T.unpack (toHex start)
, "stop" .= T.unpack (toHex stop)
]
instance FromJSON Range where
parseJSON value = do
addressRange <- parseJSON value
obj <- parseJSON value :: Parser Object
values <- obj .: "values"
pure $ Range addressRange values
instance ToJSON Range where
toJSON (Range addressRange value) = Object (addressRangeToMap addressRange <> HM.fromList (Map.toList value))
instance FromJSON Metadata where instance FromJSON Metadata where
parseJSON = withObject "root" $ \obj -> do parseJSON = withObject "metadata" $ \obj ->
globalData <- obj .: "global" Metadata <$> obj .: "global" <*> obj .: "local"
localData <- parseLocalData =<< obj .: "local"
return $ Metadata globalData localData
instance ToJSON Metadata where instance ToJSON Metadata where
toJSON Metadata{local, global} = object toJSON Metadata{local, global} = object
[ "global" .= toJSON global [ "global" .= toJSON global
, "local" .= toJSON (localDataToJson local) , "local" .= toJSON local
] ]
parseLocalData :: Value -> Parser LocalData boundedAddress :: Value -> Parser MimaAddress
parseLocalData = withObject "local" $ \obj -> do boundedAddress value = do
v <- for (HM.toList obj) unpackInner n <- parseJSON value :: Parser Integer
return $ Map.fromList (concat v) unless (n >= minVal && n <= maxVal) $
where fail $ T.unpack $
unpackInner (key, nestedValue) = do "Value '" <> toHex n <> "' out of bounds for an address."
(start, end) <- readMimaAddress (T.unpack key) pure $ fromIntegral n
innerValues <- parseJSON nestedValue
return $ zip [start..end] (repeat innerValues)
readMimaAddress :: String -> Parser (MimaAddress, MimaAddress)
readMimaAddress input = case readP_to_S readFromHex input of
[(addresses, [])] -> pure addresses
xs -> fail $ "'" ++ input ++ "' is no address, " ++ show xs
readFromHex :: ReadP (MimaAddress,MimaAddress)
readFromHex = (range <|> single) <* eof
where
range = (,) <$> (boundedAddress <* char '-') <*> boundedAddress
single = (\x -> (x, x)) <$> boundedAddress
boundedAddress :: ReadP MimaAddress
boundedAddress = do
value <- readS_to_P readHex :: ReadP Integer
if value > maxVal || value < minVal then
fail $ T.unpack $ "Value '" <> toHex value <> "' out of bounds for an address."
else
pure $ fromIntegral value
where where
maxVal = fromIntegral (maxBound :: MimaAddress) maxVal = fromIntegral (maxBound :: MimaAddress)
minVal = fromIntegral (minBound :: MimaAddress) minVal = fromIntegral (minBound :: MimaAddress)
localDataToJson :: LocalData -> Value
localDataToJson outerMap = object $ map formatInnerEntry (Map.toList outerMap)
where
formatInnerEntry (address, innerMap) = fixWidthHex 5 (toHex address) .= toJSON innerMap