{- sv2v - Author: Zachary Snow <zach@zachjs.com> - - conversion entry point -} import System.IO (hPrint, hPutStrLn, stderr, stdout) import System.Exit (exitFailure, exitSuccess) import System.FilePath (combine, splitExtension) import Control.Monad (when, zipWithM_) import Control.Monad.Except (runExceptT) import Data.List (nub) import Convert (convert) import Job (readJob, Job(..), Write(..)) import Language.SystemVerilog.AST import Language.SystemVerilog.Parser (parseFiles, Config(..)) isComment :: Description -> Bool isComment (PackageItem (Decl CommentDecl{})) = True isComment _ = False droppedKind :: Description -> Identifier droppedKind description = case description of Part _ _ Interface _ _ _ _ -> "interface" Package{} -> "package" Class{} -> "class" PackageItem Function{} -> "function" PackageItem Task {} -> "task" PackageItem (Decl Param{}) -> "localparam" _ -> "" emptyWarnings :: AST -> AST -> IO () emptyWarnings before after = if all isComment before || not (all isComment after) || null kinds then return () else if elem "interface" kinds then hPutStrLn stderr $ "Warning: Source includes an interface but the" ++ " output is empty because there are no modules without any" ++ " interface ports. Please convert interfaces alongside the" ++ " modules that instantiate them." else hPutStrLn stderr $ "Warning: Source includes a " ++ kind ++ " but no" ++ " modules. Such elements are elaborated into the modules that" ++ " use them. Please convert all sources in one invocation." where kinds = nub $ filter (not . null) $ map droppedKind before kind = head kinds rewritePath :: FilePath -> IO FilePath rewritePath path = do when (end /= ext) $ do hPutStrLn stderr $ "Refusing to write adjacent to " ++ show path ++ " because that path does not end in " ++ show ext exitFailure return $ base ++ ".v" where ext = ".sv" (base, end) = splitExtension path splitModules :: FilePath -> AST -> [(FilePath, String)] splitModules dir (PackageItem (Decl CommentDecl{}) : ast) = splitModules dir ast splitModules dir (description : ast) = (path, output) : splitModules dir ast where Part _ _ Module _ name _ _ = description path = combine dir $ name ++ ".v" output = show description ++ "\n" splitModules _ [] = [] writeOutput :: Write -> [FilePath] -> [AST] -> IO () writeOutput _ [] [] = hPutStrLn stderr "Warning: No input files specified (try `sv2v --help`)" writeOutput Stdout _ asts = hPrint stdout $ concat asts writeOutput (File f) _ asts = writeFile f $ show $ concat asts writeOutput Adjacent inPaths asts = do outPaths <- mapM rewritePath inPaths let results = map (++ "\n") $ map show asts zipWithM_ writeFile outPaths results writeOutput (Directory d) _ asts = do let (outPaths, outputs) = unzip $ splitModules d $ concat asts zipWithM_ writeFile outPaths outputs main :: IO () main = do job <- readJob -- parse the input files let config = Config { cfDefines = define job , cfIncludePaths = incdir job , cfLibraryPaths = libdir job , cfSiloed = siloed job , cfSkipPreprocessor = skipPreprocessor job , cfOversizedNumbers = oversizedNumbers job } result <- runExceptT $ parseFiles config (files job) case result of Left msg -> do hPutStrLn stderr msg exitFailure Right inputs -> do let (inPaths, asts) = unzip inputs -- convert the files if requested let converter = convert (top job) (dumpPrefix job) (exclude job) asts' <- if passThrough job then return asts else converter asts emptyWarnings (concat asts) (concat asts') -- write the converted files out writeOutput (write job) inPaths asts' exitSuccess