Added JSON5 support for parsing special numbers

NaN and both Infinities, with tests.
Special handling was added to yajl_test since different OSs don't
always generate the same output for special numbers (nan/NaN/...).
This commit is contained in:
Andrew Johnson
2020-07-09 23:57:05 -05:00
parent 98a358437f
commit 97b8df6912
4 changed files with 117 additions and 46 deletions
+64 -43
View File
@@ -399,6 +399,25 @@ yajl_lex_number(yajl_lexer lexer, const unsigned char * jsonText,
c = readChar(lexer, jsonText, offset);
}
if (c == 'I') {
const char * want = "nfinity";
do {
RETURN_IF_EOF;
c = readChar(lexer, jsonText, offset);
if (c != *want) {
unreadChar(lexer, offset);
lexer->error = yajl_lex_invalid_string;
return yajl_tok_error;
}
} while (*(++want));
if (!lexer->allowJson5) {
unreadChar(lexer, offset);
lexer->error = yajl_lex_unallowed_special_number;
return yajl_tok_error;
}
return yajl_tok_double;
}
/* a single zero, hex number, or a series of decimal digits */
if (c == '0') {
numRd++;
@@ -534,6 +553,23 @@ yajl_lex_comment(yajl_lexer lexer, const unsigned char * jsonText,
return tok;
}
/* Macro to reduce code duplication in yajl_lex_lex() */
#define LEX_WANT(tring) \
const char * want = tring; \
do { \
if (*offset >= jsonTextLen) { \
tok = yajl_tok_eof; \
goto lexed; \
} \
c = readChar(lexer, jsonText, offset); \
if (c != *want) { \
unreadChar(lexer, offset); \
lexer->error = yajl_lex_invalid_string; \
tok = yajl_tok_error; \
goto lexed; \
} \
} while (*(++want))
yajl_tok
yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText,
size_t jsonTextLen, size_t * offset,
@@ -579,59 +615,42 @@ yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText,
startOffset++;
break;
case 't': {
const char * want = "rue";
do {
if (*offset >= jsonTextLen) {
tok = yajl_tok_eof;
goto lexed;
}
c = readChar(lexer, jsonText, offset);
if (c != *want) {
unreadChar(lexer, offset);
lexer->error = yajl_lex_invalid_string;
tok = yajl_tok_error;
goto lexed;
}
} while (*(++want));
LEX_WANT("rue");
tok = yajl_tok_bool;
goto lexed;
}
case 'f': {
const char * want = "alse";
do {
if (*offset >= jsonTextLen) {
tok = yajl_tok_eof;
goto lexed;
}
c = readChar(lexer, jsonText, offset);
if (c != *want) {
unreadChar(lexer, offset);
lexer->error = yajl_lex_invalid_string;
tok = yajl_tok_error;
goto lexed;
}
} while (*(++want));
LEX_WANT("alse");
tok = yajl_tok_bool;
goto lexed;
}
case 'n': {
const char * want = "ull";
do {
if (*offset >= jsonTextLen) {
tok = yajl_tok_eof;
goto lexed;
}
c = readChar(lexer, jsonText, offset);
if (c != *want) {
unreadChar(lexer, offset);
lexer->error = yajl_lex_invalid_string;
tok = yajl_tok_error;
goto lexed;
}
} while (*(++want));
LEX_WANT("ull");
tok = yajl_tok_null;
goto lexed;
}
case 'I': {
LEX_WANT("nfinity");
if (!lexer->allowJson5) {
unreadChar(lexer, offset);
lexer->error = yajl_lex_unallowed_special_number;
tok = yajl_tok_error;
} else {
tok = yajl_tok_double;
}
goto lexed;
}
case 'N': {
LEX_WANT("aN");
if (!lexer->allowJson5) {
unreadChar(lexer, offset);
lexer->error = yajl_lex_unallowed_special_number;
tok = yajl_tok_error;
} else {
tok = yajl_tok_double;
}
goto lexed;
}
case '"': {
tok = yajl_lex_string(lexer, (const unsigned char *) jsonText,
jsonTextLen, offset);
@@ -757,7 +776,7 @@ yajl_lex_error_to_string(yajl_lex_error error)
"decimal point.";
case yajl_lex_missing_integer_after_minus:
return "malformed number, a digit is required after the "
"minus sign.";
"plus/minus sign.";
case yajl_lex_unallowed_comment:
return "probable comment found in input text, comments are "
"not enabled.";
@@ -765,6 +784,8 @@ yajl_lex_error_to_string(yajl_lex_error error)
return "malformed number, a hex digit is required after the 0x/0X.";
case yajl_lex_unallowed_hex_integer:
return "probable hex number found, JSON5 is not enabled.";
case yajl_lex_unallowed_special_number:
return "special number Infinity or NaN found, JSON5 is not enabled.";
}
return "unknown error code";
}
+1
View File
@@ -103,6 +103,7 @@ typedef enum {
yajl_lex_unallowed_comment,
yajl_lex_missing_hex_digit_after_0x,
yajl_lex_unallowed_hex_integer,
yajl_lex_unallowed_special_number,
} yajl_lex_error;
const char * yajl_lex_error_to_string(yajl_lex_error error);
+42 -2
View File
@@ -13,7 +13,7 @@ sub cases {
-5
],
input => [
"[ .1e2, 10., +3.141569, -.1e4]",
"[ .1e2, 10., +3.141569, -.1e4, NaN, Infinity, +Infinity, -Infinity ]",
""
],
gives => [
@@ -22,6 +22,10 @@ sub cases {
"double: 10",
"double: 3.14157",
"double: -1000",
"double: NaN",
"double: Infinity",
"double: Infinity",
"double: -Infinity",
"array close ']'",
"memory leaks:\t0"
]
@@ -2798,6 +2802,18 @@ sub cases {
"memory leaks:\t0"
]
},
{
name => "infinity",
opts => [],
input => [
"Infinity",
""
],
gives => [
"lexical error: special number Infinity or NaN found, JSON5 is not enabled.",
"memory leaks:\t0"
]
},
{
name => "integers",
opts => [],
@@ -2884,7 +2900,7 @@ sub cases {
"string: 'blue'",
"string: 'baby where are you?'",
"string: 'oh boo hoo!'",
"lexical error: malformed number, a digit is required after the minus sign.",
"lexical error: malformed number, a digit is required after the plus/minus sign.",
"memory leaks:\t0"
]
},
@@ -2922,6 +2938,18 @@ sub cases {
"memory leaks:\t0"
]
},
{
name => "minus_infinity",
opts => [],
input => [
"-Infinity",
""
],
gives => [
"lexical error: special number Infinity or NaN found, JSON5 is not enabled.",
"memory leaks:\t0"
]
},
{
name => "missing_integer_after_decimal_point",
opts => [],
@@ -2962,6 +2990,18 @@ sub cases {
"memory leaks:\t0"
]
},
{
name => "nan",
opts => [],
input => [
"NaN",
""
],
gives => [
"lexical error: special number Infinity or NaN found, JSON5 is not enabled.",
"memory leaks:\t0"
]
},
{
name => "non_utf8_char_in_string",
opts => [],
+10 -1
View File
@@ -17,6 +17,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <epicsMath.h>
#include <yajl_parse.h>
#include <yajl_gen.h>
@@ -85,7 +86,15 @@ static int test_yajl_integer(void *ctx, long long integerVal)
static int test_yajl_double(void *ctx, double doubleVal)
{
printf("double: %g\n", doubleVal);
if (isnan(doubleVal)) {
printf("double: NaN\n");
}
else if (isinf(doubleVal)) {
printf("double: %sInfinity\n", doubleVal > 0 ? "" : "-");
}
else {
printf("double: %g\n", doubleVal);
}
return 1;
}