8000 Header only changes. · lineCode/docopt.cpp@b45b9b6 · GitHub
[go: up one dir, main page]

Skip to content

Commit b45b9b6

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 d11d4e0 commit b45b9b6

File tree

3 files changed

+288
-320
lines changed

3 files changed

+288
-320
lines changed

docopt.cpp

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

9-
#define NO_RAW_FOR_LOOPS 0
10-
119
#include "docopt.h"
1210
#include "docopt_util.h"
1311
#include "docopt_private.h"
@@ -26,6 +24,30 @@
2624

2725
using namespace docopt;
2826

27+
const char* value::kindAsString(Kind kind)
28+
{
29+
switch (kind) {
30+
case Kind::Empty: return "empty";
31+
case Kind::Bool: return "bool";
32+
case Kind::Long: return "long";
33+
case Kind::String: return "string";
34+
case Kind::StringList: return "string-list";
35+
}
36+
return "unknown";
37+
}
38+
39+
void value::throwIfNotKind(Kind expected) const
40+
{
41+
if (kind == expected)
42+
return;
43+
44+
std::string error = "Illegal cast to ";
45+
error += kindAsString(expected);
46+
error += "; type is actually ";
47+
error += kindAsString(kind);
48+
throw std::runtime_error(std::move(error));
49+
}
50+
2951
std::ostream& docopt::operator<<(std::ostream& os, value const& val)
3052
{
3153
if (val.isBool()) {
@@ -59,6 +81,255 @@ std::ostream& docopt::operator<<(std::ostream& os, value const& val)
5981
#pragma mark -
6082
#pragma mark Pattern types
6183

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

@@ -524,13 +795,20 @@ static PatternList parse_seq(Tokens& tokens, std::vector<Option>& options)
524795
return ret;
525796
}
526797

527-
template<typename Which>
528-
std::shared_ptr<Pattern> maybe_collapse_to(PatternList&& seq)
798+
static std::shared_ptr<Pattern> maybe_collapse_to_required(PatternList&& seq)
799+
{
800+
if (seq.size()==1) {
801+
return std::move(seq[0]);
802+
}
803+
return std::make_shared<Required>(std::move(seq));
804+
}
805+
806+
static std::shared_ptr<Pattern> maybe_collapse_to_either(PatternList&& seq)
529807
{
530808
if (seq.size()==1) {
531809
return std::move(seq[0]);
532810
}
533-
return std::make_shared<Which>(std::move(seq));
811+
return std::make_shared<Either>(std::move(seq));
534812
}
535813

536814
PatternList parse_expr(Tokens& tokens, std::vector<Option>& options)
@@ -543,15 +821,15 @@ PatternList parse_expr(Tokens& tokens, std::vector<Option>& options)
543821
return seq;
544822

545823
PatternList ret;
546-
ret.emplace_back(maybe_collapse_to<Required>(std::move(seq)));
824+
ret.emplace_back(maybe_collapse_to_required(std::move(seq)));
547825

548826
while (tokens.current() == "|") {
549827
tokens.pop();
550828
seq = parse_seq(tokens, options);
551-
ret.emplace_back(maybe_collapse_to<Required>(std::move(seq)));
829+
ret.emplace_back(maybe_collapse_to_required(std::move(seq)));
552830
}
553831

554-
return { maybe_collapse_to<Either>(std::move(ret)) };
832+
return { maybe_collapse_to_either(std::move(ret)) };
555833
}
556834

557835
static Required parse_pattern(std::string const& source, std::vector<Option>& options)

0 commit comments

Comments
 (0)
0