in MiniEngine/Model/json.hpp [7169:7492]
token_type scan_number() // lgtm [cpp/use-of-goto]
{
// reset token_buffer to store the number's bytes
reset();
// the type of the parsed number; initially set to unsigned; will be
// changed if minus sign, decimal point or exponent is read
token_type number_type = token_type::value_unsigned;
// state (init): we just found out we need to scan a number
switch (current)
{
case '-':
{
add(current);
goto scan_number_minus;
}
case '0':
{
add(current);
goto scan_number_zero;
}
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
add(current);
goto scan_number_any1;
}
// all other characters are rejected outside scan_number()
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // LCOV_EXCL_LINE
}
scan_number_minus:
// state: we just parsed a leading minus sign
number_type = token_type::value_integer;
switch (get())
{
case '0':
{
add(current);
goto scan_number_zero;
}
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
add(current);
goto scan_number_any1;
}
default:
{
error_message = "invalid number; expected digit after '-'";
return token_type::parse_error;
}
}
scan_number_zero:
// state: we just parse a zero (maybe with a leading minus sign)
switch (get())
{
case '.':
{
add(decimal_point_char);
goto scan_number_decimal1;
}
case 'e':
case 'E':
{
add(current);
goto scan_number_exponent;
}
default:
goto scan_number_done;
}
scan_number_any1:
// state: we just parsed a number 0-9 (maybe with a leading minus sign)
switch (get())
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
add(current);
goto scan_number_any1;
}
case '.':
{
add(decimal_point_char);
goto scan_number_decimal1;
}
case 'e':
case 'E':
{
add(current);
goto scan_number_exponent;
}
default:
goto scan_number_done;
}
scan_number_decimal1:
// state: we just parsed a decimal point
number_type = token_type::value_float;
switch (get())
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
add(current);
goto scan_number_decimal2;
}
default:
{
error_message = "invalid number; expected digit after '.'";
return token_type::parse_error;
}
}
scan_number_decimal2:
// we just parsed at least one number after a decimal point
switch (get())
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
add(current);
goto scan_number_decimal2;
}
case 'e':
case 'E':
{
add(current);
goto scan_number_exponent;
}
default:
goto scan_number_done;
}
scan_number_exponent:
// we just parsed an exponent
number_type = token_type::value_float;
switch (get())
{
case '+':
case '-':
{
add(current);
goto scan_number_sign;
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
add(current);
goto scan_number_any2;
}
default:
{
error_message =
"invalid number; expected '+', '-', or digit after exponent";
return token_type::parse_error;
}
}
scan_number_sign:
// we just parsed an exponent sign
switch (get())
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
add(current);
goto scan_number_any2;
}
default:
{
error_message = "invalid number; expected digit after exponent sign";
return token_type::parse_error;
}
}
scan_number_any2:
// we just parsed a number after the exponent or exponent sign
switch (get())
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
add(current);
goto scan_number_any2;
}
default:
goto scan_number_done;
}
scan_number_done:
// unget the character after the number (we only read it to know that
// we are done scanning a number)
unget();
char* endptr = nullptr;
errno = 0;
// try to parse integers first and fall back to floats
if (number_type == token_type::value_unsigned)
{
const auto x = std::strtoull(token_buffer.data(), &endptr, 10);
// we checked the number format before
JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
if (errno == 0)
{
value_unsigned = static_cast<number_unsigned_t>(x);
if (value_unsigned == x)
{
return token_type::value_unsigned;
}
}
}
else if (number_type == token_type::value_integer)
{
const auto x = std::strtoll(token_buffer.data(), &endptr, 10);
// we checked the number format before
JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
if (errno == 0)
{
value_integer = static_cast<number_integer_t>(x);
if (value_integer == x)
{
return token_type::value_integer;
}
}
}
// this code is reached if we parse a floating-point number or if an
// integer conversion above failed
strtof(value_float, token_buffer.data(), &endptr);
// we checked the number format before
JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size());
return token_type::value_float;
}