1 #ifndef ROSE_CommandLine_IntervalParser_H
2 #define ROSE_CommandLine_IntervalParser_H
4 #include <Rose/StringUtility/Escape.h>
5 #include <Rose/StringUtility/NumberToString.h>
7 #include <boost/lexical_cast.hpp>
8 #include <boost/numeric/conversion/cast.hpp>
12 #include <rose_strtoull.h>
13 #include <Sawyer/CommandLine.h>
14 #include <Sawyer/Interval.h>
15 #include <Sawyer/IntervalSet.h>
19 namespace CommandLine {
31 template<
class Interval>
56 static std::string
docString(
const std::string &interval =
"interval",
const std::string &value =
"value") {
57 return (
"The " + interval +
" can be specified in a number of forms:"
58 " a single " + value +
" represents a singleton " + interval +
";"
59 " a first and inclusive last " + value +
" separated by a comma;"
60 " a begin and exclusive end " + value +
" separated by a hyphen;"
61 " a begin " + value +
" and count separated by a plus sign;"
62 " the word \"all\" represents the universal " + interval +
";"
63 " and the word \"empty\" or an empty string represents the empty " + interval +
"."
65 " When the " + interval +
" is specified as a range, the first " + value +
" must be less than or equal to"
66 " the second value. A " + value +
" can be specified in decimal, hexadecimal (leading \"0x\"),"
67 " octal (leading \"0\"), or binary (leading \"0b\"). The upper " + value +
" can be the word \"max\" when"
68 " appearing after a comma.");
75 static Interval
parse(
const char *input,
const char **rest) {
76 const char *s = input;
78 bool hadRangeError =
false, isEmpty =
false;
79 while (isspace(*s)) ++s;
81 if (!strcmp(s,
"all")) {
83 return Interval::whole();
86 if (!strcmp(s,
"empty")) {
93 uint64_t least64 = rose_strtoull(s, &r, 0);
95 throw std::runtime_error(
"unsigned integer expected for interval minimum");
98 typename Interval::Value least{};
100 least = boost::numeric_cast<typename Interval::Value>(least64);
101 }
catch (
const boost::bad_numeric_cast&) {
102 hadRangeError =
true;
107 typename Interval::Value greatest = least;
108 while (isspace(*s)) ++s;
111 if (0 == strncmp(s,
"max", 3)) {
112 greatest = Interval::whole().greatest();
114 r =
const_cast<char*
>(s);
117 uint64_t greatest64 = rose_strtoull(s, &r, 0);
119 throw std::runtime_error(
"unsigned integer expected for interval maximum");
121 hadRangeError =
true;
123 greatest = boost::numeric_cast<typename Interval::Value>(greatest64);
124 }
catch (
const boost::bad_numeric_cast&) {
125 hadRangeError =
true;
129 }
else if (
'-' == *s) {
132 uint64_t greatest64 = rose_strtoull(s, &r, 0);
134 throw std::runtime_error(
"unsigned integer expected for interval end");
136 hadRangeError =
true;
138 greatest = boost::numeric_cast<typename Interval::Value>(greatest64);
139 }
catch (
const boost::bad_numeric_cast&) {
140 hadRangeError =
true;
142 if (greatest == least)
146 }
else if (
'+' == *s) {
149 uint64_t size64 = rose_strtoull(s, &r, 0);
151 throw std::runtime_error(
"unsigned integer expected for interval size");
153 hadRangeError =
true;
154 typename Interval::Value size{};
156 size = boost::numeric_cast<typename Interval::Value>(size64);
157 }
catch (
const boost::bad_numeric_cast&) {
158 hadRangeError =
true;
162 greatest = least + size - 1;
170 std::string parsed(input, *rest - input);
172 throw std::range_error(
"overflow when parsing \"" + parsed +
"\"");
173 if (greatest < least)
174 throw std::range_error(
"interval seems backward: \"" + parsed +
"\"");
177 return Interval::hull(least, greatest);
188 static Interval
parse(
const std::string &input) {
189 const char *s = input.c_str();
190 const char *rest =
nullptr;
191 Interval retval =
parse(s, &rest);
192 while (isspace(*rest)) ++rest;
194 throw std::runtime_error(
"extra text after end of interval specification: \"" +
StringUtility::cEscape(rest) +
"\"");
199 static std::string
toString(
const Interval &interval) {
200 if (interval.isEmpty()) {
202 }
else if (interval == Interval::whole()) {
204 }
else if (interval.least() == interval.greatest()) {
205 if (interval.least() < 256) {
206 return boost::lexical_cast<std::string>(interval.least());
211 if (interval.greatest() < 256) {
212 return boost::lexical_cast<std::string>(interval.least()) +
"," +
213 boost::lexical_cast<std::string>(interval.greatest());
224 Interval val =
parse(input, rest);
225 std::string parsed(input, *rest - input);
230 template<
class Interval>
235 template<
class Interval>
240 template<
class Interval>
246 template<
class Interval>