ROSE  0.11.145.0
Parse.h
1 // WARNING: Changes to this file must be contributed back to Sawyer or else they will
2 // be clobbered by the next update from Sawyer. The Sawyer repository is at
3 // https://github.com/matzke1/sawyer.
4 
5 
6 
7 
8 #ifndef Sawyer_Parse_H
9 #define Sawyer_Parse_H
10 
11 #include <Sawyer/Optional.h>
12 #include <Sawyer/Result.h>
13 
14 #include <limits>
15 #include <string>
16 #include <type_traits>
17 #include <boost/lexical_cast.hpp>
18 
19 namespace Sawyer {
20 
25 template<class IntegralType>
27 toDigit(char ch, IntegralType radix = 10) {
28  assert(radix <= 16);
29  assert(!std::numeric_limits<IntegralType>::is_signed || radix >= 0); // trait test is to avoid compiler warning
30  IntegralType digit;
31  if (ch >= '0' && ch <= '9') {
32  digit = ch - '0';
33  } else if (ch >= 'a' && ch <= 'f') {
34  digit = ch - 'a' + 10;
35  } else if (ch >= 'A' && ch <= 'F') {
36  digit = ch - 'A' + 10;
37  } else {
38  return Sawyer::Nothing();
39  }
40 
41  if (digit < radix)
42  return digit;
43  return Sawyer::Nothing();
44 }
45 
58 template<class IntegralType>
59 typename std::enable_if<std::is_integral<IntegralType>::value, Sawyer::Result<IntegralType, std::string>>::type
60 parse(const std::string &s) {
61  using UnsignedType = typename std::make_unsigned<IntegralType>::type;
62 
63  // No template parameter deduction in constructors before C++17, so make aliases
66 
67  const char *sptr = s.c_str();
68 
69  // Optional plus or minus sign when parsing signed values
70  if ('-' == *sptr || '+' == *sptr) {
71  if (std::numeric_limits<IntegralType>::is_signed) {
72  ++sptr;
73  } else {
74  return Error("syntax error: sign not allowed for unsigned types");
75  }
76  }
77  if ('_' == *sptr)
78  return Error("syntax error: separator not allowed before first digit");
79 
80  // Radix specification
81  UnsignedType radix = 10;
82  if (strncmp("0x", sptr, 2) == 0 || strncmp("0X", sptr, 2) == 0) {
83  radix = 16;
84  sptr += 2;
85  } else if (strncmp("0b", sptr, 2) == 0) {
86  radix = 2;
87  sptr += 2;
88  }
89 
90  // Number may have a leading digit separator if it has a radix specifier
91  if (radix != 10 && '_' == *sptr)
92  ++sptr;
93 
94  // Parse the value as unsigned, but be careful of overflows.
95  UnsignedType n = 0;
96  size_t nDigits = 0;
97  for (size_t i = 0; sptr[i]; ++i) {
98  if ('_' == sptr[i]) {
99  if (0 == i || '_' == sptr[i-1] || !sptr[i+1])
100  return Error("syntax error: invalid use of digit separator");
101 
102  } else if (Sawyer::Optional<UnsignedType> digit = toDigit(sptr[i], radix)) {
103  ++nDigits;
104 
105  // Check for overflow
106  const UnsignedType shifted = n * radix;
107  if ((n != 0 && shifted / n != radix) || *digit > std::numeric_limits<UnsignedType>::max() - shifted) {
108  if ('-' == s[0]) {
109  return Error("overflow error: less than minimum value for type");
110  } else {
111  return Error("overflow error: greater than maximum value for type");
112  }
113  }
114 
115  n = shifted + *digit;
116  } else {
117  return Error("syntax error: invalid digit after parsing " + boost::lexical_cast<std::string>(nDigits) +
118  (1==nDigits ? " digit" : " digits"));
119  }
120  }
121  if (0 == nDigits)
122  return Error("syntax error: digits expected");
123 
124  // Convert the unsigned (positive) value to a signed negative if necessary, checking overflow.
125  if (std::numeric_limits<IntegralType>::is_signed) {
126  const UnsignedType signBit = (UnsignedType)1 << (8*sizeof(IntegralType) - 1);
127  if ('-' == s[0]) {
128  if (n & signBit && n != signBit)
129  return Error("overflow error: less than minimum value for type");
130  return Ok((IntegralType)(~n + 1));
131  } else if (n & signBit) {
132  return Error("overflow error: greater than maximum value for type");
133  } else {
134  return Ok((IntegralType)n);
135  }
136  } else {
137  return Ok((IntegralType)n);
138  }
139 }
140 
141 } // namespace
142 #endif
Success value.
Definition: Result.h:38
Sawyer::Optional< typename std::enable_if< std::is_integral< IntegralType >::value, IntegralType >::type > toDigit(char ch, IntegralType radix=10)
Convert a character to a numeric digit.
Definition: Parse.h:27
Error value.
Definition: Result.h:149
Holds a value or nothing.
Definition: Optional.h:49
Result containing a value or an error.
Definition: Result.h:270
Name space for the entire library.
Definition: FeasiblePath.h:767
std::enable_if< std::is_integral< IntegralType >::value, Sawyer::Result< IntegralType, std::string > >::type parse(const std::string &s)
Safely convert a string to a number.
Definition: Parse.h:60
Represents no value.
Definition: Optional.h:32