Skip to content

Instantly share code, notes, and snippets.

@paniq
Created May 28, 2019 15:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paniq/1564eab3b41a739570e0697b0125d89b to your computer and use it in GitHub Desktop.
Save paniq/1564eab3b41a739570e0697b0125d89b to your computer and use it in GitHub Desktop.
struct NumberParser {
enum {
NPF_Sign = (1 << 0),
NPF_Negative = (1 << 1),
NPF_Base = (1 << 2),
NPF_Dot = (1 << 3),
NPF_Exponent = (1 << 4),
NPF_ExponentSign = (1 << 5),
NPF_ExponentNegative = (1 << 6),
NPF_Inf = (1 << 7),
NPF_NaN = (1 << 8),
NPF_Real = NPF_Dot | NPF_Exponent | NPF_Inf | NPF_NaN,
};
uint16_t flags = 0;
int base = 10;
int dot = 0;
std::vector<uint8_t> digits;
std::vector<uint8_t> exponent_digits;
bool is_real() const {
return (flags & NPF_Real) != 0;
}
bool is_signed() const {
return (flags & NPF_Sign) != 0;
}
bool is_negative() const {
return (flags & NPF_Negative) != 0;
}
bool is_exponent_negative() const {
return (flags & NPF_ExponentNegative) != 0;
}
bool has_exponent() const {
return (flags & NPF_Exponent) != 0;
}
bool is_inf() const {
return (flags & NPF_Inf) != 0;
}
bool is_nan() const {
return (flags & NPF_NaN) != 0;
}
int64_t exponent_as_int64() const {
int i = exponent_digits.size();
assert(i <= exponent_digits.size());
int64_t exp = 1;
int64_t result = 0;
while (i-- > 0) {
result += (int64_t)exponent_digits[i] * exp;
exp *= 10ll;
}
return is_exponent_negative()?-result:result;
}
double as_double() const {
double result = 0.0;
if (is_nan()) {
result = nan("");
} else if (is_inf()) {
result = INFINITY;
} else {
int i = digits.size();
while (i-- > 0) {
double exp = std::pow((double)base, (double)(dot - i - 1));
result += (double)digits[i] * exp;
}
if (has_exponent()) {
int64_t e = exponent_as_int64();
if (base == 10) {
result *= std::pow(10.0, (double)e);
} else {
result *= std::exp2(e);
}
}
}
return is_negative()?-result:result;
}
template<typename T>
T as_integer() const {
int i = dot;
assert(i <= digits.size());
T exp = 1;
T result = 0;
while (i-- > 0) {
result += (T)digits[i] * exp;
exp *= (T)base;
}
return is_negative()?-result:result;
}
int64_t as_int64() const {
return as_integer<int64_t>();
}
uint64_t as_uint64() const {
return as_integer<uint64_t>();
}
bool parse(const char *&str) {
enum State {
State_UnknownSign = 0,
State_UnknownBase = 1,
State_ExpectBase = 2,
State_ExpectNumber = 3,
State_ExpectExponentSign = 4,
State_ExpectExponent = 5,
};
State state = State_UnknownSign;
while (auto c = *str) {
repeat:
switch(state) {
case State_UnknownSign:
state = State_UnknownBase;
switch(c) {
case '+':
flags |= NPF_Sign;
break;
case '-':
flags |= NPF_Sign | NPF_Negative;
break;
default:
goto repeat;
}
break;
case State_UnknownBase:
switch(c) {
case 'n':
case 'N': // nan?
if (str[1] && (str[1] == 'a' || str[1] == 'A')
&& str[2] && (str[2] == 'n' || str[2] == 'N')) {
str += 3;
flags |= NPF_NaN;
return true;
} else return false;
case 'i':
case 'I': // inf?
if (str[1] && (str[1] == 'n' || str[1] == 'N')
&& str[2] && (str[2] == 'f' || str[2] == 'F')) {
str += 3;
flags |= NPF_Inf;
return true;
} else return false;
case '0':
state = State_ExpectBase;
break;
default:
state = State_ExpectNumber;
goto repeat;
}
break;
case State_ExpectBase:
state = State_ExpectNumber;
switch(c) {
case 'x':
base = 16;
flags |= NPF_Base;
break;
case 'b':
base = 2;
flags |= NPF_Base;
break;
case 'o':
base = 8;
flags |= NPF_Base;
break;
default:
// we parsed a zero already
digits.push_back(0);
goto repeat;
}
break;
case State_ExpectNumber:
switch(c) {
case '.':
// duplicate dot or dot after exponent
if (flags & (NPF_Dot|NPF_Exponent)) goto done;
dot = digits.size();
flags |= NPF_Dot;
break;
case 'p':
// exponent already defined
if (flags & NPF_Exponent) goto done;
// base is not 16
if (base != 16) goto done;
state = State_ExpectExponentSign;
flags |= NPF_Exponent;
break;
case 'e':
if (base != 16) {
// exponent already defined
if (flags & NPF_Exponent) goto done;
// no digits have been defined yet
if (digits.empty()) goto done;
state = State_ExpectExponentSign;
flags |= NPF_Exponent;
break;
}
// fall-through
default: {
uint8_t digit = 0;
switch(base) {
case 2: {
if ((c >= '0') && (c <= '1')) {
digit = c - '0';
} else goto done;
} break;
case 8: {
if ((c >= '0') && (c <= '7')) {
digit = c - '0';
} else goto done;
} break;
case 10: {
if ((c >= '0') && (c <= '9')) {
digit = c - '0';
} else goto done;
} break;
case 16: {
if ((c >= '0') && (c <= '9')) {
digit = c - '0';
} else if ((c >= 'A') && (c <= 'F')) {
digit = c - 'A' + 10;
} else if ((c >= 'a') && (c <= 'f')) {
digit = c - 'a' + 10;
} else goto done;
} break;
default: goto done;
}
digits.push_back(digit);
} break;
}
break;
case State_ExpectExponentSign:
state = State_ExpectExponent;
switch(c) {
case '+':
flags |= NPF_ExponentSign;
break;
case '-':
flags |= NPF_ExponentSign | NPF_ExponentNegative;
break;
default:
goto repeat;
}
break;
case State_ExpectExponent:
if ((c >= '0') && (c <= '9')) {
exponent_digits.push_back(c - '0');
} else goto done; // unrecognized digit
break;
}
str++;
}
done:
if (!(flags & NPF_Dot))
dot = digits.size();
if (digits.empty()) return false;
if (flags & NPF_Exponent)
if (exponent_digits.empty())
return false;
return true;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment