File: Arg.fs

package info (click to toggle)
fsharp 3.1.1.26%2Bdfsg2-3
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 59,244 kB
  • ctags: 4,190
  • sloc: cs: 13,398; ml: 1,098; sh: 399; makefile: 293; xml: 82
file content (133 lines) | stat: -rwxr-xr-x 5,175 bytes parent folder | download
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
// (c) Microsoft Corporation 2005-2009. 

#if INTERNALIZED_POWER_PACK
namespace Internal.Utilities
#else
namespace Microsoft.FSharp.Text
#endif


type ArgType = 
  | ClearArg of bool ref
  | FloatArg of (float -> unit)
  | IntArg of (int -> unit)
  | RestArg of (string -> unit)
  | SetArg of bool ref
  | StringArg of (string -> unit)
  | UnitArg of (unit -> unit)
  static member Clear  r = ClearArg r
  static member Float  r = FloatArg r
  static member Int    r = IntArg r
  static member Rest   r = RestArg r
  static member Set    r = SetArg r
  static member String r = StringArg r
  static member Unit   r = UnitArg r


type ArgInfo (name,action,help) = 
  member x.Name = name
  member x.ArgType = action
  member x.HelpText = help
  
exception Bad of string
exception HelpText of string

[<Sealed>]
type ArgParser() = 
    static let getUsage specs u =  
      let sbuf = new System.Text.StringBuilder 100  
      let pstring (s:string) = sbuf.Append s |> ignore 
      let pendline s = pstring s; pstring "\n" 
      pendline u;
      List.iter (fun (arg:ArgInfo) -> 
        match arg.Name, arg.ArgType, arg.HelpText with
        | (s, (UnitArg _ | SetArg _ | ClearArg _), helpText) -> pstring "\t"; pstring s; pstring ": "; pendline helpText
        | (s, StringArg _, helpText) -> pstring "\t"; pstring s; pstring " <string>: "; pendline helpText
        | (s, IntArg _, helpText) -> pstring "\t"; pstring s; pstring " <int>: "; pendline helpText
        | (s, FloatArg _, helpText) ->  pstring "\t"; pstring s; pstring " <float>: "; pendline helpText
        | (s, RestArg _, helpText) -> pstring "\t"; pstring s; pstring " ...: "; pendline helpText)
        specs;
      pstring "\t"; pstring "--help"; pstring ": "; pendline "display this list of options";
      pstring "\t"; pstring "-help"; pstring ": "; pendline "display this list of options";
      sbuf.ToString()


    static member ParsePartial(cursor,argv,argSpecs:seq<ArgInfo>,?other,?usageText) =
        let other = defaultArg other (fun _ -> ())
        let usageText = defaultArg usageText ""
        let nargs = Array.length argv 
        incr cursor;
        let argSpecs = argSpecs |> Seq.toList
        let specs = argSpecs |> List.map (fun (arg:ArgInfo) -> arg.Name, arg.ArgType)
        while !cursor < nargs do
          let arg = argv.[!cursor] 
          let rec findMatchingArg args = 
            match args with
            | ((s, action) :: _) when s = arg -> 
               let getSecondArg () = 
                   if !cursor + 1 >= nargs then 
                     raise(Bad("option "+s+" needs an argument.\n"+getUsage argSpecs usageText));
                   argv.[!cursor+1] 
                 
               match action with 
               | UnitArg f -> 
                 f (); 
                 incr cursor
               | SetArg f ->
                 f := true; 
                 incr cursor
               | ClearArg f -> 
                 f := false; 
                 incr cursor
               | StringArg f-> 
                 let arg2 = getSecondArg() 
                 f arg2; 
                 cursor := !cursor + 2
               | IntArg f -> 
                 let arg2 = getSecondArg () 
                 let arg2 = try int32 arg2 with _ -> raise(Bad(getUsage argSpecs usageText)) in  
                 f arg2;
                 cursor := !cursor + 2;
               | FloatArg f -> 
                 let arg2 = getSecondArg() 
                 let arg2 = try float arg2 with _ -> raise(Bad(getUsage argSpecs usageText)) in 
                 f arg2; 
                 cursor := !cursor + 2;
               | RestArg f -> 
                 incr cursor;
                 while !cursor < nargs do
                     f (argv.[!cursor]);
                     incr cursor;

            | (_ :: more)  -> findMatchingArg more 
            | [] -> 
                if arg = "-help" || arg = "--help" || arg = "/help" || arg = "/help" || arg = "/?" then
                    raise (HelpText (getUsage argSpecs usageText))
                // Note: for '/abc/def' does not count as an argument
                // Note: '/abc' does
                elif arg.Length>0 && (arg.[0] = '-' || (arg.[0] = '/' && not (arg.Length > 1 && arg.[1..].Contains ("/")))) then
                    raise (Bad ("unrecognized argument: "+ arg + "\n" + getUsage argSpecs usageText))
                else 
                   other arg;
                   incr cursor
          findMatchingArg specs 

    static member Usage (specs,?usage) = 
        let usage = defaultArg usage ""
        System.Console.Error.WriteLine (getUsage (Seq.toList specs) usage)

    #if FX_NO_COMMAND_LINE_ARGS
    #else
    static member Parse (specs,?other,?usageText) = 
        let current = ref 0
        let argv = System.Environment.GetCommandLineArgs() 
        try ArgParser.ParsePartial (current, argv, specs, ?other=other, ?usageText=usageText)
        with 
          | Bad h 
          | HelpText h -> 
              System.Console.Error.WriteLine h; 
              System.Console.Error.Flush();  
              System.Environment.Exit(1); 
          | e -> 
              reraise()
    #endif