aboutsummaryrefslogtreecommitdiff
path: root/Main.hs
blob: dc76f65f1ee4da2482bff445efc115d310a54e8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
module Main where

import Control.Exception (bracket)
import Control.Monad
import System.Directory (removeFile)
import System.Environment
import System.Exit
import System.IO
import System.Process
import qualified System.Posix.Temp as Posix

import BuildIR
import CodeGen
import Defs
import Optimiser
import OptionParser
import Pretty
import ProgramParser
import TypeCheck
import Verify


infix 2 <?>
(<?>) :: (a -> Error b) -> String -> a -> Error b
f <?> pre = \a -> case f a of
    Left e -> Left $ pre ++ ": " ++ e
    Right x -> Right x


eitherToIO :: Either String a -> IO a
eitherToIO = either die return

extensionForStage :: CompilerStage -> String
extensionForStage StageTypeCheck = ".lang"
extensionForStage StageIR = ".ir"
extensionForStage StageAsm = ".asm"
extensionForStage StageObject = ".o"
extensionForStage StageExecutable = ""

inputFile :: String -> IO String
inputFile "-" = getContents
inputFile fname = readFile fname

outputFile :: String -> String -> IO ()
outputFile "-" str = putStr str
outputFile fname str = writeFile fname str

mkTempFile :: IO FilePath
mkTempFile = do
    (path, handle) <- Posix.mkstemp "/tmp/tmp.lang."
    hClose handle
    return path

main :: IO ()
main = do
    opts <- getArgs >>= eitherToIO . optionParser

    when (oShowHelp opts) $ do
        name <- getProgName
        putStrLn $ "Usage: " ++ name ++ " [options] files..."
        putStrLn ""
        putStrLn "-h, --help         Show this help"
        putStrLn "-o, --output FILE  Send output to the specified file instead of z_output[.ext]."
        putStrLn "-O, --optimise     Optimise the output. (Happens at IR stage.)"
        putStrLn "--stage typecheck  Only parse and typecheck the program, outputting a pretty-"
        putStrLn "                   printed version."
        putStrLn "--stage ir         Compile until (possibly optimised) IR, then output that."
        putStrLn "-S, --stage asm    Compile until assembly, then output that."
        putStrLn "-c, --stage obj    Compile to an object file."
        putStrLn "--stage exe        Default mode; compile and link."
        exitSuccess

    source <- case oSourceFiles opts of
        [] -> die "No source files given. Run with --help for usage information."
        [name] -> inputFile name
        _ -> die "Only one source file supported at the moment."

    when (oOutputName opts == "-" && oLastStage opts >= StageObject) $
        die "Will not write binary file to stdout."

    let outname = if oAppendExtension opts
                    then oOutputName opts ++ extensionForStage (oLastStage opts)
                    else oOutputName opts

    let laststage = oLastStage opts

    ast' <- eitherToIO $ (parseProgram <?> "Parse error") source
    ast <- eitherToIO $ (typeCheck <?> "Type error") ast'
    when (laststage == StageTypeCheck) $ do
        outputFile outname $ pretty ast
        exitSuccess

    ir <- eitherToIO $ (buildIR <?> "IR building error") ast
    iropt' <- eitherToIO $ (optimise (oOptimise opts) <?> "Error while optimising") ir
    iropt <- eitherToIO $ (verify <?> "Verify error") iropt'
    when (laststage == StageIR) $ do
        outputFile outname $ pretty iropt
        exitSuccess

    asm <- eitherToIO $ (codegen <?> "Codegen error") iropt
    when (laststage == StageAsm) $ do
        outputFile outname asm
        exitSuccess


    let objfnameIO = case laststage of
            StageObject -> return outname
            StageExecutable -> mkTempFile
            _ -> undefined

    let rmObjfileIO = case outname of
            "-" -> const $ return ()
            _ -> removeFile

    bracket objfnameIO rmObjfileIO $ \objfname -> do
        hPutStrLn stderr "Assembling with yasm..."
        let yasmprocspec = (proc "yasm" ["-w+all", "-fmacho64", "-", "-o", objfname]) {std_in = CreatePipe}
        yasmcode <- withCreateProcess yasmprocspec $ \(Just pipe) _ _ ph -> do
            hPutStr pipe asm
            hFlush pipe
            hClose pipe
            waitForProcess ph
        case yasmcode of
            ExitSuccess -> return ()
            ExitFailure _ -> die "yasm failed!"
        when (laststage == StageObject) exitSuccess

        hPutStrLn stderr "Linking with ld..."
        callProcess "ld" [objfname, "liblang.o", "-o", outname]