ROSE  0.11.145.0
SuffixMultiplierParser.h
1 #ifndef ROSE_CommandLine_SuffixMultiplierParser_H
2 #define ROSE_CommandLine_SuffixMultiplierParser_H
3 
4 #include <Rose/StringUtility/Escape.h>
5 #include <rose_strtoull.h>
6 
7 #include <algorithm>
8 #include <boost/numeric/conversion/cast.hpp>
9 #include <cassert>
10 #include <cerrno>
11 #include <cmath>
12 #include <cstdlib>
13 #include <map>
14 #include <Sawyer/CommandLine.h>
15 #include <string>
16 
17 namespace Rose {
18 namespace CommandLine {
19 
33 template<class T>
35 public:
36  enum class Preferred { NO, YES };
37 
38 private:
39  struct Suffix {
40  T multiplier;
41  Preferred preferred;
42  };
43 
44  using Suffixes = std::map<std::string /*name*/, Suffix>;
45  Suffixes suffixes_;
46  bool extendedSyntax_ = false;
47 
48 protected:
50 
52  : Sawyer::CommandLine::ValueParser(valueSaver) {}
53 
54 public:
59 
61  static Ptr instance() {
62  return Ptr(new SuffixMultiplierParser);
63  }
64 
67  return Ptr(new SuffixMultiplierParser(valueSaver));
68  }
69 
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>();
80  }
81 
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};
85  if (!alias1.empty())
86  suffixes_[alias1] = Suffix{multiplier, Preferred::NO};
87  if (!alias2.empty())
88  suffixes_[alias2] = Suffix{multiplier, Preferred::NO};
89  if (!alias3.empty())
90  suffixes_[alias3] = Suffix{multiplier, Preferred::NO};
91  if (!alias4.empty())
92  suffixes_[alias4] = Suffix{multiplier, Preferred::NO};
93  return sharedFromThis().template dynamicCast<SuffixMultiplierParser>();
94  }
113  bool extendedSyntax() const {
114  return extendedSyntax_;
115  }
116  Ptr extendedSyntax(bool b) {
117  extendedSyntax_ = b;
118  return sharedFromThis().template dynamicCast<SuffixMultiplierParser>();
119  }
123  T parse(const char *input, const char **rest) {
124  const char *s = input;
125  const char *r = nullptr;
126  T total = 0;
127 
128  while (isspace(*s)) ++s;
129 
130  while (*s) {
131  T n = parseNumber(s, &r, T{}); // throws if not possible
132  s = r;
133 
134  while (*r && !isdigit(*r))
135  ++r;
136  if (s == r)
137  break;
138  std::string suffix(s, r);
139  auto found = suffixes_.find(suffix);
140  if (found == suffixes_.end()) {
141  total += n;
142  r = const_cast<char*>(s);
143  break;
144  } else {
145  total += n * found->second.multiplier;
146  s = r;
147  }
148  }
149 
150  if (rest)
151  *rest = r;
152  return total;
153  }
154 
156  T parse(const std::string &input) {
157  const char *s = input.c_str();
158  const char *rest = nullptr;
159  T retval = parse(s, &rest);
160  while (isspace(*rest)) ++rest;
161  if (*rest)
162  throw std::runtime_error("extra text after end of interval specification: \"" + StringUtility::cEscape(rest) + "\"");
163  return retval;
164  }
165 
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;
169  try {
170  T q = boost::numeric_cast<T>(q64);
171  T r = boost::numeric_cast<T>(r64);
172  return {q, r};
173  } catch (const boost::bad_numeric_cast&) {
174  throw std::runtime_error("integer overflow");
175  }
176  }
177 
178  static std::pair<T, T> quotientRemainder(T product, T divisor, double) {
179  if (product < divisor) {
180  return {0, product};
181  } else {
182  double qd = std::floor(product / divisor);
183  double rd = std::fmod(product, divisor);
184  try {
185  T q = boost::numeric_cast<T>(qd);
186  T r = boost::numeric_cast<T>(rd);
187  return {q, r};
188  } catch (const boost::bad_numeric_cast&) {
189  throw std::runtime_error("floating-point overflow");
190  }
191  }
192  }
193 
195  std::string toString(T value) {
196  // Get a list of preferred suffixes sorted by increasing multipliers. When two or more preferred suffixes have the same
197  // multiplier, use the one with the longest name. If name lengths tie, choose one arbitrarily. We do this by first
198  // sorting the suffixes according to increasing multiplier and decreasing name length; then removing duplicates based
199  // on multipliers only.
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(),
203  [](const Pair &a) {
204  return a.second.preferred != Preferred::YES;
205  }),
206  byValue.end());
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;
214  });
215  byValue.erase(std::unique(byValue.begin(), byValue.end(),
216  [](const Pair &a, const Pair &b) {
217  return a.second.multiplier == b.second.multiplier;
218  }),
219  byValue.end());
220 
221  // Form the output by trying successively smaller suffixes.
222  std::string retval;
223  while (value > 0) {
224  while (!byValue.empty() && byValue.back().second.multiplier > value)
225  byValue.pop_back();
226  if (byValue.empty())
227  break;
228 
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;
232  value = qr.second;
233  }
234 
235  if (value > 0)
236  retval += boost::lexical_cast<std::string>(value);
237  if (retval.empty())
238  retval += "0";
239  return retval;
240  }
241 
242 private:
243  virtual Sawyer::CommandLine::ParsedValue operator()(const char *input, const char **rest,
244  const Sawyer::CommandLine::Location &loc) override {
245  T val = parse(input, rest);
246  std::string parsed(input, *rest - input);
247  return Sawyer::CommandLine::ParsedValue(val, loc, parsed, valueSaver());
248  }
249 
250  // Parse integer literal w/out leading white space
251  T parseNumber(const char *input, const char **rest, uint64_t) {
252  assert(input);
253  assert(rest);
254  char *r = nullptr;
255  errno = 0;
256  uint64_t n = rose_strtoull(input, &r, extendedSyntax_ ? 0 : 10);
257  if (input == *rest)
258  throw std::runtime_error("unsigned integer expected");
259  if (ERANGE == errno)
260  throw std::runtime_error("integer magnitude is too large");
261  try {
262  T retval = boost::numeric_cast<T>(n);
263  *rest = r;
264  return retval;
265  } catch (const boost::bad_numeric_cast&) {
266  throw std::runtime_error("integer magnitude is too large");
267  }
268  }
269 
270  // Parse floating point literal w/out leading white space
271  T parseNumber(const char *input, const char **rest, double) {
272  assert(input);
273  assert(rest);
274 
275  // Find the end of the significand (digits followed by optional decimal point and more digits). We don't worry about
276  // how many digits there are on either side of the decimal point since strtod will do more strict checking later.
277  const char *significandEnd = input;
278  bool hadDecimalPoint;
279  while (isdigit(*significandEnd) || ('.' == *significandEnd && !hadDecimalPoint)) {
280  if ('.' == *significandEnd)
281  hadDecimalPoint = true;
282  ++significandEnd;
283  }
284 
285  // For extended syntax, find the end of the optional exponent: [eE][-+]?\d+. Again, we don't have to be too precise
286  // because strtod will do the strict checking. However, we should be careful that the "e" is followed by at least
287  // one digit so as not to be confused by suffixes that start with "e" (other than the suffix "e" itself which will
288  // be ambiguous when followed immediately by a digit).
289  const char *end = significandEnd;
290  if (extendedSyntax_ && ('e' == *end || 'E' == *end)) {
291  ++end;
292  if ('+' == *end || '-' == *end)
293  ++end;
294  if (isdigit(*end)) {
295  while (isdigit(*end)) ++end;
296  } else {
297  end = significandEnd;
298  }
299  }
300 
301  const std::string toParse(input, end);
302  const char *s = toParse.c_str();
303  char *r = nullptr;
304  errno = 0;
305  double d = strtod(s, &r);
306  if (s == r)
307  throw std::runtime_error("floating-point number expected");
308  if (ERANGE == errno)
309  throw std::runtime_error("floating-point value is out of range");
310  try {
311  T retval = boost::numeric_cast<T>(d);
312  *rest = input + (r - s);
313  return retval;
314  } catch (const boost::bad_numeric_cast&) {
315  throw std::runtime_error("floating-point value is out of range");
316  }
317  }
318 };
319 
320 template<class T>
321 typename SuffixMultiplierParser<T>::Ptr suffixMultiplierParser(T &storage) {
323 }
324 
325 template<class T>
326 typename SuffixMultiplierParser<T>::Ptr suffixMultiplierParser(std::vector<T> &storage) {
327  return SuffixMultiplierParser<T>::instance(Sawyer::CommandLine::TypedSaver<std::vector<T>>::instance(storage));
328 }
329 
330 template<class T>
331 typename SuffixMultiplierParser<T>::Ptr suffixMultiplierParser() {
333 }
334 
335 } // namespace
336 } // namespace
337 
338 #endif
static Ptr instance(const Sawyer::CommandLine::ValueSaver::Ptr &valueSaver)
Allocating constructor.
Sawyer::SharedPointer< SuffixMultiplierParser > Ptr
Shared-ownership pointer to a SuffixMultiplierParser.
const ValueSaver::Ptr valueSaver() const
Property: functor responsible for saving a parsed value in user storage.
Ptr extendedSyntax(bool b)
Property: Allow extended syntax for numberic values.
static Ptr instance()
Default allocating constructor.
Main namespace for the ROSE library.
ROSE_UTIL_API std::string cEscape(const std::string &, char context= '"')
Escapes characters that are special to C/C++.
SharedPointer< ValueParser > sharedFromThis()
Create a shared pointer from this.
Information about a parsed switch value.
std::string toString(T value)
Unparse to a string.
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.
Position within a command-line.
bool extendedSyntax() const
Property: Allow extended syntax for numberic values.
Base class parsing a value from input.
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 std::string &input)
Parse from a C++ string.
T parse(const char *input, const char **rest)
Parse from a C string.