1#ifndef ROSE_CommandLine_SuffixMultiplierParser_H
2#define ROSE_CommandLine_SuffixMultiplierParser_H
5#include <Rose/StringUtility/Escape.h>
6#include <rose_strtoull.h>
9#include <boost/numeric/conversion/cast.hpp>
15#include <Sawyer/CommandLine.h>
19namespace CommandLine {
37 enum class Preferred { NO, YES };
45 using Suffixes = std::map<std::string , Suffix>;
47 bool extendedSyntax_ =
false;
78 Ptr with(
const std::string &suffix, T multiplier, Preferred preferred = Preferred::YES) {
79 suffixes_[suffix] = Suffix{.multiplier = multiplier, .preferred = preferred};
83 Ptr with(
const std::string &suffix, T multiplier,
const std::string &alias1,
const std::string &alias2 =
"",
84 const std::string &alias3 =
"",
const std::string &alias4 =
"") {
85 suffixes_[suffix] = Suffix{multiplier, Preferred::YES};
87 suffixes_[alias1] = Suffix{multiplier, Preferred::NO};
89 suffixes_[alias2] = Suffix{multiplier, Preferred::NO};
91 suffixes_[alias3] = Suffix{multiplier, Preferred::NO};
93 suffixes_[alias4] = Suffix{multiplier, Preferred::NO};
115 return extendedSyntax_;
124 T
parse(
const char *input,
const char **rest) {
125 const char *s = input;
126 const char *r =
nullptr;
129 while (isspace(*s)) ++s;
132 T n = parseNumber(s, &r, T{});
135 while (*r && !isdigit(*r))
139 std::string suffix(s, r);
140 auto found = suffixes_.find(suffix);
141 if (found == suffixes_.end()) {
143 r =
const_cast<char*
>(s);
146 total += n * found->second.multiplier;
158 const char *s = input.c_str();
159 const char *rest =
nullptr;
160 T retval =
parse(s, &rest);
161 while (isspace(*rest)) ++rest;
163 throw std::runtime_error(
"extra text after end of interval specification: \"" +
StringUtility::cEscape(rest) +
"\"");
167 static std::pair<T, T> quotientRemainder(T product, T divisor, uint64_t) {
168 uint64_t q64 = product / divisor;
169 uint64_t r64 = product % divisor;
171 T q = boost::numeric_cast<T>(q64);
172 T r = boost::numeric_cast<T>(r64);
174 }
catch (
const boost::bad_numeric_cast&) {
175 throw std::runtime_error(
"integer overflow");
179 static std::pair<T, T> quotientRemainder(T product, T divisor,
double) {
180 if (product < divisor) {
183 double qd = std::floor(product / divisor);
184 double rd = std::fmod(product, divisor);
186 T q = boost::numeric_cast<T>(qd);
187 T r = boost::numeric_cast<T>(rd);
189 }
catch (
const boost::bad_numeric_cast&) {
190 throw std::runtime_error(
"floating-point overflow");
201 using Pair = std::pair<std::string, Suffix>;
202 std::vector<Pair> byValue(suffixes_.begin(), suffixes_.end());
203 byValue.erase(std::remove_if(byValue.begin(), byValue.end(),
205 return a.second.preferred != Preferred::YES;
208 std::sort(byValue.begin(), byValue.end(),
209 [](
const Pair &a,
const Pair &b) {
210 if (a.second.multiplier != b.second.multiplier)
211 return a.second.multiplier < b.second.multiplier;
212 if (a.first.size() != b.first.size())
213 return a.first.size() > b.first.size();
214 return a.first < b.first;
216 byValue.erase(std::unique(byValue.begin(), byValue.end(),
217 [](
const Pair &a,
const Pair &b) {
218 return a.second.multiplier == b.second.multiplier;
225 while (!byValue.empty() && byValue.back().second.multiplier > value)
230 std::pair<T, T> qr = quotientRemainder(value, byValue.back().second.multiplier, T{});
231 assert(qr.first > 0);
232 retval += boost::lexical_cast<std::string>(qr.first) + byValue.back().first;
237 retval += boost::lexical_cast<std::string>(value);
246 T val = parse(input, rest);
247 std::string parsed(input, *rest - input);
252 T parseNumber(
const char *input,
const char **rest, uint64_t) {
257 uint64_t n = rose_strtoull(input, &r, extendedSyntax_ ? 0 : 10);
259 throw std::runtime_error(
"unsigned integer expected");
261 throw std::runtime_error(
"integer magnitude is too large");
263 T retval = boost::numeric_cast<T>(n);
266 }
catch (
const boost::bad_numeric_cast&) {
267 throw std::runtime_error(
"integer magnitude is too large");
272 T parseNumber(
const char *input,
const char **rest,
double) {
278 const char *significandEnd = input;
279 bool hadDecimalPoint;
280 while (isdigit(*significandEnd) || (
'.' == *significandEnd && !hadDecimalPoint)) {
281 if (
'.' == *significandEnd)
282 hadDecimalPoint =
true;
290 const char *end = significandEnd;
291 if (extendedSyntax_ && (
'e' == *end ||
'E' == *end)) {
293 if (
'+' == *end ||
'-' == *end)
296 while (isdigit(*end)) ++end;
298 end = significandEnd;
302 const std::string toParse(input, end);
303 const char *s = toParse.c_str();
306 double d = strtod(s, &r);
308 throw std::runtime_error(
"floating-point number expected");
310 throw std::runtime_error(
"floating-point value is out of range");
312 T retval = boost::numeric_cast<T>(d);
313 *rest = input + (r - s);
315 }
catch (
const boost::bad_numeric_cast&) {
316 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.