66// Copyright (c) 2013 Jared Grubb. All rights reserved.
77//
88
9- #define NO_RAW_FOR_LOOPS 0
10-
119#include " docopt.h"
1210#include " docopt_util.h"
1311#include " docopt_private.h"
2624
2725using namespace docopt ;
2826
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+
2951std::ostream& docopt::operator <<(std::ostream& os, value const & val)
3052{
3153 if (val.isBool ()) {
@@ -59,6 +81,255 @@ std::ostream& docopt::operator<<(std::ostream& os, value const& val)
5981#pragma mark -
6082#pragma mark Pattern types
6183
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 ());
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+
62333#pragma mark -
63334#pragma mark Parsing stuff
64335
@@ -524,13 +795,20 @@ static PatternList parse_seq(Tokens& tokens, std::vector<Option>& options)
524795 return ret;
525796}
526797
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)
529807{
530808 if (seq.size ()==1 ) {
531809 return std::move (seq[0 ]);
532810 }
533- return std::make_shared<Which >(std::move (seq));
811+ return std::make_shared<Either >(std::move (seq));
534812}
535813
536814PatternList parse_expr (Tokens& tokens, std::vector<Option>& options)
@@ -543,15 +821,15 @@ PatternList parse_expr(Tokens& tokens, std::vector<Option>& options)
543821 return seq;
544822
545823 PatternList ret;
546- ret.emplace_back (maybe_collapse_to<Required> (std::move (seq)));
824+ ret.emplace_back (maybe_collapse_to_required (std::move (seq)));
547825
548826 while (tokens.current () == " |" ) {
549827 tokens.pop ();
550828 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)));
552830 }
553831
554- return { maybe_collapse_to<Either> (std::move (ret)) };
832+ return { maybe_collapse_to_either (std::move (ret)) };
555833}
556834
557835static Required parse_pattern (std::string const & source, std::vector<Option>& options)
0 commit comments