|
1 | 1 |
|
2 | 2 | type key = string
|
3 | 3 | type doc = string
|
4 |
| -type usage_msg = string |
5 |
| -type anon_fun = (string -> unit) |
| 4 | +type anon_fun = rev_args:string list -> unit |
| 5 | + |
| 6 | +type string_action = |
| 7 | + | Call of (string -> unit) |
| 8 | + | Set of {mutable contents : string} |
6 | 9 |
|
7 | 10 | type spec =
|
8 |
| - | Unit of (unit -> unit) |
9 |
| - | Set of bool ref |
10 |
| - | String of (string -> unit) |
11 |
| - | Set_string of string ref |
12 |
| - | Int of (int -> unit) |
13 |
| - | Set_int of int ref |
| 11 | + | Bool of bool ref |
| 12 | + | String of string_action |
| 13 | + |
14 | 14 |
|
15 | 15 | exception Bad of string
|
16 |
| -(* exception Help of string *) |
| 16 | + |
17 | 17 |
|
18 | 18 | type error =
|
19 | 19 | | Unknown of string
|
20 |
| - | Wrong of string * string * string (* option, actual, expected *) |
21 | 20 | | Missing of string
|
22 |
| - | Message of string |
23 |
| - |
24 |
| -exception Stop of error |
25 |
| - |
26 | 21 |
|
27 | 22 | type t = (string * spec * string) list
|
28 | 23 |
|
29 | 24 | let rec assoc3 (x : string) (l : t) =
|
30 | 25 | match l with
|
31 | 26 | | [] -> None
|
32 |
| - | (y1, y2, _y3) :: _t when y1 = x -> Some y2 |
| 27 | + | (y1, y2, _) :: _ when y1 = x -> Some y2 |
33 | 28 | | _ :: t -> assoc3 x t
|
34 | 29 | ;;
|
35 | 30 |
|
36 | 31 |
|
37 |
| - |
38 |
| -let usage_b (buf : Ext_buffer.t) speclist errmsg = |
39 |
| - let print_spec buf (key, _spec, doc) = |
40 |
| - if doc <> "" then begin |
41 |
| - Ext_buffer.add_string buf " "; |
42 |
| - Ext_buffer.add_string_char buf key ' '; |
43 |
| - Ext_buffer.add_string_char buf doc '\n' |
44 |
| - end |
45 |
| - in |
46 |
| - |
47 |
| - Ext_buffer.add_string_char buf errmsg '\n'; |
48 |
| - Ext_list.iter speclist (print_spec buf) |
| 32 | +let (+>) = Ext_buffer.add_string |
| 33 | + |
| 34 | +let usage_b (buf : Ext_buffer.t) progname speclist = |
| 35 | + buf +> progname; |
| 36 | + buf +> " options:\n"; |
| 37 | + let max_col = ref 0 in |
| 38 | + Ext_list.iter speclist (fun (key,_,_) -> |
| 39 | + if String.length key > !max_col then |
| 40 | + max_col := String.length key |
| 41 | + ); |
| 42 | + Ext_list.iter speclist (fun (key,_,doc) -> |
| 43 | + buf +> " "; |
| 44 | + buf +> key ; |
| 45 | + buf +> (String.make (!max_col - String.length key + 1 ) ' '); |
| 46 | + buf +> doc; |
| 47 | + buf +> "\n" |
| 48 | + ) |
49 | 49 | ;;
|
50 | 50 |
|
51 | 51 |
|
52 | 52 |
|
53 |
| -let stop_raise progname (error : error) speclist errmsg = |
| 53 | +let stop_raise ~progname ~(error : error) speclist = |
54 | 54 | let b = Ext_buffer.create 200 in
|
55 | 55 | begin match error with
|
56 | 56 | | Unknown ("-help" | "--help" | "-h") ->
|
57 |
| - usage_b b speclist errmsg; |
58 |
| - output_string stdout (Ext_buffer.contents b); |
59 |
| - exit 0 |
60 |
| - |
| 57 | + usage_b b progname speclist ; |
| 58 | + Ext_buffer.output_buffer stdout b; |
| 59 | + exit 0 |
61 | 60 | | Unknown s ->
|
62 |
| - Ext_buffer.add_string_char b progname ':'; |
63 |
| - Ext_buffer.add_string b " unknown option '"; |
64 |
| - Ext_buffer.add_string b s ; |
65 |
| - Ext_buffer.add_string b "'.\n" |
| 61 | + b +> progname ; |
| 62 | + b +> ": unknown option '"; |
| 63 | + b +> s ; |
| 64 | + b +> "'.\n" |
66 | 65 | | Missing s ->
|
67 |
| - Ext_buffer.add_string_char b progname ':'; |
68 |
| - Ext_buffer.add_string b " option '"; |
69 |
| - Ext_buffer.add_string b s; |
70 |
| - Ext_buffer.add_string b "' needs an argument.\n" |
71 |
| - | Wrong (opt, arg, expected) -> |
72 |
| - Ext_buffer.add_string_char b progname ':'; |
73 |
| - Ext_buffer.add_string b " wrong argument '"; |
74 |
| - Ext_buffer.add_string b arg; |
75 |
| - Ext_buffer.add_string b "'; option '"; |
76 |
| - Ext_buffer.add_string b opt; |
77 |
| - Ext_buffer.add_string b "' expects "; |
78 |
| - Ext_buffer.add_string b expected; |
79 |
| - Ext_buffer.add_string b ".\n" |
80 |
| - | Message s -> |
81 |
| - Ext_buffer.add_string_char b progname ':'; |
82 |
| - Ext_buffer.add_char_string b ' ' s; |
83 |
| - Ext_buffer.add_string b ".\n" |
| 66 | + b +> progname ; |
| 67 | + b +> ": option '"; |
| 68 | + b +> s; |
| 69 | + b +> "' needs an argument.\n" |
84 | 70 | end;
|
85 |
| - usage_b b speclist errmsg; |
| 71 | + usage_b b progname speclist ; |
86 | 72 | raise (Bad (Ext_buffer.contents b))
|
87 | 73 |
|
88 | 74 |
|
89 |
| -let parse_exn (speclist : t) anonfun errmsg = |
90 |
| - let argv = Sys.argv in |
91 |
| - let stop_raise error = stop_raise argv.(0) error speclist errmsg in |
| 75 | +let parse_exn ~progname ~argv ~start (speclist : t) anonfun = |
92 | 76 | let l = Array.length argv in
|
93 |
| - let current = ref 1 in (* 0 is progname*) |
| 77 | + let current = ref start in |
| 78 | + let rev_list = ref [] in |
94 | 79 | while !current < l do
|
95 | 80 | let s = argv.(!current) in
|
| 81 | + incr current; |
96 | 82 | if s <> "" && s.[0] = '-' then begin
|
97 |
| - let action = |
98 |
| - match assoc3 s speclist with |
99 |
| - | Some action -> action |
100 |
| - | None -> stop_raise (Unknown s) |
101 |
| - in |
102 |
| - begin try |
103 |
| - let treat_action = function |
104 |
| - | Unit f -> f (); |
105 |
| - | Set r -> r := true; |
106 |
| - | String f when !current + 1 < l -> |
107 |
| - f argv.(!current + 1); |
108 |
| - incr current; |
109 |
| - | Set_string r when !current + 1 < l -> |
110 |
| - r := argv.(!current + 1); |
111 |
| - incr current; |
112 |
| - | Int f when !current + 1 < l -> |
113 |
| - let arg = argv.(!current + 1) in |
114 |
| - begin match int_of_string arg with |
115 |
| - | i -> f i |
116 |
| - | exception _ |
117 |
| - -> |
118 |
| - raise (Stop (Wrong (s, arg, "an integer"))) |
119 |
| - end; |
120 |
| - incr current; |
121 |
| - | Set_int r when !current + 1 < l -> |
122 |
| - let arg = argv.(!current + 1) in |
123 |
| - r := (try int_of_string arg |
124 |
| - with _ -> |
125 |
| - raise (Stop (Wrong (s, arg, "an integer"))) |
126 |
| - ); |
127 |
| - incr current; |
128 |
| - | _ -> raise (Stop (Missing s)) |
129 |
| - in |
130 |
| - treat_action action |
131 |
| - with Bad m -> stop_raise (Message m); |
132 |
| - | Stop e -> stop_raise e; |
133 |
| - end; |
134 |
| - incr current; |
| 83 | + match assoc3 s speclist with |
| 84 | + | Some action -> begin |
| 85 | + begin match action with |
| 86 | + | Bool r -> r := true; |
| 87 | + | String f -> |
| 88 | + if !current >= l then stop_raise ~progname ~error:(Missing s) speclist |
| 89 | + else begin |
| 90 | + let arg = argv.(!current) in |
| 91 | + incr current; |
| 92 | + match f with |
| 93 | + | Call f -> |
| 94 | + f arg |
| 95 | + | Set u -> u.contents <- arg |
| 96 | + end |
| 97 | + end; |
| 98 | + end; |
| 99 | + | None -> stop_raise ~progname ~error:(Unknown s) speclist |
135 | 100 | end else begin
|
136 |
| - (try anonfun s with Bad m -> stop_raise (Message m)); |
137 |
| - incr current; |
| 101 | + rev_list := s :: !rev_list; |
138 | 102 | end;
|
139 | 103 | done;
|
| 104 | + anonfun ~rev_args:!rev_list |
140 | 105 | ;;
|
141 | 106 |
|
142 | 107 |
|
143 |
| - |
144 |
| -(* let parse l f msg = |
145 |
| - try |
146 |
| - parse_exn l f msg; |
147 |
| - with |
148 |
| - | Bad msg -> |
149 |
| - output_string stderr msg ; exit 2; |
150 |
| - | Help msg -> |
151 |
| - output_string stdout msg; exit 0; |
152 |
| -;; |
153 |
| - *) |
0 commit comments