module Compiler.ASM.CompilerMonad
(CompilerMonad
, emitInstruction
, emitLabelDefinition
, emitJumpToLabel
, emitJumpIfFalseToLabel
, emitJumpIfTrueToLabel
, emitCallToLabel
, generateUniqueLabel
, defineSymbol
, registerSymbol
, defineStruct
, getStructDefinition
, getSymbolType
, lookupSymbol
, getStructField
, compileInIsolatedFunctionScope
, appendPseudoInstruction
) where
import Control.Monad.State
import Data.Text (Text, pack)
import Data.Sequence ((|>), (><))
import qualified Data.Map.Strict as Map
import qualified Data.Sequence as Seq
import Compiler.CompilerState (CompilerState(..), ScopeType(..))
import Compiler.PsInstruction (PsInstruction(..))
import Compiler.Instruction (Instruction)
type CompilerMonad a = StateT CompilerState (Either Text) a
appendPseudoInstruction :: PsInstruction -> CompilerMonad ()
appendPseudoInstruction :: PsInstruction -> CompilerMonad ()
appendPseudoInstruction PsInstruction
pseudoInst = (CompilerState -> CompilerState) -> CompilerMonad ()
forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify ((CompilerState -> CompilerState) -> CompilerMonad ())
-> (CompilerState -> CompilerState) -> CompilerMonad ()
forall a b. (a -> b) -> a -> b
$ \CompilerState
currentState ->
CompilerState
currentState { csCode = csCode currentState |> pseudoInst }
emitInstruction :: Instruction -> CompilerMonad ()
emitInstruction :: Instruction -> CompilerMonad ()
emitInstruction Instruction
instruction = PsInstruction -> CompilerMonad ()
appendPseudoInstruction (Instruction -> PsInstruction
Real Instruction
instruction)
emitLabelDefinition :: Text -> CompilerMonad ()
emitLabelDefinition :: Text -> CompilerMonad ()
emitLabelDefinition Text
labelName = PsInstruction -> CompilerMonad ()
appendPseudoInstruction (Text -> PsInstruction
LabelDef Text
labelName)
emitJumpToLabel :: Text -> CompilerMonad ()
emitJumpToLabel :: Text -> CompilerMonad ()
emitJumpToLabel Text
labelName = PsInstruction -> CompilerMonad ()
appendPseudoInstruction (Text -> PsInstruction
JumpLabel Text
labelName)
emitJumpIfFalseToLabel :: Text -> CompilerMonad ()
emitJumpIfFalseToLabel :: Text -> CompilerMonad ()
emitJumpIfFalseToLabel Text
labelName = PsInstruction -> CompilerMonad ()
appendPseudoInstruction (
Text -> PsInstruction
JumpIfFalseLabel Text
labelName)
emitJumpIfTrueToLabel :: Text -> CompilerMonad ()
emitJumpIfTrueToLabel :: Text -> CompilerMonad ()
emitJumpIfTrueToLabel Text
labelName = PsInstruction -> CompilerMonad ()
appendPseudoInstruction (
Text -> PsInstruction
JumpIfTrueLabel Text
labelName)
emitCallToLabel :: Text -> CompilerMonad ()
emitCallToLabel :: Text -> CompilerMonad ()
emitCallToLabel Text
labelName = PsInstruction -> CompilerMonad ()
appendPseudoInstruction (Text -> PsInstruction
CallLabel Text
labelName)
generateUniqueLabel :: Text -> CompilerMonad Text
generateUniqueLabel :: Text -> CompilerMonad Text
generateUniqueLabel Text
prefixName = do
currentState <- StateT CompilerState (Either Text) CompilerState
forall s (m :: * -> *). MonadState s m => m s
get
put $ currentState { csLabelCnt = (csLabelCnt currentState) + 1 }
return $ prefixName <> pack "_" <> pack (show (csLabelCnt currentState))
defineSymbol :: Text -> Text -> CompilerMonad (ScopeType, Int)
defineSymbol :: Text -> Text -> CompilerMonad (ScopeType, Int)
defineSymbol Text
name Text
tName = do
s <- StateT CompilerState (Either Text) CompilerState
forall s (m :: * -> *). MonadState s m => m s
get
case Map.lookup name (csSymbols s) of
Just (ScopeType
scope, Int
idx, Text
_) -> (ScopeType, Int) -> CompilerMonad (ScopeType, Int)
forall a. a -> StateT CompilerState (Either Text) a
forall (m :: * -> *) a. Monad m => a -> m a
return (ScopeType
scope, Int
idx)
Maybe (ScopeType, Int, Text)
Nothing -> let idx :: Int
idx = CompilerState -> Int
csNextIndex CompilerState
s
scope :: ScopeType
scope = CompilerState -> ScopeType
csScopeContext CompilerState
s in
CompilerState -> CompilerMonad ()
forall s (m :: * -> *). MonadState s m => s -> m ()
put CompilerState
s {
csSymbols = Map.insert name (scope, idx, tName) (csSymbols s),
csNextIndex = idx + 1
} CompilerMonad ()
-> CompilerMonad (ScopeType, Int) -> CompilerMonad (ScopeType, Int)
forall a b.
StateT CompilerState (Either Text) a
-> StateT CompilerState (Either Text) b
-> StateT CompilerState (Either Text) b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> (ScopeType, Int) -> CompilerMonad (ScopeType, Int)
forall a. a -> StateT CompilerState (Either Text) a
forall (m :: * -> *) a. Monad m => a -> m a
return (ScopeType
scope, Int
idx)
registerSymbol :: Text -> Text -> ScopeType -> Int -> CompilerMonad ()
registerSymbol :: Text -> Text -> ScopeType -> Int -> CompilerMonad ()
registerSymbol Text
name Text
tName ScopeType
scopeType Int
idx = do
s <- StateT CompilerState (Either Text) CompilerState
forall s (m :: * -> *). MonadState s m => m s
get
let newSymbols = Text
-> (ScopeType, Int, Text)
-> Map Text (ScopeType, Int, Text)
-> Map Text (ScopeType, Int, Text)
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Text
name (ScopeType
scopeType, Int
idx, Text
tName) (CompilerState -> Map Text (ScopeType, Int, Text)
csSymbols CompilerState
s)
case scopeType of
ScopeType
ScopeCapture -> CompilerState -> CompilerMonad ()
forall s (m :: * -> *). MonadState s m => s -> m ()
put (CompilerState -> CompilerMonad ())
-> CompilerState -> CompilerMonad ()
forall a b. (a -> b) -> a -> b
$ CompilerState
s { csSymbols = newSymbols }
ScopeType
_ -> CompilerState -> CompilerMonad ()
forall s (m :: * -> *). MonadState s m => s -> m ()
put (CompilerState -> CompilerMonad ())
-> CompilerState -> CompilerMonad ()
forall a b. (a -> b) -> a -> b
$ CompilerState
s {
csSymbols = newSymbols,
csNextIndex = max (csNextIndex s) (idx + 1)
}
defineStruct :: Text -> [(Text, Text)] -> CompilerMonad ()
defineStruct :: Text -> [(Text, Text)] -> CompilerMonad ()
defineStruct Text
name [(Text, Text)]
fields = do
s <- StateT CompilerState (Either Text) CompilerState
forall s (m :: * -> *). MonadState s m => m s
get
put $ s { csStructs = Map.insert name fields (csStructs s) }
getStructDefinition :: Text -> CompilerMonad [(Text, Text)]
getStructDefinition :: Text -> CompilerMonad [(Text, Text)]
getStructDefinition Text
name = do
s <- StateT CompilerState (Either Text) CompilerState
forall s (m :: * -> *). MonadState s m => m s
get
case Map.lookup name (csStructs s) of
Just [(Text, Text)]
fields -> [(Text, Text)] -> CompilerMonad [(Text, Text)]
forall a. a -> StateT CompilerState (Either Text) a
forall (m :: * -> *) a. Monad m => a -> m a
return [(Text, Text)]
fields
Maybe [(Text, Text)]
Nothing -> Either Text [(Text, Text)] -> CompilerMonad [(Text, Text)]
forall (m :: * -> *) a. Monad m => m a -> StateT CompilerState m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (Either Text [(Text, Text)] -> CompilerMonad [(Text, Text)])
-> Either Text [(Text, Text)] -> CompilerMonad [(Text, Text)]
forall a b. (a -> b) -> a -> b
$ Text -> Either Text [(Text, Text)]
forall a b. a -> Either a b
Left (String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String
"Undefined struct: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Text -> String
forall a. Show a => a -> String
show Text
name)
lookupSymbol :: Text -> CompilerMonad (Maybe (ScopeType, Int, Text))
lookupSymbol :: Text -> CompilerMonad (Maybe (ScopeType, Int, Text))
lookupSymbol Text
name = do
s <- StateT CompilerState (Either Text) CompilerState
forall s (m :: * -> *). MonadState s m => m s
get
return $ Map.lookup name (csSymbols s)
getSymbolType :: Text -> CompilerMonad Text
getSymbolType :: Text -> CompilerMonad Text
getSymbolType Text
name = do
result <- Text -> CompilerMonad (Maybe (ScopeType, Int, Text))
lookupSymbol Text
name
case result of
Just (ScopeType
_, Int
_, Text
t) -> Text -> CompilerMonad Text
forall a. a -> StateT CompilerState (Either Text) a
forall (m :: * -> *) a. Monad m => a -> m a
return Text
t
Maybe (ScopeType, Int, Text)
Nothing -> Either Text Text -> CompilerMonad Text
forall (m :: * -> *) a. Monad m => m a -> StateT CompilerState m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (Either Text Text -> CompilerMonad Text)
-> Either Text Text -> CompilerMonad Text
forall a b. (a -> b) -> a -> b
$ Text -> Either Text Text
forall a b. a -> Either a b
Left (String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String
"Undefined symbol: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Text -> String
forall a. Show a => a -> String
show Text
name)
findFieldIndex :: Int -> Text -> [(Text, Text)] -> Maybe (Int, Text)
findFieldIndex :: Int -> Text -> [(Text, Text)] -> Maybe (Int, Text)
findFieldIndex Int
_ Text
_ [] = Maybe (Int, Text)
forall a. Maybe a
Nothing
findFieldIndex Int
idx Text
target ((Text
fName, Text
fType):[(Text, Text)]
rest)
| Text
fName Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
target = (Int, Text) -> Maybe (Int, Text)
forall a. a -> Maybe a
Just (Int
idx, Text
fType)
| Bool
otherwise = Int -> Text -> [(Text, Text)] -> Maybe (Int, Text)
findFieldIndex (Int
idx Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Text
target [(Text, Text)]
rest
getStructField :: Text -> Text -> CompilerMonad (Int, Text)
getStructField :: Text -> Text -> CompilerMonad (Int, Text)
getStructField Text
name Text
fieldName = do
s <- StateT CompilerState (Either Text) CompilerState
forall s (m :: * -> *). MonadState s m => m s
get
case Map.lookup name (csStructs s) of
Maybe [(Text, Text)]
Nothing -> Either Text (Int, Text) -> CompilerMonad (Int, Text)
forall (m :: * -> *) a. Monad m => m a -> StateT CompilerState m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (Either Text (Int, Text) -> CompilerMonad (Int, Text))
-> Either Text (Int, Text) -> CompilerMonad (Int, Text)
forall a b. (a -> b) -> a -> b
$ Text -> Either Text (Int, Text)
forall a b. a -> Either a b
Left (String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String
"Unknown struct: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ Text -> String
forall a. Show a => a -> String
show Text
name)
Just [(Text, Text)]
fields -> case Int -> Text -> [(Text, Text)] -> Maybe (Int, Text)
findFieldIndex Int
0 Text
fieldName [(Text, Text)]
fields of
Just (Int
idx, Text
fieldType) -> (Int, Text) -> CompilerMonad (Int, Text)
forall a. a -> StateT CompilerState (Either Text) a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int
idx, Text
fieldType)
Maybe (Int, Text)
Nothing -> Either Text (Int, Text) -> CompilerMonad (Int, Text)
forall (m :: * -> *) a. Monad m => m a -> StateT CompilerState m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (Either Text (Int, Text) -> CompilerMonad (Int, Text))
-> Either Text (Int, Text) -> CompilerMonad (Int, Text)
forall a b. (a -> b) -> a -> b
$ Text -> Either Text (Int, Text)
forall a b. a -> Either a b
Left (String -> Text
pack (String -> Text) -> String -> Text
forall a b. (a -> b) -> a -> b
$ String
"Field '" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Text -> String
forall a. Show a => a -> String
show Text
fieldName String -> String -> String
forall a. [a] -> [a] -> [a]
++
String
"' not found in struct '" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Text -> String
forall a. Show a => a -> String
show Text
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"'")
compileInIsolatedFunctionScope :: CompilerMonad () -> CompilerMonad ()
compileInIsolatedFunctionScope :: CompilerMonad () -> CompilerMonad ()
compileInIsolatedFunctionScope CompilerMonad ()
compileAction = do
outerState <- StateT CompilerState (Either Text) CompilerState
forall s (m :: * -> *). MonadState s m => m s
get
put $ outerState
{ csCode = Seq.empty, csSymbols = Map.empty, csNextIndex = 0,
csScopeContext = ScopeLocal, csFuncs = Seq.empty }
compileAction
innerState <- get
put $ outerState
{ csFuncs = (csFuncs outerState) >< (csCode innerState) ><
(csFuncs innerState), csLabelCnt = csLabelCnt innerState }