6
6
// Copyright (c) 2013 Jared Grubb. All rights reserved.
7
7
//
8
8
9
- #define NO_RAW_FOR_LOOPS 0
10
-
11
9
#include " docopt.h"
12
10
#include " docopt_util.h"
13
11
#include " docopt_private.h"
26
24
27
25
using namespace docopt ;
28
26
27
+ const char * value::kindAsString (Kind kind)
28
+ {
29
+ switch (kind) {
30
+ case Kind::Empty: return " empty" ;
31
+ case Kind::Bool: return " bool" ;
32
+ case Kind::Long: return " long" ;
33
+ case Kind::String: return " string" ;
34
+ case Kind::StringList: return " string-list" ;
35
+ }
36
+ return " unknown" ;
37
+ }
38
+
39
+ void value::throwIfNotKind (Kind expected) const
40
+ {
41
+ if (kind == expected)
42
+ return ;
43
+
44
+ std::string error = " Illegal cast to " ;
45
+ error += kindAsString (expected);
46
+ error += " ; type is actually " ;
47
+ error += kindAsString (kind);
48
+ throw std::runtime_error (std::move (error));
49
+ }
50
+
29
51
std::ostream& docopt::operator <<(std::ostream& os, value const & val)
30
52
{
31
53
if (val.isBool ()) {
@@ -59,6 +81,255 @@ std::ostream& docopt::operator<<(std::ostream& os, value const& val)
59
81
#pragma mark -
60
82
#pragma mark Pattern types
61
83
84
+ std::vector<LeafPattern*> Pattern::leaves () {
85
+ std::vector<LeafPattern*> ret;
86
+ collect_leaves (ret);
87
+ return ret;
88
+ }
89
+
90
+ bool Required::match (PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
91
+ {
92
+ auto l = left;
93
+ auto c = collected;
94
+
95
+ for (auto const & pattern : fChildren ) {
96
+ bool ret = pattern->match (l, c);
97
+ if (!ret) {
98
+ // leave (left, collected) untouched
99
+ return false ;
100
+ }
101
+ }
102
+
103
+ left = std::move (l);
104
+ collected = std::move (c);
105
+ return true ;
106
+ }
107
+
108
+ bool LeafPattern::match (PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
109
+ {
110
+ auto match = single_match (left);
111
+ if (!match.second ) {
112
+ return false ;
113
+ }
114
+
115
+ left.erase (left.begin ()+static_cast <std::ptrdiff_t >(match.first ));
116
+
117
+ auto same_name = std::find_if (collected.begin (), collected.end (), [&](std::shared_ptr<LeafPattern> const & p) {
118
+ return p->name ()==name ();
119
+ });
120
+ if (getValue ().isLong ()) {
121
+ long val = 1 ;
122
+ if (same_name == collected.end ()) {
123
+ collected.push_back (match.second );
124
+ match.second ->setValue (value{val});
125
+ } else if ((**same_name).getValue ().isLong ()) {
126
+ val += (**same_name).getValue ().asLong ();
127
+ (**same_name).setValue (value{val});
128
+ } else {
129
+ (**same_name).setValue (value{val});
130
+ }
131
+ } else if (getValue ().isStringList ()) {
132
+ std::vector<std::string> val;
133
+ if (match.second ->getValue ().isString ()) {
134
+ val.push_back (match.second ->getValue ().asString ());
135
+ } else if (match.second ->getValue ().isStringList ()) {
136
+ val = match.second ->getValue ().asStringList ();
137
+ } else {
138
+ // / cant be!?
139
+ }
140
+
141
+ if (same_name == collected.end ()) {
142
+ collected.push_back (match.second );
143
+ match.second ->setValue (value{val});
144
+ } else if ((**same_name).getValue ().isStringList ()) {
145
+ std::vector<std::string> const & list = (**same_name).getValue ().asStringList ();
146
+ val.insert (val.begin (), list.begin (), list.end ());
147
+ (**same_name).setValue (value{val});
148
+ } else {
149
+ (**same_name).setValue (value{val});
150
+ }
151
+ } else {
152
+ collected.push_back (match.second );
153
+ }
154
+ return true ;
155
+ }
156
+
157
+ Option Option::parse (std::string const & option_description)
158
+ {
159
+ std::string shortOption, longOption;
160
+ int argcount = 0 ;
161
+ value val { false };
162
+
163
+ auto double_space = option_description.find (" " );
164
+ auto options_end = option_description.end ();
165
+ if (double_space != std::string::npos) {
166
+ options_end = option_description.begin () + static_cast <std::ptrdiff_t >(double_space);
167
+ }
168
+
169
+ static const std::regex pattern {" (-{1,2})?(.*?)([,= ]|$)" };
170
+ for (std::sregex_iterator i {option_description.begin (), options_end, pattern, std::regex_constants::match_not_null},
171
+ e{};
172
+ i != e;
173
+ ++i)
174
+ {
175
+ std::smatch const & match = *i;
176
+ if (match[1 ].matched ) { // [1] is optional.
177
+ if (match[1 ].length ()==1 ) {
178
+ shortOption = " -" + match[2 ].str ();
179
+ } else {
180
+ longOption = " --" + match[2 ].str ();
181
+ }
182
+ } else if (match[2 ].length () > 0 ) { // [2] always matches.
183
+ std::string m = match[2 ];
184
+ argcount = 1 ;
185
+ } else {
186
+ // delimeter
187
+ }
188
+
189
+ if (match[3 ].length () == 0 ) { // [3] always matches.
190
+ // Hit end of string. For some reason 'match_not_null' will let us match empty
191
+ // at the end, and then we'll spin in an infinite loop. So, if we hit an empty
192
+ // match, we know we must be at the end.
193
+ break ;
194
+ }
195
+ }
196
+
197
+ if (argcount) {
198
+ std::smatch match;
199
+ if (std::regex_search (options_end, option_description.end (),
200
+ match,
201
+ std::regex{" \\ [default: (.*)\\ ]" , std::regex::icase}))
202
+ {
203
+ val = match[1 ].str ();
204
+ }
205
+ }
206
+
207
+ return {std::move (shortOption),
208
+ std::move (longOption),
209
+ argcount,
210
+ std::move (val)};
211
+ }
212
+
213
+ bool OneOrMore::match (PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
214
+ {
215
+ assert (fChildren .size () == 1 );
216
+
217
+ auto l = left;
218
+ auto c = collected;
219
+
220
+ bool matched = true ;
221
+ size_t times = 0 ;
222
+
223
+ decltype (l) l_;
224
+ bool firstLoop = true ;
225
+
226
+ while (matched) {
227
+ // could it be that something didn't match but changed l or c?
228
+ matched = fChildren [0 ]->match (l, c);
229
+
230
+ if (matched)
231
+ ++times;
232
+
233
+ if (firstLoop) {
234
+ firstLoop = false ;
235
+ } else if (l == l_) {
236
+ break ;
237
+ }
238
+
239
+ l_ = l;
240
+ }
241
+
242
+ if (times == 0 ) {
243
+ return false ;
244
+ }
245
+
246
+ left = std::move (l);
247
+ collected = std::move (c);
248
+ return true ;
249
+ }
250
+
251
+ bool Either::match (PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
252
+ {
253
+ using Outcome = std::pair<PatternList, std::vector<std::shared_ptr<LeafPattern>>>;
254
+
255
+ std::vector<Outcome> outcomes;
256
+
257
+ for (auto const & pattern : fChildren ) {
258
+ // need a copy so we apply the same one for every iteration
259
+ auto l = left;
260
+ auto c = collected;
261
+ bool matched = pattern->match (l, c);
262
+ if (matched) {
263
+ outcomes.emplace_back (std::move (l), std::move (c));
264
+ }
265
+ }
266
+
267
+ auto min = std::min_element (outcomes.begin (), outcomes.end (), [](Outcome const & o1, Outcome const & o2) {
268
+ return o1.first .size () < o2.first .size ();
269
+ });
270
+
271
+ if (min == outcomes.end ()) {
272
+ // (left, collected) unchanged
273
+ return false ;
274
+ }
275
+
276
+ std::tie (left, collected) = std::move (*min);
277
+ return true ;
278
+ }
279
+
280
+ std::pair<size_t , std::shared_ptr<LeafPattern>> Argument::single_match (PatternList const & left) const
281
+ {
282
+ std::pair<size_t , std::shared_ptr<LeafPattern>> ret {};
283
+
284
+ for (size_t i = 0 , size = left.size (); i < size; ++i)
285
+ {
286
+ auto arg = dynamic_cast <Argument const *>(left[i].get ());
287
+ if (arg) {
288
+ ret.first = i;
289
+ ret.second = std::make_shared<Argument>(name (), arg->getValue ());
10000
290
+ break ;
291
+ }
292
+ }
293
+
294
+ return ret;
295
+ }
296
+
297
+ std::pair<size_t , std::shared_ptr<LeafPattern>> Command::single_match (PatternList const & left) const
298
+ {
299
+ std::pair<size_t , std::shared_ptr<LeafPattern>> ret {};
300
+
301
+ for (size_t i = 0 , size = left.size (); i < size; ++i)
302
+ {
303
+ auto arg = dynamic_cast <Argument const *>(left[i].get ());
304
+ if (arg) {
305
+ if (name () == arg->getValue ()) {
306
+ ret.first = i;
307
+ ret.second = std::make_shared<Command>(name (), value{true });
308
+ }
309
+ break ;
310
+ }
311
+ }
312
+
313
+ return ret;
314
+ }
315
+
316
+ std::pair<size_t , std::shared_ptr<LeafPattern>> Option::single_match (PatternList const & left) const
317
+ {
318
+ std::pair<size_t , std::shared_ptr<LeafPattern>> ret {};
319
+
320
+ for (size_t i = 0 , size = left.size (); i < size; ++i)
321
+ {
322
+ auto leaf = std::dynamic_pointer_cast<LeafPattern>(left[i]);
323
+ if (leaf && name () == leaf->name ()) {
324
+ ret.first = i;
325
+ ret.second = leaf;
326
+ break ;
327
+ }
328
+ }
329
+
330
+ return ret;
331
+ }
332
+
62
333
#pragma mark -
63
334
#pragma mark Parsing stuff
64
335
@@ -524,13 +795,20 @@ static PatternList parse_seq(Tokens& tokens, std::vector<Option>& options)
524
795
return ret;
525
796
}
526
797
527
- template <typename Which>
528
- std::shared_ptr<Pattern> maybe_collapse_to (PatternList&& seq)
798
+ static std::shared_ptr<Pattern> maybe_collapse_to_required (PatternList&& seq)
799
+ {
800
+ if (seq.size ()==1 ) {
801
+ return std::move (seq[0 ]);
802
+ }
803
+ return std::make_shared<Required>(std::move (seq));
804
+ }
805
+
806
+ static std::shared_ptr<Pattern> maybe_collapse_to_either (PatternList&& seq)
529
807
{
530
808
if (seq.size ()==1 ) {
531
809
return std::move (seq[0 ]);
532
810
}
533
- return std::make_shared<Which >(std::move (seq));
811
+ return std::make_shared<Either >(std::move (seq));
534
812
}
535
813
536
814
PatternList parse_expr (Tokens& tokens, std::vector<Option>& options)
@@ -543,15 +821,15 @@ PatternList parse_expr(Tokens& tokens, std::vector<Option>& options)
543
821
return seq;
544
822
545
823
PatternList ret;
546
- ret.emplace_back (maybe_collapse_to<Required> (std::move (seq)));
824
+ ret.emplace_back (maybe_collapse_to_required (std::move (seq)));
547
825
548
826
while (tokens.current () == " |" ) {
549
827
tokens.pop ();
550
828
seq = parse_seq (tokens, options);
551
- ret.emplace_back (maybe_collapse_to<Required> (std::move (seq)));
829
+ ret.emplace_back (maybe_collapse_to_required (std::move (seq)));
552
830
}
553
831
554
- return { maybe_collapse_to<Either> (std::move (ret)) };
832
+ return { maybe_collapse_to_either (std::move (ret)) };
555
833
}
556
834
557
835
static Required parse_pattern (std::string const & source, std::vector<Option>& options)
0 commit comments