Skip to content

Commit d11d4e0

Browse files
committed
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.
1 parent b6ba473 commit d11d4e0

File tree

4 files changed

+335
-299
lines changed

4 files changed

+335
-299
lines changed

docopt.cpp

Lines changed: 10 additions & 294 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
// Copyright (c) 2013 Jared Grubb. All rights reserved.
77
//
88

9+
#define NO_RAW_FOR_LOOPS 0
10+
911
#include "docopt.h"
1012
#include "docopt_util.h"
1113
#include "docopt_private.h"
@@ -24,38 +26,6 @@
2426

2527
using namespace docopt;
2628

27-
DocoptExitHelp::DocoptExitHelp()
28-
: std::runtime_error("Docopt --help argument encountered")
29-
{}
30-
31-
DocoptExitVersion::DocoptExitVersion()
32-
: std::runtime_error("Docopt --version argument encountered")
33-
{}
34-
35-
const char* value::kindAsString(Kind kind)
36-
{
37-
switch (kind) {
38-
case Kind::Empty: return "empty";
39-
case Kind::Bool: return "bool";
40-
case Kind::Long: return "long";
41-
case Kind::String: return "string";
42-
case Kind::StringList: return "string-list";
43-
}
44-
return "unknown";
45-
}
46-
47-
void value::throwIfNotKind(Kind expected) const
48-
{
49-
if (kind == expected)
50-
return;
51-
52-
std::string error = "Illegal cast to ";
53-
error += kindAsString(expected);
54-
error += "; type is actually ";
55-
error += kindAsString(kind);
56-
throw std::runtime_error(std::move(error));
57-
}
58-
5929
std::ostream& docopt::operator<<(std::ostream& os, value const& val)
6030
{
6131
if (val.isBool()) {
@@ -89,255 +59,6 @@ std::ostream& docopt::operator<<(std::ostream& os, value const& val)
8959
#pragma mark -
9060
#pragma mark Pattern types
9161

92-
std::vector<LeafPattern*> Pattern::leaves() {
93-
std::vector<LeafPattern*> ret;
94-
collect_leaves(ret);
95-
return ret;
96-
}
97-
98-
bool Required::match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
99-
{
100-
auto l = left;
101-
auto c = collected;
102-
103-
for(auto const& pattern : fChildren) {
104-
bool ret = pattern->match(l, c);
105-
if (!ret) {
106-
// leave (left, collected) untouched
107-
return false;
108-
}
109-
}
110-
111-
left = std::move(l);
112-
collected = std::move(c);
113-
return true;
114-
}
115-
116-
bool LeafPattern::match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
117-
{
118-
auto match = single_match(left);
119-
if (!match.second) {
120-
return false;
121-
}
122-
123-
left.erase(left.begin()+static_cast<std::ptrdiff_t>(match.first));
124-
125-
auto same_name = std::find_if(collected.begin(), collected.end(), [&](std::shared_ptr<LeafPattern> const& p) {
126-
return p->name()==name();
127-
});
128-
if (getValue().isLong()) {
129-
long val = 1;
130-
if (same_name == collected.end()) {
131-
collected.push_back(match.second);
132-
match.second->setValue(value{val});
133-
} else if ((**same_name).getValue().isLong()) {
134-
val += (**same_name).getValue().asLong();
135-
(**same_name).setValue(value{val});
136-
} else {
137-
(**same_name).setValue(value{val});
138-
}
139-
} else if (getValue().isStringList()) {
140-
std::vector<std::string> val;
141-
if (match.second->getValue().isString()) {
142-
val.push_back(match.second->getValue().asString());
143-
} else if (match.second->getValue().isStringList()) {
144-
val = match.second->getValue().asStringList();
145-
} else {
146-
/// cant be!?
147-
}
148-
149-
if (same_name == collected.end()) {
150-
collected.push_back(match.second);
151-
match.second->setValue(value{val});
152-
} else if ((**same_name).getValue().isStringList()) {
153-
std::vector<std::string> const& list = (**same_name).getValue().asStringList();
154-
val.insert(val.begin(), list.begin(), list.end());
155-
(**same_name).setValue(value{val});
156-
} else {
157-
(**same_name).setValue(value{val});
158-
}
159-
} else {
160-
collected.push_back(match.second);
161-
}
162-
return true;
163-
}
164-
165-
Option Option::parse(std::string const& option_description)
166-
{
167-
std::string shortOption, longOption;
168-
int argcount = 0;
169-
value val { false };
170-
171-
auto double_space = option_description.find(" ");
172-
auto options_end = option_description.end();
173-
if (double_space != std::string::npos) {
174-
options_end = option_description.begin() + static_cast<std::ptrdiff_t>(double_space);
175-
}
176-
177-
static const std::regex pattern {"(-{1,2})?(.*?)([,= ]|$)"};
178-
for(std::sregex_iterator i {option_description.begin(), options_end, pattern, std::regex_constants::match_not_null},
179-
e{};
180-
i != e;
181-
++i)
182-
{
183-
std::smatch const& match = *i;
184-
if (match[1].matched) { // [1] is optional.
185-
if (match[1].length()==1) {
186-
shortOption = "-" + match[2].str();
187-
} else {
188-
longOption = "--" + match[2].str();
189-
}
190-
} else if (match[2].length() > 0) { // [2] always matches.
191-
std::string m = match[2];
192-
argcount = 1;
193-
} else {
194-
// delimeter
195-
}
196-
197-
if (match[3].length() == 0) { // [3] always matches.
198-
// Hit end of string. For some reason 'match_not_null' will let us match empty
199-
// at the end, and then we'll spin in an infinite loop. So, if we hit an empty
200-
// match, we know we must be at the end.
201-
break;
202-
}
203-
}
204-
205-
if (argcount) {
206-
std::smatch match;
207-
if (std::regex_search(options_end, option_description.end(),
208-
match,
209-
std::regex{"\\[default: (.*)\\]", std::regex::icase}))
210-
{
211-
val = match[1].str();
212-
}
213-
}
214-
215-
return {std::move(shortOption),
216-
std::move(longOption),
217-
argcount,
218-
std::move(val)};
219-
}
220-
221-
bool OneOrMore::match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
222-
{
223-
assert(fChildren.size() == 1);
224-
225-
auto l = left;
226-
auto c = collected;
227-
228-
bool matched = true;
229-
size_t times = 0;
230-
231-
decltype(l) l_;
232-
bool firstLoop = true;
233-
234-
while (matched) {
235-
// could it be that something didn't match but changed l or c?
236-
matched = fChildren[0]->match(l, c);
237-
238-
if (matched)
239-
++times;
240-
241-
if (firstLoop) {
242-
firstLoop = false;
243-
} else if (l == l_) {
244-
break;
245-
}
246-
247-
l_ = l;
248-
}
249-
250-
if (times == 0) {
251-
return false;
252-
}
253-
254-
left = std::move(l);
255-
collected = std::move(c);
256-
return true;
257-
}
258-
259-
bool Either::match(PatternList& left, std::vector<std::shared_ptr<LeafPattern>>& collected) const
260-
{
261-
using Outcome = std::pair<PatternList, std::vector<std::shared_ptr<LeafPattern>>>;
262-
263-
std::vector<Outcome> outcomes;
264-
265-
for(auto const& pattern : fChildren) {
266-
// need a copy so we apply the same one for every iteration
267-
auto l = left;
268-
auto c = collected;
269-
bool matched = pattern->match(l, c);
270-
if (matched) {
271-
outcomes.emplace_back(std::move(l), std::move(c));
272-
}
273-
}
274-
275-
auto min = std::min_element(outcomes.begin(), outcomes.end(), [](Outcome const& o1, Outcome const& o2) {
276-
return o1.first.size() < o2.first.size();
277-
});
278-
279-
if (min == outcomes.end()) {
280-
// (left, collected) unchanged
281-
return false;
282-
}
283-
284-
std::tie(left, collected) = std::move(*min);
285-
return true;
286-
}
287-
288-
std::pair<size_t, std::shared_ptr<LeafPattern>> Argument::single_match(PatternList const& left) const
289-
{
290-
std::pair<size_t, std::shared_ptr<LeafPattern>> ret {};
291-
292-
for(size_t i = 0, size = left.size(); i < size; ++i)
293-
{
294-
auto arg = dynamic_cast<Argument const*>(left[i].get());
295-
if (arg) {
296-
ret.first = i;
297-
ret.second = std::make_shared<Argument>(name(), arg->getValue());
298-
break;
299-
}
300-
}
301-
302-
return ret;
303-
}
304-
305-
std::pair<size_t, std::shared_ptr<LeafPattern>> Command::single_match(PatternList const& left) const
306-
{
307-
std::pair<size_t, std::shared_ptr<LeafPattern>> ret {};
308-
309-
for(size_t i = 0, size = left.size(); i < size; ++i)
310-
{
311-
auto arg = dynamic_cast<Argument const*>(left[i].get());
312-
if (arg) {
313-
if (name() == arg->getValue()) {
314-
ret.first = i;
315-
ret.second = std::make_shared<Command>(name(), value{true});
316-
}
317-
break;
318-
}
319-
}
320-
321-
return ret;
322-
}
323-
324-
std::pair<size_t, std::shared_ptr<LeafPattern>> Option::single_match(PatternList const& left) const
325-
{
326-
std::pair<size_t, std::shared_ptr<LeafPattern>> ret {};
327-
328-
for(size_t i = 0, size = left.size(); i < size; ++i)
329-
{
330-
auto leaf = std::dynamic_pointer_cast<LeafPattern>(left[i]);
331-
if (leaf && name() == leaf->name()) {
332-
ret.first = i;
333-
ret.second = leaf;
334-
break;
335-
}
336-
}
337-
338-
return ret;
339-
}
340-
34162
#pragma mark -
34263
#pragma mark Parsing stuff
34364

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

806-
static std::shared_ptr<Pattern> maybe_collapse_to_required(PatternList&& seq)
807-
{
808-
if (seq.size()==1) {
809-
return std::move(seq[0]);
810-
}
811-
return std::make_shared<Required>(std::move(seq));
812-
}
813-
814-
static std::shared_ptr<Pattern> maybe_collapse_to_either(PatternList&& seq)
527+
template<typename Which>
528+
std::shared_ptr<Pattern> maybe_collapse_to(PatternList&& seq)
815529
{
816530
if (seq.size()==1) {
817531
return std::move(seq[0]);
818532
}
819-
return std::make_shared<Either>(std::move(seq));
533+
return std::make_shared<Which>(std::move(seq));
820534
}
821535

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

831545
PatternList ret;
832-
ret.emplace_back(maybe_collapse_to_required(std::move(seq)));
546+
ret.emplace_back(maybe_collapse_to<Required>(std::move(seq)));
833547

834548
while (tokens.current() == "|") {
835549
tokens.pop();
836550
seq = parse_seq(tokens, options);
837-
ret.emplace_back(maybe_collapse_to_required(std::move(seq)));
551+
ret.emplace_back(maybe_collapse_to<Required>(std::move(seq)));
838552
}
839553

840-
return { maybe_collapse_to_either(std::move(ret)) };
554+
return { maybe_collapse_to<Either>(std::move(ret)) };
841555
}
842556

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

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

768+
DOCOPT_INLINE
1053769
std::map<std::string, value>
1054770
docopt::docopt(std::string const& doc,
1055771
std::vector<std::string> const& argv,

docopt.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
#include <vector>
1616
#include <string>
1717

18+
#ifdef DOCOPT_HEADER_ONLY
19+
#define DOCOPT_INLINE inline
20+
#else
21+
#define DOCOPT_INLINE
22+
#endif
23+
1824
namespace docopt {
1925

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

2632
// Arguments contained '--help' and parsing was aborted early
27-
struct DocoptExitHelp : std::runtime_error { DocoptExitHelp(); };
28-
33+
struct DocoptExitHelp : std::runtime_error { DocoptExitHelp() : std::runtime_error("Docopt --help argument encountered"){} };
34+
2935
// Arguments contained '--version' and parsing was aborted early
30-
struct DocoptExitVersion : std::runtime_error { DocoptExitVersion(); };
36+
struct DocoptExitVersion : std::runtime_error { DocoptExitVersion() : std::runtime_error("Docopt --version argument encountered") {} };
3137

3238
/// Parse user options from the given option string.
3339
///
@@ -62,4 +68,8 @@ namespace docopt {
6268
bool options_first = false) noexcept;
6369
}
6470

71+
#ifdef DOCOPT_HEADER_ONLY
72+
#include "docopt.cpp"
73+
#endif
74+
6575
#endif /* defined(docopt__docopt_h_) */

0 commit comments

Comments
 (0)