8000 Header only by rmpowell77 · Pull Request #37 · docopt/docopt.cpp · GitHub
[go: up one dir, main page]

Skip to content

Header only #37

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 15, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Header only changes.
Allow a user to specify they want header only with the macro DOCOPT_HEADER_ONLY.  docopt.h will include docopt.cpp as inline functions.
  • Loading branch information
rmpowell77 committed Jan 25, 2016
commit d11d4e03566127e3a3e85f56c75c409f25ff09a3
304 changes: 10 additions & 294 deletions docopt.cpp
8000
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
// Copyright (c) 2013 Jared Grubb. All rights reserved.
//

#define NO_RAW_FOR_LOOPS 0

#include "docopt.h"
#include "docopt_util.h"
#include "docopt_private.h"
Expand All @@ -24,38 +26,6 @@

using namespace docopt;

DocoptExitHelp::DocoptExitHelp()
: std::runtime_error("Docopt --help argument encountered")
{}

DocoptExitVersion::DocoptExitVersion()
: std::runtime_error("Docopt --version argument encountered")
{}

const char* value::kindAsString(Kind kind)
{
switch (kind) {
case Kind::Empty: return "empty";
case Kind::Bool: return "bool";
case Kind::Long: return "long";
case Kind::String: return "string";
case Kind::StringList: return "string-list";
}
return "unknown";
}

void value::throwIfNotKind(Kind expected) const
{
if (kind == expected)
return;

std::string error = "Illegal cast to ";
error += kindAsString(expected);
error += "; type is actually ";
error += kindAsString(kind);
throw std::runtime_error(std::move(error));
}

std::ostream& docopt::operator<<(std::ostream& os, value const& val)
{
if (val.isBool()) {
Expand Down Expand Up @@ -89,255 +59,6 @@ std::ostream& docopt::operator<<(std::ostream& os, value const& val)
#pragma mark -
#pragma mark Pattern types

std::vector<LeafPattern*> Pattern::leaves() {
std::vector<LeafPattern*> ret;
collect_leaves(ret);
return ret;
}

bool Required::match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
{
auto l = left;
auto c = collected;

for(auto const& pattern : fChildren) {
bool ret = pattern->match(l, c);
if (!ret) {
// leave (left, collected) untouched
return false;
}
}

left = std::move(l);
collected = std::move(c);
return true;
}

bool LeafPattern::match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
{
auto match = single_match(left);
if (!match.second) {
return false;
}

left.erase(left.begin()+static_cast<std::ptrdiff_t>(match.first));

auto same_name = std::find_if(collected.begin(), collected.end(), [&](std::shared_ptr<LeafPattern> const& p) {
return p->name()==name();
});
if (getValue().isLong()) {
long val = 1;
if (same_name == collected.end()) {
collected.push_back(match.second);
match.second->setValue(value{val});
} else if ((**same_name).getValue().isLong()) {
val += (**same_name).getValue().asLong();
(**same_name).setValue(value{val});
} else {
(**same_name).setValue(value{val});
}
} else if (getValue().isStringList()) {
std::vector<std::string> val;
if (match.second->getValue().isString()) {
val.push_back(match.second->getValue().asString());
} else if (match.second->getValue().isStringList()) {
val = match.second->getValue().asStringList();
} else {
/// cant be!?
}

if (same_name == collected.end()) {
collected.push_back(match.second);
match.second->setValue(value{val});
} else if ((**same_name).getValue().isStringList()) {
std::vector<std::string> const& list = (**same_name).getValue().asStringList();
val.insert(val.begin(), list.begin(), list.end());
(**same_name).setValue(value{val});
} else {
(**same_name).setValue(value{val});
}
} else {
collected.push_back(match.second);
}
return true;
}

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() + static_cast<std::ptrdiff_t>(double_space);
}

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;
++i)
{
std::smatch const& match = *i;
if (match[1].matched) { // [1] is optional.
if (match[1].length()==1) {
shortOption = "-" + match[2].str();
} else {
longOption = "--" + match[2].str();
}
} else if (match[2].length() > 0) { // [2] always matches.
std::string m = match[2];
argcount = 1;
} else {
// delimeter
}

if (match[3].length() == 0) { // [3] always matches.
// Hit end of string. For some reason 'match_not_null' will let us match empty
// at the end, and then we'll spin in an infinite loop. So, if we hit an empty
// match, we know we must be at the end.
break;
}
}

if (argcount) {
std::smatch match;
if (std::regex_search(options_end, option_description.end(),
match,
std::regex{"\\[default: (.*)\\]", std::regex::icase}))
{
val = match[1].str();
}
}

return {std::move(shortOption),
std::move(longOption),
argcount,
std::move(val)};
}

bool OneOrMore::match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& 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;
}

bool Either::match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
{
using Outcome = std::pair<PatternList, std::vector<std::shared_ptr<LeafPattern>>>;

std::vector<Outcome> outcomes;

for(auto const& pattern : fChildren) {
// need a copy so we apply the same one for every iteration
auto l = left;
auto c = collected;
bool matched = pattern->match(l, c);
if (matched) {
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;
}

std::pair<size_t, std::shared_ptr<LeafPattern>> Argument::single_match(PatternList const& left) const
{
std::pair<size_t, std::shared_ptr<LeafPattern>> ret {};

for(size_t i = 0, size = left.size(); i < size; ++i)
{
auto arg = dynamic_cast<Argument const*>(left[i].get());
if (arg) {
ret.first = i;
ret.second = std::make_shared<Argument>(name(), arg->getValue());
break;
}
}

return ret;
}

std::pair<size_t, std::shared_ptr<LeafPattern>> Command::single_match(PatternList const& left) const
{
std::pair<size_t, std::shared_ptr<LeafPattern>> ret {};

for(size_t i = 0, size = left.size(); i < size; ++i)
{
auto arg = dynamic_cast<Argument const*>(left[i].get());
if (arg) {
if (name() == arg->getValue()) {
ret.first = i;
ret.second = std::make_shared<Command>(name(), value{true});
}
break;
}
}

return ret;
}

std::pair<size_t, std::shared_ptr<LeafPattern>> Option::single_match(PatternList const& left) const
{
std::pair<size_t, std::shared_ptr<LeafPattern>> ret {};

for(size_t i = 0, size = left.size(); i < size; ++i)
{
auto leaf = std::dynamic_pointer_cast<LeafPattern>(left[i]);
if (leaf && name() == leaf->name()) {
ret.first = i;
ret.second = leaf;
break;
}
}

return ret;
}

#pragma mark -
#pragma mark Parsing stuff

Expand Down Expand Up @@ -803,20 +524,13 @@ static PatternList parse_seq(Tokens& tokens, std::vector<Option>& options)
return ret;
}

static std::shared_ptr<Pattern> maybe_collapse_to_required(PatternList&& seq)
{
if (seq.size()==1) {
return std::move(seq[0]);
}
return std::make_shared<Required>(std::move(seq));
}

static std::shared_ptr<Pattern> maybe_collapse_to_either(PatternList&& seq)
template<typename Which>
std::shared_ptr<Pattern> maybe_collapse_to(PatternList&& seq)
{
if (seq.size()==1) {
return std::move(seq[0]);
}
return std::make_shared<Either>(std::move(seq));
return std::make_shared<Which>(std::move(seq));
}

PatternList parse_expr(Tokens& tokens, std::vector<Option>& options)
Expand All @@ -829,15 +543,15 @@ PatternList parse_expr(Tokens& tokens, std::vector<Option>& options)
return seq;

PatternList ret;
ret.emplace_back(maybe_collapse_to_required(std::move(seq)));
ret.emplace_back(maybe_collapse_to<Required>(std::move(seq)));

while (tokens.current() == "|") {
tokens.pop();
seq = parse_seq(tokens, options);
ret.emplace_back(maybe_collapse_to_required(std::move(seq)));
ret.emplace_back(maybe_collapse_to<Required>(std::move(seq)));
}

return { maybe_collapse_to_either(std::move(ret)) };
return { maybe_collapse_to<Either>(std::move(ret)) };
}

static Required parse_pattern(std::string const& source, std::vector<Option>& options)
Expand Down Expand Up @@ -1001,6 +715,7 @@ static std::pair<Required, std::vector<Option>> create_pattern_tree(std::string
return { std::move(pattern), std::move(options) };
}

DOCOPT_INLINE
std::map<std::string, value>
docopt::docopt_parse(std::string const& doc,
std::vector<std::string> const& argv,
Expand Down Expand Up @@ -1050,6 +765,7 @@ docopt::docopt_parse(std::string const& doc,
throw DocoptArgumentError("Arguments did not match expected patterns"); // BLEH. Bad error.
}

DOCOPT_INLINE
std::map<std::string, value>
docopt::docopt(std::string const& doc,
std::vector<std::string> const& argv,
Expand Down
16 changes: 13 additions & 3 deletions docopt.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
#include <vector>
#include <string>

#ifdef DOCOPT_HEADER_ONLY
#define DOCOPT_INLINE inline
#else
#define DOCOPT_INLINE
#endif

namespace docopt {

// Usage string could not be parsed (ie, the developer did something wrong)
Expand All @@ -24,10 +30,10 @@ namespace docopt {
struct DocoptArgumentError : std::runtime_error { using runtime_error::runtime_error; };

// Arguments contained '--help' and parsing was aborted early
struct DocoptExitHelp : std::runtime_error { DocoptExitHelp(); };
struct DocoptExitHelp : std::runtime_error { DocoptExitHelp() : std::runtime_error("Docopt --help argument encountered"){} };

// Arguments contained '--version' and parsing was aborted early
struct DocoptExitVersion : std::runtime_error { DocoptExitVersion(); };
struct DocoptExitVersion : std::runtime_error { DocoptExitVersion() : std::runtime_error("Docopt --version argument encountered") {} };

/// Parse user options from the given option string.
///
Expand Down Expand Up @@ -62,4 +68,8 @@ namespace docopt {
bool options_first = false) noexcept;
}

#ifdef DOCOPT_HEADER_ONLY
#include "docopt.cpp"
#endif

#endif /* defined(docopt__docopt_h_) */
Loading
0