From 24b6555d0ecf834abcb99ab39a7fa9919cec4fa6 Mon Sep 17 00:00:00 2001 From: hoxnox Date: Wed, 15 Apr 2015 16:23:26 +0300 Subject: [PATCH 01/70] Enhancements: cmake build script added Create CMakeLists.txt, wich helps build docopt into library and prepare deb and rpm packages. --- CMakeLists.txt | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..130c885 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,75 @@ +cmake_minimum_required(VERSION 2.6.4) + +option(WITH_TESTS "Build tests." OFF) +option(WITH_EXAMPLE "Build example." OFF) +option(WITH_STATIC "Build static libs." ON) + +project(docopt.cpp) +include_directories("${PROJECT_SOURCE_DIR}") + +######################################################################## +# docopt + +set(DOCOPT_SRC + docopt.cpp + docopt.h + docopt_private.h + docopt_util.h + docopt_value.h +) +if(WITH_STATIC) + add_library(docopt_s STATIC ${DOCOPT_SRC}) +endif() +add_library(docopt SHARED ${DOCOPT_SRC}) + +add_definitions("-std=c++14") + +######################################################################## +# tests + +if (WITH_EXAMPLE) + add_executable(docopt_example examples/naval_fate.cpp) + target_link_libraries(docopt_example docopt) +endif() + +######################################################################## +# example + +if (WITH_TESTS) + add_executable(docopt_testcase run_testcase.cpp) + target_link_libraries(docopt_testcase docopt) +endif() + +######################################################################## +# installation + +INSTALL(TARGETS + docopt + DESTINATION lib) +if(WITH_STATIC) + INSTALL(TARGETS + docopt_s + DESTINATION lib) +endif() +INSTALL(FILES + docopt.h + docopt_private.h + docopt_util.h + docopt_value.h + DESTINATION include/docopt) +SET(CPACK_PACKAGE_NAME "docopt") +SET(CPACK_DEBIAN_PACKAGE_DEPENDS "") +SET(CPACK_RPM_PACKAGE_REQUIRES "") +SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Beautiful command line interfaces") +SET(CPACK_PACKAGE_VENDOR "Jared Grubb") +SET(CPACK_PACKAGE_CONTACT ${CPACK_PACKAGE_VENDOR}) +SET(CPACK_PACKAGE_DESCRIPTION_FILE "${PROJECT_SOURCE_DIR}/README.rst") +SET(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE-MIT") +SET(CPACK_PACKAGE_VERSION_MAJOR 0) +SET(CPACK_PACKAGE_VERSION_MINOR 6) +SET(CPACK_PACKAGE_VERSION_PATCH 1) +SET(CPACK_DEBIAN_PACKAGE_SECTION "Development") +SET(CPACK_RPM_PACKAGE_GROUP "Development/Libraries") +SET(CPACK_RPM_PACKAGE_LICENSE "MIT") +SET(CPACK_STRIP_FILES TRUE) +INCLUDE(CPack) From d0d53d24f08b71611fb075ea04e95fd44214997b Mon Sep 17 00:00:00 2001 From: hoxnox Date: Wed, 15 Apr 2015 19:53:11 +0300 Subject: [PATCH 02/70] bugfix: low c++ std for gcc-4.9.0 compatibility --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 130c885..8b4a0cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ if(WITH_STATIC) endif() add_library(docopt SHARED ${DOCOPT_SRC}) -add_definitions("-std=c++14") +add_definitions("-std=c++11") ######################################################################## # tests From ba8b2f5f4c22fd2c62bb890193be4590bf86f14d Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 30 Apr 2015 16:34:30 +0200 Subject: [PATCH 03/70] Update README.rst Verified successful build & run of the sample application with Visual Studio 2015 RC. The blocking features -- missing from Visual C++ 2013 -- were unrestricted unions and `noexcept`; they are now supported: http://blogs.msdn.com/b/vcblog/archive/2015/04/29/c-11-14-17-features-in-vs-2015-rc.aspx --- README.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index c3979ff..5895f47 100644 --- a/README.rst +++ b/README.rst @@ -74,12 +74,11 @@ This port is written in C++11 and also requires a good C++11 standard library (in particular, one with ``regex`` support). The following compilers are known to work with docopt: -- clang 3.3 and later -- gcc 4.9 +- Clang 3.3 and later +- GCC 4.9 +- Visual C++ 2015 RC -Note that gcc-4.8 will not work due to its missing the ``regex`` module. -Note that Visual C++ 2013 will not compile this code, as its C++11 is not -quite good enough. If a later VC++ works, please let me know! +Note that GCC-4.8 will not work due to its missing the ``regex`` module. This port is licensed under the MIT license, just like the original module. However, we are also dual-licensing this code under the Boost License, version 1.0, From 94c8e59f04c3a6ba0d2aabdf42ba7456f95479f7 Mon Sep 17 00:00:00 2001 From: hoxnox Date: Sun, 24 May 2015 18:05:25 +0300 Subject: [PATCH 04/70] Enhancements: run_tests preparing corrected Now if you configure project with WITH_TESTS=1 defined, you'd have run_tests executable (python script) in your CMAKE_CURRENT_BINARY_DIR, which runs all testcases. --- CMakeLists.txt | 12 ++++++++++-- run_tests.py | 6 ++++-- 2 files changed, 14 insertions(+), 4 deletions(-) mode change 100644 => 100755 run_tests.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b4a0cf..118297e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,8 +36,16 @@ endif() # example if (WITH_TESTS) - add_executable(docopt_testcase run_testcase.cpp) - target_link_libraries(docopt_testcase docopt) + set(TESTPROG "${CMAKE_CURRENT_BINARY_DIR}/run_testcase") + set(TESTCASES "${PROJECT_SOURCE_DIR}/testcases.docopt") + add_executable(run_testcase run_testcase.cpp) + target_link_libraries(run_testcase docopt) + configure_file( + "${PROJECT_SOURCE_DIR}/run_tests.py" + "${CMAKE_CURRENT_BINARY_DIR}/run_tests" + ESCAPE_QUOTES + ) + add_test("Testcases docopt" ${TESTPROG}) endif() ######################################################################## diff --git a/run_tests.py b/run_tests.py old mode 100644 new mode 100755 index aea5f2d..6f009bd --- a/run_tests.py +++ b/run_tests.py @@ -1,8 +1,10 @@ +#!/usr/bin/env python2 + import re import json import subprocess -executable = "./run_testcase" +executable = "${TESTPROG}" def parse_test(raw): raw = re.compile('#.*$', re.M).sub('', raw).strip() @@ -24,7 +26,7 @@ def parse_test(raw): failures = 0 passes = 0 -tests = open('testcases.docopt','r').read() +tests = open('${TESTCASES}','r').read() for _, doc, cases in parse_test(tests): if not cases: continue From 4114291f4f596b0b78759f9c2407edbb6fbdbecd Mon Sep 17 00:00:00 2001 From: Ben Liblit Date: Wed, 27 May 2015 21:15:23 -0500 Subject: [PATCH 05/70] Simple typo fix: "doctopt" should be "docopt" --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 5895f47..a7e7d9b 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ ``docopt.cpp``: A C++11 Port ============================ -doctopt creates *beautiful* command-line interfaces ---------------------------------------------------- +docopt creates *beautiful* command-line interfaces +-------------------------------------------------- Isn't it awesome how ``getopt`` (and ``boost::program_options`` for you fancy folk!) generate help messages based on your code?! These timeless functions From e78e0b0721a980dcf72ee62c3f4b06c16b1cc965 Mon Sep 17 00:00:00 2001 From: John Susi Date: Mon, 10 Aug 2015 21:38:58 +0200 Subject: [PATCH 06/70] Write to supplied ostream instead of cout --- docopt.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docopt.cpp b/docopt.cpp index 25589c1..2aed8a4 100644 --- a/docopt.cpp +++ b/docopt.cpp @@ -59,28 +59,28 @@ std::ostream& docopt::operator<<(std::ostream& os, value const& val) { if (val.isBool()) { bool b = val.asBool(); - std::cout << (b ? "true" : "false"); + os << (b ? "true" : "false"); } else if (val.isLong()) { long v = val.asLong(); - std::cout << v; + os << v; } else if (val.isString()) { std::string const& str = val.asString(); - std::cout << '"' << str << '"'; + os << '"' << str << '"'; } else if (val.isStringList()) { auto const& list = val.asStringList(); - std::cout << "["; + os << "["; bool first = true; for(auto const& el : list) { if (first) { first = false; } else { - std::cout << ", "; + os << ", "; } - std::cout << '"' << el << '"'; + os << '"' << el << '"'; } - std::cout << "]"; + os << "]"; } else { - std::cout << "null"; + os << "null"; } return os; } @@ -1074,4 +1074,4 @@ docopt::docopt(std::string const& doc, std::cout << doc << std::endl; std::exit(-1); } /* Any other exception is unexpected: let std::terminate grab it */ -} \ No newline at end of file +} From 8253bfc8858b5a4cfac0916491afa60e517f4b5e Mon Sep 17 00:00:00 2001 From: Adam Getchell Date: Sat, 19 Sep 2015 10:31:27 -0700 Subject: [PATCH 07/70] Add override specifier Make AppleClang 7.0 warning go away --- docopt_private.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docopt_private.h b/docopt_private.h index 754fb3a..51f5f99 100644 --- a/docopt_private.h +++ b/docopt_private.h @@ -96,7 +96,7 @@ namespace docopt { virtual bool match(PatternList& left, std::vector>& collected) const override; - virtual bool hasValue() const { return static_cast(fValue); } + virtual bool hasValue() const override { return static_cast(fValue); } value const& getValue() const { return fValue; } void setValue(value&& v) { fValue = std::move(v); } From 78cf5f85494abb7f72b8d7ae4daee37c60312a01 Mon Sep 17 00:00:00 2001 From: rhorenov Date: Sat, 10 Oct 2015 12:46:33 +0200 Subject: [PATCH 08/70] Fixs warning in msvc2015. Fixed: warning C4800: 'const docopt::Option *': forcing value to bool 'true' or 'false' (performance warning) modified: docopt.cpp --- docopt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docopt.cpp b/docopt.cpp index 2aed8a4..6b8e697 100644 --- a/docopt.cpp +++ b/docopt.cpp @@ -535,7 +535,7 @@ class Tokens { template std::vector flat_filter(Pattern& pattern) { std::vector flattened = pattern.flat([](Pattern const* p) -> bool { - return dynamic_cast(p); + return dynamic_cast(p) != nullptr; }); // now, we're guaranteed to have T*'s, so just use static_cast From 2d10316a495c6b1946b48abc7f308bca71eacd0d Mon Sep 17 00:00:00 2001 From: Jared Grubb Date: Tue, 13 Oct 2015 23:59:44 -0700 Subject: [PATCH 09/70] Travis-CI: add support for Travis --- .travis.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..75fd76f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: cpp + +compiler: + - clang + - gcc + +before_script: + - cmake -DWITH_TESTS=1 -DWITH_EXAMPLE=1 . + +script: + - cmake --build . + - python run_tests From 66007712eb80ef8ff5f1c2d361c821a465deeb31 Mon Sep 17 00:00:00 2001 From: Jared Grubb Date: Wed, 14 Oct 2015 00:09:49 -0700 Subject: [PATCH 10/70] Travis-CI: update to newer infrastructure using the sudo-flag --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 75fd76f..9890d71 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ language: cpp +sudo: false # Use the new container infrastructure + compiler: - clang - gcc From 9de81eb2866fa58a51ebe7c7f169d6268c9ca42b Mon Sep 17 00:00:00 2001 From: Jared Grubb Date: Wed, 14 Oct 2015 00:24:13 -0700 Subject: [PATCH 11/70] Travis-CI: add packages for the various clang versions I tried to set up the matrix for various clang. I had trouble with: - linux + clang-3.5 (libstdc++ didnt work with this version and I couldnt figure out how to get it to use libc++) - linux + clang-3.8 (appears that this isnt ready yet) - linux + gcc-5 (the tool "gcc-5" wasnt found) Anyway, the matrix seems to work in these configs at least. Hint: it's the red pill. --- .travis.yml | 51 +++++++++++++++++++++++++++++++++++++++++++++----- CMakeLists.txt | 8 +++++++- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9890d71..861ce7b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,54 @@ language: cpp - sudo: false # Use the new container infrastructure -compiler: - - clang - - gcc +matrix: + include: + - os: linux + env: + - COMPILER=clang++-3.6 STDLIB=libc++ + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.6'] + packages: ["clang-3.6"] + + - os: linux + env: + - COMPILER=clang++-3.7 STDLIB=libc++ + addons: + apt: + sources: ['ubuntu-toolchain-r-test', 'llvm-toolchain-precise-3.7'] + packages: ["clang-3.7"] + + - os: osx + osx_image: xcode6.4 + env: + - COMPILER=clang++ V='Apple LLVM 6.4' + - COMPILER=clang++ V='Apple LLVM 6.4' WITH_CPP14=true + + - os: osx + osx_image: xcode7 + env: + - COMPILER=clang++ V='Apple LLVM 7.0' + - COMPILER=clang++ V='Apple LLVM 7.0' WITH_CPP14=true + +before_install: + - | + if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then + brew install cmake + fi + + - CMAKE_CXX_FLAGS+=" -Wall" + + - if [[ "${WITH_CPP14}" == "true" ]]; then CMAKE_OPTIONS+=" -DWITH_CPP14=1"; fi + - if [[ "${STDLIB}" == "libc++" ]]; then CMAKE_CXX_FLAGS+=" -stdlib=libc++"; fi + + - sh ${COMPILER} --version || true before_script: - - cmake -DWITH_TESTS=1 -DWITH_EXAMPLE=1 . + - rm -rf build/ + - mkdir build + - cd build + - cmake -DCMAKE_CXX_COMPILER=${COMPILER} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} -DWITH_TESTS=1 -DWITH_EXAMPLE=1 ${CMAKE_OPTIONS} .. script: - cmake --build . diff --git a/CMakeLists.txt b/CMakeLists.txt index 118297e..201f5f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,8 @@ cmake_minimum_required(VERSION 2.6.4) option(WITH_TESTS "Build tests." OFF) option(WITH_EXAMPLE "Build example." OFF) option(WITH_STATIC "Build static libs." ON) +option(WITH_CPP11 "Build with C++11." ON) +option(WITH_CPP14 "Build with C++14." OFF) project(docopt.cpp) include_directories("${PROJECT_SOURCE_DIR}") @@ -22,7 +24,11 @@ if(WITH_STATIC) endif() add_library(docopt SHARED ${DOCOPT_SRC}) -add_definitions("-std=c++11") +if(WITH_CPP14) + add_definitions("-std=c++14") +elseif(WITH_CPP11) + add_definitions("-std=c++11") +endif() ######################################################################## # tests From 4840f3877bbeb2be4ace550cf8e44f333ad00e78 Mon Sep 17 00:00:00 2001 From: Matt Kline Date: Wed, 21 Oct 2015 15:29:59 -0700 Subject: [PATCH 12/70] value::asLong will convert from string to long This is less surprising behavior when calling asLong() on an arg expected to be an integer value. --- docopt_value.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docopt_value.h b/docopt_value.h index aaf9aba..c78fb1b 100644 --- a/docopt_value.h +++ b/docopt_value.h @@ -13,6 +13,7 @@ #include #include // std::hash #include +#include // sscanf namespace docopt { @@ -257,6 +258,16 @@ namespace docopt { inline long value::asLong() const { + // Attempt to convert a string to a long + if (kind == Kind::String) { + // Doesn't guard against trailing characters, + // but doing so (if desired) would be trivial. + long ret; + if (sscanf(variant.strValue.c_str(), "%ld", &ret) == 1) { + return ret; + } + // else fall through + } throwIfNotKind(Kind::Long); return variant.longValue; } From 2831ca6b38fa7baaf8185f39fefa0d8719b664f2 Mon Sep 17 00:00:00 2001 From: Matt Kline Date: Wed, 21 Oct 2015 15:41:14 -0700 Subject: [PATCH 13/70] Use stol instead of sscanf --- docopt_value.h | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/docopt_value.h b/docopt_value.h index c78fb1b..fc5db0e 100644 --- a/docopt_value.h +++ b/docopt_value.h @@ -13,7 +13,6 @@ #include #include // std::hash #include -#include // sscanf namespace docopt { @@ -261,12 +260,9 @@ namespace docopt { // Attempt to convert a string to a long if (kind == Kind::String) { // Doesn't guard against trailing characters, - // but doing so (if desired) would be trivial. - long ret; - if (sscanf(variant.strValue.c_str(), "%ld", &ret) == 1) { - return ret; - } - // else fall through + // but doing so (if desired) would be trivial by checking pos. + std::size_t pos; + return stol(variant.strValue, &pos); // Throws if it can't convert } throwIfNotKind(Kind::Long); return variant.longValue; From 4edf04315d8ce6b5ab22e6a84cf32e2005fdbbc7 Mon Sep 17 00:00:00 2001 From: Christophe Philemotte Date: Fri, 25 Sep 2015 17:37:46 -0600 Subject: [PATCH 14/70] Fix wrong merge and recognition of short/long option * match short and long option when provided * regexp can recognize if the option is short or not --- docopt.cpp | 268 ++++++++++++++++++++++++++--------------------------- 1 file changed, 134 insertions(+), 134 deletions(-) diff --git a/docopt.cpp b/docopt.cpp index 2aed8a4..71d69c8 100644 --- a/docopt.cpp +++ b/docopt.cpp @@ -47,7 +47,7 @@ void value::throwIfNotKind(Kind expected) const { if (kind == expected) return; - + std::string error = "Illegal cast to "; error += kindAsString(expected); error += "; type is actually "; @@ -98,7 +98,7 @@ bool Required::match(PatternList& left, std::vector { auto l = left; auto c = collected; - + for(auto const& pattern : fChildren) { bool ret = pattern->match(l, c); if (!ret) { @@ -106,7 +106,7 @@ bool Required::match(PatternList& left, std::vector return false; } } - + left = std::move(l); collected = std::move(c); return true; @@ -118,9 +118,9 @@ bool LeafPattern::match(PatternList& left, std::vector const& p) { return p->name()==name(); }); @@ -144,7 +144,7 @@ bool LeafPattern::match(PatternList& left, std::vectorsetValue(value{val}); @@ -166,14 +166,14 @@ Option Option::parse(std::string const& option_description) std::string shortOption, longOption; int argcount = 0; value val { false }; - + auto double_space = option_description.find(" "); auto options_end = option_description.end(); if (double_space != std::string::npos) { options_end = option_description.begin() + double_space; } - - static const std::regex pattern {"(--|-)?(.*?)([,= ]|$)"}; + + static const std::regex pattern {"(-{1,2})?(.*?)([,= ]|$)"}; for(std::sregex_iterator i {option_description.begin(), options_end, pattern, std::regex_constants::match_not_null}, e{}; i != e; @@ -182,9 +182,9 @@ Option Option::parse(std::string const& option_description) std::smatch const& match = *i; if (match[1].matched) { // [1] is optional. if (match[1].length()==1) { - shortOption = "-" + match[2].str(); + shortOption = "-" + match[2].str(); } else { - longOption = "--" + match[2].str(); + longOption = "--" + match[2].str(); } } else if (match[2].length() > 0) { // [2] always matches. std::string m = match[2]; @@ -210,7 +210,7 @@ Option Option::parse(std::string const& option_description) val = match[1].str(); } } - + return {std::move(shortOption), std::move(longOption), argcount, @@ -220,36 +220,36 @@ Option Option::parse(std::string const& option_description) bool OneOrMore::match(PatternList& left, std::vector>& collected) const { assert(fChildren.size() == 1); - + auto l = left; auto c = collected; - + bool matched = true; size_t times = 0; - + decltype(l) l_; bool firstLoop = true; - + while (matched) { // could it be that something didn't match but changed l or c? matched = fChildren[0]->match(l, c); - + if (matched) ++times; - + if (firstLoop) { firstLoop = false; } else if (l == l_) { break; } - + l_ = l; } - + if (times == 0) { return false; } - + left = std::move(l); collected = std::move(c); return true; @@ -258,9 +258,9 @@ bool OneOrMore::match(PatternList& left, std::vector>& collected) const { using Outcome = std::pair>>; - + std::vector outcomes; - + for(auto const& pattern : fChildren) { // need a copy so we apply the same one for every iteration auto l = left; @@ -270,16 +270,16 @@ bool Either::match(PatternList& left, std::vector>& outcomes.emplace_back(std::move(l), std::move(c)); } } - + auto min = std::min_element(outcomes.begin(), outcomes.end(), [](Outcome const& o1, Outcome const& o2) { return o1.first.size() < o2.first.size(); }); - + if (min == outcomes.end()) { // (left, collected) unchanged return false; } - + std::tie(left, collected) = std::move(*min); return true; } @@ -287,7 +287,7 @@ bool Either::match(PatternList& left, std::vector>& std::pair> Argument::single_match(PatternList const& left) const { std::pair> ret {}; - + for(size_t i = 0, size = left.size(); i < size; ++i) { auto arg = dynamic_cast(left[i].get()); @@ -297,14 +297,14 @@ std::pair> Argument::single_match(PatternLi break; } } - + return ret; } std::pair> Command::single_match(PatternList const& left) const { std::pair> ret {}; - + for(size_t i = 0, size = left.size(); i < size; ++i) { auto arg = dynamic_cast(left[i].get()); @@ -316,14 +316,14 @@ std::pair> Command::single_match(PatternLis break; } } - + return ret; } std::pair> Option::single_match(PatternList const& left) const { std::pair> ret {}; - + for(size_t i = 0, size = left.size(); i < size; ++i) { auto leaf = std::dynamic_pointer_cast(left[i]); @@ -333,7 +333,7 @@ std::pair> Option::single_match(PatternList break; } } - + return ret; } @@ -351,13 +351,13 @@ void BranchPattern::fix_repeating_arguments() for(auto const& e : group_set) { if (group_set.count(e) == 1) continue; - + LeafPattern* leaf = dynamic_cast(e.get()); if (!leaf) continue; - + bool ensureList = false; bool ensureInt = false; - + if (dynamic_cast(leaf)) { ensureInt = true; } else if (dynamic_cast(leaf)) { @@ -369,7 +369,7 @@ void BranchPattern::fix_repeating_arguments() ensureInt = true; } } - + if (ensureList) { std::vector newValue; if (leaf->getValue().isString()) { @@ -388,37 +388,37 @@ void BranchPattern::fix_repeating_arguments() std::vector transform(PatternList pattern) { std::vector result; - + std::vector groups; groups.emplace_back(std::move(pattern)); - + while(!groups.empty()) { // pop off the first element auto children = std::move(groups[0]); groups.erase(groups.begin()); - + // find the first branch node in the list auto child_iter = std::find_if(children.begin(), children.end(), [](std::shared_ptr const& p) { return dynamic_cast(p.get()); }); - + // no branch nodes left : expansion is complete for this grouping if (child_iter == children.end()) { result.emplace_back(std::move(children)); continue; } - + // pop the child from the list auto child = std::move(*child_iter); children.erase(child_iter); - + // expand the branch in the appropriate way if (Either* either = dynamic_cast(child.get())) { // "[e] + children" for each child 'e' in Either for(auto const& eitherChild : either->children()) { PatternList group = { eitherChild }; group.insert(group.end(), children.begin(), children.end()); - + groups.emplace_back(std::move(group)); } } else if (OneOrMore* oneOrMore = dynamic_cast(child.get())) { @@ -427,19 +427,19 @@ std::vector transform(PatternList pattern) PatternList group = subchildren; group.insert(group.end(), subchildren.begin(), subchildren.end()); group.insert(group.end(), children.begin(), children.end()); - + groups.emplace_back(std::move(group)); } else { // Required, Optional, OptionsShortcut BranchPattern* branch = dynamic_cast(child.get()); - + // child.children + children PatternList group = branch->children(); group.insert(group.end(), children.begin(), children.end()); - + groups.emplace_back(std::move(group)); } } - + return result; } @@ -449,11 +449,11 @@ class Tokens { : fTokens(std::move(tokens)), fIsParsingArgv(isParsingArgv) {} - + explicit operator bool() const { return fIndex < fTokens.size(); } - + static Tokens from_pattern(std::string const& source) { static const std::regex re_separators { "(?:\\s*)" // any spaces (non-matching subgroup) @@ -462,7 +462,7 @@ class Tokens { "|" "\\.\\.\\." // elipsis ")" }; - + static const std::regex re_strings { "(?:\\s*)" // any spaces (non-matching subgroup) "(" @@ -470,13 +470,13 @@ class Tokens { "|" "\\S+" // string without <> ")" }; - + // We do two stages of regex matching. The '[]()' and '...' are strong delimeters // and need to be split out anywhere they occur (even at the end of a token). We // first split on those, and then parse the stuff between them to find the string // tokens. This is a little harder than the python version, since they have regex.split // and we dont have anything like that. - + std::vector tokens; std::for_each(std::sregex_iterator{ source.begin(), source.end(), re_separators }, std::sregex_iterator{}, @@ -491,24 +491,24 @@ class Tokens { tokens.push_back(m[1].str()); }); } - + // handle the delimter token itself if (match[1].matched) { tokens.push_back(match[1].str()); } }); - + return Tokens(tokens, false); } - + std::string const& current() const { if (*this) return fTokens[fIndex]; - + static std::string const empty; return empty; } - + std::string the_rest() const { if (!*this) return {}; @@ -516,28 +516,28 @@ class Tokens { fTokens.end(), " "); } - + std::string pop() { return std::move(fTokens.at(fIndex++)); } - + bool isParsingArgv() const { return fIsParsingArgv; } - + struct OptionError : std::runtime_error { using runtime_error::runtime_error; }; - + private: std::vector fTokens; size_t fIndex = 0; bool fIsParsingArgv; }; - + // Get all instances of 'T' from the pattern template std::vector flat_filter(Pattern& pattern) { std::vector flattened = pattern.flat([](Pattern const* p) -> bool { return dynamic_cast(p); }); - + // now, we're guaranteed to have T*'s, so just use static_cast std::vector ret; std::transform(flattened.begin(), flattened.end(), std::back_inserter(ret), [](Pattern* p) { @@ -559,7 +559,7 @@ std::vector parse_section(std::string const& name, std::string cons ")", std::regex::icase }; - + std::vector ret; std::for_each(std::sregex_iterator(source.begin(), source.end(), re_section_pattern), std::sregex_iterator(), @@ -567,20 +567,20 @@ std::vector parse_section(std::string const& name, std::string cons { ret.push_back(trim(match[1].str())); }); - + return ret; } bool is_argument_spec(std::string const& token) { if (token.empty()) return false; - + if (token[0]=='<' && token[token.size()-1]=='>') return true; - + if (std::all_of(token.begin(), token.end(), &::isupper)) return true; - + return false; } @@ -599,20 +599,20 @@ PatternList parse_long(Tokens& tokens, std::vector