Janis Streib
19.05.23 b21ff09151ca7e87348f1ea38944decc1f46f5db
commit | author | age
e9b870 1 /*****************************************************************************
JS 2  *  ___  _    _   ___ ___
3  * |  _|| |  | | | _ \ _ \   CLIPP - command line interfaces for modern C++
4  * | |_ | |_ | | |  _/  _/   version 1.2.3
5  * |___||___||_| |_| |_|     https://github.com/muellan/clipp
6  *
7  * Licensed under the MIT License <http://opensource.org/licenses/MIT>.
8  * Copyright (c) 2017-2018 André Müller <foss@andremueller-online.de>
9  *
10  * ---------------------------------------------------------------------------
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included in
19  * all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
25  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
26  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27  * OTHER DEALINGS IN THE SOFTWARE.
28  *
29  *****************************************************************************/
30
31 #ifndef AM_CLIPP_H__
32 #define AM_CLIPP_H__
33
34 #include <cstring>
35 #include <string>
36 #include <cstdlib>
37 #include <cstring>
38 #include <cctype>
39 #include <memory>
40 #include <vector>
41 #include <limits>
42 #include <stack>
43 #include <algorithm>
44 #include <sstream>
45 #include <utility>
46 #include <iterator>
47 #include <functional>
48
49
50 /*************************************************************************//**
51  *
52  * @brief primary namespace
53  *
54  *****************************************************************************/
55 namespace clipp {
56
57
58
59 /*****************************************************************************
60  *
61  * basic constants and datatype definitions
62  *
63  *****************************************************************************/
64 using arg_index = int;
65
66 using arg_string = std::string;
67 using doc_string = std::string;
68
69 using arg_list  = std::vector<arg_string>;
70
71
72
73 /*************************************************************************//**
74  *
75  * @brief tristate
76  *
77  *****************************************************************************/
78 enum class tri : char { no, yes, either };
79
80 inline constexpr bool operator == (tri t, bool b) noexcept {
81     return b ? t != tri::no : t != tri::yes;
82 }
83 inline constexpr bool operator == (bool b, tri t) noexcept { return  (t == b); }
84 inline constexpr bool operator != (tri t, bool b) noexcept { return !(t == b); }
85 inline constexpr bool operator != (bool b, tri t) noexcept { return !(t == b); }
86
87
88
89 /*************************************************************************//**
90  *
91  * @brief (start,size) index range
92  *
93  *****************************************************************************/
94 class subrange {
95 public:
96     using size_type = arg_string::size_type;
97
98     /** @brief default: no match */
99     explicit constexpr
100     subrange() noexcept :
101         at_{arg_string::npos}, length_{0}
102     {}
103
104     /** @brief match length & position within subject string */
105     explicit constexpr
106     subrange(size_type pos, size_type len) noexcept :
107         at_{pos}, length_{len}
108     {}
109
110     /** @brief position of the match within the subject string */
111     constexpr size_type at()     const noexcept { return at_; }
112     /** @brief length of the matching subsequence */
113     constexpr size_type length() const noexcept { return length_; }
114
115     /** @brief returns true, if query string is a prefix of the subject string */
116     constexpr bool prefix() const noexcept {
117         return at_ == 0;
118     }
119
120     /** @brief returns true, if query is a substring of the query string */
121     constexpr explicit operator bool () const noexcept {
122         return at_ != arg_string::npos;
123     }
124
125 private:
126     size_type at_;
127     size_type length_;
128 };
129
130
131
132 /*************************************************************************//**
133  *
134  * @brief match predicates
135  *
136  *****************************************************************************/
137 using match_predicate = std::function<bool(const arg_string&)>;
138 using match_function  = std::function<subrange(const arg_string&)>;
139
140
141
142
143
144
145 /*************************************************************************//**
146  *
147  * @brief type traits (NOT FOR DIRECT USE IN CLIENT CODE!)
148  *        no interface guarantees; might be changed or removed in the future
149  *
150  *****************************************************************************/
151 namespace traits {
152
153 /*************************************************************************//**
154  *
155  * @brief function (class) signature type trait
156  *
157  *****************************************************************************/
158 template<class Fn, class Ret, class... Args>
159 constexpr auto
160 check_is_callable(int) -> decltype(
161     std::declval<Fn>()(std::declval<Args>()...),
162     std::integral_constant<bool,
163         std::is_same<Ret,typename std::result_of<Fn(Args...)>::type>::value>{} );
164
165 template<class,class,class...>
166 constexpr auto
167 check_is_callable(long) -> std::false_type;
168
169 template<class Fn, class Ret>
170 constexpr auto
171 check_is_callable_without_arg(int) -> decltype(
172     std::declval<Fn>()(),
173     std::integral_constant<bool,
174         std::is_same<Ret,typename std::result_of<Fn()>::type>::value>{} );
175
176 template<class,class>
177 constexpr auto
178 check_is_callable_without_arg(long) -> std::false_type;
179
180
181
182 template<class Fn, class... Args>
183 constexpr auto
184 check_is_void_callable(int) -> decltype(
185     std::declval<Fn>()(std::declval<Args>()...), std::true_type{});
186
187 template<class,class,class...>
188 constexpr auto
189 check_is_void_callable(long) -> std::false_type;
190
191 template<class Fn>
192 constexpr auto
193 check_is_void_callable_without_arg(int) -> decltype(
194     std::declval<Fn>()(), std::true_type{});
195
196 template<class>
197 constexpr auto
198 check_is_void_callable_without_arg(long) -> std::false_type;
199
200
201
202 template<class Fn, class Ret>
203 struct is_callable;
204
205
206 template<class Fn, class Ret, class... Args>
207 struct is_callable<Fn, Ret(Args...)> :
208     decltype(check_is_callable<Fn,Ret,Args...>(0))
209 {};
210
211 template<class Fn, class Ret>
212 struct is_callable<Fn,Ret()> :
213     decltype(check_is_callable_without_arg<Fn,Ret>(0))
214 {};
215
216
217 template<class Fn, class... Args>
218 struct is_callable<Fn, void(Args...)> :
219     decltype(check_is_void_callable<Fn,Args...>(0))
220 {};
221
222 template<class Fn>
223 struct is_callable<Fn,void()> :
224     decltype(check_is_void_callable_without_arg<Fn>(0))
225 {};
226
227
228
229 /*************************************************************************//**
230  *
231  * @brief input range type trait
232  *
233  *****************************************************************************/
234 template<class T>
235 constexpr auto
236 check_is_input_range(int) -> decltype(
237     begin(std::declval<T>()), end(std::declval<T>()),
238     std::true_type{});
239
240 template<class T>
241 constexpr auto
242 check_is_input_range(char) -> decltype(
243     std::begin(std::declval<T>()), std::end(std::declval<T>()),
244     std::true_type{});
245
246 template<class>
247 constexpr auto
248 check_is_input_range(long) -> std::false_type;
249
250 template<class T>
251 struct is_input_range :
252     decltype(check_is_input_range<T>(0))
253 {};
254
255
256
257 /*************************************************************************//**
258  *
259  * @brief size() member type trait
260  *
261  *****************************************************************************/
262 template<class T>
263 constexpr auto
264 check_has_size_getter(int) ->
265     decltype(std::declval<T>().size(), std::true_type{});
266
267 template<class>
268 constexpr auto
269 check_has_size_getter(long) -> std::false_type;
270
271 template<class T>
272 struct has_size_getter :
273     decltype(check_has_size_getter<T>(0))
274 {};
275
276 } // namespace traits
277
278
279
280
281
282
283 /*************************************************************************//**
284  *
285  * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!)
286  *        no interface guarantees; might be changed or removed in the future
287  *
288  *****************************************************************************/
289 namespace detail {
290
291
292 /*************************************************************************//**
293  * @brief forwards string to first non-whitespace char;
294  *        std string -> unsigned conv yields max value, but we want 0;
295  *        also checks for nullptr
296  *****************************************************************************/
297 inline bool
298 fwd_to_unsigned_int(const char*& s)
299 {
300     if(!s) return false;
301     for(; std::isspace(*s); ++s);
302     if(!s[0] || s[0] == '-') return false;
303     if(s[0] == '-') return false;
304     return true;
305 }
306
307
308 /*************************************************************************//**
309  *
310  * @brief value limits clamping
311  *
312  *****************************************************************************/
313 template<class T, class V, bool = (sizeof(V) > sizeof(T))>
314 struct limits_clamped {
315     static T from(const V& v) {
316         if(v >= V(std::numeric_limits<T>::max())) {
317             return std::numeric_limits<T>::max();
318         }
319         if(v <= V(std::numeric_limits<T>::lowest())) {
320             return std::numeric_limits<T>::lowest();
321         }
322         return T(v);
323     }
324 };
325
326 template<class T, class V>
327 struct limits_clamped<T,V,false> {
328     static T from(const V& v) { return T(v); }
329 };
330
331
332 /*************************************************************************//**
333  *
334  * @brief returns value of v as a T, clamped at T's maximum
335  *
336  *****************************************************************************/
337 template<class T, class V>
338 inline T clamped_on_limits(const V& v) {
339     return limits_clamped<T,V>::from(v);
340 }
341
342
343
344
345 /*************************************************************************//**
346  *
347  * @brief type conversion helpers
348  *
349  *****************************************************************************/
350 template<class T>
351 struct make {
352     static inline T from(const char* s) {
353         if(!s) return false;
354         //a conversion from const char* to / must exist
355         return static_cast<T>(s);
356     }
357 };
358
359 template<>
360 struct make<bool> {
361     static inline bool from(const char* s) {
362         if(!s) return false;
363         return static_cast<bool>(s);
364     }
365 };
366
367 template<>
368 struct make<unsigned char> {
369     static inline unsigned char from(const char* s) {
370         if(!fwd_to_unsigned_int(s)) return (0);
371         return clamped_on_limits<unsigned char>(std::strtoull(s,nullptr,10));
372     }
373 };
374
375 template<>
376 struct make<unsigned short int> {
377     static inline unsigned short int from(const char* s) {
378         if(!fwd_to_unsigned_int(s)) return (0);
379         return clamped_on_limits<unsigned short int>(std::strtoull(s,nullptr,10));
380     }
381 };
382
383 template<>
384 struct make<unsigned int> {
385     static inline unsigned int from(const char* s) {
386         if(!fwd_to_unsigned_int(s)) return (0);
387         return clamped_on_limits<unsigned int>(std::strtoull(s,nullptr,10));
388     }
389 };
390
391 template<>
392 struct make<unsigned long int> {
393     static inline unsigned long int from(const char* s) {
394         if(!fwd_to_unsigned_int(s)) return (0);
395         return clamped_on_limits<unsigned long int>(std::strtoull(s,nullptr,10));
396     }
397 };
398
399 template<>
400 struct make<unsigned long long int> {
401     static inline unsigned long long int from(const char* s) {
402         if(!fwd_to_unsigned_int(s)) return (0);
403         return clamped_on_limits<unsigned long long int>(std::strtoull(s,nullptr,10));
404     }
405 };
406
407 template<>
408 struct make<char> {
409     static inline char from(const char* s) {
410         //parse as single character?
411         const auto n = std::strlen(s);
412         if(n == 1) return s[0];
413         //parse as integer
414         return clamped_on_limits<char>(std::strtoll(s,nullptr,10));
415     }
416 };
417
418 template<>
419 struct make<short int> {
420     static inline short int from(const char* s) {
421         return clamped_on_limits<short int>(std::strtoll(s,nullptr,10));
422     }
423 };
424
425 template<>
426 struct make<int> {
427     static inline int from(const char* s) {
428         return clamped_on_limits<int>(std::strtoll(s,nullptr,10));
429     }
430 };
431
432 template<>
433 struct make<long int> {
434     static inline long int from(const char* s) {
435         return clamped_on_limits<long int>(std::strtoll(s,nullptr,10));
436     }
437 };
438
439 template<>
440 struct make<long long int> {
441     static inline long long int from(const char* s) {
442         return (std::strtoll(s,nullptr,10));
443     }
444 };
445
446 template<>
447 struct make<float> {
448     static inline float from(const char* s) {
449         return (std::strtof(s,nullptr));
450     }
451 };
452
453 template<>
454 struct make<double> {
455     static inline double from(const char* s) {
456         return (std::strtod(s,nullptr));
457     }
458 };
459
460 template<>
461 struct make<long double> {
462     static inline long double from(const char* s) {
463         return (std::strtold(s,nullptr));
464     }
465 };
466
467 template<>
468 struct make<std::string> {
469     static inline std::string from(const char* s) {
470         return std::string(s);
471     }
472 };
473
474
475
476 /*************************************************************************//**
477  *
478  * @brief assigns boolean constant to one or multiple target objects
479  *
480  *****************************************************************************/
481 template<class T, class V = T>
482 class assign_value
483 {
484 public:
485     template<class X>
486     explicit constexpr
487     assign_value(T& target, X&& value) noexcept :
488         t_{std::addressof(target)}, v_{std::forward<X>(value)}
489     {}
490
491     void operator () () const {
492         if(t_) *t_ = v_;
493     }
494
495 private:
496     T* t_;
497     V v_;
498 };
499
500
501
502 /*************************************************************************//**
503  *
504  * @brief flips bools
505  *
506  *****************************************************************************/
507 class flip_bool
508 {
509 public:
510     explicit constexpr
511     flip_bool(bool& target) noexcept :
512         b_{&target}
513     {}
514
515     void operator () () const {
516         if(b_) *b_ = !*b_;
517     }
518
519 private:
520     bool* b_;
521 };
522
523
524
525 /*************************************************************************//**
526  *
527  * @brief increments using operator ++
528  *
529  *****************************************************************************/
530 template<class T>
531 class increment
532 {
533 public:
534     explicit constexpr
535     increment(T& target) noexcept : t_{std::addressof(target)} {}
536
537     void operator () () const {
538         if(t_) ++(*t_);
539     }
540
541 private:
542     T* t_;
543 };
544
545
546
547 /*************************************************************************//**
548  *
549  * @brief decrements using operator --
550  *
551  *****************************************************************************/
552 template<class T>
553 class decrement
554 {
555 public:
556     explicit constexpr
557     decrement(T& target) noexcept : t_{std::addressof(target)} {}
558
559     void operator () () const {
560         if(t_) --(*t_);
561     }
562
563 private:
564     T* t_;
565 };
566
567
568
569 /*************************************************************************//**
570  *
571  * @brief increments by a fixed amount using operator +=
572  *
573  *****************************************************************************/
574 template<class T>
575 class increment_by
576 {
577 public:
578     explicit constexpr
579     increment_by(T& target, T by) noexcept :
580         t_{std::addressof(target)}, by_{std::move(by)}
581     {}
582
583     void operator () () const {
584         if(t_) (*t_) += by_;
585     }
586
587 private:
588     T* t_;
589     T by_;
590 };
591
592
593
594
595 /*************************************************************************//**
596  *
597  * @brief makes a value from a string and assigns it to an object
598  *
599  *****************************************************************************/
600 template<class T>
601 class map_arg_to
602 {
603 public:
604     explicit constexpr
605     map_arg_to(T& target) noexcept : t_{std::addressof(target)} {}
606
607     void operator () (const char* s) const {
608         if(t_ && s) *t_ = detail::make<T>::from(s);
609     }
610
611 private:
612     T* t_;
613 };
614
615
616 //-------------------------------------------------------------------
617 /**
618  * @brief specialization for vectors: append element
619  */
620 template<class T>
621 class map_arg_to<std::vector<T>>
622 {
623 public:
624     map_arg_to(std::vector<T>& target): t_{std::addressof(target)} {}
625
626     void operator () (const char* s) const {
627         if(t_ && s) t_->push_back(detail::make<T>::from(s));
628     }
629
630 private:
631     std::vector<T>* t_;
632 };
633
634
635 //-------------------------------------------------------------------
636 /**
637  * @brief specialization for bools:
638  *        set to true regardless of string content
639  */
640 template<>
641 class map_arg_to<bool>
642 {
643 public:
644     map_arg_to(bool& target): t_{&target} {}
645
646     void operator () (const char* s) const {
647         if(t_ && s) *t_ = true;
648     }
649
650 private:
651     bool* t_;
652 };
653
654
655 } // namespace detail
656
657
658
659
660
661
662 /*************************************************************************//**
663  *
664  * @brief string matching and processing tools
665  *
666  *****************************************************************************/
667
668 namespace str {
669
670
671 /*************************************************************************//**
672  *
673  * @brief converts string to value of target type 'T'
674  *
675  *****************************************************************************/
676 template<class T>
677 T make(const arg_string& s)
678 {
679     return detail::make<T>::from(s);
680 }
681
682
683
684 /*************************************************************************//**
685  *
686  * @brief removes trailing whitespace from string
687  *
688  *****************************************************************************/
689 template<class C, class T, class A>
690 inline void
691 trimr(std::basic_string<C,T,A>& s)
692 {
693     if(s.empty()) return;
694
695     s.erase(
696         std::find_if_not(s.rbegin(), s.rend(),
697                          [](char c) { return std::isspace(c);} ).base(),
698         s.end() );
699 }
700
701
702 /*************************************************************************//**
703  *
704  * @brief removes leading whitespace from string
705  *
706  *****************************************************************************/
707 template<class C, class T, class A>
708 inline void
709 triml(std::basic_string<C,T,A>& s)
710 {
711     if(s.empty()) return;
712
713     s.erase(
714         s.begin(),
715         std::find_if_not(s.begin(), s.end(),
716                          [](char c) { return std::isspace(c);})
717     );
718 }
719
720
721 /*************************************************************************//**
722  *
723  * @brief removes leading and trailing whitespace from string
724  *
725  *****************************************************************************/
726 template<class C, class T, class A>
727 inline void
728 trim(std::basic_string<C,T,A>& s)
729 {
730     triml(s);
731     trimr(s);
732 }
733
734
735 /*************************************************************************//**
736  *
737  * @brief removes all whitespaces from string
738  *
739  *****************************************************************************/
740 template<class C, class T, class A>
741 inline void
742 remove_ws(std::basic_string<C,T,A>& s)
743 {
744     if(s.empty()) return;
745
746     s.erase(std::remove_if(s.begin(), s.end(),
747                            [](char c) { return std::isspace(c); }),
748             s.end() );
749 }
750
751
752 /*************************************************************************//**
753  *
754  * @brief returns true, if the 'prefix' argument
755  *        is a prefix of the 'subject' argument
756  *
757  *****************************************************************************/
758 template<class C, class T, class A>
759 inline bool
760 has_prefix(const std::basic_string<C,T,A>& subject,
761            const std::basic_string<C,T,A>& prefix)
762 {
763     if(prefix.size() > subject.size()) return false;
764     return subject.find(prefix) == 0;
765 }
766
767
768 /*************************************************************************//**
769  *
770  * @brief returns true, if the 'postfix' argument
771  *        is a postfix of the 'subject' argument
772  *
773  *****************************************************************************/
774 template<class C, class T, class A>
775 inline bool
776 has_postfix(const std::basic_string<C,T,A>& subject,
777             const std::basic_string<C,T,A>& postfix)
778 {
779     if(postfix.size() > subject.size()) return false;
780     return (subject.size() - postfix.size()) == subject.find(postfix);
781 }
782
783
784
785 /*************************************************************************//**
786 *
787 * @brief   returns longest common prefix of several
788 *          sequential random access containers
789 *
790 * @details InputRange require begin and end (member functions or overloads)
791 *          the elements of InputRange require a size() member
792 *
793 *****************************************************************************/
794 template<class InputRange>
795 auto
796 longest_common_prefix(const InputRange& strs)
797     -> typename std::decay<decltype(*begin(strs))>::type
798 {
799     static_assert(traits::is_input_range<InputRange>(),
800         "parameter must satisfy the InputRange concept");
801
802     static_assert(traits::has_size_getter<
803         typename std::decay<decltype(*begin(strs))>::type>(),
804         "elements of input range must have a ::size() member function");
805
806     using std::begin;
807     using std::end;
808
809     using item_t = typename std::decay<decltype(*begin(strs))>::type;
810     using str_size_t = typename std::decay<decltype(begin(strs)->size())>::type;
811
812     const auto n = size_t(distance(begin(strs), end(strs)));
813     if(n < 1) return item_t("");
814     if(n == 1) return *begin(strs);
815
816     //length of shortest string
817     auto m = std::min_element(begin(strs), end(strs),
818                 [](const item_t& a, const item_t& b) {
819                     return a.size() < b.size(); })->size();
820
821     //check each character until we find a mismatch
822     for(str_size_t i = 0; i < m; ++i) {
823         for(str_size_t j = 1; j < n; ++j) {
824             if(strs[j][i] != strs[j-1][i])
825                 return strs[0].substr(0, i);
826         }
827     }
828     return strs[0].substr(0, m);
829 }
830
831
832
833 /*************************************************************************//**
834  *
835  * @brief  returns longest substring range that could be found in 'arg'
836  *
837  * @param  arg         string to be searched in
838  * @param  substrings  range of candidate substrings
839  *
840  *****************************************************************************/
841 template<class C, class T, class A, class InputRange>
842 subrange
843 longest_substring_match(const std::basic_string<C,T,A>& arg,
844                         const InputRange& substrings)
845 {
846     using string_t = std::basic_string<C,T,A>;
847
848     static_assert(traits::is_input_range<InputRange>(),
849         "parameter must satisfy the InputRange concept");
850
851     static_assert(std::is_same<string_t,
852         typename std::decay<decltype(*begin(substrings))>::type>(),
853         "substrings must have same type as 'arg'");
854
855     auto i = string_t::npos;
856     auto n = string_t::size_type(0);
857     for(const auto& s : substrings) {
858         auto j = arg.find(s);
859         if(j != string_t::npos && s.size() > n) {
860             i = j;
861             n = s.size();
862         }
863     }
864     return subrange{i,n};
865 }
866
867
868
869 /*************************************************************************//**
870  *
871  * @brief  returns longest prefix range that could be found in 'arg'
872  *
873  * @param  arg       string to be searched in
874  * @param  prefixes  range of candidate prefix strings
875  *
876  *****************************************************************************/
877 template<class C, class T, class A, class InputRange>
878 subrange
879 longest_prefix_match(const std::basic_string<C,T,A>& arg,
880                      const InputRange& prefixes)
881 {
882     using string_t = std::basic_string<C,T,A>;
883     using s_size_t = typename string_t::size_type;
884
885     static_assert(traits::is_input_range<InputRange>(),
886         "parameter must satisfy the InputRange concept");
887
888     static_assert(std::is_same<string_t,
889         typename std::decay<decltype(*begin(prefixes))>::type>(),
890         "prefixes must have same type as 'arg'");
891
892     auto i = string_t::npos;
893     auto n = s_size_t(0);
894     for(const auto& s : prefixes) {
895         auto j = arg.find(s);
896         if(j == 0 && s.size() > n) {
897             i = 0;
898             n = s.size();
899         }
900     }
901     return subrange{i,n};
902 }
903
904
905
906 /*************************************************************************//**
907  *
908  * @brief returns the first occurrence of 'query' within 'subject'
909  *
910  *****************************************************************************/
911 template<class C, class T, class A>
912 inline subrange
913 substring_match(const std::basic_string<C,T,A>& subject,
914                 const std::basic_string<C,T,A>& query)
915 {
916     if(subject.empty() && query.empty()) return subrange(0,0);
917     if(subject.empty() || query.empty()) return subrange{};
918     auto i = subject.find(query);
919     if(i == std::basic_string<C,T,A>::npos) return subrange{};
920     return subrange{i,query.size()};
921 }
922
923
924
925 /*************************************************************************//**
926  *
927  * @brief returns first substring match (pos,len) within the input string
928  *        that represents a number
929  *        (with at maximum one decimal point and digit separators)
930  *
931  *****************************************************************************/
932 template<class C, class T, class A>
933 subrange
934 first_number_match(std::basic_string<C,T,A> s,
935                    C digitSeparator = C(','),
936                    C decimalPoint = C('.'),
937                    C exponential = C('e'))
938 {
939     using string_t = std::basic_string<C,T,A>;
940
941     str::trim(s);
942     if(s.empty()) return subrange{};
943
944     auto i = s.find_first_of("0123456789+-");
945     if(i == string_t::npos) {
946         i = s.find(decimalPoint);
947         if(i == string_t::npos) return subrange{};
948     }
949
950     bool point = false;
951     bool sep = false;
952     auto exp = string_t::npos;
953     auto j = i + 1;
954     for(; j < s.size(); ++j) {
955         if(s[j] == digitSeparator) {
956             if(!sep) sep = true; else break;
957         }
958         else {
959             sep = false;
960             if(s[j] == decimalPoint) {
961                 //only one decimal point before exponent allowed
962                 if(!point && exp == string_t::npos) point = true; else break;
963             }
964             else if(std::tolower(s[j]) == std::tolower(exponential)) {
965                 //only one exponent separator allowed
966                 if(exp == string_t::npos) exp = j; else break;
967             }
968             else if(exp != string_t::npos && (exp+1) == j) {
969                 //only sign or digit after exponent separator
970                 if(s[j] != '+' && s[j] != '-' && !std::isdigit(s[j])) break;
971             }
972             else if(!std::isdigit(s[j])) {
973                 break;
974             }
975         }
976     }
977
978     //if length == 1 then must be a digit
979     if(j-i == 1 && !std::isdigit(s[i])) return subrange{};
980
981     return subrange{i,j-i};
982 }
983
984
985
986 /*************************************************************************//**
987  *
988  * @brief returns first substring match (pos,len)
989  *        that represents an integer (with optional digit separators)
990  *
991  *****************************************************************************/
992 template<class C, class T, class A>
993 subrange
994 first_integer_match(std::basic_string<C,T,A> s,
995                     C digitSeparator = C(','))
996 {
997     using string_t = std::basic_string<C,T,A>;
998
999     str::trim(s);
1000     if(s.empty()) return subrange{};
1001
1002     auto i = s.find_first_of("0123456789+-");
1003     if(i == string_t::npos) return subrange{};
1004
1005     bool sep = false;
1006     auto j = i + 1;
1007     for(; j < s.size(); ++j) {
1008         if(s[j] == digitSeparator) {
1009             if(!sep) sep = true; else break;
1010         }
1011         else {
1012             sep = false;
1013             if(!std::isdigit(s[j])) break;
1014         }
1015     }
1016
1017     //if length == 1 then must be a digit
1018     if(j-i == 1 && !std::isdigit(s[i])) return subrange{};
1019
1020     return subrange{i,j-i};
1021 }
1022
1023
1024
1025 /*************************************************************************//**
1026  *
1027  * @brief returns true if candidate string represents a number
1028  *
1029  *****************************************************************************/
1030 template<class C, class T, class A>
1031 bool represents_number(const std::basic_string<C,T,A>& candidate,
1032                    C digitSeparator = C(','),
1033                    C decimalPoint = C('.'),
1034                    C exponential = C('e'))
1035 {
1036     const auto match = str::first_number_match(candidate, digitSeparator,
1037                                                decimalPoint, exponential);
1038
1039     return (match && match.length() == candidate.size());
1040 }
1041
1042
1043
1044 /*************************************************************************//**
1045  *
1046  * @brief returns true if candidate string represents an integer
1047  *
1048  *****************************************************************************/
1049 template<class C, class T, class A>
1050 bool represents_integer(const std::basic_string<C,T,A>& candidate,
1051                         C digitSeparator = C(','))
1052 {
1053     const auto match = str::first_integer_match(candidate, digitSeparator);
1054     return (match && match.length() == candidate.size());
1055 }
1056
1057 } // namespace str
1058
1059
1060
1061
1062
1063
1064 /*************************************************************************//**
1065  *
1066  * @brief makes function object with a const char* parameter
1067  *        that assigns a value to a ref-captured object
1068  *
1069  *****************************************************************************/
1070 template<class T, class V>
1071 inline detail::assign_value<T,V>
1072 set(T& target, V value) {
1073     return detail::assign_value<T>{target, std::move(value)};
1074 }
1075
1076
1077
1078 /*************************************************************************//**
1079  *
1080  * @brief makes parameter-less function object
1081  *        that assigns value(s) to a ref-captured object;
1082  *        value(s) are obtained by converting the const char* argument to
1083  *        the captured object types;
1084  *        bools are always set to true if the argument is not nullptr
1085  *
1086  *****************************************************************************/
1087 template<class T>
1088 inline detail::map_arg_to<T>
1089 set(T& target) {
1090     return detail::map_arg_to<T>{target};
1091 }
1092
1093
1094
1095 /*************************************************************************//**
1096  *
1097  * @brief makes function object that sets a bool to true
1098  *
1099  *****************************************************************************/
1100 inline detail::assign_value<bool>
1101 set(bool& target) {
1102     return detail::assign_value<bool>{target,true};
1103 }
1104
1105 /*************************************************************************//**
1106  *
1107  * @brief makes function object that sets a bool to false
1108  *
1109  *****************************************************************************/
1110 inline detail::assign_value<bool>
1111 unset(bool& target) {
1112     return detail::assign_value<bool>{target,false};
1113 }
1114
1115 /*************************************************************************//**
1116  *
1117  * @brief makes function object that flips the value of a ref-captured bool
1118  *
1119  *****************************************************************************/
1120 inline detail::flip_bool
1121 flip(bool& b) {
1122     return detail::flip_bool(b);
1123 }
1124
1125
1126
1127
1128
1129 /*************************************************************************//**
1130  *
1131  * @brief makes function object that increments using operator ++
1132  *
1133  *****************************************************************************/
1134 template<class T>
1135 inline detail::increment<T>
1136 increment(T& target) {
1137     return detail::increment<T>{target};
1138 }
1139
1140 /*************************************************************************//**
1141  *
1142  * @brief makes function object that decrements using operator --
1143  *
1144  *****************************************************************************/
1145 template<class T>
1146 inline detail::increment_by<T>
1147 increment(T& target, T by) {
1148     return detail::increment_by<T>{target, std::move(by)};
1149 }
1150
1151 /*************************************************************************//**
1152  *
1153  * @brief makes function object that increments by a fixed amount using operator +=
1154  *
1155  *****************************************************************************/
1156 template<class T>
1157 inline detail::decrement<T>
1158 decrement(T& target) {
1159     return detail::decrement<T>{target};
1160 }
1161
1162
1163
1164
1165
1166
1167 /*************************************************************************//**
1168  *
1169  * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!)
1170  *
1171  *****************************************************************************/
1172 namespace detail {
1173
1174
1175 /*************************************************************************//**
1176  *
1177  * @brief mixin that provides action definition and execution
1178  *
1179  *****************************************************************************/
1180 template<class Derived>
1181 class action_provider
1182 {
1183 private:
1184     //---------------------------------------------------------------
1185     using simple_action = std::function<void()>;
1186     using arg_action    = std::function<void(const char*)>;
1187     using index_action  = std::function<void(int)>;
1188
1189     //-----------------------------------------------------
1190     class simple_action_adapter {
1191     public:
1192         simple_action_adapter() = default;
1193         simple_action_adapter(const simple_action& a): action_(a) {}
1194         simple_action_adapter(simple_action&& a): action_(std::move(a)) {}
1195         void operator() (const char*) const { action_(); }
1196         void operator() (int) const { action_(); }
1197     private:
1198         simple_action action_;
1199     };
1200
1201
1202 public:
1203     //---------------------------------------------------------------
1204     /** @brief adds an action that has an operator() that is callable
1205      *         with a 'const char*' argument */
1206     Derived&
1207     call(arg_action a) {
1208         argActions_.push_back(std::move(a));
1209         return *static_cast<Derived*>(this);
1210     }
1211
1212     /** @brief adds an action that has an operator()() */
1213     Derived&
1214     call(simple_action a) {
1215         argActions_.push_back(simple_action_adapter(std::move(a)));
1216         return *static_cast<Derived*>(this);
1217     }
1218
1219     /** @brief adds an action that has an operator() that is callable
1220      *         with a 'const char*' argument */
1221     Derived& operator () (arg_action a)    { return call(std::move(a)); }
1222
1223     /** @brief adds an action that has an operator()() */
1224     Derived& operator () (simple_action a) { return call(std::move(a)); }
1225
1226
1227     //---------------------------------------------------------------
1228     /** @brief adds an action that will set the value of 't' from
1229      *         a 'const char*' arg */
1230     template<class Target>
1231     Derived&
1232     set(Target& t) {
1233         static_assert(!std::is_pointer<Target>::value,
1234                       "parameter target type must not be a pointer");
1235
1236         return call(clipp::set(t));
1237     }
1238
1239     /** @brief adds an action that will set the value of 't' to 'v' */
1240     template<class Target, class Value>
1241     Derived&
1242     set(Target& t, Value&& v) {
1243         return call(clipp::set(t, std::forward<Value>(v)));
1244     }
1245
1246
1247     //---------------------------------------------------------------
1248     /** @brief adds an action that will be called if a parameter
1249      *         matches an argument for the 2nd, 3rd, 4th, ... time
1250      */
1251     Derived&
1252     if_repeated(simple_action a) {
1253         repeatActions_.push_back(simple_action_adapter{std::move(a)});
1254         return *static_cast<Derived*>(this);
1255     }
1256     /** @brief adds an action that will be called with the argument's
1257      *         index if a parameter matches an argument for
1258      *         the 2nd, 3rd, 4th, ... time
1259      */
1260     Derived&
1261     if_repeated(index_action a) {
1262         repeatActions_.push_back(std::move(a));
1263         return *static_cast<Derived*>(this);
1264     }
1265
1266
1267     //---------------------------------------------------------------
1268     /** @brief adds an action that will be called if a required parameter
1269      *         is missing
1270      */
1271     Derived&
1272     if_missing(simple_action a) {
1273         missingActions_.push_back(simple_action_adapter{std::move(a)});
1274         return *static_cast<Derived*>(this);
1275     }
1276     /** @brief adds an action that will be called if a required parameter
1277      *         is missing; the action will get called with the index of
1278      *         the command line argument where the missing event occurred first
1279      */
1280     Derived&
1281     if_missing(index_action a) {
1282         missingActions_.push_back(std::move(a));
1283         return *static_cast<Derived*>(this);
1284     }
1285
1286
1287     //---------------------------------------------------------------
1288     /** @brief adds an action that will be called if a parameter
1289      *         was matched, but was unreachable in the current scope
1290      */
1291     Derived&
1292     if_blocked(simple_action a) {
1293         blockedActions_.push_back(simple_action_adapter{std::move(a)});
1294         return *static_cast<Derived*>(this);
1295     }
1296     /** @brief adds an action that will be called if a parameter
1297      *         was matched, but was unreachable in the current scope;
1298      *         the action will be called with the index of
1299      *         the command line argument where the problem occurred
1300      */
1301     Derived&
1302     if_blocked(index_action a) {
1303         blockedActions_.push_back(std::move(a));
1304         return *static_cast<Derived*>(this);
1305     }
1306
1307
1308     //---------------------------------------------------------------
1309     /** @brief adds an action that will be called if a parameter match
1310      *         was in conflict with a different alternative parameter
1311      */
1312     Derived&
1313     if_conflicted(simple_action a) {
1314         conflictActions_.push_back(simple_action_adapter{std::move(a)});
1315         return *static_cast<Derived*>(this);
1316     }
1317     /** @brief adds an action that will be called if a parameter match
1318      *         was in conflict with a different alternative parameter;
1319      *         the action will be called with the index of
1320      *         the command line argument where the problem occurred
1321      */
1322     Derived&
1323     if_conflicted(index_action a) {
1324         conflictActions_.push_back(std::move(a));
1325         return *static_cast<Derived*>(this);
1326     }
1327
1328
1329     //---------------------------------------------------------------
1330     /** @brief adds targets = either objects whose values should be
1331      *         set by command line arguments or actions that should
1332      *         be called in case of a match */
1333     template<class T, class... Ts>
1334     Derived&
1335     target(T&& t, Ts&&... ts) {
1336         target(std::forward<T>(t));
1337         target(std::forward<Ts>(ts)...);
1338         return *static_cast<Derived*>(this);
1339     }
1340
1341     /** @brief adds action that should be called in case of a match */
1342     template<class T, class = typename std::enable_if<
1343             !std::is_fundamental<typename std::decay<T>::type>() &&
1344             (traits::is_callable<T,void()>() ||
1345              traits::is_callable<T,void(const char*)>() )
1346         >::type>
1347     Derived&
1348     target(T&& t) {
1349         call(std::forward<T>(t));
1350         return *static_cast<Derived*>(this);
1351     }
1352
1353     /** @brief adds object whose value should be set by command line arguments
1354      */
1355     template<class T, class = typename std::enable_if<
1356             std::is_fundamental<typename std::decay<T>::type>() ||
1357             (!traits::is_callable<T,void()>() &&
1358              !traits::is_callable<T,void(const char*)>() )
1359         >::type>
1360     Derived&
1361     target(T& t) {
1362         set(t);
1363         return *static_cast<Derived*>(this);
1364     }
1365
1366     //TODO remove ugly empty param list overload
1367     Derived&
1368     target() {
1369         return *static_cast<Derived*>(this);
1370     }
1371
1372
1373     //---------------------------------------------------------------
1374     /** @brief adds target, see member function 'target' */
1375     template<class Target>
1376     inline friend Derived&
1377     operator << (Target&& t, Derived& p) {
1378         p.target(std::forward<Target>(t));
1379         return p;
1380     }
1381     /** @brief adds target, see member function 'target' */
1382     template<class Target>
1383     inline friend Derived&&
1384     operator << (Target&& t, Derived&& p) {
1385         p.target(std::forward<Target>(t));
1386         return std::move(p);
1387     }
1388
1389     //-----------------------------------------------------
1390     /** @brief adds target, see member function 'target' */
1391     template<class Target>
1392     inline friend Derived&
1393     operator >> (Derived& p, Target&& t) {
1394         p.target(std::forward<Target>(t));
1395         return p;
1396     }
1397     /** @brief adds target, see member function 'target' */
1398     template<class Target>
1399     inline friend Derived&&
1400     operator >> (Derived&& p, Target&& t) {
1401         p.target(std::forward<Target>(t));
1402         return std::move(p);
1403     }
1404
1405
1406     //---------------------------------------------------------------
1407     /** @brief executes all argument actions */
1408     void execute_actions(const arg_string& arg) const {
1409         int i = 0;
1410         for(const auto& a : argActions_) {
1411             ++i;
1412             a(arg.c_str());
1413         }
1414     }
1415
1416     /** @brief executes repeat actions */
1417     void notify_repeated(arg_index idx) const {
1418         for(const auto& a : repeatActions_) a(idx);
1419     }
1420     /** @brief executes missing error actions */
1421     void notify_missing(arg_index idx) const {
1422         for(const auto& a : missingActions_) a(idx);
1423     }
1424     /** @brief executes blocked error actions */
1425     void notify_blocked(arg_index idx) const {
1426         for(const auto& a : blockedActions_) a(idx);
1427     }
1428     /** @brief executes conflict error actions */
1429     void notify_conflict(arg_index idx) const {
1430         for(const auto& a : conflictActions_) a(idx);
1431     }
1432
1433 private:
1434     //---------------------------------------------------------------
1435     std::vector<arg_action> argActions_;
1436     std::vector<index_action> repeatActions_;
1437     std::vector<index_action> missingActions_;
1438     std::vector<index_action> blockedActions_;
1439     std::vector<index_action> conflictActions_;
1440 };
1441
1442
1443
1444
1445
1446
1447 /*************************************************************************//**
1448  *
1449  * @brief mixin that provides basic common settings of parameters and groups
1450  *
1451  *****************************************************************************/
1452 template<class Derived>
1453 class token
1454 {
1455 public:
1456     //---------------------------------------------------------------
1457     using doc_string = clipp::doc_string;
1458
1459
1460     //---------------------------------------------------------------
1461     /** @brief returns documentation string */
1462     const doc_string& doc() const noexcept {
1463         return doc_;
1464     }
1465
1466     /** @brief sets documentations string */
1467     Derived& doc(const doc_string& txt) {
1468         doc_ = txt;
1469         return *static_cast<Derived*>(this);
1470     }
1471
1472     /** @brief sets documentations string */
1473     Derived& doc(doc_string&& txt) {
1474         doc_ = std::move(txt);
1475         return *static_cast<Derived*>(this);
1476     }
1477
1478
1479     //---------------------------------------------------------------
1480     /** @brief returns if a group/parameter is repeatable */
1481     bool repeatable() const noexcept {
1482         return repeatable_;
1483     }
1484
1485     /** @brief sets repeatability of group/parameter */
1486     Derived& repeatable(bool yes) noexcept {
1487         repeatable_ = yes;
1488         return *static_cast<Derived*>(this);
1489     }
1490
1491
1492     //---------------------------------------------------------------
1493     /** @brief returns if a group/parameter is blocking/positional */
1494     bool blocking() const noexcept {
1495         return blocking_;
1496     }
1497
1498     /** @brief determines, if a group/parameter is blocking/positional */
1499     Derived& blocking(bool yes) noexcept {
1500         blocking_ = yes;
1501         return *static_cast<Derived*>(this);
1502     }
1503
1504
1505 private:
1506     //---------------------------------------------------------------
1507     doc_string doc_;
1508     bool repeatable_ = false;
1509     bool blocking_ = false;
1510 };
1511
1512
1513
1514
1515 /*************************************************************************//**
1516  *
1517  * @brief sets documentation strings on a token
1518  *
1519  *****************************************************************************/
1520 template<class T>
1521 inline T&
1522 operator % (doc_string docstr, token<T>& p)
1523 {
1524     return p.doc(std::move(docstr));
1525 }
1526 //---------------------------------------------------------
1527 template<class T>
1528 inline T&&
1529 operator % (doc_string docstr, token<T>&& p)
1530 {
1531     return std::move(p.doc(std::move(docstr)));
1532 }
1533
1534 //---------------------------------------------------------
1535 template<class T>
1536 inline T&
1537 operator % (token<T>& p, doc_string docstr)
1538 {
1539     return p.doc(std::move(docstr));
1540 }
1541 //---------------------------------------------------------
1542 template<class T>
1543 inline T&&
1544 operator % (token<T>&& p, doc_string docstr)
1545 {
1546     return std::move(p.doc(std::move(docstr)));
1547 }
1548
1549
1550
1551
1552 /*************************************************************************//**
1553  *
1554  * @brief sets documentation strings on a token
1555  *
1556  *****************************************************************************/
1557 template<class T>
1558 inline T&
1559 doc(doc_string docstr, token<T>& p)
1560 {
1561     return p.doc(std::move(docstr));
1562 }
1563 //---------------------------------------------------------
1564 template<class T>
1565 inline T&&
1566 doc(doc_string docstr, token<T>&& p)
1567 {
1568     return std::move(p.doc(std::move(docstr)));
1569 }
1570
1571
1572
1573 } // namespace detail
1574
1575
1576
1577 /*************************************************************************//**
1578  *
1579  * @brief contains parameter matching functions and function classes
1580  *
1581  *****************************************************************************/
1582 namespace match {
1583
1584
1585 /*************************************************************************//**
1586  *
1587  * @brief predicate that is always true
1588  *
1589  *****************************************************************************/
1590 inline bool
1591 any(const arg_string&) { return true; }
1592
1593 /*************************************************************************//**
1594  *
1595  * @brief predicate that is always false
1596  *
1597  *****************************************************************************/
1598 inline bool
1599 none(const arg_string&) { return false; }
1600
1601
1602
1603 /*************************************************************************//**
1604  *
1605  * @brief predicate that returns true if the argument string is non-empty string
1606  *
1607  *****************************************************************************/
1608 inline bool
1609 nonempty(const arg_string& s) {
1610     return !s.empty();
1611 }
1612
1613
1614
1615 /*************************************************************************//**
1616  *
1617  * @brief predicate that returns true if the argument is a non-empty
1618  *        string that consists only of alphanumeric characters
1619  *
1620  *****************************************************************************/
1621 inline bool
1622 alphanumeric(const arg_string& s) {
1623     if(s.empty()) return false;
1624     return std::all_of(s.begin(), s.end(), [](char c) {return std::isalnum(c); });
1625 }
1626
1627
1628
1629 /*************************************************************************//**
1630  *
1631  * @brief predicate that returns true if the argument is a non-empty
1632  *        string that consists only of alphabetic characters
1633  *
1634  *****************************************************************************/
1635 inline bool
1636 alphabetic(const arg_string& s) {
1637     return std::all_of(s.begin(), s.end(), [](char c) {return std::isalpha(c); });
1638 }
1639
1640
1641
1642 /*************************************************************************//**
1643  *
1644  * @brief predicate that returns false if the argument string is
1645  *        equal to any string from the exclusion list
1646  *
1647  *****************************************************************************/
1648 class none_of
1649 {
1650 public:
1651     none_of(arg_list strs):
1652         excluded_{std::move(strs)}
1653     {}
1654
1655     template<class... Strings>
1656     none_of(arg_string str, Strings&&... strs):
1657         excluded_{std::move(str), std::forward<Strings>(strs)...}
1658     {}
1659
1660     template<class... Strings>
1661     none_of(const char* str, Strings&&... strs):
1662         excluded_{arg_string(str), std::forward<Strings>(strs)...}
1663     {}
1664
1665     bool operator () (const arg_string& arg) const {
1666         return (std::find(begin(excluded_), end(excluded_), arg)
1667                 == end(excluded_));
1668     }
1669
1670 private:
1671     arg_list excluded_;
1672 };
1673
1674
1675
1676 /*************************************************************************//**
1677  *
1678  * @brief predicate that returns the first substring match within the input
1679  *        string that rmeepresents a number
1680  *        (with at maximum one decimal point and digit separators)
1681  *
1682  *****************************************************************************/
1683 class numbers
1684 {
1685 public:
1686     explicit
1687     numbers(char decimalPoint = '.',
1688             char digitSeparator = ' ',
1689             char exponentSeparator = 'e')
1690     :
1691         decpoint_{decimalPoint}, separator_{digitSeparator},
1692         exp_{exponentSeparator}
1693     {}
1694
1695     subrange operator () (const arg_string& s) const {
1696         return str::first_number_match(s, separator_, decpoint_, exp_);
1697     }
1698
1699 private:
1700     char decpoint_;
1701     char separator_;
1702     char exp_;
1703 };
1704
1705
1706
1707 /*************************************************************************//**
1708  *
1709  * @brief predicate that returns true if the input string represents an integer
1710  *        (with optional digit separators)
1711  *
1712  *****************************************************************************/
1713 class integers {
1714 public:
1715     explicit
1716     integers(char digitSeparator = ' '): separator_{digitSeparator} {}
1717
1718     subrange operator () (const arg_string& s) const {
1719         return str::first_integer_match(s, separator_);
1720     }
1721
1722 private:
1723     char separator_;
1724 };
1725
1726
1727
1728 /*************************************************************************//**
1729  *
1730  * @brief predicate that returns true if the input string represents
1731  *        a non-negative integer (with optional digit separators)
1732  *
1733  *****************************************************************************/
1734 class positive_integers {
1735 public:
1736     explicit
1737     positive_integers(char digitSeparator = ' '): separator_{digitSeparator} {}
1738
1739     subrange operator () (const arg_string& s) const {
1740         auto match = str::first_integer_match(s, separator_);
1741         if(!match) return subrange{};
1742         if(s[match.at()] == '-') return subrange{};
1743         return match;
1744     }
1745
1746 private:
1747     char separator_;
1748 };
1749
1750
1751
1752 /*************************************************************************//**
1753  *
1754  * @brief predicate that returns true if the input string
1755  *        contains a given substring
1756  *
1757  *****************************************************************************/
1758 class substring
1759 {
1760 public:
1761     explicit
1762     substring(arg_string str): str_{std::move(str)} {}
1763
1764     subrange operator () (const arg_string& s) const {
1765         return str::substring_match(s, str_);
1766     }
1767
1768 private:
1769     arg_string str_;
1770 };
1771
1772
1773
1774 /*************************************************************************//**
1775  *
1776  * @brief predicate that returns true if the input string starts
1777  *        with a given prefix
1778  *
1779  *****************************************************************************/
1780 class prefix {
1781 public:
1782     explicit
1783     prefix(arg_string p): prefix_{std::move(p)} {}
1784
1785     bool operator () (const arg_string& s) const {
1786         return s.find(prefix_) == 0;
1787     }
1788
1789 private:
1790     arg_string prefix_;
1791 };
1792
1793
1794
1795 /*************************************************************************//**
1796  *
1797  * @brief predicate that returns true if the input string does not start
1798  *        with a given prefix
1799  *
1800  *****************************************************************************/
1801 class prefix_not {
1802 public:
1803     explicit
1804     prefix_not(arg_string p): prefix_{std::move(p)} {}
1805
1806     bool operator () (const arg_string& s) const {
1807         return s.find(prefix_) != 0;
1808     }
1809
1810 private:
1811     arg_string prefix_;
1812 };
1813
1814
1815 /** @brief alias for prefix_not */
1816 using noprefix = prefix_not;
1817
1818
1819
1820 /*************************************************************************//**
1821  *
1822  * @brief predicate that returns true if the length of the input string
1823  *        is wihtin a given interval
1824  *
1825  *****************************************************************************/
1826 class length {
1827 public:
1828     explicit
1829     length(std::size_t exact):
1830         min_{exact}, max_{exact}
1831     {}
1832
1833     explicit
1834     length(std::size_t min, std::size_t max):
1835         min_{min}, max_{max}
1836     {}
1837
1838     bool operator () (const arg_string& s) const {
1839         return s.size() >= min_ && s.size() <= max_;
1840     }
1841
1842 private:
1843     std::size_t min_;
1844     std::size_t max_;
1845 };
1846
1847
1848 /*************************************************************************//**
1849  *
1850  * @brief makes function object that returns true if the input string has a
1851  *        given minimum length
1852  *
1853  *****************************************************************************/
1854 inline length min_length(std::size_t min)
1855 {
1856     return length{min, arg_string::npos-1};
1857 }
1858
1859 /*************************************************************************//**
1860  *
1861  * @brief makes function object that returns true if the input string is
1862  *        not longer than a given maximum length
1863  *
1864  *****************************************************************************/
1865 inline length max_length(std::size_t max)
1866 {
1867     return length{0, max};
1868 }
1869
1870
1871 } // namespace match
1872
1873
1874
1875
1876
1877 /*************************************************************************//**
1878  *
1879  * @brief command line parameter that can match one or many arguments.
1880  *
1881  *****************************************************************************/
1882 class parameter :
1883     public detail::token<parameter>,
1884     public detail::action_provider<parameter>
1885 {
1886     /** @brief adapts a 'match_predicate' to the 'match_function' interface */
1887     class predicate_adapter {
1888     public:
1889         explicit
1890         predicate_adapter(match_predicate pred): match_{std::move(pred)} {}
1891
1892         subrange operator () (const arg_string& arg) const {
1893             return match_(arg) ? subrange{0,arg.size()} : subrange{};
1894         }
1895
1896     private:
1897         match_predicate match_;
1898     };
1899
1900 public:
1901     //---------------------------------------------------------------
1902     /** @brief makes default parameter, that will match nothing */
1903     parameter():
1904         flags_{},
1905         matcher_{predicate_adapter{match::none}},
1906         label_{}, required_{false}, greedy_{false}
1907     {}
1908
1909     /** @brief makes "flag" parameter */
1910     template<class... Strings>
1911     explicit
1912     parameter(arg_string str, Strings&&... strs):
1913         flags_{},
1914         matcher_{predicate_adapter{match::none}},
1915         label_{}, required_{false}, greedy_{false}
1916     {
1917         add_flags(std::move(str), std::forward<Strings>(strs)...);
1918     }
1919
1920     /** @brief makes "flag" parameter from range of strings */
1921     explicit
1922     parameter(const arg_list& flaglist):
1923         flags_{},
1924         matcher_{predicate_adapter{match::none}},
1925         label_{}, required_{false}, greedy_{false}
1926     {
1927         add_flags(flaglist);
1928     }
1929
1930     //-----------------------------------------------------
1931     /** @brief makes "value" parameter with custom match predicate
1932      *         (= yes/no matcher)
1933      */
1934     explicit
1935     parameter(match_predicate filter):
1936         flags_{},
1937         matcher_{predicate_adapter{std::move(filter)}},
1938         label_{}, required_{false}, greedy_{false}
1939     {}
1940
1941     /** @brief makes "value" parameter with custom match function
1942      *         (= partial matcher)
1943      */
1944     explicit
1945     parameter(match_function filter):
1946         flags_{},
1947         matcher_{std::move(filter)},
1948         label_{}, required_{false}, greedy_{false}
1949     {}
1950
1951
1952     //---------------------------------------------------------------
1953     /** @brief returns if a parameter is required */
1954     bool
1955     required() const noexcept {
1956         return required_;
1957     }
1958
1959     /** @brief determines if a parameter is required */
1960     parameter&
1961     required(bool yes) noexcept {
1962         required_ = yes;
1963         return *this;
1964     }
1965
1966
1967     //---------------------------------------------------------------
1968     /** @brief returns if a parameter should match greedily */
1969     bool
1970     greedy() const noexcept {
1971         return greedy_;
1972     }
1973
1974     /** @brief determines if a parameter should match greedily */
1975     parameter&
1976     greedy(bool yes) noexcept {
1977         greedy_ = yes;
1978         return *this;
1979     }
1980
1981
1982     //---------------------------------------------------------------
1983     /** @brief returns parameter label;
1984      *         will be used for documentation, if flags are empty
1985      */
1986     const doc_string&
1987     label() const {
1988         return label_;
1989     }
1990
1991     /** @brief sets parameter label;
1992      *         will be used for documentation, if flags are empty
1993      */
1994     parameter&
1995     label(const doc_string& lbl) {
1996         label_ = lbl;
1997         return *this;
1998     }
1999
2000     /** @brief sets parameter label;
2001      *         will be used for documentation, if flags are empty
2002      */
2003     parameter&
2004     label(doc_string&& lbl) {
2005         label_ = lbl;
2006         return *this;
2007     }
2008
2009
2010     //---------------------------------------------------------------
2011     /** @brief returns either longest matching prefix of 'arg' in any
2012      *         of the flags or the result of the custom match operation
2013      */
2014     subrange
2015     match(const arg_string& arg) const
2016     {
2017         if(flags_.empty()) {
2018             return matcher_(arg);
2019         }
2020         else {
2021             //empty flags are not allowed
2022             if(arg.empty()) return subrange{};
2023
2024             if(std::find(flags_.begin(), flags_.end(), arg) != flags_.end()) {
2025                 return subrange{0,arg.size()};
2026             }
2027             return str::longest_prefix_match(arg, flags_);
2028         }
2029     }
2030
2031
2032     //---------------------------------------------------------------
2033     /** @brief access range of flag strings */
2034     const arg_list&
2035     flags() const noexcept {
2036         return flags_;
2037     }
2038
2039     /** @brief access custom match operation */
2040     const match_function&
2041     matcher() const noexcept {
2042         return matcher_;
2043     }
2044
2045
2046     //---------------------------------------------------------------
2047     /** @brief prepend prefix to each flag */
2048     inline friend parameter&
2049     with_prefix(const arg_string& prefix, parameter& p)
2050     {
2051         if(prefix.empty() || p.flags().empty()) return p;
2052
2053         for(auto& f : p.flags_) {
2054             if(f.find(prefix) != 0) f.insert(0, prefix);
2055         }
2056         return p;
2057     }
2058
2059
2060     /** @brief prepend prefix to each flag
2061      */
2062     inline friend parameter&
2063     with_prefixes_short_long(
2064         const arg_string& shortpfx, const arg_string& longpfx,
2065         parameter& p)
2066     {
2067         if(shortpfx.empty() && longpfx.empty()) return p;
2068         if(p.flags().empty()) return p;
2069
2070         for(auto& f : p.flags_) {
2071             if(f.size() == 1) {
2072                 if(f.find(shortpfx) != 0) f.insert(0, shortpfx);
2073             } else {
2074                 if(f.find(longpfx) != 0) f.insert(0, longpfx);
2075             }
2076         }
2077         return p;
2078     }
2079
2080
2081     //---------------------------------------------------------------
2082     /** @brief prepend suffix to each flag */
2083     inline friend parameter&
2084     with_suffix(const arg_string& suffix, parameter& p)
2085     {
2086         if(suffix.empty() || p.flags().empty()) return p;
2087
2088         for(auto& f : p.flags_) {
2089             if(f.find(suffix) + suffix.size() != f.size()) {
2090                 f.insert(f.end(), suffix.begin(), suffix.end());
2091             }
2092         }
2093         return p;
2094     }
2095
2096
2097     /** @brief prepend suffix to each flag
2098      */
2099     inline friend parameter&
2100     with_suffixes_short_long(
2101         const arg_string& shortsfx, const arg_string& longsfx,
2102         parameter& p)
2103     {
2104         if(shortsfx.empty() && longsfx.empty()) return p;
2105         if(p.flags().empty()) return p;
2106
2107         for(auto& f : p.flags_) {
2108             if(f.size() == 1) {
2109                 if(f.find(shortsfx) + shortsfx.size() != f.size()) {
2110                     f.insert(f.end(), shortsfx.begin(), shortsfx.end());
2111                 }
2112             } else {
2113                 if(f.find(longsfx) + longsfx.size() != f.size()) {
2114                     f.insert(f.end(), longsfx.begin(), longsfx.end());
2115                 }
2116             }
2117         }
2118         return p;
2119     }
2120
2121 private:
2122     //---------------------------------------------------------------
2123     void add_flags(arg_string str) {
2124         //empty flags are not allowed
2125         str::remove_ws(str);
2126         if(!str.empty()) flags_.push_back(std::move(str));
2127     }
2128
2129     //---------------------------------------------------------------
2130     void add_flags(const arg_list& strs) {
2131         if(strs.empty()) return;
2132         flags_.reserve(flags_.size() + strs.size());
2133         for(const auto& s : strs) add_flags(s);
2134     }
2135
2136     template<class String1, class String2, class... Strings>
2137     void
2138     add_flags(String1&& s1, String2&& s2, Strings&&... ss) {
2139         flags_.reserve(2 + sizeof...(ss));
2140         add_flags(std::forward<String1>(s1));
2141         add_flags(std::forward<String2>(s2), std::forward<Strings>(ss)...);
2142     }
2143
2144     arg_list flags_;
2145     match_function matcher_;
2146     doc_string label_;
2147     bool required_ = false;
2148     bool greedy_ = false;
2149 };
2150
2151
2152
2153
2154 /*************************************************************************//**
2155  *
2156  * @brief makes required non-blocking exact match parameter
2157  *
2158  *****************************************************************************/
2159 template<class String, class... Strings>
2160 inline parameter
2161 command(String&& flag, Strings&&... flags)
2162 {
2163     return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...}
2164         .required(true).blocking(true).repeatable(false);
2165 }
2166
2167
2168
2169 /*************************************************************************//**
2170  *
2171  * @brief makes required non-blocking exact match parameter
2172  *
2173  *****************************************************************************/
2174 template<class String, class... Strings>
2175 inline parameter
2176 required(String&& flag, Strings&&... flags)
2177 {
2178     return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...}
2179         .required(true).blocking(false).repeatable(false);
2180 }
2181
2182
2183
2184 /*************************************************************************//**
2185  *
2186  * @brief makes optional, non-blocking exact match parameter
2187  *
2188  *****************************************************************************/
2189 template<class String, class... Strings>
2190 inline parameter
2191 option(String&& flag, Strings&&... flags)
2192 {
2193     return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...}
2194         .required(false).blocking(false).repeatable(false);
2195 }
2196
2197
2198
2199 /*************************************************************************//**
2200  *
2201  * @brief makes required, blocking, repeatable value parameter;
2202  *        matches any non-empty string
2203  *
2204  *****************************************************************************/
2205 template<class... Targets>
2206 inline parameter
2207 value(const doc_string& label, Targets&&... tgts)
2208 {
2209     return parameter{match::nonempty}
2210         .label(label)
2211         .target(std::forward<Targets>(tgts)...)
2212         .required(true).blocking(true).repeatable(false);
2213 }
2214
2215 template<class Filter, class... Targets, class = typename std::enable_if<
2216     traits::is_callable<Filter,bool(const char*)>::value ||
2217     traits::is_callable<Filter,subrange(const char*)>::value>::type>
2218 inline parameter
2219 value(Filter&& filter, doc_string label, Targets&&... tgts)
2220 {
2221     return parameter{std::forward<Filter>(filter)}
2222         .label(label)
2223         .target(std::forward<Targets>(tgts)...)
2224         .required(true).blocking(true).repeatable(false);
2225 }
2226
2227
2228
2229 /*************************************************************************//**
2230  *
2231  * @brief makes required, blocking, repeatable value parameter;
2232  *        matches any non-empty string
2233  *
2234  *****************************************************************************/
2235 template<class... Targets>
2236 inline parameter
2237 values(const doc_string& label, Targets&&... tgts)
2238 {
2239     return parameter{match::nonempty}
2240         .label(label)
2241         .target(std::forward<Targets>(tgts)...)
2242         .required(true).blocking(true).repeatable(true);
2243 }
2244
2245 template<class Filter, class... Targets, class = typename std::enable_if<
2246     traits::is_callable<Filter,bool(const char*)>::value ||
2247     traits::is_callable<Filter,subrange(const char*)>::value>::type>
2248 inline parameter
2249 values(Filter&& filter, doc_string label, Targets&&... tgts)
2250 {
2251     return parameter{std::forward<Filter>(filter)}
2252         .label(label)
2253         .target(std::forward<Targets>(tgts)...)
2254         .required(true).blocking(true).repeatable(true);
2255 }
2256
2257
2258
2259 /*************************************************************************//**
2260  *
2261  * @brief makes optional, blocking value parameter;
2262  *        matches any non-empty string
2263  *
2264  *****************************************************************************/
2265 template<class... Targets>
2266 inline parameter
2267 opt_value(const doc_string& label, Targets&&... tgts)
2268 {
2269     return parameter{match::nonempty}
2270         .label(label)
2271         .target(std::forward<Targets>(tgts)...)
2272         .required(false).blocking(false).repeatable(false);
2273 }
2274
2275 template<class Filter, class... Targets, class = typename std::enable_if<
2276     traits::is_callable<Filter,bool(const char*)>::value ||
2277     traits::is_callable<Filter,subrange(const char*)>::value>::type>
2278 inline parameter
2279 opt_value(Filter&& filter, doc_string label, Targets&&... tgts)
2280 {
2281     return parameter{std::forward<Filter>(filter)}
2282         .label(label)
2283         .target(std::forward<Targets>(tgts)...)
2284         .required(false).blocking(false).repeatable(false);
2285 }
2286
2287
2288
2289 /*************************************************************************//**
2290  *
2291  * @brief makes optional, blocking, repeatable value parameter;
2292  *        matches any non-empty string
2293  *
2294  *****************************************************************************/
2295 template<class... Targets>
2296 inline parameter
2297 opt_values(const doc_string& label, Targets&&... tgts)
2298 {
2299     return parameter{match::nonempty}
2300         .label(label)
2301         .target(std::forward<Targets>(tgts)...)
2302         .required(false).blocking(false).repeatable(true);
2303 }
2304
2305 template<class Filter, class... Targets, class = typename std::enable_if<
2306     traits::is_callable<Filter,bool(const char*)>::value ||
2307     traits::is_callable<Filter,subrange(const char*)>::value>::type>
2308 inline parameter
2309 opt_values(Filter&& filter, doc_string label, Targets&&... tgts)
2310 {
2311     return parameter{std::forward<Filter>(filter)}
2312         .label(label)
2313         .target(std::forward<Targets>(tgts)...)
2314         .required(false).blocking(false).repeatable(true);
2315 }
2316
2317
2318
2319 /*************************************************************************//**
2320  *
2321  * @brief makes required, blocking value parameter;
2322  *        matches any string consisting of alphanumeric characters
2323  *
2324  *****************************************************************************/
2325 template<class... Targets>
2326 inline parameter
2327 word(const doc_string& label, Targets&&... tgts)
2328 {
2329     return parameter{match::alphanumeric}
2330         .label(label)
2331         .target(std::forward<Targets>(tgts)...)
2332         .required(true).blocking(true).repeatable(false);
2333 }
2334
2335
2336
2337 /*************************************************************************//**
2338  *
2339  * @brief makes required, blocking, repeatable value parameter;
2340  *        matches any string consisting of alphanumeric characters
2341  *
2342  *****************************************************************************/
2343 template<class... Targets>
2344 inline parameter
2345 words(const doc_string& label, Targets&&... tgts)
2346 {
2347     return parameter{match::alphanumeric}
2348         .label(label)
2349         .target(std::forward<Targets>(tgts)...)
2350         .required(true).blocking(true).repeatable(true);
2351 }
2352
2353
2354
2355 /*************************************************************************//**
2356  *
2357  * @brief makes optional, blocking value parameter;
2358  *        matches any string consisting of alphanumeric characters
2359  *
2360  *****************************************************************************/
2361 template<class... Targets>
2362 inline parameter
2363 opt_word(const doc_string& label, Targets&&... tgts)
2364 {
2365     return parameter{match::alphanumeric}
2366         .label(label)
2367         .target(std::forward<Targets>(tgts)...)
2368         .required(false).blocking(false).repeatable(false);
2369 }
2370
2371
2372
2373 /*************************************************************************//**
2374  *
2375  * @brief makes optional, blocking, repeatable value parameter;
2376  *        matches any string consisting of alphanumeric characters
2377  *
2378  *****************************************************************************/
2379 template<class... Targets>
2380 inline parameter
2381 opt_words(const doc_string& label, Targets&&... tgts)
2382 {
2383     return parameter{match::alphanumeric}
2384         .label(label)
2385         .target(std::forward<Targets>(tgts)...)
2386         .required(false).blocking(false).repeatable(true);
2387 }
2388
2389
2390
2391 /*************************************************************************//**
2392  *
2393  * @brief makes required, blocking value parameter;
2394  *        matches any string that represents a number
2395  *
2396  *****************************************************************************/
2397 template<class... Targets>
2398 inline parameter
2399 number(const doc_string& label, Targets&&... tgts)
2400 {
2401     return parameter{match::numbers{}}
2402         .label(label)
2403         .target(std::forward<Targets>(tgts)...)
2404         .required(true).blocking(true).repeatable(false);
2405 }
2406
2407
2408
2409 /*************************************************************************//**
2410  *
2411  * @brief makes required, blocking, repeatable value parameter;
2412  *        matches any string that represents a number
2413  *
2414  *****************************************************************************/
2415 template<class... Targets>
2416 inline parameter
2417 numbers(const doc_string& label, Targets&&... tgts)
2418 {
2419     return parameter{match::numbers{}}
2420         .label(label)
2421         .target(std::forward<Targets>(tgts)...)
2422         .required(true).blocking(true).repeatable(true);
2423 }
2424
2425
2426
2427 /*************************************************************************//**
2428  *
2429  * @brief makes optional, blocking value parameter;
2430  *        matches any string that represents a number
2431  *
2432  *****************************************************************************/
2433 template<class... Targets>
2434 inline parameter
2435 opt_number(const doc_string& label, Targets&&... tgts)
2436 {
2437     return parameter{match::numbers{}}
2438         .label(label)
2439         .target(std::forward<Targets>(tgts)...)
2440         .required(false).blocking(false).repeatable(false);
2441 }
2442
2443
2444
2445 /*************************************************************************//**
2446  *
2447  * @brief makes optional, blocking, repeatable value parameter;
2448  *        matches any string that represents a number
2449  *
2450  *****************************************************************************/
2451 template<class... Targets>
2452 inline parameter
2453 opt_numbers(const doc_string& label, Targets&&... tgts)
2454 {
2455     return parameter{match::numbers{}}
2456         .label(label)
2457         .target(std::forward<Targets>(tgts)...)
2458         .required(false).blocking(false).repeatable(true);
2459 }
2460
2461
2462
2463 /*************************************************************************//**
2464  *
2465  * @brief makes required, blocking value parameter;
2466  *        matches any string that represents an integer
2467  *
2468  *****************************************************************************/
2469 template<class... Targets>
2470 inline parameter
2471 integer(const doc_string& label, Targets&&... tgts)
2472 {
2473     return parameter{match::integers{}}
2474         .label(label)
2475         .target(std::forward<Targets>(tgts)...)
2476         .required(true).blocking(true).repeatable(false);
2477 }
2478
2479
2480
2481 /*************************************************************************//**
2482  *
2483  * @brief makes required, blocking, repeatable value parameter;
2484  *        matches any string that represents an integer
2485  *
2486  *****************************************************************************/
2487 template<class... Targets>
2488 inline parameter
2489 integers(const doc_string& label, Targets&&... tgts)
2490 {
2491     return parameter{match::integers{}}
2492         .label(label)
2493         .target(std::forward<Targets>(tgts)...)
2494         .required(true).blocking(true).repeatable(true);
2495 }
2496
2497
2498
2499 /*************************************************************************//**
2500  *
2501  * @brief makes optional, blocking value parameter;
2502  *        matches any string that represents an integer
2503  *
2504  *****************************************************************************/
2505 template<class... Targets>
2506 inline parameter
2507 opt_integer(const doc_string& label, Targets&&... tgts)
2508 {
2509     return parameter{match::integers{}}
2510         .label(label)
2511         .target(std::forward<Targets>(tgts)...)
2512         .required(false).blocking(false).repeatable(false);
2513 }
2514
2515
2516
2517 /*************************************************************************//**
2518  *
2519  * @brief makes optional, blocking, repeatable value parameter;
2520  *        matches any string that represents an integer
2521  *
2522  *****************************************************************************/
2523 template<class... Targets>
2524 inline parameter
2525 opt_integers(const doc_string& label, Targets&&... tgts)
2526 {
2527     return parameter{match::integers{}}
2528         .label(label)
2529         .target(std::forward<Targets>(tgts)...)
2530         .required(false).blocking(false).repeatable(true);
2531 }
2532
2533
2534
2535 /*************************************************************************//**
2536  *
2537  * @brief makes catch-all value parameter
2538  *
2539  *****************************************************************************/
2540 template<class... Targets>
2541 inline parameter
2542 any_other(Targets&&... tgts)
2543 {
2544     return parameter{match::any}
2545         .target(std::forward<Targets>(tgts)...)
2546         .required(false).blocking(false).repeatable(true);
2547 }
2548
2549
2550
2551 /*************************************************************************//**
2552  *
2553  * @brief makes catch-all value parameter with custom filter
2554  *
2555  *****************************************************************************/
2556 template<class Filter, class... Targets, class = typename std::enable_if<
2557     traits::is_callable<Filter,bool(const char*)>::value ||
2558     traits::is_callable<Filter,subrange(const char*)>::value>::type>
2559 inline parameter
2560 any(Filter&& filter, Targets&&... tgts)
2561 {
2562     return parameter{std::forward<Filter>(filter)}
2563         .target(std::forward<Targets>(tgts)...)
2564         .required(false).blocking(false).repeatable(true);
2565 }
2566
2567
2568
2569
2570 /*************************************************************************//**
2571  *
2572  * @brief group of parameters and/or other groups;
2573  *        can be configured to act as a group of alternatives (exclusive match)
2574  *
2575  *****************************************************************************/
2576 class group :
2577     public detail::token<group>
2578 {
2579     //---------------------------------------------------------------
2580     /**
2581         * @brief tagged union type that either stores a parameter or a group
2582         *        and provides a common interface to them
2583         *        could be replaced by std::variant in the future
2584         *
2585         *        Note to future self: do NOT try again to do this with
2586         *        dynamic polymorphism; there are a couple of
2587         *        nasty problems associated with it and the implementation
2588         *        becomes bloated and needlessly complicated.
2589         */
2590     template<class Param, class Group>
2591     struct child_t {
2592         enum class type : char {param, group};
2593     public:
2594
2595         explicit
2596         child_t(const Param&  v)          : m_{v},            type_{type::param} {}
2597         child_t(      Param&& v) noexcept : m_{std::move(v)}, type_{type::param} {}
2598
2599         explicit
2600         child_t(const Group&  g)          : m_{g},            type_{type::group} {}
2601         child_t(      Group&& g) noexcept : m_{std::move(g)}, type_{type::group} {}
2602
2603         child_t(const child_t& src): type_{src.type_} {
2604             switch(type_) {
2605                 default:
2606                 case type::param: new(&m_)data{src.m_.param}; break;
2607                 case type::group: new(&m_)data{src.m_.group}; break;
2608             }
2609         }
2610
2611         child_t(child_t&& src) noexcept : type_{src.type_} {
2612             switch(type_) {
2613                 default:
2614                 case type::param: new(&m_)data{std::move(src.m_.param)}; break;
2615                 case type::group: new(&m_)data{std::move(src.m_.group)}; break;
2616             }
2617         }
2618
2619         child_t& operator = (const child_t& src) {
2620             destroy_content();
2621             type_ = src.type_;
2622             switch(type_) {
2623                 default:
2624                 case type::param: new(&m_)data{src.m_.param}; break;
2625                 case type::group: new(&m_)data{src.m_.group}; break;
2626             }
2627             return *this;
2628         }
2629
2630         child_t& operator = (child_t&& src) noexcept {
2631             destroy_content();
2632             type_ = src.type_;
2633             switch(type_) {
2634                 default:
2635                 case type::param: new(&m_)data{std::move(src.m_.param)}; break;
2636                 case type::group: new(&m_)data{std::move(src.m_.group)}; break;
2637             }
2638             return *this;
2639         }
2640
2641         ~child_t() {
2642             destroy_content();
2643         }
2644
2645         const doc_string&
2646         doc() const noexcept {
2647             switch(type_) {
2648                 default:
2649                 case type::param: return m_.param.doc();
2650                 case type::group: return m_.group.doc();
2651             }
2652         }
2653
2654         bool blocking() const noexcept {
2655             switch(type_) {
2656                 case type::param: return m_.param.blocking();
2657                 case type::group: return m_.group.blocking();
2658                 default: return false;
2659             }
2660         }
2661         bool repeatable() const noexcept {
2662             switch(type_) {
2663                 case type::param: return m_.param.repeatable();
2664                 case type::group: return m_.group.repeatable();
2665                 default: return false;
2666             }
2667         }
2668         bool required() const noexcept {
2669             switch(type_) {
2670                 case type::param: return m_.param.required();
2671                 case type::group:
2672                     return (m_.group.exclusive() && m_.group.all_required() ) ||
2673                           (!m_.group.exclusive() && m_.group.any_required()  );
2674                 default: return false;
2675             }
2676         }
2677         bool exclusive() const noexcept {
2678             switch(type_) {
2679                 case type::group: return m_.group.exclusive();
2680                 case type::param:
2681                 default: return false;
2682             }
2683         }
2684         std::size_t param_count() const noexcept {
2685             switch(type_) {
2686                 case type::group: return m_.group.param_count();
2687                 case type::param:
2688                 default: return std::size_t(1);
2689             }
2690         }
2691         std::size_t depth() const noexcept {
2692             switch(type_) {
2693                 case type::group: return m_.group.depth();
2694                 case type::param:
2695                 default: return std::size_t(0);
2696             }
2697         }
2698
2699         void execute_actions(const arg_string& arg) const {
2700             switch(type_) {
2701                 default:
2702                 case type::group: return;
2703                 case type::param: m_.param.execute_actions(arg); break;
2704             }
2705
2706         }
2707
2708         void notify_repeated(arg_index idx) const {
2709             switch(type_) {
2710                 default:
2711                 case type::group: return;
2712                 case type::param: m_.param.notify_repeated(idx); break;
2713             }
2714         }
2715         void notify_missing(arg_index idx) const {
2716             switch(type_) {
2717                 default:
2718                 case type::group: return;
2719                 case type::param: m_.param.notify_missing(idx); break;
2720             }
2721         }
2722         void notify_blocked(arg_index idx) const {
2723             switch(type_) {
2724                 default:
2725                 case type::group: return;
2726                 case type::param: m_.param.notify_blocked(idx); break;
2727             }
2728         }
2729         void notify_conflict(arg_index idx) const {
2730             switch(type_) {
2731                 default:
2732                 case type::group: return;
2733                 case type::param: m_.param.notify_conflict(idx); break;
2734             }
2735         }
2736
2737         bool is_param() const noexcept { return type_ == type::param; }
2738         bool is_group() const noexcept { return type_ == type::group; }
2739
2740         Param& as_param() noexcept { return m_.param; }
2741         Group& as_group() noexcept { return m_.group; }
2742
2743         const Param& as_param() const noexcept { return m_.param; }
2744         const Group& as_group() const noexcept { return m_.group; }
2745
2746     private:
2747         void destroy_content() {
2748             switch(type_) {
2749                 default:
2750                 case type::param: m_.param.~Param(); break;
2751                 case type::group: m_.group.~Group(); break;
2752             }
2753         }
2754
2755         union data {
2756             data() {}
2757
2758             data(const Param&  v)          : param{v} {}
2759             data(      Param&& v) noexcept : param{std::move(v)} {}
2760
2761             data(const Group&  g)          : group{g} {}
2762             data(      Group&& g) noexcept : group{std::move(g)} {}
2763             ~data() {}
2764
2765             Param param;
2766             Group group;
2767         };
2768
2769         data m_;
2770         type type_;
2771     };
2772
2773
2774 public:
2775     //---------------------------------------------------------------
2776     using child      = child_t<parameter,group>;
2777     using value_type = child;
2778
2779 private:
2780     using children_store = std::vector<child>;
2781
2782 public:
2783     using const_iterator = children_store::const_iterator;
2784     using iterator       = children_store::iterator;
2785     using size_type      = children_store::size_type;
2786
2787
2788     //---------------------------------------------------------------
2789     /**
2790      * @brief recursively iterates over all nodes
2791      */
2792     class depth_first_traverser
2793     {
2794     public:
2795         //-----------------------------------------------------
2796         struct context {
2797             context() = default;
2798             context(const group& p):
2799                 parent{&p}, cur{p.begin()}, end{p.end()}
2800             {}
2801             const group* parent = nullptr;
2802             const_iterator cur;
2803             const_iterator end;
2804         };
2805         using context_list = std::vector<context>;
2806
2807         //-----------------------------------------------------
2808         class memento {
2809             friend class depth_first_traverser;
2810             int level_;
2811             context context_;
2812         public:
2813             int level() const noexcept { return level_; }
2814             const child* param() const noexcept { return &(*context_.cur); }
2815         };
2816
2817         depth_first_traverser() = default;
2818
2819         explicit
2820         depth_first_traverser(const group& cur): stack_{} {
2821             if(!cur.empty()) stack_.emplace_back(cur);
2822         }
2823
2824         explicit operator bool() const noexcept {
2825             return !stack_.empty();
2826         }
2827
2828         int level() const noexcept {
2829             return int(stack_.size());
2830         }
2831
2832         bool is_first_in_parent() const noexcept {
2833             if(stack_.empty()) return false;
2834             return (stack_.back().cur == stack_.back().parent->begin());
2835         }
2836
2837         bool is_last_in_parent() const noexcept {
2838             if(stack_.empty()) return false;
2839             return (stack_.back().cur+1 == stack_.back().end);
2840         }
2841
2842         bool is_last_in_path() const noexcept {
2843             if(stack_.empty()) return false;
2844             for(const auto& t : stack_) {
2845                 if(t.cur+1 != t.end) return false;
2846             }
2847             const auto& top = stack_.back();
2848             //if we have to descend into group on next ++ => not last in path
2849             if(top.cur->is_group()) return false;
2850             return true;
2851         }
2852
2853         /** @brief inside a group of alternatives >= minlevel */
2854         bool is_alternative(int minlevel = 0) const noexcept {
2855             if(stack_.empty()) return false;
2856             if(minlevel > 0) minlevel -= 1;
2857             if(minlevel >= int(stack_.size())) return false;
2858             return std::any_of(stack_.begin() + minlevel, stack_.end(),
2859                 [](const context& c) { return c.parent->exclusive(); });
2860         }
2861
2862         /** @brief repeatable or inside a repeatable group >= minlevel */
2863         bool is_repeatable(int minlevel = 0) const noexcept {
2864             if(stack_.empty()) return false;
2865             if(stack_.back().cur->repeatable()) return true;
2866             if(minlevel > 0) minlevel -= 1;
2867             if(minlevel >= int(stack_.size())) return false;
2868             return std::any_of(stack_.begin() + minlevel, stack_.end(),
2869                 [](const context& c) { return c.parent->repeatable(); });
2870         }
2871
2872         /** @brief inside a particular group */
2873         bool is_inside(const group* g) const noexcept {
2874             if(!g) return false;
2875             return std::any_of(stack_.begin(), stack_.end(),
2876                 [g](const context& c) { return c.parent == g; });
2877         }
2878
2879         /** @brief inside group with joinable flags */
2880         bool joinable() const noexcept {
2881             if(stack_.empty()) return false;
2882             return std::any_of(stack_.begin(), stack_.end(),
2883                 [](const context& c) { return c.parent->joinable(); });
2884         }
2885
2886         const context_list&
2887         stack() const {
2888             return stack_;
2889         }
2890
2891         /** @brief innermost repeat group */
2892         const group*
2893         innermost_repeat_group() const noexcept {
2894             auto i = std::find_if(stack_.rbegin(), stack_.rend(),
2895                 [](const context& c) { return c.parent->repeatable(); });
2896             return i != stack_.rend() ? i->parent : nullptr;
2897         }
2898
2899         /** @brief innermost exclusive (alternatives) group */
2900         const group*
2901         innermost_exclusive_group() const noexcept {
2902             auto i = std::find_if(stack_.rbegin(), stack_.rend(),
2903                 [](const context& c) { return c.parent->exclusive(); });
2904             return i != stack_.rend() ? i->parent : nullptr;
2905         }
2906
2907         /** @brief innermost blocking group */
2908         const group*
2909         innermost_blocking_group() const noexcept {
2910             auto i = std::find_if(stack_.rbegin(), stack_.rend(),
2911                 [](const context& c) { return c.parent->blocking(); });
2912             return i != stack_.rend() ? i->parent : nullptr;
2913         }
2914
2915         /** @brief returns the outermost group that will be left on next ++*/
2916         const group*
2917         outermost_blocking_group_fully_explored() const noexcept {
2918             if(stack_.empty()) return nullptr;
2919
2920             const group* g = nullptr;
2921             for(auto i = stack_.rbegin(); i != stack_.rend(); ++i) {
2922                 if(i->cur+1 == i->end) {
2923                     if(i->parent->blocking()) g = i->parent;
2924                 } else {
2925                     return g;
2926                 }
2927             }
2928             return g;
2929         }
2930
2931         /** @brief outermost join group */
2932         const group*
2933         outermost_join_group() const noexcept {
2934             auto i = std::find_if(stack_.begin(), stack_.end(),
2935                 [](const context& c) { return c.parent->joinable(); });
2936             return i != stack_.end() ? i->parent : nullptr;
2937         }
2938
2939         const group* root() const noexcept {
2940             return stack_.empty() ? nullptr : stack_.front().parent;
2941         }
2942
2943         /** @brief common flag prefix of all flags in current group */
2944         arg_string common_flag_prefix() const noexcept {
2945             if(stack_.empty()) return "";
2946             auto g = outermost_join_group();
2947             return g ? g->common_flag_prefix() : arg_string("");
2948         }
2949
2950         const child&
2951         operator * () const noexcept {
2952             return *stack_.back().cur;
2953         }
2954
2955         const child*
2956         operator -> () const noexcept {
2957             return &(*stack_.back().cur);
2958         }
2959
2960         const group&
2961         parent() const noexcept {
2962             return *(stack_.back().parent);
2963         }
2964
2965
2966         /** @brief go to next element of depth first search */
2967         depth_first_traverser&
2968         operator ++ () {
2969             if(stack_.empty()) return *this;
2970             //at group -> decend into group
2971             if(stack_.back().cur->is_group()) {
2972                 stack_.emplace_back(stack_.back().cur->as_group());
2973             }
2974             else {
2975                 next_sibling();
2976             }
2977             return *this;
2978         }
2979
2980         /** @brief go to next sibling of current */
2981         depth_first_traverser&
2982         next_sibling() {
2983             if(stack_.empty()) return *this;
2984             ++stack_.back().cur;
2985             //at the end of current group?
2986             while(stack_.back().cur == stack_.back().end) {
2987                 //go to parent
2988                 stack_.pop_back();
2989                 if(stack_.empty()) return *this;
2990                 //go to next sibling in parent
2991                 ++stack_.back().cur;
2992             }
2993             return *this;
2994         }
2995
2996         /** @brief go to next position after siblings of current */
2997         depth_first_traverser&
2998         next_after_siblings() {
2999             if(stack_.empty()) return *this;
3000             stack_.back().cur = stack_.back().end-1;
3001             next_sibling();
3002             return *this;
3003         }
3004
3005         /**
3006          * @brief
3007          */
3008         depth_first_traverser&
3009         back_to_ancestor(const group* g) {
3010             if(!g) return *this;
3011             while(!stack_.empty()) {
3012                 const auto& top = stack_.back().cur;
3013                 if(top->is_group() && &(top->as_group()) == g) return *this;
3014                 stack_.pop_back();
3015             }
3016             return *this;
3017         }
3018
3019         /** @brief don't visit next siblings, go back to parent on next ++
3020          *         note: renders siblings unreachable for *this
3021          **/
3022         depth_first_traverser&
3023         skip_siblings() {
3024             if(stack_.empty()) return *this;
3025             //future increments won't visit subsequent siblings:
3026             stack_.back().end = stack_.back().cur+1;
3027             return *this;
3028         }
3029
3030         /** @brief skips all other alternatives in surrounding exclusive groups
3031          *         on next ++
3032          *         note: renders alternatives unreachable for *this
3033         */
3034         depth_first_traverser&
3035         skip_alternatives() {
3036             if(stack_.empty()) return *this;
3037
3038             //exclude all other alternatives in surrounding groups
3039             //by making their current position the last one
3040             for(auto& c : stack_) {
3041                 if(c.parent && c.parent->exclusive() && c.cur < c.end)
3042                     c.end = c.cur+1;
3043             }
3044
3045             return *this;
3046         }
3047
3048         void invalidate() {
3049             stack_.clear();
3050         }
3051
3052         inline friend bool operator == (const depth_first_traverser& a,
3053                                         const depth_first_traverser& b)
3054         {
3055             if(a.stack_.empty() || b.stack_.empty()) return false;
3056
3057             //parents not the same -> different position
3058             if(a.stack_.back().parent != b.stack_.back().parent) return false;
3059
3060             bool aEnd = a.stack_.back().cur == a.stack_.back().end;
3061             bool bEnd = b.stack_.back().cur == b.stack_.back().end;
3062             //either both at the end of the same parent => same position
3063             if(aEnd && bEnd) return true;
3064             //or only one at the end => not at the same position
3065             if(aEnd || bEnd) return false;
3066             return std::addressof(*a.stack_.back().cur) ==
3067                    std::addressof(*b.stack_.back().cur);
3068         }
3069         inline friend bool operator != (const depth_first_traverser& a,
3070                                         const depth_first_traverser& b)
3071         {
3072             return !(a == b);
3073         }
3074
3075         memento
3076         undo_point() const {
3077             memento m;
3078             m.level_ = int(stack_.size());
3079             if(!stack_.empty()) m.context_ = stack_.back();
3080             return m;
3081         }
3082
3083         void undo(const memento& m) {
3084             if(m.level_ < 1) return;
3085             if(m.level_ <= int(stack_.size())) {
3086                 stack_.erase(stack_.begin() + m.level_, stack_.end());
3087                 stack_.back() = m.context_;
3088             }
3089             else if(stack_.empty() && m.level_ == 1) {
3090                 stack_.push_back(m.context_);
3091             }
3092         }
3093
3094     private:
3095         context_list stack_;
3096     };
3097
3098
3099     //---------------------------------------------------------------
3100     group() = default;
3101
3102     template<class Param, class... Params>
3103     explicit
3104     group(doc_string docstr, Param param, Params... params):
3105         children_{}, exclusive_{false}, joinable_{false}, scoped_{true}
3106     {
3107         doc(std::move(docstr));
3108         push_back(std::move(param), std::move(params)...);
3109     }
3110
3111     template<class... Params>
3112     explicit
3113     group(parameter param, Params... params):
3114         children_{}, exclusive_{false}, joinable_{false}, scoped_{true}
3115     {
3116         push_back(std::move(param), std::move(params)...);
3117     }
3118
3119     template<class P2, class... Ps>
3120     explicit
3121     group(group p1, P2 p2, Ps... ps):
3122         children_{}, exclusive_{false}, joinable_{false}, scoped_{true}
3123     {
3124         push_back(std::move(p1), std::move(p2), std::move(ps)...);
3125     }
3126
3127
3128     //-----------------------------------------------------
3129     group(const group&) = default;
3130     group(group&&) = default;
3131
3132
3133     //---------------------------------------------------------------
3134     group& operator = (const group&) = default;
3135     group& operator = (group&&) = default;
3136
3137
3138     //---------------------------------------------------------------
3139     /** @brief determines if a command line argument can be matched by a
3140      *         combination of (partial) matches through any number of children
3141      */
3142     group& joinable(bool yes) {
3143         joinable_ = yes;
3144         return *this;
3145     }
3146
3147     /** @brief returns if a command line argument can be matched by a
3148      *         combination of (partial) matches through any number of children
3149      */
3150     bool joinable() const noexcept {
3151         return joinable_;
3152     }
3153
3154
3155     //---------------------------------------------------------------
3156     /** @brief turns explicit scoping on or off
3157      *         operators , & | and other combinating functions will
3158      *         not merge groups that are marked as scoped
3159      */
3160     group& scoped(bool yes) {
3161         scoped_ = yes;
3162         return *this;
3163     }
3164
3165     /** @brief returns true if operators , & | and other combinating functions
3166      *         will merge groups and false otherwise
3167      */
3168     bool scoped() const noexcept
3169     {
3170         return scoped_;
3171     }
3172
3173
3174     //---------------------------------------------------------------
3175     /** @brief determines if children are mutually exclusive alternatives */
3176     group& exclusive(bool yes) {
3177         exclusive_ = yes;
3178         return *this;
3179     }
3180     /** @brief returns if children are mutually exclusive alternatives */
3181     bool exclusive() const noexcept {
3182         return exclusive_;
3183     }
3184
3185
3186     //---------------------------------------------------------------
3187     /** @brief returns true, if any child is required to match */
3188     bool any_required() const
3189     {
3190         return std::any_of(children_.begin(), children_.end(),
3191             [](const child& n){ return n.required(); });
3192     }
3193     /** @brief returns true, if all children are required to match */
3194     bool all_required() const
3195     {
3196         return std::all_of(children_.begin(), children_.end(),
3197             [](const child& n){ return n.required(); });
3198     }
3199
3200
3201     //---------------------------------------------------------------
3202     /** @brief returns true if any child is optional (=non-required) */
3203     bool any_optional() const {
3204         return !all_required();
3205     }
3206     /** @brief returns true if all children are optional (=non-required) */
3207     bool all_optional() const {
3208         return !any_required();
3209     }
3210
3211
3212     //---------------------------------------------------------------
3213     /** @brief returns if the entire group is blocking / positional */
3214     bool blocking() const noexcept {
3215         return token<group>::blocking() || (exclusive() && all_blocking());
3216     }
3217     //-----------------------------------------------------
3218     /** @brief determines if the entire group is blocking / positional */
3219     group& blocking(bool yes) {
3220         return token<group>::blocking(yes);
3221     }
3222
3223     //---------------------------------------------------------------
3224     /** @brief returns true if any child is blocking */
3225     bool any_blocking() const
3226     {
3227         return std::any_of(children_.begin(), children_.end(),
3228             [](const child& n){ return n.blocking(); });
3229     }
3230     //---------------------------------------------------------------
3231     /** @brief returns true if all children is blocking */
3232     bool all_blocking() const
3233     {
3234         return std::all_of(children_.begin(), children_.end(),
3235             [](const child& n){ return n.blocking(); });
3236     }
3237
3238
3239     //---------------------------------------------------------------
3240     /** @brief returns if any child is a value parameter (recursive) */
3241     bool any_flagless() const
3242     {
3243         return std::any_of(children_.begin(), children_.end(),
3244             [](const child& p){
3245                 return p.is_param() && p.as_param().flags().empty();
3246             });
3247     }
3248     /** @brief returns if all children are value parameters (recursive) */
3249     bool all_flagless() const
3250     {
3251         return std::all_of(children_.begin(), children_.end(),
3252             [](const child& p){
3253                 return p.is_param() && p.as_param().flags().empty();
3254             });
3255     }
3256
3257
3258     //---------------------------------------------------------------
3259     /** @brief adds child parameter at the end */
3260     group&
3261     push_back(const parameter& v) {
3262         children_.emplace_back(v);
3263         return *this;
3264     }
3265     //-----------------------------------------------------
3266     /** @brief adds child parameter at the end */
3267     group&
3268     push_back(parameter&& v) {
3269         children_.emplace_back(std::move(v));
3270         return *this;
3271     }
3272     //-----------------------------------------------------
3273     /** @brief adds child group at the end */
3274     group&
3275     push_back(const group& g) {
3276         children_.emplace_back(g);
3277         return *this;
3278     }
3279     //-----------------------------------------------------
3280     /** @brief adds child group at the end */
3281     group&
3282     push_back(group&& g) {
3283         children_.emplace_back(std::move(g));
3284         return *this;
3285     }
3286
3287
3288     //-----------------------------------------------------
3289     /** @brief adds children (groups and/or parameters) */
3290     template<class Param1, class Param2, class... Params>
3291     group&
3292     push_back(Param1&& param1, Param2&& param2, Params&&... params)
3293     {
3294         children_.reserve(children_.size() + 2 + sizeof...(params));
3295         push_back(std::forward<Param1>(param1));
3296         push_back(std::forward<Param2>(param2), std::forward<Params>(params)...);
3297         return *this;
3298     }
3299
3300
3301     //---------------------------------------------------------------
3302     /** @brief adds child parameter at the beginning */
3303     group&
3304     push_front(const parameter& v) {
3305         children_.emplace(children_.begin(), v);
3306         return *this;
3307     }
3308     //-----------------------------------------------------
3309     /** @brief adds child parameter at the beginning */
3310     group&
3311     push_front(parameter&& v) {
3312         children_.emplace(children_.begin(), std::move(v));
3313         return *this;
3314     }
3315     //-----------------------------------------------------
3316     /** @brief adds child group at the beginning */
3317     group&
3318     push_front(const group& g) {
3319         children_.emplace(children_.begin(), g);
3320         return *this;
3321     }
3322     //-----------------------------------------------------
3323     /** @brief adds child group at the beginning */
3324     group&
3325     push_front(group&& g) {
3326         children_.emplace(children_.begin(), std::move(g));
3327         return *this;
3328     }
3329
3330
3331     //---------------------------------------------------------------
3332     /** @brief adds all children of other group at the end */
3333     group&
3334     merge(group&& g)
3335     {
3336         children_.insert(children_.end(),
3337                       std::make_move_iterator(g.begin()),
3338                       std::make_move_iterator(g.end()));
3339         return *this;
3340     }
3341     //-----------------------------------------------------
3342     /** @brief adds all children of several other groups at the end */
3343     template<class... Groups>
3344     group&
3345     merge(group&& g1, group&& g2, Groups&&... gs)
3346     {
3347         merge(std::move(g1));
3348         merge(std::move(g2), std::forward<Groups>(gs)...);
3349         return *this;
3350     }
3351
3352
3353     //---------------------------------------------------------------
3354     /** @brief indexed, nutable access to child */
3355     child& operator [] (size_type index) noexcept {
3356         return children_[index];
3357     }
3358     /** @brief indexed, non-nutable access to child */
3359     const child& operator [] (size_type index) const noexcept {
3360         return children_[index];
3361     }
3362
3363     //---------------------------------------------------------------
3364     /** @brief mutable access to first child */
3365           child& front()       noexcept { return children_.front(); }
3366     /** @brief non-mutable access to first child */
3367     const child& front() const noexcept { return children_.front(); }
3368     //-----------------------------------------------------
3369     /** @brief mutable access to last child */
3370           child& back()       noexcept { return children_.back(); }
3371     /** @brief non-mutable access to last child */
3372     const child& back() const noexcept { return children_.back(); }
3373
3374
3375     //---------------------------------------------------------------
3376     /** @brief returns true, if group has no children, false otherwise */
3377     bool empty() const noexcept     { return children_.empty(); }
3378
3379     /** @brief returns number of children */
3380     size_type size() const noexcept { return children_.size(); }
3381
3382     /** @brief returns number of nested levels; 1 for a flat group */
3383     size_type depth() const {
3384         size_type n = 0;
3385         for(const auto& c : children_) {
3386             auto l = 1 + c.depth();
3387             if(l > n) n = l;
3388         }
3389         return n;
3390     }
3391
3392
3393     //---------------------------------------------------------------
3394     /** @brief returns mutating iterator to position of first element */
3395           iterator  begin()       noexcept { return children_.begin(); }
3396     /** @brief returns non-mutating iterator to position of first element */
3397     const_iterator  begin() const noexcept { return children_.begin(); }
3398     /** @brief returns non-mutating iterator to position of first element */
3399     const_iterator cbegin() const noexcept { return children_.begin(); }
3400
3401     /** @brief returns mutating iterator to position one past the last element */
3402           iterator  end()         noexcept { return children_.end(); }
3403     /** @brief returns non-mutating iterator to position one past the last element */
3404     const_iterator  end()   const noexcept { return children_.end(); }
3405     /** @brief returns non-mutating iterator to position one past the last element */
3406     const_iterator cend()   const noexcept { return children_.end(); }
3407
3408
3409     //---------------------------------------------------------------
3410     /** @brief returns augmented iterator for depth first searches
3411      *  @details traverser knows end of iteration and can skip over children
3412      */
3413     depth_first_traverser
3414     begin_dfs() const noexcept {
3415         return depth_first_traverser{*this};
3416     }
3417
3418
3419     //---------------------------------------------------------------
3420     /** @brief returns recursive parameter count */
3421     size_type param_count() const {
3422         size_type c = 0;
3423         for(const auto& n : children_) {
3424             c += n.param_count();
3425         }
3426         return c;
3427     }
3428
3429
3430     //---------------------------------------------------------------
3431     /** @brief returns range of all flags (recursive) */
3432     arg_list all_flags() const
3433     {
3434         std::vector<arg_string> all;
3435         gather_flags(children_, all);
3436         return all;
3437     }
3438
3439     /** @brief returns true, if no flag occurs as true
3440      *         prefix of any other flag (identical flags will be ignored) */
3441     bool flags_are_prefix_free() const
3442     {
3443         const auto fs = all_flags();
3444
3445         using std::begin; using std::end;
3446         for(auto i = begin(fs), e = end(fs); i != e; ++i) {
3447             if(!i->empty()) {
3448                 for(auto j = i+1; j != e; ++j) {
3449                     if(!j->empty() && *i != *j) {
3450                         if(i->find(*j) == 0) return false;
3451                         if(j->find(*i) == 0) return false;
3452                     }
3453                 }
3454             }
3455         }
3456
3457         return true;
3458     }
3459
3460
3461     //---------------------------------------------------------------
3462     /** @brief returns longest common prefix of all flags */
3463     arg_string common_flag_prefix() const
3464     {
3465         arg_list prefixes;
3466         gather_prefixes(children_, prefixes);
3467         return str::longest_common_prefix(prefixes);
3468     }
3469
3470
3471 private:
3472     //---------------------------------------------------------------
3473     static void
3474     gather_flags(const children_store& nodes, arg_list& all)
3475     {
3476         for(const auto& p : nodes) {
3477             if(p.is_group()) {
3478                 gather_flags(p.as_group().children_, all);
3479             }
3480             else {
3481                 const auto& pf = p.as_param().flags();
3482                 using std::begin;
3483                 using std::end;
3484                 if(!pf.empty()) all.insert(end(all), begin(pf), end(pf));
3485             }
3486         }
3487     }
3488     //---------------------------------------------------------------
3489     static void
3490     gather_prefixes(const children_store& nodes, arg_list& all)
3491     {
3492         for(const auto& p : nodes) {
3493             if(p.is_group()) {
3494                 gather_prefixes(p.as_group().children_, all);
3495             }
3496             else if(!p.as_param().flags().empty()) {
3497                 auto pfx = str::longest_common_prefix(p.as_param().flags());
3498                 if(!pfx.empty()) all.push_back(std::move(pfx));
3499             }
3500         }
3501     }
3502
3503     //---------------------------------------------------------------
3504     children_store children_;
3505     bool exclusive_ = false;
3506     bool joinable_ = false;
3507     bool scoped_ = false;
3508 };
3509
3510
3511
3512 /*************************************************************************//**
3513  *
3514  * @brief group or parameter
3515  *
3516  *****************************************************************************/
3517 using pattern = group::child;
3518
3519
3520
3521 /*************************************************************************//**
3522  *
3523  * @brief apply an action to all parameters in a group
3524  *
3525  *****************************************************************************/
3526 template<class Action>
3527 void for_all_params(group& g, Action&& action)
3528 {
3529     for(auto& p : g) {
3530         if(p.is_group()) {
3531             for_all_params(p.as_group(), action);
3532         }
3533         else {
3534             action(p.as_param());
3535         }
3536     }
3537 }
3538
3539 template<class Action>
3540 void for_all_params(const group& g, Action&& action)
3541 {
3542     for(auto& p : g) {
3543         if(p.is_group()) {
3544             for_all_params(p.as_group(), action);
3545         }
3546         else {
3547             action(p.as_param());
3548         }
3549     }
3550 }
3551
3552
3553
3554 /*************************************************************************//**
3555  *
3556  * @brief makes a group of parameters and/or groups
3557  *
3558  *****************************************************************************/
3559 inline group
3560 operator , (parameter a, parameter b)
3561 {
3562     return group{std::move(a), std::move(b)}.scoped(false);
3563 }
3564
3565 //---------------------------------------------------------
3566 inline group
3567 operator , (parameter a, group b)
3568 {
3569     return !b.scoped() && !b.blocking() && !b.exclusive() && !b.repeatable()
3570         && !b.joinable() && (b.doc().empty() || b.doc() == a.doc())
3571        ? b.push_front(std::move(a))
3572        : group{std::move(a), std::move(b)}.scoped(false);
3573 }
3574
3575 //---------------------------------------------------------
3576 inline group
3577 operator , (group a, parameter b)
3578 {
3579     return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable()
3580         && !a.joinable() && (a.doc().empty() || a.doc() == b.doc())
3581        ? a.push_back(std::move(b))
3582        : group{std::move(a), std::move(b)}.scoped(false);
3583 }
3584
3585 //---------------------------------------------------------
3586 inline group
3587 operator , (group a, group b)
3588 {
3589     return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable()
3590         && !a.joinable() && (a.doc().empty() || a.doc() == b.doc())
3591        ? a.push_back(std::move(b))
3592        : group{std::move(a), std::move(b)}.scoped(false);
3593 }
3594
3595
3596
3597 /*************************************************************************//**
3598  *
3599  * @brief makes a group of alternative parameters or groups
3600  *
3601  *****************************************************************************/
3602 template<class Param, class... Params>
3603 inline group
3604 one_of(Param param, Params... params)
3605 {
3606     return group{std::move(param), std::move(params)...}.exclusive(true);
3607 }
3608
3609
3610 /*************************************************************************//**
3611  *
3612  * @brief makes a group of alternative parameters or groups
3613  *
3614  *****************************************************************************/
3615 inline group
3616 operator | (parameter a, parameter b)
3617 {
3618     return group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3619 }
3620
3621 //-------------------------------------------------------------------
3622 inline group
3623 operator | (parameter a, group b)
3624 {
3625     return !b.scoped() && !b.blocking() && b.exclusive() && !b.repeatable()
3626         && !b.joinable()
3627         && (b.doc().empty() || b.doc() == a.doc())
3628         ? b.push_front(std::move(a))
3629         : group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3630 }
3631
3632 //-------------------------------------------------------------------
3633 inline group
3634 operator | (group a, parameter b)
3635 {
3636     return !a.scoped() && a.exclusive() && !a.repeatable() && !a.joinable()
3637         && a.blocking() == b.blocking()
3638         && (a.doc().empty() || a.doc() == b.doc())
3639         ? a.push_back(std::move(b))
3640         : group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3641 }
3642
3643 inline group
3644 operator | (group a, group b)
3645 {
3646     return !a.scoped() && a.exclusive() &&!a.repeatable() && !a.joinable()
3647         && a.blocking() == b.blocking()
3648         && (a.doc().empty() || a.doc() == b.doc())
3649         ? a.push_back(std::move(b))
3650         : group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3651 }
3652
3653
3654
3655 /*************************************************************************//**
3656  *
3657  * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!)
3658  *        no interface guarantees; might be changed or removed in the future
3659  *
3660  *****************************************************************************/
3661 namespace detail {
3662
3663 inline void set_blocking(bool) {}
3664
3665 template<class P, class... Ps>
3666 void set_blocking(bool yes, P& p, Ps&... ps) {
3667     p.blocking(yes);
3668     set_blocking(yes, ps...);
3669 }
3670
3671 } // namespace detail
3672
3673
3674 /*************************************************************************//**
3675  *
3676  * @brief makes a parameter/group sequence by making all input objects blocking
3677  *
3678  *****************************************************************************/
3679 template<class Param, class... Params>
3680 inline group
3681 in_sequence(Param param, Params... params)
3682 {
3683     detail::set_blocking(true, param, params...);
3684     return group{std::move(param), std::move(params)...}.scoped(true);
3685 }
3686
3687
3688 /*************************************************************************//**
3689  *
3690  * @brief makes a parameter/group sequence by making all input objects blocking
3691  *
3692  *****************************************************************************/
3693 inline group
3694 operator & (parameter a, parameter b)
3695 {
3696     a.blocking(true);
3697     b.blocking(true);
3698     return group{std::move(a), std::move(b)}.scoped(true);
3699 }
3700
3701 //---------------------------------------------------------
3702 inline group
3703 operator & (parameter a, group b)
3704 {
3705     a.blocking(true);
3706     return group{std::move(a), std::move(b)}.scoped(true);
3707 }
3708
3709 //---------------------------------------------------------
3710 inline group
3711 operator & (group a, parameter b)
3712 {
3713     b.blocking(true);
3714     if(a.all_blocking() && !a.exclusive() && !a.repeatable() && !a.joinable()
3715         && (a.doc().empty() || a.doc() == b.doc()))
3716     {
3717         return a.push_back(std::move(b));
3718     }
3719     else {
3720         if(!a.all_blocking()) a.blocking(true);
3721         return group{std::move(a), std::move(b)}.scoped(true);
3722     }
3723 }
3724
3725 inline group
3726 operator & (group a, group b)
3727 {
3728     if(!b.all_blocking()) b.blocking(true);
3729     if(a.all_blocking() && !a.exclusive() && !a.repeatable()
3730         && !a.joinable() && (a.doc().empty() || a.doc() == b.doc()))
3731     {
3732         return a.push_back(std::move(b));
3733     }
3734     else {
3735         if(!a.all_blocking()) a.blocking(true);
3736         return group{std::move(a), std::move(b)}.scoped(true);
3737     }
3738 }
3739
3740
3741
3742 /*************************************************************************//**
3743  *
3744  * @brief makes a group of parameters and/or groups
3745  *        where all single char flag params ("-a", "b", ...) are joinable
3746  *
3747  *****************************************************************************/
3748 inline group
3749 joinable(group g) {
3750     return g.joinable(true);
3751 }
3752
3753 //-------------------------------------------------------------------
3754 template<class... Params>
3755 inline group
3756 joinable(parameter param, Params... params)
3757 {
3758     return group{std::move(param), std::move(params)...}.joinable(true);
3759 }
3760
3761 template<class P2, class... Ps>
3762 inline group
3763 joinable(group p1, P2 p2, Ps... ps)
3764 {
3765     return group{std::move(p1), std::move(p2), std::move(ps)...}.joinable(true);
3766 }
3767
3768 template<class Param, class... Params>
3769 inline group
3770 joinable(doc_string docstr, Param param, Params... params)
3771 {
3772     return group{std::move(param), std::move(params)...}
3773                 .joinable(true).doc(std::move(docstr));
3774 }
3775
3776
3777
3778 /*************************************************************************//**
3779  *
3780  * @brief makes a repeatable copy of a parameter
3781  *
3782  *****************************************************************************/
3783 inline parameter
3784 repeatable(parameter p) {
3785     return p.repeatable(true);
3786 }
3787
3788 /*************************************************************************//**
3789  *
3790  * @brief makes a repeatable copy of a group
3791  *
3792  *****************************************************************************/
3793 inline group
3794 repeatable(group g) {
3795     return g.repeatable(true);
3796 }
3797
3798
3799
3800 /*************************************************************************//**
3801  *
3802  * @brief makes a group of parameters and/or groups
3803  *        that is repeatable as a whole
3804  *        Note that a repeatable group consisting entirely of non-blocking
3805  *        children is equivalent to a non-repeatable group of
3806  *        repeatable children.
3807  *
3808  *****************************************************************************/
3809 template<class P2, class... Ps>
3810 inline group
3811 repeatable(parameter p1, P2 p2, Ps... ps)
3812 {
3813     return group{std::move(p1), std::move(p2),
3814                  std::move(ps)...}.repeatable(true);
3815 }
3816
3817 template<class P2, class... Ps>
3818 inline group
3819 repeatable(group p1, P2 p2, Ps... ps)
3820 {
3821     return group{std::move(p1), std::move(p2),
3822                  std::move(ps)...}.repeatable(true);
3823 }
3824
3825
3826
3827 /*************************************************************************//**
3828  *
3829  * @brief makes a parameter greedy (match with top priority)
3830  *
3831  *****************************************************************************/
3832 inline parameter
3833 greedy(parameter p) {
3834     return p.greedy(true);
3835 }
3836
3837 inline parameter
3838 operator ! (parameter p) {
3839     return greedy(p);
3840 }
3841
3842
3843
3844 /*************************************************************************//**
3845  *
3846  * @brief recursively prepends a prefix to all flags
3847  *
3848  *****************************************************************************/
3849 inline parameter&&
3850 with_prefix(const arg_string& prefix, parameter&& p) {
3851     return std::move(with_prefix(prefix, p));
3852 }
3853
3854
3855 //-------------------------------------------------------------------
3856 inline group&
3857 with_prefix(const arg_string& prefix, group& g)
3858 {
3859     for(auto& p : g) {
3860         if(p.is_group()) {
3861             with_prefix(prefix, p.as_group());
3862         } else {
3863             with_prefix(prefix, p.as_param());
3864         }
3865     }
3866     return g;
3867 }
3868
3869
3870 inline group&&
3871 with_prefix(const arg_string& prefix, group&& params)
3872 {
3873     return std::move(with_prefix(prefix, params));
3874 }
3875
3876
3877 template<class Param, class... Params>
3878 inline group
3879 with_prefix(arg_string prefix, Param&& param, Params&&... params)
3880 {
3881     return with_prefix(prefix, group{std::forward<Param>(param),
3882                                      std::forward<Params>(params)...});
3883 }
3884
3885
3886
3887 /*************************************************************************//**
3888  *
3889  * @brief recursively prepends a prefix to all flags
3890  *
3891  * @param shortpfx : used for single-letter flags
3892  * @param longpfx  : used for flags with length > 1
3893  *
3894  *****************************************************************************/
3895 inline parameter&&
3896 with_prefixes_short_long(const arg_string& shortpfx, const arg_string& longpfx,
3897                          parameter&& p)
3898 {
3899     return std::move(with_prefixes_short_long(shortpfx, longpfx, p));
3900 }
3901
3902
3903 //-------------------------------------------------------------------
3904 inline group&
3905 with_prefixes_short_long(const arg_string& shortFlagPrefix,
3906                          const arg_string& longFlagPrefix,
3907                          group& g)
3908 {
3909     for(auto& p : g) {
3910         if(p.is_group()) {
3911             with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_group());
3912         } else {
3913             with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_param());
3914         }
3915     }
3916     return g;
3917 }
3918
3919
3920 inline group&&
3921 with_prefixes_short_long(const arg_string& shortFlagPrefix,
3922                          const arg_string& longFlagPrefix,
3923                          group&& params)
3924 {
3925     return std::move(with_prefixes_short_long(shortFlagPrefix, longFlagPrefix,
3926                                               params));
3927 }
3928
3929
3930 template<class Param, class... Params>
3931 inline group
3932 with_prefixes_short_long(const arg_string& shortFlagPrefix,
3933                          const arg_string& longFlagPrefix,
3934                          Param&& param, Params&&... params)
3935 {
3936     return with_prefixes_short_long(shortFlagPrefix, longFlagPrefix,
3937                                     group{std::forward<Param>(param),
3938                                           std::forward<Params>(params)...});
3939 }
3940
3941
3942
3943 /*************************************************************************//**
3944  *
3945  * @brief recursively prepends a suffix to all flags
3946  *
3947  *****************************************************************************/
3948 inline parameter&&
3949 with_suffix(const arg_string& suffix, parameter&& p) {
3950     return std::move(with_suffix(suffix, p));
3951 }
3952
3953
3954 //-------------------------------------------------------------------
3955 inline group&
3956 with_suffix(const arg_string& suffix, group& g)
3957 {
3958     for(auto& p : g) {
3959         if(p.is_group()) {
3960             with_suffix(suffix, p.as_group());
3961         } else {
3962             with_suffix(suffix, p.as_param());
3963         }
3964     }
3965     return g;
3966 }
3967
3968
3969 inline group&&
3970 with_suffix(const arg_string& suffix, group&& params)
3971 {
3972     return std::move(with_suffix(suffix, params));
3973 }
3974
3975
3976 template<class Param, class... Params>
3977 inline group
3978 with_suffix(arg_string suffix, Param&& param, Params&&... params)
3979 {
3980     return with_suffix(suffix, group{std::forward<Param>(param),
3981                                      std::forward<Params>(params)...});
3982 }
3983
3984
3985
3986 /*************************************************************************//**
3987  *
3988  * @brief recursively prepends a suffix to all flags
3989  *
3990  * @param shortsfx : used for single-letter flags
3991  * @param longsfx  : used for flags with length > 1
3992  *
3993  *****************************************************************************/
3994 inline parameter&&
3995 with_suffixes_short_long(const arg_string& shortsfx, const arg_string& longsfx,
3996                          parameter&& p)
3997 {
3998     return std::move(with_suffixes_short_long(shortsfx, longsfx, p));
3999 }
4000
4001
4002 //-------------------------------------------------------------------
4003 inline group&
4004 with_suffixes_short_long(const arg_string& shortFlagSuffix,
4005                          const arg_string& longFlagSuffix,
4006                          group& g)
4007 {
4008     for(auto& p : g) {
4009         if(p.is_group()) {
4010             with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, p.as_group());
4011         } else {
4012             with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, p.as_param());
4013         }
4014     }
4015     return g;
4016 }
4017
4018
4019 inline group&&
4020 with_suffixes_short_long(const arg_string& shortFlagSuffix,
4021                          const arg_string& longFlagSuffix,
4022                          group&& params)
4023 {
4024     return std::move(with_suffixes_short_long(shortFlagSuffix, longFlagSuffix,
4025                                               params));
4026 }
4027
4028
4029 template<class Param, class... Params>
4030 inline group
4031 with_suffixes_short_long(const arg_string& shortFlagSuffix,
4032                          const arg_string& longFlagSuffix,
4033                          Param&& param, Params&&... params)
4034 {
4035     return with_suffixes_short_long(shortFlagSuffix, longFlagSuffix,
4036                                     group{std::forward<Param>(param),
4037                                           std::forward<Params>(params)...});
4038 }
4039
4040
4041
4042
4043
4044
4045
4046
4047 /*************************************************************************//**
4048  *
4049  * @brief parsing implementation details
4050  *
4051  *****************************************************************************/
4052
4053 namespace detail {
4054
4055
4056 /*************************************************************************//**
4057  *
4058  * @brief DFS traverser that keeps track of 'scopes'
4059  *        scope = all parameters that are either bounded by
4060  *        two blocking parameters on the same depth level
4061  *        or the beginning/end of the outermost group
4062  *
4063  *****************************************************************************/
4064 class scoped_dfs_traverser
4065 {
4066 public:
4067     using dfs_traverser = group::depth_first_traverser;
4068
4069     scoped_dfs_traverser() = default;
4070
4071     explicit
4072     scoped_dfs_traverser(const group& g):
4073         pos_{g}, lastMatch_{}, posAfterLastMatch_{}, scopes_{},
4074         ignoreBlocks_{false},
4075         repeatGroupStarted_{false}, repeatGroupContinues_{false}
4076     {}
4077
4078     const dfs_traverser& base() const noexcept { return pos_; }
4079     const dfs_traverser& last_match() const noexcept { return lastMatch_; }
4080
4081     const group& parent() const noexcept { return pos_.parent(); }
4082
4083     const group* innermost_repeat_group() const noexcept {
4084         return pos_.innermost_repeat_group();
4085     }
4086     const group* outermost_join_group() const noexcept {
4087         return pos_.outermost_join_group();
4088     }
4089     const group* innermost_blocking_group() const noexcept {
4090         return pos_.innermost_blocking_group();
4091     }
4092     const group* innermost_exclusive_group() const noexcept {
4093         return pos_.innermost_exclusive_group();
4094     }
4095
4096     const pattern* operator ->() const noexcept { return pos_.operator->(); }
4097     const pattern& operator *() const noexcept { return *pos_; }
4098
4099     const pattern* ptr() const noexcept { return pos_.operator->(); }
4100
4101     explicit operator bool() const noexcept { return bool(pos_); }
4102
4103     bool joinable() const noexcept { return pos_.joinable(); }
4104     arg_string common_flag_prefix() const { return pos_.common_flag_prefix(); }
4105
4106     void ignore_blocking(bool yes) { ignoreBlocks_ = yes; }
4107
4108     void invalidate() {
4109         pos_.invalidate();
4110     }
4111
4112     bool matched() const noexcept {
4113         return (pos_ == lastMatch_);
4114     }
4115
4116     bool start_of_repeat_group() const noexcept { return repeatGroupStarted_; }
4117
4118     //-----------------------------------------------------
4119     scoped_dfs_traverser&
4120     next_sibling() { pos_.next_sibling(); return *this; }
4121
4122     scoped_dfs_traverser&
4123     next_after_siblings() { pos_.next_after_siblings(); return *this; }
4124
4125
4126     //-----------------------------------------------------
4127     scoped_dfs_traverser&
4128     operator ++ ()
4129     {
4130         if(!pos_) return *this;
4131
4132         if(pos_.is_last_in_path()) {
4133             return_to_outermost_scope();
4134             return *this;
4135         }
4136
4137         //current pattern can block if it didn't match already
4138         if(ignoreBlocks_ || matched()) {
4139             ++pos_;
4140         }
4141         else if(!pos_->is_group()) {
4142             //current group can block if we didn't have any match in it
4143             const group* g = pos_.outermost_blocking_group_fully_explored();
4144             //no match in 'g' before -> skip to after its siblings
4145             if(g && !lastMatch_.is_inside(g)) {
4146                 pos_.back_to_ancestor(g).next_after_siblings();
4147                 if(!pos_) return_to_outermost_scope();
4148             }
4149             else if(pos_->blocking()) {
4150                 if(pos_.parent().exclusive()) {
4151                     pos_.next_sibling();
4152                 } else {
4153                     //no match => skip siblings of blocking param
4154                     pos_.next_after_siblings();
4155                 }
4156                 if(!pos_) return_to_outermost_scope();
4157             } else {
4158                 ++pos_;
4159             }
4160         } else {
4161             ++pos_;
4162         }
4163         check_if_left_scope();
4164         return *this;
4165     }
4166
4167     //-----------------------------------------------------
4168     void next_after_match(scoped_dfs_traverser match)
4169     {
4170         if(!match || ignoreBlocks_) return;
4171
4172         check_repeat_group_start(match);
4173
4174         lastMatch_ = match.base();
4175
4176         // if there is a blocking ancestor -> go back to it
4177         if(!match->blocking()) {
4178             match.pos_.back_to_ancestor(match.innermost_blocking_group());
4179         }
4180
4181         //if match is not in current position & current position is blocking
4182         //=> current position has to be advanced by one so that it is
4183         //no longer reachable within current scope
4184         //(can happen for repeatable, blocking parameters)
4185         if(match.base() != pos_ && pos_->blocking()) pos_.next_sibling();
4186
4187         if(match->blocking()) {
4188             if(match.pos_.is_alternative()) {
4189                 //discard other alternatives
4190                 match.pos_.skip_alternatives();
4191             }
4192
4193             if(is_last_in_current_scope(match.pos_)) {
4194                 //if current param is not repeatable -> back to previous scope
4195                 if(!match->repeatable() && !match->is_group()) {
4196                     pos_ = std::move(match.pos_);
4197                     if(!scopes_.empty()) pos_.undo(scopes_.top());
4198                 }
4199                 else { //stay at match position
4200                     pos_ = std::move(match.pos_);
4201                 }
4202             }
4203             else { //not last in current group
4204                 //if current param is not repeatable, go directly to next
4205                 if(!match->repeatable() && !match->is_group()) {
4206                     ++match.pos_;
4207                 }
4208
4209                 if(match.pos_.level() > pos_.level()) {
4210                     scopes_.push(pos_.undo_point());
4211                     pos_ = std::move(match.pos_);
4212                 }
4213                 else if(match.pos_.level() < pos_.level()) {
4214                     return_to_level(match.pos_.level());
4215                 }
4216                 else {
4217                     pos_ = std::move(match.pos_);
4218                 }
4219             }
4220             posAfterLastMatch_ = pos_;
4221         }
4222         else {
4223             if(match.pos_.level() < pos_.level()) {
4224                 return_to_level(match.pos_.level());
4225             }
4226             posAfterLastMatch_ = pos_;
4227         }
4228         repeatGroupContinues_ = repeat_group_continues();
4229     }
4230
4231 private:
4232     //-----------------------------------------------------
4233     bool is_last_in_current_scope(const dfs_traverser& pos) const
4234     {
4235         if(scopes_.empty()) return pos.is_last_in_path();
4236         //check if we would leave the current scope on ++
4237         auto p = pos;
4238         ++p;
4239         return p.level() < scopes_.top().level();
4240     }
4241
4242     //-----------------------------------------------------
4243     void check_repeat_group_start(const scoped_dfs_traverser& newMatch)
4244     {
4245         const auto newrg = newMatch.innermost_repeat_group();
4246         if(!newrg) {
4247             repeatGroupStarted_ = false;
4248         }
4249         else if(lastMatch_.innermost_repeat_group() != newrg) {
4250             repeatGroupStarted_ = true;
4251         }
4252         else if(!repeatGroupContinues_ || !newMatch.repeatGroupContinues_) {
4253             repeatGroupStarted_ = true;
4254         }
4255         else {
4256             //special case: repeat group is outermost group
4257             //=> we can never really 'leave' and 'reenter' it
4258             //but if the current scope is the first element, then we are
4259             //conceptually at a position 'before' the group
4260             repeatGroupStarted_ = scopes_.empty() || (
4261                     newrg == pos_.root() &&
4262                     scopes_.top().param() == &(*pos_.root()->begin()) );
4263         }
4264         repeatGroupContinues_ = repeatGroupStarted_;
4265     }
4266
4267     //-----------------------------------------------------
4268     bool repeat_group_continues() const
4269     {
4270         if(!repeatGroupContinues_) return false;
4271         const auto curRepGroup = pos_.innermost_repeat_group();
4272         if(!curRepGroup) return false;
4273         if(curRepGroup != lastMatch_.innermost_repeat_group()) return false;
4274         if(!posAfterLastMatch_) return false;
4275         return true;
4276     }
4277
4278     //-----------------------------------------------------
4279     void check_if_left_scope()
4280     {
4281         if(posAfterLastMatch_) {
4282             if(pos_.level() < posAfterLastMatch_.level()) {
4283                 while(!scopes_.empty() && scopes_.top().level() >= pos_.level()) {
4284                     pos_.undo(scopes_.top());
4285                     scopes_.pop();
4286                 }
4287                 posAfterLastMatch_.invalidate();
4288             }
4289         }
4290         while(!scopes_.empty() && scopes_.top().level() > pos_.level()) {
4291             pos_.undo(scopes_.top());
4292             scopes_.pop();
4293         }
4294         repeatGroupContinues_ = repeat_group_continues();
4295     }
4296
4297     //-----------------------------------------------------
4298     void return_to_outermost_scope()
4299     {
4300         posAfterLastMatch_.invalidate();
4301
4302         if(scopes_.empty()) {
4303             pos_.invalidate();
4304             repeatGroupContinues_ = false;
4305             return;
4306         }
4307
4308         while(!scopes_.empty() && (!pos_ || pos_.level() >= 1)) {
4309             pos_.undo(scopes_.top());
4310             scopes_.pop();
4311         }
4312         while(!scopes_.empty()) scopes_.pop();
4313
4314         repeatGroupContinues_ = repeat_group_continues();
4315     }
4316
4317     //-----------------------------------------------------
4318     void return_to_level(int level)
4319     {
4320         if(pos_.level() <= level) return;
4321         while(!scopes_.empty() && pos_.level() > level) {
4322             pos_.undo(scopes_.top());
4323             scopes_.pop();
4324         }
4325     };
4326
4327     dfs_traverser pos_;
4328     dfs_traverser lastMatch_;
4329     dfs_traverser posAfterLastMatch_;
4330     std::stack<dfs_traverser::memento> scopes_;
4331     bool ignoreBlocks_ = false;
4332     bool repeatGroupStarted_ = false;
4333     bool repeatGroupContinues_ = false;
4334 };
4335
4336
4337
4338
4339 /*****************************************************************************
4340  *
4341  * some parameter property predicates
4342  *
4343  *****************************************************************************/
4344 struct select_all {
4345     bool operator () (const parameter&) const noexcept { return true; }
4346 };
4347
4348 struct select_flags {
4349     bool operator () (const parameter& p) const noexcept {
4350         return !p.flags().empty();
4351     }
4352 };
4353
4354 struct select_values {
4355     bool operator () (const parameter& p) const noexcept {
4356         return p.flags().empty();
4357     }
4358 };
4359
4360
4361
4362 /*************************************************************************//**
4363  *
4364  * @brief result of a matching operation
4365  *
4366  *****************************************************************************/
4367 class match_t {
4368 public:
4369     using size_type = arg_string::size_type;
4370
4371     match_t() = default;
4372
4373     match_t(arg_string s, scoped_dfs_traverser p):
4374         str_{std::move(s)}, pos_{std::move(p)}
4375     {}
4376
4377     size_type length() const noexcept { return str_.size(); }
4378
4379     const arg_string& str() const noexcept { return str_; }
4380     const scoped_dfs_traverser& pos() const noexcept { return pos_; }
4381
4382     explicit operator bool() const noexcept { return bool(pos_); }
4383
4384 private:
4385     arg_string str_;
4386     scoped_dfs_traverser pos_;
4387 };
4388
4389
4390
4391 /*************************************************************************//**
4392  *
4393  * @brief finds the first parameter that matches a given string;
4394  *        candidate parameters are traversed using a scoped DFS traverser
4395  *
4396  *****************************************************************************/
4397 template<class ParamSelector>
4398 match_t
4399 full_match(scoped_dfs_traverser pos, const arg_string& arg,
4400            const ParamSelector& select)
4401 {
4402     while(pos) {
4403         if(pos->is_param()) {
4404             const auto& param = pos->as_param();
4405             if(select(param)) {
4406                 const auto match = param.match(arg);
4407                 if(match && match.length() == arg.size()) {
4408                     return match_t{arg, std::move(pos)};
4409                 }
4410             }
4411         }
4412         ++pos;
4413     }
4414     return match_t{};
4415 }
4416
4417
4418
4419 /*************************************************************************//**
4420  *
4421  * @brief finds the first parameter that matches any (non-empty) prefix
4422  *        of a given string;
4423  *        candidate parameters are traversed using a scoped DFS traverser
4424  *
4425  *****************************************************************************/
4426 template<class ParamSelector>
4427 match_t
4428 longest_prefix_match(scoped_dfs_traverser pos, const arg_string& arg,
4429                      const ParamSelector& select)
4430 {
4431     match_t longest;
4432
4433     while(pos) {
4434         if(pos->is_param()) {
4435             const auto& param = pos->as_param();
4436             if(select(param)) {
4437                 auto match = param.match(arg);
4438                 if(match.prefix()) {
4439                     if(match.length() == arg.size()) {
4440                         return match_t{arg, std::move(pos)};
4441                     }
4442                     else if(match.length() > longest.length()) {
4443                         longest = match_t{arg.substr(match.at(), match.length()), 
4444                                           pos};
4445                     }
4446                 }
4447             }
4448         }
4449         ++pos;
4450     }
4451     return longest;
4452 }
4453
4454
4455
4456 /*************************************************************************//**
4457  *
4458  * @brief finds the first parameter that partially matches a given string;
4459  *        candidate parameters are traversed using a scoped DFS traverser
4460  *
4461  *****************************************************************************/
4462 template<class ParamSelector>
4463 match_t
4464 partial_match(scoped_dfs_traverser pos, const arg_string& arg,
4465               const ParamSelector& select)
4466 {
4467     while(pos) {
4468         if(pos->is_param()) {
4469             const auto& param = pos->as_param();
4470             if(select(param)) {
4471                 const auto match = param.match(arg);
4472                 if(match) {
4473                     return match_t{arg.substr(match.at(), match.length()),
4474                                    std::move(pos)};
4475                 }
4476             }
4477         }
4478         ++pos;
4479     }
4480     return match_t{};
4481 }
4482
4483 } //namespace detail
4484
4485
4486
4487
4488
4489
4490 /***************************************************************//**
4491  *
4492  * @brief default command line arguments parser
4493  *
4494  *******************************************************************/
4495 class parser
4496 {
4497 public:
4498     using dfs_traverser = group::depth_first_traverser;
4499     using scoped_dfs_traverser = detail::scoped_dfs_traverser;
4500
4501
4502     /*****************************************************//**
4503      * @brief arg -> parameter mapping
4504      *********************************************************/
4505     class arg_mapping {
4506     public:
4507         friend class parser;
4508
4509         explicit
4510         arg_mapping(arg_index idx, arg_string s,
4511                     const dfs_traverser& match)
4512         :
4513             index_{idx}, arg_{std::move(s)}, match_{match},
4514             repeat_{0}, startsRepeatGroup_{false},
4515             blocked_{false}, conflict_{false}
4516         {}
4517
4518         explicit
4519         arg_mapping(arg_index idx, arg_string s) :
4520             index_{idx}, arg_{std::move(s)}, match_{},
4521             repeat_{0}, startsRepeatGroup_{false},
4522             blocked_{false}, conflict_{false}
4523         {}
4524
4525         arg_index index() const noexcept { return index_; }
4526         const arg_string& arg() const noexcept { return arg_; }
4527
4528         const parameter* param() const noexcept {
4529             return match_ && match_->is_param()
4530                 ? &(match_->as_param()) : nullptr;
4531         }
4532
4533         std::size_t repeat() const noexcept { return repeat_; }
4534
4535         bool blocked() const noexcept { return blocked_; }
4536         bool conflict() const noexcept { return conflict_; }
4537
4538         bool bad_repeat() const noexcept {
4539             if(!param()) return false;
4540             return repeat_ > 0 && !param()->repeatable()
4541                 && !match_.innermost_repeat_group();
4542         }
4543
4544         bool any_error() const noexcept {
4545             return !match_ || blocked() || conflict() || bad_repeat();
4546         }
4547
4548     private:
4549         arg_index index_;
4550         arg_string arg_;
4551         dfs_traverser match_;
4552         std::size_t repeat_;
4553         bool startsRepeatGroup_;
4554         bool blocked_;
4555         bool conflict_;
4556     };
4557
4558     /*****************************************************//**
4559      * @brief references a non-matched, required parameter
4560      *********************************************************/
4561     class missing_event {
4562     public:
4563         explicit
4564         missing_event(const parameter* p, arg_index after):
4565             param_{p}, aftIndex_{after}
4566         {}
4567
4568         const parameter* param() const noexcept { return param_; }
4569
4570         arg_index after_index() const noexcept { return aftIndex_; }
4571
4572     private:
4573         const parameter* param_;
4574         arg_index aftIndex_;
4575     };
4576
4577     //-----------------------------------------------------
4578     using missing_events = std::vector<missing_event>;
4579     using arg_mappings   = std::vector<arg_mapping>;
4580
4581
4582 private:
4583     struct miss_candidate {
4584         miss_candidate(dfs_traverser p, arg_index idx,
4585                        bool firstInRepeatGroup = false):
4586             pos{std::move(p)}, index{idx},
4587             startsRepeatGroup{firstInRepeatGroup}
4588         {}
4589
4590         dfs_traverser pos;
4591         arg_index index;
4592         bool startsRepeatGroup;
4593     };
4594     using miss_candidates = std::vector<miss_candidate>;
4595
4596
4597 public:
4598     //---------------------------------------------------------------
4599     /** @brief initializes parser with a command line interface
4600      *  @param offset = argument index offset used for reports
4601      * */
4602     explicit
4603     parser(const group& root, arg_index offset = 0):
4604         root_{&root}, pos_{root},
4605         index_{offset-1}, eaten_{0},
4606         args_{}, missCand_{}, blocked_{false}
4607     {
4608         for_each_potential_miss(dfs_traverser{root},
4609             [this](const dfs_traverser& p){
4610                 missCand_.emplace_back(p, index_);
4611             });
4612     }
4613
4614
4615     //---------------------------------------------------------------
4616     /** @brief processes one command line argument */
4617     bool operator() (const arg_string& arg)
4618     {
4619         ++eaten_;
4620         ++index_;
4621
4622         if(!valid()) return false;
4623
4624         if(!blocked_ && try_match(arg)) return true;
4625
4626         if(try_match_blocked(arg)) return false;
4627
4628         //skipping of blocking & required patterns is not allowed
4629         if(!blocked_ && !pos_.matched() && pos_->required() && pos_->blocking()) {
4630             blocked_ = true;
4631         }
4632
4633         add_nomatch(arg);
4634         return false;
4635     }
4636
4637
4638     //---------------------------------------------------------------
4639     /** @brief returns range of argument -> parameter mappings */
4640     const arg_mappings& args() const {
4641         return args_;
4642     }
4643
4644     /** @brief returns list of missing events */
4645     missing_events missed() const {
4646         missing_events misses;
4647         misses.reserve(missCand_.size());
4648         for(auto i = missCand_.begin(); i != missCand_.end(); ++i) {
4649             misses.emplace_back(&(i->pos->as_param()), i->index);
4650         }
4651         return misses;
4652     }
4653
4654     /** @brief returns number of processed command line arguments */
4655     arg_index parse_count() const noexcept { return eaten_; }
4656
4657     /** @brief returns false if previously processed command line arguments
4658      *         lead to an invalid / inconsistent parsing result
4659      */
4660     bool valid() const noexcept { return bool(pos_); }
4661
4662     /** @brief returns false if previously processed command line arguments
4663      *         lead to an invalid / inconsistent parsing result
4664      */
4665     explicit operator bool() const noexcept { return valid(); }
4666
4667
4668 private:
4669     //---------------------------------------------------------------
4670     using match_t = detail::match_t;
4671
4672
4673     //---------------------------------------------------------------
4674     /** @brief try to match argument with unreachable parameter */
4675     bool try_match_blocked(const arg_string& arg)
4676     {
4677         //try to match ahead (using temporary parser)
4678         if(pos_) {
4679             auto ahead = *this;
4680             if(try_match_blocked(std::move(ahead), arg)) return true;
4681         }
4682
4683         //try to match from the beginning (using temporary parser)
4684         if(root_) {
4685             parser all{*root_, index_+1};
4686             if(try_match_blocked(std::move(all), arg)) return true;
4687         }
4688
4689         return false;
4690     }
4691
4692     //---------------------------------------------------------------
4693     bool try_match_blocked(parser&& parse, const arg_string& arg)
4694     {
4695         const auto nold = int(parse.args_.size());
4696
4697         parse.pos_.ignore_blocking(true);
4698
4699         if(!parse.try_match(arg)) return false;
4700
4701         for(auto i = parse.args_.begin() + nold; i != parse.args_.end(); ++i) {
4702             args_.push_back(*i);
4703             args_.back().blocked_ = true;
4704         }
4705         return true;
4706     }
4707
4708     //---------------------------------------------------------------
4709     /** @brief try to find a parameter/pattern that matches 'arg' */
4710     bool try_match(const arg_string& arg)
4711     {
4712         //match greedy parameters before everything else
4713         if(pos_->is_param() && pos_->blocking() && pos_->as_param().greedy()) {
4714             const auto match = pos_->as_param().match(arg);
4715             if(match && match.length() == arg.size()) {
4716                 add_match(detail::match_t{arg,pos_});
4717                 return true;
4718             }
4719         }
4720
4721         //try flags first (alone, joinable or strict sequence)
4722         if(try_match_full(arg, detail::select_flags{})) return true;
4723         if(try_match_joined_flags(arg)) return true;
4724         if(try_match_joined_sequence(arg, detail::select_flags{})) return true;
4725         //try value params (alone or strict sequence)
4726         if(try_match_full(arg, detail::select_values{})) return true;
4727         if(try_match_joined_sequence(arg, detail::select_all{})) return true;
4728         //try joinable params + values in any order
4729         if(try_match_joined_params(arg)) return true;
4730         return false;
4731     }
4732
4733     //---------------------------------------------------------------
4734     /**
4735      * @brief try to match full argument
4736      * @param select : predicate that candidate parameters must satisfy
4737      */
4738     template<class ParamSelector>
4739     bool try_match_full(const arg_string& arg, const ParamSelector& select)
4740     {
4741         auto match = detail::full_match(pos_, arg, select);
4742         if(!match) return false;
4743         add_match(match);
4744         return true;
4745     }
4746
4747     //---------------------------------------------------------------
4748     /**
4749      * @brief try to match argument as blocking sequence of parameters
4750      * @param select : predicate that a parameter matching the prefix of
4751      *                 'arg' must satisfy
4752      */
4753     template<class ParamSelector>
4754     bool try_match_joined_sequence(arg_string arg,
4755                                    const ParamSelector& acceptFirst)
4756     {
4757         auto fstMatch = detail::longest_prefix_match(pos_, arg, acceptFirst);
4758
4759         if(!fstMatch) return false;
4760
4761         if(fstMatch.str().size() == arg.size()) {
4762             add_match(fstMatch);
4763             return true;
4764         }
4765
4766         if(!fstMatch.pos()->blocking()) return false;
4767
4768         auto pos = fstMatch.pos();
4769         pos.ignore_blocking(true);
4770         const auto parent = &pos.parent();
4771         if(!pos->repeatable()) ++pos;
4772
4773         arg.erase(0, fstMatch.str().size());
4774         std::vector<match_t> matches { std::move(fstMatch) };
4775
4776         while(!arg.empty() && pos &&
4777               pos->blocking() && pos->is_param() &&
4778               (&pos.parent() == parent))
4779         {
4780             auto match = pos->as_param().match(arg);
4781
4782             if(match.prefix()) {
4783                 matches.emplace_back(arg.substr(0,match.length()), pos);
4784                 arg.erase(0, match.length());
4785                 if(!pos->repeatable()) ++pos;
4786             }
4787             else {
4788                 if(!pos->repeatable()) return false;
4789                 ++pos;
4790             }
4791
4792         }
4793         //if arg not fully covered => discard temporary matches
4794         if(!arg.empty() || matches.empty()) return false;
4795
4796         for(const auto& m : matches) add_match(m);
4797         return true;
4798     }
4799
4800     //-----------------------------------------------------
4801     /** @brief try to match 'arg' as a concatenation of joinable flags */
4802     bool try_match_joined_flags(const arg_string& arg)
4803     {
4804         return find_join_group(pos_, [&](const group& g) {
4805             return try_match_joined(g, arg, detail::select_flags{},
4806                                     g.common_flag_prefix());
4807         });
4808     }
4809
4810     //---------------------------------------------------------------
4811     /** @brief try to match 'arg' as a concatenation of joinable parameters */
4812     bool try_match_joined_params(const arg_string& arg)
4813     {
4814         return find_join_group(pos_, [&](const group& g) {
4815             return try_match_joined(g, arg, detail::select_all{});
4816         });
4817     }
4818
4819     //-----------------------------------------------------
4820     /** @brief try to match 'arg' as concatenation of joinable parameters
4821      *         that are all contained within one group
4822      */
4823     template<class ParamSelector>
4824     bool try_match_joined(const group& joinGroup, arg_string arg,
4825                           const ParamSelector& select,
4826                           const arg_string& prefix = "")
4827     {
4828         //temporary parser with 'joinGroup' as top-level group
4829         parser parse {joinGroup};
4830         //records temporary matches
4831         std::vector<match_t> matches;
4832
4833         while(!arg.empty()) {
4834             auto match = detail::longest_prefix_match(parse.pos_, arg, select);
4835
4836             if(!match) return false;
4837
4838             arg.erase(0, match.str().size());
4839             //make sure prefix is always present after the first match
4840             //so that, e.g., flags "-a" and "-b" will be found in "-ab"
4841             if(!arg.empty() && !prefix.empty() && arg.find(prefix) != 0 &&
4842                 prefix != match.str())
4843             {
4844                 arg.insert(0,prefix);
4845             }
4846
4847             parse.add_match(match);
4848             matches.push_back(std::move(match));
4849         }
4850
4851         if(!arg.empty() || matches.empty()) return false;
4852
4853         if(!parse.missCand_.empty()) return false;
4854         for(const auto& a : parse.args_) if(a.any_error()) return false;
4855
4856         //replay matches onto *this
4857         for(const auto& m : matches) add_match(m);
4858         return true;
4859     }
4860
4861     //-----------------------------------------------------
4862     template<class GroupSelector>
4863     bool find_join_group(const scoped_dfs_traverser& start,
4864                          const GroupSelector& accept) const
4865     {
4866         if(start && start.parent().joinable()) {
4867             const auto& g = start.parent();
4868             if(accept(g)) return true;
4869             return false;
4870         }
4871
4872         auto pos = start;
4873         while(pos) {
4874             if(pos->is_group() && pos->as_group().joinable()) {
4875                 const auto& g = pos->as_group();
4876                 if(accept(g)) return true;
4877                 pos.next_sibling();
4878             }
4879             else {
4880                 ++pos;
4881             }
4882         }
4883         return false;
4884     }
4885
4886
4887     //---------------------------------------------------------------
4888     void add_nomatch(const arg_string& arg) {
4889         args_.emplace_back(index_, arg);
4890     }
4891
4892
4893     //---------------------------------------------------------------
4894     void add_match(const match_t& match)
4895     {
4896         const auto& pos = match.pos();
4897         if(!pos || !pos->is_param()) return;
4898
4899         pos_.next_after_match(pos);
4900
4901         arg_mapping newArg{index_, match.str(), pos.base()};
4902         newArg.repeat_ = occurrences_of(&pos->as_param());
4903         newArg.conflict_ = check_conflicts(pos.base());
4904         newArg.startsRepeatGroup_ = pos_.start_of_repeat_group();
4905         args_.push_back(std::move(newArg));
4906
4907         add_miss_candidates_after(pos);
4908         clean_miss_candidates_for(pos.base());
4909         discard_alternative_miss_candidates(pos.base());
4910
4911     }
4912
4913     //-----------------------------------------------------
4914     bool check_conflicts(const dfs_traverser& match)
4915     {
4916         if(pos_.start_of_repeat_group()) return false;
4917         bool conflict = false;
4918         for(const auto& m : match.stack()) {
4919             if(m.parent->exclusive()) {
4920                 for(auto i = args_.rbegin(); i != args_.rend(); ++i) {
4921                     if(!i->blocked()) {
4922                         for(const auto& c : i->match_.stack()) {
4923                             //sibling within same exclusive group => conflict
4924                             if(c.parent == m.parent && c.cur != m.cur) {
4925                                 conflict = true;
4926                                 i->conflict_ = true;
4927                             }
4928                         }
4929                     }
4930                     //check for conflicts only within current repeat cycle
4931                     if(i->startsRepeatGroup_) break;
4932                 }
4933             }
4934         }
4935         return conflict;
4936     }
4937
4938     //-----------------------------------------------------
4939     void clean_miss_candidates_for(const dfs_traverser& match)
4940     {
4941         auto i = std::find_if(missCand_.rbegin(), missCand_.rend(),
4942             [&](const miss_candidate& m) {
4943                 return &(*m.pos) == &(*match);
4944             });
4945
4946         if(i != missCand_.rend()) {
4947             missCand_.erase(prev(i.base()));
4948         }
4949     }
4950
4951     //-----------------------------------------------------
4952     void discard_alternative_miss_candidates(const dfs_traverser& match)
4953     {
4954         if(missCand_.empty()) return;
4955         //find out, if miss candidate is sibling of one of the same
4956         //alternative groups that the current match is a member of
4957         //if so, we can discard the miss
4958
4959         //go through all exclusive groups of matching pattern
4960         for(const auto& m : match.stack()) {
4961             if(m.parent->exclusive()) {
4962                 for(auto i = int(missCand_.size())-1; i >= 0; --i) {
4963                     bool removed = false;
4964                     for(const auto& c : missCand_[i].pos.stack()) {
4965                         //sibling within same exclusive group => discard
4966                         if(c.parent == m.parent && c.cur != m.cur) {
4967                             missCand_.erase(missCand_.begin() + i);
4968                             if(missCand_.empty()) return;
4969                             removed = true;
4970                             break;
4971                         }
4972                     }
4973                     //remove miss candidates only within current repeat cycle
4974                     if(i > 0 && removed) {
4975                         if(missCand_[i-1].startsRepeatGroup) break;
4976                     } else {
4977                         if(missCand_[i].startsRepeatGroup) break;
4978                     }
4979                 }
4980             }
4981         }
4982     }
4983
4984     //-----------------------------------------------------
4985     void add_miss_candidates_after(const scoped_dfs_traverser& match)
4986     {
4987         auto npos = match.base();
4988         if(npos.is_alternative()) npos.skip_alternatives();
4989         ++npos;
4990         //need to add potential misses if:
4991         //either new repeat group was started
4992         const auto newRepGroup = match.innermost_repeat_group();
4993         if(newRepGroup) {
4994             if(pos_.start_of_repeat_group()) {
4995                 for_each_potential_miss(std::move(npos),
4996                     [&,this](const dfs_traverser& pos) {
4997                         //only add candidates within repeat group
4998                         if(newRepGroup == pos.innermost_repeat_group()) {
4999                             missCand_.emplace_back(pos, index_, true);
5000                         }
5001                     });
5002             }
5003         }
5004         //... or an optional blocking param was hit
5005         else if(match->blocking() && !match->required() &&
5006             npos.level() >= match.base().level())
5007         {
5008             for_each_potential_miss(std::move(npos),
5009                 [&,this](const dfs_traverser& pos) {
5010                     //only add new candidates
5011                     if(std::find_if(missCand_.begin(), missCand_.end(),
5012                         [&](const miss_candidate& c){
5013                             return &(*c.pos) == &(*pos);
5014                         }) == missCand_.end())
5015                     {
5016                         missCand_.emplace_back(pos, index_);
5017                     }
5018                 });
5019         }
5020
5021     }
5022
5023     //-----------------------------------------------------
5024     template<class Action>
5025     static void
5026     for_each_potential_miss(dfs_traverser pos, Action&& action)
5027     {
5028         const auto level = pos.level();
5029         while(pos && pos.level() >= level) {
5030             if(pos->is_group() ) {
5031                 const auto& g = pos->as_group();
5032                 if(g.all_optional() || (g.exclusive() && g.any_optional())) {
5033                     pos.next_sibling();
5034                 } else {
5035                     ++pos;
5036                 }
5037             } else {  //param
5038                 if(pos->required()) {
5039                     action(pos);
5040                     ++pos;
5041                 } else if(pos->blocking()) { //optional + blocking
5042                     pos.next_after_siblings();
5043                 } else {
5044                     ++pos;
5045                 }
5046             }
5047         }
5048     }
5049
5050
5051     //---------------------------------------------------------------
5052     std::size_t occurrences_of(const parameter* p) const
5053     {
5054         if(!p) return 0;
5055
5056         auto i = std::find_if(args_.rbegin(), args_.rend(),
5057             [p](const arg_mapping& a){ return a.param() == p; });
5058
5059         if(i != args_.rend()) return i->repeat() + 1;
5060         return 0;
5061     }
5062
5063
5064     //---------------------------------------------------------------
5065     const group* root_;
5066     scoped_dfs_traverser pos_;
5067     arg_index index_;
5068     arg_index eaten_;
5069     arg_mappings args_;
5070     miss_candidates missCand_;
5071     bool blocked_;
5072 };
5073
5074
5075
5076
5077 /*************************************************************************//**
5078  *
5079  * @brief contains argument -> parameter mappings
5080  *        and missing parameters
5081  *
5082  *****************************************************************************/
5083 class parsing_result
5084 {
5085 public:
5086     using arg_mapping    = parser::arg_mapping;
5087     using arg_mappings   = parser::arg_mappings;
5088     using missing_event  = parser::missing_event;
5089     using missing_events = parser::missing_events;
5090     using iterator       = arg_mappings::const_iterator;
5091
5092     //-----------------------------------------------------
5093     /** @brief default: empty result */
5094     parsing_result() = default;
5095
5096     parsing_result(arg_mappings arg2param, missing_events misses):
5097         arg2param_{std::move(arg2param)}, missing_{std::move(misses)}
5098     {}
5099
5100     //-----------------------------------------------------
5101     /** @brief returns number of arguments that could not be mapped to
5102      *         a parameter
5103      */
5104     arg_mappings::size_type
5105     unmapped_args_count() const noexcept {
5106         return std::count_if(arg2param_.begin(), arg2param_.end(),
5107             [](const arg_mapping& a){ return !a.param(); });
5108     }
5109
5110     /** @brief returns if any argument could only be matched by an
5111      *         unreachable parameter
5112      */
5113     bool any_blocked() const noexcept {
5114         return std::any_of(arg2param_.begin(), arg2param_.end(),
5115             [](const arg_mapping& a){ return a.blocked(); });
5116     }
5117
5118     /** @brief returns if any argument matched more than one parameter
5119      *         that were mutually exclusive */
5120     bool any_conflict() const noexcept {
5121         return std::any_of(arg2param_.begin(), arg2param_.end(),
5122             [](const arg_mapping& a){ return a.conflict(); });
5123     }
5124
5125     /** @brief returns if any parameter matched repeatedly although
5126      *         it was not allowed to */
5127     bool any_bad_repeat() const noexcept {
5128         return std::any_of(arg2param_.begin(), arg2param_.end(),
5129             [](const arg_mapping& a){ return a.bad_repeat(); });
5130     }
5131
5132     /** @brief returns true if any parsing error / violation of the
5133      *         command line interface definition occurred */
5134     bool any_error() const noexcept {
5135         return unmapped_args_count() > 0 || !missing().empty() ||
5136                any_blocked() || any_conflict() || any_bad_repeat();
5137     }
5138
5139     /** @brief returns true if no parsing error / violation of the
5140      *         command line interface definition occurred */
5141     explicit operator bool() const noexcept { return !any_error(); }
5142
5143     /** @brief access to range of missing parameter match events */
5144     const missing_events& missing() const noexcept { return missing_; }
5145
5146     /** @brief returns non-mutating iterator to position of
5147      *         first argument -> parameter mapping  */
5148     iterator begin() const noexcept { return arg2param_.begin(); }
5149     /** @brief returns non-mutating iterator to position one past the
5150      *         last argument -> parameter mapping  */
5151     iterator end()   const noexcept { return arg2param_.end(); }
5152
5153 private:
5154     //-----------------------------------------------------
5155     arg_mappings arg2param_;
5156     missing_events missing_;
5157 };
5158
5159
5160
5161
5162 namespace detail {
5163 namespace {
5164
5165 /*************************************************************************//**
5166  *
5167  * @brief correct some common problems
5168  *        does not - and MUST NOT - change the number of arguments
5169  *        (no insertions or deletions allowed)
5170  *
5171  *****************************************************************************/
5172 void sanitize_args(arg_list& args)
5173 {
5174     //e.g. {"-o12", ".34"} -> {"-o", "12.34"}
5175
5176     if(args.empty()) return;
5177
5178     for(auto i = begin(args)+1; i != end(args); ++i) {
5179         if(i != begin(args) && i->size() > 1 &&
5180             i->find('.') == 0 && std::isdigit((*i)[1]) )
5181         {
5182             //find trailing digits in previous arg
5183             using std::prev;
5184             auto& prv = *prev(i);
5185             auto fstDigit = std::find_if_not(prv.rbegin(), prv.rend(),
5186                 [](arg_string::value_type c){
5187                     return std::isdigit(c);
5188                 }).base();
5189
5190             //handle leading sign
5191             if(fstDigit > prv.begin() &&
5192                 (*prev(fstDigit) == '+' || *prev(fstDigit) == '-'))
5193             {
5194                 --fstDigit;
5195             }
5196
5197             //prepend digits from previous arg
5198             i->insert(begin(*i), fstDigit, end(prv));
5199
5200             //erase digits in previous arg
5201             prv.erase(fstDigit, end(prv));
5202         }
5203     }
5204 }
5205
5206
5207
5208 /*************************************************************************//**
5209  *
5210  * @brief executes actions based on a parsing result
5211  *
5212  *****************************************************************************/
5213 void execute_actions(const parsing_result& res)
5214 {
5215     for(const auto& m : res) {
5216         if(m.param()) {
5217             const auto& param = *(m.param());
5218
5219             if(m.repeat() > 0) param.notify_repeated(m.index());
5220             if(m.blocked())    param.notify_blocked(m.index());
5221             if(m.conflict())   param.notify_conflict(m.index());
5222             //main action
5223             if(!m.any_error()) param.execute_actions(m.arg());
5224         }
5225     }
5226
5227     for(auto m : res.missing()) {
5228         if(m.param()) m.param()->notify_missing(m.after_index());
5229     }
5230 }
5231
5232
5233
5234 /*************************************************************************//**
5235  *
5236  * @brief parses input args
5237  *
5238  *****************************************************************************/
5239 static parsing_result
5240 parse_args(const arg_list& args, const group& cli,
5241            arg_index offset = 0)
5242 {
5243     //parse args and store unrecognized arg indices
5244     parser parse{cli, offset};
5245     for(const auto& arg : args) {
5246         parse(arg);
5247         if(!parse.valid()) break;
5248     }
5249
5250     return parsing_result{parse.args(), parse.missed()};
5251 }
5252
5253 /*************************************************************************//**
5254  *
5255  * @brief parses input args & executes actions
5256  *
5257  *****************************************************************************/
5258 static parsing_result
5259 parse_and_execute(const arg_list& args, const group& cli,
5260                   arg_index offset = 0)
5261 {
5262     auto result = parse_args(args, cli, offset);
5263
5264     execute_actions(result);
5265
5266     return result;
5267 }
5268
5269 } //anonymous namespace
5270 } // namespace detail
5271
5272
5273
5274
5275 /*************************************************************************//**
5276  *
5277  * @brief parses vector of arg strings and executes actions
5278  *
5279  *****************************************************************************/
5280 inline parsing_result
5281 parse(arg_list args, const group& cli)
5282 {
5283     detail::sanitize_args(args);
5284     return detail::parse_and_execute(args, cli);
5285 }
5286
5287
5288 /*************************************************************************//**
5289  *
5290  * @brief parses initializer_list of C-style arg strings and executes actions
5291  *
5292  *****************************************************************************/
5293 inline parsing_result
5294 parse(std::initializer_list<const char*> arglist, const group& cli)
5295 {
5296     arg_list args;
5297     args.reserve(arglist.size());
5298     for(auto a : arglist) {
5299         args.push_back(a);
5300     }
5301
5302     return parse(std::move(args), cli);
5303 }
5304
5305
5306 /*************************************************************************//**
5307  *
5308  * @brief parses range of arg strings and executes actions
5309  *
5310  *****************************************************************************/
5311 template<class InputIterator>
5312 inline parsing_result
5313 parse(InputIterator first, InputIterator last, const group& cli)
5314 {
5315     return parse(arg_list(first,last), cli);
5316 }
5317
5318
5319 /*************************************************************************//**
5320  *
5321  * @brief parses the standard array of command line arguments; omits argv[0]
5322  *
5323  *****************************************************************************/
5324 inline parsing_result
5325 parse(const int argc, char* argv[], const group& cli, arg_index offset = 1)
5326 {
5327     arg_list args;
5328     if(offset < argc) args.assign(argv+offset, argv+argc);
5329     detail::sanitize_args(args);
5330     return detail::parse_and_execute(args, cli, offset);
5331 }
5332
5333
5334
5335
5336
5337
5338 /*************************************************************************//**
5339  *
5340  * @brief filter predicate for parameters and groups;
5341  *        Can be used to limit documentation generation to parameter subsets.
5342  *
5343  *****************************************************************************/
5344 class param_filter
5345 {
5346 public:
5347     /** @brief only allow parameters with given prefix */
5348     param_filter& prefix(const arg_string& p) noexcept {
5349         prefix_ = p; return *this;
5350     }
5351     /** @brief only allow parameters with given prefix */
5352     param_filter& prefix(arg_string&& p) noexcept {
5353         prefix_ = std::move(p); return *this;
5354     }
5355     const arg_string& prefix()  const noexcept { return prefix_; }
5356
5357     /** @brief only allow parameters with given requirement status */
5358     param_filter& required(tri t)  noexcept { required_ = t; return *this; }
5359     tri           required() const noexcept { return required_; }
5360
5361     /** @brief only allow parameters with given blocking status */
5362     param_filter& blocking(tri t)  noexcept { blocking_ = t; return *this; }
5363     tri           blocking() const noexcept { return blocking_; }
5364
5365     /** @brief only allow parameters with given repeatable status */
5366     param_filter& repeatable(tri t)  noexcept { repeatable_ = t; return *this; }
5367     tri           repeatable() const noexcept { return repeatable_; }
5368
5369     /** @brief only allow parameters with given docstring status */
5370     param_filter& has_doc(tri t)  noexcept { hasDoc_ = t; return *this; }
5371     tri           has_doc() const noexcept { return hasDoc_; }
5372
5373
5374     /** @brief returns true, if parameter satisfies all filters */
5375     bool operator() (const parameter& p) const noexcept {
5376         if(!prefix_.empty()) {
5377             if(!std::any_of(p.flags().begin(), p.flags().end(),
5378                 [&](const arg_string& flag){
5379                     return str::has_prefix(flag, prefix_);
5380                 })) return false;
5381         }
5382         if(required()   != p.required())     return false;
5383         if(blocking()   != p.blocking())     return false;
5384         if(repeatable() != p.repeatable())   return false;
5385         if(has_doc()    != !p.doc().empty()) return false;
5386         return true;
5387     }
5388
5389 private:
5390     arg_string prefix_;
5391     tri required_   = tri::either;
5392     tri blocking_   = tri::either;
5393     tri repeatable_ = tri::either;
5394     tri hasDoc_     = tri::yes;
5395 };
5396
5397
5398
5399
5400
5401
5402 /*************************************************************************//**
5403  *
5404  * @brief documentation formatting options
5405  *
5406  *****************************************************************************/
5407 class doc_formatting
5408 {
5409 public:
5410     using string = doc_string;
5411
5412     /** @brief same as 'first_column' */
5413 #if __cplusplus >= 201402L
5414     [[deprecated]]
5415 #endif
5416     doc_formatting& start_column(int col) { return first_column(col); }
5417 #if __cplusplus >= 201402L
5418     [[deprecated]]
5419 #endif
5420     int start_column() const noexcept { return first_column(); }
5421
5422     /** @brief determines column where documentation printing starts */
5423     doc_formatting&
5424     first_column(int col) {
5425         //limit to [0,last_column] but push doc_column to the right if necessary
5426         if(col < 0) col = 0;
5427         else if(col > last_column()) col = last_column();
5428         if(col > doc_column()) doc_column(first_column());
5429         firstCol_ = col;
5430         return *this;
5431     }
5432     int first_column() const noexcept {
5433         return firstCol_;
5434     }
5435
5436     /** @brief determines column where docstrings start */
5437     doc_formatting&
5438     doc_column(int col) {
5439         //limit to [first_column,last_column]
5440         if(col < 0) col = 0;
5441         else if(col < first_column()) col = first_column();
5442         else if(col > last_column()) col = last_column();
5443         docCol_ = col;
5444         return *this;
5445     }
5446     int doc_column() const noexcept {
5447         return docCol_;
5448     }
5449
5450     /** @brief determines column that no documentation text must exceed;
5451      *         (text should be wrapped appropriately after this column)
5452      */
5453     doc_formatting&
5454     last_column(int col) {
5455         //limit to [first_column,oo] but push doc_column to the left if necessary
5456         if(col < first_column()) col = first_column();
5457         if(col < doc_column()) doc_column(col);
5458         lastCol_ = col;
5459         return *this;
5460     }
5461
5462     int last_column() const noexcept {
5463         return lastCol_;
5464     }
5465
5466     /** @brief determines indent of documentation lines
5467      *         for children of a documented group */
5468     doc_formatting& indent_size(int indent) { indentSize_ = indent; return *this; }
5469     int             indent_size() const noexcept  { return indentSize_; }
5470
5471     /** @brief determines string to be used
5472      *         if a parameter has no flags and no label  */
5473     doc_formatting& empty_label(const string& label) {
5474         emptyLabel_ = label;
5475         return *this;
5476     }
5477     const string& empty_label() const noexcept { return emptyLabel_; }
5478
5479     /** @brief determines string for separating parameters */
5480     doc_formatting& param_separator(const string& sep) {
5481         paramSep_ = sep;
5482         return *this;
5483     }
5484     const string& param_separator() const noexcept { return paramSep_; }
5485
5486     /** @brief determines string for separating groups (in usage lines) */
5487     doc_formatting& group_separator(const string& sep) {
5488         groupSep_ = sep;
5489         return *this;
5490     }
5491     const string& group_separator() const noexcept { return groupSep_; }
5492
5493     /** @brief determines string for separating alternative parameters */
5494     doc_formatting& alternative_param_separator(const string& sep) {
5495         altParamSep_ = sep;
5496         return *this;
5497     }
5498     const string& alternative_param_separator() const noexcept { return altParamSep_; }
5499
5500     /** @brief determines string for separating alternative groups */
5501     doc_formatting& alternative_group_separator(const string& sep) {
5502         altGroupSep_ = sep;
5503         return *this;
5504     }
5505     const string& alternative_group_separator() const noexcept { return altGroupSep_; }
5506
5507     /** @brief determines string for separating flags of the same parameter */
5508     doc_formatting& flag_separator(const string& sep) {
5509         flagSep_ = sep;
5510         return *this;
5511     }
5512     const string& flag_separator() const noexcept { return flagSep_; }
5513
5514     /** @brief determines strings surrounding parameter labels */
5515     doc_formatting&
5516     surround_labels(const string& prefix, const string& postfix) {
5517         labelPre_ = prefix;
5518         labelPst_ = postfix;
5519         return *this;
5520     }
5521     const string& label_prefix()  const noexcept { return labelPre_; }
5522     const string& label_postfix() const noexcept { return labelPst_; }
5523
5524     /** @brief determines strings surrounding optional parameters/groups */
5525     doc_formatting&
5526     surround_optional(const string& prefix, const string& postfix) {
5527         optionPre_ = prefix;
5528         optionPst_ = postfix;
5529         return *this;
5530     }
5531     const string& optional_prefix()  const noexcept { return optionPre_; }
5532     const string& optional_postfix() const noexcept { return optionPst_; }
5533
5534     /** @brief determines strings surrounding repeatable parameters/groups */
5535     doc_formatting&
5536     surround_repeat(const string& prefix, const string& postfix) {
5537         repeatPre_ = prefix;
5538         repeatPst_ = postfix;
5539         return *this;
5540     }
5541     const string& repeat_prefix()  const noexcept { return repeatPre_; }
5542     const string& repeat_postfix() const noexcept { return repeatPst_; }
5543
5544     /** @brief determines strings surrounding exclusive groups */
5545     doc_formatting&
5546     surround_alternatives(const string& prefix, const string& postfix) {
5547         alternPre_ = prefix;
5548         alternPst_ = postfix;
5549         return *this;
5550     }
5551     const string& alternatives_prefix()  const noexcept { return alternPre_; }
5552     const string& alternatives_postfix() const noexcept { return alternPst_; }
5553
5554     /** @brief determines strings surrounding alternative flags */
5555     doc_formatting&
5556     surround_alternative_flags(const string& prefix, const string& postfix) {
5557         alternFlagPre_ = prefix;
5558         alternFlagPst_ = postfix;
5559         return *this;
5560     }
5561     const string& alternative_flags_prefix()  const noexcept { return alternFlagPre_; }
5562     const string& alternative_flags_postfix() const noexcept { return alternFlagPst_; }
5563
5564     /** @brief determines strings surrounding non-exclusive groups */
5565     doc_formatting&
5566     surround_group(const string& prefix, const string& postfix) {
5567         groupPre_ = prefix;
5568         groupPst_ = postfix;
5569         return *this;
5570     }
5571     const string& group_prefix()  const noexcept { return groupPre_; }
5572     const string& group_postfix() const noexcept { return groupPst_; }
5573
5574     /** @brief determines strings surrounding joinable groups */
5575     doc_formatting&
5576     surround_joinable(const string& prefix, const string& postfix) {
5577         joinablePre_ = prefix;
5578         joinablePst_ = postfix;
5579         return *this;
5580     }
5581     const string& joinable_prefix()  const noexcept { return joinablePre_; }
5582     const string& joinable_postfix() const noexcept { return joinablePst_; }
5583
5584     /** @brief determines maximum number of flags per parameter to be printed
5585      *         in detailed parameter documentation lines */
5586     doc_formatting& max_flags_per_param_in_doc(int max) {
5587         maxAltInDocs_ = max > 0 ? max : 0;
5588         return *this;
5589     }
5590     int max_flags_per_param_in_doc() const noexcept { return maxAltInDocs_; }
5591
5592     /** @brief determines maximum number of flags per parameter to be printed
5593      *         in usage lines */
5594     doc_formatting& max_flags_per_param_in_usage(int max) {
5595         maxAltInUsage_ = max > 0 ? max : 0;
5596         return *this;
5597     }
5598     int max_flags_per_param_in_usage() const noexcept { return maxAltInUsage_; }
5599
5600     /** @brief determines number of empty rows after one single-line
5601      *         documentation entry */
5602     doc_formatting& line_spacing(int lines) {
5603         lineSpc_ = lines > 0 ? lines : 0;
5604         return *this;
5605     }
5606     int line_spacing() const noexcept { return lineSpc_; }
5607
5608     /** @brief determines number of empty rows before and after a paragraph;
5609      *         a paragraph is defined by a documented group or if
5610      *         a parameter documentation entry used more than one line */
5611     doc_formatting& paragraph_spacing(int lines) {
5612         paragraphSpc_ = lines > 0 ? lines : 0;
5613         return *this;
5614     }
5615     int paragraph_spacing() const noexcept { return paragraphSpc_; }
5616
5617     /** @brief determines if alternative flags with a common prefix should
5618      *         be printed in a merged fashion */
5619     doc_formatting& merge_alternative_flags_with_common_prefix(bool yes = true) {
5620         mergeAltCommonPfx_ = yes;
5621         return *this;
5622     }
5623     bool merge_alternative_flags_with_common_prefix() const noexcept {
5624         return mergeAltCommonPfx_;
5625     }
5626
5627     /** @brief determines if joinable flags with a common prefix should
5628      *         be printed in a merged fashion */
5629     doc_formatting& merge_joinable_with_common_prefix(bool yes = true) {
5630         mergeJoinableCommonPfx_ = yes;
5631         return *this;
5632     }
5633     bool merge_joinable_with_common_prefix() const noexcept {
5634         return mergeJoinableCommonPfx_;
5635     }
5636
5637     /** @brief determines if children of exclusive groups should be printed
5638      *         on individual lines if the exceed 'alternatives_min_split_size'
5639      */
5640     doc_formatting& split_alternatives(bool yes = true) {
5641         splitTopAlt_ = yes;
5642         return *this;
5643     }
5644     bool split_alternatives() const noexcept {
5645         return splitTopAlt_;
5646     }
5647
5648     /** @brief determines how many children exclusive groups can have before
5649      *         their children are printed on individual usage lines */
5650     doc_formatting& alternatives_min_split_size(int size) {
5651         groupSplitSize_ = size > 0 ? size : 0;
5652         return *this;
5653     }
5654     int alternatives_min_split_size() const noexcept { return groupSplitSize_; }
5655
5656     /** @brief determines whether to ignore new line characters in docstrings
5657      */
5658     doc_formatting& ignore_newline_chars(bool yes = true) {
5659         ignoreNewlines_ = yes;
5660         return *this;
5661     }
5662     bool ignore_newline_chars() const noexcept {
5663         return ignoreNewlines_;
5664     }
5665
5666 private:
5667     string paramSep_      = string(" ");
5668     string groupSep_      = string(" ");
5669     string altParamSep_   = string("|");
5670     string altGroupSep_   = string(" | ");
5671     string flagSep_       = string(", ");
5672     string labelPre_      = string("<");
5673     string labelPst_      = string(">");
5674     string optionPre_     = string("[");
5675     string optionPst_     = string("]");
5676     string repeatPre_     = string("");
5677     string repeatPst_     = string("...");
5678     string groupPre_      = string("(");
5679     string groupPst_      = string(")");
5680     string alternPre_     = string("(");
5681     string alternPst_     = string(")");
5682     string alternFlagPre_ = string("");
5683     string alternFlagPst_ = string("");
5684     string joinablePre_   = string("(");
5685     string joinablePst_   = string(")");
5686     string emptyLabel_    = string("");
5687     int firstCol_ = 8;
5688     int docCol_ = 20;
5689     int lastCol_ = 100;
5690     int indentSize_ = 4;
5691     int maxAltInUsage_ = 1;
5692     int maxAltInDocs_ = 32;
5693     int lineSpc_ = 0;
5694     int paragraphSpc_ = 1;
5695     int groupSplitSize_ = 3;
5696     bool splitTopAlt_ = true;
5697     bool mergeAltCommonPfx_ = false;
5698     bool mergeJoinableCommonPfx_ = true;
5699     bool ignoreNewlines_ = false;
5700 };
5701
5702
5703
5704 namespace detail {
5705
5706 /*************************************************************************//**
5707  *
5708  * @brief stream decorator
5709  *        that applies formatting like line wrapping
5710  *
5711  *****************************************************************************/
5712 template<class OStream = std::ostream, class StringT = doc_string>
5713 class formatting_ostream
5714 {
5715 public:
5716     using string_type = StringT;
5717     using size_type   = typename string_type::size_type;
5718     using char_type   = typename string_type::value_type;
5719
5720     formatting_ostream(OStream& os):
5721         os_(os),
5722         curCol_{0}, firstCol_{0}, lastCol_{100},
5723         hangingIndent_{0}, paragraphSpacing_{0}, paragraphSpacingThreshold_{2},
5724         curBlankLines_{0}, curParagraphLines_{1},
5725         totalNonBlankLines_{0},
5726         ignoreInputNls_{false}
5727     {}
5728
5729
5730     //---------------------------------------------------------------
5731     const OStream& base() const noexcept { return os_; }
5732           OStream& base()       noexcept { return os_; }
5733
5734     bool good() const { return os_.good(); }
5735
5736
5737     //---------------------------------------------------------------
5738     /** @brief determines the leftmost border of the text body */
5739     formatting_ostream& first_column(int c) {
5740         firstCol_ = c < 0 ? 0 : c;
5741         return *this;
5742     }
5743     int first_column() const noexcept { return firstCol_; }
5744
5745     /** @brief determines the rightmost border of the text body */
5746     formatting_ostream& last_column(int c) {
5747         lastCol_ = c < 0 ? 0 : c;
5748         return *this;
5749     }
5750
5751     int last_column() const noexcept { return lastCol_; }
5752
5753     int text_width() const noexcept {
5754         return lastCol_ - firstCol_;
5755     }
5756
5757     /** @brief additional indentation for the 2nd, 3rd, ... line of
5758                a paragraph (sequence of soft-wrapped lines) */
5759     formatting_ostream& hanging_indent(int amount) {
5760         hangingIndent_ = amount;
5761         return *this;
5762     }
5763     int hanging_indent() const noexcept {
5764         return hangingIndent_;
5765     }
5766
5767     /** @brief amount of blank lines between paragraphs */
5768     formatting_ostream& paragraph_spacing(int lines) {
5769         paragraphSpacing_ = lines;
5770         return *this;
5771     }
5772     int paragraph_spacing() const noexcept {
5773         return paragraphSpacing_;
5774     }
5775
5776     /** @brief insert paragraph spacing
5777                if paragraph is at least 'lines' lines long */
5778     formatting_ostream& min_paragraph_lines_for_spacing(int lines) {
5779         paragraphSpacingThreshold_ = lines;
5780         return *this;
5781     }
5782     int min_paragraph_lines_for_spacing() const noexcept {
5783         return paragraphSpacingThreshold_;
5784     }
5785
5786     /** @brief if set to true, newline characters will be ignored */
5787     formatting_ostream& ignore_newline_chars(bool yes) {
5788         ignoreInputNls_ = yes;
5789         return *this;
5790     }
5791
5792     bool ignore_newline_chars() const noexcept {
5793         return ignoreInputNls_;
5794     }
5795
5796
5797     //---------------------------------------------------------------
5798     /* @brief insert 'n' spaces */
5799     void write_spaces(int n) {
5800         if(n < 1) return;
5801         os_ << string_type(size_type(n), ' ');
5802         curCol_ += n;
5803     }
5804
5805     /* @brief go to new line, but continue current paragraph */
5806     void wrap_soft(int times = 1) {
5807         if(times < 1) return;
5808         if(times > 1) {
5809             os_ << string_type(size_type(times), '\n');
5810         } else {
5811             os_ << '\n';
5812         }
5813         curCol_ = 0;
5814         ++curParagraphLines_;
5815     }
5816
5817     /* @brief go to new line, and start a new paragraph */
5818     void wrap_hard(int times = 1) {
5819         if(times < 1) return;
5820
5821         if(paragraph_spacing() > 0 &&
5822            paragraph_lines() >= min_paragraph_lines_for_spacing())
5823         {
5824             times = paragraph_spacing() + 1;
5825         }
5826
5827         if(times > 1) {
5828             os_ << string_type(size_type(times), '\n');
5829             curBlankLines_ += times - 1;
5830         } else {
5831             os_ << '\n';
5832         }
5833         if(at_begin_of_line()) {
5834             ++curBlankLines_;
5835         }
5836         curCol_ = 0;
5837         curParagraphLines_ = 1;
5838     }
5839
5840
5841     //---------------------------------------------------------------
5842     bool at_begin_of_line() const noexcept {
5843         return curCol_ <= current_line_begin();
5844     }
5845     int current_line_begin() const noexcept {
5846         return in_hanging_part_of_paragraph()
5847             ? firstCol_ + hangingIndent_
5848             : firstCol_;
5849     }
5850
5851     int current_column() const noexcept {
5852         return curCol_;
5853     }
5854
5855     int total_non_blank_lines() const noexcept {
5856         return totalNonBlankLines_;
5857     }
5858     int paragraph_lines() const noexcept {
5859         return curParagraphLines_;
5860     }
5861     int blank_lines_before_paragraph() const noexcept {
5862         return curBlankLines_;
5863     }
5864
5865
5866     //---------------------------------------------------------------
5867     template<class T>
5868     friend formatting_ostream&
5869     operator << (formatting_ostream& os, const T& x) {
5870         os.write(x);
5871         return os;
5872     }
5873
5874     void flush() {
5875         os_.flush();
5876     }
5877
5878
5879 private:
5880     bool in_hanging_part_of_paragraph() const noexcept {
5881         return hanging_indent() > 0 && paragraph_lines() > 1;
5882     }
5883     bool current_line_empty() const noexcept {
5884         return curCol_ < 1;
5885     }
5886     bool left_of_text_area() const noexcept {
5887         return curCol_ < current_line_begin();
5888     }
5889     bool right_of_text_area() const noexcept {
5890         return curCol_ > lastCol_;
5891     }
5892     int columns_left_in_line() const noexcept {
5893         return lastCol_ - std::max(current_line_begin(), curCol_);
5894     }
5895
5896     void fix_indent() {
5897         if(left_of_text_area()) {
5898             const auto fst = current_line_begin();
5899             write_spaces(fst - curCol_);
5900             curCol_ = fst;
5901         }
5902     }
5903
5904     template<class Iter>
5905     bool only_whitespace(Iter first, Iter last) const {
5906         return last == std::find_if_not(first, last,
5907                 [](char_type c) { return std::isspace(c); });
5908     }
5909
5910     /** @brief write any object */
5911     template<class T>
5912     void write(const T& x) {
5913         std::ostringstream ss;
5914         ss << x;
5915         write(std::move(ss).str());
5916     }
5917
5918     /** @brief write a stringstream */
5919     void write(const std::ostringstream& s) {
5920         write(s.str());
5921     }
5922
5923     /** @brief write a string */
5924     void write(const string_type& s) {
5925         write(s.begin(), s.end());
5926     }
5927
5928     /** @brief partition output into lines */
5929     template<class Iter>
5930     void write(Iter first, Iter last)
5931     {
5932         if(first == last) return;
5933         if(*first == '\n') {
5934             if(!ignore_newline_chars()) wrap_hard();
5935             ++first;
5936             if(first == last) return;
5937         }
5938         auto i = std::find(first, last, '\n');
5939         if(i != last) {
5940             if(ignore_newline_chars()) ++i;
5941             if(i != last) {
5942                 write_line(first, i);
5943                 write(i, last);
5944             }
5945         }
5946         else {
5947             write_line(first, last);
5948         }
5949     }
5950
5951     /** @brief handle line wrapping due to column constraints */
5952     template<class Iter>
5953     void write_line(Iter first, Iter last)
5954     {
5955         if(first == last) return;
5956         if(only_whitespace(first, last)) return;
5957
5958         if(right_of_text_area()) wrap_soft();
5959
5960         if(at_begin_of_line()) {
5961             //discard whitespace, it we start a new line
5962             first = std::find_if(first, last,
5963                         [](char_type c) { return !std::isspace(c); });
5964             if(first == last) return;
5965         }
5966
5967         const auto n = int(std::distance(first,last));
5968         const auto m = columns_left_in_line();
5969         //if text to be printed is too long for one line -> wrap
5970         if(n > m) {
5971             //break before word, if break is mid-word
5972             auto breakat = first + m;
5973             while(breakat > first && !std::isspace(*breakat)) --breakat;
5974             //could not find whitespace before word -> try after the word
5975             if(!std::isspace(*breakat) && breakat == first) {
5976                 breakat = std::find_if(first+m, last,
5977                           [](char_type c) { return std::isspace(c); });
5978             }
5979             if(breakat > first) {
5980                 if(curCol_ < 1) ++totalNonBlankLines_;
5981                 fix_indent();
5982                 std::copy(first, breakat, std::ostream_iterator<char_type>(os_));
5983                 curBlankLines_ = 0;
5984             }
5985             if(breakat < last) {
5986                 wrap_soft();
5987                 write_line(breakat, last);
5988             }
5989         }
5990         else {
5991             if(curCol_ < 1) ++totalNonBlankLines_;
5992             fix_indent();
5993             std::copy(first, last, std::ostream_iterator<char_type>(os_));
5994             curCol_ += n;
5995             curBlankLines_ = 0;
5996         }
5997     }
5998
5999     /** @brief write a single character */
6000     void write(char_type c)
6001     {
6002         if(c == '\n') {
6003             if(!ignore_newline_chars()) wrap_hard();
6004         }
6005         else {
6006             if(at_begin_of_line()) ++totalNonBlankLines_;
6007             fix_indent();
6008             os_ << c;
6009             ++curCol_;
6010         }
6011     }
6012
6013     OStream& os_;
6014     int curCol_;
6015     int firstCol_;
6016     int lastCol_;
6017     int hangingIndent_;
6018     int paragraphSpacing_;
6019     int paragraphSpacingThreshold_;
6020     int curBlankLines_;
6021     int curParagraphLines_;
6022     int totalNonBlankLines_;
6023     bool ignoreInputNls_;
6024 };
6025
6026
6027 }
6028
6029
6030
6031
6032 /*************************************************************************//**
6033  *
6034  * @brief   generates usage lines
6035  *
6036  * @details lazily evaluated
6037  *
6038  *****************************************************************************/
6039 class usage_lines
6040 {
6041 public:
6042     using string = doc_string;
6043
6044     usage_lines(const group& cli, string prefix = "",
6045                 const doc_formatting& fmt = doc_formatting{})
6046     :
6047         cli_(cli), fmt_(fmt), prefix_(std::move(prefix))
6048     {
6049         if(!prefix_.empty()) prefix_ += ' ';
6050     }
6051
6052     usage_lines(const group& cli, const doc_formatting& fmt):
6053         usage_lines(cli, "", fmt)
6054     {}
6055
6056     usage_lines& ommit_outermost_group_surrounders(bool yes) {
6057         ommitOutermostSurrounders_ = yes;
6058         return *this;
6059     }
6060     bool ommit_outermost_group_surrounders() const {
6061         return ommitOutermostSurrounders_;
6062     }
6063
6064     template<class OStream>
6065     inline friend OStream& operator << (OStream& os, const usage_lines& p) {
6066         p.write(os);
6067         return os;
6068     }
6069
6070     string str() const {
6071         std::ostringstream os; os << *this; return os.str();
6072     }
6073
6074
6075 private:
6076     using stream_t = detail::formatting_ostream<>;
6077     const group& cli_;
6078     doc_formatting fmt_;
6079     string prefix_;
6080     bool ommitOutermostSurrounders_ = false;
6081
6082
6083     //-----------------------------------------------------
6084     struct context {
6085         group::depth_first_traverser pos;
6086         std::stack<string> separators;
6087         std::stack<string> postfixes;
6088         int level = 0;
6089         const group* outermost = nullptr;
6090         bool linestart = false;
6091         bool useOutermost = true;
6092         int line = 0;
6093
6094         bool is_singleton() const noexcept {
6095             return linestart && pos.is_last_in_path();
6096         }
6097         bool is_alternative() const noexcept {
6098             return pos.parent().exclusive();
6099         }
6100     };
6101
6102
6103     /***************************************************************//**
6104      *
6105      * @brief writes usage text for command line parameters
6106      *
6107      *******************************************************************/
6108     template<class OStream>
6109     void write(OStream& os) const
6110     {
6111         detail::formatting_ostream<OStream> fos(os);
6112         fos.first_column(fmt_.first_column());
6113         fos.last_column(fmt_.last_column());
6114
6115         auto hindent = int(prefix_.size());
6116         if(fos.first_column() + hindent >= int(0.4 * fos.text_width())) {
6117             hindent = fmt_.indent_size();
6118         }
6119         fos.hanging_indent(hindent);
6120
6121         fos.paragraph_spacing(fmt_.paragraph_spacing());
6122         fos.min_paragraph_lines_for_spacing(2);
6123         fos.ignore_newline_chars(fmt_.ignore_newline_chars());
6124
6125         context cur;
6126         cur.pos = cli_.begin_dfs();
6127         cur.linestart = true;
6128         cur.level = cur.pos.level();
6129         cur.outermost = &cli_;
6130
6131         write(fos, cur, prefix_);
6132     }
6133
6134
6135     /***************************************************************//**
6136      *
6137      * @brief writes usage text for command line parameters
6138      *
6139      * @param prefix   all that goes in front of current things to print
6140      *
6141      *******************************************************************/
6142     template<class OStream>
6143     void write(OStream& os, context cur, string prefix) const
6144     {
6145         if(!cur.pos) return;
6146
6147         std::ostringstream buf;
6148         if(cur.linestart) buf << prefix;
6149         const auto initPos = buf.tellp();
6150
6151         cur.level = cur.pos.level();
6152
6153         if(cur.useOutermost) {
6154             //we cannot start outside of the outermost group
6155             //so we have to treat it separately
6156             start_group(buf, cur.pos.parent(), cur);
6157             if(!cur.pos) {
6158                 os << buf.str();
6159                 return;
6160             }
6161         }
6162         else {
6163             //don't visit siblings of starter node
6164             cur.pos.skip_siblings();
6165         }
6166         check_end_group(buf, cur);
6167
6168         do {
6169             if(buf.tellp() > initPos) cur.linestart = false;
6170             if(!cur.linestart && !cur.pos.is_first_in_parent()) {
6171                 buf << cur.separators.top();
6172             }
6173             if(cur.pos->is_group()) {
6174                 start_group(buf, cur.pos->as_group(), cur);
6175                 if(!cur.pos) {
6176                     os << buf.str();
6177                     return;
6178                 }
6179             }
6180             else {
6181                 buf << param_label(cur.pos->as_param(), cur);
6182                 ++cur.pos;
6183             }
6184             check_end_group(buf, cur);
6185         } while(cur.pos);
6186
6187         os << buf.str();
6188     }
6189
6190
6191     /***************************************************************//**
6192      *
6193      * @brief handles pattern group surrounders and separators
6194      *        and alternative splitting
6195      *
6196      *******************************************************************/
6197     void start_group(std::ostringstream& os,
6198                      const group& group, context& cur) const
6199     {
6200         //does cur.pos already point to a member or to group itself?
6201         //needed for special treatment of outermost group
6202         const bool alreadyInside = &(cur.pos.parent()) == &group;
6203
6204         auto lbl = joined_label(group, cur);
6205         if(!lbl.empty()) {
6206             os << lbl;
6207             cur.linestart = false;
6208             //skip over entire group as its label has already been created
6209             if(alreadyInside) {
6210                 cur.pos.next_after_siblings();
6211             } else {
6212                 cur.pos.next_sibling();
6213             }
6214         }
6215         else {
6216             const bool splitAlternatives = group.exclusive() &&
6217                 fmt_.split_alternatives() &&
6218                 std::any_of(group.begin(), group.end(),
6219                     [this](const pattern& p) {
6220                         return int(p.param_count()) >= fmt_.alternatives_min_split_size();
6221                     });
6222
6223             if(splitAlternatives) {
6224                 cur.postfixes.push("");
6225                 cur.separators.push("");
6226                 //recursively print alternative paths in decision-DAG
6227                 //enter group?
6228                 if(!alreadyInside) ++cur.pos;
6229                 cur.linestart = true;
6230                 cur.useOutermost = false;
6231                 auto pfx = os.str();
6232                 os.str("");
6233                 //print paths in DAG starting at each group member
6234                 for(std::size_t i = 0; i < group.size(); ++i) {
6235                     std::stringstream buf;
6236                     cur.outermost = cur.pos->is_group() ? &(cur.pos->as_group()) : nullptr;
6237                     write(buf, cur, pfx);
6238                     if(buf.tellp() > int(pfx.size())) {
6239                         os << buf.str();
6240                         if(i < group.size()-1) {
6241                             if(cur.line > 0) {
6242                                 os << string(fmt_.line_spacing(), '\n');
6243                             }
6244                             ++cur.line;
6245                             os << '\n';
6246                         }
6247                     }
6248                     cur.pos.next_sibling(); //do not descend into members
6249                 }
6250                 cur.pos.invalidate(); //signal end-of-path
6251                 return;
6252             }
6253             else {
6254                 //pre & postfixes, separators
6255                 auto surround = group_surrounders(group, cur);
6256                 os << surround.first;
6257                 cur.postfixes.push(std::move(surround.second));
6258                 cur.separators.push(group_separator(group, fmt_));
6259                 //descend into group?
6260                 if(!alreadyInside) ++cur.pos;
6261             }
6262         }
6263         cur.level = cur.pos.level();
6264     }
6265
6266
6267     /***************************************************************//**
6268      *
6269      *******************************************************************/
6270     void check_end_group(std::ostringstream& os, context& cur) const
6271     {
6272         for(; cur.level > cur.pos.level(); --cur.level) {
6273             os << cur.postfixes.top();
6274             cur.postfixes.pop();
6275             cur.separators.pop();
6276         }
6277         cur.level = cur.pos.level();
6278     }
6279
6280
6281     /***************************************************************//**
6282      *
6283      * @brief makes usage label for one command line parameter
6284      *
6285      *******************************************************************/
6286     string param_label(const parameter& p, const context& cur) const
6287     {
6288         const auto& parent = cur.pos.parent();
6289
6290         const bool startsOptionalSequence =
6291             parent.size() > 1 && p.blocking() && cur.pos.is_first_in_parent();
6292
6293         const bool outermost =
6294             ommitOutermostSurrounders_ && cur.outermost == &parent;
6295
6296         const bool showopt = !cur.is_alternative() && !p.required()
6297             && !startsOptionalSequence && !outermost;
6298
6299         const bool showrep = p.repeatable() && !outermost;
6300
6301         string lbl;
6302
6303         if(showrep) lbl += fmt_.repeat_prefix();
6304         if(showopt) lbl += fmt_.optional_prefix();
6305
6306         const auto& flags = p.flags();
6307         if(!flags.empty()) {
6308             const int n = std::min(fmt_.max_flags_per_param_in_usage(),
6309                                    int(flags.size()));
6310
6311             const bool surrAlt = n > 1 && !showopt && !cur.is_singleton();
6312
6313             if(surrAlt) lbl += fmt_.alternative_flags_prefix();
6314             bool sep = false;
6315             for(int i = 0; i < n; ++i) {
6316                 if(sep) {
6317                     if(cur.is_singleton())
6318                         lbl += fmt_.alternative_group_separator();
6319                     else
6320                         lbl += fmt_.flag_separator();
6321                 }
6322                 lbl += flags[i];
6323                 sep = true;
6324             }
6325             if(surrAlt) lbl += fmt_.alternative_flags_postfix();
6326         }
6327         else {
6328              if(!p.label().empty()) {
6329                  lbl += fmt_.label_prefix()
6330                      + p.label()
6331                      + fmt_.label_postfix();
6332              } else if(!fmt_.empty_label().empty()) {
6333                  lbl += fmt_.label_prefix()
6334                      + fmt_.empty_label()
6335                      + fmt_.label_postfix();
6336              } else {
6337                  return "";
6338              }
6339         }
6340
6341         if(showopt) lbl += fmt_.optional_postfix();
6342         if(showrep) lbl += fmt_.repeat_postfix();
6343
6344         return lbl;
6345     }
6346
6347
6348     /***************************************************************//**
6349      *
6350      * @brief prints flags in one group in a merged fashion
6351      *
6352      *******************************************************************/
6353     string joined_label(const group& g, const context& cur) const
6354     {
6355         if(!fmt_.merge_alternative_flags_with_common_prefix() &&
6356            !fmt_.merge_joinable_with_common_prefix()) return "";
6357
6358         const bool flagsonly = std::all_of(g.begin(), g.end(),
6359             [](const pattern& p){
6360                 return p.is_param() && !p.as_param().flags().empty();
6361             });
6362
6363         if(!flagsonly) return "";
6364
6365         const bool showOpt = g.all_optional() &&
6366             !(ommitOutermostSurrounders_ && cur.outermost == &g);
6367
6368         auto pfx = g.common_flag_prefix();
6369         if(pfx.empty()) return "";
6370
6371         const auto n = pfx.size();
6372         if(g.exclusive() &&
6373            fmt_.merge_alternative_flags_with_common_prefix())
6374         {
6375             string lbl;
6376             if(showOpt) lbl += fmt_.optional_prefix();
6377             lbl += pfx + fmt_.alternatives_prefix();
6378             bool first = true;
6379             for(const auto& p : g) {
6380                 if(p.is_param()) {
6381                     if(first)
6382                         first = false;
6383                     else
6384                         lbl += fmt_.alternative_param_separator();
6385                     lbl += p.as_param().flags().front().substr(n);
6386                 }
6387             }
6388             lbl += fmt_.alternatives_postfix();
6389             if(showOpt) lbl += fmt_.optional_postfix();
6390             return lbl;
6391         }
6392         //no alternatives, but joinable flags
6393         else if(g.joinable() &&
6394             fmt_.merge_joinable_with_common_prefix())
6395         {
6396             const bool allSingleChar = std::all_of(g.begin(), g.end(),
6397                 [&](const pattern& p){
6398                     return p.is_param() &&
6399                         p.as_param().flags().front().substr(n).size() == 1;
6400                 });
6401
6402             if(allSingleChar) {
6403                 string lbl;
6404                 if(showOpt) lbl += fmt_.optional_prefix();
6405                 lbl += pfx;
6406                 for(const auto& p : g) {
6407                     if(p.is_param())
6408                         lbl += p.as_param().flags().front().substr(n);
6409                 }
6410                 if(showOpt) lbl += fmt_.optional_postfix();
6411                 return lbl;
6412             }
6413         }
6414
6415         return "";
6416     }
6417
6418
6419     /***************************************************************//**
6420      *
6421      * @return symbols with which to surround a group
6422      *
6423      *******************************************************************/
6424     std::pair<string,string>
6425     group_surrounders(const group& group, const context& cur) const
6426     {
6427         string prefix;
6428         string postfix;
6429
6430         const bool isOutermost = &group == cur.outermost;
6431         if(isOutermost && ommitOutermostSurrounders_)
6432             return {string{}, string{}};
6433
6434         if(group.exclusive()) {
6435             if(group.all_optional()) {
6436                 prefix  = fmt_.optional_prefix();
6437                 postfix = fmt_.optional_postfix();
6438                 if(group.all_flagless()) {
6439                     prefix  += fmt_.label_prefix();
6440                     postfix = fmt_.label_prefix() + postfix;
6441                 }
6442             } else if(group.all_flagless()) {
6443                 prefix  = fmt_.label_prefix();
6444                 postfix = fmt_.label_postfix();
6445             } else if(!cur.is_singleton() || !isOutermost) {
6446                 prefix  = fmt_.alternatives_prefix();
6447                 postfix = fmt_.alternatives_postfix();
6448             }
6449         }
6450         else if(group.size() > 1 &&
6451                 group.front().blocking() && !group.front().required())
6452         {
6453             prefix  = fmt_.optional_prefix();
6454             postfix = fmt_.optional_postfix();
6455         }
6456         else if(group.size() > 1 && cur.is_alternative() &&
6457                 &group != cur.outermost)
6458         {
6459             prefix  = fmt_.group_prefix();
6460             postfix = fmt_.group_postfix();
6461         }
6462         else if(!group.exclusive() &&
6463             group.joinable() && !cur.linestart)
6464         {
6465             prefix  = fmt_.joinable_prefix();
6466             postfix = fmt_.joinable_postfix();
6467         }
6468
6469         if(group.repeatable()) {
6470             if(prefix.empty()) prefix = fmt_.group_prefix();
6471             prefix = fmt_.repeat_prefix() + prefix;
6472             if(postfix.empty()) postfix = fmt_.group_postfix();
6473             postfix += fmt_.repeat_postfix();
6474         }
6475
6476         return {std::move(prefix), std::move(postfix)};
6477     }
6478
6479
6480     /***************************************************************//**
6481      *
6482      * @return symbol that separates members of a group
6483      *
6484      *******************************************************************/
6485     static string
6486     group_separator(const group& group, const doc_formatting& fmt)
6487     {
6488         const bool only1ParamPerMember = std::all_of(group.begin(), group.end(),
6489             [](const pattern& p) { return p.param_count() < 2; });
6490
6491         if(only1ParamPerMember) {
6492             if(group.exclusive()) {
6493                 return fmt.alternative_param_separator();
6494             } else {
6495                 return fmt.param_separator();
6496             }
6497         }
6498         else { //there is at least one large group inside
6499             if(group.exclusive()) {
6500                 return fmt.alternative_group_separator();
6501             } else {
6502                 return fmt.group_separator();
6503             }
6504         }
6505     }
6506 };
6507
6508
6509
6510
6511 /*************************************************************************//**
6512  *
6513  * @brief   generates parameter and group documentation from docstrings
6514  *
6515  * @details lazily evaluated
6516  *
6517  *****************************************************************************/
6518 class documentation
6519 {
6520 public:
6521     using string          = doc_string;
6522     using filter_function = std::function<bool(const parameter&)>;
6523
6524     documentation(const group& cli,
6525                   const doc_formatting& fmt = doc_formatting{},
6526                   filter_function filter = param_filter{})
6527     :
6528         cli_(cli), fmt_{fmt}, usgFmt_{fmt}, filter_{std::move(filter)}
6529     {
6530         //necessary, because we re-use "usage_lines" to generate
6531         //labels for documented groups
6532         usgFmt_.max_flags_per_param_in_usage(
6533             usgFmt_.max_flags_per_param_in_doc());
6534     }
6535
6536     documentation(const group& cli, filter_function filter) :
6537         documentation{cli, doc_formatting{}, std::move(filter)}
6538     {}
6539
6540     documentation(const group& cli, const param_filter& filter) :
6541         documentation{cli, doc_formatting{},
6542                       [filter](const parameter& p) { return filter(p); }}
6543     {}
6544
6545     template<class OStream>
6546     inline friend OStream& operator << (OStream& os, const documentation& p) {
6547         p.write(os);
6548         return os;
6549     }
6550
6551     string str() const {
6552         std::ostringstream os;
6553         write(os);
6554         return os.str();
6555     }
6556
6557
6558 private:
6559     using dfs_traverser = group::depth_first_traverser;
6560
6561     const group& cli_;
6562     doc_formatting fmt_;
6563     doc_formatting usgFmt_;
6564     filter_function filter_;
6565     enum class paragraph { param, group };
6566
6567
6568     /***************************************************************//**
6569      *
6570      * @brief writes documentation to output stream
6571      *
6572      *******************************************************************/
6573      template<class OStream>
6574      void write(OStream& os) const {
6575         detail::formatting_ostream<OStream> fos(os);
6576         fos.first_column(fmt_.first_column());
6577         fos.last_column(fmt_.last_column());
6578         fos.hanging_indent(0);
6579         fos.paragraph_spacing(0);
6580         fos.ignore_newline_chars(fmt_.ignore_newline_chars());
6581         print_doc(fos, cli_);
6582      }
6583
6584
6585     /***************************************************************//**
6586      *
6587      * @brief writes full documentation text for command line parameters
6588      *
6589      *******************************************************************/
6590     template<class OStream>
6591     void print_doc(detail::formatting_ostream<OStream>& os,
6592                    const group& cli, int indentLvl = 0) const
6593     {
6594         if(cli.empty()) return;
6595
6596         //if group itself doesn't have docstring
6597         if(cli.doc().empty()) {
6598             for(const auto& p : cli) {
6599                 print_doc(os, p, indentLvl);
6600             }
6601         }
6602         else { //group itself does have docstring
6603             bool anyDocInside = std::any_of(cli.begin(), cli.end(),
6604                 [](const pattern& p){ return !p.doc().empty(); });
6605
6606             if(anyDocInside) { //group docstring as title, then child entries
6607                 handle_spacing(os, paragraph::group, indentLvl);
6608                 os << cli.doc();
6609                 for(const auto& p : cli) {
6610                     print_doc(os, p, indentLvl + 1);
6611                 }
6612             }
6613             else { //group label first then group docstring
6614                 auto lbl = usage_lines(cli, usgFmt_)
6615                            .ommit_outermost_group_surrounders(true).str();
6616
6617                 str::trim(lbl);
6618                 handle_spacing(os, paragraph::param, indentLvl);
6619                 print_entry(os, lbl, cli.doc());
6620             }
6621         }
6622     }
6623
6624
6625     /***************************************************************//**
6626      *
6627      * @brief writes documentation text for one group or parameter
6628      *
6629      *******************************************************************/
6630     template<class OStream>
6631     void print_doc(detail::formatting_ostream<OStream>& os,
6632                    const pattern& ptrn, int indentLvl) const
6633     {
6634         if(ptrn.is_group()) {
6635             print_doc(os, ptrn.as_group(), indentLvl);
6636         }
6637         else {
6638             const auto& p = ptrn.as_param();
6639             if(!filter_(p)) return;
6640
6641             handle_spacing(os, paragraph::param, indentLvl);
6642             print_entry(os, param_label(p, fmt_), p.doc());
6643         }
6644     }
6645
6646     /***************************************************************//**
6647      *
6648      * @brief handles line and paragraph spacings
6649      *
6650      *******************************************************************/
6651     template<class OStream>
6652     void handle_spacing(detail::formatting_ostream<OStream>& os,
6653                         paragraph p, int indentLvl) const
6654     {
6655         const auto oldIndent = os.first_column();
6656         const auto indent = fmt_.first_column() + indentLvl * fmt_.indent_size();
6657
6658         if(os.total_non_blank_lines() < 1) {
6659             os.first_column(indent);
6660             return;
6661         }
6662
6663         if(os.paragraph_lines() > 1 || indent < oldIndent) {
6664             os.wrap_hard(fmt_.paragraph_spacing() + 1);
6665         } else {
6666             os.wrap_hard();
6667         }
6668
6669         if(p == paragraph::group) {
6670             if(os.blank_lines_before_paragraph() < fmt_.paragraph_spacing()) {
6671                 os.wrap_hard(fmt_.paragraph_spacing() - os.blank_lines_before_paragraph());
6672             }
6673         }
6674         else if(os.blank_lines_before_paragraph() < fmt_.line_spacing()) {
6675             os.wrap_hard(fmt_.line_spacing() - os.blank_lines_before_paragraph());
6676         }
6677         os.first_column(indent);
6678     }
6679
6680     /*********************************************************************//**
6681      *
6682      * @brief prints one entry = label + docstring
6683      *
6684      ************************************************************************/
6685     template<class OStream>
6686     void print_entry(detail::formatting_ostream<OStream>& os,
6687                      const string& label, const string& docstr) const
6688     {
6689         if(label.empty()) return;
6690
6691         os << label;
6692
6693         if(!docstr.empty()) {
6694             if(os.current_column() >= fmt_.doc_column()) os.wrap_soft();
6695             const auto oldcol = os.first_column();
6696             os.first_column(fmt_.doc_column());
6697             os << docstr;
6698             os.first_column(oldcol);
6699         }
6700     }
6701
6702
6703     /*********************************************************************//**
6704      *
6705      * @brief makes label for one parameter
6706      *
6707      ************************************************************************/
6708     static doc_string
6709     param_label(const parameter& param, const doc_formatting& fmt)
6710     {
6711         doc_string lbl;
6712
6713         if(param.repeatable()) lbl += fmt.repeat_prefix();
6714
6715         const auto& flags = param.flags();
6716         if(!flags.empty()) {
6717             lbl += flags[0];
6718             const int n = std::min(fmt.max_flags_per_param_in_doc(),
6719                                    int(flags.size()));
6720             for(int i = 1; i < n; ++i) {
6721                 lbl += fmt.flag_separator() + flags[i];
6722             }
6723         }
6724         else if(!param.label().empty() || !fmt.empty_label().empty()) {
6725             lbl += fmt.label_prefix();
6726             if(!param.label().empty()) {
6727                 lbl += param.label();
6728             } else {
6729                 lbl += fmt.empty_label();
6730             }
6731             lbl += fmt.label_postfix();
6732         }
6733
6734         if(param.repeatable()) lbl += fmt.repeat_postfix();
6735
6736         return lbl;
6737     }
6738
6739 };
6740
6741
6742
6743
6744 /*************************************************************************//**
6745  *
6746  * @brief stores strings for man page sections
6747  *
6748  *****************************************************************************/
6749 class man_page
6750 {
6751 public:
6752     //---------------------------------------------------------------
6753     using string = doc_string;
6754
6755     //---------------------------------------------------------------
6756     /** @brief man page section */
6757     class section {
6758     public:
6759         using string = doc_string;
6760
6761         section(string stitle, string scontent):
6762             title_{std::move(stitle)}, content_{std::move(scontent)}
6763         {}
6764
6765         const string& title()   const noexcept { return title_; }
6766         const string& content() const noexcept { return content_; }
6767
6768     private:
6769         string title_;
6770         string content_;
6771     };
6772
6773 private:
6774     using section_store = std::vector<section>;
6775
6776 public:
6777     //---------------------------------------------------------------
6778     using value_type     = section;
6779     using const_iterator = section_store::const_iterator;
6780     using size_type      = section_store::size_type;
6781
6782
6783     //---------------------------------------------------------------
6784     man_page&
6785     append_section(string title, string content)
6786     {
6787         sections_.emplace_back(std::move(title), std::move(content));
6788         return *this;
6789     }
6790     //-----------------------------------------------------
6791     man_page&
6792     prepend_section(string title, string content)
6793     {
6794         sections_.emplace(sections_.begin(),
6795                           std::move(title), std::move(content));
6796         return *this;
6797     }
6798
6799
6800     //---------------------------------------------------------------
6801     const section& operator [] (size_type index) const noexcept {
6802         return sections_[index];
6803     }
6804
6805     //---------------------------------------------------------------
6806     size_type size() const noexcept { return sections_.size(); }
6807
6808     bool empty() const noexcept { return sections_.empty(); }
6809
6810
6811     //---------------------------------------------------------------
6812     const_iterator begin() const noexcept { return sections_.begin(); }
6813     const_iterator end()   const noexcept { return sections_.end(); }
6814
6815
6816     //---------------------------------------------------------------
6817     man_page& program_name(const string& n) {
6818         progName_ = n;
6819         return *this;
6820     }
6821     man_page& program_name(string&& n) {
6822         progName_ = std::move(n);
6823         return *this;
6824     }
6825     const string& program_name() const noexcept {
6826         return progName_;
6827     }
6828
6829
6830     //---------------------------------------------------------------
6831     man_page& section_row_spacing(int rows) {
6832         sectionSpc_ = rows > 0 ? rows : 0;
6833         return *this;
6834     }
6835     int section_row_spacing() const noexcept { return sectionSpc_; }
6836
6837
6838 private:
6839     int sectionSpc_ = 1;
6840     section_store sections_;
6841     string progName_;
6842 };
6843
6844
6845
6846 /*************************************************************************//**
6847  *
6848  * @brief generates man sections from command line parameters
6849  *        with sections "synopsis" and "options"
6850  *
6851  *****************************************************************************/
6852 inline man_page
6853 make_man_page(const group& cli,
6854               doc_string progname = "",
6855               const doc_formatting& fmt = doc_formatting{})
6856 {
6857     man_page man;
6858     man.append_section("SYNOPSIS", usage_lines(cli,progname,fmt).str());
6859     man.append_section("OPTIONS", documentation(cli,fmt).str());
6860     return man;
6861 }
6862
6863
6864
6865 /*************************************************************************//**
6866  *
6867  * @brief   generates man page based on command line parameters
6868  *
6869  *****************************************************************************/
6870 template<class OStream>
6871 OStream&
6872 operator << (OStream& os, const man_page& man)
6873 {
6874     bool first = true;
6875     const auto secSpc = doc_string(man.section_row_spacing() + 1, '\n');
6876     for(const auto& section : man) {
6877         if(!section.content().empty()) {
6878             if(first) first = false; else os << secSpc;
6879             if(!section.title().empty()) os << section.title() << '\n';
6880             os << section.content();
6881         }
6882     }
6883     os << '\n';
6884     return os;
6885 }
6886
6887
6888
6889
6890
6891 /*************************************************************************//**
6892  *
6893  * @brief printing methods for debugging command line interfaces
6894  *
6895  *****************************************************************************/
6896 namespace debug {
6897
6898
6899 /*************************************************************************//**
6900  *
6901  * @brief prints first flag or value label of a parameter
6902  *
6903  *****************************************************************************/
6904 inline doc_string doc_label(const parameter& p)
6905 {
6906     if(!p.flags().empty()) return p.flags().front();
6907     if(!p.label().empty()) return p.label();
6908     return doc_string{"<?>"};
6909 }
6910
6911 inline doc_string doc_label(const group&)
6912 {
6913     return "<group>";
6914 }
6915
6916 inline doc_string doc_label(const pattern& p)
6917 {
6918     return p.is_group() ? doc_label(p.as_group()) : doc_label(p.as_param());
6919 }
6920
6921
6922 /*************************************************************************//**
6923  *
6924  * @brief prints parsing result
6925  *
6926  *****************************************************************************/
6927 template<class OStream>
6928 void print(OStream& os, const parsing_result& result)
6929 {
6930     for(const auto& m : result) {
6931         os << "#" << m.index() << " " << m.arg() << " -> ";
6932         auto p = m.param();
6933         if(p) {
6934             os << doc_label(*p) << " \t";
6935             if(m.repeat() > 0) {
6936                 os << (m.bad_repeat() ? "[bad repeat " : "[repeat ")
6937                    <<  m.repeat() << "]";
6938             }
6939             if(m.blocked())  os << " [blocked]";
6940             if(m.conflict()) os << " [conflict]";
6941             os << '\n';
6942         }
6943         else {
6944             os << " [unmapped]\n";
6945         }
6946     }
6947
6948     for(const auto& m : result.missing()) {
6949         auto p = m.param();
6950         if(p) {
6951             os << doc_label(*p) << " \t";
6952             os << " [missing after " << m.after_index() << "]\n";
6953         }
6954     }
6955 }
6956
6957
6958 /*************************************************************************//**
6959  *
6960  * @brief prints parameter label and some properties
6961  *
6962  *****************************************************************************/
6963 template<class OStream>
6964 void print(OStream& os, const parameter& p)
6965 {
6966     if(p.greedy()) os << '!';
6967     if(p.blocking()) os << '~';
6968     if(!p.required()) os << '[';
6969     os << doc_label(p);
6970     if(p.repeatable()) os << "...";
6971     if(!p.required()) os << "]";
6972 }
6973
6974
6975 //-------------------------------------------------------------------
6976 template<class OStream>
6977 void print(OStream& os, const group& g, int level = 0);
6978
6979
6980 /*************************************************************************//**
6981  *
6982  * @brief prints group or parameter; uses indentation
6983  *
6984  *****************************************************************************/
6985 template<class OStream>
6986 void print(OStream& os, const pattern& param, int level = 0)
6987 {
6988     if(param.is_group()) {
6989         print(os, param.as_group(), level);
6990     }
6991     else {
6992         os << doc_string(4*level, ' ');
6993         print(os, param.as_param());
6994     }
6995 }
6996
6997
6998 /*************************************************************************//**
6999  *
7000  * @brief prints group and its contents; uses indentation
7001  *
7002  *****************************************************************************/
7003 template<class OStream>
7004 void print(OStream& os, const group& g, int level)
7005 {
7006     auto indent = doc_string(4*level, ' ');
7007     os << indent;
7008     if(g.blocking()) os << '~';
7009     if(g.joinable()) os << 'J';
7010     os << (g.exclusive() ? "(|\n" : "(\n");
7011     for(const auto& p : g) {
7012         print(os, p, level+1);
7013     }
7014     os << '\n' << indent << (g.exclusive() ? "|)" : ")");
7015     if(g.repeatable()) os << "...";
7016     os << '\n';
7017 }
7018
7019
7020 } // namespace debug
7021 } //namespace clipp
7022
7023 #endif
7024