ROSE 0.11.145.147
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://gitlab.com/charger7534/sawyer.git.
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
19namespace Sawyer {
20
25template<class IntegralType>
27toDigit(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
58template<class IntegralType>
59typename std::enable_if<std::is_integral<IntegralType>::value, Sawyer::Result<IntegralType, std::string>>::type
60parse(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
Error value.
Definition Result.h:170
Represents no value.
Definition Optional.h:36
Success value.
Definition Result.h:35
Holds a value or nothing.
Definition Optional.h:56
Result containing a value or an error.
Definition Result.h:315
Sawyer support library.
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