summaryrefslogtreecommitdiff
path: root/driver.fun
blob: ebb3dfd1257f625decfb434879d08ad071ecd213 (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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
functor Driver(E: EMIT): DRIVER = struct
  structure I = E.I
  structure P = E.I.P

  datatype execMode = EmAsm | EmObj | EmExe

  type config = {
    file: string option,
    outFile: string option,
    includeDirs: string list,
    libDir: string option,
    mode: execMode,
    verbose: bool,
    debugParser: bool,
    debugIl: bool,
    debugEmit: bool
  }

  fun mod2int m =
    case m of
      EmAsm => 0
    | EmObj => 1
    | EmExe => 2

  fun modCmp (m1, m2) = compare (mod2int m1) (mod2int m2)

  val updateC = fn z =>
  let
    fun from file outFile includeDirs libDir mode verbose
      debugParser debugIl debugEmit =
      { file, outFile, includeDirs, libDir, mode, verbose,
        debugParser, debugIl, debugEmit }
    fun to f { file, outFile, includeDirs, libDir, mode, verbose,
      debugParser, debugIl, debugEmit  }
    =
      f file outFile includeDirs libDir mode verbose
        debugParser debugIl debugEmit
  in
    FRU.makeUpdate9 (from, from, to)
  end z

  val initConfig: config = { file = NONE, outFile = NONE,
  includeDirs = [], libDir = NONE, mode = EmExe,
    verbose = false,
    debugParser = false, debugIl = false, debugEmit = false }

  val die = fn z => die 1 z

  fun finish ({ file = NONE, ... }: config) = die `"No file specified" %
    | finish conf =
    let
      val conf = updateC conf u#includeDirs rev %
    in
      case #outFile conf of
        NONE =>
        let
          val sfx =
            case #mode conf of
              EmAsm => ".s"
            | EmObj => ".o"
            | EmExe => ""
        in
          updateC conf s#outFile (SOME (valOf (#file conf) ^ sfx)) %
        end
      | SOME _ => conf
    end

  fun parseFlag conf "-v" tail =
        parseCmdArgs (updateC conf s#verbose true %) tail
    | parseFlag conf "-dp" tail =
        parseCmdArgs (updateC conf s#debugParser true %) tail
    | parseFlag conf "-di" tail =
        parseCmdArgs (updateC conf s#debugIl true %) tail
    | parseFlag conf "-de" tail =
        parseCmdArgs (updateC conf s#debugEmit true %) tail
    | parseFlag conf "-c" tail =
        parseCmdArgs (updateC conf s#mode EmObj %) tail
    | parseFlag conf "-S" tail =
        parseCmdArgs (updateC conf s#mode EmAsm %) tail
    | parseFlag _ "-o" [] =
        die `"-o: expected file name after argument" %
    | parseFlag (C as { outFile, ... }) "-o" (file :: tail) = (
        case outFile of
          NONE => parseCmdArgs (updateC C s#outFile (SOME file) %) tail
       | SOME _ => die `file `": output file name is already specified" %
    )
    | parseFlag _ "-L" [] =
        die `"-L: expected directory name after argument" %
    | parseFlag (C as { libDir, ... }) "-L" ((dir: string) :: tail) = (
        case libDir of
          NONE => parseCmdArgs (updateC C s#libDir (SOME dir) %) tail
       | SOME _ => die `dir `": libdir name is already specified" %
    )
    | parseFlag _ arg _ = die `arg `": unknown flag" %

  and parseCmdArgs conf [] = finish conf
    | parseCmdArgs _ ("-I" :: []) =
        die `"-I: expected directory path after flag" %
    | parseCmdArgs conf ("-I" :: path :: tail) =
        parseCmdArgs (updateC conf u#includeDirs
            (fn dirs => path :: dirs) %) tail
    | parseCmdArgs (C as { file, ... }) (arg :: tail) =
      if String.sub (arg, 0) = #"-" then
        parseFlag C arg tail
      else
        case file of
          NONE =>
            let
              val size = size arg
            in
              if String.extract (arg, size - 2, NONE) <> ".c" then
                die `arg `": expected file with .c suffix" %
              else
                let
                  val file = String.substring (arg, 0, size - 2)
                in
                  parseCmdArgs (updateC C s#file (SOME file) %) tail
                end
            end
        | SOME _ => die `arg `": file already specified" %

  fun callProgram verbose name args clean =
  let
    open Posix.Process
    fun exec () =
    let
      val args = name :: args
      fun ps s out = Printf out `s %
      val () =
        if verbose then
          printfn Plist ps args (" ", false, 0) %
        else
          ()
    in
      execp (name, args);
      die `"canot exec " `name `": exec failed" %
    end
  in
    case fork () of
      NONE => exec ()
    | SOME _ =>
        let
          val (_, status) = wait ()
        in
          case status of
            W_EXITED => clean ()
          | W_EXITSTATUS st =>
              die `name `": failed with " W (Word8.toLargeWord st) `"code" %
          | _ => raise Unimplemented
        end
  end

  fun linkArgs _ _ NONE = die `"libdir: is not specified" %
    | linkArgs input output (SOME d) =
    let
      open OS
      val crt = Path.joinDirFile { dir = d, file = "crt1.o" }
      val libc = Path.joinDirFile { dir = d, file =  "libc.a" }
    in
      ["-o", output, input, crt, libc]
    end

  fun assemble { mode, outFile, verbose, ... } asmFile =
  let
    val outputFile =
      case mode of
        EmObj => valOf outFile
      | _ => String.substring (asmFile, 0, size asmFile - 2) ^ ".o"

    val args = ["-f", "elf64", "-o", outputFile, asmFile]
    val clean =
      fn () =>
        let
          val () = if verbose then printfn `"removing " `asmFile % else ()
        in
          Posix.FileSys.unlink asmFile
        end
  in
    callProgram verbose "nasm" args clean;
    outputFile
  end

  fun link { libDir, outFile, verbose, ... } objFile =
  let
    val args = linkArgs objFile (valOf outFile) libDir
    val clean =
      fn () =>
        let
          val () = if verbose then printfn `"removing " `objFile % else ()
        in
          Posix.FileSys.unlink objFile
        end
  in
    callProgram verbose "ld" args clean
  end

  fun parse { file, includeDirs, debugParser, ... } =
  let
    val file = valOf file
    val debugFile = if debugParser then SOME (file ^ ".p") else NONE
    val parseCtx = P.createCtx file includeDirs debugFile

    fun collect ctx =
    let
      val (continue, ctx) = P.parseDef ctx
    in
      if continue then
        collect ctx
      else
        P.finalize ctx
    end
  in
    collect parseCtx
  end

  fun ilConv parseCtx config =
  let
    val debugFile =
      if #debugIl config then
        SOME (valOf (#file config) ^ ".i")
      else
        NONE
    val progInfo = P.explode parseCtx
  in
    I.createCtx progInfo debugFile
  end

  fun emit ilCtx { debugEmit, file, outFile, mode, ... }=
  let
    val debugFile =
      if debugEmit then
        SOME (valOf file ^ ".e")
      else
        NONE
    val out =
      case mode of
        EmAsm => valOf $ outFile
      | _ => OS.FileSys.tmpName () ^ ".s"
  in
    E.emit out ilCtx debugFile;
    out
  end

  fun exec () =
  let
    val config = parseCmdArgs initConfig (CommandLine.arguments ())

    val parseCtx = parse config
    val ilCtx = ilConv parseCtx config
    val asmFile = emit ilCtx config
  in
    if modCmp (#mode config, EmAsm) = GREATER then
      let
        val objFile = assemble config asmFile
      in
        if modCmp (#mode config, EmObj) = GREATER then
          link config objFile
        else
          ()
      end
    else
      ()
  end
end