Split up phase 2 into multiple modules

This commit is contained in:
Joscha 2020-04-05 09:51:24 +00:00
parent d82ce69b1b
commit 8d0e70cf5d
5 changed files with 503 additions and 436 deletions

View file

@ -1,441 +1,10 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
module Mima.Asm.Phase2
( phaseS1 -- TODO only leave the proper types
, phaseS2
( phase2
) where
import Control.Monad
import Control.Monad.Trans.Class
import Control.Monad.Trans.State
import qualified Data.Aeson as A
import Data.Foldable
import qualified Data.Map.Strict as Map
import Data.Maybe
import qualified Data.Text as T
import Data.Void
import qualified Mima.Asm.Phase1 as P1
import Mima.Asm.Types
import qualified Mima.Asm.Phase1 as P1
import Mima.Asm.Weed
import qualified Mima.Vm.Instruction as Vm
import qualified Mima.Vm.Word as Vm
import qualified Mima.Vm.State as Vm
data Subphase
= S1
-- ^ Freshly converted from 'Phase1'. Arrays are converted into multiple
-- literal values. Comments are removed.
| S2
-- ^ After resolving all .org-s and relative positions and assigning each
-- token an address.
| S3
-- ^ After extracting and removing all labels and .meta-s. This step results
-- in a map to resolve labels and a complete set of .meta-* metadata.
| S4
-- ^ After resolving all labels. Instructions are converted into literal
-- values.
| S5
-- ^ After extracting all initial register values.
-- | The name of a label or a meta tag.
data Name s = Name s T.Text
deriving (Show, Functor)
instance Onion Name where
peel (Name s _) = s
-- | A location defined by an absolute or relative address or by a label.
data Location1 s
= Loc1Absolute s Vm.MimaAddress
| Loc1Relative s Integer
| Loc1Label (Name s)
| Loc1LabelRel s (Name s) s Integer
deriving (Show, Functor)
instance Onion Location1 where
peel (Loc1Absolute s _) = s
peel (Loc1Relative s _) = s
peel (Loc1Label l) = peel l
peel (Loc1LabelRel s _ _ _) = s
-- | A location defined by an absolute address or by a label.
data Location2 s
= Loc2Absolute s Vm.MimaAddress
| Loc2Label (Name s)
| Loc2LabelRel s (Name s) s Integer
deriving (Show, Functor)
instance Onion Location2 where
peel (Loc2Absolute s _) = s
peel (Loc2Label l) = peel l
peel (Loc2LabelRel s _ _ _) = s
-- | A type family for locations in various stages of resolution.
type family LocationX (t :: Subphase) (s :: *)
type instance LocationX 'S1 s = Location1 s
type instance LocationX 'S2 s = Location2 s
type instance LocationX 'S3 s = Location2 s
type instance LocationX 'S4 s = Vm.MimaAddress
type instance LocationX 'S5 s = Vm.MimaAddress
-- | A type family for addresses of various tokens.
type family AddressX (t :: Subphase) (s :: *)
type instance AddressX 'S1 s = ()
type instance AddressX 'S2 s = Vm.MimaAddress
type instance AddressX 'S3 s = Vm.MimaAddress
type instance AddressX 'S4 s = Vm.MimaAddress
type instance AddressX 'S5 s = Vm.MimaAddress
-- | A representation for .org addresses.
data OrgAddress s
= OrgAddrAbsolute s Vm.MimaAddress
| OrgAddrRelative s Integer
deriving (Show, Functor)
instance Onion OrgAddress where
peel (OrgAddrAbsolute s _) = s
peel (OrgAddrRelative s _) = s
type family TokenOrgX (t :: Subphase) (s :: *)
type instance TokenOrgX 'S1 s = OrgAddress s
type instance TokenOrgX 'S2 s = Void
type instance TokenOrgX 'S3 s = Void
type instance TokenOrgX 'S4 s = Void
type instance TokenOrgX 'S5 s = Void
type family TokenLabelX (t :: Subphase) (s :: *)
type instance TokenLabelX 'S1 s = Name s
type instance TokenLabelX 'S2 s = Name s
type instance TokenLabelX 'S3 s = Void
type instance TokenLabelX 'S4 s = Void
type instance TokenLabelX 'S5 s = Void
-- | A wrapper that annotates a 'A.Value' with an @s@ value.
data JsonValue s = JsonValue s A.Value
deriving (Show, Functor)
instance Onion JsonValue where
peel (JsonValue s _) = s
-- | A representation for .meta-start and .meta-stop directives.
data Meta s
= MetaStart s (Name s) (JsonValue s)
| MetaStop s (Name s)
deriving (Show, Functor)
instance Onion Meta where
peel (MetaStart s _ _) = s
peel (MetaStop s _) = s
type family TokenMetaX (t :: Subphase) (s :: *)
type instance TokenMetaX 'S1 s = Meta s
type instance TokenMetaX 'S2 s = Meta s
type instance TokenMetaX 'S3 s = Void
type instance TokenMetaX 'S4 s = Void
type instance TokenMetaX 'S5 s = Void
-- | A stripped-down representation of Mima words that does not have an 'Onion'
-- instance because none is required.
data MimaWord (t :: Subphase) (s :: *)
= WordRaw Vm.MimaWord
| WordLocation (LocationX t s)
deriving instance Show s => Show (MimaWord 'S1 s)
deriving instance Show s => Show (MimaWord 'S2 s)
deriving instance Show s => Show (MimaWord 'S3 s)
deriving instance Show s => Show (MimaWord 'S4 s)
deriving instance Show s => Show (MimaWord 'S5 s)
-- | A stripped-down representation of Mima instructions that does not have an
-- 'Onion' instance because none is required.
data Instruction (t :: Subphase) (s :: *)
= SmallInstruction Vm.SmallOpcode (LocationX t s)
| LargeInstruction Vm.LargeOpcode (Maybe Vm.SmallValue)
deriving instance Show s => Show (Instruction 'S1 s)
deriving instance Show s => Show (Instruction 'S2 s)
deriving instance Show s => Show (Instruction 'S3 s)
deriving instance Show s => Show (Instruction 'S4 s)
deriving instance Show s => Show (Instruction 'S5 s)
type family TokenInstrX (t :: Subphase) (s :: *)
type instance TokenInstrX 'S1 s = Instruction 'S1 s
type instance TokenInstrX 'S2 s = Instruction 'S2 s
type instance TokenInstrX 'S3 s = Instruction 'S3 s
type instance TokenInstrX 'S4 s = Void
type instance TokenInstrX 'S5 s = Void
data RegisterDirective (t :: Subphase) (s :: *)
= RegIar s (LocationX t s)
| RegAcc s (MimaWord t s)
| RegRa s (LocationX t s)
| RegSp s (LocationX t s)
| RegFp s (LocationX t s)
deriving instance Show s => Show (RegisterDirective 'S1 s)
deriving instance Show s => Show (RegisterDirective 'S2 s)
deriving instance Show s => Show (RegisterDirective 'S3 s)
deriving instance Show s => Show (RegisterDirective 'S4 s)
deriving instance Show s => Show (RegisterDirective 'S5 s)
instance Onion (RegisterDirective t) where
peel (RegIar s _) = s
peel (RegAcc s _) = s
peel (RegRa s _) = s
peel (RegSp s _) = s
peel (RegFp s _) = s
type family TokenRegX (t :: Subphase) (s :: *)
type instance TokenRegX 'S1 s = RegisterDirective 'S1 s
type instance TokenRegX 'S2 s = RegisterDirective 'S2 s
type instance TokenRegX 'S3 s = RegisterDirective 'S3 s
type instance TokenRegX 'S4 s = RegisterDirective 'S4 s
type instance TokenRegX 'S5 s = Void
data AsmToken (t :: Subphase) (s :: *)
= TokenOrg s (TokenOrgX t s)
| TokenLabel s (AddressX t s) (TokenLabelX t s)
| TokenMeta s (AddressX t s) (TokenMetaX t s)
| TokenLit s (AddressX t s) (MimaWord t s)
| TokenInstr s (AddressX t s) (TokenInstrX t s)
| TokenReg s (AddressX t s) (TokenRegX t s)
deriving instance Show s => Show (AsmToken 'S1 s)
deriving instance Show s => Show (AsmToken 'S2 s)
deriving instance Show s => Show (AsmToken 'S3 s)
deriving instance Show s => Show (AsmToken 'S4 s)
deriving instance Show s => Show (AsmToken 'S5 s)
instance Onion (AsmToken t) where
peel (TokenOrg s _) = s
peel (TokenLabel s _ _) = s
peel (TokenMeta s _ _) = s
peel (TokenLit s _ _) = s
peel (TokenInstr s _ _) = s
peel (TokenReg s _ _) = s
type Phase2 t s = [AsmToken t s]
{- Phae 1 to Phase 2 -}
p1ToP2Name :: P1.Name s -> Name s
p1ToP2Name (P1.Name s text) = Name s text
p1ToP2JsonValue :: P1.JsonValue s -> JsonValue s
p1ToP2JsonValue (P1.JsonValue s value) = JsonValue s value
intToBounded :: forall s n. (Bounded n, Integral n) => s -> Integer -> Weed (WeedError s) n
intToBounded s val = do
when (val < minVal || val > maxVal) $
harmless $ errorWith s "value out of bounds"
pure $ fromInteger val
where
maxVal = toInteger (maxBound :: n)
minVal = -maxVal - 1
p1ToP2Address :: P1.Address s -> WeedS1 s (OrgAddress s)
p1ToP2Address (P1.AddressAbsolute s addr) = lift $ OrgAddrAbsolute s <$> intToBounded s addr
p1ToP2Address (P1.AddressRelative s offset) = pure $ OrgAddrRelative s offset
p1ToP2Location :: P1.Location s -> WeedS1 s (Location1 s)
p1ToP2Location (P1.LocationAddress (P1.AddressAbsolute s addr)) = lift $ Loc1Absolute s <$> intToBounded s addr
p1ToP2Location (P1.LocationAddress (P1.AddressRelative s offset)) =
pure $ Loc1Relative s offset
p1ToP2Location (P1.LocationLabel name) = pure $ Loc1Label $ p1ToP2Name name
p1ToP2Location (P1.LocationLabelRel s name s1 offset)
= pure $ Loc1LabelRel s (p1ToP2Name name) s1 offset
p1ToP2Instruction :: P1.Instruction s -> WeedS1 s (Instruction 'S1 s)
p1ToP2Instruction (P1.SmallInstruction _ (P1.SmallOpcode _ so) loc) = SmallInstruction so <$> p1ToP2Location loc
p1ToP2Instruction (P1.LargeInstruction _ (P1.LargeOpcode _ lo) maybeSv) = do
val <- case maybeSv of
Nothing -> pure Nothing
Just (P1.SmallValue s v) -> lift $ Just <$> intToBounded s v
pure $ LargeInstruction lo val
p1ToP2Word :: P1.MimaWord s -> WeedS1 s (MimaWord 'S1 s)
p1ToP2Word (P1.WordRaw s w) = lift $ WordRaw <$> intToBounded s w
p1ToP2Word (P1.WordLocation loc) = WordLocation <$> p1ToP2Location loc
p1ToP2RegDir :: P1.RegisterDirective s -> WeedS1 s (RegisterDirective 'S1 s)
p1ToP2RegDir (P1.RegIar s _ loc) = RegIar s <$> p1ToP2Location loc
p1ToP2RegDir (P1.RegAcc s _ word) = RegAcc s <$> p1ToP2Word word
p1ToP2RegDir (P1.RegRa s _ loc) = RegRa s <$> p1ToP2Location loc
p1ToP2RegDir (P1.RegSp s _ loc) = RegSp s <$> p1ToP2Location loc
p1ToP2RegDir (P1.RegFp s _ loc) = RegFp s <$> p1ToP2Location loc
{- Subphase 1 -}
data MetaS1 s = MetaS1 s (P1.Name s) (P1.JsonValue s)
deriving (Show)
instance Onion MetaS1 where
peel (MetaS1 s _ _) = s
data StateS1 s = StateS1
{ s1Metas :: Map.Map T.Text (MetaS1 s)
, s1Tokens :: [AsmToken 'S1 s]
} deriving (Show)
type WeedS1 s = StateT (StateS1 s) (Weed (WeedError s))
s1AddMeta :: s -> P1.Name s -> P1.JsonValue s -> WeedS1 s ()
s1AddMeta s name@(P1.Name namePos nameText) value = do
s1 <- get
when (nameText `Map.member` s1Metas s1) $
lift $ harmless $ errorWith namePos "duplicate .meta names"
let meta = MetaS1 s name value
put s1{s1Metas = Map.insert nameText meta $ s1Metas s1}
s1TakeMetas :: WeedS1 s [MetaS1 s]
s1TakeMetas = do
s <- get
put s{s1Metas = Map.empty}
pure $ Map.elems $ s1Metas s
s1WithMetas :: WeedS1 s () -> WeedS1 s ()
s1WithMetas f = do
metas <- s1TakeMetas
for_ (reverse metas) $ \(MetaS1 s name value) ->
s1AddToken $ TokenMeta s () $
MetaStart s (p1ToP2Name name) (p1ToP2JsonValue value)
f
for_ metas $ \(MetaS1 s name _) ->
s1AddToken $ TokenMeta s () $ MetaStop s (p1ToP2Name name)
s1AddToken :: AsmToken 'S1 s -> WeedS1 s ()
s1AddToken t = modify $ \s -> s{s1Tokens = t : s1Tokens s}
s1AddP1Token :: P1.AsmToken s -> WeedS1 s ()
s1AddP1Token (P1.TokenLabel name) =
s1AddToken $ TokenLabel (peel name) () $ p1ToP2Name name
s1AddP1Token (P1.TokenInstruction instr) = do
i <- p1ToP2Instruction instr
s1WithMetas $ s1AddToken $ TokenInstr (peel instr) () i
s1AddP1Token (P1.TokenDirective (P1.Reg s _ regDir)) = do
r <- p1ToP2RegDir regDir
s1AddToken $ TokenReg s () r
s1AddP1Token (P1.TokenDirective (P1.Org s _ addr)) = do
s1WithMetas $ pure ()
a <- p1ToP2Address addr
s1AddToken $ TokenOrg s a
s1AddP1Token (P1.TokenDirective (P1.Lit s _ word)) = do
w <- p1ToP2Word word
s1WithMetas $ s1AddToken $ TokenLit s () w
s1AddP1Token (P1.TokenDirective (P1.Arr s _ ws)) =
s1WithMetas $ for_ ws $ \word -> do
w <- p1ToP2Word word
pure $ s1AddToken $ TokenLit s () w
s1AddP1Token (P1.TokenDirective (P1.Meta s _ name value)) =
s1AddMeta s name value
s1AddP1Token (P1.TokenDirective (P1.MetaStart s _ name value)) =
s1AddToken $ TokenMeta s () $
MetaStart s (p1ToP2Name name) (p1ToP2JsonValue value)
s1AddP1Token (P1.TokenDirective (P1.MetaStop s _ name)) =
s1AddToken $ TokenMeta s () $ MetaStop s (p1ToP2Name name)
s1AddP1Token P1.TokenComment{} = pure ()
phaseS1 :: P1.Phase1 s -> Weed (WeedError s) (Phase2 'S1 s)
phaseS1 ts = do
let initialS = StateS1 Map.empty []
s <- flip execStateT initialS $ do
traverse_ s1AddP1Token ts
s1WithMetas $ pure ()
pure $ reverse $ s1Tokens s
{- Subphase 2 -}
data StateS2 s = StateS2
{ s2CurrentAddress :: Vm.MimaAddress
, s2AddressFilled :: Bool
} deriving (Show)
type WeedS2 s = StateT (StateS2 s) (Weed (WeedError s))
s2AddAddress :: s -> Int -> WeedS2 s ()
s2AddAddress s amount = do
s2 <- get
s2SetAddress s (s2CurrentAddress s2 + fromIntegral amount)
s2SetAddress :: s -> Vm.MimaAddress -> WeedS2 s ()
s2SetAddress s newAddress = do
s2 <- get
let oldAddress = s2CurrentAddress s2
when (oldAddress > newAddress) $
lift $ harmless $
errorWith s "new address must not be smaller than current address"
put $ s2{s2CurrentAddress = newAddress}
when (newAddress /= oldAddress) $
modify $ \s2' -> s2'{s2AddressFilled = False}
s2NextAddress :: s -> WeedS2 s Vm.MimaAddress
s2NextAddress s = do
s2 <- get
when (s2AddressFilled s2) $ s2AddAddress s 1
pure $ s2CurrentAddress s2
s2ConvertLocation :: Vm.MimaAddress -> LocationX 'S1 s -> WeedS2 s (LocationX 'S2 s)
s2ConvertLocation _ (Loc1Absolute s addr) = pure $ Loc2Absolute s addr
s2ConvertLocation _ (Loc1Label name) = pure $ Loc2Label name
s2ConvertLocation _ (Loc1LabelRel s name s1 offset)
= pure $ Loc2LabelRel s name s1 offset
s2ConvertLocation baseAddr (Loc1Relative s delta) = do
let newAddr = toInteger baseAddr + delta
val <- lift $ intToBounded s newAddr
pure $ Loc2Absolute s val
s2ConvertMimaWord :: Vm.MimaAddress -> MimaWord 'S1 s -> WeedS2 s (MimaWord 'S2 s)
s2ConvertMimaWord baseAddr (WordLocation loc) =
WordLocation <$> s2ConvertLocation baseAddr loc
s2ConvertMimaWord _ (WordRaw word) = pure $ WordRaw word
s2ConvertInstruction :: Vm.MimaAddress -> Instruction 'S1 s -> WeedS2 s (Instruction 'S2 s)
s2ConvertInstruction baseAddr (SmallInstruction opcode loc) =
SmallInstruction opcode <$> s2ConvertLocation baseAddr loc
s2ConvertInstruction _ (LargeInstruction opcode val) =
pure $ LargeInstruction opcode val
s2ConvertRegisterDirective :: Vm.MimaAddress -> RegisterDirective 'S1 s -> WeedS2 s (RegisterDirective 'S2 s)
s2ConvertRegisterDirective baseAddr (RegIar s loc) =
RegIar s <$> s2ConvertLocation baseAddr loc
s2ConvertRegisterDirective baseAddr (RegAcc s word) =
RegAcc s <$> s2ConvertMimaWord baseAddr word
s2ConvertRegisterDirective baseAddr (RegRa s loc) =
RegRa s <$> s2ConvertLocation baseAddr loc
s2ConvertRegisterDirective baseAddr (RegSp s loc) =
RegSp s <$> s2ConvertLocation baseAddr loc
s2ConvertRegisterDirective baseAddr (RegFp s loc) =
RegFp s <$> s2ConvertLocation baseAddr loc
s2ConvertP2Token :: AsmToken 'S1 s -> WeedS2 s (Maybe (AsmToken 'S2 s))
s2ConvertP2Token (TokenOrg _ (OrgAddrAbsolute s address))
= Nothing <$ s2SetAddress s address
s2ConvertP2Token (TokenOrg _ (OrgAddrRelative s address))
| address < 0 = Nothing <$ s2SetAddress s (maxBound + fromIntegral address)
| otherwise = Nothing <$ s2AddAddress s (fromIntegral address)
s2ConvertP2Token (TokenLabel s _ name) = do
address <- s2CurrentAddress <$> get
pure $ Just $ TokenLabel s address name
s2ConvertP2Token (TokenMeta s _ meta) = do
address <- s2CurrentAddress <$> get
pure $ Just $ TokenMeta s address meta
s2ConvertP2Token (TokenLit s _ word) = do
address <- s2NextAddress s
newWord <- s2ConvertMimaWord address word
pure $ Just $ TokenLit s address newWord
s2ConvertP2Token (TokenInstr s _ instr) = do
address <- s2NextAddress s
Just . TokenInstr s address <$> s2ConvertInstruction address instr
s2ConvertP2Token (TokenReg s _ reg) = do
address <- s2CurrentAddress <$> get
Just . TokenReg s address <$> s2ConvertRegisterDirective address reg
phaseS2 :: Phase2 'S1 s -> Weed (WeedError s) (Phase2 'S2 s)
phaseS2 s1 = do
let initialS = StateS2 0 False
catMaybes <$> evalStateT (traverse s2ConvertP2Token s1) initialS
phase2 :: P1.Phase1 s -> Weed (WeedError s) Vm.MimaState
phase2 = error "to be implemented"

View file

@ -0,0 +1,138 @@
{-# LANGUAGE DataKinds #-}
module Mima.Asm.Phase2.Subphase1
( subphase1
) where
import Control.Monad
import Control.Monad.Trans.Class
import Control.Monad.Trans.State
import Data.Foldable
import qualified Data.Map.Strict as Map
import qualified Data.Text as T
import qualified Mima.Asm.Phase1 as P1
import Mima.Asm.Phase2.Types
import Mima.Asm.Phase2.Util
import Mima.Asm.Types
import Mima.Asm.Weed
{- Converting phase 1 types to phase 2 types -}
p1ToP2Name :: P1.Name s -> Name s
p1ToP2Name (P1.Name s text) = Name s text
p1ToP2JsonValue :: P1.JsonValue s -> JsonValue s
p1ToP2JsonValue (P1.JsonValue s value) = JsonValue s value
p1ToP2Address :: P1.Address s -> WeedS1 s (OrgAddress s)
p1ToP2Address (P1.AddressAbsolute s addr) = lift $ OrgAddrAbsolute s <$> intToBounded s addr
p1ToP2Address (P1.AddressRelative s offset) = pure $ OrgAddrRelative s offset
p1ToP2Location :: P1.Location s -> WeedS1 s (Location1 s)
p1ToP2Location (P1.LocationAddress (P1.AddressAbsolute s addr)) =
lift $ Loc1Absolute s <$> intToBounded s addr
p1ToP2Location (P1.LocationAddress (P1.AddressRelative s offset)) =
pure $ Loc1Relative s offset
p1ToP2Location (P1.LocationLabel name) = pure $ Loc1Label $ p1ToP2Name name
p1ToP2Location (P1.LocationLabelRel s name s' offset) =
pure $ Loc1LabelRel s (p1ToP2Name name) s' offset
p1ToP2Instruction :: P1.Instruction s -> WeedS1 s (Instruction 'S1 s)
p1ToP2Instruction (P1.SmallInstruction _ (P1.SmallOpcode _ so) loc) =
SmallInstruction so <$> p1ToP2Location loc
p1ToP2Instruction (P1.LargeInstruction _ (P1.LargeOpcode _ lo) maybeSv) = do
val <- case maybeSv of
Nothing -> pure Nothing
Just (P1.SmallValue s v) -> lift $ Just <$> intToBounded s v
pure $ LargeInstruction lo val
p1ToP2Word :: P1.MimaWord s -> WeedS1 s (MimaWord 'S1 s)
p1ToP2Word (P1.WordRaw s w) = lift $ WordRaw <$> intToBounded s w
p1ToP2Word (P1.WordLocation loc) = WordLocation <$> p1ToP2Location loc
p1ToP2RegDir :: P1.RegisterDirective s -> WeedS1 s (RegisterDirective 'S1 s)
p1ToP2RegDir (P1.RegIar s _ loc) = RegIar s <$> p1ToP2Location loc
p1ToP2RegDir (P1.RegAcc s _ word) = RegAcc s <$> p1ToP2Word word
p1ToP2RegDir (P1.RegRa s _ loc) = RegRa s <$> p1ToP2Location loc
p1ToP2RegDir (P1.RegSp s _ loc) = RegSp s <$> p1ToP2Location loc
p1ToP2RegDir (P1.RegFp s _ loc) = RegFp s <$> p1ToP2Location loc
{- Subphase 1 -}
data SingleMeta s = SingleMeta s (P1.Name s) (P1.JsonValue s)
deriving (Show)
instance Onion SingleMeta where
peel (SingleMeta s _ _) = s
data StateS1 s = StateS1
{ s1Metas :: Map.Map T.Text (SingleMeta s)
, s1Tokens :: [AsmToken 'S1 s]
} deriving (Show)
type WeedS1 s = StateT (StateS1 s) (Weed (WeedError s))
addMeta :: s -> P1.Name s -> P1.JsonValue s -> WeedS1 s ()
addMeta s name@(P1.Name namePos nameText) value = do
s1 <- get
when (nameText `Map.member` s1Metas s1) $
lift $ harmless $ errorWith namePos "duplicate .meta names"
let meta = SingleMeta s name value
put s1{s1Metas = Map.insert nameText meta $ s1Metas s1}
takeMetas :: WeedS1 s [SingleMeta s]
takeMetas = do
s <- get
put s{s1Metas = Map.empty}
pure $ Map.elems $ s1Metas s
withMetas :: WeedS1 s () -> WeedS1 s ()
withMetas f = do
metas <- takeMetas
for_ (reverse metas) $ \(SingleMeta s name value) ->
addToken $ TokenMeta s () $
MetaStart s (p1ToP2Name name) (p1ToP2JsonValue value)
f
for_ metas $ \(SingleMeta s name _) ->
addToken $ TokenMeta s () $ MetaStop s (p1ToP2Name name)
addToken :: AsmToken 'S1 s -> WeedS1 s ()
addToken t = modify $ \s -> s{s1Tokens = t : s1Tokens s}
addP1Token :: P1.AsmToken s -> WeedS1 s ()
addP1Token (P1.TokenLabel name) =
addToken $ TokenLabel (peel name) () $ p1ToP2Name name
addP1Token (P1.TokenInstruction instr) = do
i <- p1ToP2Instruction instr
withMetas $ addToken $ TokenInstr (peel instr) () i
addP1Token (P1.TokenDirective (P1.Reg s _ regDir)) = do
r <- p1ToP2RegDir regDir
addToken $ TokenReg s () r
addP1Token (P1.TokenDirective (P1.Org s _ addr)) = do
withMetas $ pure ()
a <- p1ToP2Address addr
addToken $ TokenOrg s a
addP1Token (P1.TokenDirective (P1.Lit s _ word)) = do
w <- p1ToP2Word word
withMetas $ addToken $ TokenLit s () w
addP1Token (P1.TokenDirective (P1.Arr s _ ws)) =
withMetas $ for_ ws $ \word -> do
w <- p1ToP2Word word
addToken $ TokenLit s () w
addP1Token (P1.TokenDirective (P1.Meta s _ name value)) =
addMeta s name value
addP1Token (P1.TokenDirective (P1.MetaStart s _ name value)) =
addToken $ TokenMeta s () $
MetaStart s (p1ToP2Name name) (p1ToP2JsonValue value)
addP1Token (P1.TokenDirective (P1.MetaStop s _ name)) =
addToken $ TokenMeta s () $ MetaStop s (p1ToP2Name name)
addP1Token P1.TokenComment{} = pure ()
subphase1 :: P1.Phase1 s -> Weed (WeedError s) (Phase2 'S1 s)
subphase1 ts = do
let initialS = StateS1 Map.empty []
s <- flip execStateT initialS $ do
traverse_ addP1Token ts
withMetas $ pure ()
pure $ reverse $ s1Tokens s

View file

@ -0,0 +1,105 @@
{-# LANGUAGE DataKinds #-}
module Mima.Asm.Phase2.Subphase2
( subphase2
) where
import Control.Monad
import Control.Monad.Trans.Class
import Control.Monad.Trans.State
import Data.Maybe
import Mima.Asm.Phase2.Types
import Mima.Asm.Phase2.Util
import Mima.Asm.Weed
import qualified Mima.Vm.Word as Vm
data StateS2 s = StateS2
{ s2CurrentAddress :: Vm.MimaAddress
, s2AddressFilled :: Bool
} deriving (Show)
type WeedS2 s = StateT (StateS2 s) (Weed (WeedError s))
addAddress :: s -> Int -> WeedS2 s ()
addAddress s amount = do
s2 <- get
setAddress s (s2CurrentAddress s2 + fromIntegral amount)
setAddress :: s -> Vm.MimaAddress -> WeedS2 s ()
setAddress s newAddress = do
s2 <- get
let oldAddress = s2CurrentAddress s2
when (oldAddress > newAddress) $
lift $ harmless $
errorWith s "new address must not be smaller than current address"
put $ s2{s2CurrentAddress = newAddress}
when (newAddress /= oldAddress) $
modify $ \s2' -> s2'{s2AddressFilled = False}
nextAddress :: s -> WeedS2 s Vm.MimaAddress
nextAddress s = do
s2 <- get
when (s2AddressFilled s2) $ addAddress s 1
pure $ s2CurrentAddress s2
convertLocation :: Vm.MimaAddress -> LocationX 'S1 s -> WeedS2 s (LocationX 'S2 s)
convertLocation _ (Loc1Absolute s addr) = pure $ Loc2Absolute s addr
convertLocation _ (Loc1Label name) = pure $ Loc2Label name
convertLocation _ (Loc1LabelRel s name s' offset) =
pure $ Loc2LabelRel s name s' offset
convertLocation baseAddr (Loc1Relative s delta) = do
let newAddr = toInteger baseAddr + delta
val <- lift $ intToBounded s newAddr
pure $ Loc2Absolute s val
convertMimaWord :: Vm.MimaAddress -> MimaWord 'S1 s -> WeedS2 s (MimaWord 'S2 s)
convertMimaWord baseAddr (WordLocation loc) =
WordLocation <$> convertLocation baseAddr loc
convertMimaWord _ (WordRaw word) = pure $ WordRaw word
convertInstruction :: Vm.MimaAddress -> Instruction 'S1 s -> WeedS2 s (Instruction 'S2 s)
convertInstruction baseAddr (SmallInstruction opcode loc) =
SmallInstruction opcode <$> convertLocation baseAddr loc
convertInstruction _ (LargeInstruction opcode val) =
pure $ LargeInstruction opcode val
convertRegisterDirective :: Vm.MimaAddress -> RegisterDirective 'S1 s -> WeedS2 s (RegisterDirective 'S2 s)
convertRegisterDirective baseAddr (RegIar s loc) =
RegIar s <$> convertLocation baseAddr loc
convertRegisterDirective baseAddr (RegAcc s word) =
RegAcc s <$> convertMimaWord baseAddr word
convertRegisterDirective baseAddr (RegRa s loc) =
RegRa s <$> convertLocation baseAddr loc
convertRegisterDirective baseAddr (RegSp s loc) =
RegSp s <$> convertLocation baseAddr loc
convertRegisterDirective baseAddr (RegFp s loc) =
RegFp s <$> convertLocation baseAddr loc
convertP2Token :: AsmToken 'S1 s -> WeedS2 s (Maybe (AsmToken 'S2 s))
convertP2Token (TokenOrg _ (OrgAddrAbsolute s address))
= Nothing <$ setAddress s address
convertP2Token (TokenOrg _ (OrgAddrRelative s address))
| address < 0 = Nothing <$ setAddress s (maxBound + fromIntegral address)
| otherwise = Nothing <$ addAddress s (fromIntegral address)
convertP2Token (TokenLabel s _ name) = do
address <- s2CurrentAddress <$> get
pure $ Just $ TokenLabel s address name
convertP2Token (TokenMeta s _ meta) = do
address <- s2CurrentAddress <$> get
pure $ Just $ TokenMeta s address meta
convertP2Token (TokenLit s _ word) = do
address <- nextAddress s
newWord <- convertMimaWord address word
pure $ Just $ TokenLit s address newWord
convertP2Token (TokenInstr s _ instr) = do
address <- nextAddress s
Just . TokenInstr s address <$> convertInstruction address instr
convertP2Token (TokenReg s _ reg) = do
address <- s2CurrentAddress <$> get
Just . TokenReg s address <$> convertRegisterDirective address reg
subphase2 :: Phase2 'S1 s -> Weed (WeedError s) (Phase2 'S2 s)
subphase2 s1 = do
let initialS = StateS2 0 False
catMaybes <$> evalStateT (traverse convertP2Token s1) initialS

View file

@ -0,0 +1,237 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
module Mima.Asm.Phase2.Types
( Phase2
, Subphase(..)
-- * Utility types
, Name(..)
, AddressX
-- * Locations
, Location1(..)
, Location2(..)
, LocationX
-- * Tokens
, AsmToken(..)
-- ** Org token
, OrgAddress(..)
, TokenOrgX
-- ** Label token
, TokenLabelX
-- ** Meta token
, JsonValue(..)
, Meta(..)
, TokenMetaX
-- ** Instruction token
, MimaWord(..)
, Instruction(..)
, TokenInstrX
-- ** Register token
, RegisterDirective(..)
, TokenRegX
) where
import qualified Data.Aeson as A
import qualified Data.Text as T
import Data.Void
import Mima.Asm.Types
import qualified Mima.Vm.Instruction as Vm
import qualified Mima.Vm.Word as Vm
data Subphase
= S1
-- ^ Freshly converted from 'Phase1'. Arrays are converted into multiple
-- literal values. Comments are removed.
| S2
-- ^ After resolving all .org-s and relative positions and assigning each
-- token an address.
| S3
-- ^ After extracting and removing all labels and .meta-s. This step results
-- in a map to resolve labels and a complete set of .meta-* metadata.
| S4
-- ^ After resolving all labels. Instructions are converted into literal
-- values.
| S5
-- ^ After extracting all initial register values.
-- | The name of a label or a meta tag.
data Name s = Name s T.Text
deriving (Show, Functor)
instance Onion Name where
peel (Name s _) = s
-- | A location defined by an absolute or relative address or by a label.
data Location1 s
= Loc1Absolute s Vm.MimaAddress
| Loc1Relative s Integer
| Loc1Label (Name s)
| Loc1LabelRel s (Name s) s Integer
deriving (Show, Functor)
instance Onion Location1 where
peel (Loc1Absolute s _) = s
peel (Loc1Relative s _) = s
peel (Loc1Label l) = peel l
peel (Loc1LabelRel s _ _ _) = s
-- | A location defined by an absolute address or by a label.
data Location2 s
= Loc2Absolute s Vm.MimaAddress
| Loc2Label (Name s)
| Loc2LabelRel s (Name s) s Integer
deriving (Show, Functor)
instance Onion Location2 where
peel (Loc2Absolute s _) = s
peel (Loc2Label l) = peel l
peel (Loc2LabelRel s _ _ _) = s
-- | A type family for locations in various stages of resolution.
type family LocationX (t :: Subphase) (s :: *)
type instance LocationX 'S1 s = Location1 s
type instance LocationX 'S2 s = Location2 s
type instance LocationX 'S3 s = Location2 s
type instance LocationX 'S4 s = Vm.MimaAddress
type instance LocationX 'S5 s = Vm.MimaAddress
-- | A type family for addresses of various tokens.
type family AddressX (t :: Subphase) (s :: *)
type instance AddressX 'S1 s = ()
type instance AddressX 'S2 s = Vm.MimaAddress
type instance AddressX 'S3 s = Vm.MimaAddress
type instance AddressX 'S4 s = Vm.MimaAddress
type instance AddressX 'S5 s = Vm.MimaAddress
-- | A representation for .org addresses.
data OrgAddress s
= OrgAddrAbsolute s Vm.MimaAddress
| OrgAddrRelative s Integer
deriving (Show, Functor)
instance Onion OrgAddress where
peel (OrgAddrAbsolute s _) = s
peel (OrgAddrRelative s _) = s
type family TokenOrgX (t :: Subphase) (s :: *)
type instance TokenOrgX 'S1 s = OrgAddress s
type instance TokenOrgX 'S2 s = Void
type instance TokenOrgX 'S3 s = Void
type instance TokenOrgX 'S4 s = Void
type instance TokenOrgX 'S5 s = Void
type family TokenLabelX (t :: Subphase) (s :: *)
type instance TokenLabelX 'S1 s = Name s
type instance TokenLabelX 'S2 s = Name s
type instance TokenLabelX 'S3 s = Void
type instance TokenLabelX 'S4 s = Void
type instance TokenLabelX 'S5 s = Void
-- | A wrapper that annotates a 'A.Value' with an @s@ value.
data JsonValue s = JsonValue s A.Value
deriving (Show, Functor)
instance Onion JsonValue where
peel (JsonValue s _) = s
-- | A representation for .meta-start and .meta-stop directives.
data Meta s
= MetaStart s (Name s) (JsonValue s)
| MetaStop s (Name s)
deriving (Show, Functor)
instance Onion Meta where
peel (MetaStart s _ _) = s
peel (MetaStop s _) = s
type family TokenMetaX (t :: Subphase) (s :: *)
type instance TokenMetaX 'S1 s = Meta s
type instance TokenMetaX 'S2 s = Meta s
type instance TokenMetaX 'S3 s = Void
type instance TokenMetaX 'S4 s = Void
type instance TokenMetaX 'S5 s = Void
-- | A stripped-down representation of Mima words that does not have an 'Onion'
-- instance because none is required.
data MimaWord (t :: Subphase) (s :: *)
= WordRaw Vm.MimaWord
| WordLocation (LocationX t s)
deriving instance Show s => Show (MimaWord 'S1 s)
deriving instance Show s => Show (MimaWord 'S2 s)
deriving instance Show s => Show (MimaWord 'S3 s)
deriving instance Show s => Show (MimaWord 'S4 s)
deriving instance Show s => Show (MimaWord 'S5 s)
-- | A stripped-down representation of Mima instructions that does not have an
-- 'Onion' instance because none is required.
data Instruction (t :: Subphase) (s :: *)
= SmallInstruction Vm.SmallOpcode (LocationX t s)
| LargeInstruction Vm.LargeOpcode (Maybe Vm.SmallValue)
deriving instance Show s => Show (Instruction 'S1 s)
deriving instance Show s => Show (Instruction 'S2 s)
deriving instance Show s => Show (Instruction 'S3 s)
deriving instance Show s => Show (Instruction 'S4 s)
deriving instance Show s => Show (Instruction 'S5 s)
type family TokenInstrX (t :: Subphase) (s :: *)
type instance TokenInstrX 'S1 s = Instruction 'S1 s
type instance TokenInstrX 'S2 s = Instruction 'S2 s
type instance TokenInstrX 'S3 s = Instruction 'S3 s
type instance TokenInstrX 'S4 s = Void
type instance TokenInstrX 'S5 s = Void
data RegisterDirective (t :: Subphase) (s :: *)
= RegIar s (LocationX t s)
| RegAcc s (MimaWord t s)
| RegRa s (LocationX t s)
| RegSp s (LocationX t s)
| RegFp s (LocationX t s)
deriving instance Show s => Show (RegisterDirective 'S1 s)
deriving instance Show s => Show (RegisterDirective 'S2 s)
deriving instance Show s => Show (RegisterDirective 'S3 s)
deriving instance Show s => Show (RegisterDirective 'S4 s)
deriving instance Show s => Show (RegisterDirective 'S5 s)
instance Onion (RegisterDirective t) where
peel (RegIar s _) = s
peel (RegAcc s _) = s
peel (RegRa s _) = s
peel (RegSp s _) = s
peel (RegFp s _) = s
type family TokenRegX (t :: Subphase) (s :: *)
type instance TokenRegX 'S1 s = RegisterDirective 'S1 s
type instance TokenRegX 'S2 s = RegisterDirective 'S2 s
type instance TokenRegX 'S3 s = RegisterDirective 'S3 s
type instance TokenRegX 'S4 s = RegisterDirective 'S4 s
type instance TokenRegX 'S5 s = Void
data AsmToken (t :: Subphase) (s :: *)
= TokenOrg s (TokenOrgX t s)
| TokenLabel s (AddressX t s) (TokenLabelX t s)
| TokenMeta s (AddressX t s) (TokenMetaX t s)
| TokenLit s (AddressX t s) (MimaWord t s)
| TokenInstr s (AddressX t s) (TokenInstrX t s)
| TokenReg s (AddressX t s) (TokenRegX t s)
deriving instance Show s => Show (AsmToken 'S1 s)
deriving instance Show s => Show (AsmToken 'S2 s)
deriving instance Show s => Show (AsmToken 'S3 s)
deriving instance Show s => Show (AsmToken 'S4 s)
deriving instance Show s => Show (AsmToken 'S5 s)
instance Onion (AsmToken t) where
peel (TokenOrg s _) = s
peel (TokenLabel s _ _) = s
peel (TokenMeta s _ _) = s
peel (TokenLit s _ _) = s
peel (TokenInstr s _ _) = s
peel (TokenReg s _ _) = s
type Phase2 t s = [AsmToken t s]

View file

@ -0,0 +1,18 @@
{-# LANGUAGE ScopedTypeVariables #-}
module Mima.Asm.Phase2.Util
( intToBounded
) where
import Control.Monad
import Mima.Asm.Weed
intToBounded :: forall s n. (Bounded n, Integral n) => s -> Integer -> Weed (WeedError s) n
intToBounded s val = do
when (val < minVal || val > maxVal) $
harmless $ errorWith s "value out of bounds"
pure $ fromInteger val
where
maxVal = toInteger (maxBound :: n)
minVal = -maxVal - 1