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:
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 => [],
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user