Sockeye: Better error messages for checker
[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 SockeyeASTFrontend as ASTF
26 import SockeyeASTIntermediate as ASTI
27 import SockeyeASTBackend as ASTB
28
29 import SockeyeParser
30 import SockeyeChecker
31 import qualified SockeyeBackendPrintAST as PrintAST
32 import qualified SockeyeBackendProlog as Prolog
33
34 import Text.Groom(groom)
35
36 {- Exit codes -}
37 usageError :: ExitCode
38 usageError = ExitFailure 1
39
40 parseError :: ExitCode
41 parseError = ExitFailure 2
42
43 checkError :: ExitCode
44 checkError = ExitFailure 3
45
46
47
48 {- Compilation targets -}
49 data Target = None | PrintAST | Prolog
50
51 {- Possible options for the Sockeye Compiler -}
52 data Options = Options { optInputFile  :: FilePath
53                        , optTarget     :: Target
54                        , optOutputFile :: Maybe FilePath
55                        }
56
57 {- Default options -}
58 defaultOptions :: Options
59 defaultOptions = Options { optInputFile  = ""
60                          , optTarget     = Prolog
61                          , optOutputFile = Nothing
62                          }
63
64 {- Set the input file name -}
65 optSetInputFileName :: FilePath -> Options -> Options
66 optSetInputFileName f o = o { optInputFile = f }
67
68 {- Set the target -}
69 optSetTarget :: Target -> Options -> Options
70 optSetTarget t o = o { optTarget = t }
71
72 {- Set the outpue file name -}
73 optSetOutputFile :: Maybe String -> Options -> Options
74 optSetOutputFile f o = o { optOutputFile = f }
75
76 {- Prints usage information possibly with usage errors -}
77 usage :: [String] -> IO ()
78 usage errors = do
79     prg <- getProgName
80     let usageString = "Usage: " ++ prg ++ " [options] file\nOptions:"
81     hPutStrLn stderr $ usageInfo (concat errors ++ usageString) options
82     hPutStrLn stderr "The backend (capital letter options) specified last takes precedence."
83
84
85 {- Setup option parser -}
86 options :: [OptDescr (Options -> IO Options)]
87 options = 
88     [ Option "P" ["Prolog"]
89         (NoArg (\opts -> return $ optSetTarget Prolog opts))
90         "Generate a prolog file that can be loaded into the SKB (default)."
91     , Option "A" ["AST"]
92         (NoArg (\opts -> return $ optSetTarget PrintAST opts))
93         "Print the AST."
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 (ASTF.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 :: ASTF.SockeyeSpec -> IO ASTI.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 {- Compiles the AST with the appropriate backend -}
146 compile :: Target -> ASTB.NetSpec -> IO String
147 compile None     _   = return ""
148 compile PrintAST ast = return $ PrintAST.compile ast
149 compile Prolog   ast = return $ Prolog.compile ast
150
151 {- Outputs the compilation result -}
152 output :: Maybe FilePath -> String -> IO ()
153 output outFile out = do
154     case outFile of
155         Nothing -> putStr out
156         Just f  -> writeFile f out
157
158 main = do
159     args <- getArgs
160     opts <- compilerOpts args
161     let inFile = optInputFile opts
162     parsedAst <- parseFile inFile
163     intermAst <- checkAST parsedAst
164     putStrLn $ groom intermAst
165     -- out <- compile (optTarget opts) ast
166     -- output (optOutputFile opts) out
167