Sockeye: Start implementation of 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             hPutStrLn stderr $ "Checks failed:"
142             hPutStr stderr $ show fail
143             exitWith checkError
144         Right intermAst -> return intermAst
145
146 {- Compiles the AST with the appropriate backend -}
147 compile :: Target -> ASTB.NetSpec -> IO String
148 compile None     _   = return ""
149 compile PrintAST ast = return $ PrintAST.compile ast
150 compile Prolog   ast = return $ Prolog.compile ast
151
152 {- Outputs the compilation result -}
153 output :: Maybe FilePath -> String -> IO ()
154 output outFile out = do
155     case outFile of
156         Nothing -> putStr out
157         Just f  -> writeFile f out
158
159 main = do
160     args <- getArgs
161     opts <- compilerOpts args
162     let inFile = optInputFile opts
163     parsedAst <- parseFile inFile
164     intermAst <- checkAST parsedAst
165     putStrLn $ groom intermAst
166     -- out <- compile (optTarget opts) ast
167     -- output (optOutputFile opts) out
168