66// Copyright (c) 2013 Jared Grubb. All rights reserved.
77//
88
9+ #define NO_RAW_FOR_LOOPS 0
10+
911#include " docopt.h"
1012#include " docopt_util.h"
1113#include " docopt_private.h"
2426
2527using namespace docopt ;
2628
27- DocoptExitHelp::DocoptExitHelp ()
28- : std::runtime_error(" Docopt --help argument encountered" )
29- {}
30-
31- DocoptExitVersion::DocoptExitVersion ()
32- : std::runtime_error(" Docopt --version argument encountered" )
33- {}
34-
35- const char * value::kindAsString (Kind kind)
36- {
37- switch (kind) {
38- case Kind::Empty: return " empty" ;
39- case Kind::Bool: return " bool" ;
40- case Kind::Long: return " long" ;
41- case Kind::String: return " string" ;
42- case Kind::StringList: return " string-list" ;
43- }
44- return " unknown" ;
45- }
46-
47- void value::throwIfNotKind (Kind expected) const
48- {
49- if (kind == expected)
50- return ;
51-
52- std::string error = " Illegal cast to " ;
53- error += kindAsString (expected);
54- error += " ; type is actually " ;
55- error += kindAsString (kind);
56- throw std::runtime_error (std::move (error));
57- }
58-
5929std::ostream& docopt::operator <<(std::ostream& os, value const & val)
6030{
6131 if (val.isBool ()) {
@@ -89,255 +59,6 @@ std::ostream& docopt::operator<<(std::ostream& os, value const& val)
8959#pragma mark -
9060#pragma mark Pattern types
9161
92- std::vector<LeafPattern*> Pattern::leaves () {
93- std::vector<LeafPattern*> ret;
94- collect_leaves (ret);
95- return ret;
96- }
97-
98- bool Required::match (PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
99- {
100- auto l = left;
101- auto c = collected;
102-
103- for (auto const & pattern : fChildren ) {
104- bool ret = pattern->match (l, c);
105- if (!ret) {
106- // leave (left, collected) untouched
107- return false ;
108- }
109- }
110-
111- left = std::move (l);
112- collected = std::move (c);
113- return true ;
114- }
115-
116- bool LeafPattern::match (PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
117- {
118- auto match = single_match (left);
119- if (!match.second ) {
120- return false ;
121- }
122-
123- left.erase (left.begin ()+static_cast <std::ptrdiff_t >(match.first ));
124-
125- auto same_name = std::find_if (collected.begin (), collected.end (), [&](std::shared_ptr<LeafPattern> const & p) {
126- return p->name ()==name ();
127- });
128- if (getValue ().isLong ()) {
129- long val = 1 ;
130- if (same_name == collected.end ()) {
131- collected.push_back (match.second );
132- match.second ->setValue (value{val});
133- } else if ((**same_name).getValue ().isLong ()) {
134- val += (**same_name).getValue ().asLong ();
135- (**same_name).setValue (value{val});
136- } else {
137- (**same_name).setValue (value{val});
138- }
139- } else if (getValue ().isStringList ()) {
140- std::vector<std::string> val;
141- if (match.second ->getValue ().isString ()) {
142- val.push_back (match.second ->getValue ().asString ());
143- } else if (match.second ->getValue ().isStringList ()) {
144- val = match.second ->getValue ().asStringList ();
145- } else {
146- // / cant be!?
147- }
148-
149- if (same_name == collected.end ()) {
150- collected.push_back (match.second );
151- match.second ->setValue (value{val});
152- } else if ((**same_name).getValue ().isStringList ()) {
153- std::vector<std::string> const & list = (**same_name).getValue ().asStringList ();
154- val.insert (val.begin (), list.begin (), list.end ());
155- (**same_name).setValue (value{val});
156- } else {
157- (**same_name).setValue (value{val});
158- }
159- } else {
160- collected.push_back (match.second );
161- }
162- return true ;
163- }
164-
165- Option Option::parse (std::string const & option_description)
166- {
167- std::string shortOption, longOption;
168- int argcount = 0 ;
169- value val { false };
170-
171- auto double_space = option_description.find (" " );
172- auto options_end = option_description.end ();
173- if (double_space != std::string::npos) {
174- options_end = option_description.begin () + static_cast <std::ptrdiff_t >(double_space);
175- }
176-
177- static const std::regex pattern {" (-{1,2})?(.*?)([,= ]|$)" };
178- for (std::sregex_iterator i {option_description.begin (), options_end, pattern, std::regex_constants::match_not_null},
179- e{};
180- i != e;
181- ++i)
182- {
183- std::smatch const & match = *i;
184- if (match[1 ].matched ) { // [1] is optional.
185- if (match[1 ].length ()==1 ) {
186- shortOption = " -" + match[2 ].str ();
187- } else {
188- longOption = " --" + match[2 ].str ();
189- }
190- } else if (match[2 ].length () > 0 ) { // [2] always matches.
191- std::string m = match[2 ];
192- argcount = 1 ;
193- } else {
194- // delimeter
195- }
196-
197- if (match[3 ].length () == 0 ) { // [3] always matches.
198- // Hit end of string. For some reason 'match_not_null' will let us match empty
199- // at the end, and then we'll spin in an infinite loop. So, if we hit an empty
200- // match, we know we must be at the end.
201- break ;
202- }
203- }
204-
205- if (argcount) {
206- std::smatch match;
207- if (std::regex_search (options_end, option_description.end (),
208- match,
209- std::regex{" \\ [default: (.*)\\ ]" , std::regex::icase}))
210- {
211- val = match[1 ].str ();
212- }
213- }
214-
215- return {std::move (shortOption),
216- std::move (longOption),
217- argcount,
218- std::move (val)};
219- }
220-
221- bool OneOrMore::match (PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
222- {
223- assert (fChildren .size () == 1 );
224-
225- auto l = left;
226- auto c = collected;
227-
228- bool matched = true ;
229- size_t times = 0 ;
230-
231- decltype (l) l_;
232- bool firstLoop = true ;
233-
234- while (matched) {
235- // could it be that something didn't match but changed l or c?
236- matched = fChildren [0 ]->match (l, c);
237-
238- if (matched)
239- ++times;
240-
241- if (firstLoop) {
242- firstLoop = false ;
243- } else if (l == l_) {
244- break ;
245- }
246-
247- l_ = l;
248- }
249-
250- if (times == 0 ) {
251- return false ;
252- }
253-
254- left = std::move (l);
255- collected = std::move (c);
256- return true ;
257- }
258-
259- bool Either::match (PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
260- {
261- using Outcome = std::pair<PatternList, std::vector<std::shared_ptr<LeafPattern>>>;
262-
263- std::vector<Outcome> outcomes;
264-
265- for (auto const & pattern : fChildren ) {
266- // need a copy so we apply the same one for every iteration
267- auto l = left;
268- auto c = collected;
269- bool matched = pattern->match (l, c);
270- if (matched) {
271- outcomes.emplace_back (std::move (l), std::move (c));
272- }
273- }
274-
275- auto min = std::min_element (outcomes.begin (), outcomes.end (), [](Outcome const & o1, Outcome const & o2) {
276- return o1.first .size () < o2.first .size ();
277- });
278-
279- if (min == outcomes.end ()) {
280- // (left, collected) unchanged
281- return false ;
282- }
283-
284- std::tie (left, collected) = std::move (*min);
285- return true ;
286- }
287-
288- std::pair<size_t , std::shared_ptr<LeafPattern>> Argument::single_match (PatternList const & left) const
289- {
290- std::pair<size_t , std::shared_ptr<LeafPattern>> ret {};
291-
292- for (size_t i = 0 , size = left.size (); i < size; ++i)
293- {
294- auto arg = dynamic_cast <Argument const *>(left[i].get ());
295- if (arg) {
296- ret.first = i;
297- ret.second = std::make_shared<Argument>(name (), arg->getValue ());
298- break ;
299- }
300- }
301-
302- return ret;
303- }
304-
305- std::pair<size_t , std::shared_ptr<LeafPattern>> Command::single_match (PatternList const & left) const
306- {
307- std::pair<size_t , std::shared_ptr<LeafPattern>> ret {};
308-
309- for (size_t i = 0 , size = left.size (); i < size; ++i)
310- {
311- auto arg = dynamic_cast <Argument const *>(left[i].get ());
312- if (arg) {
313- if (name () == arg->getValue ()) {
314- ret.first = i;
315- ret.second = std::make_shared<Command>(name (), value{true });
316- }
317- break ;
318- }
319- }
320-
321- return ret;
322- }
323-
324- std::pair<size_t , std::shared_ptr<LeafPattern>> Option::single_match (PatternList const & left) const
325- {
326- std::pair<size_t , std::shared_ptr<LeafPattern>> ret {};
327-
328- for (size_t i = 0 , size = left.size (); i < size; ++i)
329- {
330- auto leaf = std::dynamic_pointer_cast<LeafPattern>(left[i]);
331- if (leaf && name () == leaf->name ()) {
332- ret.first = i;
333- ret.second = leaf;
334- break ;
335- }
336- }
337-
338- return ret;
339- }
340-
34162#pragma mark -
34263#pragma mark Parsing stuff
34364
@@ -803,20 +524,13 @@ static PatternList parse_seq(Tokens& tokens, std::vector<Option>& options)
803524 return ret;
804525}
805526
806- static std::shared_ptr<Pattern> maybe_collapse_to_required (PatternList&& seq)
807- {
808- if (seq.size ()==1 ) {
809- return std::move (seq[0 ]);
810- }
811- return std::make_shared<Required>(std::move (seq));
812- }
813-
814- static std::shared_ptr<Pattern> maybe_collapse_to_either (PatternList&& seq)
527+ template <typename Which>
528+ std::shared_ptr<Pattern> maybe_collapse_to (PatternList&& seq)
815529{
816530 if (seq.size ()==1 ) {
817531 return std::move (seq[0 ]);
818532 }
819- return std::make_shared<Either >(std::move (seq));
533+ return std::make_shared<Which >(std::move (seq));
820534}
821535
822536PatternList parse_expr (Tokens& tokens, std::vector<Option>& options)
@@ -829,15 +543,15 @@ PatternList parse_expr(Tokens& tokens, std::vector<Option>& options)
829543 return seq;
830544
831545 PatternList ret;
832- ret.emplace_back (maybe_collapse_to_required (std::move (seq)));
546+ ret.emplace_back (maybe_collapse_to<Required> (std::move (seq)));
833547
834548 while (tokens.current () == " |" ) {
835549 tokens.pop ();
836550 seq = parse_seq (tokens, options);
837- ret.emplace_back (maybe_collapse_to_required (std::move (seq)));
551+ ret.emplace_back (maybe_collapse_to<Required> (std::move (seq)));
838552 }
839553
840- return { maybe_collapse_to_either (std::move (ret)) };
554+ return { maybe_collapse_to<Either> (std::move (ret)) };
841555}
842556
843557static Required parse_pattern (std::string const & source, std::vector<Option>& options)
@@ -1001,6 +715,7 @@ static std::pair<Required, std::vector<Option>> create_pattern_tree(std::string
1001715 return { std::move (pattern), std::move (options) };
1002716}
1003717
718+ DOCOPT_INLINE
1004719std::map<std::string, value>
1005720docopt::docopt_parse (std::string const & doc,
1006721 std::vector<std::string> const & argv,
@@ -1050,6 +765,7 @@ docopt::docopt_parse(std::string const& doc,
1050765 throw DocoptArgumentError (" Arguments did not match expected patterns" ); // BLEH. Bad error.
1051766}
1052767
768+ DOCOPT_INLINE
1053769std::map<std::string, value>
1054770docopt::docopt (std::string const & doc,
1055771 std::vector<std::string> const & argv,
0 commit comments