Skip to content

Commit

Permalink
fixed JSON number conversion
Browse files Browse the repository at this point in the history
Fixed `jnum_chk` by correcting the output of `jnum_gen`.
Fixed an incorrect prior use of `make rebuild_jnum_test`
and hand verified the generated `jnum_test.c` file.

Added large e-notation values to the jnum.testset.

Improved `json_alloc()` to explicitly initialize critical booleans
and pointers when allocating a new JSON parse tree node.

Both `json_process_decimal()` and `json_process_floating()` no
longer set `parsed` and `converted` booleans.  That task
if performed by the `json_conv_number()` function.

The `json_conv_number()` now correctly attempts to convert
a given JSON number in floating point, e-notation and
integer depending on if the result of `is_floating_notation()`,
or `is_e_notation()` or `is_decimal()`, respectively.
Those tests are used to set the `is_floating`, `is_e_notatiion`,
and `is_integer` booleans, also respectively.

Improved JSON debug messages from `json_process_decimal()` and
`json_process_floating()`.
  • Loading branch information
lcn2 committed Jul 23, 2023
1 parent d9b18ee commit 49e2b7c
Show file tree
Hide file tree
Showing 7 changed files with 3,741 additions and 4,033 deletions.
28 changes: 27 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
# Major changes to the IOCCC entry toolkit


## Release 1.0.38 2023-07-23

Fixed `jnum_chk` by correcting the output of `jnum_gen`.
Fixed an incorrect prior use of `make rebuild_jnum_test`
and hand verified the generated `jnum_test.c` file.

Added large e-notation values to the jnum.testset.

Improved `json_alloc()` to explicitly initialize critical booleans
and pointers when allocating a new JSON parse tree node.

Both `json_process_decimal()` and `json_process_floating()` no
longer set `parsed` and `converted` booleans. That task
if performed by the `json_conv_number()` function.

The `json_conv_number()` now correctly attempts to convert
a given JSON number in floating point, e-notation and
integer depending on if the result of `is_floating_notation()`,
or `is_e_notation()` or `is_decimal()`, respectively.
Those tests are used to set the `is_floating`, `is_e_notatiion`,
and `is_integer` booleans, also respectively.

Improved JSON debug messages from `json_process_decimal()` and
`json_process_floating()`.


## Release 1.0.37 2023-07-22

Use of `-H` in `jnamval` implies `-N`.
Expand All @@ -23,7 +50,6 @@ For number conversions in `jval` and `jnamval` use the macros to check for
converted/parsed booleans.



## Release 1.0.35 2023-07-19

Add initial version of man pages of `jfmt(1)`, `jval(1)` and `jnamval(1)` to
Expand Down
144 changes: 82 additions & 62 deletions jparse/json_parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -1757,8 +1757,7 @@ json_alloc(enum item_type type)
* even if the NUL is well beyond the end of the JSON number.
*
* NOTE: While it is OK if str has trailing whitespace, str[len-1] must be an
* ASCII digit. It is assumed that str[len-1] is the final JSON number
* character.
* ASCII digit. It is assumed that str[len-1] is the final JSON number character.
*/
static bool
json_process_decimal(struct json_number *item, char const *str, size_t len)
Expand Down Expand Up @@ -1846,15 +1845,12 @@ json_process_decimal(struct json_number *item, char const *str, size_t len)
item->as_maxint = strtoimax(str, &endptr, 10);
if (errno == ERANGE || errno == EINVAL || endptr == str || endptr == NULL) {
if (errno == ERANGE) {
/* if only a range problem then we can say it's parsable but not converted */
item->parsed = true;
dbg(DBG_VVVHIGH, "negative integer out of range, strtoimax failed to convert");
} else {
dbg(DBG_VVVHIGH, "invalid negative integer, strtoimax failed to convert");
}
dbg(DBG_VVVHIGH, "strtoimax failed to convert");
item->converted = false;
return false; /* processing failed */
}
item->converted = true;
item->parsed = true;
item->maxint_sized = true;
dbg(DBG_VVVHIGH, "strtoimax for <%s> returned: %jd", str, item->as_maxint);

Expand Down Expand Up @@ -1948,15 +1944,12 @@ json_process_decimal(struct json_number *item, char const *str, size_t len)
item->as_umaxint = strtoumax(str, &endptr, 10);
if (errno == ERANGE || errno == EINVAL || endptr == str || endptr == NULL) {
if (errno == ERANGE) {
/* if range issue we know it's parsable */
item->parsed = true;
dbg(DBG_VVVHIGH, "positive integer out of range, strtoumax failed to convert");
} else {
dbg(DBG_VVVHIGH, "invalid positive integer, strtoumax failed to convert");
}

dbg(DBG_VVVHIGH, "strtoumax failed to convert");
item->converted = false;
return false; /* processing failed */
}
item->converted = true;
item->umaxint_sized = true;
dbg(DBG_VVVHIGH, "strtoumax for <%s> returned: %ju", str, item->as_umaxint);

Expand Down Expand Up @@ -2331,10 +2324,8 @@ json_process_floating(struct json_number *item, char const *str, size_t len)
item->parsed = true;
}
dbg(DBG_VVVHIGH, "strtold failed to convert");
item->converted = false;
return false; /* processing failed */
}
item->converted = true;
item->longdouble_sized = true;
item->as_longdouble_int = (item->as_longdouble == floorl(item->as_longdouble));
dbg(DBG_VVVHIGH, "strtold for <%s> returned as %%Lg: %.22Lg", str, item->as_longdouble);
Expand All @@ -2360,11 +2351,9 @@ json_process_floating(struct json_number *item, char const *str, size_t len)
item->parsed = true;
}
item->double_sized = false;
item->converted = false;
dbg(DBG_VVVHIGH, "strtod for <%s> failed", str);
} else {
item->double_sized = true;
item->converted = true;
item->parsed = true;
item->as_double_int = (item->as_double == floor(item->as_double));
dbg(DBG_VVVHIGH, "strtod for <%s> returned as %%lg: %.22lg", str, item->as_double);
Expand All @@ -2386,7 +2375,6 @@ json_process_floating(struct json_number *item, char const *str, size_t len)
item->float_sized = false;
dbg(DBG_VVVHIGH, "strtof for <%s> failed", str);
} else {
item->converted = true;
item->parsed = true;
item->float_sized = true;
item->as_float_int = (item->as_longdouble == floorl(item->as_longdouble));
Expand All @@ -2408,7 +2396,7 @@ json_process_floating(struct json_number *item, char const *str, size_t len)
*
* A JSON number string is of the form:
*
* ({JSON_INTEGER}|{JSON_INTEGER}{JSON_FRACTION}|{JSON_INTEGER}{JSON_FRACTION}{JSON_EXPONENT}|{JSON_INTEGER}{JSON_EXPONENT})
* ({JSON_INTEGER}|{JSON_INTEGER}{JSON_FRACTION}|{JSON_INTEGER}{JSON_FRACTION}{JSON_EXPONENT}|{JSON_INTEGER}{JSON_EXPONENT})
*
* where {JSON_INTEGER} is of the form:
*
Expand All @@ -2427,7 +2415,7 @@ json_process_floating(struct json_number *item, char const *str, size_t len)
* len length, starting at ptr, of the JSON number string
*
* returns:
* allocated JSON parser tree node of the converted JSON number
* allocated JSON parser tree node of the parsed JSON number
*
* NOTE: This function will not return on malloc error.
* NOTE: This function will not return NULL.
Expand Down Expand Up @@ -2455,10 +2443,10 @@ json_conv_number(char const *ptr, size_t len)
* initialize the JSON item
*/
item = &(ret->item.number);
item->parsed = false;
item->converted = false;
item->as_str = NULL;
item->first = NULL;
item->converted = false;
item->parsed = false;
item->is_negative = false;
item->is_floating = false;
item->is_e_notation = false;
Expand Down Expand Up @@ -2544,66 +2532,98 @@ json_conv_number(char const *ptr, size_t len)
return ret;
}


decimal = is_decimal(item->first, item->number_len);
e_notation = is_e_notation(item->first, item->number_len);
floating_notation = is_floating_notation(item->first, item->number_len);
/*
* case: JSON number is a base 10 integer in ASCII
* attempt to determine the type of JSON number we have been given
*/
if (decimal) {
floating_notation = is_floating_notation(item->first, item->number_len);
item->is_floating = floating_notation;
/**/
e_notation = is_e_notation(item->first, item->number_len);
item->is_e_notation = e_notation;
/**/
decimal = is_decimal(item->first, item->number_len);
item->is_integer = decimal;

item->parsed = true; /* it's parsable but we now have to check if it can be converted */
/*
* case: JSON number is a floating point number
*/
if (floating_notation) {

/* process JSON number as a base 10 integer in ASCII */
success = json_process_decimal(item, item->first, item->number_len);
/* process JSON number as floating point number */
success = json_process_floating(item, item->first, item->number_len);
if (success == false) {
warn(__func__, "JSON number as base 10 integer in ASCII processing failed: <%*.*s>",
(int)item->number_len, (int)item->number_len, item->first);

/*
* this JSON number is not a true floating point value
*/
json_dbg(JSON_DBG_HIGH, __func__, "JSON number as floating point number failed: <%s>",
item->as_str);

} else {
item->is_integer = true;
item->is_floating = false;
item->is_e_notation = false;

/*
* this JSON number is a converted floating point value
*/
item->converted = true;
json_dbg(JSON_DBG_VHIGH, __func__, "converted JSON floating point number of type %s: <%s>",
json_item_type_name(ret), item->as_str);
}
item->parsed = true; /* floating point number has been parsed, regardless of conversion status */
}

/*
* case: JSON number is not a base 10 integer in ASCII
*
* The JSON number might be a floating point or e-notation number.
*
* We have to check e notation first as e notation must follow the rules of
* floating point notation with the addition of the e notation rules.
* case: JSON number is an e-notation number
*/
} else if (e_notation) {
item->parsed = true;
if (e_notation) {

/* process JSON number as floating point or e-notation number */
success = json_process_floating(item, item->first, item->number_len);
if (success == false) {
warn(__func__, "JSON number as e-notation number failed: <%*.*s>",
(int)item->number_len, (int)item->number_len, item->first);

/*
* this JSON number is not a true e_notation value
*/
json_dbg(JSON_DBG_HIGH, __func__, "JSON number as e-notation number failed: <%s>", item->as_str);

} else {
item->is_integer = false;
item->is_e_notation = true;

/*
* this JSON number is a converted e_notation value
*/
item->converted = true;
json_dbg(JSON_DBG_VHIGH, __func__, "converted JSON e-notation number of type %s: <%s>",
json_item_type_name(ret), item->as_str);
}
} else if (floating_notation) {
item->parsed = true;
item->parsed = true; /* e-notation number has been parsed, regardless of conversion status */
}

/* process JSON number as floating point number */
success = json_process_floating(item, item->first, item->number_len);
/*
* case: JSON number is a base 10 integer in ASCII
*/
if (decimal) {

/*
* process JSON number as a base 10 integer in ASCII
*/
success = json_process_decimal(item, item->first, item->number_len);
if (success == false) {
warn(__func__, "JSON number as floating point number failed: <%*.*s>",
(int)item->number_len, (int)item->number_len, item->first);

/*
* this JSON number is not a true integer value
*/
json_dbg(JSON_DBG_HIGH, __func__, "JSON number as base 10 integer in ASCII processing failed: <%s>", item->as_str);

} else {
item->is_integer = false;
item->is_e_notation = false;
item->is_floating = true;

/*
* this JSON number is a converted integer value
*/
item->converted = true;
json_dbg(JSON_DBG_VHIGH, __func__, "converted JSON integer of type %s: <%s>",
json_item_type_name(ret), item->as_str);
}
} else {
item->converted = false;
item->parsed = false;
item->parsed = true; /* 10 integer has been parsed, regardless of conversion status */
}
json_dbg(JSON_DBG_VHIGH, __func__, "JSON return type: %s", json_item_type_name(ret));

/*
* return the JSON parse tree item
Expand Down
2 changes: 1 addition & 1 deletion jparse/json_parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,10 @@ struct json_number

bool is_floating; /* true ==> as_str had a '.' in it such as 1.234, false ==> no '.' found */
bool is_e_notation; /* true ==> e notation used such as 1e10, false ==> no e notation found */
bool is_integer; /* true ==> converted to some integer type below */

/* integer values */

bool is_integer; /* true ==> converted to some integer type below */
bool int8_sized; /* true ==> converted JSON integer to C int8_t */
int8_t as_int8; /* JSON integer value in int8_t form, if int8_sized == true */

Expand Down
4 changes: 2 additions & 2 deletions jparse/test_jparse/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,12 @@ CFLAGS= ${C_STD} ${C_OPT} -pedantic ${WARN_FLAGS} ${LDFLAGS}

# source files that are permanent (not made, nor removed)
#
C_SRC= jnum_chk.c jnum_test.c jnum_gen.c jnum_header.c
C_SRC= jnum_chk.c jnum_gen.c jnum_header.c
H_SRC= jnum_chk.h jnum_gen.h

# source files that do not conform to strict picky standards
#
LESS_PICKY_CSRC=
LESS_PICKY_CSRC= jnum_test.c
LESS_PICKY_HSRC=

# all shell scripts
Expand Down
6 changes: 6 additions & 0 deletions jparse/test_jparse/jnum.testset
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
-1e10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-1e1000000000
-1.0e1000000000
-8589934594.0
-8589934594.1
-8589934594.2e2
Expand Down Expand Up @@ -443,3 +446,6 @@
8589934594.1
8589934594.2e2
8589934594.2E-4
1e1000000000
1.0e1000000000
1e10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Loading

0 comments on commit 49e2b7c

Please sign in to comment.