|
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