From d94d7c8972e2ba5fb01cb981ce52c708e87cbe26 Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Mon, 2 Jun 2025 21:42:44 +0200 Subject: [PATCH] Berry change number parser for json to reuse same parser as lexer (#23505) --- CHANGELOG.md | 1 + lib/libesp32/berry/src/be_jsonlib.c | 194 ++++++++++++++++------------ 2 files changed, 109 insertions(+), 86 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 441b02b20..cf3cd8a36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/lib/libesp32/berry/src/be_jsonlib.c b/lib/libesp32/berry/src/be_jsonlib.c index e6667dc2e..33aca5dc4 100644 --- a/lib/libesp32/berry/src/be_jsonlib.c +++ b/lib/libesp32/berry/src/be_jsonlib.c @@ -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 */