Sockeye: Implement proper module instantiation check
[barrelfish] / tools / sockeye / Main.hs
1 {-
2   SockeyeMain.hs: Sockeye
3
4   Copyright (c) 2017, ETH Zurich.
5
6   All rights reserved.
7
8   This file is distributed under the terms in the attached LICENSE file.
9   If you do not find this file, copies can be found by writing to:
10   ETH Zurich D-INFK, CAB F.78, Universitaetstr. 6, CH-8092 Zurich,
11   Attn: Systems Group.
12 -}
13
14 module Main where
15
16 import Control.Monad
17
18 import Data.List
19
20 import System.Console.GetOpt
21 import System.Exit
22 import System.Environment
23 import System.IO
24
25 import qualified SockeyeASTParser as ParseAST
26 import qualified SockeyeAST as AST
27 import qualified SockeyeASTDecodingNet as NetAST
28
29 import SockeyeParser
30 import SockeyeChecker
31 import SockeyeNetBuilder
32
33 import qualified SockeyeBackendProlog as Prolog
34
35 import Debug.Trace
36 import Text.Groom(groom)
37
38 {- Exit codes -}
39 usageError :: ExitCode
40 usageError = ExitFailure 1
41
42 parseError :: ExitCode
43 parseError = ExitFailure 2
44
45 checkError :: ExitCode
46 checkError = ExitFailure 3
47
48 buildError :: ExitCode
49 buildError = ExitFailure 4
50
51 {- Compilation targets -}
52 data Target = None | Prolog
53
54 {- Possible options for the Sockeye Compiler -}
55 data Options = Options { optInputFile  :: FilePath
56                        , optTarget     :: Target
57                        , optOutputFile :: Maybe FilePath
58                        }
59
60 {- Default options -}
61 defaultOptions :: Options
62 defaultOptions = Options { optInputFile  = ""
63                          , optTarget     = Prolog
64                          , optOutputFile = Nothing
65                          }
66
67 {- Set the input file name -}
68 optSetInputFileName :: FilePath -> Options -> Options
69 optSetInputFileName f o = o { optInputFile = f }
70
71 {- Set the target -}
72 optSetTarget :: Target -> Options -> Options
73 optSetTarget t o = o { optTarget = t }
74
75 {- Set the outpue file name -}
76 optSetOutputFile :: Maybe String -> Options -> Options
77 optSetOutputFile f o = o { optOutputFile = f }
78
79 {- Prints usage information possibly with usage errors -}
80 usage :: [String] -> IO ()
81 usage errors = do
82     prg <- getProgName
83     let usageString = "Usage: " ++ prg ++ " [options] file\nOptions:"
84     hPutStrLn stderr $ usageInfo (concat errors ++ usageString) options
85     hPutStrLn stderr "The backend (capital letter options) specified last takes precedence."
86
87
88 {- Setup option parser -}
89 options :: [OptDescr (Options -> IO Options)]
90 options = 
91     [ Option "P" ["Prolog"]
92         (NoArg (\opts -> return $ optSetTarget Prolog opts))
93         "Generate a prolog file that can be loaded into the SKB (default)."
94     , Option "C" ["Check"]
95         (NoArg (\opts -> return $ optSetTarget None opts))
96         "Just check the file, do not compile."
97     , Option "o" ["output-file"]
98         (ReqArg (\f opts -> return $ optSetOutputFile (Just f) opts) "FILE")
99         "If no output file is specified the compilation result is written to stdout."
100     , Option "h" ["help"]
101         (NoArg (\_ -> do
102                     usage []
103                     exitWith ExitSuccess))
104         "Show help."
105     ]
106
107 {- evaluates the compiler options -}
108 compilerOpts :: [String] -> IO (Options)
109 compilerOpts argv =
110     case getOpt Permute options argv of
111         (actions, fs, []) -> do
112             opts <- foldl (>>=) (return defaultOptions) actions
113             case fs of
114                 []  -> do
115                     usage ["No input file\n"]
116                     exitWith usageError
117                 [f] -> return $ optSetInputFileName f opts
118                 _   -> do
119                     usage ["Multiple input files not supported\n"]
120                     exitWith usageError
121
122         (_, _, errors) -> do
123             usage errors
124             exitWith $ usageError
125
126 {- Runs the parser -}
127 parseFile :: FilePath -> IO (ParseAST.SockeyeSpec)
128 parseFile file = do
129     src <- readFile file
130     case parseSockeye file src of
131         Left err -> do
132             hPutStrLn stderr $ "Parse error at " ++ show err
133             exitWith parseError
134         Right ast -> return ast
135
136 {- Runs the checker -}
137 checkAST :: ParseAST.SockeyeSpec -> IO AST.SockeyeSpec
138 checkAST parsedAst = do
139     case checkSockeye parsedAst of 
140         Left fail -> do
141             hPutStr stderr $ show fail
142             exitWith checkError
143         Right intermAst -> return intermAst
144
145 {- Builds the decoding net from the Sockeye AST -}
146 buildNet :: AST.SockeyeSpec -> IO NetAST.NetSpec
147 buildNet ast = do
148     case sockeyeBuildNet ast of 
149         Left fail -> do
150             hPutStr stderr $ show fail
151             exitWith buildError
152         Right netAst -> return netAst
153
154 {- Compiles the AST with the appropriate backend -}
155 compile :: Target -> NetAST.NetSpec -> IO String
156 compile None     _   = return ""
157 compile Prolog   ast = return $ Prolog.compile ast
158
159 {- Outputs the compilation result -}
160 output :: Maybe FilePath -> String -> IO ()
161 output outFile out = do
162     case outFile of
163         Nothing -> putStr out
164         Just f  -> writeFile f out
165
166 main = do
167     args <- getArgs
168     opts <- compilerOpts args
169     let inFile = optInputFile opts
170     parsedAst <- parseFile inFile
171     ast <- checkAST parsedAst
172     netAst <- buildNet ast
173     out <- compile (optTarget opts) netAst
174     output (optOutputFile opts) out
175