Berry change number parser for json to reuse same parser as lexer (#23505)

This commit is contained in:
s-hadinger 2025-06-02 21:42:44 +02:00 committed by GitHub
parent de0d88514a
commit d94d7c8972
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 109 additions and 86 deletions

View File

@ -22,6 +22,7 @@ All notable changes to this project will be documented in this file.
- ESP32 Platform from 2025.05.40 to 2025.05.30, Framework (Arduino Core) from v3.2.0.250504 to v3.1.3.250504 and IDF from v5.4.1.250501 to v5.3.3.250501 (#23404)
- ESP8266 platform update from 2024.09.00 to 2025.05.00 (#23448)
- Increase number of supported LoRaWan nodes from 4 to 16
- Berry change number parser for json to reuse same parser as lexer
### Fixed
- Haspmota `haspmota.parse()` page parsing (#23403)

View File

@ -13,6 +13,9 @@
#if BE_USE_JSON_MODULE
#define is_space(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n')
#define is_digit(c) ((c) >= '0' && (c) <= '9')
#define MAX_INDENT 24
#define INDENT_WIDTH 2
#define INDENT_CHAR ' '
@ -30,11 +33,6 @@ static const char* skip_space(const char *s)
return s;
}
static int is_digit(int c)
{
return c >= '0' && c <= '9';
}
static const char* match_char(const char *json, int ch)
{
json = skip_space(json);
@ -249,90 +247,114 @@ static const char* parser_array(bvm *vm, const char *json)
return json;
}
enum {
JSON_NUMBER_INVALID = 0,
JSON_NUMBER_INTEGER = 1,
JSON_NUMBER_REAL = 2
};
int check_json_number(const char *json) {
if (!json || *json == '\0') {
return JSON_NUMBER_INVALID;
}
const char *p = json;
bbool has_fraction = bfalse;
bbool has_exponent = bfalse;
// Skip leading whitespace
while (is_space(*p)) {
p++;
}
if (*p == '\0') {
return JSON_NUMBER_INVALID;
}
// Handle optional minus sign
if (*p == '-') {
p++;
if (*p == '\0') {
return JSON_NUMBER_INVALID;
}
}
// Integer part
if (*p == '0') {
// If starts with 0, next char must not be a digit (unless it's decimal point or exponent)
p++;
if (is_digit(*p)) {
return JSON_NUMBER_INVALID; // Leading zeros not allowed (except standalone 0)
}
} else if (is_digit(*p)) {
// First digit must be 1-9, then any digits
p++;
while (is_digit(*p)) {
p++;
}
} else {
return JSON_NUMBER_INVALID; // Must start with digit
}
// Optional fractional part
if (*p == '.') {
has_fraction = btrue;
p++;
if (!is_digit(*p)) {
return JSON_NUMBER_INVALID; // Must have at least one digit after decimal point
}
while (is_digit(*p)) {
p++;
}
}
// Optional exponent part
if (*p == 'e' || *p == 'E') {
has_exponent = btrue;
p++;
// Optional sign in exponent
if (*p == '+' || *p == '-') {
p++;
}
if (!is_digit(*p)) {
return JSON_NUMBER_INVALID; // Must have at least one digit in exponent
}
while (is_digit(*p)) {
p++;
}
}
// Number ends here - check that next char is not a continuation
// Valid JSON number termination: whitespace, null, or JSON delimiters
if (*p != '\0' && !is_space(*p) && *p != ',' && *p != ']' && *p != '}' && *p != ':') {
return JSON_NUMBER_INVALID;
}
// Determine return value based on what was found
// Any number with exponent (e/E) is always real, regardless of fractional part
if (has_exponent || has_fraction) {
return JSON_NUMBER_REAL; // real number
} else {
return JSON_NUMBER_INTEGER; // integer
}
}
static const char* parser_number(bvm *vm, const char *json)
{
char c = *json++;
bbool is_neg = c == '-';
if(is_neg) {
c = *json++;
if(!is_digit(c)) {
/* minus must be followed by digit */
return NULL;
}
const char *endstr = NULL;
int number_type = check_json_number(json);
switch (number_type) {
case JSON_NUMBER_INTEGER:
be_pushint(vm, be_str2int(json, &endstr));
break;
case JSON_NUMBER_REAL:
be_pushreal(vm, be_str2real(json, &endstr));
break;
default:
endstr = NULL;
}
bint intv = 0;
if(c != '0') {
/* parse integer part */
while(is_digit(c)) {
intv = intv * 10 + c - '0';
c = *json++;
}
} else {
/*
Number starts with zero, this is only allowed
if the number is just '0' or
it has a fractional part or exponent.
*/
c = *json++;
}
if(c != '.' && c != 'e' && c != 'E') {
/*
No fractional part or exponent follows, this is an integer.
If digits follow after it (for example due to a leading zero)
this will cause an error in the calling function.
*/
be_pushint(vm, intv * (is_neg ? -1 : 1));
json--;
return json;
}
breal realval = (breal) intv;
if(c == '.') {
breal deci = 0.0, point = 0.1;
/* fractional part */
c = *json++;
if(!is_digit(c)) {
/* decimal point must be followed by digit */
return NULL;
}
while (is_digit(c)) {
deci = deci + ((breal)c - '0') * point;
point *= (breal)0.1;
c = *json++;
}
realval += deci;
}
if(c == 'e' || c == 'E') {
c = *json++;
/* exponent part */
breal ratio = c == '-' ? (breal)0.1 : 10;
if (c == '+' || c == '-') {
c = *json++;
if(!is_digit(c)) {
return NULL;
}
}
if(!is_digit(c)) {
/* e and sign must be followed by digit */
return NULL;
}
unsigned int e = 0;
while (is_digit(c)) {
e = e * 10 + c - '0';
c = *json++;
}
/* e > 0 must be here to prevent infinite loops when e overflows */
while (e--) {
realval *= ratio;
}
}
be_pushreal(vm, realval * (is_neg ? -1.0 : 1.0));
json--;
return json;
return endstr;
}
/* parser json value */