summaryrefslogtreecommitdiff
path: root/src/Language/Fiddle/Compiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/Language/Fiddle/Compiler')
-rw-r--r--src/Language/Fiddle/Compiler/Stage1.hs355
-rw-r--r--src/Language/Fiddle/Compiler/Stage2.hs644
2 files changed, 477 insertions, 522 deletions
diff --git a/src/Language/Fiddle/Compiler/Stage1.hs b/src/Language/Fiddle/Compiler/Stage1.hs
index d2fe885..aae80e4 100644
--- a/src/Language/Fiddle/Compiler/Stage1.hs
+++ b/src/Language/Fiddle/Compiler/Stage1.hs
@@ -1,6 +1,7 @@
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE IncoherentInstances #-}
-{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Language.Fiddle.Compiler.Stage1 (toStage2) where
@@ -14,216 +15,133 @@ import Data.Text (Text)
import qualified Data.Text as Text
import Data.Type.Bool
import Debug.Trace
-import GHC.Generics
import GHC.TypeLits
import Language.Fiddle.Ast
import Language.Fiddle.Compiler
import Language.Fiddle.Types
import Text.Printf (printf)
-newtype Linkage = Linkage Text deriving (Show)
+type Annot = Commented SourceSpan
newtype Path = Path [PathExpression]
newtype PathExpression = PathExpression String
-type Annot = Commented SourceSpan
+type M = Compile State
joinPath :: Path -> String
joinPath (Path l) = intercalate "#" $ reverse (map (\(PathExpression s) -> s) l)
+toStage2 :: FiddleUnit Stage1 I Annot -> Compile () (FiddleUnit Stage2 I Annot)
+toStage2 = fmap snd . subCompile (State [] []) . advanceStage (Path mempty)
+
-- Shorthand for Identity
type I = Identity
-data Stage2CompilerState a
- = Stage2CompilerState
+newtype Linkage = Linkage Text deriving (Show)
+
+data State
+ = State
-- Anonymous object type bodies that need to be re-linked
- ![(Linkage, ObjTypeBody Stage2 I a)]
+ ![(Linkage, ObjTypeBody Stage2 I Annot)]
-- Anonymous enum bodies that need to be re-linked
- ![(Linkage, AnonymousBitsType Stage2 I a)]
-
-type M a = Compile (Stage2CompilerState a)
-
-internObjType :: Path -> ObjTypeBody Stage2 I a -> M a (Identifier I a)
-internObjType path body =
- let str = Text.pack $ joinPath path
- in do
- modify $ \(Stage2CompilerState objTypeBodies a) ->
- Stage2CompilerState ((Linkage str, body) : objTypeBodies) a
- return (Identifier str (annot body))
-
-internAnonymousBitsType :: Path -> AnonymousBitsType Stage2 I a -> M a (Identifier I a)
-internAnonymousBitsType path anonymousBitsType =
- let str = Text.pack $ joinPath path
- in do
- modify $ \(Stage2CompilerState a anonymousBitsTypes) ->
- Stage2CompilerState a ((Linkage str, anonymousBitsType) : anonymousBitsTypes)
- return (Identifier str (annot anonymousBitsType))
-
--- The second stage is a simplified version of the AST without anonymous
--- declarations.
-toStage2 :: FiddleUnit Stage1 I Annot -> Compile () (FiddleUnit Stage2 I Annot)
-toStage2 (FiddleUnit decls annot) = do
- (s, a) <-
- subCompile (Stage2CompilerState [] []) $
- FiddleUnit <$> reconfigureFiddleDecls (Path []) decls <*> pure annot
- return a
-
-reconfigureFiddleDecls :: Path -> [Directed FiddleDecl Stage1 I Annot] -> M Annot [Directed FiddleDecl Stage2 I Annot]
-reconfigureFiddleDecls p decls = do
- -- Stage2CompilerState anonymousObjTypes anonymousBitsTypes, decls <- pushState $ do
- -- put (Stage2CompilerState [] [])
- -- gets (,) <*> mapM (fiddleDeclToStage2 p) decls
-
- lastState <- get
- put (Stage2CompilerState [] [])
- decls <- mapM (mapDirectedM $ fiddleDeclToStage2 p) decls
- (Stage2CompilerState 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
-
-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
-
-fiddleDeclToStage2 :: Path -> FiddleDecl Stage1 I Annot -> M Annot (FiddleDecl Stage2 I Annot)
-fiddleDeclToStage2 path decl = do
- case decl of
- (OptionDecl i1 i2 a) -> return $ OptionDecl i1 i2 a
- (PackageDecl n (Identity body) a) -> do
- (PackageDecl n . Identity <$> packageBodyToStage2 (pushName n path) body) <*> pure a
- (UsingDecl n a) -> return $ UsingDecl n a
- (LocationDecl i expr a) -> LocationDecl i <$> toStage2Expr expr <*> pure a
- (BitsDecl i typ a) -> BitsDecl i <$> bitsTypeToStage2 (pushId i path) typ <*> pure a
- (ObjTypeDecl i body a) -> ObjTypeDecl i <$> mapM (objTypeBodyToStage2 (pushId i path)) body <*> pure a
- (ImportDecl importStatement a) -> return $ ImportDecl importStatement a
- (ObjectDecl i expr typ a) ->
- ObjectDecl i <$> toStage2Expr expr <*> objectTypeToStage2 (pushId i path) typ <*> pure a
-
-bitsTypeToStage2 :: Path -> BitType Stage1 I Annot -> M Annot (BitType Stage2 I Annot)
-bitsTypeToStage2 path = \case
- RawBits expr a -> RawBits <$> toStage2Expr expr <*> pure a
- EnumBitType expr enumBody a ->
- EnumBitType <$> toStage2Expr expr <*> mapM (enumBodyToStage2 path) enumBody <*> pure a
-
-enumBodyToStage2 :: Path -> EnumBody Stage1 I Annot -> M Annot (EnumBody Stage2 I Annot)
-enumBodyToStage2 path = \case
- EnumBody constants a -> EnumBody <$> mapM (mapDirectedM (enumConstantToStage2 path)) constants <*> pure a
-
-enumConstantToStage2 :: Path -> EnumConstantDecl Stage1 I Annot -> M Annot (EnumConstantDecl Stage2 I Annot)
-enumConstantToStage2 path = \case
- EnumConstantDecl i e a -> EnumConstantDecl i <$> toStage2Expr e <*> pure a
- EnumConstantReserved e a -> EnumConstantReserved <$> toStage2Expr e <*> pure a
-
-objTypeBodyToStage2 :: Path -> ObjTypeBody Stage1 I Annot -> M Annot (ObjTypeBody Stage2 I Annot)
-objTypeBodyToStage2 path (ObjTypeBody bodyType decls annot) =
- ObjTypeBody bodyType <$> mapM (mapDirectedM $ objTypeDeclToStage2 path) decls <*> pure annot
-
-objTypeDeclToStage2 :: Path -> ObjTypeDecl Stage1 I Annot -> M Annot (ObjTypeDecl Stage2 I Annot)
-objTypeDeclToStage2 path = \case
- (AssertPosStatement w expr annot) ->
- AssertPosStatement w
- <$> toStage2Expr expr
- <*> pure annot
- (TypeSubStructure (Identity deferredBody) maybeIdent annot) ->
- let path' = maybe path (`pushId` path) maybeIdent
- in TypeSubStructure . Identity
- <$> objTypeBodyToStage2 path' deferredBody
- <*> pure maybeIdent
- <*> pure annot
- (ReservedDecl expr a) ->
- ReservedDecl <$> toStage2Expr expr <*> pure a
- (RegisterDecl maybeModifier maybeIdentifier expression maybeBody annot) ->
- let path' = maybe path (`pushId` path) maybeIdentifier
- in RegisterDecl
- maybeModifier
- maybeIdentifier
- <$> toStage2Expr expression
- <*> mapM (registerBodyToStage2 path') maybeBody
- <*> pure annot
-
-registerBodyToStage2 :: Path -> RegisterBody Stage1 I Annot -> M Annot (RegisterBody Stage2 I Annot)
-registerBodyToStage2 path (RegisterBody bodyType (Identity (DeferredRegisterBody registerBitsDecl a1)) a2) =
- RegisterBody bodyType . Identity
- <$> ( DeferredRegisterBody
- <$> mapM (mapDirectedM $ registerBitsDeclToStage2 path) registerBitsDecl
- <*> pure a1
- )
- <*> pure a2
-
-registerBitsDeclToStage2 :: Path -> RegisterBitsDecl Stage1 I Annot -> M Annot (RegisterBitsDecl Stage2 I Annot)
-registerBitsDeclToStage2 path = \case
- ReservedBits expr a -> ReservedBits <$> toStage2Expr expr <*> pure a
- BitsSubStructure registerBody maybeIdent annot ->
- let path' = maybe path (`pushId` path) maybeIdent
- in BitsSubStructure <$> registerBodyToStage2 path' registerBody <*> pure maybeIdent <*> pure annot
- DefinedBits maybeModifier identifier registerBitsTyperef annot ->
- let path' = pushId identifier path
- in ( DefinedBits
- maybeModifier
- identifier
- <$> registerBitsTypeRefToStage2 path' registerBitsTyperef
- <*> pure annot
- )
-
-registerBitsTypeRefToStage2 :: Path -> RegisterBitsTypeRef Stage1 I Annot -> M Annot (RegisterBitsTypeRef Stage2 I Annot)
-registerBitsTypeRefToStage2 path = \case
- RegisterBitsArray typeref expr annot ->
- RegisterBitsArray
- <$> registerBitsTypeRefToStage2 path typeref
- <*> toStage2Expr expr
- <*> pure annot
- RegisterBitsReference name annot -> return (RegisterBitsReference name annot)
- RegisterBitsJustBits expr annot -> RegisterBitsJustBits <$> toStage2Expr expr <*> pure annot
- RegisterBitsAnonymousType _ anonType annot -> do
- ident <- internAnonymousBitsType path =<< anonymousBitsTypeToStage2 path anonType
- return $ RegisterBitsReference (identToName ident) annot
-
-identToName :: Identifier I a -> Name I a
-identToName ident = Name (NonEmpty.singleton ident) (annot ident)
-
-anonymousBitsTypeToStage2 :: Path -> AnonymousBitsType Stage1 I Annot -> M Annot (AnonymousBitsType Stage2 I Annot)
-anonymousBitsTypeToStage2 path = \case
- AnonymousEnumBody expr (Identity body) annot ->
- AnonymousEnumBody
- <$> toStage2Expr expr
- <*> (Identity <$> enumBodyToStage2 path body)
- <*> pure annot
-
-objectTypeToStage2 :: Path -> ObjType Stage1 I Annot -> M Annot (ObjType Stage2 I Annot)
-objectTypeToStage2 path = \case
- (AnonymousObjType _ (Identity body) annot) -> do
- body' <- objTypeBodyToStage2 path body
- identifier <- internObjType path body'
- return (ReferencedObjType (identToName identifier) annot)
- (ReferencedObjType name annot) -> return $ ReferencedObjType name annot
- (ArrayObjType objType expr a) ->
- ArrayObjType <$> objectTypeToStage2 path objType <*> toStage2Expr expr <*> pure a
-
-packageBodyToStage2 :: Path -> PackageBody Stage1 I Annot -> M Annot (PackageBody Stage2 I Annot)
-packageBodyToStage2 p (PackageBody decls a) =
- PackageBody <$> reconfigureFiddleDecls p decls <*> pure a
-
-toStage2Expr :: Expression Stage1 I Annot -> M Annot (Expression Stage2 I Annot)
-toStage2Expr = \case
- (Var i a) -> return $ Var i a
- (LitNum t a) -> LitNum <$> parseNum (unCommented a) t <*> pure a
-
-parseNum :: SourceSpan -> Text -> M a Integer
+ ![(Linkage, AnonymousBitsType Stage2 I Annot)]
+
+instance CompilationStage Stage1 where
+ type StageAfter Stage1 = Stage2
+ type StageMonad Stage1 = M
+ type StageState Stage1 = Path
+ type StageFunctor Stage1 = Identity
+ type StageAnnotation Stage1 = Annot
+
+deriving instance AdvanceStage Stage1 ObjTypeBody
+
+deriving instance AdvanceStage Stage1 DeferredRegisterBody
+
+deriving instance AdvanceStage Stage1 RegisterBody
+
+deriving instance AdvanceStage Stage1 AnonymousBitsType
+
+deriving instance AdvanceStage Stage1 BitType
+
+deriving instance AdvanceStage Stage1 EnumBody
+
+deriving instance AdvanceStage Stage1 EnumConstantDecl
+
+deriving instance (AdvanceStage Stage1 t) => AdvanceStage Stage1 (Directed t)
+
+instance AdvanceStage Stage1 RegisterBitsDecl where
+ modifyState t =
+ return
+ . case t of
+ DefinedBits {definedBitsIdent = i} -> pushId i
+ _ -> id
+
+instance AdvanceStage Stage1 PackageBody where
+ advanceStage p (PackageBody decls a) =
+ PackageBody <$> reconfigureFiddleDecls p decls <*> pure a
+
+instance AdvanceStage Stage1 ObjTypeDecl where
+ modifyState t =
+ return
+ . case t of
+ TypeSubStructure {subStructureName = (Just n)} -> pushId n
+ RegisterDecl {regIdent = (Just n)} -> pushId n
+ _ -> id
+
+instance AdvanceStage Stage1 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 Stage1 FiddleUnit where
+ advanceStage path (FiddleUnit decls a) =
+ FiddleUnit <$> reconfigureFiddleDecls path decls <*> pure a
+
+instance AdvanceStage Stage1 Expression where
+ advanceStage _ = \case
+ (Var i a) -> return $ Var i a
+ (LitNum t a) -> LitNum <$> parseNum (unCommented a) t <*> pure a
+
+instance AdvanceStage Stage1 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 Stage1 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
@@ -255,3 +173,58 @@ parseNum span txt = fromMayberOrFail span "Unable to parse number" $
then Nothing
else Just (fromIntegral a')
)
+
+reconfigureFiddleDecls ::
+ Path ->
+ [Directed FiddleDecl Stage1 I Annot] ->
+ M [Directed FiddleDecl Stage2 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 Stage2 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 Stage2 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
diff --git a/src/Language/Fiddle/Compiler/Stage2.hs b/src/Language/Fiddle/Compiler/Stage2.hs
index 2035e3d..1363620 100644
--- a/src/Language/Fiddle/Compiler/Stage2.hs
+++ b/src/Language/Fiddle/Compiler/Stage2.hs
@@ -1,13 +1,14 @@
+{-# LANGUAGE ConstraintKinds #-}
+{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE IncoherentInstances #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
--- Stage3 doesn't change much from Stage2. Stage3 primarily removes the assert
--- statements and checks that they are consistent with the calculations.
module Language.Fiddle.Compiler.Stage2 (toStage3) where
import Control.Monad (forM, forM_, unless, when)
+import Control.Monad.Identity (Identity (Identity))
import Control.Monad.RWS (MonadState (get), MonadWriter (tell), gets, modify')
import Data.Foldable (Foldable (toList), foldlM)
import Data.Functor.Identity
@@ -21,13 +22,20 @@ import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
import qualified Data.Set as Set
import qualified Data.Text as Text
-import Data.Word
+import Data.Word (Word32)
import Language.Fiddle.Ast
import Language.Fiddle.Compiler
+import Language.Fiddle.Internal.Scopes
import Language.Fiddle.Types (Commented (unCommented), SourceSpan)
import Text.Printf (printf)
import Prelude hiding (unzip)
+newtype GlobalState = GlobalState
+ { globalScope :: Scope String (Either SizeBits SizeBytes)
+ }
+
+newtype LocalState = LocalState (ScopePath String)
+
type I = Identity
type Annot = Commented SourceSpan
@@ -36,344 +44,77 @@ type SizeBits = Word32
type SizeBytes = Word32
-data Scope t
- = Scope
- { subScopes :: Map String (Scope t),
- scopeValues :: Map String t
- }
-
-instance Semigroup (Scope t) where
- (Scope a1 b1) <> (Scope a2 b2) = Scope (a1 <> a2) (b1 <> b2)
-
-instance Monoid (Scope t) where
- mempty = Scope mempty mempty
-
-data ScopePath = ScopePath
- { currentScope :: [String],
- usingPaths :: [[String]]
- }
-
-instance Semigroup ScopePath where
- (ScopePath a1 b1) <> (ScopePath a2 b2) = ScopePath (a1 <> a2) (b1 <> b2)
-
-instance Monoid ScopePath where
- mempty = ScopePath mempty mempty
-
-emptyScope :: Scope t
-emptyScope = Scope mempty mempty
-
-insertScope :: NonEmpty String -> t -> Scope t -> Scope t
-insertScope (s :| []) v (Scope ss sv) = Scope ss (Map.insert s v sv)
-insertScope (s :| (a : as)) v (Scope ss sv) =
- Scope
- ( Map.alter
- ( \case
- (fromMaybe emptyScope -> mp) -> Just (insertScope (a :| as) v mp)
- )
- s
- ss
- )
- sv
-
-lookupScope :: NonEmpty String -> Scope t -> Maybe t
-lookupScope (s :| []) (Scope _ sv) = Map.lookup s sv
-lookupScope (s :| (a : as)) (Scope ss _) = do
- subscope <- Map.lookup s ss
- lookupScope (a :| as) subscope
-
-lookupScopeWithPath :: ScopePath -> NonEmpty String -> Scope t -> Maybe t
-lookupScopeWithPath (ScopePath current others) key scope =
- let all = reverse (inits current) ++ others
- e = forM all $ \prefix -> do
- case lookupScope (NonEmpty.prependList prefix key) scope of
- Just s -> Left s
- Nothing -> Right ()
- in case e of
- Left v -> Just v
- Right _ -> Nothing
-
-data Stage3State = Stage3State
- { inScope :: Scope (Either SizeBits SizeBytes),
- scopePath :: ScopePath
- }
-
-insertTypeSize :: Identifier f a -> SizeBits -> Compile Stage3State ()
-insertTypeSize (Identifier s _) size = do
- modify' $
- \stage3State ->
- let fullName =
- NonEmpty.prependList
- ((currentScope . scopePath) stage3State)
- (NonEmpty.singleton (Text.unpack s))
- in stage3State
- { inScope =
- insertScope fullName (Right size) (inScope stage3State)
- }
-
-lookupTypeSize :: Name I Annot -> Compile Stage3State SizeBits
-lookupTypeSize (Name idents a) = do
- let path = fmap (\(Identifier s _) -> Text.unpack s) idents
- scopePath <- gets scopePath
- mSize <- gets $ lookupScopeWithPath scopePath path . inScope
- case mSize of
- Just (Right sz) -> return sz
- _ -> do
- tell
- [ Diagnostic
- Error
- ( printf
- "Cannot resolve %s"
- (intercalate "." $ NonEmpty.toList path)
- )
- (unCommented a)
- ]
- compilationFailure
-
-emptyState = Stage3State mempty mempty
-
toStage3 :: FiddleUnit Stage2 I Annot -> Compile () (FiddleUnit Stage3 I Annot)
-toStage3 (FiddleUnit decls a) =
- snd
- <$> subCompile
- emptyState
- ( FiddleUnit <$> mapM (mapDirectedM fiddleDeclToStage3) decls <*> pure a
- )
+toStage3 = fmap snd . subCompile (GlobalState mempty) . advanceStage (LocalState mempty)
+
+instance CompilationStage Stage2 where
+ type StageAfter Stage2 = Stage3
+ type StageMonad Stage2 = Compile GlobalState
+ type StageState Stage2 = LocalState
+ type StageFunctor Stage2 = Identity
+ type StageAnnotation Stage2 = Commented SourceSpan
+
+deriving instance AdvanceStage Stage2 FiddleUnit
+
+deriving instance AdvanceStage Stage2 Expression
+
+instance AdvanceStage Stage2 FiddleDecl where
+ modifyState t s = case t of
+ (BitsDecl id typ a) -> do
+ typeSize <- getTypeSize typ
+ insertTypeSize s id typeSize
+ return s
+ (PackageDecl n _ _) -> do
+ let strs = nameToList n
+ let (LocalState scopePath) = s
+
+ return $
+ LocalState $
+ scopePath {currentScope = strs ++ currentScope scopePath}
+ (UsingDecl n _) ->
+ let (LocalState scopePath) = s
+ in return $
+ LocalState $
+ scopePath
+ { usingPaths = nameToList n : usingPaths scopePath
+ }
+ _ -> return s
-exprToSize ::
- (NumberType stage ~ Integer) =>
- Expression stage I Annot ->
- Compile s Integer
-exprToSize (LitNum num _) = return num
-exprToSize e = do
- tell [Diagnostic Error "Variables not allowed" (unCommented $ annot e)]
- compilationFailure
+nameToList :: Name f a -> [String]
+nameToList (Name idents _) = map (\(Identifier (Text.unpack -> s) _) -> s) (toList idents)
-getTypeSize :: BitType Stage2 I Annot -> Compile s SizeBits
-getTypeSize (RawBits expr _) = fromIntegral <$> exprToSize expr
-getTypeSize (EnumBitType expr (Identity (EnumBody constants _)) ann) = do
- declaredSize <- fromIntegral <$> exprToSize expr
+instance AdvanceStage Stage2 ObjTypeBody where
+ advanceStage s body = fst <$> objTypeBodyToStage3 s body 0
- -- If the declared size is less than or equal to 4, we'll enforce that the
- -- enum is packed. This is to make sure the user has covered all bases.
- when (declaredSize <= 4) $ do
- imap <-
- foldlM
- ( \imap (undirected -> enumConst) -> do
- number <- case enumConst of
- EnumConstantDecl _ expr _ -> exprToSize expr
- EnumConstantReserved expr _ -> exprToSize expr
+deriving instance AdvanceStage Stage2 ObjType
- when (number >= 2 ^ declaredSize) $
- tell
- [ Diagnostic
- Error
- ( printf
- "Enum constant too large. Max allowed %d\n"
- ((2 :: Int) ^ declaredSize)
- )
- (unCommented (annot enumConst))
- ]
+deriving instance AdvanceStage Stage2 DeferredRegisterBody
- return $ IntMap.insert (fromIntegral number) True imap
- )
- IntMap.empty
- constants
- let missing =
- filter (not . (`IntMap.member` imap)) [0 .. 2 ^ declaredSize - 1]
- unless (null missing) $
- tell
- [ Diagnostic
- Warning
- ( printf
- "Missing enum constants %s. Small enums should be fully \
- \ populated. Use 'reserved' if needed."
- (intercalate ", " (map show missing))
- )
- (unCommented ann)
- ]
+deriving instance AdvanceStage Stage2 RegisterBitsDecl
- return declaredSize
+instance AdvanceStage Stage2 RegisterBody where
+ advanceStage s body = fst <$> registerBodyToStage3 s body
-addCurrentScope :: [String] -> Compile Stage3State ()
-addCurrentScope s = do
- modify' $ \st@(Stage3State {scopePath = (ScopePath current others)}) ->
- st {scopePath = ScopePath (current ++ s) others}
-
-fiddleDeclToStage3 ::
- FiddleDecl Stage2 I Annot -> Compile Stage3State (FiddleDecl Stage3 I Annot)
-fiddleDeclToStage3 = \case
- OptionDecl i1 i2 a -> return $ OptionDecl i1 i2 a
- PackageDecl n@(Name idents _) body a -> do
- let strs = map (\(Identifier (Text.unpack -> s) _) -> s) (toList idents)
- Stage3State {scopePath = savedScopePath} <- get
- addCurrentScope strs
- PackageDecl n
- <$> mapM packageBodyToStage3 body
- <*> pure a
- <* modify' (\st -> st {scopePath = savedScopePath})
- UsingDecl n@(Name idents _) a -> do
- let strs = map (\(Identifier t _) -> Text.unpack t) (toList idents)
- modify'
- ( \st ->
- let (ScopePath cur using) = scopePath st
- in st
- { scopePath = ScopePath cur (strs : using)
- }
- )
- return $ UsingDecl n a
- LocationDecl id expr a -> return $ LocationDecl id (switchStage expr) a
- BitsDecl id typ a -> do
- typeSize <- getTypeSize typ
- insertTypeSize id typeSize
- return $ BitsDecl id (switchStage typ) a
- ObjTypeDecl ident body a ->
- ObjTypeDecl ident
- <$> mapM (\bt -> fst <$> objTypeBodyToStage3 bt 0) body
- <*> pure a
- ImportDecl importStatement a -> return $ ImportDecl importStatement a
- ObjectDecl ident expr typ a ->
- ObjectDecl
- ident
- (switchStage expr)
- <$> objTypeToStage3 typ
- <*> pure a
-
-objTypeToStage3 ::
- ObjType Stage2 I Annot -> Compile Stage3State (ObjType Stage3 I Annot)
-objTypeToStage3 = \case
- ArrayObjType objtype expr a ->
- ArrayObjType
- <$> objTypeToStage3 objtype
- <*> pure (switchStage expr)
- <*> pure a
- ReferencedObjType ident a -> return $ ReferencedObjType ident a
+deriving instance AdvanceStage Stage2 RegisterBitsTypeRef
-registerBodyToStage3 ::
- RegisterBody Stage2 I Annot ->
- Compile Stage3State (RegisterBody Stage3 I Annot, Word32)
-registerBodyToStage3
- (RegisterBody bodyType (Identity deferredRegisterBody) a') = do
- let isUnion = case bodyType of
- Union {} -> True
- _ -> False
+deriving instance AdvanceStage Stage2 AnonymousBitsType
- case deferredRegisterBody of
- DeferredRegisterBody decls a -> do
- (cur, returned) <-
- foldlM
- ( \(cursor, returned) decl ->
- case undirected decl of
- ReservedBits expr a -> do
- size <- fromIntegral <$> exprToSize expr
- let s3 =
- mapDirected
- (const $ ReservedBits (switchStage expr) a)
- decl
- if isUnion
- then checkUnion cursor size (s3 : returned) a
- else
- return (cursor + size, s3 : returned)
- BitsSubStructure registerBody maybeIdent annot -> do
- checkBitsSubStructure registerBody maybeIdent annot
-
- (newBody, subsize) <- registerBodyToStage3 registerBody
- let s3 =
- mapDirected
- (const $ BitsSubStructure newBody maybeIdent annot)
- decl
-
- if isUnion
- then checkUnion cursor subsize (s3 : returned) a
- else
- return (cursor + subsize, s3 : returned)
- DefinedBits modifier identifier typeref a -> do
- (s3TypeRef, size) <- registerBitsTypeRefToStage3 typeref
- let s3 =
- mapDirected
- (const $ DefinedBits modifier identifier s3TypeRef a)
- decl
+deriving instance AdvanceStage Stage2 BitType
- if isUnion
- then checkUnion cursor size (s3 : returned) a
- else
- return (cursor + size, s3 : returned)
- )
- (0, [])
- decls
+deriving instance AdvanceStage Stage2 EnumBody
- return
- ( RegisterBody
- bodyType
- (Identity (DeferredRegisterBody (reverse returned) a))
- a',
- cur
- )
- where
- checkBitsSubStructure
- (RegisterBody bodyType (Identity (DeferredRegisterBody decls _)) _)
- maybeIdent
- annot =
- let emitWarning s = tell [Diagnostic Warning s (unCommented annot)]
- in case () of
- ()
- | [_] <- decls,
- (Union {}) <- bodyType ->
- emitWarning "Union with a single field. Should this be a struct?"
- ()
- | [_] <- decls,
- (Struct {}) <- bodyType,
- Nothing <- maybeIdent ->
- emitWarning "Anonymous sub-struct with single field is superfluous."
- ()
- | [] <- decls ->
- emitWarning
- ( printf
- "Empty sub-%s is superfluous."
- ( case bodyType of
- Union {} -> "union"
- Struct {} -> "struct"
- )
- )
- _ -> return ()
+deriving instance AdvanceStage Stage2 EnumConstantDecl
-checkUnion :: Word32 -> Word32 -> b -> Commented SourceSpan -> Compile Stage3State (Word32, b)
-checkUnion cursor subsize ret a = do
- when (cursor /= 0 && subsize /= cursor) $ do
- tell
- [ Diagnostic
- Warning
- ( printf
- "Jagged union found. Found size %d, expected %d.\n \
- \ Please wrap smaller fields in a struct with padding so all \
- \ fields are the same size?"
- subsize
- cursor
- )
- (unCommented a)
- ]
- return (max cursor subsize, ret)
+deriving instance AdvanceStage Stage2 PackageBody
-registerBitsTypeRefToStage3 ::
- RegisterBitsTypeRef Stage2 I Annot ->
- Compile Stage3State (RegisterBitsTypeRef Stage3 I Annot, Word32)
-registerBitsTypeRefToStage3 = \case
- RegisterBitsArray ref expr a -> do
- (ref', size) <- registerBitsTypeRefToStage3 ref
- multiplier <- exprToSize expr
- return
- ( RegisterBitsArray ref' (switchStage expr) a,
- size * fromIntegral multiplier
- )
- RegisterBitsReference name a ->
- (RegisterBitsReference name a,) <$> lookupTypeSize name
- RegisterBitsJustBits expr a ->
- (RegisterBitsJustBits (switchStage expr) a,)
- . fromIntegral
- <$> exprToSize expr
+deriving instance (AdvanceStage Stage2 t) => AdvanceStage Stage2 (Directed t)
objTypeBodyToStage3 ::
- ObjTypeBody Stage2 I Annot -> Word32 -> Compile Stage3State (ObjTypeBody Stage3 I Annot, Word32)
-objTypeBodyToStage3 (ObjTypeBody bodyType decls a) startOff = do
+ LocalState ->
+ ObjTypeBody Stage2 I Annot ->
+ Word32 ->
+ Compile GlobalState (ObjTypeBody Stage3 I Annot, Word32)
+objTypeBodyToStage3 st (ObjTypeBody bodyType decls a) startOff = do
let isUnion = case bodyType of
Union {} -> True
_ -> False
@@ -383,7 +124,9 @@ objTypeBodyToStage3 (ObjTypeBody bodyType decls a) startOff = do
case undirected decl of
RegisterDecl mMod mIdent expr mBody a -> do
(s3RegisterBody, mCalculatedSize) <-
- fUnzip <$> mapM registerBodyToStage3 mBody
+ fUnzip <$> mapM (registerBodyToStage3 st) mBody
+
+ nExpr <- advanceStage st expr
let s3 =
mapDirected
@@ -391,7 +134,7 @@ objTypeBodyToStage3 (ObjTypeBody bodyType decls a) startOff = do
RegisterDecl
mMod
mIdent
- (switchStage expr)
+ nExpr
s3RegisterBody
a
)
@@ -436,6 +179,7 @@ objTypeBodyToStage3 (ObjTypeBody bodyType decls a) startOff = do
TypeSubStructure (Identity subBody) maybeIdent annot -> do
(newBody, size) <-
objTypeBodyToStage3
+ st
subBody
( if isUnion then startOff else cursor
)
@@ -456,8 +200,10 @@ objTypeBodyToStage3 (ObjTypeBody bodyType decls a) startOff = do
"Can only reserve a multiple of 8 bits in this context."
(unCommented a)
]
+
+ expr' <- advanceStage st expr
let size = size' `div` 8
- let s3 = mapDirected (const $ ReservedDecl (switchStage expr) annot) decl
+ let s3 = mapDirected (const $ ReservedDecl expr' annot) decl
if isUnion
then
checkUnion cursor size (s3 : returned) a
@@ -517,6 +263,242 @@ objTypeBodyToStage3 (ObjTypeBody bodyType decls a) startOff = do
pushApply (Just (a, b)) = (Just a, Just b)
pushApply Nothing = (Nothing, Nothing)
-packageBodyToStage3 :: PackageBody Stage2 I Annot -> Compile Stage3State (PackageBody Stage3 I Annot)
-packageBodyToStage3 (PackageBody decls a) =
- PackageBody <$> mapM (mapDirectedM fiddleDeclToStage3) decls <*> pure a
+registerBodyToStage3 ::
+ LocalState ->
+ RegisterBody Stage2 I Annot ->
+ Compile GlobalState (RegisterBody Stage3 I Annot, Word32)
+registerBodyToStage3
+ st
+ (RegisterBody bodyType (Identity deferredRegisterBody) a') = do
+ let isUnion = case bodyType of
+ Union {} -> True
+ _ -> False
+
+ case deferredRegisterBody of
+ DeferredRegisterBody decls a -> do
+ (cur, returned) <-
+ foldlM
+ ( \(cursor, returned) decl ->
+ case undirected decl of
+ ReservedBits expr a -> do
+ size <- fromIntegral <$> exprToSize expr
+ expr' <- advanceStage st expr
+ let s3 =
+ mapDirected
+ (const $ ReservedBits expr' a)
+ decl
+ if isUnion
+ then checkUnion cursor size (s3 : returned) a
+ else
+ return (cursor + size, s3 : returned)
+ BitsSubStructure registerBody maybeIdent annot -> do
+ checkBitsSubStructure registerBody maybeIdent annot
+
+ (newBody, subsize) <- registerBodyToStage3 st registerBody
+ let s3 =
+ mapDirected
+ (const $ BitsSubStructure newBody maybeIdent annot)
+ decl
+
+ if isUnion
+ then checkUnion cursor subsize (s3 : returned) a
+ else
+ return (cursor + subsize, s3 : returned)
+ DefinedBits modifier identifier typeref a -> do
+ (s3TypeRef, size) <- registerBitsTypeRefToStage3 st typeref
+ let s3 =
+ mapDirected
+ (const $ DefinedBits modifier identifier s3TypeRef a)
+ decl
+
+ if isUnion
+ then checkUnion cursor size (s3 : returned) a
+ else
+ return (cursor + size, s3 : returned)
+ )
+ (0, [])
+ decls
+
+ return
+ ( RegisterBody
+ bodyType
+ (Identity (DeferredRegisterBody (reverse returned) a))
+ a',
+ cur
+ )
+ where
+ checkBitsSubStructure
+ (RegisterBody bodyType (Identity (DeferredRegisterBody decls _)) _)
+ maybeIdent
+ annot =
+ let emitWarning s = tell [Diagnostic Warning s (unCommented annot)]
+ in case () of
+ ()
+ | [_] <- decls,
+ (Union {}) <- bodyType ->
+ emitWarning "Union with a single field. Should this be a struct?"
+ ()
+ | [_] <- decls,
+ (Struct {}) <- bodyType,
+ Nothing <- maybeIdent ->
+ emitWarning "Anonymous sub-struct with single field is superfluous."
+ ()
+ | [] <- decls ->
+ emitWarning
+ ( printf
+ "Empty sub-%s is superfluous."
+ ( case bodyType of
+ Union {} -> "union"
+ Struct {} -> "struct"
+ )
+ )
+ _ -> return ()
+
+registerBitsTypeRefToStage3 ::
+ LocalState ->
+ RegisterBitsTypeRef Stage2 I Annot ->
+ Compile GlobalState (RegisterBitsTypeRef Stage3 I Annot, Word32)
+registerBitsTypeRefToStage3 localState = \case
+ RegisterBitsArray ref expr a -> do
+ (ref', size) <- registerBitsTypeRefToStage3 localState ref
+ multiplier <- exprToSize expr
+ expr' <- advanceStage localState expr
+ return
+ ( RegisterBitsArray ref' expr' a,
+ size * fromIntegral multiplier
+ )
+ RegisterBitsReference name a ->
+ (RegisterBitsReference name a,) <$> lookupTypeSize localState name
+ RegisterBitsJustBits expr a -> do
+ expr' <- advanceStage localState expr
+ (RegisterBitsJustBits expr' a,)
+ . fromIntegral
+ <$> exprToSize expr
+
+checkUnion :: Word32 -> Word32 -> b -> Commented SourceSpan -> Compile s (Word32, b)
+checkUnion cursor subsize ret a = do
+ when (cursor /= 0 && subsize /= cursor) $ do
+ tell
+ [ Diagnostic
+ Warning
+ ( printf
+ "Jagged union found. Found size %d, expected %d.\n \
+ \ Please wrap smaller fields in a struct with padding so all \
+ \ fields are the same size?"
+ subsize
+ cursor
+ )
+ (unCommented a)
+ ]
+ return (max cursor subsize, ret)
+
+exprToSize ::
+ (NumberType stage ~ Integer) =>
+ Expression stage I Annot ->
+ Compile s Integer
+exprToSize (LitNum num _) = return num
+exprToSize e = do
+ tell [Diagnostic Error "Variables not allowed" (unCommented $ annot e)]
+ compilationFailure
+
+lookupTypeSize :: LocalState -> Name I Annot -> Compile GlobalState SizeBits
+lookupTypeSize (LocalState scopePath) (Name idents a) = do
+ -- Convert the list of identifiers to a string path
+ let path = fmap (\(Identifier s _) -> Text.unpack s) idents
+
+ -- Get the current scope and perform the lookup
+ results <- gets $ lookupScopeWithPath scopePath path . globalScope
+
+ case results of
+ -- Successfully resolved to a unique size
+ [(_, Right sz)] -> return sz
+ -- Multiple ambiguous results found
+ matches@(_ : _) -> do
+ -- Generate a list of ambiguous paths for error reporting
+ let ambiguousPaths =
+ map
+ ( \(resolvedPath, _) ->
+ intercalate "." (NonEmpty.toList resolvedPath)
+ )
+ matches
+ tell
+ [ Diagnostic
+ Error
+ ( printf
+ "Ambiguous occurrence of '%s'. Multiple matches found:\n%s"
+ (intercalate "." $ NonEmpty.toList path)
+ (unlines ambiguousPaths) -- List all ambiguous paths
+ )
+ (unCommented a)
+ ]
+ compilationFailure
+
+ -- No matches found
+ _ -> do
+ tell
+ [ Diagnostic
+ Error
+ ( printf
+ "Cannot resolve '%s'. No matching symbols found."
+ (intercalate "." $ NonEmpty.toList path)
+ )
+ (unCommented a)
+ ]
+ compilationFailure
+
+getTypeSize :: BitType Stage2 I Annot -> Compile s SizeBits
+getTypeSize (RawBits expr _) = fromIntegral <$> exprToSize expr
+getTypeSize (EnumBitType expr (Identity (EnumBody constants _)) ann) = do
+ declaredSize <- fromIntegral <$> exprToSize expr
+
+ -- If the declared size is less than or equal to 4, we'll enforce that the
+ -- enum is packed. This is to make sure the user has covered all bases.
+ when (declaredSize <= 4) $ do
+ imap <-
+ foldlM
+ ( \imap (undirected -> enumConst) -> do
+ number <- case enumConst of
+ EnumConstantDecl _ expr _ -> exprToSize expr
+ EnumConstantReserved expr _ -> exprToSize expr
+
+ when (number >= 2 ^ declaredSize) $
+ tell
+ [ Diagnostic
+ Error
+ ( printf
+ "Enum constant too large. Max allowed %d\n"
+ ((2 :: Int) ^ declaredSize)
+ )
+ (unCommented (annot enumConst))
+ ]
+
+ return $ IntMap.insert (fromIntegral number) True imap
+ )
+ IntMap.empty
+ constants
+ let missing =
+ filter (not . (`IntMap.member` imap)) [0 .. 2 ^ declaredSize - 1]
+ unless (null missing) $
+ tell
+ [ Diagnostic
+ Warning
+ ( printf
+ "Missing enum constants %s. Small enums should be fully \
+ \ populated. Use 'reserved' if needed."
+ (intercalate ", " (map show missing))
+ )
+ (unCommented ann)
+ ]
+
+ return declaredSize
+
+insertTypeSize :: LocalState -> Identifier f a -> SizeBits -> Compile GlobalState ()
+insertTypeSize (LocalState scopePath) (Identifier s _) size = do
+ modify' $
+ \(GlobalState globalScope) ->
+ let fullName =
+ NonEmpty.prependList
+ (currentScope scopePath)
+ (NonEmpty.singleton (Text.unpack s))
+ in GlobalState $
+ insertScope fullName (Right size) globalScope