1#ifndef ROSE_CommandLine_SuffixMultiplierParser_H
2#define ROSE_CommandLine_SuffixMultiplierParser_H
4#include <Rose/StringUtility/Escape.h>
5#include <rose_strtoull.h>
8#include <boost/numeric/conversion/cast.hpp>
14#include <Sawyer/CommandLine.h>
18namespace CommandLine {
36 enum class Preferred { NO, YES };
44 using Suffixes = std::map<std::string , Suffix>;
46 bool extendedSyntax_ =
false;
77 Ptr with(
const std::string &suffix, T multiplier, Preferred preferred = Preferred::YES) {
78 suffixes_[suffix] = Suffix{.multiplier = multiplier, .preferred = preferred};
79 return sharedFromThis().template dynamicCast<SuffixMultiplierParser>();
82 Ptr with(
const std::string &suffix, T multiplier,
const std::string &alias1,
const std::string &alias2 =
"",
83 const std::string &alias3 =
"",
const std::string &alias4 =
"") {
84 suffixes_[suffix] = Suffix{multiplier, Preferred::YES};
86 suffixes_[alias1] = Suffix{multiplier, Preferred::NO};
88 suffixes_[alias2] = Suffix{multiplier, Preferred::NO};
90 suffixes_[alias3] = Suffix{multiplier, Preferred::NO};
92 suffixes_[alias4] = Suffix{multiplier, Preferred::NO};
93 return sharedFromThis().template dynamicCast<SuffixMultiplierParser>();
114 return extendedSyntax_;
118 return sharedFromThis().template dynamicCast<SuffixMultiplierParser>();
123 T
parse(
const char *input,
const char **rest) {
124 const char *s = input;
125 const char *r =
nullptr;
128 while (isspace(*s)) ++s;
131 T n = parseNumber(s, &r, T{});
134 while (*r && !isdigit(*r))
138 std::string suffix(s, r);
139 auto found = suffixes_.find(suffix);
140 if (found == suffixes_.end()) {
142 r =
const_cast<char*
>(s);
145 total += n * found->second.multiplier;
157 const char *s = input.c_str();
158 const char *rest =
nullptr;
159 T retval =
parse(s, &rest);
160 while (isspace(*rest)) ++rest;
162 throw std::runtime_error(
"extra text after end of interval specification: \"" +
StringUtility::cEscape(rest) +
"\"");
166 static std::pair<T, T> quotientRemainder(T product, T divisor, uint64_t) {
167 uint64_t q64 = product / divisor;
168 uint64_t r64 = product % divisor;
170 T q = boost::numeric_cast<T>(q64);
171 T r = boost::numeric_cast<T>(r64);
173 }
catch (
const boost::bad_numeric_cast&) {
174 throw std::runtime_error(
"integer overflow");
178 static std::pair<T, T> quotientRemainder(T product, T divisor,
double) {
179 if (product < divisor) {
182 double qd = std::floor(product / divisor);
183 double rd = std::fmod(product, divisor);
185 T q = boost::numeric_cast<T>(qd);
186 T r = boost::numeric_cast<T>(rd);
188 }
catch (
const boost::bad_numeric_cast&) {
189 throw std::runtime_error(
"floating-point overflow");
200 using Pair = std::pair<std::string, Suffix>;
201 std::vector<Pair> byValue(suffixes_.begin(), suffixes_.end());
202 byValue.erase(std::remove_if(byValue.begin(), byValue.end(),
204 return a.second.preferred != Preferred::YES;
207 std::sort(byValue.begin(), byValue.end(),
208 [](
const Pair &a,
const Pair &b) {
209 if (a.second.multiplier != b.second.multiplier)
210 return a.second.multiplier < b.second.multiplier;
211 if (a.first.size() != b.first.size())
212 return a.first.size() > b.first.size();
213 return a.first < b.first;
215 byValue.erase(std::unique(byValue.begin(), byValue.end(),
216 [](
const Pair &a,
const Pair &b) {
217 return a.second.multiplier == b.second.multiplier;
224 while (!byValue.empty() && byValue.back().second.multiplier > value)
229 std::pair<T, T> qr = quotientRemainder(value, byValue.back().second.multiplier, T{});
230 assert(qr.first > 0);
231 retval += boost::lexical_cast<std::string>(qr.first) + byValue.back().first;
236 retval += boost::lexical_cast<std::string>(value);
245 T val = parse(input, rest);
246 std::string parsed(input, *rest - input);
251 T parseNumber(
const char *input,
const char **rest, uint64_t) {
256 uint64_t n = rose_strtoull(input, &r, extendedSyntax_ ? 0 : 10);
258 throw std::runtime_error(
"unsigned integer expected");
260 throw std::runtime_error(
"integer magnitude is too large");
262 T retval = boost::numeric_cast<T>(n);
265 }
catch (
const boost::bad_numeric_cast&) {
266 throw std::runtime_error(
"integer magnitude is too large");
271 T parseNumber(
const char *input,
const char **rest,
double) {
277 const char *significandEnd = input;
278 bool hadDecimalPoint;
279 while (isdigit(*significandEnd) || (
'.' == *significandEnd && !hadDecimalPoint)) {
280 if (
'.' == *significandEnd)
281 hadDecimalPoint =
true;
289 const char *end = significandEnd;
290 if (extendedSyntax_ && (
'e' == *end ||
'E' == *end)) {
292 if (
'+' == *end ||
'-' == *end)
295 while (isdigit(*end)) ++end;
297 end = significandEnd;
301 const std::string toParse(input, end);
302 const char *s = toParse.c_str();
305 double d = strtod(s, &r);
307 throw std::runtime_error(
"floating-point number expected");
309 throw std::runtime_error(
"floating-point value is out of range");
311 T retval = boost::numeric_cast<T>(d);
312 *rest = input + (r - s);
314 }
catch (
const boost::bad_numeric_cast&) {
315 throw std::runtime_error(
"floating-point value is out of range");
Parse values followed by unit names.
Ptr with(const std::string &suffix, T multiplier, Preferred preferred=Preferred::YES)
Insert a suffix definition.
T parse(const char *input, const char **rest)
Parse from a C string.
static Ptr instance(const Sawyer::CommandLine::ValueSaver::Ptr &valueSaver)
Allocating constructor.
Ptr extendedSyntax(bool b)
Property: Allow extended syntax for numberic values.
Ptr with(const std::string &suffix, T multiplier, const std::string &alias1, const std::string &alias2="", const std::string &alias3="", const std::string &alias4="")
Insert a suffix definition.
Sawyer::SharedPointer< SuffixMultiplierParser > Ptr
Shared-ownership pointer to a Rose::CommandLine::SuffixMultiplierParser.
bool extendedSyntax() const
Property: Allow extended syntax for numberic values.
std::string toString(T value)
Unparse to a string.
T parse(const std::string &input)
Parse from a C++ string.
static Ptr instance()
Default allocating constructor.
Information about a parsed switch value.
Base class parsing a value from input.
const ValueSaver::Ptr valueSaver() const
Property: functor responsible for saving a parsed value in user storage.
SharedPointer< ValueParser > sharedFromThis()
Create a shared pointer from this.
ROSE_UTIL_API std::string cEscape(const std::string &, char context='"')
Escapes characters that are special to C/C++.
Position within a command-line.