Package.hs 6.77 KB
Newer Older
1 2 3
{- sv2v
 - Author: Zachary Snow <zach@zachjs.com>
 -
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 - Conversion for packages, exports, and imports
 -
 - TODO: We do not yet handle exports.
 - TODO: The scoping rules are not being entirely followed yet.
 - TODO: Explicit imports may introduce name conflicts because of carried items.
 -
 - The SystemVerilog scoping rules for exports and imports are not entirely
 - trivial. We do not explicitly handle the "error" scenarios detailed Table
 - 26-1 of Section 26-3 of IEEE 1800-2017. Users generally shouldn't be relying
 - on this tool to catch and report such wild naming conflicts that are outlined
 - there.
 -
 - Summary:
 - * In scopes which have a local declaration of an identifier, that identifier
 -   refers to that local declaration.
 - * If there is no local declaration, the identifier refers to the imported
 -   declaration.
 - * If there is an explicit import of that identifier, the identifier refers to
 -   the imported declaration.
 - * Usages of conflicting wildcard imports are not allowed.
24 25 26 27 28 29 30 31 32 33 34 35
 -}

module Convert.Package (convert) where

import Control.Monad.Writer
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set

import Convert.Traverse
import Language.SystemVerilog.AST

type Packages = Map.Map Identifier PackageItems
36
type PackageItems = [(Identifier, PackageItem)]
37 38 39
type Idents = Set.Set Identifier

convert :: [AST] -> [AST]
40
convert = step
41 42 43 44 45 46 47
    where
        step :: [AST] -> [AST]
        step curr =
            if next == curr
                then curr
                else step next
            where
48 49 50
                next = traverseFiles
                    (collectDescriptionsM collectDescriptionM)
                    convertFile curr
51 52

convertFile :: Packages -> AST -> AST
53
convertFile packages ast =
54 55 56 57 58 59 60 61
    (++) globalItems $
    filter (not . isCollected) $
    traverseDescriptions (traverseDescription packages) $
    ast
    where
        globalItems = map PackageItem $
             concatMap (uncurry globalPackageItems) $ Map.toList packages
        isCollected :: Description -> Bool
62
        isCollected (Package _ name _) = Map.member name packages
63
        isCollected _ = False
64 65 66

globalPackageItems :: Identifier -> PackageItems -> [PackageItem]
globalPackageItems name items =
67
    map (prefixPackageItem name (packageItemIdents items)) (map snd items)
68 69 70 71

packageItemIdents :: PackageItems -> Idents
packageItemIdents items =
    Set.union
72 73
        (Set.fromList $ map fst items)
        (Set.unions $ map (packageItemSubIdents . snd) items)
74 75 76 77 78
    where
        packageItemSubIdents :: PackageItem -> Idents
        packageItemSubIdents (Typedef (Enum _ enumItems _) _) =
            Set.fromList $ map fst enumItems
        packageItemSubIdents _ = Set.empty
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93

prefixPackageItem :: Identifier -> Idents -> PackageItem -> PackageItem
prefixPackageItem packageName idents item =
    item''
    where
        prefix :: Identifier -> Identifier
        prefix x =
            if Set.member x idents
                then packageName ++ "_" ++ x
                else x
        item' = case item of
            Function       a b x c d  -> Function       a b (prefix x) c d
            Task           a   x c d  -> Task           a   (prefix x) c d
            Typedef          a x      -> Typedef          a (prefix x)
            Decl (Variable a b x c d) -> Decl (Variable a b (prefix x) c d)
94 95
            Decl (Param    a b x c  ) -> Decl (Param    a b (prefix x) c  )
            Decl (ParamType  a x b  ) -> Decl (ParamType  a (prefix x) b  )
96
            other -> other
97
        convertType (Alias Nothing x rs) = Alias Nothing (prefix x) rs
98 99 100 101
        convertType (Enum mt items rs) = Enum mt items' rs
            where
                items' = map prefixItem items
                prefixItem (x, me) = (prefix x, me)
102
        convertType other = other
103 104
        convertExpr (Ident x) = Ident $ prefix x
        convertExpr other = other
105 106
        convertLHS (LHSIdent x) = LHSIdent $ prefix x
        convertLHS other = other
107
        converter =
108
            (traverseTypes $ traverseNestedTypes convertType) .
109 110
            (traverseExprs $ traverseNestedExprs convertExpr) .
            (traverseLHSs  $ traverseNestedLHSs  convertLHS )
111 112 113 114 115 116
        MIPackageItem item'' = converter $ MIPackageItem item'

collectDescriptionM :: Description -> Writer Packages ()
collectDescriptionM (Package _ name items) =
    if any isImport items
        then return ()
117
        else tell $ Map.singleton name itemList
118
    where
119 120 121
        itemList = concatMap toPackageItems items
        toPackageItems :: PackageItem -> PackageItems
        toPackageItems item =
122
            case piName item of
123 124
                Nothing -> []
                Just x -> [(x, item)]
125 126 127 128 129 130 131
        isImport :: PackageItem -> Bool
        isImport (Import _ _) = True
        isImport _ = False
collectDescriptionM _ = return ()

traverseDescription :: Packages -> Description -> Description
traverseDescription packages description =
132 133 134 135 136 137 138 139 140 141 142
    traverseModuleItems (traverseModuleItem existingItemNames packages)
    description
    where
        existingItemNames = execWriter $
            collectModuleItemsM writePIName description
        writePIName :: ModuleItem -> Writer Idents ()
        writePIName (MIPackageItem item) =
            case piName item of
                Nothing -> return ()
                Just x -> tell $ Set.singleton x
        writePIName _ = return ()
143

144 145
traverseModuleItem :: Idents -> Packages -> ModuleItem -> ModuleItem
traverseModuleItem existingItemNames packages (MIPackageItem (Import x y)) =
146 147 148 149 150
    if Map.member x packages
        then Generate $ map (GenModuleItem . MIPackageItem) items
        else MIPackageItem $ Import x y
    where
        packageItems = packages Map.! x
151 152 153
        filterer itemName = case y of
                Nothing -> Set.notMember itemName existingItemNames
                Just ident -> ident == itemName
154
        items = map snd $ filter (filterer . fst) $ packageItems
155
traverseModuleItem _ _ item =
156
    (traverseExprs $ traverseNestedExprs traverseExpr) $
157
    (traverseTypes $ traverseNestedTypes traverseType) $
158
    item
159 160 161 162 163 164 165 166 167 168
    where

        traverseExpr :: Expr -> Expr
        traverseExpr (PSIdent x y) = Ident $ x ++ "_" ++ y
        traverseExpr other = other

        traverseType :: Type -> Type
        traverseType (Alias (Just ps) xx rs) =
            Alias Nothing (ps ++ "_" ++ xx) rs
        traverseType other = other
169 170 171 172 173 174 175

-- returns the "name" of a package item, if it has one
piName :: PackageItem -> Maybe Identifier
piName (Function _ _ ident _ _) = Just ident
piName (Task     _   ident _ _) = Just ident
piName (Typedef    _ ident    ) = Just ident
piName (Decl (Variable _ _ ident _ _)) = Just ident
176 177
piName (Decl (Param    _ _ ident   _)) = Just ident
piName (Decl (ParamType  _ ident   _)) = Just ident
178
piName (Decl (CommentDecl          _)) = Nothing
179 180 181
piName (Import  _ _) = Nothing
piName (Export    _) = Nothing
piName (Directive _) = Nothing