├── Makefile ├── README.md ├── argx.hpp ├── main.cpp ├── tokenize.cpp ├── tokenize.hpp ├── tree.cpp └── tree.hpp /Makefile: -------------------------------------------------------------------------------- 1 | FLAG=-std=c++11 2 | 3 | install: solparser clean 4 | 5 | tree.o: 6 | g++ -c -o ./tree.o ./tree.cpp $(FLAG) 7 | 8 | tokenize.o: 9 | g++ -c -o ./tokenize.o ./tokenize.cpp $(FLAG) 10 | 11 | main.o: 12 | g++ -c -o ./main.o ./main.cpp $(FLAG) 13 | 14 | solparser: main.o tokenize.o tree.o 15 | g++ -o solparser ./main.o ./tokenize.o ./tree.o $(FLAG) 16 | 17 | clean: 18 | rm -f *.o 19 | 20 | allclear: 21 | rm -f *.o 22 | rm -f solparser 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # solparser 2 | 3 | ## Install 4 | ``` 5 | make install 6 | ``` 7 | 8 | ## How to use 9 | ``` 10 | -h, --help Display this help menu 11 | 12 | read source options: 13 | -f[NAME], --file=[NAME] read the source file 14 | -d, --stdin read the stdin 15 | 16 | 17 | $ ./solparser -f ./test.sol 18 | ``` 19 | # solparser 20 | -------------------------------------------------------------------------------- /argx.hpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2016-2017 Taylor C. Richberger and Pavel 2 | * Belikov 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | 23 | /** \file args.hxx 24 | * \brief this single-header lets you use all of the args functionality 25 | * 26 | * The important stuff is done inside the args namespace 27 | */ 28 | 29 | #ifndef ARGS_HXX 30 | #define ARGS_HXX 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #ifdef ARGS_TESTNAMESPACE 45 | namespace argstest 46 | { 47 | #else 48 | 49 | /** \namespace args 50 | * \brief contains all the functionality of the args library 51 | */ 52 | namespace args 53 | { 54 | #endif 55 | /** Getter to grab the value from the argument type. 56 | * 57 | * If the Get() function of the type returns a reference, so does this, and 58 | * the value will be modifiable. 59 | */ 60 | template 61 | auto get(Option &option_) -> decltype(option_.Get()) 62 | { 63 | return option_.Get(); 64 | } 65 | 66 | /** (INTERNAL) Count UTF-8 glyphs 67 | * 68 | * This is not reliable, and will fail for combinatory glyphs, but it's 69 | * good enough here for now. 70 | * 71 | * \param string The string to count glyphs from 72 | * \return The UTF-8 glyphs in the string 73 | */ 74 | inline std::string::size_type Glyphs(const std::string &string_) 75 | { 76 | std::string::size_type length = 0; 77 | for (const char c: string_) 78 | { 79 | if ((c & 0xc0) != 0x80) 80 | { 81 | ++length; 82 | } 83 | } 84 | return length; 85 | } 86 | 87 | /** (INTERNAL) Wrap a vector of words into a vector of lines 88 | * 89 | * Empty words are skipped. Word "\n" forces wrapping. 90 | * 91 | * \param begin The begin iterator 92 | * \param end The end iterator 93 | * \param width The width of the body 94 | * \param firstlinewidth the width of the first line, defaults to the width of the body 95 | * \param firstlineindent the indent of the first line, defaults to 0 96 | * \return the vector of lines 97 | */ 98 | template 99 | inline std::vector Wrap(It begin, 100 | It end, 101 | const std::string::size_type width, 102 | std::string::size_type firstlinewidth = 0, 103 | std::string::size_type firstlineindent = 0) 104 | { 105 | std::vector output; 106 | std::string line(firstlineindent, ' '); 107 | bool empty = true; 108 | 109 | if (firstlinewidth == 0) 110 | { 111 | firstlinewidth = width; 112 | } 113 | 114 | auto currentwidth = firstlinewidth; 115 | 116 | for (auto it = begin; it != end; ++it) 117 | { 118 | if (it->empty()) 119 | { 120 | continue; 121 | } 122 | 123 | if (*it == "\n") 124 | { 125 | if (!empty) 126 | { 127 | output.push_back(line); 128 | line.clear(); 129 | empty = true; 130 | currentwidth = width; 131 | } 132 | 133 | continue; 134 | } 135 | 136 | auto itemsize = Glyphs(*it); 137 | if ((line.length() + 1 + itemsize) > currentwidth) 138 | { 139 | if (!empty) 140 | { 141 | output.push_back(line); 142 | line.clear(); 143 | empty = true; 144 | currentwidth = width; 145 | } 146 | } 147 | 148 | if (itemsize > 0) 149 | { 150 | if (!empty) 151 | { 152 | line += ' '; 153 | } 154 | 155 | line += *it; 156 | empty = false; 157 | } 158 | } 159 | 160 | if (!empty) 161 | { 162 | output.push_back(line); 163 | } 164 | 165 | return output; 166 | } 167 | 168 | namespace detail 169 | { 170 | template 171 | std::string Join(const T& array, const std::string &delimiter) 172 | { 173 | std::string res; 174 | for (auto &element : array) 175 | { 176 | if (!res.empty()) 177 | { 178 | res += delimiter; 179 | } 180 | 181 | res += element; 182 | } 183 | 184 | return res; 185 | } 186 | } 187 | 188 | /** (INTERNAL) Wrap a string into a vector of lines 189 | * 190 | * This is quick and hacky, but works well enough. You can specify a 191 | * different width for the first line 192 | * 193 | * \param width The width of the body 194 | * \param firstlinewid the width of the first line, defaults to the width of the body 195 | * \return the vector of lines 196 | */ 197 | inline std::vector Wrap(const std::string &in, const std::string::size_type width, std::string::size_type firstlinewidth = 0) 198 | { 199 | // Preserve existing line breaks 200 | const auto newlineloc = in.find('\n'); 201 | if (newlineloc != in.npos) 202 | { 203 | auto first = Wrap(std::string(in, 0, newlineloc), width); 204 | auto second = Wrap(std::string(in, newlineloc + 1), width); 205 | first.insert( 206 | std::end(first), 207 | std::make_move_iterator(std::begin(second)), 208 | std::make_move_iterator(std::end(second))); 209 | return first; 210 | } 211 | 212 | std::istringstream stream(in); 213 | std::string::size_type indent = 0; 214 | 215 | for (char c : in) 216 | { 217 | if (!isspace(c)) 218 | { 219 | break; 220 | } 221 | ++indent; 222 | } 223 | 224 | return Wrap(std::istream_iterator(stream), std::istream_iterator(), 225 | width, firstlinewidth, indent); 226 | } 227 | 228 | #ifdef ARGS_NOEXCEPT 229 | /// Error class, for when ARGS_NOEXCEPT is defined 230 | enum class Error 231 | { 232 | None, 233 | Usage, 234 | Parse, 235 | Validation, 236 | Required, 237 | Map, 238 | Extra, 239 | Help, 240 | Subparser, 241 | Completion, 242 | }; 243 | #else 244 | /** Base error class 245 | */ 246 | class Error : public std::runtime_error 247 | { 248 | public: 249 | Error(const std::string &problem) : std::runtime_error(problem) {} 250 | virtual ~Error() {} 251 | }; 252 | 253 | /** Errors that occur during usage 254 | */ 255 | class UsageError : public Error 256 | { 257 | public: 258 | UsageError(const std::string &problem) : Error(problem) {} 259 | virtual ~UsageError() {} 260 | }; 261 | 262 | /** Errors that occur during regular parsing 263 | */ 264 | class ParseError : public Error 265 | { 266 | public: 267 | ParseError(const std::string &problem) : Error(problem) {} 268 | virtual ~ParseError() {} 269 | }; 270 | 271 | /** Errors that are detected from group validation after parsing finishes 272 | */ 273 | class ValidationError : public Error 274 | { 275 | public: 276 | ValidationError(const std::string &problem) : Error(problem) {} 277 | virtual ~ValidationError() {} 278 | }; 279 | 280 | /** Errors that when a required flag is omitted 281 | */ 282 | class RequiredError : public ValidationError 283 | { 284 | public: 285 | RequiredError(const std::string &problem) : ValidationError(problem) {} 286 | virtual ~RequiredError() {} 287 | }; 288 | 289 | /** Errors in map lookups 290 | */ 291 | class MapError : public ParseError 292 | { 293 | public: 294 | MapError(const std::string &problem) : ParseError(problem) {} 295 | virtual ~MapError() {} 296 | }; 297 | 298 | /** Error that occurs when a singular flag is specified multiple times 299 | */ 300 | class ExtraError : public ParseError 301 | { 302 | public: 303 | ExtraError(const std::string &problem) : ParseError(problem) {} 304 | virtual ~ExtraError() {} 305 | }; 306 | 307 | /** An exception that indicates that the user has requested help 308 | */ 309 | class Help : public Error 310 | { 311 | public: 312 | Help(const std::string &flag) : Error(flag) {} 313 | virtual ~Help() {} 314 | }; 315 | 316 | /** (INTERNAL) An exception that emulates coroutine-like control flow for subparsers. 317 | */ 318 | class SubparserError : public Error 319 | { 320 | public: 321 | SubparserError() : Error("") {} 322 | virtual ~SubparserError() {} 323 | }; 324 | 325 | /** An exception that contains autocompletion reply 326 | */ 327 | class Completion : public Error 328 | { 329 | public: 330 | Completion(const std::string &flag) : Error(flag) {} 331 | virtual ~Completion() {} 332 | }; 333 | #endif 334 | 335 | /** A simple unified option type for unified initializer lists for the Matcher class. 336 | */ 337 | struct EitherFlag 338 | { 339 | const bool isShort; 340 | const char shortFlag; 341 | const std::string longFlag; 342 | EitherFlag(const std::string &flag) : isShort(false), shortFlag(), longFlag(flag) {} 343 | EitherFlag(const char *flag) : isShort(false), shortFlag(), longFlag(flag) {} 344 | EitherFlag(const char flag) : isShort(true), shortFlag(flag), longFlag() {} 345 | 346 | /** Get just the long flags from an initializer list of EitherFlags 347 | */ 348 | static std::unordered_set GetLong(std::initializer_list flags) 349 | { 350 | std::unordered_set longFlags; 351 | for (const EitherFlag &flag: flags) 352 | { 353 | if (!flag.isShort) 354 | { 355 | longFlags.insert(flag.longFlag); 356 | } 357 | } 358 | return longFlags; 359 | } 360 | 361 | /** Get just the short flags from an initializer list of EitherFlags 362 | */ 363 | static std::unordered_set GetShort(std::initializer_list flags) 364 | { 365 | std::unordered_set shortFlags; 366 | for (const EitherFlag &flag: flags) 367 | { 368 | if (flag.isShort) 369 | { 370 | shortFlags.insert(flag.shortFlag); 371 | } 372 | } 373 | return shortFlags; 374 | } 375 | 376 | std::string str() const 377 | { 378 | return isShort ? std::string(1, shortFlag) : longFlag; 379 | } 380 | 381 | std::string str(const std::string &shortPrefix, const std::string &longPrefix) const 382 | { 383 | return isShort ? shortPrefix + std::string(1, shortFlag) : longPrefix + longFlag; 384 | } 385 | }; 386 | 387 | 388 | 389 | /** A class of "matchers", specifying short and flags that can possibly be 390 | * matched. 391 | * 392 | * This is supposed to be constructed and then passed in, not used directly 393 | * from user code. 394 | */ 395 | class Matcher 396 | { 397 | private: 398 | const std::unordered_set shortFlags; 399 | const std::unordered_set longFlags; 400 | 401 | public: 402 | /** Specify short and long flags separately as iterators 403 | * 404 | * ex: `args::Matcher(shortFlags.begin(), shortFlags.end(), longFlags.begin(), longFlags.end())` 405 | */ 406 | template 407 | Matcher(ShortIt shortFlagsStart, ShortIt shortFlagsEnd, LongIt longFlagsStart, LongIt longFlagsEnd) : 408 | shortFlags(shortFlagsStart, shortFlagsEnd), 409 | longFlags(longFlagsStart, longFlagsEnd) 410 | { 411 | if (shortFlags.empty() && longFlags.empty()) 412 | { 413 | #ifndef ARGS_NOEXCEPT 414 | throw UsageError("empty Matcher"); 415 | #endif 416 | } 417 | } 418 | 419 | #ifdef ARGS_NOEXCEPT 420 | /// Only for ARGS_NOEXCEPT 421 | Error GetError() const noexcept 422 | { 423 | return shortFlags.empty() && longFlags.empty() ? Error::Usage : Error::None; 424 | } 425 | #endif 426 | 427 | /** Specify short and long flags separately as iterables 428 | * 429 | * ex: `args::Matcher(shortFlags, longFlags)` 430 | */ 431 | template 432 | Matcher(Short &&shortIn, Long &&longIn) : 433 | Matcher(std::begin(shortIn), std::end(shortIn), std::begin(longIn), std::end(longIn)) 434 | {} 435 | 436 | /** Specify a mixed single initializer-list of both short and long flags 437 | * 438 | * This is the fancy one. It takes a single initializer list of 439 | * any number of any mixed kinds of flags. Chars are 440 | * automatically interpreted as short flags, and strings are 441 | * automatically interpreted as long flags: 442 | * 443 | * args::Matcher{'a'} 444 | * args::Matcher{"foo"} 445 | * args::Matcher{'h', "help"} 446 | * args::Matcher{"foo", 'f', 'F', "FoO"} 447 | */ 448 | Matcher(std::initializer_list in) : 449 | Matcher(EitherFlag::GetShort(in), EitherFlag::GetLong(in)) {} 450 | 451 | Matcher(Matcher &&other) : shortFlags(std::move(other.shortFlags)), longFlags(std::move(other.longFlags)) 452 | {} 453 | 454 | ~Matcher() {} 455 | 456 | /** (INTERNAL) Check if there is a match of a short flag 457 | */ 458 | bool Match(const char flag) const 459 | { 460 | return shortFlags.find(flag) != shortFlags.end(); 461 | } 462 | 463 | /** (INTERNAL) Check if there is a match of a long flag 464 | */ 465 | bool Match(const std::string &flag) const 466 | { 467 | return longFlags.find(flag) != longFlags.end(); 468 | } 469 | 470 | /** (INTERNAL) Check if there is a match of a flag 471 | */ 472 | bool Match(const EitherFlag &flag) const 473 | { 474 | return flag.isShort ? Match(flag.shortFlag) : Match(flag.longFlag); 475 | } 476 | 477 | /** (INTERNAL) Get all flag strings as a vector, with the prefixes embedded 478 | */ 479 | std::vector GetFlagStrings() const 480 | { 481 | std::vector flagStrings; 482 | flagStrings.reserve(shortFlags.size() + longFlags.size()); 483 | for (const char flag: shortFlags) 484 | { 485 | flagStrings.emplace_back(flag); 486 | } 487 | for (const std::string &flag: longFlags) 488 | { 489 | flagStrings.emplace_back(flag); 490 | } 491 | return flagStrings; 492 | } 493 | 494 | /** (INTERNAL) Get long flag if it exists or any short flag 495 | */ 496 | EitherFlag GetLongOrAny() const 497 | { 498 | if (!longFlags.empty()) 499 | { 500 | return *longFlags.begin(); 501 | } 502 | 503 | if (!shortFlags.empty()) 504 | { 505 | return *shortFlags.begin(); 506 | } 507 | 508 | // should be unreachable 509 | return ' '; 510 | } 511 | 512 | /** (INTERNAL) Get short flag if it exists or any long flag 513 | */ 514 | EitherFlag GetShortOrAny() const 515 | { 516 | if (!shortFlags.empty()) 517 | { 518 | return *shortFlags.begin(); 519 | } 520 | 521 | if (!longFlags.empty()) 522 | { 523 | return *longFlags.begin(); 524 | } 525 | 526 | // should be unreachable 527 | return ' '; 528 | } 529 | }; 530 | 531 | /** Attributes for flags. 532 | */ 533 | enum class Options 534 | { 535 | /** Default options. 536 | */ 537 | None = 0x0, 538 | 539 | /** Flag can't be passed multiple times. 540 | */ 541 | Single = 0x01, 542 | 543 | /** Flag can't be omitted. 544 | */ 545 | Required = 0x02, 546 | 547 | /** Flag is excluded from usage line. 548 | */ 549 | HiddenFromUsage = 0x04, 550 | 551 | /** Flag is excluded from options help. 552 | */ 553 | HiddenFromDescription = 0x08, 554 | 555 | /** Flag is global and can be used in any subcommand. 556 | */ 557 | Global = 0x10, 558 | 559 | /** Flag stops a parser. 560 | */ 561 | KickOut = 0x20, 562 | 563 | /** Flag is excluded from auto completion. 564 | */ 565 | HiddenFromCompletion = 0x40, 566 | 567 | /** Flag is excluded from options help and usage line 568 | */ 569 | Hidden = HiddenFromUsage | HiddenFromDescription | HiddenFromCompletion, 570 | }; 571 | 572 | inline Options operator | (Options lhs, Options rhs) 573 | { 574 | return static_cast(static_cast(lhs) | static_cast(rhs)); 575 | } 576 | 577 | inline Options operator & (Options lhs, Options rhs) 578 | { 579 | return static_cast(static_cast(lhs) & static_cast(rhs)); 580 | } 581 | 582 | class FlagBase; 583 | class PositionalBase; 584 | class Command; 585 | class ArgumentParser; 586 | 587 | /** A simple structure of parameters for easy user-modifyable help menus 588 | */ 589 | struct HelpParams 590 | { 591 | /** The width of the help menu 592 | */ 593 | unsigned int width = 80; 594 | /** The indent of the program line 595 | */ 596 | unsigned int progindent = 2; 597 | /** The indent of the program trailing lines for long parameters 598 | */ 599 | unsigned int progtailindent = 4; 600 | /** The indent of the description and epilogs 601 | */ 602 | unsigned int descriptionindent = 4; 603 | /** The indent of the flags 604 | */ 605 | unsigned int flagindent = 6; 606 | /** The indent of the flag descriptions 607 | */ 608 | unsigned int helpindent = 40; 609 | /** The additional indent each group adds 610 | */ 611 | unsigned int eachgroupindent = 2; 612 | 613 | /** The minimum gutter between each flag and its help 614 | */ 615 | unsigned int gutter = 1; 616 | 617 | /** Show the terminator when both options and positional parameters are present 618 | */ 619 | bool showTerminator = true; 620 | 621 | /** Show the {OPTIONS} on the prog line when this is true 622 | */ 623 | bool showProglineOptions = true; 624 | 625 | /** Show the positionals on the prog line when this is true 626 | */ 627 | bool showProglinePositionals = true; 628 | 629 | /** The prefix for short flags 630 | */ 631 | std::string shortPrefix; 632 | 633 | /** The prefix for long flags 634 | */ 635 | std::string longPrefix; 636 | 637 | /** The separator for short flags 638 | */ 639 | std::string shortSeparator; 640 | 641 | /** The separator for long flags 642 | */ 643 | std::string longSeparator; 644 | 645 | /** The program name for help generation 646 | */ 647 | std::string programName; 648 | 649 | /** Show command's flags 650 | */ 651 | bool showCommandChildren = false; 652 | 653 | /** Show command's descriptions and epilog 654 | */ 655 | bool showCommandFullHelp = false; 656 | 657 | /** The postfix for progline when showProglineOptions is true and command has any flags 658 | */ 659 | std::string proglineOptions = "{OPTIONS}"; 660 | 661 | /** The prefix for progline when command has any subcommands 662 | */ 663 | std::string proglineCommand = "COMMAND"; 664 | 665 | /** The prefix for progline value 666 | */ 667 | std::string proglineValueOpen = " <"; 668 | 669 | /** The postfix for progline value 670 | */ 671 | std::string proglineValueClose = ">"; 672 | 673 | /** The prefix for progline required argument 674 | */ 675 | std::string proglineRequiredOpen = ""; 676 | 677 | /** The postfix for progline required argument 678 | */ 679 | std::string proglineRequiredClose = ""; 680 | 681 | /** The prefix for progline non-required argument 682 | */ 683 | std::string proglineNonrequiredOpen = "["; 684 | 685 | /** The postfix for progline non-required argument 686 | */ 687 | std::string proglineNonrequiredClose = "]"; 688 | 689 | /** Show flags in program line 690 | */ 691 | bool proglineShowFlags = false; 692 | 693 | /** Use short flags in program lines when possible 694 | */ 695 | bool proglinePreferShortFlags = false; 696 | 697 | /** Program line prefix 698 | */ 699 | std::string usageString; 700 | 701 | /** String shown in help before flags descriptions 702 | */ 703 | std::string optionsString = "OPTIONS:"; 704 | 705 | /** Display value name after all the long and short flags 706 | */ 707 | bool useValueNameOnce = false; 708 | 709 | /** Show value name 710 | */ 711 | bool showValueName = true; 712 | 713 | /** Add newline before flag description 714 | */ 715 | bool addNewlineBeforeDescription = false; 716 | 717 | /** The prefix for option value 718 | */ 719 | std::string valueOpen = "["; 720 | 721 | /** The postfix for option value 722 | */ 723 | std::string valueClose = "]"; 724 | 725 | /** Add choices to argument description 726 | */ 727 | bool addChoices = false; 728 | 729 | /** The prefix for choices 730 | */ 731 | std::string choiceString = "\nOne of: "; 732 | 733 | /** Add default values to argument description 734 | */ 735 | bool addDefault = false; 736 | 737 | /** The prefix for default values 738 | */ 739 | std::string defaultString = "\nDefault: "; 740 | }; 741 | 742 | /** A number of arguments which can be consumed by an option. 743 | * 744 | * Represents a closed interval [min, max]. 745 | */ 746 | struct Nargs 747 | { 748 | const size_t min; 749 | const size_t max; 750 | 751 | Nargs(size_t min_, size_t max_) : min{min_}, max{max_} 752 | { 753 | #ifndef ARGS_NOEXCEPT 754 | if (max < min) 755 | { 756 | throw UsageError("Nargs: max > min"); 757 | } 758 | #endif 759 | } 760 | 761 | Nargs(size_t num_) : min{num_}, max{num_} 762 | { 763 | } 764 | 765 | friend bool operator == (const Nargs &lhs, const Nargs &rhs) 766 | { 767 | return lhs.min == rhs.min && lhs.max == rhs.max; 768 | } 769 | 770 | friend bool operator != (const Nargs &lhs, const Nargs &rhs) 771 | { 772 | return !(lhs == rhs); 773 | } 774 | }; 775 | 776 | /** Base class for all match types 777 | */ 778 | class Base 779 | { 780 | private: 781 | Options options = {}; 782 | 783 | protected: 784 | bool matched = false; 785 | const std::string help; 786 | #ifdef ARGS_NOEXCEPT 787 | /// Only for ARGS_NOEXCEPT 788 | mutable Error error = Error::None; 789 | mutable std::string errorMsg; 790 | #endif 791 | 792 | public: 793 | Base(const std::string &help_, Options options_ = {}) : options(options_), help(help_) {} 794 | virtual ~Base() {} 795 | 796 | Options GetOptions() const noexcept 797 | { 798 | return options; 799 | } 800 | 801 | bool IsRequired() const noexcept 802 | { 803 | return (GetOptions() & Options::Required) != Options::None; 804 | } 805 | 806 | virtual bool Matched() const noexcept 807 | { 808 | return matched; 809 | } 810 | 811 | virtual void Validate(const std::string &, const std::string &) const 812 | { 813 | } 814 | 815 | operator bool() const noexcept 816 | { 817 | return Matched(); 818 | } 819 | 820 | virtual std::vector> GetDescription(const HelpParams &, const unsigned indentLevel) const 821 | { 822 | std::tuple description; 823 | std::get<1>(description) = help; 824 | std::get<2>(description) = indentLevel; 825 | return { std::move(description) }; 826 | } 827 | 828 | virtual std::vector GetCommands() 829 | { 830 | return {}; 831 | } 832 | 833 | virtual bool IsGroup() const 834 | { 835 | return false; 836 | } 837 | 838 | virtual FlagBase *Match(const EitherFlag &) 839 | { 840 | return nullptr; 841 | } 842 | 843 | virtual PositionalBase *GetNextPositional() 844 | { 845 | return nullptr; 846 | } 847 | 848 | virtual std::vector GetAllFlags() 849 | { 850 | return {}; 851 | } 852 | 853 | virtual bool HasFlag() const 854 | { 855 | return false; 856 | } 857 | 858 | virtual bool HasPositional() const 859 | { 860 | return false; 861 | } 862 | 863 | virtual bool HasCommand() const 864 | { 865 | return false; 866 | } 867 | 868 | virtual std::vector GetProgramLine(const HelpParams &) const 869 | { 870 | return {}; 871 | } 872 | 873 | /// Sets a kick-out value for building subparsers 874 | void KickOut(bool kickout_) noexcept 875 | { 876 | if (kickout_) 877 | { 878 | options = options | Options::KickOut; 879 | } 880 | else 881 | { 882 | options = static_cast(static_cast(options) & ~static_cast(Options::KickOut)); 883 | } 884 | } 885 | 886 | /// Gets the kick-out value for building subparsers 887 | bool KickOut() const noexcept 888 | { 889 | return (options & Options::KickOut) != Options::None; 890 | } 891 | 892 | virtual void Reset() noexcept 893 | { 894 | matched = false; 895 | #ifdef ARGS_NOEXCEPT 896 | error = Error::None; 897 | errorMsg.clear(); 898 | #endif 899 | } 900 | 901 | #ifdef ARGS_NOEXCEPT 902 | /// Only for ARGS_NOEXCEPT 903 | virtual Error GetError() const 904 | { 905 | return error; 906 | } 907 | 908 | /// Only for ARGS_NOEXCEPT 909 | std::string GetErrorMsg() const 910 | { 911 | return errorMsg; 912 | } 913 | #endif 914 | }; 915 | 916 | /** Base class for all match types that have a name 917 | */ 918 | class NamedBase : public Base 919 | { 920 | protected: 921 | const std::string name; 922 | bool kickout = false; 923 | std::string defaultString; 924 | bool defaultStringManual = false; 925 | std::vector choicesStrings; 926 | bool choicesStringManual = false; 927 | 928 | virtual std::string GetDefaultString(const HelpParams&) const { return {}; } 929 | 930 | virtual std::vector GetChoicesStrings(const HelpParams&) const { return {}; } 931 | 932 | virtual std::string GetNameString(const HelpParams&) const { return Name(); } 933 | 934 | void AddDescriptionPostfix(std::string &dest, const bool isManual, const std::string &manual, bool isGenerated, const std::string &generated, const std::string &str) const 935 | { 936 | if (isManual && !manual.empty()) 937 | { 938 | dest += str; 939 | dest += manual; 940 | } 941 | else if (!isManual && isGenerated && !generated.empty()) 942 | { 943 | dest += str; 944 | dest += generated; 945 | } 946 | } 947 | 948 | public: 949 | NamedBase(const std::string &name_, const std::string &help_, Options options_ = {}) : Base(help_, options_), name(name_) {} 950 | virtual ~NamedBase() {} 951 | 952 | /** Sets default value string that will be added to argument description. 953 | * Use empty string to disable it for this argument. 954 | */ 955 | void HelpDefault(const std::string &str) 956 | { 957 | defaultStringManual = true; 958 | defaultString = str; 959 | } 960 | 961 | /** Gets default value string that will be added to argument description. 962 | */ 963 | std::string HelpDefault(const HelpParams ¶ms) const 964 | { 965 | return defaultStringManual ? defaultString : GetDefaultString(params); 966 | } 967 | 968 | /** Sets choices strings that will be added to argument description. 969 | * Use empty vector to disable it for this argument. 970 | */ 971 | void HelpChoices(const std::vector &array) 972 | { 973 | choicesStringManual = true; 974 | choicesStrings = array; 975 | } 976 | 977 | /** Gets choices strings that will be added to argument description. 978 | */ 979 | std::vector HelpChoices(const HelpParams ¶ms) const 980 | { 981 | return choicesStringManual ? choicesStrings : GetChoicesStrings(params); 982 | } 983 | 984 | virtual std::vector> GetDescription(const HelpParams ¶ms, const unsigned indentLevel) const override 985 | { 986 | std::tuple description; 987 | std::get<0>(description) = GetNameString(params); 988 | std::get<1>(description) = help; 989 | std::get<2>(description) = indentLevel; 990 | 991 | AddDescriptionPostfix(std::get<1>(description), choicesStringManual, detail::Join(choicesStrings, ", "), params.addChoices, detail::Join(GetChoicesStrings(params), ", "), params.choiceString); 992 | AddDescriptionPostfix(std::get<1>(description), defaultStringManual, defaultString, params.addDefault, GetDefaultString(params), params.defaultString); 993 | 994 | return { std::move(description) }; 995 | } 996 | 997 | virtual std::string Name() const 998 | { 999 | return name; 1000 | } 1001 | }; 1002 | 1003 | namespace detail 1004 | { 1005 | template 1006 | struct IsConvertableToString : std::false_type {}; 1007 | 1008 | template 1009 | struct IsConvertableToString() << std::declval(), int())> : std::true_type {}; 1010 | 1011 | template 1012 | typename std::enable_if::value, std::string>::type 1013 | ToString(const T &value) 1014 | { 1015 | std::ostringstream s; 1016 | s << value; 1017 | return s.str(); 1018 | } 1019 | 1020 | template 1021 | typename std::enable_if::value, std::string>::type 1022 | ToString(const T &) 1023 | { 1024 | return {}; 1025 | } 1026 | 1027 | template 1028 | std::vector MapKeysToStrings(const T &map) 1029 | { 1030 | std::vector res; 1031 | using K = typename std::decayfirst)>::type; 1032 | if (IsConvertableToString::value) 1033 | { 1034 | for (const auto &p : map) 1035 | { 1036 | res.push_back(detail::ToString(p.first)); 1037 | } 1038 | 1039 | std::sort(res.begin(), res.end()); 1040 | } 1041 | return res; 1042 | } 1043 | } 1044 | 1045 | /** Base class for all flag options 1046 | */ 1047 | class FlagBase : public NamedBase 1048 | { 1049 | protected: 1050 | const Matcher matcher; 1051 | 1052 | virtual std::string GetNameString(const HelpParams ¶ms) const override 1053 | { 1054 | const std::string postfix = !params.showValueName || NumberOfArguments() == 0 ? std::string() : Name(); 1055 | std::string flags; 1056 | const auto flagStrings = matcher.GetFlagStrings(); 1057 | const bool useValueNameOnce = flagStrings.size() == 1 ? false : params.useValueNameOnce; 1058 | for (auto it = flagStrings.begin(); it != flagStrings.end(); ++it) 1059 | { 1060 | auto &flag = *it; 1061 | if (it != flagStrings.begin()) 1062 | { 1063 | flags += ", "; 1064 | } 1065 | 1066 | flags += flag.isShort ? params.shortPrefix : params.longPrefix; 1067 | flags += flag.str(); 1068 | 1069 | if (!postfix.empty() && (!useValueNameOnce || it + 1 == flagStrings.end())) 1070 | { 1071 | flags += flag.isShort ? params.shortSeparator : params.longSeparator; 1072 | flags += params.valueOpen + postfix + params.valueClose; 1073 | } 1074 | } 1075 | 1076 | return flags; 1077 | } 1078 | 1079 | public: 1080 | FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : NamedBase(name_, help_, extraError_ ? Options::Single : Options()), matcher(std::move(matcher_)) {} 1081 | 1082 | FlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : NamedBase(name_, help_, options_), matcher(std::move(matcher_)) {} 1083 | 1084 | virtual ~FlagBase() {} 1085 | 1086 | virtual FlagBase *Match(const EitherFlag &flag) override 1087 | { 1088 | if (matcher.Match(flag)) 1089 | { 1090 | if ((GetOptions() & Options::Single) != Options::None && matched) 1091 | { 1092 | std::ostringstream problem; 1093 | problem << "Flag '" << flag.str() << "' was passed multiple times, but is only allowed to be passed once"; 1094 | #ifdef ARGS_NOEXCEPT 1095 | error = Error::Extra; 1096 | errorMsg = problem.str(); 1097 | #else 1098 | throw ExtraError(problem.str()); 1099 | #endif 1100 | } 1101 | matched = true; 1102 | return this; 1103 | } 1104 | return nullptr; 1105 | } 1106 | 1107 | virtual std::vector GetAllFlags() override 1108 | { 1109 | return { this }; 1110 | } 1111 | 1112 | const Matcher &GetMatcher() const 1113 | { 1114 | return matcher; 1115 | } 1116 | 1117 | virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override 1118 | { 1119 | if (!Matched() && IsRequired()) 1120 | { 1121 | std::ostringstream problem; 1122 | problem << "Flag '" << matcher.GetLongOrAny().str(shortPrefix, longPrefix) << "' is required"; 1123 | #ifdef ARGS_NOEXCEPT 1124 | error = Error::Required; 1125 | errorMsg = problem.str(); 1126 | #else 1127 | throw RequiredError(problem.str()); 1128 | #endif 1129 | } 1130 | } 1131 | 1132 | virtual std::vector GetProgramLine(const HelpParams ¶ms) const override 1133 | { 1134 | if (!params.proglineShowFlags) 1135 | { 1136 | return {}; 1137 | } 1138 | 1139 | const std::string postfix = NumberOfArguments() == 0 ? std::string() : Name(); 1140 | const EitherFlag flag = params.proglinePreferShortFlags ? matcher.GetShortOrAny() : matcher.GetLongOrAny(); 1141 | std::string res = flag.str(params.shortPrefix, params.longPrefix); 1142 | if (!postfix.empty()) 1143 | { 1144 | res += params.proglineValueOpen + postfix + params.proglineValueClose; 1145 | } 1146 | 1147 | return { IsRequired() ? params.proglineRequiredOpen + res + params.proglineRequiredClose 1148 | : params.proglineNonrequiredOpen + res + params.proglineNonrequiredClose }; 1149 | } 1150 | 1151 | virtual bool HasFlag() const override 1152 | { 1153 | return true; 1154 | } 1155 | 1156 | #ifdef ARGS_NOEXCEPT 1157 | /// Only for ARGS_NOEXCEPT 1158 | virtual Error GetError() const override 1159 | { 1160 | const auto nargs = NumberOfArguments(); 1161 | if (nargs.min > nargs.max) 1162 | { 1163 | return Error::Usage; 1164 | } 1165 | 1166 | const auto matcherError = matcher.GetError(); 1167 | if (matcherError != Error::None) 1168 | { 1169 | return matcherError; 1170 | } 1171 | 1172 | return error; 1173 | } 1174 | #endif 1175 | 1176 | /** Defines how many values can be consumed by this option. 1177 | * 1178 | * \return closed interval [min, max] 1179 | */ 1180 | virtual Nargs NumberOfArguments() const noexcept = 0; 1181 | 1182 | /** Parse values of this option. 1183 | * 1184 | * \param value Vector of values. It's size must be in NumberOfArguments() interval. 1185 | */ 1186 | virtual void ParseValue(const std::vector &value) = 0; 1187 | }; 1188 | 1189 | /** Base class for value-accepting flag options 1190 | */ 1191 | class ValueFlagBase : public FlagBase 1192 | { 1193 | public: 1194 | ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false) : FlagBase(name_, help_, std::move(matcher_), extraError_) {} 1195 | ValueFlagBase(const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) : FlagBase(name_, help_, std::move(matcher_), options_) {} 1196 | virtual ~ValueFlagBase() {} 1197 | 1198 | virtual Nargs NumberOfArguments() const noexcept override 1199 | { 1200 | return 1; 1201 | } 1202 | }; 1203 | 1204 | class CompletionFlag : public ValueFlagBase 1205 | { 1206 | public: 1207 | std::vector reply; 1208 | size_t cword = 0; 1209 | std::string syntax; 1210 | 1211 | template 1212 | CompletionFlag(GroupClass &group_, Matcher &&matcher_): ValueFlagBase("completion", "completion flag", std::move(matcher_), Options::Hidden) 1213 | { 1214 | group_.AddCompletion(*this); 1215 | } 1216 | 1217 | virtual ~CompletionFlag() {} 1218 | 1219 | virtual Nargs NumberOfArguments() const noexcept override 1220 | { 1221 | return 2; 1222 | } 1223 | 1224 | virtual void ParseValue(const std::vector &value_) override 1225 | { 1226 | syntax = value_.at(0); 1227 | std::istringstream(value_.at(1)) >> cword; 1228 | } 1229 | 1230 | /** Get the completion reply 1231 | */ 1232 | std::string Get() noexcept 1233 | { 1234 | return detail::Join(reply, "\n"); 1235 | } 1236 | 1237 | virtual void Reset() noexcept override 1238 | { 1239 | ValueFlagBase::Reset(); 1240 | cword = 0; 1241 | syntax.clear(); 1242 | reply.clear(); 1243 | } 1244 | }; 1245 | 1246 | 1247 | /** Base class for positional options 1248 | */ 1249 | class PositionalBase : public NamedBase 1250 | { 1251 | protected: 1252 | bool ready; 1253 | 1254 | public: 1255 | PositionalBase(const std::string &name_, const std::string &help_, Options options_ = {}) : NamedBase(name_, help_, options_), ready(true) {} 1256 | virtual ~PositionalBase() {} 1257 | 1258 | bool Ready() 1259 | { 1260 | return ready; 1261 | } 1262 | 1263 | virtual void ParseValue(const std::string &value_) = 0; 1264 | 1265 | virtual void Reset() noexcept override 1266 | { 1267 | matched = false; 1268 | ready = true; 1269 | #ifdef ARGS_NOEXCEPT 1270 | error = Error::None; 1271 | errorMsg.clear(); 1272 | #endif 1273 | } 1274 | 1275 | virtual PositionalBase *GetNextPositional() override 1276 | { 1277 | return Ready() ? this : nullptr; 1278 | } 1279 | 1280 | virtual bool HasPositional() const override 1281 | { 1282 | return true; 1283 | } 1284 | 1285 | virtual std::vector GetProgramLine(const HelpParams ¶ms) const override 1286 | { 1287 | return { IsRequired() ? params.proglineRequiredOpen + Name() + params.proglineRequiredClose 1288 | : params.proglineNonrequiredOpen + Name() + params.proglineNonrequiredClose }; 1289 | } 1290 | 1291 | virtual void Validate(const std::string &, const std::string &) const override 1292 | { 1293 | if (IsRequired() && !Matched()) 1294 | { 1295 | std::ostringstream problem; 1296 | problem << "Option '" << Name() << "' is required"; 1297 | #ifdef ARGS_NOEXCEPT 1298 | error = Error::Required; 1299 | errorMsg = problem.str(); 1300 | #else 1301 | throw RequiredError(problem.str()); 1302 | #endif 1303 | } 1304 | } 1305 | }; 1306 | 1307 | /** Class for all kinds of validating groups, including ArgumentParser 1308 | */ 1309 | class Group : public Base 1310 | { 1311 | private: 1312 | std::vector children; 1313 | std::function validator; 1314 | 1315 | public: 1316 | /** Default validators 1317 | */ 1318 | struct Validators 1319 | { 1320 | static bool Xor(const Group &group) 1321 | { 1322 | return group.MatchedChildren() == 1; 1323 | } 1324 | 1325 | static bool AtLeastOne(const Group &group) 1326 | { 1327 | return group.MatchedChildren() >= 1; 1328 | } 1329 | 1330 | static bool AtMostOne(const Group &group) 1331 | { 1332 | return group.MatchedChildren() <= 1; 1333 | } 1334 | 1335 | static bool All(const Group &group) 1336 | { 1337 | return group.Children().size() == group.MatchedChildren(); 1338 | } 1339 | 1340 | static bool AllOrNone(const Group &group) 1341 | { 1342 | return (All(group) || None(group)); 1343 | } 1344 | 1345 | static bool AllChildGroups(const Group &group) 1346 | { 1347 | return std::none_of(std::begin(group.Children()), std::end(group.Children()), [](const Base* child) -> bool { 1348 | return child->IsGroup() && !child->Matched(); 1349 | }); 1350 | } 1351 | 1352 | static bool DontCare(const Group &) 1353 | { 1354 | return true; 1355 | } 1356 | 1357 | static bool CareTooMuch(const Group &) 1358 | { 1359 | return false; 1360 | } 1361 | 1362 | static bool None(const Group &group) 1363 | { 1364 | return group.MatchedChildren() == 0; 1365 | } 1366 | }; 1367 | /// If help is empty, this group will not be printed in help output 1368 | Group(const std::string &help_ = std::string(), const std::function &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_) {} 1369 | /// If help is empty, this group will not be printed in help output 1370 | Group(Group &group_, const std::string &help_ = std::string(), const std::function &validator_ = Validators::DontCare, Options options_ = {}) : Base(help_, options_), validator(validator_) 1371 | { 1372 | group_.Add(*this); 1373 | } 1374 | virtual ~Group() {} 1375 | 1376 | /** Append a child to this Group. 1377 | */ 1378 | void Add(Base &child) 1379 | { 1380 | children.emplace_back(&child); 1381 | } 1382 | 1383 | /** Get all this group's children 1384 | */ 1385 | const std::vector &Children() const 1386 | { 1387 | return children; 1388 | } 1389 | 1390 | /** Return the first FlagBase that matches flag, or nullptr 1391 | * 1392 | * \param flag The flag with prefixes stripped 1393 | * \return the first matching FlagBase pointer, or nullptr if there is no match 1394 | */ 1395 | virtual FlagBase *Match(const EitherFlag &flag) override 1396 | { 1397 | for (Base *child: Children()) 1398 | { 1399 | if (FlagBase *match = child->Match(flag)) 1400 | { 1401 | return match; 1402 | } 1403 | } 1404 | return nullptr; 1405 | } 1406 | 1407 | virtual std::vector GetAllFlags() override 1408 | { 1409 | std::vector res; 1410 | for (Base *child: Children()) 1411 | { 1412 | auto childRes = child->GetAllFlags(); 1413 | res.insert(res.end(), childRes.begin(), childRes.end()); 1414 | } 1415 | return res; 1416 | } 1417 | 1418 | virtual void Validate(const std::string &shortPrefix, const std::string &longPrefix) const override 1419 | { 1420 | for (Base *child: Children()) 1421 | { 1422 | child->Validate(shortPrefix, longPrefix); 1423 | } 1424 | } 1425 | 1426 | /** Get the next ready positional, or nullptr if there is none 1427 | * 1428 | * \return the first ready PositionalBase pointer, or nullptr if there is no match 1429 | */ 1430 | virtual PositionalBase *GetNextPositional() override 1431 | { 1432 | for (Base *child: Children()) 1433 | { 1434 | if (auto next = child->GetNextPositional()) 1435 | { 1436 | return next; 1437 | } 1438 | } 1439 | return nullptr; 1440 | } 1441 | 1442 | /** Get whether this has any FlagBase children 1443 | * 1444 | * \return Whether or not there are any FlagBase children 1445 | */ 1446 | virtual bool HasFlag() const override 1447 | { 1448 | return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasFlag(); }); 1449 | } 1450 | 1451 | /** Get whether this has any PositionalBase children 1452 | * 1453 | * \return Whether or not there are any PositionalBase children 1454 | */ 1455 | virtual bool HasPositional() const override 1456 | { 1457 | return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasPositional(); }); 1458 | } 1459 | 1460 | /** Get whether this has any Command children 1461 | * 1462 | * \return Whether or not there are any Command children 1463 | */ 1464 | virtual bool HasCommand() const override 1465 | { 1466 | return std::any_of(Children().begin(), Children().end(), [](Base *child) { return child->HasCommand(); }); 1467 | } 1468 | 1469 | /** Count the number of matched children this group has 1470 | */ 1471 | std::vector::size_type MatchedChildren() const 1472 | { 1473 | return std::count_if(std::begin(Children()), std::end(Children()), [](const Base *child){return child->Matched();}); 1474 | } 1475 | 1476 | /** Whether or not this group matches validation 1477 | */ 1478 | virtual bool Matched() const noexcept override 1479 | { 1480 | return validator(*this); 1481 | } 1482 | 1483 | /** Get validation 1484 | */ 1485 | bool Get() const 1486 | { 1487 | return Matched(); 1488 | } 1489 | 1490 | /** Get all the child descriptions for help generation 1491 | */ 1492 | virtual std::vector> GetDescription(const HelpParams ¶ms, const unsigned int indent) const override 1493 | { 1494 | std::vector> descriptions; 1495 | 1496 | // Push that group description on the back if not empty 1497 | unsigned addindent = 0; 1498 | if (!help.empty()) 1499 | { 1500 | descriptions.emplace_back(help, "", indent); 1501 | addindent = 1; 1502 | } 1503 | 1504 | for (Base *child: Children()) 1505 | { 1506 | if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None) 1507 | { 1508 | continue; 1509 | } 1510 | 1511 | auto groupDescriptions = child->GetDescription(params, indent + addindent); 1512 | descriptions.insert( 1513 | std::end(descriptions), 1514 | std::make_move_iterator(std::begin(groupDescriptions)), 1515 | std::make_move_iterator(std::end(groupDescriptions))); 1516 | } 1517 | return descriptions; 1518 | } 1519 | 1520 | /** Get the names of positional parameters 1521 | */ 1522 | virtual std::vector GetProgramLine(const HelpParams ¶ms) const override 1523 | { 1524 | std::vector names; 1525 | for (Base *child: Children()) 1526 | { 1527 | if ((child->GetOptions() & Options::HiddenFromUsage) != Options::None) 1528 | { 1529 | continue; 1530 | } 1531 | 1532 | auto groupNames = child->GetProgramLine(params); 1533 | names.insert( 1534 | std::end(names), 1535 | std::make_move_iterator(std::begin(groupNames)), 1536 | std::make_move_iterator(std::end(groupNames))); 1537 | } 1538 | return names; 1539 | } 1540 | 1541 | virtual std::vector GetCommands() override 1542 | { 1543 | std::vector res; 1544 | for (const auto &child : Children()) 1545 | { 1546 | auto subparsers = child->GetCommands(); 1547 | res.insert(std::end(res), std::begin(subparsers), std::end(subparsers)); 1548 | } 1549 | return res; 1550 | } 1551 | 1552 | virtual bool IsGroup() const override 1553 | { 1554 | return true; 1555 | } 1556 | 1557 | virtual void Reset() noexcept override 1558 | { 1559 | Base::Reset(); 1560 | 1561 | for (auto &child: Children()) 1562 | { 1563 | child->Reset(); 1564 | } 1565 | #ifdef ARGS_NOEXCEPT 1566 | error = Error::None; 1567 | errorMsg.clear(); 1568 | #endif 1569 | } 1570 | 1571 | #ifdef ARGS_NOEXCEPT 1572 | /// Only for ARGS_NOEXCEPT 1573 | virtual Error GetError() const override 1574 | { 1575 | if (error != Error::None) 1576 | { 1577 | return error; 1578 | } 1579 | 1580 | auto it = std::find_if(Children().begin(), Children().end(), [](const Base *child){return child->GetError() != Error::None;}); 1581 | if (it == Children().end()) 1582 | { 1583 | return Error::None; 1584 | } else 1585 | { 1586 | return (*it)->GetError(); 1587 | } 1588 | } 1589 | #endif 1590 | 1591 | }; 1592 | 1593 | /** Class for using global options in ArgumentParser. 1594 | */ 1595 | class GlobalOptions : public Group 1596 | { 1597 | public: 1598 | GlobalOptions(Group &base, Base &options_) : Group(base, {}, Group::Validators::DontCare, Options::Global) 1599 | { 1600 | Add(options_); 1601 | } 1602 | }; 1603 | 1604 | /** Utility class for building subparsers with coroutines/callbacks. 1605 | * 1606 | * Brief example: 1607 | * \code 1608 | * Command command(argumentParser, "command", "my command", [](args::Subparser &s) 1609 | * { 1610 | * // your command flags/positionals 1611 | * s.Parse(); //required 1612 | * //your command code 1613 | * }); 1614 | * \endcode 1615 | * 1616 | * For ARGS_NOEXCEPT mode don't forget to check `s.GetError()` after `s.Parse()` 1617 | * and return if it isn't equals to args::Error::None. 1618 | * 1619 | * \sa Command 1620 | */ 1621 | class Subparser : public Group 1622 | { 1623 | private: 1624 | std::vector args; 1625 | std::vector kicked; 1626 | ArgumentParser *parser = nullptr; 1627 | const HelpParams &helpParams; 1628 | const Command &command; 1629 | bool isParsed = false; 1630 | 1631 | public: 1632 | Subparser(std::vector args_, ArgumentParser &parser_, const Command &command_, const HelpParams &helpParams_) 1633 | : args(std::move(args_)), parser(&parser_), helpParams(helpParams_), command(command_) 1634 | { 1635 | } 1636 | 1637 | Subparser(const Command &command_, const HelpParams &helpParams_) : helpParams(helpParams_), command(command_) 1638 | { 1639 | } 1640 | 1641 | Subparser(const Subparser&) = delete; 1642 | Subparser(Subparser&&) = delete; 1643 | Subparser &operator = (const Subparser&) = delete; 1644 | Subparser &operator = (Subparser&&) = delete; 1645 | 1646 | const Command &GetCommand() 1647 | { 1648 | return command; 1649 | } 1650 | 1651 | /** (INTERNAL) Determines whether Parse was called or not. 1652 | */ 1653 | bool IsParsed() const 1654 | { 1655 | return isParsed; 1656 | } 1657 | 1658 | /** Continue parsing arguments for new command. 1659 | */ 1660 | void Parse(); 1661 | 1662 | /** Returns a vector of kicked out arguments. 1663 | * 1664 | * \sa Base::KickOut 1665 | */ 1666 | const std::vector &KickedOut() const noexcept 1667 | { 1668 | return kicked; 1669 | } 1670 | }; 1671 | 1672 | /** Main class for building subparsers. 1673 | * 1674 | * /sa Subparser 1675 | */ 1676 | class Command : public Group 1677 | { 1678 | private: 1679 | friend class Subparser; 1680 | 1681 | std::string name; 1682 | std::string help; 1683 | std::string description; 1684 | std::string epilog; 1685 | std::string proglinePostfix; 1686 | 1687 | std::function parserCoroutine; 1688 | bool commandIsRequired = true; 1689 | Command *selectedCommand = nullptr; 1690 | 1691 | mutable std::vector> subparserDescription; 1692 | mutable std::vector subparserProgramLine; 1693 | mutable bool subparserHasFlag = false; 1694 | mutable bool subparserHasPositional = false; 1695 | mutable bool subparserHasCommand = false; 1696 | #ifdef ARGS_NOEXCEPT 1697 | mutable Error subparserError = Error::None; 1698 | #endif 1699 | mutable Subparser *subparser = nullptr; 1700 | 1701 | protected: 1702 | 1703 | class RaiiSubparser 1704 | { 1705 | public: 1706 | RaiiSubparser(ArgumentParser &parser_, std::vector args_); 1707 | RaiiSubparser(const Command &command_, const HelpParams ¶ms_); 1708 | 1709 | ~RaiiSubparser() 1710 | { 1711 | command.subparser = oldSubparser; 1712 | } 1713 | 1714 | Subparser &Parser() 1715 | { 1716 | return parser; 1717 | } 1718 | 1719 | private: 1720 | const Command &command; 1721 | Subparser parser; 1722 | Subparser *oldSubparser; 1723 | }; 1724 | 1725 | Command() = default; 1726 | 1727 | std::function &GetCoroutine() 1728 | { 1729 | return selectedCommand != nullptr ? selectedCommand->GetCoroutine() : parserCoroutine; 1730 | } 1731 | 1732 | Command &SelectedCommand() 1733 | { 1734 | Command *res = this; 1735 | while (res->selectedCommand != nullptr) 1736 | { 1737 | res = res->selectedCommand; 1738 | } 1739 | 1740 | return *res; 1741 | } 1742 | 1743 | const Command &SelectedCommand() const 1744 | { 1745 | const Command *res = this; 1746 | while (res->selectedCommand != nullptr) 1747 | { 1748 | res = res->selectedCommand; 1749 | } 1750 | 1751 | return *res; 1752 | } 1753 | 1754 | void UpdateSubparserHelp(const HelpParams ¶ms) const 1755 | { 1756 | if (parserCoroutine) 1757 | { 1758 | RaiiSubparser coro(*this, params); 1759 | #ifndef ARGS_NOEXCEPT 1760 | try 1761 | { 1762 | parserCoroutine(coro.Parser()); 1763 | } 1764 | catch (args::SubparserError&) 1765 | { 1766 | } 1767 | #else 1768 | parserCoroutine(coro.Parser()); 1769 | #endif 1770 | } 1771 | } 1772 | 1773 | public: 1774 | Command(Group &base_, std::string name_, std::string help_, std::function coroutine_ = {}) 1775 | : name(std::move(name_)), help(std::move(help_)), parserCoroutine(std::move(coroutine_)) 1776 | { 1777 | base_.Add(*this); 1778 | } 1779 | 1780 | /** The description that appears on the prog line after options 1781 | */ 1782 | const std::string &ProglinePostfix() const 1783 | { return proglinePostfix; } 1784 | 1785 | /** The description that appears on the prog line after options 1786 | */ 1787 | void ProglinePostfix(const std::string &proglinePostfix_) 1788 | { this->proglinePostfix = proglinePostfix_; } 1789 | 1790 | /** The description that appears above options 1791 | */ 1792 | const std::string &Description() const 1793 | { return description; } 1794 | /** The description that appears above options 1795 | */ 1796 | 1797 | void Description(const std::string &description_) 1798 | { this->description = description_; } 1799 | 1800 | /** The description that appears below options 1801 | */ 1802 | const std::string &Epilog() const 1803 | { return epilog; } 1804 | 1805 | /** The description that appears below options 1806 | */ 1807 | void Epilog(const std::string &epilog_) 1808 | { this->epilog = epilog_; } 1809 | 1810 | /** The name of command 1811 | */ 1812 | const std::string &Name() const 1813 | { return name; } 1814 | 1815 | /** The description of command 1816 | */ 1817 | const std::string &Help() const 1818 | { return help; } 1819 | 1820 | /** If value is true, parser will fail if no command was parsed. 1821 | * 1822 | * Default: true. 1823 | */ 1824 | void RequireCommand(bool value) 1825 | { commandIsRequired = value; } 1826 | 1827 | virtual bool IsGroup() const override 1828 | { return false; } 1829 | 1830 | virtual bool Matched() const noexcept override 1831 | { return Base::Matched(); } 1832 | 1833 | operator bool() const noexcept 1834 | { return Matched(); } 1835 | 1836 | void Match() noexcept 1837 | { matched = true; } 1838 | 1839 | void SelectCommand(Command *c) noexcept 1840 | { 1841 | selectedCommand = c; 1842 | 1843 | if (c != nullptr) 1844 | { 1845 | c->Match(); 1846 | } 1847 | } 1848 | 1849 | virtual FlagBase *Match(const EitherFlag &flag) override 1850 | { 1851 | if (selectedCommand != nullptr) 1852 | { 1853 | if (auto *res = selectedCommand->Match(flag)) 1854 | { 1855 | return res; 1856 | } 1857 | 1858 | for (auto *child: Children()) 1859 | { 1860 | if ((child->GetOptions() & Options::Global) != Options::None) 1861 | { 1862 | if (auto *res = child->Match(flag)) 1863 | { 1864 | return res; 1865 | } 1866 | } 1867 | } 1868 | 1869 | return nullptr; 1870 | } 1871 | 1872 | if (subparser != nullptr) 1873 | { 1874 | return subparser->Match(flag); 1875 | } 1876 | 1877 | return Matched() ? Group::Match(flag) : nullptr; 1878 | } 1879 | 1880 | virtual std::vector GetAllFlags() override 1881 | { 1882 | std::vector res; 1883 | 1884 | if (!Matched()) 1885 | { 1886 | return res; 1887 | } 1888 | 1889 | for (auto *child: Children()) 1890 | { 1891 | if (selectedCommand == nullptr || (child->GetOptions() & Options::Global) != Options::None) 1892 | { 1893 | auto childFlags = child->GetAllFlags(); 1894 | res.insert(res.end(), childFlags.begin(), childFlags.end()); 1895 | } 1896 | } 1897 | 1898 | if (selectedCommand != nullptr) 1899 | { 1900 | auto childFlags = selectedCommand->GetAllFlags(); 1901 | res.insert(res.end(), childFlags.begin(), childFlags.end()); 1902 | } 1903 | 1904 | if (subparser != nullptr) 1905 | { 1906 | auto childFlags = subparser->GetAllFlags(); 1907 | res.insert(res.end(), childFlags.begin(), childFlags.end()); 1908 | } 1909 | 1910 | return res; 1911 | } 1912 | 1913 | virtual PositionalBase *GetNextPositional() override 1914 | { 1915 | if (selectedCommand != nullptr) 1916 | { 1917 | if (auto *res = selectedCommand->GetNextPositional()) 1918 | { 1919 | return res; 1920 | } 1921 | 1922 | for (auto *child: Children()) 1923 | { 1924 | if ((child->GetOptions() & Options::Global) != Options::None) 1925 | { 1926 | if (auto *res = child->GetNextPositional()) 1927 | { 1928 | return res; 1929 | } 1930 | } 1931 | } 1932 | 1933 | return nullptr; 1934 | } 1935 | 1936 | if (subparser != nullptr) 1937 | { 1938 | return subparser->GetNextPositional(); 1939 | } 1940 | 1941 | return Matched() ? Group::GetNextPositional() : nullptr; 1942 | } 1943 | 1944 | virtual bool HasFlag() const override 1945 | { 1946 | return subparserHasFlag || Group::HasFlag(); 1947 | } 1948 | 1949 | virtual bool HasPositional() const override 1950 | { 1951 | return subparserHasPositional || Group::HasPositional(); 1952 | } 1953 | 1954 | virtual bool HasCommand() const override 1955 | { 1956 | return true; 1957 | } 1958 | 1959 | std::vector GetCommandProgramLine(const HelpParams ¶ms) const 1960 | { 1961 | UpdateSubparserHelp(params); 1962 | 1963 | auto res = Group::GetProgramLine(params); 1964 | res.insert(res.end(), subparserProgramLine.begin(), subparserProgramLine.end()); 1965 | 1966 | if (!params.proglineCommand.empty() && (Group::HasCommand() || subparserHasCommand)) 1967 | { 1968 | res.insert(res.begin(), commandIsRequired ? params.proglineCommand : "[" + params.proglineCommand + "]"); 1969 | } 1970 | 1971 | if (!Name().empty()) 1972 | { 1973 | res.insert(res.begin(), Name()); 1974 | } 1975 | 1976 | if ((subparserHasFlag || Group::HasFlag()) && params.showProglineOptions && !params.proglineShowFlags) 1977 | { 1978 | res.push_back(params.proglineOptions); 1979 | } 1980 | 1981 | if (!ProglinePostfix().empty()) 1982 | { 1983 | std::string line; 1984 | for (char c : ProglinePostfix()) 1985 | { 1986 | if (isspace(c)) 1987 | { 1988 | if (!line.empty()) 1989 | { 1990 | res.push_back(line); 1991 | line.clear(); 1992 | } 1993 | 1994 | if (c == '\n') 1995 | { 1996 | res.push_back("\n"); 1997 | } 1998 | } 1999 | else 2000 | { 2001 | line += c; 2002 | } 2003 | } 2004 | 2005 | if (!line.empty()) 2006 | { 2007 | res.push_back(line); 2008 | } 2009 | } 2010 | 2011 | return res; 2012 | } 2013 | 2014 | virtual std::vector GetProgramLine(const HelpParams ¶ms) const override 2015 | { 2016 | if (!Matched()) 2017 | { 2018 | return {}; 2019 | } 2020 | 2021 | return GetCommandProgramLine(params); 2022 | } 2023 | 2024 | virtual std::vector GetCommands() override 2025 | { 2026 | if (selectedCommand != nullptr) 2027 | { 2028 | return selectedCommand->GetCommands(); 2029 | } 2030 | 2031 | if (Matched()) 2032 | { 2033 | return Group::GetCommands(); 2034 | } 2035 | 2036 | return { this }; 2037 | } 2038 | 2039 | virtual std::vector> GetDescription(const HelpParams ¶ms, const unsigned int indent) const override 2040 | { 2041 | std::vector> descriptions; 2042 | unsigned addindent = 0; 2043 | 2044 | UpdateSubparserHelp(params); 2045 | 2046 | if (!Matched()) 2047 | { 2048 | if (params.showCommandFullHelp) 2049 | { 2050 | std::ostringstream s; 2051 | bool empty = true; 2052 | for (const auto &progline: GetCommandProgramLine(params)) 2053 | { 2054 | if (!empty) 2055 | { 2056 | s << ' '; 2057 | } 2058 | else 2059 | { 2060 | empty = false; 2061 | } 2062 | 2063 | s << progline; 2064 | } 2065 | 2066 | descriptions.emplace_back(s.str(), "", indent); 2067 | } 2068 | else 2069 | { 2070 | descriptions.emplace_back(Name(), help, indent); 2071 | } 2072 | 2073 | if (!params.showCommandChildren && !params.showCommandFullHelp) 2074 | { 2075 | return descriptions; 2076 | } 2077 | 2078 | addindent = 1; 2079 | } 2080 | 2081 | if (params.showCommandFullHelp && !Matched()) 2082 | { 2083 | descriptions.emplace_back("", "", indent + addindent); 2084 | descriptions.emplace_back(Description().empty() ? Help() : Description(), "", indent + addindent); 2085 | descriptions.emplace_back("", "", indent + addindent); 2086 | } 2087 | 2088 | for (Base *child: Children()) 2089 | { 2090 | if ((child->GetOptions() & Options::HiddenFromDescription) != Options::None) 2091 | { 2092 | continue; 2093 | } 2094 | 2095 | auto groupDescriptions = child->GetDescription(params, indent + addindent); 2096 | descriptions.insert( 2097 | std::end(descriptions), 2098 | std::make_move_iterator(std::begin(groupDescriptions)), 2099 | std::make_move_iterator(std::end(groupDescriptions))); 2100 | } 2101 | 2102 | for (auto childDescription: subparserDescription) 2103 | { 2104 | std::get<2>(childDescription) += indent + addindent; 2105 | descriptions.push_back(std::move(childDescription)); 2106 | } 2107 | 2108 | if (params.showCommandFullHelp && !Matched()) 2109 | { 2110 | descriptions.emplace_back("", "", indent + addindent); 2111 | if (!Epilog().empty()) 2112 | { 2113 | descriptions.emplace_back(Epilog(), "", indent + addindent); 2114 | descriptions.emplace_back("", "", indent + addindent); 2115 | } 2116 | } 2117 | 2118 | return descriptions; 2119 | } 2120 | 2121 | virtual void Validate(const std::string &shortprefix, const std::string &longprefix) const override 2122 | { 2123 | if (!Matched()) 2124 | { 2125 | return; 2126 | } 2127 | 2128 | for (Base *child: Children()) 2129 | { 2130 | if (child->IsGroup() && !child->Matched()) 2131 | { 2132 | std::ostringstream problem; 2133 | problem << "Group validation failed somewhere!"; 2134 | #ifdef ARGS_NOEXCEPT 2135 | error = Error::Validation; 2136 | errorMsg = problem.str(); 2137 | #else 2138 | throw ValidationError(problem.str()); 2139 | #endif 2140 | } 2141 | 2142 | child->Validate(shortprefix, longprefix); 2143 | } 2144 | 2145 | if (subparser != nullptr) 2146 | { 2147 | subparser->Validate(shortprefix, longprefix); 2148 | } 2149 | 2150 | if (selectedCommand == nullptr && commandIsRequired && (Group::HasCommand() || subparserHasCommand)) 2151 | { 2152 | std::ostringstream problem; 2153 | problem << "Command is required"; 2154 | #ifdef ARGS_NOEXCEPT 2155 | error = Error::Validation; 2156 | errorMsg = problem.str(); 2157 | #else 2158 | throw ValidationError(problem.str()); 2159 | #endif 2160 | } 2161 | } 2162 | 2163 | virtual void Reset() noexcept override 2164 | { 2165 | Group::Reset(); 2166 | selectedCommand = nullptr; 2167 | subparserProgramLine.clear(); 2168 | subparserDescription.clear(); 2169 | subparserHasFlag = false; 2170 | subparserHasPositional = false; 2171 | subparserHasCommand = false; 2172 | #ifdef ARGS_NOEXCEPT 2173 | subparserError = Error::None; 2174 | #endif 2175 | } 2176 | 2177 | #ifdef ARGS_NOEXCEPT 2178 | /// Only for ARGS_NOEXCEPT 2179 | virtual Error GetError() const override 2180 | { 2181 | if (!Matched()) 2182 | { 2183 | return Error::None; 2184 | } 2185 | 2186 | if (error != Error::None) 2187 | { 2188 | return error; 2189 | } 2190 | 2191 | if (subparserError != Error::None) 2192 | { 2193 | return subparserError; 2194 | } 2195 | 2196 | return Group::GetError(); 2197 | } 2198 | #endif 2199 | }; 2200 | 2201 | /** The main user facing command line argument parser class 2202 | */ 2203 | class ArgumentParser : public Command 2204 | { 2205 | friend class Subparser; 2206 | 2207 | private: 2208 | std::string longprefix; 2209 | std::string shortprefix; 2210 | 2211 | std::string longseparator; 2212 | 2213 | std::string terminator; 2214 | 2215 | bool allowJoinedShortValue = true; 2216 | bool allowJoinedLongValue = true; 2217 | bool allowSeparateShortValue = true; 2218 | bool allowSeparateLongValue = true; 2219 | 2220 | CompletionFlag *completion = nullptr; 2221 | bool readCompletion = false; 2222 | 2223 | protected: 2224 | enum class OptionType 2225 | { 2226 | LongFlag, 2227 | ShortFlag, 2228 | Positional 2229 | }; 2230 | 2231 | OptionType ParseOption(const std::string &s, bool allowEmpty = false) 2232 | { 2233 | if (s.find(longprefix) == 0 && (allowEmpty || s.length() > longprefix.length())) 2234 | { 2235 | return OptionType::LongFlag; 2236 | } 2237 | 2238 | if (s.find(shortprefix) == 0 && (allowEmpty || s.length() > shortprefix.length())) 2239 | { 2240 | return OptionType::ShortFlag; 2241 | } 2242 | 2243 | return OptionType::Positional; 2244 | } 2245 | 2246 | template 2247 | bool Complete(FlagBase &flag, It it, It end) 2248 | { 2249 | auto nextIt = it; 2250 | if (!readCompletion || (++nextIt != end)) 2251 | { 2252 | return false; 2253 | } 2254 | 2255 | const auto &chunk = *it; 2256 | for (auto &choice : flag.HelpChoices(helpParams)) 2257 | { 2258 | AddCompletionReply(chunk, choice); 2259 | } 2260 | 2261 | #ifndef ARGS_NOEXCEPT 2262 | throw Completion(completion->Get()); 2263 | #else 2264 | return true; 2265 | #endif 2266 | } 2267 | 2268 | /** (INTERNAL) Parse flag's values 2269 | * 2270 | * \param arg The string to display in error message as a flag name 2271 | * \param[in, out] it The iterator to first value. It will point to the last value 2272 | * \param end The end iterator 2273 | * \param joinedArg Joined value (e.g. bar in --foo=bar) 2274 | * \param canDiscardJoined If true joined value can be parsed as flag not as a value (as in -abcd) 2275 | * \param[out] values The vector to store parsed arg's values 2276 | */ 2277 | template 2278 | std::string ParseArgsValues(FlagBase &flag, const std::string &arg, It &it, It end, 2279 | const bool allowSeparate, const bool allowJoined, 2280 | const bool hasJoined, const std::string &joinedArg, 2281 | const bool canDiscardJoined, std::vector &values) 2282 | { 2283 | values.clear(); 2284 | 2285 | Nargs nargs = flag.NumberOfArguments(); 2286 | 2287 | if (hasJoined && !allowJoined && nargs.min != 0) 2288 | { 2289 | return "Flag '" + arg + "' was passed a joined argument, but these are disallowed"; 2290 | } 2291 | 2292 | if (hasJoined) 2293 | { 2294 | if (!canDiscardJoined || nargs.max != 0) 2295 | { 2296 | values.push_back(joinedArg); 2297 | } 2298 | } else if (!allowSeparate) 2299 | { 2300 | if (nargs.min != 0) 2301 | { 2302 | return "Flag '" + arg + "' was passed a separate argument, but these are disallowed"; 2303 | } 2304 | } else 2305 | { 2306 | auto valueIt = it; 2307 | ++valueIt; 2308 | 2309 | while (valueIt != end && 2310 | values.size() < nargs.max && 2311 | (nargs.min == nargs.max || ParseOption(*valueIt) == OptionType::Positional)) 2312 | { 2313 | if (Complete(flag, valueIt, end)) 2314 | { 2315 | it = end; 2316 | return ""; 2317 | } 2318 | 2319 | values.push_back(*valueIt); 2320 | ++it; 2321 | ++valueIt; 2322 | } 2323 | } 2324 | 2325 | if (values.size() > nargs.max) 2326 | { 2327 | return "Passed an argument into a non-argument flag: " + arg; 2328 | } else if (values.size() < nargs.min) 2329 | { 2330 | if (nargs.min == 1 && nargs.max == 1) 2331 | { 2332 | return "Flag '" + arg + "' requires an argument but received none"; 2333 | } else if (nargs.min == 1) 2334 | { 2335 | return "Flag '" + arg + "' requires at least one argument but received none"; 2336 | } else if (nargs.min != nargs.max) 2337 | { 2338 | return "Flag '" + arg + "' requires at least " + std::to_string(nargs.min) + 2339 | " arguments but received " + std::to_string(values.size()); 2340 | } else 2341 | { 2342 | return "Flag '" + arg + "' requires " + std::to_string(nargs.min) + 2343 | " arguments but received " + std::to_string(values.size()); 2344 | } 2345 | } 2346 | 2347 | return {}; 2348 | } 2349 | 2350 | template 2351 | bool ParseLong(It &it, It end) 2352 | { 2353 | const auto &chunk = *it; 2354 | const auto argchunk = chunk.substr(longprefix.size()); 2355 | // Try to separate it, in case of a separator: 2356 | const auto separator = longseparator.empty() ? argchunk.npos : argchunk.find(longseparator); 2357 | // If the separator is in the argument, separate it. 2358 | const auto arg = (separator != argchunk.npos ? 2359 | std::string(argchunk, 0, separator) 2360 | : argchunk); 2361 | const auto joined = (separator != argchunk.npos ? 2362 | argchunk.substr(separator + longseparator.size()) 2363 | : std::string()); 2364 | 2365 | if (auto flag = Match(arg)) 2366 | { 2367 | std::vector values; 2368 | const std::string errorMessage = ParseArgsValues(*flag, arg, it, end, allowSeparateLongValue, allowJoinedLongValue, 2369 | separator != argchunk.npos, joined, false, values); 2370 | if (!errorMessage.empty()) 2371 | { 2372 | #ifndef ARGS_NOEXCEPT 2373 | throw ParseError(errorMessage); 2374 | #else 2375 | error = Error::Parse; 2376 | errorMsg = errorMessage; 2377 | return false; 2378 | #endif 2379 | } 2380 | 2381 | if (!readCompletion) 2382 | { 2383 | flag->ParseValue(values); 2384 | } 2385 | 2386 | if (flag->KickOut()) 2387 | { 2388 | ++it; 2389 | return false; 2390 | } 2391 | } else 2392 | { 2393 | const std::string errorMessage("Flag could not be matched: " + arg); 2394 | #ifndef ARGS_NOEXCEPT 2395 | throw ParseError(errorMessage); 2396 | #else 2397 | error = Error::Parse; 2398 | errorMsg = errorMessage; 2399 | return false; 2400 | #endif 2401 | } 2402 | 2403 | return true; 2404 | } 2405 | 2406 | template 2407 | bool ParseShort(It &it, It end) 2408 | { 2409 | const auto &chunk = *it; 2410 | const auto argchunk = chunk.substr(shortprefix.size()); 2411 | for (auto argit = std::begin(argchunk); argit != std::end(argchunk); ++argit) 2412 | { 2413 | const auto arg = *argit; 2414 | 2415 | if (auto flag = Match(arg)) 2416 | { 2417 | const std::string value(argit + 1, std::end(argchunk)); 2418 | std::vector values; 2419 | const std::string errorMessage = ParseArgsValues(*flag, std::string(1, arg), it, end, 2420 | allowSeparateShortValue, allowJoinedShortValue, 2421 | !value.empty(), value, !value.empty(), values); 2422 | 2423 | if (!errorMessage.empty()) 2424 | { 2425 | #ifndef ARGS_NOEXCEPT 2426 | throw ParseError(errorMessage); 2427 | #else 2428 | error = Error::Parse; 2429 | errorMsg = errorMessage; 2430 | return false; 2431 | #endif 2432 | } 2433 | 2434 | if (!readCompletion) 2435 | { 2436 | flag->ParseValue(values); 2437 | } 2438 | 2439 | if (flag->KickOut()) 2440 | { 2441 | ++it; 2442 | return false; 2443 | } 2444 | 2445 | if (!values.empty()) 2446 | { 2447 | break; 2448 | } 2449 | } else 2450 | { 2451 | const std::string errorMessage("Flag could not be matched: '" + std::string(1, arg) + "'"); 2452 | #ifndef ARGS_NOEXCEPT 2453 | throw ParseError(errorMessage); 2454 | #else 2455 | error = Error::Parse; 2456 | errorMsg = errorMessage; 2457 | return false; 2458 | #endif 2459 | } 2460 | } 2461 | 2462 | return true; 2463 | } 2464 | 2465 | bool AddCompletionReply(const std::string &cur, const std::string &choice) 2466 | { 2467 | if (cur.empty() || choice.find(cur) == 0) 2468 | { 2469 | if (completion->syntax == "bash" && ParseOption(choice) == OptionType::LongFlag && choice.find(longseparator) != std::string::npos) 2470 | { 2471 | completion->reply.push_back(choice.substr(choice.find(longseparator) + 1)); 2472 | } else 2473 | { 2474 | completion->reply.push_back(choice); 2475 | } 2476 | return true; 2477 | } 2478 | 2479 | return false; 2480 | } 2481 | 2482 | template 2483 | bool Complete(It it, It end) 2484 | { 2485 | auto nextIt = it; 2486 | if (!readCompletion || (++nextIt != end)) 2487 | { 2488 | return false; 2489 | } 2490 | 2491 | const auto &chunk = *it; 2492 | auto pos = GetNextPositional(); 2493 | std::vector commands = GetCommands(); 2494 | const auto optionType = ParseOption(chunk, true); 2495 | 2496 | if (!commands.empty() && (chunk.empty() || optionType == OptionType::Positional)) 2497 | { 2498 | for (auto &cmd : commands) 2499 | { 2500 | if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) 2501 | { 2502 | AddCompletionReply(chunk, cmd->Name()); 2503 | } 2504 | } 2505 | } else 2506 | { 2507 | bool hasPositionalCompletion = true; 2508 | 2509 | if (!commands.empty()) 2510 | { 2511 | for (auto &cmd : commands) 2512 | { 2513 | if ((cmd->GetOptions() & Options::HiddenFromCompletion) == Options::None) 2514 | { 2515 | AddCompletionReply(chunk, cmd->Name()); 2516 | } 2517 | } 2518 | } else if (pos) 2519 | { 2520 | if ((pos->GetOptions() & Options::HiddenFromCompletion) == Options::None) 2521 | { 2522 | auto choices = pos->HelpChoices(helpParams); 2523 | hasPositionalCompletion = !choices.empty() || optionType != OptionType::Positional; 2524 | for (auto &choice : choices) 2525 | { 2526 | AddCompletionReply(chunk, choice); 2527 | } 2528 | } 2529 | } 2530 | 2531 | if (hasPositionalCompletion) 2532 | { 2533 | auto flags = GetAllFlags(); 2534 | for (auto flag : flags) 2535 | { 2536 | if ((flag->GetOptions() & Options::HiddenFromCompletion) != Options::None) 2537 | { 2538 | continue; 2539 | } 2540 | 2541 | auto &matcher = flag->GetMatcher(); 2542 | if (!AddCompletionReply(chunk, matcher.GetShortOrAny().str(shortprefix, longprefix))) 2543 | { 2544 | for (auto &flagName : matcher.GetFlagStrings()) 2545 | { 2546 | if (AddCompletionReply(chunk, flagName.str(shortprefix, longprefix))) 2547 | { 2548 | break; 2549 | } 2550 | } 2551 | } 2552 | } 2553 | 2554 | if (optionType == OptionType::LongFlag && allowJoinedLongValue) 2555 | { 2556 | const auto separator = longseparator.empty() ? chunk.npos : chunk.find(longseparator); 2557 | if (separator != chunk.npos) 2558 | { 2559 | std::string arg(chunk, 0, separator); 2560 | if (auto flag = this->Match(arg.substr(longprefix.size()))) 2561 | { 2562 | for (auto &choice : flag->HelpChoices(helpParams)) 2563 | { 2564 | AddCompletionReply(chunk, arg + longseparator + choice); 2565 | } 2566 | } 2567 | } 2568 | } else if (optionType == OptionType::ShortFlag && allowJoinedShortValue) 2569 | { 2570 | if (chunk.size() > shortprefix.size() + 1) 2571 | { 2572 | auto arg = chunk.at(shortprefix.size()); 2573 | //TODO: support -abcVALUE where a and b take no value 2574 | if (auto flag = this->Match(arg)) 2575 | { 2576 | for (auto &choice : flag->HelpChoices(helpParams)) 2577 | { 2578 | AddCompletionReply(chunk, shortprefix + arg + choice); 2579 | } 2580 | } 2581 | } 2582 | } 2583 | } 2584 | } 2585 | 2586 | #ifndef ARGS_NOEXCEPT 2587 | throw Completion(completion->Get()); 2588 | #else 2589 | return true; 2590 | #endif 2591 | } 2592 | 2593 | template 2594 | It Parse(It begin, It end) 2595 | { 2596 | bool terminated = false; 2597 | std::vector commands = GetCommands(); 2598 | 2599 | // Check all arg chunks 2600 | for (auto it = begin; it != end; ++it) 2601 | { 2602 | if (Complete(it, end)) 2603 | { 2604 | return end; 2605 | } 2606 | 2607 | const auto &chunk = *it; 2608 | 2609 | if (!terminated && chunk == terminator) 2610 | { 2611 | terminated = true; 2612 | } else if (!terminated && ParseOption(chunk) == OptionType::LongFlag) 2613 | { 2614 | if (!ParseLong(it, end)) 2615 | { 2616 | return it; 2617 | } 2618 | } else if (!terminated && ParseOption(chunk) == OptionType::ShortFlag) 2619 | { 2620 | if (!ParseShort(it, end)) 2621 | { 2622 | return it; 2623 | } 2624 | } else if (!terminated && !commands.empty()) 2625 | { 2626 | auto itCommand = std::find_if(commands.begin(), commands.end(), [&chunk](Command *c) { return c->Name() == chunk; }); 2627 | if (itCommand == commands.end()) 2628 | { 2629 | const std::string errorMessage("Unknown command: " + chunk); 2630 | #ifndef ARGS_NOEXCEPT 2631 | throw ParseError(errorMessage); 2632 | #else 2633 | error = Error::Parse; 2634 | errorMsg = errorMessage; 2635 | return it; 2636 | #endif 2637 | } 2638 | 2639 | SelectCommand(*itCommand); 2640 | 2641 | if (const auto &coroutine = GetCoroutine()) 2642 | { 2643 | ++it; 2644 | RaiiSubparser coro(*this, std::vector(it, end)); 2645 | coroutine(coro.Parser()); 2646 | #ifdef ARGS_NOEXCEPT 2647 | error = GetError(); 2648 | if (error != Error::None) 2649 | { 2650 | return end; 2651 | } 2652 | 2653 | if (!coro.Parser().IsParsed()) 2654 | { 2655 | error = Error::Usage; 2656 | return end; 2657 | } 2658 | #else 2659 | if (!coro.Parser().IsParsed()) 2660 | { 2661 | throw UsageError("Subparser::Parse was not called"); 2662 | } 2663 | #endif 2664 | 2665 | break; 2666 | } 2667 | 2668 | commands = GetCommands(); 2669 | } else 2670 | { 2671 | auto pos = GetNextPositional(); 2672 | if (pos) 2673 | { 2674 | pos->ParseValue(chunk); 2675 | 2676 | if (pos->KickOut()) 2677 | { 2678 | return ++it; 2679 | } 2680 | } else 2681 | { 2682 | const std::string errorMessage("Passed in argument, but no positional arguments were ready to receive it: " + chunk); 2683 | #ifndef ARGS_NOEXCEPT 2684 | throw ParseError(errorMessage); 2685 | #else 2686 | error = Error::Parse; 2687 | errorMsg = errorMessage; 2688 | return it; 2689 | #endif 2690 | } 2691 | } 2692 | 2693 | if (!readCompletion && completion != nullptr && completion->Matched()) 2694 | { 2695 | #ifdef ARGS_NOEXCEPT 2696 | error = Error::Completion; 2697 | #endif 2698 | readCompletion = true; 2699 | ++it; 2700 | size_t argsLeft = std::distance(it, end); 2701 | if (completion->cword == 0 || argsLeft <= 1 || completion->cword >= argsLeft) 2702 | { 2703 | #ifndef ARGS_NOEXCEPT 2704 | throw Completion(""); 2705 | #endif 2706 | } 2707 | 2708 | std::vector curArgs(++it, end); 2709 | curArgs.resize(completion->cword); 2710 | 2711 | if (completion->syntax == "bash") 2712 | { 2713 | // bash tokenizes --flag=value as --flag=value 2714 | for (size_t idx = 0; idx < curArgs.size(); ) 2715 | { 2716 | if (idx > 0 && curArgs[idx] == "=") 2717 | { 2718 | curArgs[idx - 1] += "="; 2719 | if (idx + 1 < curArgs.size()) 2720 | { 2721 | curArgs[idx - 1] += curArgs[idx + 1]; 2722 | curArgs.erase(curArgs.begin() + idx, curArgs.begin() + idx + 2); 2723 | } else 2724 | { 2725 | curArgs.erase(curArgs.begin() + idx); 2726 | } 2727 | } else 2728 | { 2729 | ++idx; 2730 | } 2731 | } 2732 | 2733 | } 2734 | #ifndef ARGS_NOEXCEPT 2735 | try 2736 | { 2737 | Parse(curArgs.begin(), curArgs.end()); 2738 | throw Completion(""); 2739 | } 2740 | catch (Completion &) 2741 | { 2742 | throw; 2743 | } 2744 | catch (args::Error&) 2745 | { 2746 | throw Completion(""); 2747 | } 2748 | #else 2749 | return Parse(curArgs.begin(), curArgs.end()); 2750 | #endif 2751 | } 2752 | } 2753 | 2754 | Validate(shortprefix, longprefix); 2755 | return end; 2756 | } 2757 | 2758 | public: 2759 | HelpParams helpParams; 2760 | 2761 | ArgumentParser(const std::string &description_, const std::string &epilog_ = std::string()) 2762 | { 2763 | Description(description_); 2764 | Epilog(epilog_); 2765 | LongPrefix("--"); 2766 | ShortPrefix("-"); 2767 | LongSeparator("="); 2768 | Terminator("--"); 2769 | SetArgumentSeparations(true, true, true, true); 2770 | matched = true; 2771 | } 2772 | 2773 | void AddCompletion(CompletionFlag &completionFlag) 2774 | { 2775 | completion = &completionFlag; 2776 | Add(completionFlag); 2777 | } 2778 | 2779 | /** The program name for help generation 2780 | */ 2781 | const std::string &Prog() const 2782 | { return helpParams.programName; } 2783 | /** The program name for help generation 2784 | */ 2785 | void Prog(const std::string &prog_) 2786 | { this->helpParams.programName = prog_; } 2787 | 2788 | /** The prefix for long flags 2789 | */ 2790 | const std::string &LongPrefix() const 2791 | { return longprefix; } 2792 | /** The prefix for long flags 2793 | */ 2794 | void LongPrefix(const std::string &longprefix_) 2795 | { 2796 | this->longprefix = longprefix_; 2797 | this->helpParams.longPrefix = longprefix_; 2798 | } 2799 | 2800 | /** The prefix for short flags 2801 | */ 2802 | const std::string &ShortPrefix() const 2803 | { return shortprefix; } 2804 | /** The prefix for short flags 2805 | */ 2806 | void ShortPrefix(const std::string &shortprefix_) 2807 | { 2808 | this->shortprefix = shortprefix_; 2809 | this->helpParams.shortPrefix = shortprefix_; 2810 | } 2811 | 2812 | /** The separator for long flags 2813 | */ 2814 | const std::string &LongSeparator() const 2815 | { return longseparator; } 2816 | /** The separator for long flags 2817 | */ 2818 | void LongSeparator(const std::string &longseparator_) 2819 | { 2820 | if (longseparator_.empty()) 2821 | { 2822 | const std::string errorMessage("longseparator can not be set to empty"); 2823 | #ifdef ARGS_NOEXCEPT 2824 | error = Error::Usage; 2825 | errorMsg = errorMessage; 2826 | #else 2827 | throw UsageError(errorMessage); 2828 | #endif 2829 | } else 2830 | { 2831 | this->longseparator = longseparator_; 2832 | this->helpParams.longSeparator = allowJoinedLongValue ? longseparator_ : " "; 2833 | } 2834 | } 2835 | 2836 | /** The terminator that forcibly separates flags from positionals 2837 | */ 2838 | const std::string &Terminator() const 2839 | { return terminator; } 2840 | /** The terminator that forcibly separates flags from positionals 2841 | */ 2842 | void Terminator(const std::string &terminator_) 2843 | { this->terminator = terminator_; } 2844 | 2845 | /** Get the current argument separation parameters. 2846 | * 2847 | * See SetArgumentSeparations for details on what each one means. 2848 | */ 2849 | void GetArgumentSeparations( 2850 | bool &allowJoinedShortValue_, 2851 | bool &allowJoinedLongValue_, 2852 | bool &allowSeparateShortValue_, 2853 | bool &allowSeparateLongValue_) const 2854 | { 2855 | allowJoinedShortValue_ = this->allowJoinedShortValue; 2856 | allowJoinedLongValue_ = this->allowJoinedLongValue; 2857 | allowSeparateShortValue_ = this->allowSeparateShortValue; 2858 | allowSeparateLongValue_ = this->allowSeparateLongValue; 2859 | } 2860 | 2861 | /** Change allowed option separation. 2862 | * 2863 | * \param allowJoinedShortValue_ Allow a short flag that accepts an argument to be passed its argument immediately next to it (ie. in the same argv field) 2864 | * \param allowJoinedLongValue_ Allow a long flag that accepts an argument to be passed its argument separated by the longseparator (ie. in the same argv field) 2865 | * \param allowSeparateShortValue_ Allow a short flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field) 2866 | * \param allowSeparateLongValue_ Allow a long flag that accepts an argument to be passed its argument separated by whitespace (ie. in the next argv field) 2867 | */ 2868 | void SetArgumentSeparations( 2869 | const bool allowJoinedShortValue_, 2870 | const bool allowJoinedLongValue_, 2871 | const bool allowSeparateShortValue_, 2872 | const bool allowSeparateLongValue_) 2873 | { 2874 | this->allowJoinedShortValue = allowJoinedShortValue_; 2875 | this->allowJoinedLongValue = allowJoinedLongValue_; 2876 | this->allowSeparateShortValue = allowSeparateShortValue_; 2877 | this->allowSeparateLongValue = allowSeparateLongValue_; 2878 | 2879 | this->helpParams.longSeparator = allowJoinedLongValue ? longseparator : " "; 2880 | this->helpParams.shortSeparator = allowJoinedShortValue ? "" : " "; 2881 | } 2882 | 2883 | /** Pass the help menu into an ostream 2884 | */ 2885 | void Help(std::ostream &help_) const 2886 | { 2887 | auto &command = SelectedCommand(); 2888 | const auto &commandDescription = command.Description().empty() ? command.Help() : command.Description(); 2889 | const auto description_text = Wrap(commandDescription, helpParams.width - helpParams.descriptionindent); 2890 | const auto epilog_text = Wrap(command.Epilog(), helpParams.width - helpParams.descriptionindent); 2891 | 2892 | const bool hasoptions = command.HasFlag(); 2893 | const bool hasarguments = command.HasPositional(); 2894 | 2895 | std::vector prognameline; 2896 | prognameline.push_back(helpParams.usageString); 2897 | prognameline.push_back(Prog()); 2898 | auto commandProgLine = command.GetProgramLine(helpParams); 2899 | prognameline.insert(prognameline.end(), commandProgLine.begin(), commandProgLine.end()); 2900 | 2901 | const auto proglines = Wrap(prognameline.begin(), prognameline.end(), 2902 | helpParams.width - (helpParams.progindent + helpParams.progtailindent), 2903 | helpParams.width - helpParams.progindent); 2904 | auto progit = std::begin(proglines); 2905 | if (progit != std::end(proglines)) 2906 | { 2907 | help_ << std::string(helpParams.progindent, ' ') << *progit << '\n'; 2908 | ++progit; 2909 | } 2910 | for (; progit != std::end(proglines); ++progit) 2911 | { 2912 | help_ << std::string(helpParams.progtailindent, ' ') << *progit << '\n'; 2913 | } 2914 | 2915 | help_ << '\n'; 2916 | 2917 | if (!description_text.empty()) 2918 | { 2919 | for (const auto &line: description_text) 2920 | { 2921 | help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n"; 2922 | } 2923 | help_ << "\n"; 2924 | } 2925 | 2926 | bool lastDescriptionIsNewline = false; 2927 | 2928 | if (!helpParams.optionsString.empty()) 2929 | { 2930 | help_ << std::string(helpParams.progindent, ' ') << helpParams.optionsString << "\n\n"; 2931 | } 2932 | 2933 | for (const auto &desc: command.GetDescription(helpParams, 0)) 2934 | { 2935 | lastDescriptionIsNewline = std::get<0>(desc).empty() && std::get<1>(desc).empty(); 2936 | const auto groupindent = std::get<2>(desc) * helpParams.eachgroupindent; 2937 | const auto flags = Wrap(std::get<0>(desc), helpParams.width - (helpParams.flagindent + helpParams.helpindent + helpParams.gutter)); 2938 | const auto info = Wrap(std::get<1>(desc), helpParams.width - (helpParams.helpindent + groupindent)); 2939 | 2940 | std::string::size_type flagssize = 0; 2941 | for (auto flagsit = std::begin(flags); flagsit != std::end(flags); ++flagsit) 2942 | { 2943 | if (flagsit != std::begin(flags)) 2944 | { 2945 | help_ << '\n'; 2946 | } 2947 | help_ << std::string(groupindent + helpParams.flagindent, ' ') << *flagsit; 2948 | flagssize = Glyphs(*flagsit); 2949 | } 2950 | 2951 | auto infoit = std::begin(info); 2952 | // groupindent is on both sides of this inequality, and therefore can be removed 2953 | if ((helpParams.flagindent + flagssize + helpParams.gutter) > helpParams.helpindent || infoit == std::end(info) || helpParams.addNewlineBeforeDescription) 2954 | { 2955 | help_ << '\n'; 2956 | } else 2957 | { 2958 | // groupindent is on both sides of the minus sign, and therefore doesn't actually need to be in here 2959 | help_ << std::string(helpParams.helpindent - (helpParams.flagindent + flagssize), ' ') << *infoit << '\n'; 2960 | ++infoit; 2961 | } 2962 | for (; infoit != std::end(info); ++infoit) 2963 | { 2964 | help_ << std::string(groupindent + helpParams.helpindent, ' ') << *infoit << '\n'; 2965 | } 2966 | } 2967 | if (hasoptions && hasarguments && helpParams.showTerminator) 2968 | { 2969 | lastDescriptionIsNewline = false; 2970 | for (const auto &item: Wrap(std::string("\"") + terminator + "\" can be used to terminate flag options and force all following arguments to be treated as positional options", helpParams.width - helpParams.flagindent)) 2971 | { 2972 | help_ << std::string(helpParams.flagindent, ' ') << item << '\n'; 2973 | } 2974 | } 2975 | 2976 | if (!lastDescriptionIsNewline) 2977 | { 2978 | help_ << "\n"; 2979 | } 2980 | 2981 | for (const auto &line: epilog_text) 2982 | { 2983 | help_ << std::string(helpParams.descriptionindent, ' ') << line << "\n"; 2984 | } 2985 | } 2986 | 2987 | /** Generate a help menu as a string. 2988 | * 2989 | * \return the help text as a single string 2990 | */ 2991 | std::string Help() const 2992 | { 2993 | std::ostringstream help_; 2994 | Help(help_); 2995 | return help_.str(); 2996 | } 2997 | 2998 | virtual void Reset() noexcept override 2999 | { 3000 | Command::Reset(); 3001 | matched = true; 3002 | readCompletion = false; 3003 | } 3004 | 3005 | /** Parse all arguments. 3006 | * 3007 | * \param begin an iterator to the beginning of the argument list 3008 | * \param end an iterator to the past-the-end element of the argument list 3009 | * \return the iterator after the last parsed value. Only useful for kick-out 3010 | */ 3011 | template 3012 | It ParseArgs(It begin, It end) 3013 | { 3014 | // Reset all Matched statuses and errors 3015 | Reset(); 3016 | #ifdef ARGS_NOEXCEPT 3017 | error = GetError(); 3018 | if (error != Error::None) 3019 | { 3020 | return end; 3021 | } 3022 | #endif 3023 | return Parse(begin, end); 3024 | } 3025 | 3026 | /** Parse all arguments. 3027 | * 3028 | * \param args an iterable of the arguments 3029 | * \return the iterator after the last parsed value. Only useful for kick-out 3030 | */ 3031 | template 3032 | auto ParseArgs(const T &args) -> decltype(std::begin(args)) 3033 | { 3034 | return ParseArgs(std::begin(args), std::end(args)); 3035 | } 3036 | 3037 | /** Convenience function to parse the CLI from argc and argv 3038 | * 3039 | * Just assigns the program name and vectorizes arguments for passing into ParseArgs() 3040 | * 3041 | * \return whether or not all arguments were parsed. This works for detecting kick-out, but is generally useless as it can't do anything with it. 3042 | */ 3043 | bool ParseCLI(const int argc, const char * const * argv) 3044 | { 3045 | if (Prog().empty()) 3046 | { 3047 | Prog(argv[0]); 3048 | } 3049 | const std::vector args(argv + 1, argv + argc); 3050 | return ParseArgs(args) == std::end(args); 3051 | } 3052 | 3053 | template 3054 | bool ParseCLI(const T &args) 3055 | { 3056 | return ParseArgs(args) == std::end(args); 3057 | } 3058 | }; 3059 | 3060 | inline Command::RaiiSubparser::RaiiSubparser(ArgumentParser &parser_, std::vector args_) 3061 | : command(parser_.SelectedCommand()), parser(std::move(args_), parser_, command, parser_.helpParams), oldSubparser(command.subparser) 3062 | { 3063 | command.subparser = &parser; 3064 | } 3065 | 3066 | inline Command::RaiiSubparser::RaiiSubparser(const Command &command_, const HelpParams ¶ms_): command(command_), parser(command, params_), oldSubparser(command.subparser) 3067 | { 3068 | command.subparser = &parser; 3069 | } 3070 | 3071 | inline void Subparser::Parse() 3072 | { 3073 | isParsed = true; 3074 | Reset(); 3075 | command.subparserDescription = GetDescription(helpParams, 0); 3076 | command.subparserHasFlag = HasFlag(); 3077 | command.subparserHasPositional = HasPositional(); 3078 | command.subparserHasCommand = HasCommand(); 3079 | command.subparserProgramLine = GetProgramLine(helpParams); 3080 | if (parser == nullptr) 3081 | { 3082 | #ifndef ARGS_NOEXCEPT 3083 | throw args::SubparserError(); 3084 | #else 3085 | error = Error::Subparser; 3086 | return; 3087 | #endif 3088 | } 3089 | 3090 | auto it = parser->Parse(args.begin(), args.end()); 3091 | command.Validate(parser->ShortPrefix(), parser->LongPrefix()); 3092 | kicked.assign(it, args.end()); 3093 | 3094 | #ifdef ARGS_NOEXCEPT 3095 | command.subparserError = GetError(); 3096 | #endif 3097 | } 3098 | 3099 | inline std::ostream &operator<<(std::ostream &os, const ArgumentParser &parser) 3100 | { 3101 | parser.Help(os); 3102 | return os; 3103 | } 3104 | 3105 | /** Boolean argument matcher 3106 | */ 3107 | class Flag : public FlagBase 3108 | { 3109 | public: 3110 | Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): FlagBase(name_, help_, std::move(matcher_), options_) 3111 | { 3112 | group_.Add(*this); 3113 | } 3114 | 3115 | Flag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const bool extraError_ = false): Flag(group_, name_, help_, std::move(matcher_), extraError_ ? Options::Single : Options::None) 3116 | { 3117 | } 3118 | 3119 | virtual ~Flag() {} 3120 | 3121 | /** Get whether this was matched 3122 | */ 3123 | bool Get() const 3124 | { 3125 | return Matched(); 3126 | } 3127 | 3128 | virtual Nargs NumberOfArguments() const noexcept override 3129 | { 3130 | return 0; 3131 | } 3132 | 3133 | virtual void ParseValue(const std::vector&) override 3134 | { 3135 | } 3136 | }; 3137 | 3138 | /** Help flag class 3139 | * 3140 | * Works like a regular flag, but throws an instance of Help when it is matched 3141 | */ 3142 | class HelpFlag : public Flag 3143 | { 3144 | public: 3145 | HelpFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_ = {}): Flag(group_, name_, help_, std::move(matcher_), options_) {} 3146 | 3147 | virtual ~HelpFlag() {} 3148 | 3149 | virtual void ParseValue(const std::vector &) 3150 | { 3151 | #ifdef ARGS_NOEXCEPT 3152 | error = Error::Help; 3153 | errorMsg = Name(); 3154 | #else 3155 | throw Help(Name()); 3156 | #endif 3157 | } 3158 | 3159 | /** Get whether this was matched 3160 | */ 3161 | bool Get() const noexcept 3162 | { 3163 | return Matched(); 3164 | } 3165 | }; 3166 | 3167 | /** A flag class that simply counts the number of times it's matched 3168 | */ 3169 | class CounterFlag : public Flag 3170 | { 3171 | private: 3172 | const int startcount; 3173 | int count; 3174 | 3175 | public: 3176 | CounterFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const int startcount_ = 0, Options options_ = {}): 3177 | Flag(group_, name_, help_, std::move(matcher_), options_), startcount(startcount_), count(startcount_) {} 3178 | 3179 | virtual ~CounterFlag() {} 3180 | 3181 | virtual FlagBase *Match(const EitherFlag &arg) override 3182 | { 3183 | auto me = FlagBase::Match(arg); 3184 | if (me) 3185 | { 3186 | ++count; 3187 | } 3188 | return me; 3189 | } 3190 | 3191 | /** Get the count 3192 | */ 3193 | int &Get() noexcept 3194 | { 3195 | return count; 3196 | } 3197 | 3198 | virtual void Reset() noexcept override 3199 | { 3200 | FlagBase::Reset(); 3201 | count = startcount; 3202 | } 3203 | }; 3204 | 3205 | /** A flag class that calls a function when it's matched 3206 | */ 3207 | class ActionFlag : public FlagBase 3208 | { 3209 | private: 3210 | std::function &)> action; 3211 | Nargs nargs; 3212 | 3213 | public: 3214 | ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, std::function &)> action_, Options options_ = {}): 3215 | FlagBase(name_, help_, std::move(matcher_), options_), action(std::move(action_)), nargs(nargs_) 3216 | { 3217 | group_.Add(*this); 3218 | } 3219 | 3220 | ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function action_, Options options_ = {}): 3221 | FlagBase(name_, help_, std::move(matcher_), options_), nargs(1) 3222 | { 3223 | group_.Add(*this); 3224 | action = [action_](const std::vector &a) { return action_(a.at(0)); }; 3225 | } 3226 | 3227 | ActionFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, std::function action_, Options options_ = {}): 3228 | FlagBase(name_, help_, std::move(matcher_), options_), nargs(0) 3229 | { 3230 | group_.Add(*this); 3231 | action = [action_](const std::vector &) { return action_(); }; 3232 | } 3233 | 3234 | virtual Nargs NumberOfArguments() const noexcept override 3235 | { return nargs; } 3236 | 3237 | virtual void ParseValue(const std::vector &value) override 3238 | { action(value); } 3239 | }; 3240 | 3241 | /** A default Reader class for argument classes 3242 | * 3243 | * If destination type is assignable to std::string it uses an assignment to std::string. 3244 | * Otherwise ValueReader simply uses a std::istringstream to read into the destination type, and 3245 | * raises a ParseError if there are any characters left. 3246 | */ 3247 | struct ValueReader 3248 | { 3249 | template 3250 | typename std::enable_if::value, bool>::type 3251 | operator ()(const std::string &name, const std::string &value, T &destination) 3252 | { 3253 | std::istringstream ss(value); 3254 | ss >> destination >> std::ws; 3255 | 3256 | if (ss.rdbuf()->in_avail() > 0) 3257 | { 3258 | #ifdef ARGS_NOEXCEPT 3259 | (void)name; 3260 | return false; 3261 | #else 3262 | std::ostringstream problem; 3263 | problem << "Argument '" << name << "' received invalid value type '" << value << "'"; 3264 | throw ParseError(problem.str()); 3265 | #endif 3266 | } 3267 | return true; 3268 | } 3269 | 3270 | template 3271 | typename std::enable_if::value, bool>::type 3272 | operator()(const std::string &, const std::string &value, T &destination) 3273 | { 3274 | destination = value; 3275 | return true; 3276 | } 3277 | }; 3278 | 3279 | /** An argument-accepting flag class 3280 | * 3281 | * \tparam T the type to extract the argument as 3282 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 3283 | */ 3284 | template < 3285 | typename T, 3286 | typename Reader = ValueReader> 3287 | class ValueFlag : public ValueFlagBase 3288 | { 3289 | protected: 3290 | T value; 3291 | T defaultValue; 3292 | 3293 | virtual std::string GetDefaultString(const HelpParams&) const override 3294 | { 3295 | return detail::ToString(defaultValue); 3296 | } 3297 | 3298 | private: 3299 | Reader reader; 3300 | 3301 | public: 3302 | 3303 | ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), value(defaultValue_), defaultValue(defaultValue_) 3304 | { 3305 | group_.Add(*this); 3306 | } 3307 | 3308 | ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), const bool extraError_ = false): ValueFlag(group_, name_, help_, std::move(matcher_), defaultValue_, extraError_ ? Options::Single : Options::None) 3309 | { 3310 | } 3311 | 3312 | ValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_): ValueFlag(group_, name_, help_, std::move(matcher_), T(), options_) 3313 | { 3314 | } 3315 | 3316 | virtual ~ValueFlag() {} 3317 | 3318 | virtual void ParseValue(const std::vector &values_) override 3319 | { 3320 | const std::string &value_ = values_.at(0); 3321 | 3322 | #ifdef ARGS_NOEXCEPT 3323 | if (!reader(name, value_, this->value)) 3324 | { 3325 | error = Error::Parse; 3326 | } 3327 | #else 3328 | reader(name, value_, this->value); 3329 | #endif 3330 | } 3331 | 3332 | virtual void Reset() noexcept override 3333 | { 3334 | ValueFlagBase::Reset(); 3335 | value = defaultValue; 3336 | } 3337 | 3338 | /** Get the value 3339 | */ 3340 | T &Get() noexcept 3341 | { 3342 | return value; 3343 | } 3344 | 3345 | /** Get the default value 3346 | */ 3347 | const T &GetDefault() noexcept 3348 | { 3349 | return defaultValue; 3350 | } 3351 | }; 3352 | 3353 | /** An optional argument-accepting flag class 3354 | * 3355 | * \tparam T the type to extract the argument as 3356 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 3357 | */ 3358 | template < 3359 | typename T, 3360 | typename Reader = ValueReader> 3361 | class ImplicitValueFlag : public ValueFlag 3362 | { 3363 | protected: 3364 | T implicitValue; 3365 | 3366 | public: 3367 | 3368 | ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &implicitValue_, const T &defaultValue_ = T(), Options options_ = {}) 3369 | : ValueFlag(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(implicitValue_) 3370 | { 3371 | } 3372 | 3373 | ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const T &defaultValue_ = T(), Options options_ = {}) 3374 | : ValueFlag(group_, name_, help_, std::move(matcher_), defaultValue_, options_), implicitValue(defaultValue_) 3375 | { 3376 | } 3377 | 3378 | ImplicitValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Options options_) 3379 | : ValueFlag(group_, name_, help_, std::move(matcher_), {}, options_), implicitValue() 3380 | { 3381 | } 3382 | 3383 | virtual ~ImplicitValueFlag() {} 3384 | 3385 | virtual Nargs NumberOfArguments() const noexcept override 3386 | { 3387 | return {0, 1}; 3388 | } 3389 | 3390 | virtual void ParseValue(const std::vector &value_) override 3391 | { 3392 | if (value_.empty()) 3393 | { 3394 | this->value = implicitValue; 3395 | } else 3396 | { 3397 | ValueFlag::ParseValue(value_); 3398 | } 3399 | } 3400 | }; 3401 | 3402 | /** A variadic arguments accepting flag class 3403 | * 3404 | * \tparam T the type to extract the argument as 3405 | * \tparam List the list type that houses the values 3406 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 3407 | */ 3408 | template < 3409 | typename T, 3410 | template class List = std::vector, 3411 | typename Reader = ValueReader> 3412 | class NargsValueFlag : public FlagBase 3413 | { 3414 | protected: 3415 | 3416 | List values; 3417 | Nargs nargs; 3418 | Reader reader; 3419 | 3420 | public: 3421 | 3422 | typedef List Container; 3423 | typedef T value_type; 3424 | typedef typename Container::allocator_type allocator_type; 3425 | typedef typename Container::pointer pointer; 3426 | typedef typename Container::const_pointer const_pointer; 3427 | typedef T& reference; 3428 | typedef const T& const_reference; 3429 | typedef typename Container::size_type size_type; 3430 | typedef typename Container::difference_type difference_type; 3431 | typedef typename Container::iterator iterator; 3432 | typedef typename Container::const_iterator const_iterator; 3433 | typedef std::reverse_iterator reverse_iterator; 3434 | typedef std::reverse_iterator const_reverse_iterator; 3435 | 3436 | NargsValueFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, Nargs nargs_, const List &defaultValues_ = {}, Options options_ = {}) 3437 | : FlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_), nargs(nargs_) 3438 | { 3439 | group_.Add(*this); 3440 | } 3441 | 3442 | virtual ~NargsValueFlag() {} 3443 | 3444 | virtual Nargs NumberOfArguments() const noexcept override 3445 | { 3446 | return nargs; 3447 | } 3448 | 3449 | virtual void ParseValue(const std::vector &values_) override 3450 | { 3451 | values.clear(); 3452 | 3453 | for (const std::string &value : values_) 3454 | { 3455 | T v; 3456 | #ifdef ARGS_NOEXCEPT 3457 | if (!reader(name, value, v)) 3458 | { 3459 | error = Error::Parse; 3460 | } 3461 | #else 3462 | reader(name, value, v); 3463 | #endif 3464 | values.insert(std::end(values), v); 3465 | } 3466 | } 3467 | 3468 | List &Get() noexcept 3469 | { 3470 | return values; 3471 | } 3472 | 3473 | iterator begin() noexcept 3474 | { 3475 | return values.begin(); 3476 | } 3477 | 3478 | const_iterator begin() const noexcept 3479 | { 3480 | return values.begin(); 3481 | } 3482 | 3483 | const_iterator cbegin() const noexcept 3484 | { 3485 | return values.cbegin(); 3486 | } 3487 | 3488 | iterator end() noexcept 3489 | { 3490 | return values.end(); 3491 | } 3492 | 3493 | const_iterator end() const noexcept 3494 | { 3495 | return values.end(); 3496 | } 3497 | 3498 | const_iterator cend() const noexcept 3499 | { 3500 | return values.cend(); 3501 | } 3502 | }; 3503 | 3504 | /** An argument-accepting flag class that pushes the found values into a list 3505 | * 3506 | * \tparam T the type to extract the argument as 3507 | * \tparam List the list type that houses the values 3508 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 3509 | */ 3510 | template < 3511 | typename T, 3512 | template class List = std::vector, 3513 | typename Reader = ValueReader> 3514 | class ValueFlagList : public ValueFlagBase 3515 | { 3516 | private: 3517 | using Container = List; 3518 | Container values; 3519 | Reader reader; 3520 | 3521 | public: 3522 | 3523 | typedef T value_type; 3524 | typedef typename Container::allocator_type allocator_type; 3525 | typedef typename Container::pointer pointer; 3526 | typedef typename Container::const_pointer const_pointer; 3527 | typedef T& reference; 3528 | typedef const T& const_reference; 3529 | typedef typename Container::size_type size_type; 3530 | typedef typename Container::difference_type difference_type; 3531 | typedef typename Container::iterator iterator; 3532 | typedef typename Container::const_iterator const_iterator; 3533 | typedef std::reverse_iterator reverse_iterator; 3534 | typedef std::reverse_iterator const_reverse_iterator; 3535 | 3536 | ValueFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Container &defaultValues_ = Container(), Options options_ = {}): 3537 | ValueFlagBase(name_, help_, std::move(matcher_), options_), values(defaultValues_) 3538 | { 3539 | group_.Add(*this); 3540 | } 3541 | 3542 | virtual ~ValueFlagList() {} 3543 | 3544 | virtual void ParseValue(const std::vector &values_) override 3545 | { 3546 | const std::string &value_ = values_.at(0); 3547 | 3548 | T v; 3549 | #ifdef ARGS_NOEXCEPT 3550 | if (!reader(name, value_, v)) 3551 | { 3552 | error = Error::Parse; 3553 | } 3554 | #else 3555 | reader(name, value_, v); 3556 | #endif 3557 | values.insert(std::end(values), v); 3558 | } 3559 | 3560 | /** Get the values 3561 | */ 3562 | Container &Get() noexcept 3563 | { 3564 | return values; 3565 | } 3566 | 3567 | virtual std::string Name() const override 3568 | { 3569 | return name + std::string("..."); 3570 | } 3571 | 3572 | virtual void Reset() noexcept override 3573 | { 3574 | ValueFlagBase::Reset(); 3575 | values.clear(); 3576 | } 3577 | 3578 | iterator begin() noexcept 3579 | { 3580 | return values.begin(); 3581 | } 3582 | 3583 | const_iterator begin() const noexcept 3584 | { 3585 | return values.begin(); 3586 | } 3587 | 3588 | const_iterator cbegin() const noexcept 3589 | { 3590 | return values.cbegin(); 3591 | } 3592 | 3593 | iterator end() noexcept 3594 | { 3595 | return values.end(); 3596 | } 3597 | 3598 | const_iterator end() const noexcept 3599 | { 3600 | return values.end(); 3601 | } 3602 | 3603 | const_iterator cend() const noexcept 3604 | { 3605 | return values.cend(); 3606 | } 3607 | }; 3608 | 3609 | /** A mapping value flag class 3610 | * 3611 | * \tparam K the type to extract the argument as 3612 | * \tparam T the type to store the result as 3613 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 3614 | * \tparam Map The Map type. Should operate like std::map or std::unordered_map 3615 | */ 3616 | template < 3617 | typename K, 3618 | typename T, 3619 | typename Reader = ValueReader, 3620 | template class Map = std::unordered_map> 3621 | class MapFlag : public ValueFlagBase 3622 | { 3623 | private: 3624 | const Map map; 3625 | T value; 3626 | Reader reader; 3627 | 3628 | protected: 3629 | virtual std::vector GetChoicesStrings(const HelpParams &) const override 3630 | { 3631 | return detail::MapKeysToStrings(map); 3632 | } 3633 | 3634 | public: 3635 | 3636 | MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map &map_, const T &defaultValue_, Options options_): ValueFlagBase(name_, help_, std::move(matcher_), options_), map(map_), value(defaultValue_) 3637 | { 3638 | group_.Add(*this); 3639 | } 3640 | 3641 | MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map &map_, const T &defaultValue_ = T(), const bool extraError_ = false): MapFlag(group_, name_, help_, std::move(matcher_), map_, defaultValue_, extraError_ ? Options::Single : Options::None) 3642 | { 3643 | } 3644 | 3645 | MapFlag(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map &map_, Options options_): MapFlag(group_, name_, help_, std::move(matcher_), map_, T(), options_) 3646 | { 3647 | } 3648 | 3649 | virtual ~MapFlag() {} 3650 | 3651 | virtual void ParseValue(const std::vector &values_) override 3652 | { 3653 | const std::string &value_ = values_.at(0); 3654 | 3655 | K key; 3656 | #ifdef ARGS_NOEXCEPT 3657 | if (!reader(name, value_, key)) 3658 | { 3659 | error = Error::Parse; 3660 | } 3661 | #else 3662 | reader(name, value_, key); 3663 | #endif 3664 | auto it = map.find(key); 3665 | if (it == std::end(map)) 3666 | { 3667 | std::ostringstream problem; 3668 | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; 3669 | #ifdef ARGS_NOEXCEPT 3670 | error = Error::Map; 3671 | errorMsg = problem.str(); 3672 | #else 3673 | throw MapError(problem.str()); 3674 | #endif 3675 | } else 3676 | { 3677 | this->value = it->second; 3678 | } 3679 | } 3680 | 3681 | /** Get the value 3682 | */ 3683 | T &Get() noexcept 3684 | { 3685 | return value; 3686 | } 3687 | }; 3688 | 3689 | /** A mapping value flag list class 3690 | * 3691 | * \tparam K the type to extract the argument as 3692 | * \tparam T the type to store the result as 3693 | * \tparam List the list type that houses the values 3694 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 3695 | * \tparam Map The Map type. Should operate like std::map or std::unordered_map 3696 | */ 3697 | template < 3698 | typename K, 3699 | typename T, 3700 | template class List = std::vector, 3701 | typename Reader = ValueReader, 3702 | template class Map = std::unordered_map> 3703 | class MapFlagList : public ValueFlagBase 3704 | { 3705 | private: 3706 | using Container = List; 3707 | const Map map; 3708 | Container values; 3709 | Reader reader; 3710 | 3711 | protected: 3712 | virtual std::vector GetChoicesStrings(const HelpParams &) const override 3713 | { 3714 | return detail::MapKeysToStrings(map); 3715 | } 3716 | 3717 | public: 3718 | typedef T value_type; 3719 | typedef typename Container::allocator_type allocator_type; 3720 | typedef typename Container::pointer pointer; 3721 | typedef typename Container::const_pointer const_pointer; 3722 | typedef T& reference; 3723 | typedef const T& const_reference; 3724 | typedef typename Container::size_type size_type; 3725 | typedef typename Container::difference_type difference_type; 3726 | typedef typename Container::iterator iterator; 3727 | typedef typename Container::const_iterator const_iterator; 3728 | typedef std::reverse_iterator reverse_iterator; 3729 | typedef std::reverse_iterator const_reverse_iterator; 3730 | 3731 | MapFlagList(Group &group_, const std::string &name_, const std::string &help_, Matcher &&matcher_, const Map &map_, const Container &defaultValues_ = Container()): ValueFlagBase(name_, help_, std::move(matcher_)), map(map_), values(defaultValues_) 3732 | { 3733 | group_.Add(*this); 3734 | } 3735 | 3736 | virtual ~MapFlagList() {} 3737 | 3738 | virtual void ParseValue(const std::vector &values_) override 3739 | { 3740 | const std::string &value = values_.at(0); 3741 | 3742 | K key; 3743 | #ifdef ARGS_NOEXCEPT 3744 | if (!reader(name, value, key)) 3745 | { 3746 | error = Error::Parse; 3747 | } 3748 | #else 3749 | reader(name, value, key); 3750 | #endif 3751 | auto it = map.find(key); 3752 | if (it == std::end(map)) 3753 | { 3754 | std::ostringstream problem; 3755 | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; 3756 | #ifdef ARGS_NOEXCEPT 3757 | error = Error::Map; 3758 | errorMsg = problem.str(); 3759 | #else 3760 | throw MapError(problem.str()); 3761 | #endif 3762 | } else 3763 | { 3764 | this->values.emplace_back(it->second); 3765 | } 3766 | } 3767 | 3768 | /** Get the value 3769 | */ 3770 | Container &Get() noexcept 3771 | { 3772 | return values; 3773 | } 3774 | 3775 | virtual std::string Name() const override 3776 | { 3777 | return name + std::string("..."); 3778 | } 3779 | 3780 | virtual void Reset() noexcept override 3781 | { 3782 | ValueFlagBase::Reset(); 3783 | values.clear(); 3784 | } 3785 | 3786 | iterator begin() noexcept 3787 | { 3788 | return values.begin(); 3789 | } 3790 | 3791 | const_iterator begin() const noexcept 3792 | { 3793 | return values.begin(); 3794 | } 3795 | 3796 | const_iterator cbegin() const noexcept 3797 | { 3798 | return values.cbegin(); 3799 | } 3800 | 3801 | iterator end() noexcept 3802 | { 3803 | return values.end(); 3804 | } 3805 | 3806 | const_iterator end() const noexcept 3807 | { 3808 | return values.end(); 3809 | } 3810 | 3811 | const_iterator cend() const noexcept 3812 | { 3813 | return values.cend(); 3814 | } 3815 | }; 3816 | 3817 | /** A positional argument class 3818 | * 3819 | * \tparam T the type to extract the argument as 3820 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 3821 | */ 3822 | template < 3823 | typename T, 3824 | typename Reader = ValueReader> 3825 | class Positional : public PositionalBase 3826 | { 3827 | private: 3828 | T value; 3829 | Reader reader; 3830 | public: 3831 | Positional(Group &group_, const std::string &name_, const std::string &help_, const T &defaultValue_ = T(), Options options_ = {}): PositionalBase(name_, help_, options_), value(defaultValue_) 3832 | { 3833 | group_.Add(*this); 3834 | } 3835 | 3836 | Positional(Group &group_, const std::string &name_, const std::string &help_, Options options_): Positional(group_, name_, help_, T(), options_) 3837 | { 3838 | } 3839 | 3840 | virtual ~Positional() {} 3841 | 3842 | virtual void ParseValue(const std::string &value_) override 3843 | { 3844 | #ifdef ARGS_NOEXCEPT 3845 | if (!reader(name, value_, this->value)) 3846 | { 3847 | error = Error::Parse; 3848 | } 3849 | #else 3850 | reader(name, value_, this->value); 3851 | #endif 3852 | ready = false; 3853 | matched = true; 3854 | } 3855 | 3856 | /** Get the value 3857 | */ 3858 | T &Get() noexcept 3859 | { 3860 | return value; 3861 | } 3862 | }; 3863 | 3864 | /** A positional argument class that pushes the found values into a list 3865 | * 3866 | * \tparam T the type to extract the argument as 3867 | * \tparam List the list type that houses the values 3868 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 3869 | */ 3870 | template < 3871 | typename T, 3872 | template class List = std::vector, 3873 | typename Reader = ValueReader> 3874 | class PositionalList : public PositionalBase 3875 | { 3876 | private: 3877 | using Container = List; 3878 | Container values; 3879 | Reader reader; 3880 | 3881 | public: 3882 | typedef T value_type; 3883 | typedef typename Container::allocator_type allocator_type; 3884 | typedef typename Container::pointer pointer; 3885 | typedef typename Container::const_pointer const_pointer; 3886 | typedef T& reference; 3887 | typedef const T& const_reference; 3888 | typedef typename Container::size_type size_type; 3889 | typedef typename Container::difference_type difference_type; 3890 | typedef typename Container::iterator iterator; 3891 | typedef typename Container::const_iterator const_iterator; 3892 | typedef std::reverse_iterator reverse_iterator; 3893 | typedef std::reverse_iterator const_reverse_iterator; 3894 | 3895 | PositionalList(Group &group_, const std::string &name_, const std::string &help_, const Container &defaultValues_ = Container(), Options options_ = {}): PositionalBase(name_, help_, options_), values(defaultValues_) 3896 | { 3897 | group_.Add(*this); 3898 | } 3899 | 3900 | PositionalList(Group &group_, const std::string &name_, const std::string &help_, Options options_): PositionalList(group_, name_, help_, {}, options_) 3901 | { 3902 | } 3903 | 3904 | virtual ~PositionalList() {} 3905 | 3906 | virtual void ParseValue(const std::string &value_) override 3907 | { 3908 | T v; 3909 | #ifdef ARGS_NOEXCEPT 3910 | if (!reader(name, value_, v)) 3911 | { 3912 | error = Error::Parse; 3913 | } 3914 | #else 3915 | reader(name, value_, v); 3916 | #endif 3917 | values.insert(std::end(values), v); 3918 | matched = true; 3919 | } 3920 | 3921 | virtual std::string Name() const override 3922 | { 3923 | return name + std::string("..."); 3924 | } 3925 | 3926 | /** Get the values 3927 | */ 3928 | Container &Get() noexcept 3929 | { 3930 | return values; 3931 | } 3932 | 3933 | virtual void Reset() noexcept override 3934 | { 3935 | PositionalBase::Reset(); 3936 | values.clear(); 3937 | } 3938 | 3939 | iterator begin() noexcept 3940 | { 3941 | return values.begin(); 3942 | } 3943 | 3944 | const_iterator begin() const noexcept 3945 | { 3946 | return values.begin(); 3947 | } 3948 | 3949 | const_iterator cbegin() const noexcept 3950 | { 3951 | return values.cbegin(); 3952 | } 3953 | 3954 | iterator end() noexcept 3955 | { 3956 | return values.end(); 3957 | } 3958 | 3959 | const_iterator end() const noexcept 3960 | { 3961 | return values.end(); 3962 | } 3963 | 3964 | const_iterator cend() const noexcept 3965 | { 3966 | return values.cend(); 3967 | } 3968 | }; 3969 | 3970 | /** A positional argument mapping class 3971 | * 3972 | * \tparam K the type to extract the argument as 3973 | * \tparam T the type to store the result as 3974 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 3975 | * \tparam Map The Map type. Should operate like std::map or std::unordered_map 3976 | */ 3977 | template < 3978 | typename K, 3979 | typename T, 3980 | typename Reader = ValueReader, 3981 | template class Map = std::unordered_map> 3982 | class MapPositional : public PositionalBase 3983 | { 3984 | private: 3985 | const Map map; 3986 | T value; 3987 | Reader reader; 3988 | 3989 | protected: 3990 | virtual std::vector GetChoicesStrings(const HelpParams &) const override 3991 | { 3992 | return detail::MapKeysToStrings(map); 3993 | } 3994 | 3995 | public: 3996 | 3997 | MapPositional(Group &group_, const std::string &name_, const std::string &help_, const Map &map_, const T &defaultValue_ = T(), Options options_ = {}): 3998 | PositionalBase(name_, help_, options_), map(map_), value(defaultValue_) 3999 | { 4000 | group_.Add(*this); 4001 | } 4002 | 4003 | virtual ~MapPositional() {} 4004 | 4005 | virtual void ParseValue(const std::string &value_) override 4006 | { 4007 | K key; 4008 | #ifdef ARGS_NOEXCEPT 4009 | if (!reader(name, value_, key)) 4010 | { 4011 | error = Error::Parse; 4012 | } 4013 | #else 4014 | reader(name, value_, key); 4015 | #endif 4016 | auto it = map.find(key); 4017 | if (it == std::end(map)) 4018 | { 4019 | std::ostringstream problem; 4020 | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; 4021 | #ifdef ARGS_NOEXCEPT 4022 | error = Error::Map; 4023 | errorMsg = problem.str(); 4024 | #else 4025 | throw MapError(problem.str()); 4026 | #endif 4027 | } else 4028 | { 4029 | this->value = it->second; 4030 | ready = false; 4031 | matched = true; 4032 | } 4033 | } 4034 | 4035 | /** Get the value 4036 | */ 4037 | T &Get() noexcept 4038 | { 4039 | return value; 4040 | } 4041 | }; 4042 | 4043 | /** A positional argument mapping list class 4044 | * 4045 | * \tparam K the type to extract the argument as 4046 | * \tparam T the type to store the result as 4047 | * \tparam List the list type that houses the values 4048 | * \tparam Reader The functor type used to read the argument, taking the name, value, and destination reference with operator(), and returning a bool (if ARGS_NOEXCEPT is defined) 4049 | * \tparam Map The Map type. Should operate like std::map or std::unordered_map 4050 | */ 4051 | template < 4052 | typename K, 4053 | typename T, 4054 | template class List = std::vector, 4055 | typename Reader = ValueReader, 4056 | template class Map = std::unordered_map> 4057 | class MapPositionalList : public PositionalBase 4058 | { 4059 | private: 4060 | using Container = List; 4061 | 4062 | const Map map; 4063 | Container values; 4064 | Reader reader; 4065 | 4066 | protected: 4067 | virtual std::vector GetChoicesStrings(const HelpParams &) const override 4068 | { 4069 | return detail::MapKeysToStrings(map); 4070 | } 4071 | 4072 | public: 4073 | typedef T value_type; 4074 | typedef typename Container::allocator_type allocator_type; 4075 | typedef typename Container::pointer pointer; 4076 | typedef typename Container::const_pointer const_pointer; 4077 | typedef T& reference; 4078 | typedef const T& const_reference; 4079 | typedef typename Container::size_type size_type; 4080 | typedef typename Container::difference_type difference_type; 4081 | typedef typename Container::iterator iterator; 4082 | typedef typename Container::const_iterator const_iterator; 4083 | typedef std::reverse_iterator reverse_iterator; 4084 | typedef std::reverse_iterator const_reverse_iterator; 4085 | 4086 | MapPositionalList(Group &group_, const std::string &name_, const std::string &help_, const Map &map_, const Container &defaultValues_ = Container(), Options options_ = {}): 4087 | PositionalBase(name_, help_, options_), map(map_), values(defaultValues_) 4088 | { 4089 | group_.Add(*this); 4090 | } 4091 | 4092 | virtual ~MapPositionalList() {} 4093 | 4094 | virtual void ParseValue(const std::string &value_) override 4095 | { 4096 | K key; 4097 | #ifdef ARGS_NOEXCEPT 4098 | if (!reader(name, value_, key)) 4099 | { 4100 | error = Error::Parse; 4101 | } 4102 | #else 4103 | reader(name, value_, key); 4104 | #endif 4105 | auto it = map.find(key); 4106 | if (it == std::end(map)) 4107 | { 4108 | std::ostringstream problem; 4109 | problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; 4110 | #ifdef ARGS_NOEXCEPT 4111 | error = Error::Map; 4112 | errorMsg = problem.str(); 4113 | #else 4114 | throw MapError(problem.str()); 4115 | #endif 4116 | } else 4117 | { 4118 | this->values.emplace_back(it->second); 4119 | matched = true; 4120 | } 4121 | } 4122 | 4123 | /** Get the value 4124 | */ 4125 | Container &Get() noexcept 4126 | { 4127 | return values; 4128 | } 4129 | 4130 | virtual std::string Name() const override 4131 | { 4132 | return name + std::string("..."); 4133 | } 4134 | 4135 | virtual void Reset() noexcept override 4136 | { 4137 | PositionalBase::Reset(); 4138 | values.clear(); 4139 | } 4140 | 4141 | iterator begin() noexcept 4142 | { 4143 | return values.begin(); 4144 | } 4145 | 4146 | const_iterator begin() const noexcept 4147 | { 4148 | return values.begin(); 4149 | } 4150 | 4151 | const_iterator cbegin() const noexcept 4152 | { 4153 | return values.cbegin(); 4154 | } 4155 | 4156 | iterator end() noexcept 4157 | { 4158 | return values.end(); 4159 | } 4160 | 4161 | const_iterator end() const noexcept 4162 | { 4163 | return values.end(); 4164 | } 4165 | 4166 | const_iterator cend() const noexcept 4167 | { 4168 | return values.cend(); 4169 | } 4170 | }; 4171 | } 4172 | 4173 | #endif 4174 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "./tree.hpp" 2 | #include "./argx.hpp" 3 | 4 | string ReadFile(const string &fileName){ 5 | ifstream f(fileName); 6 | string s; 7 | 8 | if(f.is_open()){ 9 | f.seekg(0, ios::end); 10 | 11 | size_t size = f.tellg(); 12 | 13 | s.resize(size); 14 | 15 | f.seekg(0, ios::beg); 16 | 17 | f.read(&s[0], size); 18 | } else { 19 | cout << "file open error!" << endl; 20 | exit(-1); 21 | } 22 | 23 | return s; 24 | } 25 | 26 | void printTab(int count){ 27 | for(int i=0; i &T){ 32 | cout << "[ "; 33 | for ( auto i: T){ 34 | cout << "'\x1b[91m" << i.str << "\x1b[0m', "; 35 | } 36 | cout << "]" << endl; 37 | } 38 | 39 | void SoltreeView(const SolTree &tree, int count = 0){ 40 | printTab(count); 41 | cout << tree.name << " "; 42 | cout << "\n"; 43 | if(tree.inherited.size()){ 44 | cout << " is "; 45 | for(auto i : tree.inherited){ 46 | cout << i.name << " "; 47 | } 48 | } 49 | for(auto i : tree.children){ 50 | SoltreeView(i, count+1); 51 | } 52 | } 53 | 54 | int main(int argc, char **argv){ 55 | 56 | args::ArgumentParser parser("This is a solidity parse program.", "This goes after the options."); 57 | args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"}); 58 | 59 | args::Group inputGroup(parser, "read source options:", args::Group::Validators::Xor); 60 | args::ValueFlag file(inputGroup, "NAME", "read the source file", {'f', "file"}); 61 | args::Flag sinput(inputGroup, "stdin", "read the stdin", {'d', "stdin"}); 62 | 63 | try { 64 | parser.ParseCLI(argc ,argv); 65 | 66 | 67 | if(file || sinput){ 68 | string source; 69 | 70 | if(file){ 71 | source = ReadFile(args::get(file)); 72 | 73 | cout << source << endl; 74 | 75 | } else if(sinput){ 76 | cout << "stdin input!!!" << endl; 77 | } 78 | 79 | auto tree = SolParser(source); 80 | 81 | SoltreeView(tree); 82 | 83 | } 84 | } catch (args::Completion& e){ 85 | cout << e.what() << endl; 86 | return 0; 87 | } catch (const args::Help&){ 88 | cout << parser; 89 | return 0; 90 | } catch (const args::ParseError& e){ 91 | cerr << e.what() << std::endl; 92 | cerr << parser; 93 | return -1; 94 | } catch (const args::ValidationError e){ 95 | std::cerr << parser; 96 | return 1; 97 | } 98 | return 0; 99 | 100 | } 101 | -------------------------------------------------------------------------------- /tokenize.cpp: -------------------------------------------------------------------------------- 1 | #include "tokenize.hpp" 2 | 3 | bool ishex(char c) 4 | { 5 | if('0' <= c && c <= '9') return true; 6 | if('A' <= c && c <= 'F') return true; 7 | if('a' <= c && c <= 'f') return true; 8 | return false; 9 | } 10 | 11 | int hexToInt(char c) 12 | { 13 | if('0' <= c && c <= '9') return c - '0'; 14 | if('A' <= c && c <= 'F') return c - 'A' + 10; 15 | if('a' <= c && c <= 'f') return c - 'a' + 10; 16 | return -1; 17 | } 18 | 19 | char DecodeEscape(string::const_iterator& it, string::const_iterator end, bool& error) 20 | { 21 | if(it == end || *it != '\\') 22 | { 23 | error = true; 24 | return 0; 25 | } 26 | ++it; 27 | error = false; 28 | switch(*it) 29 | { 30 | case '\\': 31 | ++it; 32 | return '\\'; 33 | case 't': 34 | ++it; 35 | return '\t'; 36 | case 'n': 37 | ++it; 38 | return '\n'; 39 | case 'r': 40 | ++it; 41 | return '\r'; 42 | case '\'': 43 | ++it; 44 | return '\''; 45 | case '"': 46 | ++it; 47 | return '\"'; 48 | case '0': 49 | ++it; 50 | return '\0'; 51 | case 'x': 52 | { 53 | ++it; 54 | int h = 0; 55 | for(int i = 0; i < 2; ++i) 56 | { 57 | if(!ishex(*it)) 58 | { 59 | error = true; 60 | return 0; 61 | } 62 | h = h * 16 + hexToInt(*it); 63 | ++it; 64 | } 65 | return h; 66 | } 67 | } 68 | error = true; 69 | return '\\'; 70 | } 71 | 72 | bool IsCutCharacter(wchar_t c) 73 | { 74 | switch(c) 75 | { 76 | case ' ': 77 | case '\t': 78 | case '\r': 79 | case '\n': 80 | case '<': 81 | case '>': 82 | case '[': 83 | case ']': 84 | case '=': 85 | case '!': 86 | case '+': 87 | case '-': 88 | case '*': 89 | case '/': 90 | case '%': 91 | case '^': 92 | case '?': 93 | case '.': 94 | case ':': 95 | case ';': 96 | case ',': 97 | case '{': 98 | case '}': 99 | case '(': 100 | case ')': 101 | case '|': 102 | return true; 103 | } 104 | return false; 105 | } 106 | 107 | 108 | 109 | vector Tokenize(const string &str){ 110 | 111 | vector ret; 112 | 113 | auto it = str.begin(); 114 | auto marker = str.begin(); 115 | 116 | TokenType stage = TokenType::none; 117 | 118 | int line = 1; 119 | string s; 120 | 121 | char tmp = 0; 122 | 123 | for(;it != str.end(); it++){ 124 | switch(stage){ 125 | case TokenType::none: 126 | switch(*it){ 127 | case '\n': 128 | line++; 129 | break; 130 | case ' ': 131 | case '\t': 132 | case '\r': 133 | break; 134 | case '\"': 135 | case '\'': 136 | stage = TokenType::string; 137 | marker = it; 138 | tmp = *it; 139 | break; 140 | case '0': 141 | case '1': 142 | case '2': 143 | case '3': 144 | case '4': 145 | case '5': 146 | case '6': 147 | case '7': 148 | case '8': 149 | case '9': 150 | stage = TokenType::integer; 151 | marker = it; 152 | break; 153 | case '<': 154 | if(it+1 != str.end() && it[1] == '='){ 155 | ret.push_back(Token("<=", TokenType::lessequal, line)); 156 | it++; 157 | } else { 158 | ret.push_back(Token("<", TokenType::less, line)); 159 | } 160 | break; 161 | case '>': 162 | if(it+1 != str.end() && it[1] == '='){ 163 | ret.push_back(Token(">=", TokenType::greaterequal, line)); 164 | it++; 165 | } else { 166 | ret.push_back(Token(">", TokenType::greater, line)); 167 | } 168 | break; 169 | case '[': 170 | ret.push_back(Token("[", TokenType::lsbracket, line)); 171 | break; 172 | case ']': 173 | ret.push_back(Token("]", TokenType::rsbracket, line)); 174 | break; 175 | case '(': 176 | ret.push_back(Token("(", TokenType::lparen, line)); 177 | break; 178 | case ')': 179 | ret.push_back(Token(")", TokenType::rparen, line)); 180 | break; 181 | case '{': 182 | ret.push_back(Token("{", TokenType::lbracket, line)); 183 | break; 184 | case '}': 185 | ret.push_back(Token("}", TokenType::rbracket, line)); 186 | break; 187 | case '=': 188 | if( it+1 != str.end() && it[1] == '='){ 189 | ret.push_back(Token("==", TokenType::equal, line)); 190 | it++; 191 | } else { 192 | ret.push_back(Token("=", TokenType::assign, line)); 193 | } 194 | break; 195 | case '!': 196 | if( it+1 != str.end() && it[1] == '='){ 197 | ret.push_back(Token("!=", TokenType::notequal, line)); 198 | it++; 199 | } else { 200 | ret.push_back(Token("!", TokenType::not_, line)); 201 | } 202 | break; 203 | case '+': 204 | ret.push_back(Token("+", TokenType::plus, line)); 205 | break; 206 | case '-': 207 | ret.push_back(Token("-", TokenType::minus, line)); 208 | break; 209 | case '*': 210 | ret.push_back(Token("*", TokenType::asterisk, line)); 211 | break; 212 | case '/': 213 | if( it+1 != str.end() && it[1] == '/'){ 214 | stage = TokenType::comment1; 215 | it++; 216 | } else if( it+1 != str.end() && it[1] == '*'){ 217 | stage = TokenType::comment2; 218 | it++; 219 | } else { 220 | ret.push_back(Token("/", TokenType::divide, line)); 221 | } 222 | break; 223 | case '%': 224 | ret.push_back(Token("%", TokenType::percent, line)); 225 | break; 226 | case '^': 227 | ret.push_back(Token("^", TokenType::hat, line)); 228 | break; 229 | case '?': 230 | ret.push_back(Token("?", TokenType::question, line)); 231 | break; 232 | case '.': 233 | ret.push_back(Token(".", TokenType::dot, line)); 234 | break; 235 | case ':': 236 | ret.push_back(Token(":", TokenType::colon, line)); 237 | break; 238 | case ';': 239 | ret.push_back(Token(";", TokenType::semicolon, line)); 240 | break; 241 | case ',': 242 | ret.push_back(Token(",", TokenType::comma, line)); 243 | break; 244 | case '|': 245 | if( it+1 != str.end() && it[1] == '|'){ 246 | ret.push_back(Token("||", TokenType::doubleor_, line)); 247 | it++; 248 | } else { 249 | ret.push_back(Token("|", TokenType::or_, line)); 250 | } 251 | break; 252 | case '&': 253 | if( it+1 != str.end() && it[1] == '&'){ 254 | ret.push_back(Token("&&", TokenType::doubleand_, line)); 255 | it++; 256 | } else { 257 | ret.push_back(Token("&", TokenType::and_, line)); 258 | } 259 | break; 260 | default: 261 | stage = TokenType::identifier; 262 | marker = it; 263 | break; 264 | } 265 | break; 266 | case TokenType::string: 267 | for(;*it != tmp; it++){ 268 | if(it == str.end() || *it == '\n' || *it == '\r'){ 269 | stage = TokenType::none; 270 | ret.push_back(Token(s, TokenType::error, line)); 271 | break; 272 | } else if(*it == '\\'){ 273 | bool err; 274 | s.push_back(DecodeEscape(it, str.end(), err)); 275 | if(err){ 276 | stage = TokenType::none; 277 | ret.push_back(Token(s, TokenType::error, line)); 278 | 279 | break; 280 | } 281 | it--; 282 | } else { 283 | s.push_back(*it); 284 | } 285 | } 286 | 287 | stage = TokenType::none; 288 | ret.push_back(Token(s, TokenType::string, line)); 289 | break; 290 | case TokenType::integer: 291 | if(*it == '.'){ 292 | stage = TokenType::real; 293 | } else if(IsCutCharacter(*it)){ 294 | stage = TokenType::none; 295 | ret.push_back(Token(string(marker, it--), TokenType::integer, line)); 296 | } else if(!isdigit(*it)){ 297 | stage = TokenType::none; 298 | ret.push_back(Token(string(marker, it--), TokenType::error, line)); 299 | } 300 | break; 301 | case TokenType::real: 302 | if(IsCutCharacter(*it)){ 303 | stage = TokenType::none; 304 | ret.push_back(Token(string(marker, it--), TokenType::real, line)); 305 | } else if(!isdigit(*it)){ 306 | stage = TokenType::none; 307 | ret.push_back(Token(string(marker, it--), TokenType::real, line)); 308 | } 309 | break; 310 | case TokenType::identifier: 311 | if(IsCutCharacter(*it)){ 312 | stage = TokenType::none; 313 | string s(marker, it--); 314 | if(s == "true"){ 315 | ret.push_back(Token(s, TokenType::true_, line)); 316 | } else if(s == "false"){ 317 | ret.push_back(Token(s, TokenType::false_, line)); 318 | } else if(s == "this"){ 319 | ret.push_back(Token(s, TokenType::this_, line)); 320 | } else if(s == "contract"){ 321 | ret.push_back(Token(s, TokenType::contract, line)); 322 | } else if(s == "library"){ 323 | ret.push_back(Token(s, TokenType::library, line)); 324 | } else if(s == "interface"){ 325 | ret.push_back(Token(s, TokenType::interface, line)); 326 | } else if(s == "constructor"){ 327 | ret.push_back(Token(s, TokenType::constructor, line)); 328 | } else if(s == "if"){ 329 | ret.push_back(Token(s, TokenType::if_, line)); 330 | } else if(s == "else"){ 331 | ret.push_back(Token(s, TokenType::else_, line)); 332 | } else if(s == "for"){ 333 | ret.push_back(Token(s, TokenType::for_, line)); 334 | } else if(s == "while"){ 335 | ret.push_back(Token(s, TokenType::while_, line)); 336 | } else if(s == "do"){ 337 | ret.push_back(Token(s, TokenType::do_, line)); 338 | } else if(s == "return"){ 339 | ret.push_back(Token(s, TokenType::return_, line)); 340 | } else if(s == "break"){ 341 | ret.push_back(Token(s, TokenType::break_, line)); 342 | } else if(s == "continue"){ 343 | ret.push_back(Token(s, TokenType::continue_, line)); 344 | } else if(s == "throw"){ 345 | ret.push_back(Token(s, TokenType::throw_, line)); 346 | } else if(s == "public"){ 347 | ret.push_back(Token(s, TokenType::public_, line)); 348 | } else if(s == "external"){ 349 | ret.push_back(Token(s, TokenType::external_, line)); 350 | } else if(s == "private"){ 351 | ret.push_back(Token(s, TokenType::private_, line)); 352 | } else if(s == "internal"){ 353 | ret.push_back(Token(s, TokenType::internal_, line)); 354 | } else if(s == "is"){ 355 | ret.push_back(Token(s, TokenType::is, line)); 356 | } else { 357 | ret.push_back(Token(s, TokenType::identifier, line)); 358 | } 359 | } 360 | break; 361 | case TokenType::comment1: 362 | if(*it == '\n'){ 363 | line++; 364 | stage = TokenType::none; 365 | } 366 | break; 367 | case TokenType::comment2: 368 | switch(*it){ 369 | case '\n': 370 | line++; 371 | break; 372 | case '*': 373 | if( it+1 != str.end() && it[1] == '/'){ 374 | it++; 375 | stage = TokenType::none; 376 | } 377 | break; 378 | } 379 | break; 380 | } 381 | } 382 | ret.push_back(Token("", TokenType::eof, line)); 383 | 384 | return ret; 385 | 386 | } 387 | -------------------------------------------------------------------------------- /tokenize.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | 11 | enum class TokenType{ 12 | none, 13 | eof, 14 | error, 15 | string, 16 | integer, 17 | real, 18 | identifier, 19 | contract, 20 | library, 21 | interface, 22 | constructor, 23 | function, 24 | assign, // = 25 | equal, // == 26 | question, // ? 27 | plus, // + 28 | minus, // - 29 | asterisk, // * 30 | divide, // / 31 | percent, // % 32 | hat, // ^ 33 | dot, // . 34 | colon, // : 35 | semicolon, // ; 36 | comma, // , 37 | lbracket, // { 38 | rbracket, // } 39 | lsbracket, // [ 40 | rsbracket, // ] 41 | lparen, // ( 42 | rparen, // ) 43 | lessequal, // <= 44 | less, // < 45 | greaterequal, // >= 46 | greater, // > 47 | notequal, // != 48 | or_, // | 49 | doubleor_, // || 50 | and_, // & 51 | doubleand_, // && 52 | not_, // ! 53 | true_, // true 54 | false_, // false 55 | this_, // this 56 | if_, // i) 57 | else_, // else 58 | for_, // for 59 | while_, // while 60 | do_, // do 61 | return_, // return 62 | break_, // break 63 | continue_, // continue 64 | throw_, // throw 65 | public_, // public 66 | external_, // external 67 | private_, // private 68 | internal_, // internal 69 | is, // is 70 | comment1, // // 71 | comment2, // /* 72 | }; 73 | 74 | struct Token{ 75 | string str; 76 | TokenType type; 77 | int line; 78 | Token(string s = string(), TokenType t = TokenType::none, int _line = 0) : str(s.c_str()), type(t), line(_line) {} 79 | }; 80 | 81 | vector Tokenize(const string &str); 82 | typedef vector::const_iterator Tokit; 83 | -------------------------------------------------------------------------------- /tree.cpp: -------------------------------------------------------------------------------- 1 | #include "./tree.hpp" 2 | 3 | int S_variable(Tokit begin, SolTree &out){ 4 | 5 | } 6 | 7 | int S_ConditionalStatement(Tokit begin, SolTree &out){ 8 | vector tmp; 9 | int count = 0; 10 | auto it = begin; 11 | 12 | cout << "S_ConditionalStatement() called!!" << endl; 13 | cout << "it->str : " << it->str << endl; 14 | 15 | } 16 | 17 | int S_while(Tokit begin, SolTree &out){ 18 | vector tmp; 19 | int count = 0; 20 | auto it = begin; 21 | 22 | cout << "S_while() called!!" << endl; 23 | cout << "it->str : " << it->str << endl; 24 | 25 | assert(it->type == TokenType::lparen && (it+1)->type != TokenType::eof); 26 | it++; count++; 27 | 28 | 29 | 30 | return count; 31 | } 32 | 33 | int S_for(Tokit begin, SolTree &out){ 34 | 35 | } 36 | 37 | int S_do_while(Tokit begin, SolTree &out){ 38 | 39 | } 40 | 41 | int S_modifier(Tokit begin, SolTree &out){ 42 | 43 | } 44 | 45 | int S_loop_and_if(Tokit begin, SolTree &out){ 46 | } 47 | 48 | int S_Function(Tokit begin, SolTree &out){ 49 | vector tmp; 50 | int count = 0; 51 | auto it = begin; 52 | 53 | cout << "S_Function() called!!" << endl; 54 | cout << "it->str : " << it->str << endl; 55 | 56 | if((it+1)->type == TokenType::lparen){ 57 | it++; count++; 58 | for(; it->type != TokenType::rparen && it->type != TokenType::eof; it++){ 59 | count++; 60 | } 61 | for(; it->type != TokenType::lbracket && it->type != TokenType::eof; it++){ 62 | count++; 63 | if(it->type == TokenType::identifier){ 64 | out.usemodifier.push_back(SolTree(it->str, NodeType::modifier)); 65 | } 66 | } 67 | 68 | if(it->type == TokenType::lbracket){ 69 | count++; 70 | it++; 71 | } 72 | 73 | for(; it->type != TokenType::rbracket && it->type != TokenType::eof; it++){ 74 | int c = 0; 75 | switch(it->type){ 76 | case TokenType::for_: 77 | case TokenType::while_: 78 | case TokenType::do_: 79 | if((it+1)->type == TokenType::lparen && (it+1)->type != TokenType::eof){ 80 | auto p = SolTree(it->str, NodeType::loop); 81 | it++; count++; 82 | if((it-1)->type == TokenType::while_){ 83 | c = S_while(it, p); 84 | } else if((it-1)->type == TokenType::for_){ 85 | c = S_for(it, p); 86 | } else { 87 | c = S_do_while(it, p); 88 | } 89 | it += c; 90 | count += c; 91 | tmp.push_back(p); 92 | } else { 93 | cout << "loop error!! line : " << it->line << endl; 94 | exit(-1); 95 | } 96 | break; 97 | case TokenType::if_: 98 | case TokenType::else_: 99 | if((it+1)->type == TokenType::lparen && (it+1)->type != TokenType::eof){ 100 | auto p = SolTree(it->str, NodeType::conditionalstatement); 101 | it++; count++; 102 | c = S_ConditionalStatement(it, p); 103 | it += c; 104 | count += c; 105 | } else { 106 | cout << "conditional statement error!! line : " << it->line << endl; 107 | exit(-1); 108 | } 109 | break; 110 | } 111 | } 112 | } 113 | return count; 114 | } 115 | 116 | int S_Parent(Tokit begin, SolTree &out){ 117 | vector tmp; 118 | int count = 0; 119 | auto it = begin; 120 | 121 | cout << "S_Parent() called!!" << endl; 122 | cout << " it->str : " << it->str << endl; 123 | 124 | if(it->type == TokenType::lbracket){ 125 | it++; 126 | count++; 127 | for(; it->type != TokenType::rbracket && it->type != TokenType::eof; it++){ 128 | int c = 0; 129 | count++; 130 | switch(it->type){ 131 | case TokenType::constructor: 132 | case TokenType::function: 133 | if((it+1)->type != TokenType::eof){ 134 | auto p = SolTree((it+1)->str, NodeType::function); 135 | it++; 136 | count++; 137 | c = S_Function(it, p); 138 | count += c; 139 | tmp.push_back(p); 140 | } else { 141 | cout << "function error!! line : " << it->line << endl; 142 | exit(-1); 143 | } 144 | break; 145 | default: 146 | tmp.push_back(SolTree(it->str, NodeType::none)); 147 | break; 148 | } 149 | } 150 | } else { 151 | cout << "error! line : " << it->line; 152 | exit(-1); 153 | } 154 | 155 | out.children = tmp; 156 | 157 | return count; 158 | } 159 | 160 | int S_Top(Tokit begin, SolTree &out){ 161 | unsigned int ret = 0; 162 | 163 | vector tmp; 164 | 165 | auto it = begin; 166 | 167 | cout << "S_Top() called!!" << endl; 168 | 169 | for(; it->type != TokenType::eof;){ 170 | if(it->type == TokenType::contract || it->type == TokenType::library || it->type == TokenType::interface){ 171 | SolTree p; 172 | if((it+1)->type != TokenType::eof){ 173 | switch(it->type){ 174 | case TokenType::contract: 175 | p = SolTree((it+1)->str, NodeType::contract); 176 | it+=2; 177 | break; 178 | case TokenType::library: 179 | p = SolTree((it+1)->str, NodeType::library); 180 | it+=2; 181 | break; 182 | case TokenType::interface: 183 | p = SolTree((it+1)->str, NodeType::interface); 184 | it+=2; 185 | break; 186 | } 187 | 188 | if(it->type == TokenType::is){ 189 | it++; 190 | for(; it->type != TokenType::lbracket && it->type != TokenType::eof; it++) 191 | p.inherited.push_back(SolTree(it->str, NodeType::none)); 192 | it += S_Parent(it, p); 193 | } else if(it->type == TokenType::lbracket){ 194 | it += S_Parent(it, p); 195 | } else { 196 | cout << "Error!!! line : " << it->line << endl; 197 | return -1; 198 | } 199 | tmp.push_back(p); 200 | ret++; 201 | } 202 | } else { 203 | it++; 204 | } 205 | } 206 | 207 | out.children = tmp; 208 | 209 | return ret; 210 | } 211 | 212 | SolTree SolParser(const string &source){ 213 | 214 | auto tokens = Tokenize(source); 215 | 216 | //SolTree ret = SolTree(nodeType::top, "TopNode", TokenType::none); 217 | SolTree ret = SolTree("TopNode", NodeType::top); 218 | S_Top(tokens.begin(), ret); 219 | 220 | return ret; 221 | } 222 | -------------------------------------------------------------------------------- /tree.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "./tokenize.hpp" 3 | 4 | enum class NodeType{ 5 | none, 6 | top, 7 | contract, 8 | library, 9 | interface, 10 | constructor, 11 | function, 12 | modifier, 13 | globalvariable, 14 | localvariable, 15 | loop, 16 | conditionalstatement, 17 | }; 18 | 19 | struct SolTree{ 20 | //nodeType type; 21 | string name; 22 | NodeType type; 23 | vector children; // children node tree 24 | vector inherited; // contract parents 25 | vector usemodifier; // modifiers used in function 26 | vector argument; // function and modifier 27 | vector conditional; // Conditional Statement content 28 | 29 | //SolTree( nodeType t=nodeType::none, string _name = string(), TokenType _token = TokenType::none) : type(t), name(_name.c_str()), token(_token){} 30 | SolTree(string _name = string(), NodeType _type= NodeType::none) : name(_name.c_str()), type(_type){} 31 | }; 32 | 33 | SolTree SolParser(const string &source); 34 | --------------------------------------------------------------------------------