7cd1c750b2e616108798fd623e66ba01dd1f0cb8
[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 Text.Groom(groom)
36
37 {- Exit codes -}
38 usageError :: ExitCode
39 usageError = ExitFailure 1
40
41 parseError :: ExitCode
42 parseError = ExitFailure 2
43
44 checkError :: ExitCode
45 checkError = ExitFailure 3
46
47 buildError :: ExitCode
48 buildError = ExitFailure 4
49
50 {- Compilation targets -}
51 data Target = None | Prolog
52
53 {- Possible options for the Sockeye Compiler -}
54 data Options = Options { optInputFile  :: FilePath
55                        , optTarget     :: Target
56                        , optOutputFile :: Maybe FilePath
57                        }
58
59 {- Default options -}
60 defaultOptions :: Options
61 defaultOptions = Options { optInputFile  = ""
62                          , optTarget     = Prolog
63                          , optOutputFile = Nothing
64                          }
65
66 {- Set the input file name -}
67 optSetInputFileName :: FilePath -> Options -> Options
68 optSetInputFileName f o = o { optInputFile = f }
69
70 {- Set the target -}
71 optSetTarget :: Target -> Options -> Options
72 optSetTarget t o = o { optTarget = t }
73
74 {- Set the outpue file name -}
75 optSetOutputFile :: Maybe String -> Options -> Options
76 optSetOutputFile f o = o { optOutputFile = f }
77
78 {- Prints usage information possibly with usage errors -}
79 usage :: [String] -> IO ()
80 usage errors = do
81     prg <- getProgName
82     let usageString = "Usage: " ++ prg ++ " [options] file\nOptions:"
83     hPutStrLn stderr $ usageInfo (concat errors ++ usageString) options
84     hPutStrLn stderr "The backend (capital letter options) specified last takes precedence."
85
86
87 {- Setup option parser -}
88 options :: [OptDescr (Options -> IO Options)]
89 options = 
90     [ Option "P" ["Prolog"]
91         (NoArg (\opts -> return $ optSetTarget Prolog opts))
92         "Generate a prolog file that can be loaded into the SKB (default)."
93     , Option "C" ["Check"]
94         (NoArg (\opts -> return $ optSetTarget None opts))
95         "Just check the file, do not compile."
96     , Option "o" ["output-file"]
97         (ReqArg (\f opts -> return $ optSetOutputFile (Just f) opts) "FILE")
98         "If no output file is specified the compilation result is written to stdout."
99     , Option "h" ["help"]
100         (NoArg (\_ -> do
101                     usage []
102                     exitWith ExitSuccess))
103         "Show help."
104     ]
105
106 {- evaluates the compiler options -}
107 compilerOpts :: [String] -> IO (Options)
108 compilerOpts argv =
109     case getOpt Permute options argv of
110         (actions, fs, []) -> do
111             opts <- foldl (>>=) (return defaultOptions) actions
112             case fs of
113                 []  -> do
114                     usage ["No input file\n"]
115                     exitWith usageError
116                 [f] -> return $ optSetInputFileName f opts
117                 _   -> do
118                     usage ["Multiple input files not supported\n"]
119                     exitWith usageError
120
121         (_, _, errors) -> do
122             usage errors
123             exitWith $ usageError
124
125 {- Runs the parser -}
126 parseFile :: FilePath -> IO (ParseAST.SockeyeSpec)
127 parseFile file = do
128     src <- readFile file
129     case parseSockeye file src of
130         Left err -> do
131             hPutStrLn stderr $ "Parse error at " ++ show err
132             exitWith parseError
133         Right ast -> return ast
134
135 {- Runs the checker -}
136 checkAST :: ParseAST.SockeyeSpec -> IO AST.SockeyeSpec
137 checkAST parsedAst = do
138     case checkSockeye parsedAst of 
139         Left fail -> do
140             hPutStr stderr $ show fail
141             exitWith checkError
142         Right intermAst -> return intermAst
143
144 {- Builds the decoding net from the Sockeye AST -}
145 buildNet :: AST.SockeyeSpec -> IO NetAST.NetSpec
146 buildNet ast = do
147     case sockeyeBuildNet ast of 
148         Left fail -> do
149             hPutStr stderr $ show fail
150             exitWith buildError
151         Right netAst -> return netAst
152
153 {- Compiles the AST with the appropriate backend -}
154 compile :: Target -> NetAST.NetSpec -> IO String
155 compile None     _   = return ""
156 compile Prolog   ast = return $ Prolog.compile ast
157
158 {- Outputs the compilation result -}
159 output :: Maybe FilePath -> String -> IO ()
160 output outFile out = do
161     case outFile of
162         Nothing -> putStr out
163         Just f  -> writeFile f out
164
165 main = do
166     args <- getArgs
167     opts <- compilerOpts args
168     let inFile = optInputFile opts
169     parsedAst <- parseFile inFile
170     ast <- checkAST parsedAst
171     -- putStrLn $ groom ast ++ "\n"
172     netAst <- buildNet ast
173     -- putStrLn $ groom netAst ++ "\n"
174     out <- compile (optTarget opts) netAst
175     output (optOutputFile opts) out
176