summaryrefslogtreecommitdiffstats
path: root/opts.sml
blob: ea5b93575e5699c9df8c2ab6db741013e200a130 (plain) (blame)
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
structure Opts =
struct
  datatype 'a optDesc = BoolOpt of bool -> unit
                      | StringOpt of string -> unit

  structure StringMap = Map(type k = string val cmp = String.compare)

  fun error (msg : string) : 'a =
    (print msg ;
     OS.Process.exit (OS.Process.failure))

  fun boolFromString s =
    case s of
      "1" => true
    | "t" => true
    | "T" => true
    | "true" => true
    | "TRUE" => true
    | "True" => true
    | "0" => false
    | "f" => false
    | "F" => false
    | "false" => false
    | "FALSE" => false
    | "False" => false
    | _ => error "invalid boolean value"

  fun getOpt (desc : (string * 'a optDesc) list) : string list =
    let
      val parsers = StringMap.fromList desc
      fun go [] = []
        | go (arg :: args) =
            if arg = "-" orelse not (String.isPrefix "-" arg)
            then arg :: args
            else if arg = "--"
            then args
            else
              let
                val name =
                  if String.isPrefix "--" arg
                  then String.extract (arg, 2, NONE)
                  else String.extract (arg, 1, NONE)
                val _ =
                  if String.isPrefix "-" name orelse String.isPrefix "=" name
                  then error "bad flag syntax"
                  else ()
                (* It's a flag. Does it have an argument? *)
                val (name', value) =
                  case CharVector.findi (fn (_, x) => x = #"=") name of
                    SOME (i, _) => (substring (name, 0, i), String.extract (name, i + 1, NONE))
                  | NONE => (name, "")
                val parser =
                  case StringMap.lookup name' parsers of
                    SOME x => x
                  | NONE => error ("flag provided but not defined: " ^ String.toString name')
              in
                case parser of
                  BoolOpt func =>
                    if value = ""
                    then
                      (func true ;
                       go args)
                    else
                      (func (boolFromString value) ;
                       go args)
                | StringOpt func =>
                    (* It must have a value, which might be the next argument. *)
                    if value = "" andalso not (null args)
                    then
                      (func (hd args) ;
                       go (tl args))
                    else if value = ""
                    then error ("flag needs an argument: " ^ String.toString name')
                    else
                      (func value ;
                       go args)
              end
    in
      go (CommandLine.arguments ())
    end
end