diff options
Diffstat (limited to 'src/Language/Fiddle/Compiler/Expansion.hs')
-rw-r--r-- | src/Language/Fiddle/Compiler/Expansion.hs | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/src/Language/Fiddle/Compiler/Expansion.hs b/src/Language/Fiddle/Compiler/Expansion.hs new file mode 100644 index 0000000..8cfd0f0 --- /dev/null +++ b/src/Language/Fiddle/Compiler/Expansion.hs @@ -0,0 +1,230 @@ +{-# LANGUAGE ConstraintKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE IncoherentInstances #-} +{-# LANGUAGE UndecidableInstances #-} + +module Language.Fiddle.Compiler.Expansion (expandAst) where + +import Control.Monad.Identity (Identity (..)) +import Control.Monad.State (get, gets, modify, put) +import qualified Data.Char as Char +import Data.List (intercalate) +import qualified Data.List.NonEmpty as NonEmpty +import Data.Text (Text) +import qualified Data.Text as Text +import Data.Type.Bool +import Debug.Trace +import GHC.TypeLits +import Language.Fiddle.Ast +import Language.Fiddle.Compiler +import Language.Fiddle.Types +import Text.Printf (printf) + +type Annot = Commented SourceSpan + +newtype Path = Path [PathExpression] + +newtype PathExpression = PathExpression String + +type M = Compile State + +joinPath :: Path -> String +joinPath (Path l) = intercalate "#" $ reverse (map (\(PathExpression s) -> s) l) + +expandAst :: FiddleUnit Parsed I Annot -> Compile () (FiddleUnit Expanded I Annot) +expandAst = fmap snd . subCompile (State [] []) . advanceStage (Path mempty) + +-- Shorthand for Identity +type I = Identity + +newtype Linkage = Linkage Text deriving (Show) + +data State + = State + -- Anonymous object type bodies that need to be re-linked + ![(Linkage, ObjTypeBody Expanded I Annot)] + -- Anonymous enum bodies that need to be re-linked + ![(Linkage, AnonymousBitsType Expanded I Annot)] + +instance CompilationStage Parsed where + type StageAfter Parsed = Expanded + type StageMonad Parsed = M + type StageState Parsed = Path + type StageFunctor Parsed = Identity + type StageAnnotation Parsed = Annot + +deriving instance AdvanceStage Parsed ObjTypeBody + +deriving instance AdvanceStage Parsed DeferredRegisterBody + +deriving instance AdvanceStage Parsed RegisterBody + +deriving instance AdvanceStage Parsed AnonymousBitsType + +deriving instance AdvanceStage Parsed BitType + +deriving instance AdvanceStage Parsed EnumBody + +deriving instance AdvanceStage Parsed EnumConstantDecl + +deriving instance (AdvanceStage Parsed t) => AdvanceStage Parsed (Directed t) + +instance AdvanceStage Parsed RegisterBitsDecl where + modifyState t = + return + . case t of + DefinedBits {definedBitsIdent = i} -> pushId i + _ -> id + +instance AdvanceStage Parsed PackageBody where + advanceStage p (PackageBody decls a) = + PackageBody <$> reconfigureFiddleDecls p decls <*> pure a + +instance AdvanceStage Parsed ObjTypeDecl where + modifyState t = + return + . case t of + TypeSubStructure {subStructureName = (Just n)} -> pushId n + RegisterDecl {regIdent = (Just n)} -> pushId n + _ -> id + +instance AdvanceStage Parsed FiddleDecl where + modifyState t = + return + . case t of + PackageDecl {packageName = n} -> pushName n + BitsDecl {bitsIdent = i} -> pushId i + ObjTypeDecl {objTypeIdent = i} -> pushId i + ObjectDecl {objectIdent = i} -> pushId i + _ -> id + +instance AdvanceStage Parsed FiddleUnit where + advanceStage path (FiddleUnit decls a) = + FiddleUnit <$> reconfigureFiddleDecls path decls <*> pure a + +instance AdvanceStage Parsed Expression where + advanceStage _ = \case + (Var i a) -> return $ Var i a + (LitNum t a) -> LitNum <$> parseNum (unCommented a) t <*> pure a + +instance AdvanceStage Parsed RegisterBitsTypeRef where + advanceStage path = \case + RegisterBitsArray typeref expr annot -> + RegisterBitsArray + <$> advanceStage path typeref + <*> advanceStage path expr + <*> pure annot + RegisterBitsReference name annot -> + return $ RegisterBitsReference name annot + RegisterBitsJustBits expr annot -> + RegisterBitsJustBits + <$> advanceStage path expr + <*> pure annot + RegisterBitsAnonymousType _ anonType annot -> do + ident <- + internAnonymousBitsType path + =<< advanceStage path anonType + return $ RegisterBitsReference (identToName ident) annot + +instance AdvanceStage Parsed ObjType where + advanceStage path = \case + (AnonymousObjType _ (Identity body) annot) -> do + body' <- advanceStage path body + identifier <- internObjType path body' + return (ReferencedObjType (identToName identifier) annot) + (ReferencedObjType name annot) -> + return $ ReferencedObjType name annot + (ArrayObjType objType expr a) -> + ArrayObjType + <$> advanceStage path objType + <*> advanceStage path expr + <*> pure a + +parseNum :: SourceSpan -> Text -> Compile s Integer +parseNum span txt = fromMayberOrFail span "Unable to parse number" $ + case Text.unpack (Text.take 2 txt) of + "0b" -> toNumWithRadix (Text.drop 2 txt) 2 + "0x" -> toNumWithRadix (Text.drop 2 txt) 16 + ('0' : _) -> toNumWithRadix (Text.tail txt) 8 + _ -> toNumWithRadix txt 10 + where + removeUnders :: Text -> Text + removeUnders = Text.replace (Text.pack "_") Text.empty + + toNumWithRadix :: Text -> Int -> Maybe Integer + toNumWithRadix (removeUnders -> txt) (fromIntegral -> radix) = + Text.foldl + ( \mAcc x -> + mAcc >>= (\acc -> (acc * radix +) <$> digitToInt x radix) + ) + (Just 0) + txt + + digitToInt :: Char -> Integer -> Maybe Integer + digitToInt (Char.toLower -> ch) radix = + let a + | Char.isDigit ch = Just (Char.ord ch - Char.ord '0') + | ch >= 'a' && ch <= 'f' = Just $ (Char.ord ch - Char.ord 'a') + 10 + | otherwise = Nothing + in a + >>= ( \a' -> + if a' >= fromIntegral radix + then Nothing + else Just (fromIntegral a') + ) + +reconfigureFiddleDecls :: + Path -> + [Directed FiddleDecl Parsed I Annot] -> + M [Directed FiddleDecl Expanded I Annot] +reconfigureFiddleDecls p decls = do + lastState <- get + put (State [] []) + decls <- mapM (mapDirectedM $ advanceStage p) decls + (State anonymousObjTypes anonymousBitsTypes) <- get + put lastState + + return $ + map (asDirected . resolveAnonymousObjType) anonymousObjTypes + ++ map (asDirected . resolveAnonymousBitsType) anonymousBitsTypes + ++ decls + where + resolveAnonymousObjType (Linkage linkage, objTypeBody) = + ObjTypeDecl + (Identifier linkage (annot objTypeBody)) + (pure objTypeBody) + (annot objTypeBody) + + resolveAnonymousBitsType (Linkage linkage, AnonymousEnumBody expr body a) = + BitsDecl (Identifier linkage a) (EnumBitType expr body a) a + +identToName :: Identifier I a -> Name I a +identToName ident = Name (NonEmpty.singleton ident) (annot ident) + +internObjType :: Path -> ObjTypeBody Expanded I Annot -> M (Identifier I Annot) +internObjType path body = + let str = Text.pack $ joinPath path + in do + modify $ \(State objTypeBodies a) -> + State ((Linkage str, body) : objTypeBodies) a + return (Identifier str (annot body)) + +internAnonymousBitsType :: + Path -> + AnonymousBitsType Expanded I Annot -> + M (Identifier I Annot) +internAnonymousBitsType path anonymousBitsType = + let str = Text.pack $ joinPath path + in do + modify $ \(State a anonymousBitsTypes) -> + State a ((Linkage str, anonymousBitsType) : anonymousBitsTypes) + return (Identifier str (annot anonymousBitsType)) + +pushId :: Identifier f a -> Path -> Path +pushId (Identifier str _) (Path lst) = + Path (PathExpression (Text.unpack str) : lst) + +pushName :: Name f a -> Path -> Path +pushName (Name idents _) path = + foldl (flip pushId) path idents |