File indexing completed on 2025-01-18 10:18:18
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031 #ifndef AM_CLIPP_H__
0032 #define AM_CLIPP_H__
0033
0034 #include <cstring>
0035 #include <string>
0036 #include <cstdlib>
0037 #include <cstring>
0038 #include <cctype>
0039 #include <memory>
0040 #include <vector>
0041 #include <limits>
0042 #include <stack>
0043 #include <algorithm>
0044 #include <sstream>
0045 #include <utility>
0046 #include <iterator>
0047 #include <functional>
0048
0049
0050
0051
0052
0053
0054
0055 namespace clipp {
0056
0057
0058
0059
0060
0061
0062
0063
0064 using arg_index = int;
0065
0066 using arg_string = std::string;
0067 using doc_string = std::string;
0068
0069 using arg_list = std::vector<arg_string>;
0070
0071
0072
0073
0074
0075
0076
0077
0078 enum class tri : char { no, yes, either };
0079
0080 inline constexpr bool operator == (tri t, bool b) noexcept {
0081 return b ? t != tri::no : t != tri::yes;
0082 }
0083 inline constexpr bool operator == (bool b, tri t) noexcept { return (t == b); }
0084 inline constexpr bool operator != (tri t, bool b) noexcept { return !(t == b); }
0085 inline constexpr bool operator != (bool b, tri t) noexcept { return !(t == b); }
0086
0087
0088
0089
0090
0091
0092
0093
0094 class subrange {
0095 public:
0096 using size_type = arg_string::size_type;
0097
0098
0099 explicit constexpr
0100 subrange() noexcept :
0101 at_{arg_string::npos}, length_{0}
0102 {}
0103
0104
0105 explicit constexpr
0106 subrange(size_type pos, size_type len) noexcept :
0107 at_{pos}, length_{len}
0108 {}
0109
0110
0111 constexpr size_type at() const noexcept { return at_; }
0112
0113 constexpr size_type length() const noexcept { return length_; }
0114
0115
0116 constexpr bool prefix() const noexcept {
0117 return at_ == 0;
0118 }
0119
0120
0121 constexpr explicit operator bool () const noexcept {
0122 return at_ != arg_string::npos;
0123 }
0124
0125 private:
0126 size_type at_;
0127 size_type length_;
0128 };
0129
0130
0131
0132
0133
0134
0135
0136
0137 using match_predicate = std::function<bool(const arg_string&)>;
0138 using match_function = std::function<subrange(const arg_string&)>;
0139
0140
0141
0142
0143
0144
0145
0146
0147
0148
0149
0150
0151 namespace traits {
0152
0153
0154
0155
0156
0157
0158 #if defined(__cpp_lib_is_invocable)
0159 template<class Fn, class Ret, class... Args>
0160 constexpr auto
0161 check_is_callable(int) -> decltype(
0162 std::declval<Fn>()(std::declval<Args>()...),
0163 std::integral_constant<bool,
0164 std::is_same<Ret,typename std::invoke_result<Fn,Args...>::type>::value>{} );
0165
0166 template<class Fn, class Ret>
0167 constexpr auto
0168 check_is_callable_without_arg(int) -> decltype(
0169 std::declval<Fn>()(),
0170 std::integral_constant<bool,
0171 std::is_same<Ret,typename std::invoke_result<Fn>::type>::value>{} );
0172 #else
0173 template<class Fn, class Ret, class... Args>
0174 constexpr auto
0175 check_is_callable(int) -> decltype(
0176 std::declval<Fn>()(std::declval<Args>()...),
0177 std::integral_constant<bool,
0178 std::is_same<Ret,typename std::result_of<Fn(Args...)>::type>::value>{} );
0179
0180 template<class Fn, class Ret>
0181 constexpr auto
0182 check_is_callable_without_arg(int) -> decltype(
0183 std::declval<Fn>()(),
0184 std::integral_constant<bool,
0185 std::is_same<Ret,typename std::result_of<Fn()>::type>::value>{} );
0186 #endif
0187
0188 template<class,class,class...>
0189 constexpr auto
0190 check_is_callable(long) -> std::false_type;
0191
0192 template<class,class>
0193 constexpr auto
0194 check_is_callable_without_arg(long) -> std::false_type;
0195
0196
0197
0198 template<class Fn, class... Args>
0199 constexpr auto
0200 check_is_void_callable(int) -> decltype(
0201 std::declval<Fn>()(std::declval<Args>()...), std::true_type{});
0202
0203 template<class,class,class...>
0204 constexpr auto
0205 check_is_void_callable(long) -> std::false_type;
0206
0207 template<class Fn>
0208 constexpr auto
0209 check_is_void_callable_without_arg(int) -> decltype(
0210 std::declval<Fn>()(), std::true_type{});
0211
0212 template<class>
0213 constexpr auto
0214 check_is_void_callable_without_arg(long) -> std::false_type;
0215
0216
0217
0218 template<class Fn, class Ret>
0219 struct is_callable;
0220
0221
0222 template<class Fn, class Ret, class... Args>
0223 struct is_callable<Fn, Ret(Args...)> :
0224 decltype(check_is_callable<Fn,Ret,Args...>(0))
0225 {};
0226
0227 template<class Fn, class Ret>
0228 struct is_callable<Fn,Ret()> :
0229 decltype(check_is_callable_without_arg<Fn,Ret>(0))
0230 {};
0231
0232
0233 template<class Fn, class... Args>
0234 struct is_callable<Fn, void(Args...)> :
0235 decltype(check_is_void_callable<Fn,Args...>(0))
0236 {};
0237
0238 template<class Fn>
0239 struct is_callable<Fn,void()> :
0240 decltype(check_is_void_callable_without_arg<Fn>(0))
0241 {};
0242
0243
0244
0245
0246
0247
0248
0249
0250 template<class T>
0251 constexpr auto
0252 check_is_input_range(int) -> decltype(
0253 begin(std::declval<T>()), end(std::declval<T>()),
0254 std::true_type{});
0255
0256 template<class T>
0257 constexpr auto
0258 check_is_input_range(char) -> decltype(
0259 std::begin(std::declval<T>()), std::end(std::declval<T>()),
0260 std::true_type{});
0261
0262 template<class>
0263 constexpr auto
0264 check_is_input_range(long) -> std::false_type;
0265
0266 template<class T>
0267 struct is_input_range :
0268 decltype(check_is_input_range<T>(0))
0269 {};
0270
0271
0272
0273
0274
0275
0276
0277
0278 template<class T>
0279 constexpr auto
0280 check_has_size_getter(int) ->
0281 decltype(std::declval<T>().size(), std::true_type{});
0282
0283 template<class>
0284 constexpr auto
0285 check_has_size_getter(long) -> std::false_type;
0286
0287 template<class T>
0288 struct has_size_getter :
0289 decltype(check_has_size_getter<T>(0))
0290 {};
0291
0292 }
0293
0294
0295
0296
0297
0298
0299
0300
0301
0302
0303
0304
0305 namespace detail {
0306
0307
0308
0309
0310
0311
0312
0313 inline bool
0314 fwd_to_unsigned_int(const char*& s)
0315 {
0316 if(!s) return false;
0317 for(; std::isspace(*s); ++s);
0318 if(!s[0] || s[0] == '-') return false;
0319 if(s[0] == '-') return false;
0320 return true;
0321 }
0322
0323
0324
0325
0326
0327
0328
0329 template<class T, class V, bool = (sizeof(V) > sizeof(T))>
0330 struct limits_clamped {
0331 static T from(const V& v) {
0332 if(v >= V(std::numeric_limits<T>::max())) {
0333 return std::numeric_limits<T>::max();
0334 }
0335 if(v <= V(std::numeric_limits<T>::lowest())) {
0336 return std::numeric_limits<T>::lowest();
0337 }
0338 return T(v);
0339 }
0340 };
0341
0342 template<class T, class V>
0343 struct limits_clamped<T,V,false> {
0344 static T from(const V& v) { return T(v); }
0345 };
0346
0347
0348
0349
0350
0351
0352
0353 template<class T, class V>
0354 inline T clamped_on_limits(const V& v) {
0355 return limits_clamped<T,V>::from(v);
0356 }
0357
0358
0359
0360
0361
0362
0363
0364
0365
0366 template<class T>
0367 struct make {
0368 static inline T from(const char* s) {
0369 if(!s) return false;
0370
0371 return static_cast<T>(s);
0372 }
0373 };
0374
0375 template<>
0376 struct make<bool> {
0377 static inline bool from(const char* s) {
0378 if(!s) return false;
0379 return static_cast<bool>(s);
0380 }
0381 };
0382
0383 template<>
0384 struct make<unsigned char> {
0385 static inline unsigned char from(const char* s) {
0386 if(!fwd_to_unsigned_int(s)) return (0);
0387 return clamped_on_limits<unsigned char>(std::strtoull(s,nullptr,10));
0388 }
0389 };
0390
0391 template<>
0392 struct make<unsigned short int> {
0393 static inline unsigned short int from(const char* s) {
0394 if(!fwd_to_unsigned_int(s)) return (0);
0395 return clamped_on_limits<unsigned short int>(std::strtoull(s,nullptr,10));
0396 }
0397 };
0398
0399 template<>
0400 struct make<unsigned int> {
0401 static inline unsigned int from(const char* s) {
0402 if(!fwd_to_unsigned_int(s)) return (0);
0403 return clamped_on_limits<unsigned int>(std::strtoull(s,nullptr,10));
0404 }
0405 };
0406
0407 template<>
0408 struct make<unsigned long int> {
0409 static inline unsigned long int from(const char* s) {
0410 if(!fwd_to_unsigned_int(s)) return (0);
0411 return clamped_on_limits<unsigned long int>(std::strtoull(s,nullptr,10));
0412 }
0413 };
0414
0415 template<>
0416 struct make<unsigned long long int> {
0417 static inline unsigned long long int from(const char* s) {
0418 if(!fwd_to_unsigned_int(s)) return (0);
0419 return clamped_on_limits<unsigned long long int>(std::strtoull(s,nullptr,10));
0420 }
0421 };
0422
0423 template<>
0424 struct make<char> {
0425 static inline char from(const char* s) {
0426
0427 const auto n = std::strlen(s);
0428 if(n == 1) return s[0];
0429
0430 return clamped_on_limits<char>(std::strtoll(s,nullptr,10));
0431 }
0432 };
0433
0434 template<>
0435 struct make<short int> {
0436 static inline short int from(const char* s) {
0437 return clamped_on_limits<short int>(std::strtoll(s,nullptr,10));
0438 }
0439 };
0440
0441 template<>
0442 struct make<int> {
0443 static inline int from(const char* s) {
0444 return clamped_on_limits<int>(std::strtoll(s,nullptr,10));
0445 }
0446 };
0447
0448 template<>
0449 struct make<long int> {
0450 static inline long int from(const char* s) {
0451 return clamped_on_limits<long int>(std::strtoll(s,nullptr,10));
0452 }
0453 };
0454
0455 template<>
0456 struct make<long long int> {
0457 static inline long long int from(const char* s) {
0458 return (std::strtoll(s,nullptr,10));
0459 }
0460 };
0461
0462 template<>
0463 struct make<float> {
0464 static inline float from(const char* s) {
0465 return (std::strtof(s,nullptr));
0466 }
0467 };
0468
0469 template<>
0470 struct make<double> {
0471 static inline double from(const char* s) {
0472 return (std::strtod(s,nullptr));
0473 }
0474 };
0475
0476 template<>
0477 struct make<long double> {
0478 static inline long double from(const char* s) {
0479 return (std::strtold(s,nullptr));
0480 }
0481 };
0482
0483 template<>
0484 struct make<std::string> {
0485 static inline std::string from(const char* s) {
0486 return std::string(s);
0487 }
0488 };
0489
0490
0491
0492
0493
0494
0495
0496
0497 template<class T, class V = T>
0498 class assign_value
0499 {
0500 public:
0501 template<class X>
0502 explicit constexpr
0503 assign_value(T& target, X&& value) noexcept :
0504 t_{std::addressof(target)}, v_{std::forward<X>(value)}
0505 {}
0506
0507 void operator () () const {
0508 if(t_) *t_ = v_;
0509 }
0510
0511 private:
0512 T* t_;
0513 V v_;
0514 };
0515
0516
0517
0518
0519
0520
0521
0522
0523 class flip_bool
0524 {
0525 public:
0526 explicit constexpr
0527 flip_bool(bool& target) noexcept :
0528 b_{&target}
0529 {}
0530
0531 void operator () () const {
0532 if(b_) *b_ = !*b_;
0533 }
0534
0535 private:
0536 bool* b_;
0537 };
0538
0539
0540
0541
0542
0543
0544
0545
0546 template<class T>
0547 class increment
0548 {
0549 public:
0550 explicit constexpr
0551 increment(T& target) noexcept : t_{std::addressof(target)} {}
0552
0553 void operator () () const {
0554 if(t_) ++(*t_);
0555 }
0556
0557 private:
0558 T* t_;
0559 };
0560
0561
0562
0563
0564
0565
0566
0567
0568 template<class T>
0569 class decrement
0570 {
0571 public:
0572 explicit constexpr
0573 decrement(T& target) noexcept : t_{std::addressof(target)} {}
0574
0575 void operator () () const {
0576 if(t_) --(*t_);
0577 }
0578
0579 private:
0580 T* t_;
0581 };
0582
0583
0584
0585
0586
0587
0588
0589
0590 template<class T>
0591 class increment_by
0592 {
0593 public:
0594 explicit constexpr
0595 increment_by(T& target, T by) noexcept :
0596 t_{std::addressof(target)}, by_{std::move(by)}
0597 {}
0598
0599 void operator () () const {
0600 if(t_) (*t_) += by_;
0601 }
0602
0603 private:
0604 T* t_;
0605 T by_;
0606 };
0607
0608
0609
0610
0611
0612
0613
0614
0615
0616 template<class T>
0617 class map_arg_to
0618 {
0619 public:
0620 explicit constexpr
0621 map_arg_to(T& target) noexcept : t_{std::addressof(target)} {}
0622
0623 void operator () (const char* s) const {
0624 if(t_ && s) *t_ = detail::make<T>::from(s);
0625 }
0626
0627 private:
0628 T* t_;
0629 };
0630
0631
0632
0633
0634
0635
0636 template<class T>
0637 class map_arg_to<std::vector<T>>
0638 {
0639 public:
0640 map_arg_to(std::vector<T>& target): t_{std::addressof(target)} {}
0641
0642 void operator () (const char* s) const {
0643 if(t_ && s) t_->push_back(detail::make<T>::from(s));
0644 }
0645
0646 private:
0647 std::vector<T>* t_;
0648 };
0649
0650
0651
0652
0653
0654
0655
0656 template<>
0657 class map_arg_to<bool>
0658 {
0659 public:
0660 map_arg_to(bool& target): t_{&target} {}
0661
0662 void operator () (const char* s) const {
0663 if(t_ && s) *t_ = true;
0664 }
0665
0666 private:
0667 bool* t_;
0668 };
0669
0670
0671 }
0672
0673
0674
0675
0676
0677
0678
0679
0680
0681
0682
0683
0684 namespace str {
0685
0686
0687
0688
0689
0690
0691
0692 template<class T>
0693 T make(const arg_string& s)
0694 {
0695 return detail::make<T>::from(s);
0696 }
0697
0698
0699
0700
0701
0702
0703
0704
0705 template<class C, class T, class A>
0706 inline void
0707 trimr(std::basic_string<C,T,A>& s)
0708 {
0709 if(s.empty()) return;
0710
0711 s.erase(
0712 std::find_if_not(s.rbegin(), s.rend(),
0713 [](char c) { return std::isspace(c);} ).base(),
0714 s.end() );
0715 }
0716
0717
0718
0719
0720
0721
0722
0723 template<class C, class T, class A>
0724 inline void
0725 triml(std::basic_string<C,T,A>& s)
0726 {
0727 if(s.empty()) return;
0728
0729 s.erase(
0730 s.begin(),
0731 std::find_if_not(s.begin(), s.end(),
0732 [](char c) { return std::isspace(c);})
0733 );
0734 }
0735
0736
0737
0738
0739
0740
0741
0742 template<class C, class T, class A>
0743 inline void
0744 trim(std::basic_string<C,T,A>& s)
0745 {
0746 triml(s);
0747 trimr(s);
0748 }
0749
0750
0751
0752
0753
0754
0755
0756 template<class C, class T, class A>
0757 inline void
0758 remove_ws(std::basic_string<C,T,A>& s)
0759 {
0760 if(s.empty()) return;
0761
0762 s.erase(std::remove_if(s.begin(), s.end(),
0763 [](char c) { return std::isspace(c); }),
0764 s.end() );
0765 }
0766
0767
0768
0769
0770
0771
0772
0773
0774 template<class C, class T, class A>
0775 inline bool
0776 has_prefix(const std::basic_string<C,T,A>& subject,
0777 const std::basic_string<C,T,A>& prefix)
0778 {
0779 if(prefix.size() > subject.size()) return false;
0780 return subject.find(prefix) == 0;
0781 }
0782
0783
0784
0785
0786
0787
0788
0789
0790 template<class C, class T, class A>
0791 inline bool
0792 has_postfix(const std::basic_string<C,T,A>& subject,
0793 const std::basic_string<C,T,A>& postfix)
0794 {
0795 if(postfix.size() > subject.size()) return false;
0796 return (subject.size() - postfix.size()) == subject.find(postfix);
0797 }
0798
0799
0800
0801
0802
0803
0804
0805
0806
0807
0808
0809
0810 template<class InputRange>
0811 auto
0812 longest_common_prefix(const InputRange& strs)
0813 -> typename std::decay<decltype(*begin(strs))>::type
0814 {
0815 static_assert(traits::is_input_range<InputRange>(),
0816 "parameter must satisfy the InputRange concept");
0817
0818 static_assert(traits::has_size_getter<
0819 typename std::decay<decltype(*begin(strs))>::type>(),
0820 "elements of input range must have a ::size() member function");
0821
0822 using std::begin;
0823 using std::end;
0824
0825 using item_t = typename std::decay<decltype(*begin(strs))>::type;
0826 using str_size_t = typename std::decay<decltype(begin(strs)->size())>::type;
0827
0828 const auto n = size_t(distance(begin(strs), end(strs)));
0829 if(n < 1) return item_t("");
0830 if(n == 1) return *begin(strs);
0831
0832
0833 auto m = std::min_element(begin(strs), end(strs),
0834 [](const item_t& a, const item_t& b) {
0835 return a.size() < b.size(); })->size();
0836
0837
0838 for(str_size_t i = 0; i < m; ++i) {
0839 for(str_size_t j = 1; j < n; ++j) {
0840 if(strs[j][i] != strs[j-1][i])
0841 return strs[0].substr(0, i);
0842 }
0843 }
0844 return strs[0].substr(0, m);
0845 }
0846
0847
0848
0849
0850
0851
0852
0853
0854
0855
0856
0857 template<class C, class T, class A, class InputRange>
0858 subrange
0859 longest_substring_match(const std::basic_string<C,T,A>& arg,
0860 const InputRange& substrings)
0861 {
0862 using string_t = std::basic_string<C,T,A>;
0863
0864 static_assert(traits::is_input_range<InputRange>(),
0865 "parameter must satisfy the InputRange concept");
0866
0867 static_assert(std::is_same<string_t,
0868 typename std::decay<decltype(*begin(substrings))>::type>(),
0869 "substrings must have same type as 'arg'");
0870
0871 auto i = string_t::npos;
0872 auto n = string_t::size_type(0);
0873 for(const auto& s : substrings) {
0874 auto j = arg.find(s);
0875 if(j != string_t::npos && s.size() > n) {
0876 i = j;
0877 n = s.size();
0878 }
0879 }
0880 return subrange{i,n};
0881 }
0882
0883
0884
0885
0886
0887
0888
0889
0890
0891
0892
0893 template<class C, class T, class A, class InputRange>
0894 subrange
0895 longest_prefix_match(const std::basic_string<C,T,A>& arg,
0896 const InputRange& prefixes)
0897 {
0898 using string_t = std::basic_string<C,T,A>;
0899 using s_size_t = typename string_t::size_type;
0900
0901 static_assert(traits::is_input_range<InputRange>(),
0902 "parameter must satisfy the InputRange concept");
0903
0904 static_assert(std::is_same<string_t,
0905 typename std::decay<decltype(*begin(prefixes))>::type>(),
0906 "prefixes must have same type as 'arg'");
0907
0908 auto i = string_t::npos;
0909 auto n = s_size_t(0);
0910 for(const auto& s : prefixes) {
0911 auto j = arg.find(s);
0912 if(j == 0 && s.size() > n) {
0913 i = 0;
0914 n = s.size();
0915 }
0916 }
0917 return subrange{i,n};
0918 }
0919
0920
0921
0922
0923
0924
0925
0926
0927 template<class C, class T, class A>
0928 inline subrange
0929 substring_match(const std::basic_string<C,T,A>& subject,
0930 const std::basic_string<C,T,A>& query)
0931 {
0932 if(subject.empty() && query.empty()) return subrange(0,0);
0933 if(subject.empty() || query.empty()) return subrange{};
0934 auto i = subject.find(query);
0935 if(i == std::basic_string<C,T,A>::npos) return subrange{};
0936 return subrange{i,query.size()};
0937 }
0938
0939
0940
0941
0942
0943
0944
0945
0946
0947
0948 template<class C, class T, class A>
0949 subrange
0950 first_number_match(std::basic_string<C,T,A> s,
0951 C digitSeparator = C(','),
0952 C decimalPoint = C('.'),
0953 C exponential = C('e'))
0954 {
0955 using string_t = std::basic_string<C,T,A>;
0956
0957 str::trim(s);
0958 if(s.empty()) return subrange{};
0959
0960 auto i = s.find_first_of("0123456789+-");
0961 if(i == string_t::npos) {
0962 i = s.find(decimalPoint);
0963 if(i == string_t::npos) return subrange{};
0964 }
0965
0966 bool point = false;
0967 bool sep = false;
0968 auto exp = string_t::npos;
0969 auto j = i + 1;
0970 for(; j < s.size(); ++j) {
0971 if(s[j] == digitSeparator) {
0972 if(!sep) sep = true; else break;
0973 }
0974 else {
0975 sep = false;
0976 if(s[j] == decimalPoint) {
0977
0978 if(!point && exp == string_t::npos) point = true; else break;
0979 }
0980 else if(std::tolower(s[j]) == std::tolower(exponential)) {
0981
0982 if(exp == string_t::npos) exp = j; else break;
0983 }
0984 else if(exp != string_t::npos && (exp+1) == j) {
0985
0986 if(s[j] != '+' && s[j] != '-' && !std::isdigit(s[j])) break;
0987 }
0988 else if(!std::isdigit(s[j])) {
0989 break;
0990 }
0991 }
0992 }
0993
0994
0995 if(j-i == 1 && !std::isdigit(s[i])) return subrange{};
0996
0997 return subrange{i,j-i};
0998 }
0999
1000
1001
1002
1003
1004
1005
1006
1007
1008 template<class C, class T, class A>
1009 subrange
1010 first_integer_match(std::basic_string<C,T,A> s,
1011 C digitSeparator = C(','))
1012 {
1013 using string_t = std::basic_string<C,T,A>;
1014
1015 str::trim(s);
1016 if(s.empty()) return subrange{};
1017
1018 auto i = s.find_first_of("0123456789+-");
1019 if(i == string_t::npos) return subrange{};
1020
1021 bool sep = false;
1022 auto j = i + 1;
1023 for(; j < s.size(); ++j) {
1024 if(s[j] == digitSeparator) {
1025 if(!sep) sep = true; else break;
1026 }
1027 else {
1028 sep = false;
1029 if(!std::isdigit(s[j])) break;
1030 }
1031 }
1032
1033
1034 if(j-i == 1 && !std::isdigit(s[i])) return subrange{};
1035
1036 return subrange{i,j-i};
1037 }
1038
1039
1040
1041
1042
1043
1044
1045
1046 template<class C, class T, class A>
1047 bool represents_number(const std::basic_string<C,T,A>& candidate,
1048 C digitSeparator = C(','),
1049 C decimalPoint = C('.'),
1050 C exponential = C('e'))
1051 {
1052 const auto match = str::first_number_match(candidate, digitSeparator,
1053 decimalPoint, exponential);
1054
1055 return (match && match.length() == candidate.size());
1056 }
1057
1058
1059
1060
1061
1062
1063
1064
1065 template<class C, class T, class A>
1066 bool represents_integer(const std::basic_string<C,T,A>& candidate,
1067 C digitSeparator = C(','))
1068 {
1069 const auto match = str::first_integer_match(candidate, digitSeparator);
1070 return (match && match.length() == candidate.size());
1071 }
1072
1073 }
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086 template<class T, class V>
1087 inline detail::assign_value<T,V>
1088 set(T& target, V value) {
1089 return detail::assign_value<T>{target, std::move(value)};
1090 }
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103 template<class T>
1104 inline detail::map_arg_to<T>
1105 set(T& target) {
1106 return detail::map_arg_to<T>{target};
1107 }
1108
1109
1110
1111
1112
1113
1114
1115
1116 inline detail::assign_value<bool>
1117 set(bool& target) {
1118 return detail::assign_value<bool>{target,true};
1119 }
1120
1121
1122
1123
1124
1125
1126 inline detail::assign_value<bool>
1127 unset(bool& target) {
1128 return detail::assign_value<bool>{target,false};
1129 }
1130
1131
1132
1133
1134
1135
1136 inline detail::flip_bool
1137 flip(bool& b) {
1138 return detail::flip_bool(b);
1139 }
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150 template<class T>
1151 inline detail::increment<T>
1152 increment(T& target) {
1153 return detail::increment<T>{target};
1154 }
1155
1156
1157
1158
1159
1160
1161 template<class T>
1162 inline detail::increment_by<T>
1163 increment(T& target, T by) {
1164 return detail::increment_by<T>{target, std::move(by)};
1165 }
1166
1167
1168
1169
1170
1171
1172 template<class T>
1173 inline detail::decrement<T>
1174 decrement(T& target) {
1175 return detail::decrement<T>{target};
1176 }
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188 namespace detail {
1189
1190
1191
1192
1193
1194
1195
1196 template<class Derived>
1197 class action_provider
1198 {
1199 private:
1200
1201 using simple_action = std::function<void()>;
1202 using arg_action = std::function<void(const char*)>;
1203 using index_action = std::function<void(int)>;
1204
1205
1206 class simple_action_adapter {
1207 public:
1208 simple_action_adapter() = default;
1209 simple_action_adapter(const simple_action& a): action_(a) {}
1210 simple_action_adapter(simple_action&& a): action_(std::move(a)) {}
1211 void operator() (const char*) const { action_(); }
1212 void operator() (int) const { action_(); }
1213 private:
1214 simple_action action_;
1215 };
1216
1217
1218 public:
1219
1220
1221
1222 Derived&
1223 call(arg_action a) {
1224 argActions_.push_back(std::move(a));
1225 return *static_cast<Derived*>(this);
1226 }
1227
1228
1229 Derived&
1230 call(simple_action a) {
1231 argActions_.push_back(simple_action_adapter(std::move(a)));
1232 return *static_cast<Derived*>(this);
1233 }
1234
1235
1236
1237 Derived& operator () (arg_action a) { return call(std::move(a)); }
1238
1239
1240 Derived& operator () (simple_action a) { return call(std::move(a)); }
1241
1242
1243
1244
1245
1246 template<class Target>
1247 Derived&
1248 set(Target& t) {
1249 static_assert(!std::is_pointer<Target>::value,
1250 "parameter target type must not be a pointer");
1251
1252 return call(clipp::set(t));
1253 }
1254
1255
1256 template<class Target, class Value>
1257 Derived&
1258 set(Target& t, Value&& v) {
1259 return call(clipp::set(t, std::forward<Value>(v)));
1260 }
1261
1262
1263
1264
1265
1266
1267 Derived&
1268 if_repeated(simple_action a) {
1269 repeatActions_.push_back(simple_action_adapter{std::move(a)});
1270 return *static_cast<Derived*>(this);
1271 }
1272
1273
1274
1275
1276 Derived&
1277 if_repeated(index_action a) {
1278 repeatActions_.push_back(std::move(a));
1279 return *static_cast<Derived*>(this);
1280 }
1281
1282
1283
1284
1285
1286
1287 Derived&
1288 if_missing(simple_action a) {
1289 missingActions_.push_back(simple_action_adapter{std::move(a)});
1290 return *static_cast<Derived*>(this);
1291 }
1292
1293
1294
1295
1296 Derived&
1297 if_missing(index_action a) {
1298 missingActions_.push_back(std::move(a));
1299 return *static_cast<Derived*>(this);
1300 }
1301
1302
1303
1304
1305
1306
1307 Derived&
1308 if_blocked(simple_action a) {
1309 blockedActions_.push_back(simple_action_adapter{std::move(a)});
1310 return *static_cast<Derived*>(this);
1311 }
1312
1313
1314
1315
1316
1317 Derived&
1318 if_blocked(index_action a) {
1319 blockedActions_.push_back(std::move(a));
1320 return *static_cast<Derived*>(this);
1321 }
1322
1323
1324
1325
1326
1327
1328 Derived&
1329 if_conflicted(simple_action a) {
1330 conflictActions_.push_back(simple_action_adapter{std::move(a)});
1331 return *static_cast<Derived*>(this);
1332 }
1333
1334
1335
1336
1337
1338 Derived&
1339 if_conflicted(index_action a) {
1340 conflictActions_.push_back(std::move(a));
1341 return *static_cast<Derived*>(this);
1342 }
1343
1344
1345
1346
1347
1348
1349 template<class T, class... Ts>
1350 Derived&
1351 target(T&& t, Ts&&... ts) {
1352 target(std::forward<T>(t));
1353 target(std::forward<Ts>(ts)...);
1354 return *static_cast<Derived*>(this);
1355 }
1356
1357
1358 template<class T, class = typename std::enable_if<
1359 !std::is_fundamental<typename std::decay<T>::type>() &&
1360 (traits::is_callable<T,void()>() ||
1361 traits::is_callable<T,void(const char*)>() )
1362 >::type>
1363 Derived&
1364 target(T&& t) {
1365 call(std::forward<T>(t));
1366 return *static_cast<Derived*>(this);
1367 }
1368
1369
1370
1371 template<class T, class = typename std::enable_if<
1372 std::is_fundamental<typename std::decay<T>::type>() ||
1373 (!traits::is_callable<T,void()>() &&
1374 !traits::is_callable<T,void(const char*)>() )
1375 >::type>
1376 Derived&
1377 target(T& t) {
1378 set(t);
1379 return *static_cast<Derived*>(this);
1380 }
1381
1382
1383 Derived&
1384 target() {
1385 return *static_cast<Derived*>(this);
1386 }
1387
1388
1389
1390
1391 template<class Target>
1392 inline friend Derived&
1393 operator << (Target&& t, Derived& p) {
1394 p.target(std::forward<Target>(t));
1395 return p;
1396 }
1397
1398 template<class Target>
1399 inline friend Derived&&
1400 operator << (Target&& t, Derived&& p) {
1401 p.target(std::forward<Target>(t));
1402 return std::move(p);
1403 }
1404
1405
1406
1407 template<class Target>
1408 inline friend Derived&
1409 operator >> (Derived& p, Target&& t) {
1410 p.target(std::forward<Target>(t));
1411 return p;
1412 }
1413
1414 template<class Target>
1415 inline friend Derived&&
1416 operator >> (Derived&& p, Target&& t) {
1417 p.target(std::forward<Target>(t));
1418 return std::move(p);
1419 }
1420
1421
1422
1423
1424 void execute_actions(const arg_string& arg) const {
1425 for(const auto& a : argActions_) {
1426 a(arg.c_str());
1427 }
1428 }
1429
1430
1431 void notify_repeated(arg_index idx) const {
1432 for(const auto& a : repeatActions_) a(idx);
1433 }
1434
1435 void notify_missing(arg_index idx) const {
1436 for(const auto& a : missingActions_) a(idx);
1437 }
1438
1439 void notify_blocked(arg_index idx) const {
1440 for(const auto& a : blockedActions_) a(idx);
1441 }
1442
1443 void notify_conflict(arg_index idx) const {
1444 for(const auto& a : conflictActions_) a(idx);
1445 }
1446
1447 private:
1448
1449 std::vector<arg_action> argActions_;
1450 std::vector<index_action> repeatActions_;
1451 std::vector<index_action> missingActions_;
1452 std::vector<index_action> blockedActions_;
1453 std::vector<index_action> conflictActions_;
1454 };
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466 template<class Derived>
1467 class token
1468 {
1469 public:
1470
1471 using doc_string = clipp::doc_string;
1472
1473
1474
1475
1476 const doc_string& doc() const noexcept {
1477 return doc_;
1478 }
1479
1480
1481 Derived& doc(const doc_string& txt) {
1482 doc_ = txt;
1483 return *static_cast<Derived*>(this);
1484 }
1485
1486
1487 Derived& doc(doc_string&& txt) {
1488 doc_ = std::move(txt);
1489 return *static_cast<Derived*>(this);
1490 }
1491
1492
1493
1494
1495 bool repeatable() const noexcept {
1496 return repeatable_;
1497 }
1498
1499
1500 Derived& repeatable(bool yes) noexcept {
1501 repeatable_ = yes;
1502 return *static_cast<Derived*>(this);
1503 }
1504
1505
1506
1507
1508 bool blocking() const noexcept {
1509 return blocking_;
1510 }
1511
1512
1513 Derived& blocking(bool yes) noexcept {
1514 blocking_ = yes;
1515 return *static_cast<Derived*>(this);
1516 }
1517
1518
1519 private:
1520
1521 doc_string doc_;
1522 bool repeatable_ = false;
1523 bool blocking_ = false;
1524 };
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534 template<class T>
1535 inline T&
1536 operator % (doc_string docstr, token<T>& p)
1537 {
1538 return p.doc(std::move(docstr));
1539 }
1540
1541 template<class T>
1542 inline T&&
1543 operator % (doc_string docstr, token<T>&& p)
1544 {
1545 return std::move(p.doc(std::move(docstr)));
1546 }
1547
1548
1549 template<class T>
1550 inline T&
1551 operator % (token<T>& p, doc_string docstr)
1552 {
1553 return p.doc(std::move(docstr));
1554 }
1555
1556 template<class T>
1557 inline T&&
1558 operator % (token<T>&& p, doc_string docstr)
1559 {
1560 return std::move(p.doc(std::move(docstr)));
1561 }
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571 template<class T>
1572 inline T&
1573 doc(doc_string docstr, token<T>& p)
1574 {
1575 return p.doc(std::move(docstr));
1576 }
1577
1578 template<class T>
1579 inline T&&
1580 doc(doc_string docstr, token<T>&& p)
1581 {
1582 return std::move(p.doc(std::move(docstr)));
1583 }
1584
1585
1586
1587 }
1588
1589
1590
1591
1592
1593
1594
1595
1596 namespace match {
1597
1598
1599
1600
1601
1602
1603
1604 inline bool
1605 any(const arg_string&) { return true; }
1606
1607
1608
1609
1610
1611
1612 inline bool
1613 none(const arg_string&) { return false; }
1614
1615
1616
1617
1618
1619
1620
1621
1622 inline bool
1623 nonempty(const arg_string& s) {
1624 return !s.empty();
1625 }
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635 inline bool
1636 alphanumeric(const arg_string& s) {
1637 if(s.empty()) return false;
1638 return std::all_of(s.begin(), s.end(), [](char c) {return std::isalnum(c); });
1639 }
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649 inline bool
1650 alphabetic(const arg_string& s) {
1651 return std::all_of(s.begin(), s.end(), [](char c) {return std::isalpha(c); });
1652 }
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662 class none_of
1663 {
1664 public:
1665 none_of(arg_list strs):
1666 excluded_{std::move(strs)}
1667 {}
1668
1669 template<class... Strings>
1670 none_of(arg_string str, Strings&&... strs):
1671 excluded_{std::move(str), std::forward<Strings>(strs)...}
1672 {}
1673
1674 template<class... Strings>
1675 none_of(const char* str, Strings&&... strs):
1676 excluded_{arg_string(str), std::forward<Strings>(strs)...}
1677 {}
1678
1679 bool operator () (const arg_string& arg) const {
1680 return (std::find(begin(excluded_), end(excluded_), arg)
1681 == end(excluded_));
1682 }
1683
1684 private:
1685 arg_list excluded_;
1686 };
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697 class numbers
1698 {
1699 public:
1700 explicit
1701 numbers(char decimalPoint = '.',
1702 char digitSeparator = ' ',
1703 char exponentSeparator = 'e')
1704 :
1705 decpoint_{decimalPoint}, separator_{digitSeparator},
1706 exp_{exponentSeparator}
1707 {}
1708
1709 subrange operator () (const arg_string& s) const {
1710 return str::first_number_match(s, separator_, decpoint_, exp_);
1711 }
1712
1713 private:
1714 char decpoint_;
1715 char separator_;
1716 char exp_;
1717 };
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727 class integers {
1728 public:
1729 explicit
1730 integers(char digitSeparator = ' '): separator_{digitSeparator} {}
1731
1732 subrange operator () (const arg_string& s) const {
1733 return str::first_integer_match(s, separator_);
1734 }
1735
1736 private:
1737 char separator_;
1738 };
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748 class positive_integers {
1749 public:
1750 explicit
1751 positive_integers(char digitSeparator = ' '): separator_{digitSeparator} {}
1752
1753 subrange operator () (const arg_string& s) const {
1754 auto match = str::first_integer_match(s, separator_);
1755 if(!match) return subrange{};
1756 if(s[match.at()] == '-') return subrange{};
1757 return match;
1758 }
1759
1760 private:
1761 char separator_;
1762 };
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772 class substring
1773 {
1774 public:
1775 explicit
1776 substring(arg_string str): str_{std::move(str)} {}
1777
1778 subrange operator () (const arg_string& s) const {
1779 return str::substring_match(s, str_);
1780 }
1781
1782 private:
1783 arg_string str_;
1784 };
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794 class prefix {
1795 public:
1796 explicit
1797 prefix(arg_string p): prefix_{std::move(p)} {}
1798
1799 bool operator () (const arg_string& s) const {
1800 return s.find(prefix_) == 0;
1801 }
1802
1803 private:
1804 arg_string prefix_;
1805 };
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815 class prefix_not {
1816 public:
1817 explicit
1818 prefix_not(arg_string p): prefix_{std::move(p)} {}
1819
1820 bool operator () (const arg_string& s) const {
1821 return s.find(prefix_) != 0;
1822 }
1823
1824 private:
1825 arg_string prefix_;
1826 };
1827
1828
1829
1830 using noprefix = prefix_not;
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840 class length {
1841 public:
1842 explicit
1843 length(std::size_t exact):
1844 min_{exact}, max_{exact}
1845 {}
1846
1847 explicit
1848 length(std::size_t min, std::size_t max):
1849 min_{min}, max_{max}
1850 {}
1851
1852 bool operator () (const arg_string& s) const {
1853 return s.size() >= min_ && s.size() <= max_;
1854 }
1855
1856 private:
1857 std::size_t min_;
1858 std::size_t max_;
1859 };
1860
1861
1862
1863
1864
1865
1866
1867
1868 inline length min_length(std::size_t min)
1869 {
1870 return length{min, arg_string::npos-1};
1871 }
1872
1873
1874
1875
1876
1877
1878
1879 inline length max_length(std::size_t max)
1880 {
1881 return length{0, max};
1882 }
1883
1884
1885 }
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896 class parameter :
1897 public detail::token<parameter>,
1898 public detail::action_provider<parameter>
1899 {
1900
1901 class predicate_adapter {
1902 public:
1903 explicit
1904 predicate_adapter(match_predicate pred): match_{std::move(pred)} {}
1905
1906 subrange operator () (const arg_string& arg) const {
1907 return match_(arg) ? subrange{0,arg.size()} : subrange{};
1908 }
1909
1910 private:
1911 match_predicate match_;
1912 };
1913
1914 public:
1915
1916
1917 parameter():
1918 flags_{},
1919 matcher_{predicate_adapter{match::none}},
1920 label_{}, required_{false}, greedy_{false}
1921 {}
1922
1923
1924 template<class... Strings>
1925 explicit
1926 parameter(arg_string str, Strings&&... strs):
1927 flags_{},
1928 matcher_{predicate_adapter{match::none}},
1929 label_{}, required_{false}, greedy_{false}
1930 {
1931 add_flags(std::move(str), std::forward<Strings>(strs)...);
1932 }
1933
1934
1935 explicit
1936 parameter(const arg_list& flaglist):
1937 flags_{},
1938 matcher_{predicate_adapter{match::none}},
1939 label_{}, required_{false}, greedy_{false}
1940 {
1941 add_flags(flaglist);
1942 }
1943
1944
1945
1946
1947
1948 explicit
1949 parameter(match_predicate filter):
1950 flags_{},
1951 matcher_{predicate_adapter{std::move(filter)}},
1952 label_{}, required_{false}, greedy_{false}
1953 {}
1954
1955
1956
1957
1958 explicit
1959 parameter(match_function filter):
1960 flags_{},
1961 matcher_{std::move(filter)},
1962 label_{}, required_{false}, greedy_{false}
1963 {}
1964
1965
1966
1967
1968 bool
1969 required() const noexcept {
1970 return required_;
1971 }
1972
1973
1974 parameter&
1975 required(bool yes) noexcept {
1976 required_ = yes;
1977 return *this;
1978 }
1979
1980
1981
1982
1983 bool
1984 greedy() const noexcept {
1985 return greedy_;
1986 }
1987
1988
1989 parameter&
1990 greedy(bool yes) noexcept {
1991 greedy_ = yes;
1992 return *this;
1993 }
1994
1995
1996
1997
1998
1999
2000 const doc_string&
2001 label() const {
2002 return label_;
2003 }
2004
2005
2006
2007
2008 parameter&
2009 label(const doc_string& lbl) {
2010 label_ = lbl;
2011 return *this;
2012 }
2013
2014
2015
2016
2017 parameter&
2018 label(doc_string&& lbl) {
2019 label_ = lbl;
2020 return *this;
2021 }
2022
2023
2024
2025
2026
2027
2028 subrange
2029 match(const arg_string& arg) const
2030 {
2031 if(flags_.empty()) {
2032 return matcher_(arg);
2033 }
2034 else {
2035
2036 if(arg.empty()) return subrange{};
2037
2038 if(std::find(flags_.begin(), flags_.end(), arg) != flags_.end()) {
2039 return subrange{0,arg.size()};
2040 }
2041 return str::longest_prefix_match(arg, flags_);
2042 }
2043 }
2044
2045
2046
2047
2048 const arg_list&
2049 flags() const noexcept {
2050 return flags_;
2051 }
2052
2053
2054 const match_function&
2055 matcher() const noexcept {
2056 return matcher_;
2057 }
2058
2059
2060
2061
2062 inline friend parameter&
2063 with_prefix(const arg_string& prefix, parameter& p)
2064 {
2065 if(prefix.empty() || p.flags().empty()) return p;
2066
2067 for(auto& f : p.flags_) {
2068 if(f.find(prefix) != 0) f.insert(0, prefix);
2069 }
2070 return p;
2071 }
2072
2073
2074
2075
2076 inline friend parameter&
2077 with_prefixes_short_long(
2078 const arg_string& shortpfx, const arg_string& longpfx,
2079 parameter& p)
2080 {
2081 if(shortpfx.empty() && longpfx.empty()) return p;
2082 if(p.flags().empty()) return p;
2083
2084 for(auto& f : p.flags_) {
2085 if(f.size() == 1) {
2086 if(f.find(shortpfx) != 0) f.insert(0, shortpfx);
2087 } else {
2088 if(f.find(longpfx) != 0) f.insert(0, longpfx);
2089 }
2090 }
2091 return p;
2092 }
2093
2094
2095
2096
2097 inline friend parameter&
2098 with_suffix(const arg_string& suffix, parameter& p)
2099 {
2100 if(suffix.empty() || p.flags().empty()) return p;
2101
2102 for(auto& f : p.flags_) {
2103 if(f.find(suffix) + suffix.size() != f.size()) {
2104 f.insert(f.end(), suffix.begin(), suffix.end());
2105 }
2106 }
2107 return p;
2108 }
2109
2110
2111
2112
2113 inline friend parameter&
2114 with_suffixes_short_long(
2115 const arg_string& shortsfx, const arg_string& longsfx,
2116 parameter& p)
2117 {
2118 if(shortsfx.empty() && longsfx.empty()) return p;
2119 if(p.flags().empty()) return p;
2120
2121 for(auto& f : p.flags_) {
2122 if(f.size() == 1) {
2123 if(f.find(shortsfx) + shortsfx.size() != f.size()) {
2124 f.insert(f.end(), shortsfx.begin(), shortsfx.end());
2125 }
2126 } else {
2127 if(f.find(longsfx) + longsfx.size() != f.size()) {
2128 f.insert(f.end(), longsfx.begin(), longsfx.end());
2129 }
2130 }
2131 }
2132 return p;
2133 }
2134
2135 private:
2136
2137 void add_flags(arg_string str) {
2138
2139 str::remove_ws(str);
2140 if(!str.empty()) flags_.push_back(std::move(str));
2141 }
2142
2143
2144 void add_flags(const arg_list& strs) {
2145 if(strs.empty()) return;
2146 flags_.reserve(flags_.size() + strs.size());
2147 for(const auto& s : strs) add_flags(s);
2148 }
2149
2150 template<class String1, class String2, class... Strings>
2151 void
2152 add_flags(String1&& s1, String2&& s2, Strings&&... ss) {
2153 flags_.reserve(2 + sizeof...(ss));
2154 add_flags(std::forward<String1>(s1));
2155 add_flags(std::forward<String2>(s2), std::forward<Strings>(ss)...);
2156 }
2157
2158 arg_list flags_;
2159 match_function matcher_;
2160 doc_string label_;
2161 bool required_ = false;
2162 bool greedy_ = false;
2163 };
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173 template<class String, class... Strings>
2174 inline parameter
2175 command(String&& flag, Strings&&... flags)
2176 {
2177 return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...}
2178 .required(true).blocking(true).repeatable(false);
2179 }
2180
2181
2182
2183
2184
2185
2186
2187
2188 template<class String, class... Strings>
2189 inline parameter
2190 required(String&& flag, Strings&&... flags)
2191 {
2192 return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...}
2193 .required(true).blocking(false).repeatable(false);
2194 }
2195
2196
2197
2198
2199
2200
2201
2202
2203 template<class String, class... Strings>
2204 inline parameter
2205 option(String&& flag, Strings&&... flags)
2206 {
2207 return parameter{std::forward<String>(flag), std::forward<Strings>(flags)...}
2208 .required(false).blocking(false).repeatable(false);
2209 }
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219 template<class... Targets>
2220 inline parameter
2221 value(const doc_string& label, Targets&&... tgts)
2222 {
2223 return parameter{match::nonempty}
2224 .label(label)
2225 .target(std::forward<Targets>(tgts)...)
2226 .required(true).blocking(true).repeatable(false);
2227 }
2228
2229 template<class Filter, class... Targets, class = typename std::enable_if<
2230 traits::is_callable<Filter,bool(const char*)>::value ||
2231 traits::is_callable<Filter,subrange(const char*)>::value>::type>
2232 inline parameter
2233 value(Filter&& filter, doc_string label, Targets&&... tgts)
2234 {
2235 return parameter{std::forward<Filter>(filter)}
2236 .label(label)
2237 .target(std::forward<Targets>(tgts)...)
2238 .required(true).blocking(true).repeatable(false);
2239 }
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249 template<class... Targets>
2250 inline parameter
2251 values(const doc_string& label, Targets&&... tgts)
2252 {
2253 return parameter{match::nonempty}
2254 .label(label)
2255 .target(std::forward<Targets>(tgts)...)
2256 .required(true).blocking(true).repeatable(true);
2257 }
2258
2259 template<class Filter, class... Targets, class = typename std::enable_if<
2260 traits::is_callable<Filter,bool(const char*)>::value ||
2261 traits::is_callable<Filter,subrange(const char*)>::value>::type>
2262 inline parameter
2263 values(Filter&& filter, doc_string label, Targets&&... tgts)
2264 {
2265 return parameter{std::forward<Filter>(filter)}
2266 .label(label)
2267 .target(std::forward<Targets>(tgts)...)
2268 .required(true).blocking(true).repeatable(true);
2269 }
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279 template<class... Targets>
2280 inline parameter
2281 opt_value(const doc_string& label, Targets&&... tgts)
2282 {
2283 return parameter{match::nonempty}
2284 .label(label)
2285 .target(std::forward<Targets>(tgts)...)
2286 .required(false).blocking(false).repeatable(false);
2287 }
2288
2289 template<class Filter, class... Targets, class = typename std::enable_if<
2290 traits::is_callable<Filter,bool(const char*)>::value ||
2291 traits::is_callable<Filter,subrange(const char*)>::value>::type>
2292 inline parameter
2293 opt_value(Filter&& filter, doc_string label, Targets&&... tgts)
2294 {
2295 return parameter{std::forward<Filter>(filter)}
2296 .label(label)
2297 .target(std::forward<Targets>(tgts)...)
2298 .required(false).blocking(false).repeatable(false);
2299 }
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309 template<class... Targets>
2310 inline parameter
2311 opt_values(const doc_string& label, Targets&&... tgts)
2312 {
2313 return parameter{match::nonempty}
2314 .label(label)
2315 .target(std::forward<Targets>(tgts)...)
2316 .required(false).blocking(false).repeatable(true);
2317 }
2318
2319 template<class Filter, class... Targets, class = typename std::enable_if<
2320 traits::is_callable<Filter,bool(const char*)>::value ||
2321 traits::is_callable<Filter,subrange(const char*)>::value>::type>
2322 inline parameter
2323 opt_values(Filter&& filter, doc_string label, Targets&&... tgts)
2324 {
2325 return parameter{std::forward<Filter>(filter)}
2326 .label(label)
2327 .target(std::forward<Targets>(tgts)...)
2328 .required(false).blocking(false).repeatable(true);
2329 }
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339 template<class... Targets>
2340 inline parameter
2341 word(const doc_string& label, Targets&&... tgts)
2342 {
2343 return parameter{match::alphanumeric}
2344 .label(label)
2345 .target(std::forward<Targets>(tgts)...)
2346 .required(true).blocking(true).repeatable(false);
2347 }
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357 template<class... Targets>
2358 inline parameter
2359 words(const doc_string& label, Targets&&... tgts)
2360 {
2361 return parameter{match::alphanumeric}
2362 .label(label)
2363 .target(std::forward<Targets>(tgts)...)
2364 .required(true).blocking(true).repeatable(true);
2365 }
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375 template<class... Targets>
2376 inline parameter
2377 opt_word(const doc_string& label, Targets&&... tgts)
2378 {
2379 return parameter{match::alphanumeric}
2380 .label(label)
2381 .target(std::forward<Targets>(tgts)...)
2382 .required(false).blocking(false).repeatable(false);
2383 }
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393 template<class... Targets>
2394 inline parameter
2395 opt_words(const doc_string& label, Targets&&... tgts)
2396 {
2397 return parameter{match::alphanumeric}
2398 .label(label)
2399 .target(std::forward<Targets>(tgts)...)
2400 .required(false).blocking(false).repeatable(true);
2401 }
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411 template<class... Targets>
2412 inline parameter
2413 number(const doc_string& label, Targets&&... tgts)
2414 {
2415 return parameter{match::numbers{}}
2416 .label(label)
2417 .target(std::forward<Targets>(tgts)...)
2418 .required(true).blocking(true).repeatable(false);
2419 }
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429 template<class... Targets>
2430 inline parameter
2431 numbers(const doc_string& label, Targets&&... tgts)
2432 {
2433 return parameter{match::numbers{}}
2434 .label(label)
2435 .target(std::forward<Targets>(tgts)...)
2436 .required(true).blocking(true).repeatable(true);
2437 }
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447 template<class... Targets>
2448 inline parameter
2449 opt_number(const doc_string& label, Targets&&... tgts)
2450 {
2451 return parameter{match::numbers{}}
2452 .label(label)
2453 .target(std::forward<Targets>(tgts)...)
2454 .required(false).blocking(false).repeatable(false);
2455 }
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465 template<class... Targets>
2466 inline parameter
2467 opt_numbers(const doc_string& label, Targets&&... tgts)
2468 {
2469 return parameter{match::numbers{}}
2470 .label(label)
2471 .target(std::forward<Targets>(tgts)...)
2472 .required(false).blocking(false).repeatable(true);
2473 }
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483 template<class... Targets>
2484 inline parameter
2485 integer(const doc_string& label, Targets&&... tgts)
2486 {
2487 return parameter{match::integers{}}
2488 .label(label)
2489 .target(std::forward<Targets>(tgts)...)
2490 .required(true).blocking(true).repeatable(false);
2491 }
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501 template<class... Targets>
2502 inline parameter
2503 integers(const doc_string& label, Targets&&... tgts)
2504 {
2505 return parameter{match::integers{}}
2506 .label(label)
2507 .target(std::forward<Targets>(tgts)...)
2508 .required(true).blocking(true).repeatable(true);
2509 }
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519 template<class... Targets>
2520 inline parameter
2521 opt_integer(const doc_string& label, Targets&&... tgts)
2522 {
2523 return parameter{match::integers{}}
2524 .label(label)
2525 .target(std::forward<Targets>(tgts)...)
2526 .required(false).blocking(false).repeatable(false);
2527 }
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537 template<class... Targets>
2538 inline parameter
2539 opt_integers(const doc_string& label, Targets&&... tgts)
2540 {
2541 return parameter{match::integers{}}
2542 .label(label)
2543 .target(std::forward<Targets>(tgts)...)
2544 .required(false).blocking(false).repeatable(true);
2545 }
2546
2547
2548
2549
2550
2551
2552
2553
2554 template<class... Targets>
2555 inline parameter
2556 any_other(Targets&&... tgts)
2557 {
2558 return parameter{match::any}
2559 .target(std::forward<Targets>(tgts)...)
2560 .required(false).blocking(false).repeatable(true);
2561 }
2562
2563
2564
2565
2566
2567
2568
2569
2570 template<class Filter, class... Targets, class = typename std::enable_if<
2571 traits::is_callable<Filter,bool(const char*)>::value ||
2572 traits::is_callable<Filter,subrange(const char*)>::value>::type>
2573 inline parameter
2574 any(Filter&& filter, Targets&&... tgts)
2575 {
2576 return parameter{std::forward<Filter>(filter)}
2577 .target(std::forward<Targets>(tgts)...)
2578 .required(false).blocking(false).repeatable(true);
2579 }
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590 class group :
2591 public detail::token<group>
2592 {
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604 template<class Param, class Group>
2605 struct child_t {
2606 enum class type : char {param, group};
2607 public:
2608
2609 explicit
2610 child_t(const Param& v) : m_{v}, type_{type::param} {}
2611 child_t( Param&& v) noexcept : m_{std::move(v)}, type_{type::param} {}
2612
2613 explicit
2614 child_t(const Group& g) : m_{g}, type_{type::group} {}
2615 child_t( Group&& g) noexcept : m_{std::move(g)}, type_{type::group} {}
2616
2617 child_t(const child_t& src): type_{src.type_} {
2618 switch(type_) {
2619 default:
2620 case type::param: new(&m_)data{src.m_.param}; break;
2621 case type::group: new(&m_)data{src.m_.group}; break;
2622 }
2623 }
2624
2625 child_t(child_t&& src) noexcept : type_{src.type_} {
2626 switch(type_) {
2627 default:
2628 case type::param: new(&m_)data{std::move(src.m_.param)}; break;
2629 case type::group: new(&m_)data{std::move(src.m_.group)}; break;
2630 }
2631 }
2632
2633 child_t& operator = (const child_t& src) {
2634 destroy_content();
2635 type_ = src.type_;
2636 switch(type_) {
2637 default:
2638 case type::param: new(&m_)data{src.m_.param}; break;
2639 case type::group: new(&m_)data{src.m_.group}; break;
2640 }
2641 return *this;
2642 }
2643
2644 child_t& operator = (child_t&& src) noexcept {
2645 destroy_content();
2646 type_ = src.type_;
2647 switch(type_) {
2648 default:
2649 case type::param: new(&m_)data{std::move(src.m_.param)}; break;
2650 case type::group: new(&m_)data{std::move(src.m_.group)}; break;
2651 }
2652 return *this;
2653 }
2654
2655 ~child_t() {
2656 destroy_content();
2657 }
2658
2659 const doc_string&
2660 doc() const noexcept {
2661 switch(type_) {
2662 default:
2663 case type::param: return m_.param.doc();
2664 case type::group: return m_.group.doc();
2665 }
2666 }
2667
2668 bool blocking() const noexcept {
2669 switch(type_) {
2670 case type::param: return m_.param.blocking();
2671 case type::group: return m_.group.blocking();
2672 default: return false;
2673 }
2674 }
2675 bool repeatable() const noexcept {
2676 switch(type_) {
2677 case type::param: return m_.param.repeatable();
2678 case type::group: return m_.group.repeatable();
2679 default: return false;
2680 }
2681 }
2682 bool required() const noexcept {
2683 switch(type_) {
2684 case type::param: return m_.param.required();
2685 case type::group:
2686 return (m_.group.exclusive() && m_.group.all_required() ) ||
2687 (!m_.group.exclusive() && m_.group.any_required() );
2688 default: return false;
2689 }
2690 }
2691 bool exclusive() const noexcept {
2692 switch(type_) {
2693 case type::group: return m_.group.exclusive();
2694 case type::param:
2695 default: return false;
2696 }
2697 }
2698 std::size_t param_count() const noexcept {
2699 switch(type_) {
2700 case type::group: return m_.group.param_count();
2701 case type::param:
2702 default: return std::size_t(1);
2703 }
2704 }
2705 std::size_t depth() const noexcept {
2706 switch(type_) {
2707 case type::group: return m_.group.depth();
2708 case type::param:
2709 default: return std::size_t(0);
2710 }
2711 }
2712
2713 void execute_actions(const arg_string& arg) const {
2714 switch(type_) {
2715 default:
2716 case type::group: return;
2717 case type::param: m_.param.execute_actions(arg); break;
2718 }
2719
2720 }
2721
2722 void notify_repeated(arg_index idx) const {
2723 switch(type_) {
2724 default:
2725 case type::group: return;
2726 case type::param: m_.param.notify_repeated(idx); break;
2727 }
2728 }
2729 void notify_missing(arg_index idx) const {
2730 switch(type_) {
2731 default:
2732 case type::group: return;
2733 case type::param: m_.param.notify_missing(idx); break;
2734 }
2735 }
2736 void notify_blocked(arg_index idx) const {
2737 switch(type_) {
2738 default:
2739 case type::group: return;
2740 case type::param: m_.param.notify_blocked(idx); break;
2741 }
2742 }
2743 void notify_conflict(arg_index idx) const {
2744 switch(type_) {
2745 default:
2746 case type::group: return;
2747 case type::param: m_.param.notify_conflict(idx); break;
2748 }
2749 }
2750
2751 bool is_param() const noexcept { return type_ == type::param; }
2752 bool is_group() const noexcept { return type_ == type::group; }
2753
2754 Param& as_param() noexcept { return m_.param; }
2755 Group& as_group() noexcept { return m_.group; }
2756
2757 const Param& as_param() const noexcept { return m_.param; }
2758 const Group& as_group() const noexcept { return m_.group; }
2759
2760 private:
2761 void destroy_content() {
2762 switch(type_) {
2763 default:
2764 case type::param: m_.param.~Param(); break;
2765 case type::group: m_.group.~Group(); break;
2766 }
2767 }
2768
2769 union data {
2770 data() {}
2771
2772 data(const Param& v) : param{v} {}
2773 data( Param&& v) noexcept : param{std::move(v)} {}
2774
2775 data(const Group& g) : group{g} {}
2776 data( Group&& g) noexcept : group{std::move(g)} {}
2777 ~data() {}
2778
2779 Param param;
2780 Group group;
2781 };
2782
2783 data m_;
2784 type type_;
2785 };
2786
2787
2788 public:
2789
2790 using child = child_t<parameter,group>;
2791 using value_type = child;
2792
2793 private:
2794 using children_store = std::vector<child>;
2795
2796 public:
2797 using const_iterator = children_store::const_iterator;
2798 using iterator = children_store::iterator;
2799 using size_type = children_store::size_type;
2800
2801
2802
2803
2804
2805
2806 class depth_first_traverser
2807 {
2808 public:
2809
2810 struct context {
2811 context() = default;
2812 context(const group& p):
2813 parent{&p}, cur{p.begin()}, end{p.end()}
2814 {}
2815 const group* parent = nullptr;
2816 const_iterator cur;
2817 const_iterator end;
2818 };
2819 using context_list = std::vector<context>;
2820
2821
2822 class memento {
2823 friend class depth_first_traverser;
2824 int level_;
2825 context context_;
2826 public:
2827 int level() const noexcept { return level_; }
2828 const child* param() const noexcept { return &(*context_.cur); }
2829 };
2830
2831 depth_first_traverser() = default;
2832
2833 explicit
2834 depth_first_traverser(const group& cur): stack_{} {
2835 if(!cur.empty()) stack_.emplace_back(cur);
2836 }
2837
2838 explicit operator bool() const noexcept {
2839 return !stack_.empty();
2840 }
2841
2842 int level() const noexcept {
2843 return int(stack_.size());
2844 }
2845
2846 bool is_first_in_parent() const noexcept {
2847 if(stack_.empty()) return false;
2848 return (stack_.back().cur == stack_.back().parent->begin());
2849 }
2850
2851 bool is_last_in_parent() const noexcept {
2852 if(stack_.empty()) return false;
2853 return (stack_.back().cur+1 == stack_.back().end);
2854 }
2855
2856 bool is_last_in_path() const noexcept {
2857 if(stack_.empty()) return false;
2858 for(const auto& t : stack_) {
2859 if(t.cur+1 != t.end) return false;
2860 }
2861 const auto& top = stack_.back();
2862
2863 if(top.cur->is_group()) return false;
2864 return true;
2865 }
2866
2867
2868 bool is_alternative(int minlevel = 0) const noexcept {
2869 if(stack_.empty()) return false;
2870 if(minlevel > 0) minlevel -= 1;
2871 if(minlevel >= int(stack_.size())) return false;
2872 return std::any_of(stack_.begin() + minlevel, stack_.end(),
2873 [](const context& c) { return c.parent->exclusive(); });
2874 }
2875
2876
2877 bool is_repeatable(int minlevel = 0) const noexcept {
2878 if(stack_.empty()) return false;
2879 if(stack_.back().cur->repeatable()) return true;
2880 if(minlevel > 0) minlevel -= 1;
2881 if(minlevel >= int(stack_.size())) return false;
2882 return std::any_of(stack_.begin() + minlevel, stack_.end(),
2883 [](const context& c) { return c.parent->repeatable(); });
2884 }
2885
2886
2887 bool is_inside(const group* g) const noexcept {
2888 if(!g) return false;
2889 return std::any_of(stack_.begin(), stack_.end(),
2890 [g](const context& c) { return c.parent == g; });
2891 }
2892
2893
2894 bool joinable() const noexcept {
2895 if(stack_.empty()) return false;
2896 return std::any_of(stack_.begin(), stack_.end(),
2897 [](const context& c) { return c.parent->joinable(); });
2898 }
2899
2900 const context_list&
2901 stack() const {
2902 return stack_;
2903 }
2904
2905
2906 const group*
2907 innermost_repeat_group() const noexcept {
2908 auto i = std::find_if(stack_.rbegin(), stack_.rend(),
2909 [](const context& c) { return c.parent->repeatable(); });
2910 return i != stack_.rend() ? i->parent : nullptr;
2911 }
2912
2913
2914 const group*
2915 innermost_exclusive_group() const noexcept {
2916 auto i = std::find_if(stack_.rbegin(), stack_.rend(),
2917 [](const context& c) { return c.parent->exclusive(); });
2918 return i != stack_.rend() ? i->parent : nullptr;
2919 }
2920
2921
2922 const group*
2923 innermost_blocking_group() const noexcept {
2924 auto i = std::find_if(stack_.rbegin(), stack_.rend(),
2925 [](const context& c) { return c.parent->blocking(); });
2926 return i != stack_.rend() ? i->parent : nullptr;
2927 }
2928
2929
2930 const group*
2931 outermost_blocking_group_fully_explored() const noexcept {
2932 if(stack_.empty()) return nullptr;
2933
2934 const group* g = nullptr;
2935 for(auto i = stack_.rbegin(); i != stack_.rend(); ++i) {
2936 if(i->cur+1 == i->end) {
2937 if(i->parent->blocking()) g = i->parent;
2938 } else {
2939 return g;
2940 }
2941 }
2942 return g;
2943 }
2944
2945
2946 const group*
2947 outermost_join_group() const noexcept {
2948 auto i = std::find_if(stack_.begin(), stack_.end(),
2949 [](const context& c) { return c.parent->joinable(); });
2950 return i != stack_.end() ? i->parent : nullptr;
2951 }
2952
2953 const group* root() const noexcept {
2954 return stack_.empty() ? nullptr : stack_.front().parent;
2955 }
2956
2957
2958 arg_string common_flag_prefix() const noexcept {
2959 if(stack_.empty()) return "";
2960 auto g = outermost_join_group();
2961 return g ? g->common_flag_prefix() : arg_string("");
2962 }
2963
2964 const child&
2965 operator * () const noexcept {
2966 return *stack_.back().cur;
2967 }
2968
2969 const child*
2970 operator -> () const noexcept {
2971 return &(*stack_.back().cur);
2972 }
2973
2974 const group&
2975 parent() const noexcept {
2976 return *(stack_.back().parent);
2977 }
2978
2979
2980
2981 depth_first_traverser&
2982 operator ++ () {
2983 if(stack_.empty()) return *this;
2984
2985 if(stack_.back().cur->is_group()) {
2986 stack_.emplace_back(stack_.back().cur->as_group());
2987 }
2988 else {
2989 next_sibling();
2990 }
2991 return *this;
2992 }
2993
2994
2995 depth_first_traverser&
2996 next_sibling() {
2997 if(stack_.empty()) return *this;
2998 ++stack_.back().cur;
2999
3000 while(stack_.back().cur == stack_.back().end) {
3001
3002 stack_.pop_back();
3003 if(stack_.empty()) return *this;
3004
3005 ++stack_.back().cur;
3006 }
3007 return *this;
3008 }
3009
3010
3011 depth_first_traverser&
3012 next_after_siblings() {
3013 if(stack_.empty()) return *this;
3014 stack_.back().cur = stack_.back().end-1;
3015 next_sibling();
3016 return *this;
3017 }
3018
3019
3020
3021
3022 depth_first_traverser&
3023 back_to_ancestor(const group* g) {
3024 if(!g) return *this;
3025 while(!stack_.empty()) {
3026 const auto& top = stack_.back().cur;
3027 if(top->is_group() && &(top->as_group()) == g) return *this;
3028 stack_.pop_back();
3029 }
3030 return *this;
3031 }
3032
3033
3034
3035
3036 depth_first_traverser&
3037 skip_siblings() {
3038 if(stack_.empty()) return *this;
3039
3040 stack_.back().end = stack_.back().cur+1;
3041 return *this;
3042 }
3043
3044
3045
3046
3047
3048 depth_first_traverser&
3049 skip_alternatives() {
3050 if(stack_.empty()) return *this;
3051
3052
3053
3054 for(auto& c : stack_) {
3055 if(c.parent && c.parent->exclusive() && c.cur < c.end)
3056 c.end = c.cur+1;
3057 }
3058
3059 return *this;
3060 }
3061
3062 void invalidate() {
3063 stack_.clear();
3064 }
3065
3066 inline friend bool operator == (const depth_first_traverser& a,
3067 const depth_first_traverser& b)
3068 {
3069 if(a.stack_.empty() || b.stack_.empty()) return false;
3070
3071
3072 if(a.stack_.back().parent != b.stack_.back().parent) return false;
3073
3074 bool aEnd = a.stack_.back().cur == a.stack_.back().end;
3075 bool bEnd = b.stack_.back().cur == b.stack_.back().end;
3076
3077 if(aEnd && bEnd) return true;
3078
3079 if(aEnd || bEnd) return false;
3080 return std::addressof(*a.stack_.back().cur) ==
3081 std::addressof(*b.stack_.back().cur);
3082 }
3083 inline friend bool operator != (const depth_first_traverser& a,
3084 const depth_first_traverser& b)
3085 {
3086 return !(a == b);
3087 }
3088
3089 memento
3090 undo_point() const {
3091 memento m;
3092 m.level_ = int(stack_.size());
3093 if(!stack_.empty()) m.context_ = stack_.back();
3094 return m;
3095 }
3096
3097 void undo(const memento& m) {
3098 if(m.level_ < 1) return;
3099 if(m.level_ <= int(stack_.size())) {
3100 stack_.erase(stack_.begin() + m.level_, stack_.end());
3101 stack_.back() = m.context_;
3102 }
3103 else if(stack_.empty() && m.level_ == 1) {
3104 stack_.push_back(m.context_);
3105 }
3106 }
3107
3108 private:
3109 context_list stack_;
3110 };
3111
3112
3113
3114 group() = default;
3115
3116 template<class Param, class... Params>
3117 explicit
3118 group(doc_string docstr, Param param, Params... params):
3119 children_{}, exclusive_{false}, joinable_{false}, scoped_{true}
3120 {
3121 doc(std::move(docstr));
3122 push_back(std::move(param), std::move(params)...);
3123 }
3124
3125 template<class... Params>
3126 explicit
3127 group(parameter param, Params... params):
3128 children_{}, exclusive_{false}, joinable_{false}, scoped_{true}
3129 {
3130 push_back(std::move(param), std::move(params)...);
3131 }
3132
3133 template<class P2, class... Ps>
3134 explicit
3135 group(group p1, P2 p2, Ps... ps):
3136 children_{}, exclusive_{false}, joinable_{false}, scoped_{true}
3137 {
3138 push_back(std::move(p1), std::move(p2), std::move(ps)...);
3139 }
3140
3141
3142
3143 group(const group&) = default;
3144 group(group&&) = default;
3145
3146
3147
3148 group& operator = (const group&) = default;
3149 group& operator = (group&&) = default;
3150
3151
3152
3153
3154
3155
3156 group& joinable(bool yes) {
3157 joinable_ = yes;
3158 return *this;
3159 }
3160
3161
3162
3163
3164 bool joinable() const noexcept {
3165 return joinable_;
3166 }
3167
3168
3169
3170
3171
3172
3173
3174 group& scoped(bool yes) {
3175 scoped_ = yes;
3176 return *this;
3177 }
3178
3179
3180
3181
3182 bool scoped() const noexcept
3183 {
3184 return scoped_;
3185 }
3186
3187
3188
3189
3190 group& exclusive(bool yes) {
3191 exclusive_ = yes;
3192 return *this;
3193 }
3194
3195 bool exclusive() const noexcept {
3196 return exclusive_;
3197 }
3198
3199
3200
3201
3202 bool any_required() const
3203 {
3204 return std::any_of(children_.begin(), children_.end(),
3205 [](const child& n){ return n.required(); });
3206 }
3207
3208 bool all_required() const
3209 {
3210 return std::all_of(children_.begin(), children_.end(),
3211 [](const child& n){ return n.required(); });
3212 }
3213
3214
3215
3216
3217 bool any_optional() const {
3218 return !all_required();
3219 }
3220
3221 bool all_optional() const {
3222 return !any_required();
3223 }
3224
3225
3226
3227
3228 bool blocking() const noexcept {
3229 return token<group>::blocking() || (exclusive() && all_blocking());
3230 }
3231
3232
3233 group& blocking(bool yes) {
3234 return token<group>::blocking(yes);
3235 }
3236
3237
3238
3239 bool any_blocking() const
3240 {
3241 return std::any_of(children_.begin(), children_.end(),
3242 [](const child& n){ return n.blocking(); });
3243 }
3244
3245
3246 bool all_blocking() const
3247 {
3248 return std::all_of(children_.begin(), children_.end(),
3249 [](const child& n){ return n.blocking(); });
3250 }
3251
3252
3253
3254
3255 bool any_flagless() const
3256 {
3257 return std::any_of(children_.begin(), children_.end(),
3258 [](const child& p){
3259 return p.is_param() && p.as_param().flags().empty();
3260 });
3261 }
3262
3263 bool all_flagless() const
3264 {
3265 return std::all_of(children_.begin(), children_.end(),
3266 [](const child& p){
3267 return p.is_param() && p.as_param().flags().empty();
3268 });
3269 }
3270
3271
3272
3273
3274 group&
3275 push_back(const parameter& v) {
3276 children_.emplace_back(v);
3277 return *this;
3278 }
3279
3280
3281 group&
3282 push_back(parameter&& v) {
3283 children_.emplace_back(std::move(v));
3284 return *this;
3285 }
3286
3287
3288 group&
3289 push_back(const group& g) {
3290 children_.emplace_back(g);
3291 return *this;
3292 }
3293
3294
3295 group&
3296 push_back(group&& g) {
3297 children_.emplace_back(std::move(g));
3298 return *this;
3299 }
3300
3301
3302
3303
3304 template<class Param1, class Param2, class... Params>
3305 group&
3306 push_back(Param1&& param1, Param2&& param2, Params&&... params)
3307 {
3308 children_.reserve(children_.size() + 2 + sizeof...(params));
3309 push_back(std::forward<Param1>(param1));
3310 push_back(std::forward<Param2>(param2), std::forward<Params>(params)...);
3311 return *this;
3312 }
3313
3314
3315
3316
3317 group&
3318 push_front(const parameter& v) {
3319 children_.emplace(children_.begin(), v);
3320 return *this;
3321 }
3322
3323
3324 group&
3325 push_front(parameter&& v) {
3326 children_.emplace(children_.begin(), std::move(v));
3327 return *this;
3328 }
3329
3330
3331 group&
3332 push_front(const group& g) {
3333 children_.emplace(children_.begin(), g);
3334 return *this;
3335 }
3336
3337
3338 group&
3339 push_front(group&& g) {
3340 children_.emplace(children_.begin(), std::move(g));
3341 return *this;
3342 }
3343
3344
3345
3346
3347 group&
3348 merge(group&& g)
3349 {
3350 children_.insert(children_.end(),
3351 std::make_move_iterator(g.begin()),
3352 std::make_move_iterator(g.end()));
3353 return *this;
3354 }
3355
3356
3357 template<class... Groups>
3358 group&
3359 merge(group&& g1, group&& g2, Groups&&... gs)
3360 {
3361 merge(std::move(g1));
3362 merge(std::move(g2), std::forward<Groups>(gs)...);
3363 return *this;
3364 }
3365
3366
3367
3368
3369 child& operator [] (size_type index) noexcept {
3370 return children_[index];
3371 }
3372
3373 const child& operator [] (size_type index) const noexcept {
3374 return children_[index];
3375 }
3376
3377
3378
3379 child& front() noexcept { return children_.front(); }
3380
3381 const child& front() const noexcept { return children_.front(); }
3382
3383
3384 child& back() noexcept { return children_.back(); }
3385
3386 const child& back() const noexcept { return children_.back(); }
3387
3388
3389
3390
3391 bool empty() const noexcept { return children_.empty(); }
3392
3393
3394 size_type size() const noexcept { return children_.size(); }
3395
3396
3397 size_type depth() const {
3398 size_type n = 0;
3399 for(const auto& c : children_) {
3400 auto l = 1 + c.depth();
3401 if(l > n) n = l;
3402 }
3403 return n;
3404 }
3405
3406
3407
3408
3409 iterator begin() noexcept { return children_.begin(); }
3410
3411 const_iterator begin() const noexcept { return children_.begin(); }
3412
3413 const_iterator cbegin() const noexcept { return children_.begin(); }
3414
3415
3416 iterator end() noexcept { return children_.end(); }
3417
3418 const_iterator end() const noexcept { return children_.end(); }
3419
3420 const_iterator cend() const noexcept { return children_.end(); }
3421
3422
3423
3424
3425
3426
3427 depth_first_traverser
3428 begin_dfs() const noexcept {
3429 return depth_first_traverser{*this};
3430 }
3431
3432
3433
3434
3435 size_type param_count() const {
3436 size_type c = 0;
3437 for(const auto& n : children_) {
3438 c += n.param_count();
3439 }
3440 return c;
3441 }
3442
3443
3444
3445
3446 arg_list all_flags() const
3447 {
3448 std::vector<arg_string> all;
3449 gather_flags(children_, all);
3450 return all;
3451 }
3452
3453
3454
3455 bool flags_are_prefix_free() const
3456 {
3457 const auto fs = all_flags();
3458
3459 using std::begin; using std::end;
3460 for(auto i = begin(fs), e = end(fs); i != e; ++i) {
3461 if(!i->empty()) {
3462 for(auto j = i+1; j != e; ++j) {
3463 if(!j->empty() && *i != *j) {
3464 if(i->find(*j) == 0) return false;
3465 if(j->find(*i) == 0) return false;
3466 }
3467 }
3468 }
3469 }
3470
3471 return true;
3472 }
3473
3474
3475
3476
3477 arg_string common_flag_prefix() const
3478 {
3479 arg_list prefixes;
3480 gather_prefixes(children_, prefixes);
3481 return str::longest_common_prefix(prefixes);
3482 }
3483
3484
3485 private:
3486
3487 static void
3488 gather_flags(const children_store& nodes, arg_list& all)
3489 {
3490 for(const auto& p : nodes) {
3491 if(p.is_group()) {
3492 gather_flags(p.as_group().children_, all);
3493 }
3494 else {
3495 const auto& pf = p.as_param().flags();
3496 using std::begin;
3497 using std::end;
3498 if(!pf.empty()) all.insert(end(all), begin(pf), end(pf));
3499 }
3500 }
3501 }
3502
3503 static void
3504 gather_prefixes(const children_store& nodes, arg_list& all)
3505 {
3506 for(const auto& p : nodes) {
3507 if(p.is_group()) {
3508 gather_prefixes(p.as_group().children_, all);
3509 }
3510 else if(!p.as_param().flags().empty()) {
3511 auto pfx = str::longest_common_prefix(p.as_param().flags());
3512 if(!pfx.empty()) all.push_back(std::move(pfx));
3513 }
3514 }
3515 }
3516
3517
3518 children_store children_;
3519 bool exclusive_ = false;
3520 bool joinable_ = false;
3521 bool scoped_ = false;
3522 };
3523
3524
3525
3526
3527
3528
3529
3530
3531 using pattern = group::child;
3532
3533
3534
3535
3536
3537
3538
3539
3540 template<class Action>
3541 void for_all_params(group& g, Action&& action)
3542 {
3543 for(auto& p : g) {
3544 if(p.is_group()) {
3545 for_all_params(p.as_group(), action);
3546 }
3547 else {
3548 action(p.as_param());
3549 }
3550 }
3551 }
3552
3553 template<class Action>
3554 void for_all_params(const group& g, Action&& action)
3555 {
3556 for(auto& p : g) {
3557 if(p.is_group()) {
3558 for_all_params(p.as_group(), action);
3559 }
3560 else {
3561 action(p.as_param());
3562 }
3563 }
3564 }
3565
3566
3567
3568
3569
3570
3571
3572
3573 inline group
3574 operator , (parameter a, parameter b)
3575 {
3576 return group{std::move(a), std::move(b)}.scoped(false);
3577 }
3578
3579
3580 inline group
3581 operator , (parameter a, group b)
3582 {
3583 return !b.scoped() && !b.blocking() && !b.exclusive() && !b.repeatable()
3584 && !b.joinable() && (b.doc().empty() || b.doc() == a.doc())
3585 ? b.push_front(std::move(a))
3586 : group{std::move(a), std::move(b)}.scoped(false);
3587 }
3588
3589
3590 inline group
3591 operator , (group a, parameter b)
3592 {
3593 return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable()
3594 && !a.joinable() && (a.doc().empty() || a.doc() == b.doc())
3595 ? a.push_back(std::move(b))
3596 : group{std::move(a), std::move(b)}.scoped(false);
3597 }
3598
3599
3600 inline group
3601 operator , (group a, group b)
3602 {
3603 return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable()
3604 && !a.joinable() && (a.doc().empty() || a.doc() == b.doc())
3605 ? a.push_back(std::move(b))
3606 : group{std::move(a), std::move(b)}.scoped(false);
3607 }
3608
3609
3610
3611
3612
3613
3614
3615
3616 template<class Param, class... Params>
3617 inline group
3618 one_of(Param param, Params... params)
3619 {
3620 return group{std::move(param), std::move(params)...}.exclusive(true);
3621 }
3622
3623
3624
3625
3626
3627
3628
3629 inline group
3630 operator | (parameter a, parameter b)
3631 {
3632 return group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3633 }
3634
3635
3636 inline group
3637 operator | (parameter a, group b)
3638 {
3639 return !b.scoped() && !b.blocking() && b.exclusive() && !b.repeatable()
3640 && !b.joinable()
3641 && (b.doc().empty() || b.doc() == a.doc())
3642 ? b.push_front(std::move(a))
3643 : group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3644 }
3645
3646
3647 inline group
3648 operator | (group a, parameter b)
3649 {
3650 return !a.scoped() && a.exclusive() && !a.repeatable() && !a.joinable()
3651 && a.blocking() == b.blocking()
3652 && (a.doc().empty() || a.doc() == b.doc())
3653 ? a.push_back(std::move(b))
3654 : group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3655 }
3656
3657 inline group
3658 operator | (group a, group b)
3659 {
3660 return !a.scoped() && a.exclusive() &&!a.repeatable() && !a.joinable()
3661 && a.blocking() == b.blocking()
3662 && (a.doc().empty() || a.doc() == b.doc())
3663 ? a.push_back(std::move(b))
3664 : group{std::move(a), std::move(b)}.scoped(false).exclusive(true);
3665 }
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675 namespace detail {
3676
3677 inline void set_blocking(bool) {}
3678
3679 template<class P, class... Ps>
3680 void set_blocking(bool yes, P& p, Ps&... ps) {
3681 p.blocking(yes);
3682 set_blocking(yes, ps...);
3683 }
3684
3685 }
3686
3687
3688
3689
3690
3691
3692
3693 template<class Param, class... Params>
3694 inline group
3695 in_sequence(Param param, Params... params)
3696 {
3697 detail::set_blocking(true, param, params...);
3698 return group{std::move(param), std::move(params)...}.scoped(true);
3699 }
3700
3701
3702
3703
3704
3705
3706
3707 inline group
3708 operator & (parameter a, parameter b)
3709 {
3710 a.blocking(true);
3711 b.blocking(true);
3712 return group{std::move(a), std::move(b)}.scoped(true);
3713 }
3714
3715
3716 inline group
3717 operator & (parameter a, group b)
3718 {
3719 a.blocking(true);
3720 return group{std::move(a), std::move(b)}.scoped(true);
3721 }
3722
3723
3724 inline group
3725 operator & (group a, parameter b)
3726 {
3727 b.blocking(true);
3728 if(a.all_blocking() && !a.exclusive() && !a.repeatable() && !a.joinable()
3729 && (a.doc().empty() || a.doc() == b.doc()))
3730 {
3731 return a.push_back(std::move(b));
3732 }
3733 else {
3734 if(!a.all_blocking()) a.blocking(true);
3735 return group{std::move(a), std::move(b)}.scoped(true);
3736 }
3737 }
3738
3739 inline group
3740 operator & (group a, group b)
3741 {
3742 if(!b.all_blocking()) b.blocking(true);
3743 if(a.all_blocking() && !a.exclusive() && !a.repeatable()
3744 && !a.joinable() && (a.doc().empty() || a.doc() == b.doc()))
3745 {
3746 return a.push_back(std::move(b));
3747 }
3748 else {
3749 if(!a.all_blocking()) a.blocking(true);
3750 return group{std::move(a), std::move(b)}.scoped(true);
3751 }
3752 }
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762 inline group
3763 joinable(group g) {
3764 return g.joinable(true);
3765 }
3766
3767
3768 template<class... Params>
3769 inline group
3770 joinable(parameter param, Params... params)
3771 {
3772 return group{std::move(param), std::move(params)...}.joinable(true);
3773 }
3774
3775 template<class P2, class... Ps>
3776 inline group
3777 joinable(group p1, P2 p2, Ps... ps)
3778 {
3779 return group{std::move(p1), std::move(p2), std::move(ps)...}.joinable(true);
3780 }
3781
3782 template<class Param, class... Params>
3783 inline group
3784 joinable(doc_string docstr, Param param, Params... params)
3785 {
3786 return group{std::move(param), std::move(params)...}
3787 .joinable(true).doc(std::move(docstr));
3788 }
3789
3790
3791
3792
3793
3794
3795
3796
3797 inline parameter
3798 repeatable(parameter p) {
3799 return p.repeatable(true);
3800 }
3801
3802
3803
3804
3805
3806
3807 inline group
3808 repeatable(group g) {
3809 return g.repeatable(true);
3810 }
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823 template<class P2, class... Ps>
3824 inline group
3825 repeatable(parameter p1, P2 p2, Ps... ps)
3826 {
3827 return group{std::move(p1), std::move(p2),
3828 std::move(ps)...}.repeatable(true);
3829 }
3830
3831 template<class P2, class... Ps>
3832 inline group
3833 repeatable(group p1, P2 p2, Ps... ps)
3834 {
3835 return group{std::move(p1), std::move(p2),
3836 std::move(ps)...}.repeatable(true);
3837 }
3838
3839
3840
3841
3842
3843
3844
3845
3846 inline parameter
3847 greedy(parameter p) {
3848 return p.greedy(true);
3849 }
3850
3851 inline parameter
3852 operator ! (parameter p) {
3853 return greedy(p);
3854 }
3855
3856
3857
3858
3859
3860
3861
3862
3863 inline parameter&&
3864 with_prefix(const arg_string& prefix, parameter&& p) {
3865 return std::move(with_prefix(prefix, p));
3866 }
3867
3868
3869
3870 inline group&
3871 with_prefix(const arg_string& prefix, group& g)
3872 {
3873 for(auto& p : g) {
3874 if(p.is_group()) {
3875 with_prefix(prefix, p.as_group());
3876 } else {
3877 with_prefix(prefix, p.as_param());
3878 }
3879 }
3880 return g;
3881 }
3882
3883
3884 inline group&&
3885 with_prefix(const arg_string& prefix, group&& params)
3886 {
3887 return std::move(with_prefix(prefix, params));
3888 }
3889
3890
3891 template<class Param, class... Params>
3892 inline group
3893 with_prefix(arg_string prefix, Param&& param, Params&&... params)
3894 {
3895 return with_prefix(prefix, group{std::forward<Param>(param),
3896 std::forward<Params>(params)...});
3897 }
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909 inline parameter&&
3910 with_prefixes_short_long(const arg_string& shortpfx, const arg_string& longpfx,
3911 parameter&& p)
3912 {
3913 return std::move(with_prefixes_short_long(shortpfx, longpfx, p));
3914 }
3915
3916
3917
3918 inline group&
3919 with_prefixes_short_long(const arg_string& shortFlagPrefix,
3920 const arg_string& longFlagPrefix,
3921 group& g)
3922 {
3923 for(auto& p : g) {
3924 if(p.is_group()) {
3925 with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_group());
3926 } else {
3927 with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_param());
3928 }
3929 }
3930 return g;
3931 }
3932
3933
3934 inline group&&
3935 with_prefixes_short_long(const arg_string& shortFlagPrefix,
3936 const arg_string& longFlagPrefix,
3937 group&& params)
3938 {
3939 return std::move(with_prefixes_short_long(shortFlagPrefix, longFlagPrefix,
3940 params));
3941 }
3942
3943
3944 template<class Param, class... Params>
3945 inline group
3946 with_prefixes_short_long(const arg_string& shortFlagPrefix,
3947 const arg_string& longFlagPrefix,
3948 Param&& param, Params&&... params)
3949 {
3950 return with_prefixes_short_long(shortFlagPrefix, longFlagPrefix,
3951 group{std::forward<Param>(param),
3952 std::forward<Params>(params)...});
3953 }
3954
3955
3956
3957
3958
3959
3960
3961
3962 inline parameter&&
3963 with_suffix(const arg_string& suffix, parameter&& p) {
3964 return std::move(with_suffix(suffix, p));
3965 }
3966
3967
3968
3969 inline group&
3970 with_suffix(const arg_string& suffix, group& g)
3971 {
3972 for(auto& p : g) {
3973 if(p.is_group()) {
3974 with_suffix(suffix, p.as_group());
3975 } else {
3976 with_suffix(suffix, p.as_param());
3977 }
3978 }
3979 return g;
3980 }
3981
3982
3983 inline group&&
3984 with_suffix(const arg_string& suffix, group&& params)
3985 {
3986 return std::move(with_suffix(suffix, params));
3987 }
3988
3989
3990 template<class Param, class... Params>
3991 inline group
3992 with_suffix(arg_string suffix, Param&& param, Params&&... params)
3993 {
3994 return with_suffix(suffix, group{std::forward<Param>(param),
3995 std::forward<Params>(params)...});
3996 }
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008 inline parameter&&
4009 with_suffixes_short_long(const arg_string& shortsfx, const arg_string& longsfx,
4010 parameter&& p)
4011 {
4012 return std::move(with_suffixes_short_long(shortsfx, longsfx, p));
4013 }
4014
4015
4016
4017 inline group&
4018 with_suffixes_short_long(const arg_string& shortFlagSuffix,
4019 const arg_string& longFlagSuffix,
4020 group& g)
4021 {
4022 for(auto& p : g) {
4023 if(p.is_group()) {
4024 with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, p.as_group());
4025 } else {
4026 with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, p.as_param());
4027 }
4028 }
4029 return g;
4030 }
4031
4032
4033 inline group&&
4034 with_suffixes_short_long(const arg_string& shortFlagSuffix,
4035 const arg_string& longFlagSuffix,
4036 group&& params)
4037 {
4038 return std::move(with_suffixes_short_long(shortFlagSuffix, longFlagSuffix,
4039 params));
4040 }
4041
4042
4043 template<class Param, class... Params>
4044 inline group
4045 with_suffixes_short_long(const arg_string& shortFlagSuffix,
4046 const arg_string& longFlagSuffix,
4047 Param&& param, Params&&... params)
4048 {
4049 return with_suffixes_short_long(shortFlagSuffix, longFlagSuffix,
4050 group{std::forward<Param>(param),
4051 std::forward<Params>(params)...});
4052 }
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067 namespace detail {
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078 class scoped_dfs_traverser
4079 {
4080 public:
4081 using dfs_traverser = group::depth_first_traverser;
4082
4083 scoped_dfs_traverser() = default;
4084
4085 explicit
4086 scoped_dfs_traverser(const group& g):
4087 pos_{g}, lastMatch_{}, posAfterLastMatch_{}, scopes_{},
4088 ignoreBlocks_{false},
4089 repeatGroupStarted_{false}, repeatGroupContinues_{false}
4090 {}
4091
4092 const dfs_traverser& base() const noexcept { return pos_; }
4093 const dfs_traverser& last_match() const noexcept { return lastMatch_; }
4094
4095 const group& parent() const noexcept { return pos_.parent(); }
4096
4097 const group* innermost_repeat_group() const noexcept {
4098 return pos_.innermost_repeat_group();
4099 }
4100 const group* outermost_join_group() const noexcept {
4101 return pos_.outermost_join_group();
4102 }
4103 const group* innermost_blocking_group() const noexcept {
4104 return pos_.innermost_blocking_group();
4105 }
4106 const group* innermost_exclusive_group() const noexcept {
4107 return pos_.innermost_exclusive_group();
4108 }
4109
4110 const pattern* operator ->() const noexcept { return pos_.operator->(); }
4111 const pattern& operator *() const noexcept { return *pos_; }
4112
4113 const pattern* ptr() const noexcept { return pos_.operator->(); }
4114
4115 explicit operator bool() const noexcept { return bool(pos_); }
4116
4117 bool joinable() const noexcept { return pos_.joinable(); }
4118 arg_string common_flag_prefix() const { return pos_.common_flag_prefix(); }
4119
4120 void ignore_blocking(bool yes) { ignoreBlocks_ = yes; }
4121
4122 void invalidate() {
4123 pos_.invalidate();
4124 }
4125
4126 bool matched() const noexcept {
4127 return (pos_ == lastMatch_);
4128 }
4129
4130 bool start_of_repeat_group() const noexcept { return repeatGroupStarted_; }
4131
4132
4133 scoped_dfs_traverser&
4134 next_sibling() { pos_.next_sibling(); return *this; }
4135
4136 scoped_dfs_traverser&
4137 next_after_siblings() { pos_.next_after_siblings(); return *this; }
4138
4139
4140
4141 scoped_dfs_traverser&
4142 operator ++ ()
4143 {
4144 if(!pos_) return *this;
4145
4146 if(pos_.is_last_in_path()) {
4147 return_to_outermost_scope();
4148 return *this;
4149 }
4150
4151
4152 if(ignoreBlocks_ || matched()) {
4153 ++pos_;
4154 }
4155 else if(!pos_->is_group()) {
4156
4157 const group* g = pos_.outermost_blocking_group_fully_explored();
4158
4159 if(g && !lastMatch_.is_inside(g)) {
4160 pos_.back_to_ancestor(g).next_after_siblings();
4161 if(!pos_) return_to_outermost_scope();
4162 }
4163 else if(pos_->blocking()) {
4164 if(pos_.parent().exclusive()) {
4165 pos_.next_sibling();
4166 } else {
4167
4168 pos_.next_after_siblings();
4169 }
4170 if(!pos_) return_to_outermost_scope();
4171 } else {
4172 ++pos_;
4173 }
4174 } else {
4175 ++pos_;
4176 }
4177 check_if_left_scope();
4178 return *this;
4179 }
4180
4181
4182 void next_after_match(scoped_dfs_traverser match)
4183 {
4184 if(!match || ignoreBlocks_) return;
4185
4186 check_repeat_group_start(match);
4187
4188 lastMatch_ = match.base();
4189
4190
4191 if(!match->blocking()) {
4192 match.pos_.back_to_ancestor(match.innermost_blocking_group());
4193 }
4194
4195
4196
4197
4198
4199 if(match.base() != pos_ && pos_->blocking()) pos_.next_sibling();
4200
4201 if(match->blocking()) {
4202 if(match.pos_.is_alternative()) {
4203
4204 match.pos_.skip_alternatives();
4205 }
4206
4207 if(is_last_in_current_scope(match.pos_)) {
4208
4209 if(!match->repeatable() && !match->is_group()) {
4210 pos_ = std::move(match.pos_);
4211 if(!scopes_.empty()) pos_.undo(scopes_.top());
4212 }
4213 else {
4214 pos_ = std::move(match.pos_);
4215 }
4216 }
4217 else {
4218
4219 if(!match->repeatable() && !match->is_group()) {
4220 ++match.pos_;
4221 }
4222
4223 if(match.pos_.level() > pos_.level()) {
4224 scopes_.push(pos_.undo_point());
4225 pos_ = std::move(match.pos_);
4226 }
4227 else if(match.pos_.level() < pos_.level()) {
4228 return_to_level(match.pos_.level());
4229 }
4230 else {
4231 pos_ = std::move(match.pos_);
4232 }
4233 }
4234 posAfterLastMatch_ = pos_;
4235 }
4236 else {
4237 if(match.pos_.level() < pos_.level()) {
4238 return_to_level(match.pos_.level());
4239 }
4240 posAfterLastMatch_ = pos_;
4241 }
4242 repeatGroupContinues_ = repeat_group_continues();
4243 }
4244
4245 private:
4246
4247 bool is_last_in_current_scope(const dfs_traverser& pos) const
4248 {
4249 if(scopes_.empty()) return pos.is_last_in_path();
4250
4251 auto p = pos;
4252 ++p;
4253 return p.level() < scopes_.top().level();
4254 }
4255
4256
4257 void check_repeat_group_start(const scoped_dfs_traverser& newMatch)
4258 {
4259 const auto newrg = newMatch.innermost_repeat_group();
4260 if(!newrg) {
4261 repeatGroupStarted_ = false;
4262 }
4263 else if(lastMatch_.innermost_repeat_group() != newrg) {
4264 repeatGroupStarted_ = true;
4265 }
4266 else if(!repeatGroupContinues_ || !newMatch.repeatGroupContinues_) {
4267 repeatGroupStarted_ = true;
4268 }
4269 else {
4270
4271
4272
4273
4274 repeatGroupStarted_ = scopes_.empty() || (
4275 newrg == pos_.root() &&
4276 scopes_.top().param() == &(*pos_.root()->begin()) );
4277 }
4278 repeatGroupContinues_ = repeatGroupStarted_;
4279 }
4280
4281
4282 bool repeat_group_continues() const
4283 {
4284 if(!repeatGroupContinues_) return false;
4285 const auto curRepGroup = pos_.innermost_repeat_group();
4286 if(!curRepGroup) return false;
4287 if(curRepGroup != lastMatch_.innermost_repeat_group()) return false;
4288 if(!posAfterLastMatch_) return false;
4289 return true;
4290 }
4291
4292
4293 void check_if_left_scope()
4294 {
4295 if(posAfterLastMatch_) {
4296 if(pos_.level() < posAfterLastMatch_.level()) {
4297 while(!scopes_.empty() && scopes_.top().level() >= pos_.level()) {
4298 pos_.undo(scopes_.top());
4299 scopes_.pop();
4300 }
4301 posAfterLastMatch_.invalidate();
4302 }
4303 }
4304 while(!scopes_.empty() && scopes_.top().level() > pos_.level()) {
4305 pos_.undo(scopes_.top());
4306 scopes_.pop();
4307 }
4308 repeatGroupContinues_ = repeat_group_continues();
4309 }
4310
4311
4312 void return_to_outermost_scope()
4313 {
4314 posAfterLastMatch_.invalidate();
4315
4316 if(scopes_.empty()) {
4317 pos_.invalidate();
4318 repeatGroupContinues_ = false;
4319 return;
4320 }
4321
4322 while(!scopes_.empty() && (!pos_ || pos_.level() >= 1)) {
4323 pos_.undo(scopes_.top());
4324 scopes_.pop();
4325 }
4326 while(!scopes_.empty()) scopes_.pop();
4327
4328 repeatGroupContinues_ = repeat_group_continues();
4329 }
4330
4331
4332 void return_to_level(int level)
4333 {
4334 if(pos_.level() <= level) return;
4335 while(!scopes_.empty() && pos_.level() > level) {
4336 pos_.undo(scopes_.top());
4337 scopes_.pop();
4338 }
4339 };
4340
4341 dfs_traverser pos_;
4342 dfs_traverser lastMatch_;
4343 dfs_traverser posAfterLastMatch_;
4344 std::stack<dfs_traverser::memento> scopes_;
4345 bool ignoreBlocks_ = false;
4346 bool repeatGroupStarted_ = false;
4347 bool repeatGroupContinues_ = false;
4348 };
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358 struct select_all {
4359 bool operator () (const parameter&) const noexcept { return true; }
4360 };
4361
4362 struct select_flags {
4363 bool operator () (const parameter& p) const noexcept {
4364 return !p.flags().empty();
4365 }
4366 };
4367
4368 struct select_values {
4369 bool operator () (const parameter& p) const noexcept {
4370 return p.flags().empty();
4371 }
4372 };
4373
4374
4375
4376
4377
4378
4379
4380
4381 class match_t {
4382 public:
4383 using size_type = arg_string::size_type;
4384
4385 match_t() = default;
4386
4387 match_t(arg_string s, scoped_dfs_traverser p):
4388 str_{std::move(s)}, pos_{std::move(p)}
4389 {}
4390
4391 size_type length() const noexcept { return str_.size(); }
4392
4393 const arg_string& str() const noexcept { return str_; }
4394 const scoped_dfs_traverser& pos() const noexcept { return pos_; }
4395
4396 explicit operator bool() const noexcept { return bool(pos_); }
4397
4398 private:
4399 arg_string str_;
4400 scoped_dfs_traverser pos_;
4401 };
4402
4403
4404
4405
4406
4407
4408
4409
4410
4411 template<class ParamSelector>
4412 match_t
4413 full_match(scoped_dfs_traverser pos, const arg_string& arg,
4414 const ParamSelector& select)
4415 {
4416 while(pos) {
4417 if(pos->is_param()) {
4418 const auto& param = pos->as_param();
4419 if(select(param)) {
4420 const auto match = param.match(arg);
4421 if(match && match.length() == arg.size()) {
4422 return match_t{arg, std::move(pos)};
4423 }
4424 }
4425 }
4426 ++pos;
4427 }
4428 return match_t{};
4429 }
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440 template<class ParamSelector>
4441 match_t
4442 longest_prefix_match(scoped_dfs_traverser pos, const arg_string& arg,
4443 const ParamSelector& select)
4444 {
4445 match_t longest;
4446
4447 while(pos) {
4448 if(pos->is_param()) {
4449 const auto& param = pos->as_param();
4450 if(select(param)) {
4451 auto match = param.match(arg);
4452 if(match.prefix()) {
4453 if(match.length() == arg.size()) {
4454 return match_t{arg, std::move(pos)};
4455 }
4456 else if(match.length() > longest.length()) {
4457 longest = match_t{arg.substr(match.at(), match.length()),
4458 pos};
4459 }
4460 }
4461 }
4462 }
4463 ++pos;
4464 }
4465 return longest;
4466 }
4467
4468
4469
4470
4471
4472
4473
4474
4475
4476 template<class ParamSelector>
4477 match_t
4478 partial_match(scoped_dfs_traverser pos, const arg_string& arg,
4479 const ParamSelector& select)
4480 {
4481 while(pos) {
4482 if(pos->is_param()) {
4483 const auto& param = pos->as_param();
4484 if(select(param)) {
4485 const auto match = param.match(arg);
4486 if(match) {
4487 return match_t{arg.substr(match.at(), match.length()),
4488 std::move(pos)};
4489 }
4490 }
4491 }
4492 ++pos;
4493 }
4494 return match_t{};
4495 }
4496
4497 }
4498
4499
4500
4501
4502
4503
4504
4505
4506
4507
4508
4509 class parser
4510 {
4511 public:
4512 using dfs_traverser = group::depth_first_traverser;
4513 using scoped_dfs_traverser = detail::scoped_dfs_traverser;
4514
4515
4516
4517
4518
4519 class arg_mapping {
4520 public:
4521 friend class parser;
4522
4523 explicit
4524 arg_mapping(arg_index idx, arg_string s,
4525 const dfs_traverser& match)
4526 :
4527 index_{idx}, arg_{std::move(s)}, match_{match},
4528 repeat_{0}, startsRepeatGroup_{false},
4529 blocked_{false}, conflict_{false}
4530 {}
4531
4532 explicit
4533 arg_mapping(arg_index idx, arg_string s) :
4534 index_{idx}, arg_{std::move(s)}, match_{},
4535 repeat_{0}, startsRepeatGroup_{false},
4536 blocked_{false}, conflict_{false}
4537 {}
4538
4539 arg_index index() const noexcept { return index_; }
4540 const arg_string& arg() const noexcept { return arg_; }
4541
4542 const parameter* param() const noexcept {
4543 return match_ && match_->is_param()
4544 ? &(match_->as_param()) : nullptr;
4545 }
4546
4547 std::size_t repeat() const noexcept { return repeat_; }
4548
4549 bool blocked() const noexcept { return blocked_; }
4550 bool conflict() const noexcept { return conflict_; }
4551
4552 bool bad_repeat() const noexcept {
4553 if(!param()) return false;
4554 return repeat_ > 0 && !param()->repeatable()
4555 && !match_.innermost_repeat_group();
4556 }
4557
4558 bool any_error() const noexcept {
4559 return !match_ || blocked() || conflict() || bad_repeat();
4560 }
4561
4562 private:
4563 arg_index index_;
4564 arg_string arg_;
4565 dfs_traverser match_;
4566 std::size_t repeat_;
4567 bool startsRepeatGroup_;
4568 bool blocked_;
4569 bool conflict_;
4570 };
4571
4572
4573
4574
4575 class missing_event {
4576 public:
4577 explicit
4578 missing_event(const parameter* p, arg_index after):
4579 param_{p}, aftIndex_{after}
4580 {}
4581
4582 const parameter* param() const noexcept { return param_; }
4583
4584 arg_index after_index() const noexcept { return aftIndex_; }
4585
4586 private:
4587 const parameter* param_;
4588 arg_index aftIndex_;
4589 };
4590
4591
4592 using missing_events = std::vector<missing_event>;
4593 using arg_mappings = std::vector<arg_mapping>;
4594
4595
4596 private:
4597 struct miss_candidate {
4598 miss_candidate(dfs_traverser p, arg_index idx,
4599 bool firstInRepeatGroup = false):
4600 pos{std::move(p)}, index{idx},
4601 startsRepeatGroup{firstInRepeatGroup}
4602 {}
4603
4604 dfs_traverser pos;
4605 arg_index index;
4606 bool startsRepeatGroup;
4607 };
4608 using miss_candidates = std::vector<miss_candidate>;
4609
4610
4611 public:
4612
4613
4614
4615
4616 explicit
4617 parser(const group& root, arg_index offset = 0):
4618 root_{&root}, pos_{root},
4619 index_{offset-1}, eaten_{0},
4620 args_{}, missCand_{}, blocked_{false}
4621 {
4622 for_each_potential_miss(dfs_traverser{root},
4623 [this](const dfs_traverser& p){
4624 missCand_.emplace_back(p, index_);
4625 });
4626 }
4627
4628
4629
4630
4631 bool operator() (const arg_string& arg)
4632 {
4633 ++eaten_;
4634 ++index_;
4635
4636 if(!valid()) return false;
4637
4638 if(!blocked_ && try_match(arg)) return true;
4639
4640 if(try_match_blocked(arg)) return false;
4641
4642
4643 if(!blocked_ && !pos_.matched() && pos_->required() && pos_->blocking()) {
4644 blocked_ = true;
4645 }
4646
4647 add_nomatch(arg);
4648 return false;
4649 }
4650
4651
4652
4653
4654 const arg_mappings& args() const {
4655 return args_;
4656 }
4657
4658
4659 missing_events missed() const {
4660 missing_events misses;
4661 misses.reserve(missCand_.size());
4662 for(auto i = missCand_.begin(); i != missCand_.end(); ++i) {
4663 misses.emplace_back(&(i->pos->as_param()), i->index);
4664 }
4665 return misses;
4666 }
4667
4668
4669 arg_index parse_count() const noexcept { return eaten_; }
4670
4671
4672
4673
4674 bool valid() const noexcept { return bool(pos_); }
4675
4676
4677
4678
4679 explicit operator bool() const noexcept { return valid(); }
4680
4681
4682 private:
4683
4684 using match_t = detail::match_t;
4685
4686
4687
4688
4689 bool try_match_blocked(const arg_string& arg)
4690 {
4691
4692 if(pos_) {
4693 auto ahead = *this;
4694 if(try_match_blocked(std::move(ahead), arg)) return true;
4695 }
4696
4697
4698 if(root_) {
4699 parser all{*root_, index_+1};
4700 if(try_match_blocked(std::move(all), arg)) return true;
4701 }
4702
4703 return false;
4704 }
4705
4706
4707 bool try_match_blocked(parser&& parse, const arg_string& arg)
4708 {
4709 const auto nold = int(parse.args_.size());
4710
4711 parse.pos_.ignore_blocking(true);
4712
4713 if(!parse.try_match(arg)) return false;
4714
4715 for(auto i = parse.args_.begin() + nold; i != parse.args_.end(); ++i) {
4716 args_.push_back(*i);
4717 args_.back().blocked_ = true;
4718 }
4719 return true;
4720 }
4721
4722
4723
4724 bool try_match(const arg_string& arg)
4725 {
4726
4727 if(pos_->is_param() && pos_->blocking() && pos_->as_param().greedy()) {
4728 const auto match = pos_->as_param().match(arg);
4729 if(match && match.length() == arg.size()) {
4730 add_match(detail::match_t{arg,pos_});
4731 return true;
4732 }
4733 }
4734
4735
4736 if(try_match_full(arg, detail::select_flags{})) return true;
4737 if(try_match_joined_flags(arg)) return true;
4738 if(try_match_joined_sequence(arg, detail::select_flags{})) return true;
4739
4740 if(try_match_full(arg, detail::select_values{})) return true;
4741 if(try_match_joined_sequence(arg, detail::select_all{})) return true;
4742
4743 if(try_match_joined_params(arg)) return true;
4744 return false;
4745 }
4746
4747
4748
4749
4750
4751
4752 template<class ParamSelector>
4753 bool try_match_full(const arg_string& arg, const ParamSelector& select)
4754 {
4755 auto match = detail::full_match(pos_, arg, select);
4756 if(!match) return false;
4757 add_match(match);
4758 return true;
4759 }
4760
4761
4762
4763
4764
4765
4766
4767 template<class ParamSelector>
4768 bool try_match_joined_sequence(arg_string arg,
4769 const ParamSelector& acceptFirst)
4770 {
4771 auto fstMatch = detail::longest_prefix_match(pos_, arg, acceptFirst);
4772
4773 if(!fstMatch) return false;
4774
4775 if(fstMatch.str().size() == arg.size()) {
4776 add_match(fstMatch);
4777 return true;
4778 }
4779
4780 if(!fstMatch.pos()->blocking()) return false;
4781
4782 auto pos = fstMatch.pos();
4783 pos.ignore_blocking(true);
4784 const auto parent = &pos.parent();
4785 if(!pos->repeatable()) ++pos;
4786
4787 arg.erase(0, fstMatch.str().size());
4788 std::vector<match_t> matches { std::move(fstMatch) };
4789
4790 while(!arg.empty() && pos &&
4791 pos->blocking() && pos->is_param() &&
4792 (&pos.parent() == parent))
4793 {
4794 auto match = pos->as_param().match(arg);
4795
4796 if(match.prefix()) {
4797 matches.emplace_back(arg.substr(0,match.length()), pos);
4798 arg.erase(0, match.length());
4799 if(!pos->repeatable()) ++pos;
4800 }
4801 else {
4802 if(!pos->repeatable()) return false;
4803 ++pos;
4804 }
4805
4806 }
4807
4808 if(!arg.empty() || matches.empty()) return false;
4809
4810 for(const auto& m : matches) add_match(m);
4811 return true;
4812 }
4813
4814
4815
4816 bool try_match_joined_flags(const arg_string& arg)
4817 {
4818 return find_join_group(pos_, [&](const group& g) {
4819 return try_match_joined(g, arg, detail::select_flags{},
4820 g.common_flag_prefix());
4821 });
4822 }
4823
4824
4825
4826 bool try_match_joined_params(const arg_string& arg)
4827 {
4828 return find_join_group(pos_, [&](const group& g) {
4829 return try_match_joined(g, arg, detail::select_all{});
4830 });
4831 }
4832
4833
4834
4835
4836
4837 template<class ParamSelector>
4838 bool try_match_joined(const group& joinGroup, arg_string arg,
4839 const ParamSelector& select,
4840 const arg_string& prefix = "")
4841 {
4842
4843 parser parse {joinGroup};
4844
4845 std::vector<match_t> matches;
4846
4847 while(!arg.empty()) {
4848 auto match = detail::longest_prefix_match(parse.pos_, arg, select);
4849
4850 if(!match) return false;
4851
4852 arg.erase(0, match.str().size());
4853
4854
4855 if(!arg.empty() && !prefix.empty() && arg.find(prefix) != 0 &&
4856 prefix != match.str())
4857 {
4858 arg.insert(0,prefix);
4859 }
4860
4861 parse.add_match(match);
4862 matches.push_back(std::move(match));
4863 }
4864
4865 if(!arg.empty() || matches.empty()) return false;
4866
4867 if(!parse.missCand_.empty()) return false;
4868 for(const auto& a : parse.args_) if(a.any_error()) return false;
4869
4870
4871 for(const auto& m : matches) add_match(m);
4872 return true;
4873 }
4874
4875
4876 template<class GroupSelector>
4877 bool find_join_group(const scoped_dfs_traverser& start,
4878 const GroupSelector& accept) const
4879 {
4880 if(start && start.parent().joinable()) {
4881 const auto& g = start.parent();
4882 if(accept(g)) return true;
4883 return false;
4884 }
4885
4886 auto pos = start;
4887 while(pos) {
4888 if(pos->is_group() && pos->as_group().joinable()) {
4889 const auto& g = pos->as_group();
4890 if(accept(g)) return true;
4891 pos.next_sibling();
4892 }
4893 else {
4894 ++pos;
4895 }
4896 }
4897 return false;
4898 }
4899
4900
4901
4902 void add_nomatch(const arg_string& arg) {
4903 args_.emplace_back(index_, arg);
4904 }
4905
4906
4907
4908 void add_match(const match_t& match)
4909 {
4910 const auto& pos = match.pos();
4911 if(!pos || !pos->is_param()) return;
4912
4913 pos_.next_after_match(pos);
4914
4915 arg_mapping newArg{index_, match.str(), pos.base()};
4916 newArg.repeat_ = occurrences_of(&pos->as_param());
4917 newArg.conflict_ = check_conflicts(pos.base());
4918 newArg.startsRepeatGroup_ = pos_.start_of_repeat_group();
4919 args_.push_back(std::move(newArg));
4920
4921 add_miss_candidates_after(pos);
4922 clean_miss_candidates_for(pos.base());
4923 discard_alternative_miss_candidates(pos.base());
4924
4925 }
4926
4927
4928 bool check_conflicts(const dfs_traverser& match)
4929 {
4930 if(pos_.start_of_repeat_group()) return false;
4931 bool conflict = false;
4932 for(const auto& m : match.stack()) {
4933 if(m.parent->exclusive()) {
4934 for(auto i = args_.rbegin(); i != args_.rend(); ++i) {
4935 if(!i->blocked()) {
4936 for(const auto& c : i->match_.stack()) {
4937
4938 if(c.parent == m.parent && c.cur != m.cur) {
4939 conflict = true;
4940 i->conflict_ = true;
4941 }
4942 }
4943 }
4944
4945 if(i->startsRepeatGroup_) break;
4946 }
4947 }
4948 }
4949 return conflict;
4950 }
4951
4952
4953 void clean_miss_candidates_for(const dfs_traverser& match)
4954 {
4955 auto i = std::find_if(missCand_.rbegin(), missCand_.rend(),
4956 [&](const miss_candidate& m) {
4957 return &(*m.pos) == &(*match);
4958 });
4959
4960 if(i != missCand_.rend()) {
4961 missCand_.erase(prev(i.base()));
4962 }
4963 }
4964
4965
4966 void discard_alternative_miss_candidates(const dfs_traverser& match)
4967 {
4968 if(missCand_.empty()) return;
4969
4970
4971
4972
4973
4974 for(const auto& m : match.stack()) {
4975 if(m.parent->exclusive()) {
4976 for(auto i = int(missCand_.size())-1; i >= 0; --i) {
4977 bool removed = false;
4978 for(const auto& c : missCand_[i].pos.stack()) {
4979
4980 if(c.parent == m.parent && c.cur != m.cur) {
4981 missCand_.erase(missCand_.begin() + i);
4982 if(missCand_.empty()) return;
4983 removed = true;
4984 break;
4985 }
4986 }
4987
4988 if(i > 0 && removed) {
4989 if(missCand_[i-1].startsRepeatGroup) break;
4990 } else {
4991 if(missCand_[i].startsRepeatGroup) break;
4992 }
4993 }
4994 }
4995 }
4996 }
4997
4998
4999 void add_miss_candidates_after(const scoped_dfs_traverser& match)
5000 {
5001 auto npos = match.base();
5002 if(npos.is_alternative()) npos.skip_alternatives();
5003 ++npos;
5004
5005
5006 const auto newRepGroup = match.innermost_repeat_group();
5007 if(newRepGroup) {
5008 if(pos_.start_of_repeat_group()) {
5009 for_each_potential_miss(std::move(npos),
5010 [&,this](const dfs_traverser& pos) {
5011
5012 if(newRepGroup == pos.innermost_repeat_group()) {
5013 missCand_.emplace_back(pos, index_, true);
5014 }
5015 });
5016 }
5017 }
5018
5019 else if(match->blocking() && !match->required() &&
5020 npos.level() >= match.base().level())
5021 {
5022 for_each_potential_miss(std::move(npos),
5023 [&,this](const dfs_traverser& pos) {
5024
5025 if(std::find_if(missCand_.begin(), missCand_.end(),
5026 [&](const miss_candidate& c){
5027 return &(*c.pos) == &(*pos);
5028 }) == missCand_.end())
5029 {
5030 missCand_.emplace_back(pos, index_);
5031 }
5032 });
5033 }
5034
5035 }
5036
5037
5038 template<class Action>
5039 static void
5040 for_each_potential_miss(dfs_traverser pos, Action&& action)
5041 {
5042 const auto level = pos.level();
5043 while(pos && pos.level() >= level) {
5044 if(pos->is_group() ) {
5045 const auto& g = pos->as_group();
5046 if(g.all_optional() || (g.exclusive() && g.any_optional())) {
5047 pos.next_sibling();
5048 } else {
5049 ++pos;
5050 }
5051 } else {
5052 if(pos->required()) {
5053 action(pos);
5054 ++pos;
5055 } else if(pos->blocking()) {
5056 pos.next_after_siblings();
5057 } else {
5058 ++pos;
5059 }
5060 }
5061 }
5062 }
5063
5064
5065
5066 std::size_t occurrences_of(const parameter* p) const
5067 {
5068 if(!p) return 0;
5069
5070 auto i = std::find_if(args_.rbegin(), args_.rend(),
5071 [p](const arg_mapping& a){ return a.param() == p; });
5072
5073 if(i != args_.rend()) return i->repeat() + 1;
5074 return 0;
5075 }
5076
5077
5078
5079 const group* root_;
5080 scoped_dfs_traverser pos_;
5081 arg_index index_;
5082 arg_index eaten_;
5083 arg_mappings args_;
5084 miss_candidates missCand_;
5085 bool blocked_;
5086 };
5087
5088
5089
5090
5091
5092
5093
5094
5095
5096
5097 class parsing_result
5098 {
5099 public:
5100 using arg_mapping = parser::arg_mapping;
5101 using arg_mappings = parser::arg_mappings;
5102 using missing_event = parser::missing_event;
5103 using missing_events = parser::missing_events;
5104 using iterator = arg_mappings::const_iterator;
5105
5106
5107
5108 parsing_result() = default;
5109
5110 parsing_result(arg_mappings arg2param, missing_events misses):
5111 arg2param_{std::move(arg2param)}, missing_{std::move(misses)}
5112 {}
5113
5114
5115
5116
5117
5118 arg_mappings::size_type
5119 unmapped_args_count() const noexcept {
5120 return std::count_if(arg2param_.begin(), arg2param_.end(),
5121 [](const arg_mapping& a){ return !a.param(); });
5122 }
5123
5124
5125
5126
5127 bool any_blocked() const noexcept {
5128 return std::any_of(arg2param_.begin(), arg2param_.end(),
5129 [](const arg_mapping& a){ return a.blocked(); });
5130 }
5131
5132
5133
5134 bool any_conflict() const noexcept {
5135 return std::any_of(arg2param_.begin(), arg2param_.end(),
5136 [](const arg_mapping& a){ return a.conflict(); });
5137 }
5138
5139
5140
5141 bool any_bad_repeat() const noexcept {
5142 return std::any_of(arg2param_.begin(), arg2param_.end(),
5143 [](const arg_mapping& a){ return a.bad_repeat(); });
5144 }
5145
5146
5147
5148 bool any_error() const noexcept {
5149 return unmapped_args_count() > 0 || !missing().empty() ||
5150 any_blocked() || any_conflict() || any_bad_repeat();
5151 }
5152
5153
5154
5155 explicit operator bool() const noexcept { return !any_error(); }
5156
5157
5158 const missing_events& missing() const noexcept { return missing_; }
5159
5160
5161
5162 iterator begin() const noexcept { return arg2param_.begin(); }
5163
5164
5165 iterator end() const noexcept { return arg2param_.end(); }
5166
5167 private:
5168
5169 arg_mappings arg2param_;
5170 missing_events missing_;
5171 };
5172
5173
5174
5175
5176 namespace detail {
5177 namespace {
5178
5179
5180
5181
5182
5183
5184
5185
5186 void sanitize_args(arg_list& args)
5187 {
5188
5189
5190 if(args.empty()) return;
5191
5192 for(auto i = begin(args)+1; i != end(args); ++i) {
5193 if(i != begin(args) && i->size() > 1 &&
5194 i->find('.') == 0 && std::isdigit((*i)[1]) )
5195 {
5196
5197 using std::prev;
5198 auto& prv = *prev(i);
5199 auto fstDigit = std::find_if_not(prv.rbegin(), prv.rend(),
5200 [](arg_string::value_type c){
5201 return std::isdigit(c);
5202 }).base();
5203
5204
5205 if(fstDigit > prv.begin() &&
5206 (*prev(fstDigit) == '+' || *prev(fstDigit) == '-'))
5207 {
5208 --fstDigit;
5209 }
5210
5211
5212 i->insert(begin(*i), fstDigit, end(prv));
5213
5214
5215 prv.erase(fstDigit, end(prv));
5216 }
5217 }
5218 }
5219
5220
5221
5222
5223
5224
5225
5226
5227 void execute_actions(const parsing_result& res)
5228 {
5229 for(const auto& m : res) {
5230 if(m.param()) {
5231 const auto& param = *(m.param());
5232
5233 if(m.repeat() > 0) param.notify_repeated(m.index());
5234 if(m.blocked()) param.notify_blocked(m.index());
5235 if(m.conflict()) param.notify_conflict(m.index());
5236
5237 if(!m.any_error()) param.execute_actions(m.arg());
5238 }
5239 }
5240
5241 for(auto m : res.missing()) {
5242 if(m.param()) m.param()->notify_missing(m.after_index());
5243 }
5244 }
5245
5246
5247
5248
5249
5250
5251
5252
5253 static parsing_result
5254 parse_args(const arg_list& args, const group& cli,
5255 arg_index offset = 0)
5256 {
5257
5258 parser parse{cli, offset};
5259 for(const auto& arg : args) {
5260 parse(arg);
5261 if(!parse.valid()) break;
5262 }
5263
5264 return parsing_result{parse.args(), parse.missed()};
5265 }
5266
5267
5268
5269
5270
5271
5272 static parsing_result
5273 parse_and_execute(const arg_list& args, const group& cli,
5274 arg_index offset = 0)
5275 {
5276 auto result = parse_args(args, cli, offset);
5277
5278 execute_actions(result);
5279
5280 return result;
5281 }
5282
5283 }
5284 }
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294 inline parsing_result
5295 parse(arg_list args, const group& cli)
5296 {
5297 detail::sanitize_args(args);
5298 return detail::parse_and_execute(args, cli);
5299 }
5300
5301
5302
5303
5304
5305
5306
5307 inline parsing_result
5308 parse(std::initializer_list<const char*> arglist, const group& cli)
5309 {
5310 arg_list args;
5311 args.reserve(arglist.size());
5312 for(auto a : arglist) {
5313 args.push_back(a);
5314 }
5315
5316 return parse(std::move(args), cli);
5317 }
5318
5319
5320
5321
5322
5323
5324
5325 template<class InputIterator>
5326 inline parsing_result
5327 parse(InputIterator first, InputIterator last, const group& cli)
5328 {
5329 return parse(arg_list(first,last), cli);
5330 }
5331
5332
5333
5334
5335
5336
5337
5338 inline parsing_result
5339 parse(const int argc, char* argv[], const group& cli, arg_index offset = 1)
5340 {
5341 arg_list args;
5342 if(offset < argc) args.assign(argv+offset, argv+argc);
5343 detail::sanitize_args(args);
5344 return detail::parse_and_execute(args, cli, offset);
5345 }
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358 class param_filter
5359 {
5360 public:
5361
5362 param_filter& prefix(const arg_string& p) noexcept {
5363 prefix_ = p; return *this;
5364 }
5365
5366 param_filter& prefix(arg_string&& p) noexcept {
5367 prefix_ = std::move(p); return *this;
5368 }
5369 const arg_string& prefix() const noexcept { return prefix_; }
5370
5371
5372 param_filter& required(tri t) noexcept { required_ = t; return *this; }
5373 tri required() const noexcept { return required_; }
5374
5375
5376 param_filter& blocking(tri t) noexcept { blocking_ = t; return *this; }
5377 tri blocking() const noexcept { return blocking_; }
5378
5379
5380 param_filter& repeatable(tri t) noexcept { repeatable_ = t; return *this; }
5381 tri repeatable() const noexcept { return repeatable_; }
5382
5383
5384 param_filter& has_doc(tri t) noexcept { hasDoc_ = t; return *this; }
5385 tri has_doc() const noexcept { return hasDoc_; }
5386
5387
5388
5389 bool operator() (const parameter& p) const noexcept {
5390 if(!prefix_.empty()) {
5391 if(!std::any_of(p.flags().begin(), p.flags().end(),
5392 [&](const arg_string& flag){
5393 return str::has_prefix(flag, prefix_);
5394 })) return false;
5395 }
5396 if(required() != p.required()) return false;
5397 if(blocking() != p.blocking()) return false;
5398 if(repeatable() != p.repeatable()) return false;
5399 if(has_doc() != !p.doc().empty()) return false;
5400 return true;
5401 }
5402
5403 private:
5404 arg_string prefix_;
5405 tri required_ = tri::either;
5406 tri blocking_ = tri::either;
5407 tri repeatable_ = tri::either;
5408 tri hasDoc_ = tri::yes;
5409 };
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421 class doc_formatting
5422 {
5423 public:
5424 using string = doc_string;
5425
5426
5427 #if __cplusplus >= 201402L
5428 [[deprecated]]
5429 #endif
5430 doc_formatting& start_column(int col) { return first_column(col); }
5431 #if __cplusplus >= 201402L
5432 [[deprecated]]
5433 #endif
5434 int start_column() const noexcept { return first_column(); }
5435
5436
5437 doc_formatting&
5438 first_column(int col) {
5439
5440 if(col < 0) col = 0;
5441 else if(col > last_column()) col = last_column();
5442 if(col > doc_column()) doc_column(first_column());
5443 firstCol_ = col;
5444 return *this;
5445 }
5446 int first_column() const noexcept {
5447 return firstCol_;
5448 }
5449
5450
5451 doc_formatting&
5452 doc_column(int col) {
5453
5454 if(col < 0) col = 0;
5455 else if(col < first_column()) col = first_column();
5456 else if(col > last_column()) col = last_column();
5457 docCol_ = col;
5458 return *this;
5459 }
5460 int doc_column() const noexcept {
5461 return docCol_;
5462 }
5463
5464
5465
5466
5467 doc_formatting&
5468 last_column(int col) {
5469
5470 if(col < first_column()) col = first_column();
5471 if(col < doc_column()) doc_column(col);
5472 lastCol_ = col;
5473 return *this;
5474 }
5475
5476 int last_column() const noexcept {
5477 return lastCol_;
5478 }
5479
5480
5481
5482 doc_formatting& indent_size(int indent) { indentSize_ = indent; return *this; }
5483 int indent_size() const noexcept { return indentSize_; }
5484
5485
5486
5487 doc_formatting& empty_label(const string& label) {
5488 emptyLabel_ = label;
5489 return *this;
5490 }
5491 const string& empty_label() const noexcept { return emptyLabel_; }
5492
5493
5494 doc_formatting& param_separator(const string& sep) {
5495 paramSep_ = sep;
5496 return *this;
5497 }
5498 const string& param_separator() const noexcept { return paramSep_; }
5499
5500
5501 doc_formatting& group_separator(const string& sep) {
5502 groupSep_ = sep;
5503 return *this;
5504 }
5505 const string& group_separator() const noexcept { return groupSep_; }
5506
5507
5508 doc_formatting& alternative_param_separator(const string& sep) {
5509 altParamSep_ = sep;
5510 return *this;
5511 }
5512 const string& alternative_param_separator() const noexcept { return altParamSep_; }
5513
5514
5515 doc_formatting& alternative_group_separator(const string& sep) {
5516 altGroupSep_ = sep;
5517 return *this;
5518 }
5519 const string& alternative_group_separator() const noexcept { return altGroupSep_; }
5520
5521
5522 doc_formatting& flag_separator(const string& sep) {
5523 flagSep_ = sep;
5524 return *this;
5525 }
5526 const string& flag_separator() const noexcept { return flagSep_; }
5527
5528
5529 doc_formatting&
5530 surround_labels(const string& prefix, const string& postfix) {
5531 labelPre_ = prefix;
5532 labelPst_ = postfix;
5533 return *this;
5534 }
5535 const string& label_prefix() const noexcept { return labelPre_; }
5536 const string& label_postfix() const noexcept { return labelPst_; }
5537
5538
5539 doc_formatting&
5540 surround_optional(const string& prefix, const string& postfix) {
5541 optionPre_ = prefix;
5542 optionPst_ = postfix;
5543 return *this;
5544 }
5545 const string& optional_prefix() const noexcept { return optionPre_; }
5546 const string& optional_postfix() const noexcept { return optionPst_; }
5547
5548
5549 doc_formatting&
5550 surround_repeat(const string& prefix, const string& postfix) {
5551 repeatPre_ = prefix;
5552 repeatPst_ = postfix;
5553 return *this;
5554 }
5555 const string& repeat_prefix() const noexcept { return repeatPre_; }
5556 const string& repeat_postfix() const noexcept { return repeatPst_; }
5557
5558
5559 doc_formatting&
5560 surround_alternatives(const string& prefix, const string& postfix) {
5561 alternPre_ = prefix;
5562 alternPst_ = postfix;
5563 return *this;
5564 }
5565 const string& alternatives_prefix() const noexcept { return alternPre_; }
5566 const string& alternatives_postfix() const noexcept { return alternPst_; }
5567
5568
5569 doc_formatting&
5570 surround_alternative_flags(const string& prefix, const string& postfix) {
5571 alternFlagPre_ = prefix;
5572 alternFlagPst_ = postfix;
5573 return *this;
5574 }
5575 const string& alternative_flags_prefix() const noexcept { return alternFlagPre_; }
5576 const string& alternative_flags_postfix() const noexcept { return alternFlagPst_; }
5577
5578
5579 doc_formatting&
5580 surround_group(const string& prefix, const string& postfix) {
5581 groupPre_ = prefix;
5582 groupPst_ = postfix;
5583 return *this;
5584 }
5585 const string& group_prefix() const noexcept { return groupPre_; }
5586 const string& group_postfix() const noexcept { return groupPst_; }
5587
5588
5589 doc_formatting&
5590 surround_joinable(const string& prefix, const string& postfix) {
5591 joinablePre_ = prefix;
5592 joinablePst_ = postfix;
5593 return *this;
5594 }
5595 const string& joinable_prefix() const noexcept { return joinablePre_; }
5596 const string& joinable_postfix() const noexcept { return joinablePst_; }
5597
5598
5599
5600 doc_formatting& max_flags_per_param_in_doc(int max) {
5601 maxAltInDocs_ = max > 0 ? max : 0;
5602 return *this;
5603 }
5604 int max_flags_per_param_in_doc() const noexcept { return maxAltInDocs_; }
5605
5606
5607
5608 doc_formatting& max_flags_per_param_in_usage(int max) {
5609 maxAltInUsage_ = max > 0 ? max : 0;
5610 return *this;
5611 }
5612 int max_flags_per_param_in_usage() const noexcept { return maxAltInUsage_; }
5613
5614
5615
5616 doc_formatting& line_spacing(int lines) {
5617 lineSpc_ = lines > 0 ? lines : 0;
5618 return *this;
5619 }
5620 int line_spacing() const noexcept { return lineSpc_; }
5621
5622
5623
5624
5625 doc_formatting& paragraph_spacing(int lines) {
5626 paragraphSpc_ = lines > 0 ? lines : 0;
5627 return *this;
5628 }
5629 int paragraph_spacing() const noexcept { return paragraphSpc_; }
5630
5631
5632
5633 doc_formatting& merge_alternative_flags_with_common_prefix(bool yes = true) {
5634 mergeAltCommonPfx_ = yes;
5635 return *this;
5636 }
5637 bool merge_alternative_flags_with_common_prefix() const noexcept {
5638 return mergeAltCommonPfx_;
5639 }
5640
5641
5642
5643 doc_formatting& merge_joinable_with_common_prefix(bool yes = true) {
5644 mergeJoinableCommonPfx_ = yes;
5645 return *this;
5646 }
5647 bool merge_joinable_with_common_prefix() const noexcept {
5648 return mergeJoinableCommonPfx_;
5649 }
5650
5651
5652
5653
5654 doc_formatting& split_alternatives(bool yes = true) {
5655 splitTopAlt_ = yes;
5656 return *this;
5657 }
5658 bool split_alternatives() const noexcept {
5659 return splitTopAlt_;
5660 }
5661
5662
5663
5664 doc_formatting& alternatives_min_split_size(int size) {
5665 groupSplitSize_ = size > 0 ? size : 0;
5666 return *this;
5667 }
5668 int alternatives_min_split_size() const noexcept { return groupSplitSize_; }
5669
5670
5671
5672 doc_formatting& ignore_newline_chars(bool yes = true) {
5673 ignoreNewlines_ = yes;
5674 return *this;
5675 }
5676 bool ignore_newline_chars() const noexcept {
5677 return ignoreNewlines_;
5678 }
5679
5680 private:
5681 string paramSep_ = string(" ");
5682 string groupSep_ = string(" ");
5683 string altParamSep_ = string("|");
5684 string altGroupSep_ = string(" | ");
5685 string flagSep_ = string(", ");
5686 string labelPre_ = string("<");
5687 string labelPst_ = string(">");
5688 string optionPre_ = string("[");
5689 string optionPst_ = string("]");
5690 string repeatPre_ = string("");
5691 string repeatPst_ = string("...");
5692 string groupPre_ = string("(");
5693 string groupPst_ = string(")");
5694 string alternPre_ = string("(");
5695 string alternPst_ = string(")");
5696 string alternFlagPre_ = string("");
5697 string alternFlagPst_ = string("");
5698 string joinablePre_ = string("(");
5699 string joinablePst_ = string(")");
5700 string emptyLabel_ = string("");
5701 int firstCol_ = 8;
5702 int docCol_ = 20;
5703 int lastCol_ = 100;
5704 int indentSize_ = 4;
5705 int maxAltInUsage_ = 1;
5706 int maxAltInDocs_ = 32;
5707 int lineSpc_ = 0;
5708 int paragraphSpc_ = 1;
5709 int groupSplitSize_ = 3;
5710 bool splitTopAlt_ = true;
5711 bool mergeAltCommonPfx_ = false;
5712 bool mergeJoinableCommonPfx_ = true;
5713 bool ignoreNewlines_ = false;
5714 };
5715
5716
5717
5718 namespace detail {
5719
5720
5721
5722
5723
5724
5725
5726 template<class OStream = std::ostream, class StringT = doc_string>
5727 class formatting_ostream
5728 {
5729 public:
5730 using string_type = StringT;
5731 using size_type = typename string_type::size_type;
5732 using char_type = typename string_type::value_type;
5733
5734 formatting_ostream(OStream& os):
5735 os_(os),
5736 curCol_{0}, firstCol_{0}, lastCol_{100},
5737 hangingIndent_{0}, paragraphSpacing_{0}, paragraphSpacingThreshold_{2},
5738 curBlankLines_{0}, curParagraphLines_{1},
5739 totalNonBlankLines_{0},
5740 ignoreInputNls_{false}
5741 {}
5742
5743
5744
5745 const OStream& base() const noexcept { return os_; }
5746 OStream& base() noexcept { return os_; }
5747
5748 bool good() const { return os_.good(); }
5749
5750
5751
5752
5753 formatting_ostream& first_column(int c) {
5754 firstCol_ = c < 0 ? 0 : c;
5755 return *this;
5756 }
5757 int first_column() const noexcept { return firstCol_; }
5758
5759
5760 formatting_ostream& last_column(int c) {
5761 lastCol_ = c < 0 ? 0 : c;
5762 return *this;
5763 }
5764
5765 int last_column() const noexcept { return lastCol_; }
5766
5767 int text_width() const noexcept {
5768 return lastCol_ - firstCol_;
5769 }
5770
5771
5772
5773 formatting_ostream& hanging_indent(int amount) {
5774 hangingIndent_ = amount;
5775 return *this;
5776 }
5777 int hanging_indent() const noexcept {
5778 return hangingIndent_;
5779 }
5780
5781
5782 formatting_ostream& paragraph_spacing(int lines) {
5783 paragraphSpacing_ = lines;
5784 return *this;
5785 }
5786 int paragraph_spacing() const noexcept {
5787 return paragraphSpacing_;
5788 }
5789
5790
5791
5792 formatting_ostream& min_paragraph_lines_for_spacing(int lines) {
5793 paragraphSpacingThreshold_ = lines;
5794 return *this;
5795 }
5796 int min_paragraph_lines_for_spacing() const noexcept {
5797 return paragraphSpacingThreshold_;
5798 }
5799
5800
5801 formatting_ostream& ignore_newline_chars(bool yes) {
5802 ignoreInputNls_ = yes;
5803 return *this;
5804 }
5805
5806 bool ignore_newline_chars() const noexcept {
5807 return ignoreInputNls_;
5808 }
5809
5810
5811
5812
5813 void write_spaces(int n) {
5814 if(n < 1) return;
5815 os_ << string_type(size_type(n), ' ');
5816 curCol_ += n;
5817 }
5818
5819
5820 void wrap_soft(int times = 1) {
5821 if(times < 1) return;
5822 if(times > 1) {
5823 os_ << string_type(size_type(times), '\n');
5824 } else {
5825 os_ << '\n';
5826 }
5827 curCol_ = 0;
5828 ++curParagraphLines_;
5829 }
5830
5831
5832 void wrap_hard(int times = 1) {
5833 if(times < 1) return;
5834
5835 if(paragraph_spacing() > 0 &&
5836 paragraph_lines() >= min_paragraph_lines_for_spacing())
5837 {
5838 times = paragraph_spacing() + 1;
5839 }
5840
5841 if(times > 1) {
5842 os_ << string_type(size_type(times), '\n');
5843 curBlankLines_ += times - 1;
5844 } else {
5845 os_ << '\n';
5846 }
5847 if(at_begin_of_line()) {
5848 ++curBlankLines_;
5849 }
5850 curCol_ = 0;
5851 curParagraphLines_ = 1;
5852 }
5853
5854
5855
5856 bool at_begin_of_line() const noexcept {
5857 return curCol_ <= current_line_begin();
5858 }
5859 int current_line_begin() const noexcept {
5860 return in_hanging_part_of_paragraph()
5861 ? firstCol_ + hangingIndent_
5862 : firstCol_;
5863 }
5864
5865 int current_column() const noexcept {
5866 return curCol_;
5867 }
5868
5869 int total_non_blank_lines() const noexcept {
5870 return totalNonBlankLines_;
5871 }
5872 int paragraph_lines() const noexcept {
5873 return curParagraphLines_;
5874 }
5875 int blank_lines_before_paragraph() const noexcept {
5876 return curBlankLines_;
5877 }
5878
5879
5880
5881 template<class T>
5882 friend formatting_ostream&
5883 operator << (formatting_ostream& os, const T& x) {
5884 os.write(x);
5885 return os;
5886 }
5887
5888 void flush() {
5889 os_.flush();
5890 }
5891
5892
5893 private:
5894 bool in_hanging_part_of_paragraph() const noexcept {
5895 return hanging_indent() > 0 && paragraph_lines() > 1;
5896 }
5897 bool current_line_empty() const noexcept {
5898 return curCol_ < 1;
5899 }
5900 bool left_of_text_area() const noexcept {
5901 return curCol_ < current_line_begin();
5902 }
5903 bool right_of_text_area() const noexcept {
5904 return curCol_ > lastCol_;
5905 }
5906 int columns_left_in_line() const noexcept {
5907 return lastCol_ - std::max(current_line_begin(), curCol_);
5908 }
5909
5910 void fix_indent() {
5911 if(left_of_text_area()) {
5912 const auto fst = current_line_begin();
5913 write_spaces(fst - curCol_);
5914 curCol_ = fst;
5915 }
5916 }
5917
5918 template<class Iter>
5919 bool only_whitespace(Iter first, Iter last) const {
5920 return last == std::find_if_not(first, last,
5921 [](char_type c) { return std::isspace(c); });
5922 }
5923
5924
5925 template<class T>
5926 void write(const T& x) {
5927 std::ostringstream ss;
5928 ss << x;
5929 write(std::move(ss).str());
5930 }
5931
5932
5933 void write(const std::ostringstream& s) {
5934 write(s.str());
5935 }
5936
5937
5938 void write(const string_type& s) {
5939 write(s.begin(), s.end());
5940 }
5941
5942
5943 template<class Iter>
5944 void write(Iter first, Iter last)
5945 {
5946 if(first == last) return;
5947 if(*first == '\n') {
5948 if(!ignore_newline_chars()) wrap_hard();
5949 ++first;
5950 if(first == last) return;
5951 }
5952 auto i = std::find(first, last, '\n');
5953 if(i != last) {
5954 if(ignore_newline_chars()) ++i;
5955 if(i != last) {
5956 write_line(first, i);
5957 write(i, last);
5958 }
5959 }
5960 else {
5961 write_line(first, last);
5962 }
5963 }
5964
5965
5966 template<class Iter>
5967 void write_line(Iter first, Iter last)
5968 {
5969 if(first == last) return;
5970 if(only_whitespace(first, last)) return;
5971
5972 if(right_of_text_area()) wrap_soft();
5973
5974 if(at_begin_of_line()) {
5975
5976 first = std::find_if(first, last,
5977 [](char_type c) { return !std::isspace(c); });
5978 if(first == last) return;
5979 }
5980
5981 const auto n = int(std::distance(first,last));
5982 const auto m = columns_left_in_line();
5983
5984 if(n > m) {
5985
5986 auto breakat = first + m;
5987 while(breakat > first && !std::isspace(*breakat)) --breakat;
5988
5989 if(!std::isspace(*breakat) && breakat == first) {
5990 breakat = std::find_if(first+m, last,
5991 [](char_type c) { return std::isspace(c); });
5992 }
5993 if(breakat > first) {
5994 if(curCol_ < 1) ++totalNonBlankLines_;
5995 fix_indent();
5996 std::copy(first, breakat, std::ostream_iterator<char_type>(os_));
5997 curBlankLines_ = 0;
5998 }
5999 if(breakat < last) {
6000 wrap_soft();
6001 write_line(breakat, last);
6002 }
6003 }
6004 else {
6005 if(curCol_ < 1) ++totalNonBlankLines_;
6006 fix_indent();
6007 std::copy(first, last, std::ostream_iterator<char_type>(os_));
6008 curCol_ += n;
6009 curBlankLines_ = 0;
6010 }
6011 }
6012
6013
6014 void write(char_type c)
6015 {
6016 if(c == '\n') {
6017 if(!ignore_newline_chars()) wrap_hard();
6018 }
6019 else {
6020 if(at_begin_of_line()) ++totalNonBlankLines_;
6021 fix_indent();
6022 os_ << c;
6023 ++curCol_;
6024 }
6025 }
6026
6027 OStream& os_;
6028 int curCol_;
6029 int firstCol_;
6030 int lastCol_;
6031 int hangingIndent_;
6032 int paragraphSpacing_;
6033 int paragraphSpacingThreshold_;
6034 int curBlankLines_;
6035 int curParagraphLines_;
6036 int totalNonBlankLines_;
6037 bool ignoreInputNls_;
6038 };
6039
6040
6041 }
6042
6043
6044
6045
6046
6047
6048
6049
6050
6051
6052
6053 class usage_lines
6054 {
6055 public:
6056 using string = doc_string;
6057
6058 usage_lines(const group& cli, string prefix = "",
6059 const doc_formatting& fmt = doc_formatting{})
6060 :
6061 cli_(cli), fmt_(fmt), prefix_(std::move(prefix))
6062 {
6063 if(!prefix_.empty()) prefix_ += ' ';
6064 }
6065
6066 usage_lines(const group& cli, const doc_formatting& fmt):
6067 usage_lines(cli, "", fmt)
6068 {}
6069
6070 usage_lines& ommit_outermost_group_surrounders(bool yes) {
6071 ommitOutermostSurrounders_ = yes;
6072 return *this;
6073 }
6074 bool ommit_outermost_group_surrounders() const {
6075 return ommitOutermostSurrounders_;
6076 }
6077
6078 template<class OStream>
6079 inline friend OStream& operator << (OStream& os, const usage_lines& p) {
6080 p.write(os);
6081 return os;
6082 }
6083
6084 string str() const {
6085 std::ostringstream os; os << *this; return os.str();
6086 }
6087
6088
6089 private:
6090 using stream_t = detail::formatting_ostream<>;
6091 const group& cli_;
6092 doc_formatting fmt_;
6093 string prefix_;
6094 bool ommitOutermostSurrounders_ = false;
6095
6096
6097
6098 struct context {
6099 group::depth_first_traverser pos;
6100 std::stack<string> separators;
6101 std::stack<string> postfixes;
6102 int level = 0;
6103 const group* outermost = nullptr;
6104 bool linestart = false;
6105 bool useOutermost = true;
6106 int line = 0;
6107
6108 bool is_singleton() const noexcept {
6109 return linestart && pos.is_last_in_path();
6110 }
6111 bool is_alternative() const noexcept {
6112 return pos.parent().exclusive();
6113 }
6114 };
6115
6116
6117
6118
6119
6120
6121
6122 template<class OStream>
6123 void write(OStream& os) const
6124 {
6125 detail::formatting_ostream<OStream> fos(os);
6126 fos.first_column(fmt_.first_column());
6127 fos.last_column(fmt_.last_column());
6128
6129 auto hindent = int(prefix_.size());
6130 if(fos.first_column() + hindent >= int(0.4 * fos.text_width())) {
6131 hindent = fmt_.indent_size();
6132 }
6133 fos.hanging_indent(hindent);
6134
6135 fos.paragraph_spacing(fmt_.paragraph_spacing());
6136 fos.min_paragraph_lines_for_spacing(2);
6137 fos.ignore_newline_chars(fmt_.ignore_newline_chars());
6138
6139 context cur;
6140 cur.pos = cli_.begin_dfs();
6141 cur.linestart = true;
6142 cur.level = cur.pos.level();
6143 cur.outermost = &cli_;
6144
6145 write(fos, cur, prefix_);
6146 }
6147
6148
6149
6150
6151
6152
6153
6154
6155
6156 template<class OStream>
6157 void write(OStream& os, context cur, string prefix) const
6158 {
6159 if(!cur.pos) return;
6160
6161 std::ostringstream buf;
6162 if(cur.linestart) buf << prefix;
6163 const auto initPos = buf.tellp();
6164
6165 cur.level = cur.pos.level();
6166
6167 if(cur.useOutermost) {
6168
6169
6170 start_group(buf, cur.pos.parent(), cur);
6171 if(!cur.pos) {
6172 os << buf.str();
6173 return;
6174 }
6175 }
6176 else {
6177
6178 cur.pos.skip_siblings();
6179 }
6180 check_end_group(buf, cur);
6181
6182 do {
6183 if(buf.tellp() > initPos) cur.linestart = false;
6184 if(!cur.linestart && !cur.pos.is_first_in_parent()) {
6185 buf << cur.separators.top();
6186 }
6187 if(cur.pos->is_group()) {
6188 start_group(buf, cur.pos->as_group(), cur);
6189 if(!cur.pos) {
6190 os << buf.str();
6191 return;
6192 }
6193 }
6194 else {
6195 buf << param_label(cur.pos->as_param(), cur);
6196 ++cur.pos;
6197 }
6198 check_end_group(buf, cur);
6199 } while(cur.pos);
6200
6201 os << buf.str();
6202 }
6203
6204
6205
6206
6207
6208
6209
6210
6211 void start_group(std::ostringstream& os,
6212 const group& group, context& cur) const
6213 {
6214
6215
6216 const bool alreadyInside = &(cur.pos.parent()) == &group;
6217
6218 auto lbl = joined_label(group, cur);
6219 if(!lbl.empty()) {
6220 os << lbl;
6221 cur.linestart = false;
6222
6223 if(alreadyInside) {
6224 cur.pos.next_after_siblings();
6225 } else {
6226 cur.pos.next_sibling();
6227 }
6228 }
6229 else {
6230 const bool splitAlternatives = group.exclusive() &&
6231 fmt_.split_alternatives() &&
6232 std::any_of(group.begin(), group.end(),
6233 [this](const pattern& p) {
6234 return int(p.param_count()) >= fmt_.alternatives_min_split_size();
6235 });
6236
6237 if(splitAlternatives) {
6238 cur.postfixes.push("");
6239 cur.separators.push("");
6240
6241
6242 if(!alreadyInside) ++cur.pos;
6243 cur.linestart = true;
6244 cur.useOutermost = false;
6245 auto pfx = os.str();
6246 os.str("");
6247
6248 for(std::size_t i = 0; i < group.size(); ++i) {
6249 std::stringstream buf;
6250 cur.outermost = cur.pos->is_group() ? &(cur.pos->as_group()) : nullptr;
6251 write(buf, cur, pfx);
6252 if(buf.tellp() > int(pfx.size())) {
6253 os << buf.str();
6254 if(i < group.size()-1) {
6255 if(cur.line > 0) {
6256 os << string(fmt_.line_spacing(), '\n');
6257 }
6258 ++cur.line;
6259 os << '\n';
6260 }
6261 }
6262 cur.pos.next_sibling();
6263 }
6264 cur.pos.invalidate();
6265 return;
6266 }
6267 else {
6268
6269 auto surround = group_surrounders(group, cur);
6270 os << surround.first;
6271 cur.postfixes.push(std::move(surround.second));
6272 cur.separators.push(group_separator(group, fmt_));
6273
6274 if(!alreadyInside) ++cur.pos;
6275 }
6276 }
6277 cur.level = cur.pos.level();
6278 }
6279
6280
6281
6282
6283
6284 void check_end_group(std::ostringstream& os, context& cur) const
6285 {
6286 for(; cur.level > cur.pos.level(); --cur.level) {
6287 os << cur.postfixes.top();
6288 cur.postfixes.pop();
6289 cur.separators.pop();
6290 }
6291 cur.level = cur.pos.level();
6292 }
6293
6294
6295
6296
6297
6298
6299
6300 string param_label(const parameter& p, const context& cur) const
6301 {
6302 const auto& parent = cur.pos.parent();
6303
6304 const bool startsOptionalSequence =
6305 parent.size() > 1 && p.blocking() && cur.pos.is_first_in_parent();
6306
6307 const bool outermost =
6308 ommitOutermostSurrounders_ && cur.outermost == &parent;
6309
6310 const bool showopt = !cur.is_alternative() && !p.required()
6311 && !startsOptionalSequence && !outermost;
6312
6313 const bool showrep = p.repeatable() && !outermost;
6314
6315 string lbl;
6316
6317 if(showrep) lbl += fmt_.repeat_prefix();
6318 if(showopt) lbl += fmt_.optional_prefix();
6319
6320 const auto& flags = p.flags();
6321 if(!flags.empty()) {
6322 const int n = std::min(fmt_.max_flags_per_param_in_usage(),
6323 int(flags.size()));
6324
6325 const bool surrAlt = n > 1 && !showopt && !cur.is_singleton();
6326
6327 if(surrAlt) lbl += fmt_.alternative_flags_prefix();
6328 bool sep = false;
6329 for(int i = 0; i < n; ++i) {
6330 if(sep) {
6331 if(cur.is_singleton())
6332 lbl += fmt_.alternative_group_separator();
6333 else
6334 lbl += fmt_.flag_separator();
6335 }
6336 lbl += flags[i];
6337 sep = true;
6338 }
6339 if(surrAlt) lbl += fmt_.alternative_flags_postfix();
6340 }
6341 else {
6342 if(!p.label().empty()) {
6343 lbl += fmt_.label_prefix()
6344 + p.label()
6345 + fmt_.label_postfix();
6346 } else if(!fmt_.empty_label().empty()) {
6347 lbl += fmt_.label_prefix()
6348 + fmt_.empty_label()
6349 + fmt_.label_postfix();
6350 } else {
6351 return "";
6352 }
6353 }
6354
6355 if(showopt) lbl += fmt_.optional_postfix();
6356 if(showrep) lbl += fmt_.repeat_postfix();
6357
6358 return lbl;
6359 }
6360
6361
6362
6363
6364
6365
6366
6367 string joined_label(const group& g, const context& cur) const
6368 {
6369 if(!fmt_.merge_alternative_flags_with_common_prefix() &&
6370 !fmt_.merge_joinable_with_common_prefix()) return "";
6371
6372 const bool flagsonly = std::all_of(g.begin(), g.end(),
6373 [](const pattern& p){
6374 return p.is_param() && !p.as_param().flags().empty();
6375 });
6376
6377 if(!flagsonly) return "";
6378
6379 const bool showOpt = g.all_optional() &&
6380 !(ommitOutermostSurrounders_ && cur.outermost == &g);
6381
6382 auto pfx = g.common_flag_prefix();
6383 if(pfx.empty()) return "";
6384
6385 const auto n = pfx.size();
6386 if(g.exclusive() &&
6387 fmt_.merge_alternative_flags_with_common_prefix())
6388 {
6389 string lbl;
6390 if(showOpt) lbl += fmt_.optional_prefix();
6391 lbl += pfx + fmt_.alternatives_prefix();
6392 bool first = true;
6393 for(const auto& p : g) {
6394 if(p.is_param()) {
6395 if(first)
6396 first = false;
6397 else
6398 lbl += fmt_.alternative_param_separator();
6399 lbl += p.as_param().flags().front().substr(n);
6400 }
6401 }
6402 lbl += fmt_.alternatives_postfix();
6403 if(showOpt) lbl += fmt_.optional_postfix();
6404 return lbl;
6405 }
6406
6407 else if(g.joinable() &&
6408 fmt_.merge_joinable_with_common_prefix())
6409 {
6410 const bool allSingleChar = std::all_of(g.begin(), g.end(),
6411 [&](const pattern& p){
6412 return p.is_param() &&
6413 p.as_param().flags().front().substr(n).size() == 1;
6414 });
6415
6416 if(allSingleChar) {
6417 string lbl;
6418 if(showOpt) lbl += fmt_.optional_prefix();
6419 lbl += pfx;
6420 for(const auto& p : g) {
6421 if(p.is_param())
6422 lbl += p.as_param().flags().front().substr(n);
6423 }
6424 if(showOpt) lbl += fmt_.optional_postfix();
6425 return lbl;
6426 }
6427 }
6428
6429 return "";
6430 }
6431
6432
6433
6434
6435
6436
6437
6438 std::pair<string,string>
6439 group_surrounders(const group& group, const context& cur) const
6440 {
6441 string prefix;
6442 string postfix;
6443
6444 const bool isOutermost = &group == cur.outermost;
6445 if(isOutermost && ommitOutermostSurrounders_)
6446 return {string{}, string{}};
6447
6448 if(group.exclusive()) {
6449 if(group.all_optional()) {
6450 prefix = fmt_.optional_prefix();
6451 postfix = fmt_.optional_postfix();
6452 if(group.all_flagless()) {
6453 prefix += fmt_.label_prefix();
6454 postfix = fmt_.label_prefix() + postfix;
6455 }
6456 } else if(group.all_flagless()) {
6457 prefix = fmt_.label_prefix();
6458 postfix = fmt_.label_postfix();
6459 } else if(!cur.is_singleton() || !isOutermost) {
6460 prefix = fmt_.alternatives_prefix();
6461 postfix = fmt_.alternatives_postfix();
6462 }
6463 }
6464 else if(group.size() > 1 &&
6465 group.front().blocking() && !group.front().required())
6466 {
6467 prefix = fmt_.optional_prefix();
6468 postfix = fmt_.optional_postfix();
6469 }
6470 else if(group.size() > 1 && cur.is_alternative() &&
6471 &group != cur.outermost)
6472 {
6473 prefix = fmt_.group_prefix();
6474 postfix = fmt_.group_postfix();
6475 }
6476 else if(!group.exclusive() &&
6477 group.joinable() && !cur.linestart)
6478 {
6479 prefix = fmt_.joinable_prefix();
6480 postfix = fmt_.joinable_postfix();
6481 }
6482
6483 if(group.repeatable()) {
6484 if(prefix.empty()) prefix = fmt_.group_prefix();
6485 prefix = fmt_.repeat_prefix() + prefix;
6486 if(postfix.empty()) postfix = fmt_.group_postfix();
6487 postfix += fmt_.repeat_postfix();
6488 }
6489
6490 return {std::move(prefix), std::move(postfix)};
6491 }
6492
6493
6494
6495
6496
6497
6498
6499 static string
6500 group_separator(const group& group, const doc_formatting& fmt)
6501 {
6502 const bool only1ParamPerMember = std::all_of(group.begin(), group.end(),
6503 [](const pattern& p) { return p.param_count() < 2; });
6504
6505 if(only1ParamPerMember) {
6506 if(group.exclusive()) {
6507 return fmt.alternative_param_separator();
6508 } else {
6509 return fmt.param_separator();
6510 }
6511 }
6512 else {
6513 if(group.exclusive()) {
6514 return fmt.alternative_group_separator();
6515 } else {
6516 return fmt.group_separator();
6517 }
6518 }
6519 }
6520 };
6521
6522
6523
6524
6525
6526
6527
6528
6529
6530
6531
6532 class documentation
6533 {
6534 public:
6535 using string = doc_string;
6536 using filter_function = std::function<bool(const parameter&)>;
6537
6538 documentation(const group& cli,
6539 const doc_formatting& fmt = doc_formatting{},
6540 filter_function filter = param_filter{})
6541 :
6542 cli_(cli), fmt_{fmt}, usgFmt_{fmt}, filter_{std::move(filter)}
6543 {
6544
6545
6546 usgFmt_.max_flags_per_param_in_usage(
6547 usgFmt_.max_flags_per_param_in_doc());
6548 }
6549
6550 documentation(const group& cli, filter_function filter) :
6551 documentation{cli, doc_formatting{}, std::move(filter)}
6552 {}
6553
6554 documentation(const group& cli, const param_filter& filter) :
6555 documentation{cli, doc_formatting{},
6556 [filter](const parameter& p) { return filter(p); }}
6557 {}
6558
6559 template<class OStream>
6560 inline friend OStream& operator << (OStream& os, const documentation& p) {
6561 p.write(os);
6562 return os;
6563 }
6564
6565 string str() const {
6566 std::ostringstream os;
6567 write(os);
6568 return os.str();
6569 }
6570
6571
6572 private:
6573 using dfs_traverser = group::depth_first_traverser;
6574
6575 const group& cli_;
6576 doc_formatting fmt_;
6577 doc_formatting usgFmt_;
6578 filter_function filter_;
6579 enum class paragraph { param, group };
6580
6581
6582
6583
6584
6585
6586
6587 template<class OStream>
6588 void write(OStream& os) const {
6589 detail::formatting_ostream<OStream> fos(os);
6590 fos.first_column(fmt_.first_column());
6591 fos.last_column(fmt_.last_column());
6592 fos.hanging_indent(0);
6593 fos.paragraph_spacing(0);
6594 fos.ignore_newline_chars(fmt_.ignore_newline_chars());
6595 print_doc(fos, cli_);
6596 }
6597
6598
6599
6600
6601
6602
6603
6604 template<class OStream>
6605 void print_doc(detail::formatting_ostream<OStream>& os,
6606 const group& cli, int indentLvl = 0) const
6607 {
6608 if(cli.empty()) return;
6609
6610
6611 if(cli.doc().empty()) {
6612 for(const auto& p : cli) {
6613 print_doc(os, p, indentLvl);
6614 }
6615 }
6616 else {
6617 bool anyDocInside = std::any_of(cli.begin(), cli.end(),
6618 [](const pattern& p){ return !p.doc().empty(); });
6619
6620 if(anyDocInside) {
6621 handle_spacing(os, paragraph::group, indentLvl);
6622 os << cli.doc();
6623 for(const auto& p : cli) {
6624 print_doc(os, p, indentLvl + 1);
6625 }
6626 }
6627 else {
6628 auto lbl = usage_lines(cli, usgFmt_)
6629 .ommit_outermost_group_surrounders(true).str();
6630
6631 str::trim(lbl);
6632 handle_spacing(os, paragraph::param, indentLvl);
6633 print_entry(os, lbl, cli.doc());
6634 }
6635 }
6636 }
6637
6638
6639
6640
6641
6642
6643
6644 template<class OStream>
6645 void print_doc(detail::formatting_ostream<OStream>& os,
6646 const pattern& ptrn, int indentLvl) const
6647 {
6648 if(ptrn.is_group()) {
6649 print_doc(os, ptrn.as_group(), indentLvl);
6650 }
6651 else {
6652 const auto& p = ptrn.as_param();
6653 if(!filter_(p)) return;
6654
6655 handle_spacing(os, paragraph::param, indentLvl);
6656 print_entry(os, param_label(p, fmt_), p.doc());
6657 }
6658 }
6659
6660
6661
6662
6663
6664
6665 template<class OStream>
6666 void handle_spacing(detail::formatting_ostream<OStream>& os,
6667 paragraph p, int indentLvl) const
6668 {
6669 const auto oldIndent = os.first_column();
6670 const auto indent = fmt_.first_column() + indentLvl * fmt_.indent_size();
6671
6672 if(os.total_non_blank_lines() < 1) {
6673 os.first_column(indent);
6674 return;
6675 }
6676
6677 if(os.paragraph_lines() > 1 || indent < oldIndent) {
6678 os.wrap_hard(fmt_.paragraph_spacing() + 1);
6679 } else {
6680 os.wrap_hard();
6681 }
6682
6683 if(p == paragraph::group) {
6684 if(os.blank_lines_before_paragraph() < fmt_.paragraph_spacing()) {
6685 os.wrap_hard(fmt_.paragraph_spacing() - os.blank_lines_before_paragraph());
6686 }
6687 }
6688 else if(os.blank_lines_before_paragraph() < fmt_.line_spacing()) {
6689 os.wrap_hard(fmt_.line_spacing() - os.blank_lines_before_paragraph());
6690 }
6691 os.first_column(indent);
6692 }
6693
6694
6695
6696
6697
6698
6699 template<class OStream>
6700 void print_entry(detail::formatting_ostream<OStream>& os,
6701 const string& label, const string& docstr) const
6702 {
6703 if(label.empty()) return;
6704
6705 os << label;
6706
6707 if(!docstr.empty()) {
6708 if(os.current_column() >= fmt_.doc_column()) os.wrap_soft();
6709 const auto oldcol = os.first_column();
6710 os.first_column(fmt_.doc_column());
6711 os << docstr;
6712 os.first_column(oldcol);
6713 }
6714 }
6715
6716
6717
6718
6719
6720
6721
6722 static doc_string
6723 param_label(const parameter& param, const doc_formatting& fmt)
6724 {
6725 doc_string lbl;
6726
6727 if(param.repeatable()) lbl += fmt.repeat_prefix();
6728
6729 const auto& flags = param.flags();
6730 if(!flags.empty()) {
6731 lbl += flags[0];
6732 const int n = std::min(fmt.max_flags_per_param_in_doc(),
6733 int(flags.size()));
6734 for(int i = 1; i < n; ++i) {
6735 lbl += fmt.flag_separator() + flags[i];
6736 }
6737 }
6738 else if(!param.label().empty() || !fmt.empty_label().empty()) {
6739 lbl += fmt.label_prefix();
6740 if(!param.label().empty()) {
6741 lbl += param.label();
6742 } else {
6743 lbl += fmt.empty_label();
6744 }
6745 lbl += fmt.label_postfix();
6746 }
6747
6748 if(param.repeatable()) lbl += fmt.repeat_postfix();
6749
6750 return lbl;
6751 }
6752
6753 };
6754
6755
6756
6757
6758
6759
6760
6761
6762
6763 class man_page
6764 {
6765 public:
6766
6767 using string = doc_string;
6768
6769
6770
6771 class section {
6772 public:
6773 using string = doc_string;
6774
6775 section(string stitle, string scontent):
6776 title_{std::move(stitle)}, content_{std::move(scontent)}
6777 {}
6778
6779 const string& title() const noexcept { return title_; }
6780 const string& content() const noexcept { return content_; }
6781
6782 private:
6783 string title_;
6784 string content_;
6785 };
6786
6787 private:
6788 using section_store = std::vector<section>;
6789
6790 public:
6791
6792 using value_type = section;
6793 using const_iterator = section_store::const_iterator;
6794 using size_type = section_store::size_type;
6795
6796
6797
6798 man_page&
6799 append_section(string title, string content)
6800 {
6801 sections_.emplace_back(std::move(title), std::move(content));
6802 return *this;
6803 }
6804
6805 man_page&
6806 prepend_section(string title, string content)
6807 {
6808 sections_.emplace(sections_.begin(),
6809 std::move(title), std::move(content));
6810 return *this;
6811 }
6812
6813
6814
6815 const section& operator [] (size_type index) const noexcept {
6816 return sections_[index];
6817 }
6818
6819
6820 size_type size() const noexcept { return sections_.size(); }
6821
6822 bool empty() const noexcept { return sections_.empty(); }
6823
6824
6825
6826 const_iterator begin() const noexcept { return sections_.begin(); }
6827 const_iterator end() const noexcept { return sections_.end(); }
6828
6829
6830
6831 man_page& program_name(const string& n) {
6832 progName_ = n;
6833 return *this;
6834 }
6835 man_page& program_name(string&& n) {
6836 progName_ = std::move(n);
6837 return *this;
6838 }
6839 const string& program_name() const noexcept {
6840 return progName_;
6841 }
6842
6843
6844
6845 man_page& section_row_spacing(int rows) {
6846 sectionSpc_ = rows > 0 ? rows : 0;
6847 return *this;
6848 }
6849 int section_row_spacing() const noexcept { return sectionSpc_; }
6850
6851
6852 private:
6853 int sectionSpc_ = 1;
6854 section_store sections_;
6855 string progName_;
6856 };
6857
6858
6859
6860
6861
6862
6863
6864
6865
6866 inline man_page
6867 make_man_page(const group& cli,
6868 doc_string progname = "",
6869 const doc_formatting& fmt = doc_formatting{})
6870 {
6871 man_page man;
6872 man.append_section("SYNOPSIS", usage_lines(cli,progname,fmt).str());
6873 man.append_section("OPTIONS", documentation(cli,fmt).str());
6874 return man;
6875 }
6876
6877
6878
6879
6880
6881
6882
6883
6884 template<class OStream>
6885 OStream&
6886 operator << (OStream& os, const man_page& man)
6887 {
6888 bool first = true;
6889 const auto secSpc = doc_string(man.section_row_spacing() + 1, '\n');
6890 for(const auto& section : man) {
6891 if(!section.content().empty()) {
6892 if(first) first = false; else os << secSpc;
6893 if(!section.title().empty()) os << section.title() << '\n';
6894 os << section.content();
6895 }
6896 }
6897 os << '\n';
6898 return os;
6899 }
6900
6901
6902
6903
6904
6905
6906
6907
6908
6909
6910 namespace debug {
6911
6912
6913
6914
6915
6916
6917
6918 inline doc_string doc_label(const parameter& p)
6919 {
6920 if(!p.flags().empty()) return p.flags().front();
6921 if(!p.label().empty()) return p.label();
6922 return doc_string{"<?>"};
6923 }
6924
6925 inline doc_string doc_label(const group&)
6926 {
6927 return "<group>";
6928 }
6929
6930 inline doc_string doc_label(const pattern& p)
6931 {
6932 return p.is_group() ? doc_label(p.as_group()) : doc_label(p.as_param());
6933 }
6934
6935
6936
6937
6938
6939
6940
6941 template<class OStream>
6942 void print(OStream& os, const parsing_result& result)
6943 {
6944 for(const auto& m : result) {
6945 os << "#" << m.index() << " " << m.arg() << " -> ";
6946 auto p = m.param();
6947 if(p) {
6948 os << doc_label(*p) << " \t";
6949 if(m.repeat() > 0) {
6950 os << (m.bad_repeat() ? "[bad repeat " : "[repeat ")
6951 << m.repeat() << "]";
6952 }
6953 if(m.blocked()) os << " [blocked]";
6954 if(m.conflict()) os << " [conflict]";
6955 os << '\n';
6956 }
6957 else {
6958 os << " [unmapped]\n";
6959 }
6960 }
6961
6962 for(const auto& m : result.missing()) {
6963 auto p = m.param();
6964 if(p) {
6965 os << doc_label(*p) << " \t";
6966 os << " [missing after " << m.after_index() << "]\n";
6967 }
6968 }
6969 }
6970
6971
6972
6973
6974
6975
6976
6977 template<class OStream>
6978 void print(OStream& os, const parameter& p)
6979 {
6980 if(p.greedy()) os << '!';
6981 if(p.blocking()) os << '~';
6982 if(!p.required()) os << '[';
6983 os << doc_label(p);
6984 if(p.repeatable()) os << "...";
6985 if(!p.required()) os << "]";
6986 }
6987
6988
6989
6990 template<class OStream>
6991 void print(OStream& os, const group& g, int level = 0);
6992
6993
6994
6995
6996
6997
6998
6999 template<class OStream>
7000 void print(OStream& os, const pattern& param, int level = 0)
7001 {
7002 if(param.is_group()) {
7003 print(os, param.as_group(), level);
7004 }
7005 else {
7006 os << doc_string(4*level, ' ');
7007 print(os, param.as_param());
7008 }
7009 }
7010
7011
7012
7013
7014
7015
7016
7017 template<class OStream>
7018 void print(OStream& os, const group& g, int level)
7019 {
7020 auto indent = doc_string(4*level, ' ');
7021 os << indent;
7022 if(g.blocking()) os << '~';
7023 if(g.joinable()) os << 'J';
7024 os << (g.exclusive() ? "(|\n" : "(\n");
7025 for(const auto& p : g) {
7026 print(os, p, level+1);
7027 }
7028 os << '\n' << indent << (g.exclusive() ? "|)" : ")");
7029 if(g.repeatable()) os << "...";
7030 os << '\n';
7031 }
7032
7033
7034 }
7035 }
7036
7037 #endif
7038