Implement some of the JSON5 enhancements to string and numeric literals.

This is an incremental check-in of work in progress.

FossilOrigin-Name: 9508efa9d61c0ff0eb73100dd52889dadc5fa2a2091b944a9b8a74e8a5c50e82
This commit is contained in:
drh 2023-04-26 20:26:14 +00:00
parent f14b2e35fa
commit c5ee2d8e80
3 changed files with 147 additions and 27 deletions

View File

@ -1,5 +1,5 @@
C Partial\simplementation\sof\sJSON5\snumeric\sliteral\sextensions.\s\sUse\sa\sswitch()\nstatement\sin\sthe\sparser\sfor\sbetter\sperformance.
D 2023-04-26T17:30:28.870
C Implement\ssome\sof\sthe\sJSON5\senhancements\sto\sstring\sand\snumeric\sliterals.\nThis\sis\san\sincremental\scheck-in\sof\swork\sin\sprogress.
D 2023-04-26T20:26:14.388
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -592,7 +592,7 @@ F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4
F src/json.c a346dbe63628f8cb9c6bf6bbccb8fc81a49730db2311ec9b483d7fc3582da223
F src/json.c f82b86599b220a2c60731eba883bbea92ee4d6e61aaa56740179e8aa5e6ec596
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136
F src/main.c 09bc5191f75dc48fc4dfddda143cb864c0c3dbc3297eb9a9c8e01fea58ff847d
@ -2063,8 +2063,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P ac411dbdcbf0b9040fb5b7de173271f383a6aa303d57f22ebd200809a5b4a6d3
R 0e63126100fc0a5fcfa419eb4dbeae91
P 78404dc37024cad5fe7eacf78ea85b56f08b129a1b9a046c3e1b11275068a485
R e7aefca3f1ce2ae26f999bdfb414a2b2
U drh
Z 7c56c83a7327e89fcacb5797373de3a9
Z b316d61c8d04f67716fa9ffd06cd770b
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
78404dc37024cad5fe7eacf78ea85b56f08b129a1b9a046c3e1b11275068a485
9508efa9d61c0ff0eb73100dd52889dadc5fa2a2091b944a9b8a74e8a5c50e82

View File

@ -104,6 +104,7 @@ static const char * const jsonType[] = {
#define JNODE_PATCH 0x10 /* Patch with JsonNode.u.pPatch */
#define JNODE_APPEND 0x20 /* More ARRAY/OBJECT entries at u.iAppend */
#define JNODE_LABEL 0x40 /* Is a label of an object */
#define JNODE_JSON5 0x80 /* Node contains JSON5 enhancements */
/* A single node of parsed JSON
@ -295,6 +296,72 @@ static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
assert( p->nUsed<p->nAlloc );
}
/*
** The zIn[0..N] string is a JSON5 string literal. Append to p a translation
** of the string literal that standard JSON and that omits all JSON5
** features.
*/
static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){
jsonAppendChar(p, '"');
jsonAppendRaw(p, &zIn[1], N-2); /* TODO: translate JSON5 escapes */
jsonAppendChar(p, '"');
}
/*
** The zIn[0..N] string is a JSON5 integer literal. Append to p a translation
** of the string literal that standard JSON and that omits all JSON5
** features.
*/
static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){
if( zIn[0]=='+' ){
zIn++;
N--;
}else if( zIn[0]=='-' ){
jsonAppendChar(p, '-');
zIn++;
N--;
}
while( zIn[0]=='0' && N>1 ){
zIn++;
N--;
}
jsonAppendRaw(p, zIn, N);
}
/*
** The zIn[0..N] string is a JSON5 real literal. Append to p a translation
** of the string literal that standard JSON and that omits all JSON5
** features.
*/
static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){
int i;
if( zIn[0]=='+' ){
zIn++;
N--;
}else if( zIn[0]=='-' ){
jsonAppendChar(p, '-');
zIn++;
N--;
}
if( zIn[0]=='.' ){
jsonAppendChar(p, '0');
}
for(i=0; i<N; i++){
if( zIn[i]=='.' && (i+1==N || !sqlite3Isdigit(zIn[i+1])) ){
jsonAppendRaw(p, zIn, i);
zIn += i;
N -= i;
jsonAppendChar(p, '0');
break;
}
}
if( N>0 ){
jsonAppendRaw(p, zIn, N);
}
}
/*
** Append a function parameter value to the JSON string under
** construction.
@ -425,17 +492,32 @@ static void jsonRenderNode(
break;
}
case JSON_STRING: {
assert( pNode->eU==1 );
if( pNode->jnFlags & JNODE_RAW ){
assert( pNode->eU==1 );
jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
break;
}else if( pNode->jnFlags & JNODE_JSON5 ){
jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n);
}else{
jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
}
/* no break */ deliberate_fall_through
break;
}
case JSON_REAL: {
assert( pNode->eU==1 );
if( pNode->jnFlags & JNODE_JSON5 ){
jsonAppendNormalizedReal(pOut, pNode->u.zJContent, pNode->n);
}else{
jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
}
break;
}
case JSON_REAL:
case JSON_INT: {
assert( pNode->eU==1 );
jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
if( pNode->jnFlags & JNODE_JSON5 ){
jsonAppendNormalizedInt(pOut, pNode->u.zJContent, pNode->n);
}else{
jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
}
break;
}
case JSON_ARRAY: {
@ -615,9 +697,10 @@ static void jsonReturn(
const char *z;
char *zOut;
u32 j;
u32 nOut = n;
assert( pNode->eU==1 );
z = pNode->u.zJContent;
zOut = sqlite3_malloc( n+1 );
zOut = sqlite3_malloc( nOut+1 );
if( zOut==0 ){
sqlite3_result_error_nomem(pCtx);
break;
@ -669,6 +752,15 @@ static void jsonReturn(
c = '\r';
}else if( c=='t' ){
c = '\t';
}else if( c=='v' ){
c = '\v';
}else if( c=='\'' ){
c = '\'';
}else if( c=='0' ){
c = 0;
}else if( c=='x' ){
c = (jsonHexToInt(z[i+1])<<4) | jsonHexToInt(z[i+2]);
i += 2;
}
zOut[j++] = c;
}
@ -749,13 +841,18 @@ static int jsonParseAddNode(
return pParse->nNode++;
}
/*
** Return true if z[] begins with 2 (or more) hexadecimal digits
*/
static int jsonIs2Hex(const char *z){
return sqlite3Isxdigit(z[0]) && sqlite3Isxdigit(z[1]);
}
/*
** Return true if z[] begins with 4 (or more) hexadecimal digits
*/
static int jsonIs4Hex(const char *z){
int i;
for(i=0; i<4; i++) if( !sqlite3Isxdigit(z[i]) ) return 0;
return 1;
return jsonIs2Hex(z) && jsonIs2Hex(&z[2]);
}
/*
@ -978,9 +1075,17 @@ json_parse_restart:
pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
return j+1;
}
case '"': {
case '\'': {
u8 jnFlags;
char cDelim;
pParse->has5 = 1;
jnFlags = JNODE_JSON5;
goto parse_string;
case '"':
/* Parse string */
u8 jnFlags = 0;
jnFlags = 0;
parse_string:
cDelim = z[i];
j = i+1;
for(;;){
c = z[j];
@ -992,12 +1097,16 @@ json_parse_restart:
c = z[++j];
if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
|| c=='n' || c=='r' || c=='t'
|| (c=='u' && jsonIs4Hex(z+j+1)) ){
jnFlags = JNODE_ESCAPE;
|| (c=='u' && jsonIs4Hex(&z[j+1])) ){
jnFlags |= JNODE_ESCAPE;
}else if( c=='\'' || c=='0' || c=='v'
|| (c=='x' && jsonIs2Hex(&z[j+1])) ){
jnFlags |= (JNODE_ESCAPE|JNODE_JSON5);
pParse->has5 = 1;
}else{
return -1;
}
}else if( c=='"' ){
}else if( c==cDelim ){
break;
}
j++;
@ -1027,9 +1136,11 @@ json_parse_restart:
}
return -1;
}
case '+':
case '+': {
u8 seenDP, seenE, jnFlags;
pParse->has5 = 1;
/* fall through */
jnFlags = JNODE_JSON5;
goto parse_number;
case '-':
case '0':
case '1':
@ -1040,10 +1151,12 @@ json_parse_restart:
case '6':
case '7':
case '8':
case '9': {
case '9':
/* Parse number */
u8 seenDP = 0;
u8 seenE = 0;
jnFlags = 0;
parse_number:
seenDP = 0;
seenE = 0;
assert( '-' < '0' );
assert( '+' < '0' );
assert( '.' < '0' );
@ -1053,10 +1166,14 @@ json_parse_restart:
if( c=='0' ){
if( sqlite3Isdigit(z[i+1]) ){
pParse->has5 = 1;
jnFlags = JNODE_JSON5;
}
}else{
if( !sqlite3Isdigit(z[i+1]) ) return -1;
if( z[i+1]=='0' && sqlite3Isdigit(z[i+2]) ) pParse->has5 = 1;
if( z[i+1]=='0' && sqlite3Isdigit(z[i+2]) ){
pParse->has5 = 1;
jnFlags = JNODE_JSON5;
}
}
}
for(j=i+1;; j++){
@ -1103,6 +1220,9 @@ json_parse_restart:
if( z[j-1]<'0' ) return -1;
jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT,
j - i, &z[i]);
if( jnFlags && !pParse->oom ){
pParse->aNode[pParse->nNode-1].jnFlags = jnFlags;
}
return j;
}
case '}': {