[plist] Fix json parsing and test

It turns out my test was backwards: it fed the json text into a string
object then pulled it out, which of course round-tripped correctly. Now
things do round-trip correctly, but I had to ignore white-space
differences and edit the numbers as text->double->text doesn't
necessarily round-trip well (though double->text->double does with 17
digits).
This commit is contained in:
Bill Currie 2024-10-19 02:15:27 +09:00
parent cb0eeb9366
commit d780146be3
2 changed files with 54 additions and 35 deletions

View file

@ -141,8 +141,6 @@ init_quotables (void)
quotable_bitmap[*c / 8] &= ~(1 << (*c % 8)); quotable_bitmap[*c / 8] &= ~(1 << (*c % 8));
} }
static plitem_t *pl_parsepropertylistitem (pldata_t *pl);
static const char * static const char *
dict_get_key (const void *i, void *unused) dict_get_key (const void *i, void *unused)
{ {
@ -753,12 +751,13 @@ make_byte (byte h, byte l)
} }
static int static int
pl_parsekeyvalue (pldata_t *pl, plitem_t *dict, bool end_ok) pl_parsekeyvalue (pldata_t *pl, plitem_t *dict,
plitem_t *(*parse_item) (pldata_t *pl), bool end_ok)
{ {
plitem_t *key = 0; plitem_t *key = 0;
plitem_t *value = 0; plitem_t *value = 0;
if (!(key = pl_parsepropertylistitem (pl))) { if (!(key = parse_item (pl))) {
return 0; return 0;
} }
if (key->type != QFString) { if (key->type != QFString) {
@ -771,7 +770,7 @@ pl_parsekeyvalue (pldata_t *pl, plitem_t *dict, bool end_ok)
} }
pl->pos++; pl->pos++;
if (!(value = pl_parsepropertylistitem (pl))) { if (!(value = parse_item (pl))) {
goto error; goto error;
} }
@ -797,14 +796,14 @@ error:
} }
static plitem_t * static plitem_t *
pl_parsedictionary (pldata_t *pl) pl_parsedictionary (pldata_t *pl, plitem_t *(*parse_item) (pldata_t *pl))
{ {
plitem_t *dict = PL_NewDictionary (pl->hashctx); plitem_t *dict = PL_NewDictionary (pl->hashctx);
dict->line = pl->line; dict->line = pl->line;
pl->pos++; // skip over opening { pl->pos++; // skip over opening {
while (pl_skipspace (pl, false) && pl->ptr[pl->pos] != '}') { while (pl_skipspace (pl, false) && pl->ptr[pl->pos] != '}') {
if (!pl_parsekeyvalue (pl, dict, false)) { if (!pl_parsekeyvalue (pl, dict, parse_item, false)) {
PL_Release (dict); PL_Release (dict);
return nullptr; return nullptr;
} }
@ -820,11 +819,12 @@ pl_parsedictionary (pldata_t *pl)
} }
static int static int
pl_parsevalue (pldata_t *pl, plitem_t *array, int end_ok) pl_parsevalue (pldata_t *pl, plitem_t *array,
plitem_t *(*parse_item) (pldata_t *pl), int end_ok)
{ {
plitem_t *value; plitem_t *value;
if (!(value = pl_parsepropertylistitem (pl))) { if (!(value = parse_item (pl))) {
return 0; return 0;
} }
if (!PL_A_AddObject (array, value)) { if (!PL_A_AddObject (array, value)) {
@ -847,7 +847,7 @@ pl_parsevalue (pldata_t *pl, plitem_t *array, int end_ok)
} }
static plitem_t * static plitem_t *
pl_parsearray (pldata_t *pl) pl_parsearray (pldata_t *pl, plitem_t *(*parse_item) (pldata_t *pl))
{ {
plitem_t *array = PL_NewArray (); plitem_t *array = PL_NewArray ();
array->line = pl->line; array->line = pl->line;
@ -855,7 +855,7 @@ pl_parsearray (pldata_t *pl)
pl->pos++; // skip over opening ( pl->pos++; // skip over opening (
while (pl_skipspace (pl, false) && pl->ptr[pl->pos] != ')') { while (pl_skipspace (pl, false) && pl->ptr[pl->pos] != ')') {
if (!pl_parsevalue (pl, array, 0)) { if (!pl_parsevalue (pl, array, parse_item, false)) {
PL_Release (array); PL_Release (array);
return nullptr; return nullptr;
} }
@ -1080,8 +1080,8 @@ pl_parsepropertylistitem (pldata_t *pl)
} }
switch (pl->ptr[pl->pos]) { switch (pl->ptr[pl->pos]) {
case '{': return pl_parsedictionary (pl); case '{': return pl_parsedictionary (pl, pl_parsepropertylistitem);
case '(': return pl_parsearray (pl); case '(': return pl_parsearray (pl, pl_parsepropertylistitem);
case '<': return pl_parsebinary (pl); case '<': return pl_parsebinary (pl);
case '"': return pl_parsequotedstring (pl); case '"': return pl_parsequotedstring (pl);
default: return pl_parseunquotedstring (pl); default: return pl_parseunquotedstring (pl);
@ -1089,7 +1089,7 @@ pl_parsepropertylistitem (pldata_t *pl)
} }
static plitem_t * static plitem_t *
pl_parsejson_array (pldata_t *pl) pl_parsejson_array (pldata_t *pl, plitem_t *(*parse_item) (pldata_t *pl))
{ {
plitem_t *array = PL_NewArray (); plitem_t *array = PL_NewArray ();
array->line = pl->line; array->line = pl->line;
@ -1097,7 +1097,7 @@ pl_parsejson_array (pldata_t *pl)
pl->pos++; // skip over opening [ pl->pos++; // skip over opening [
while (pl_skipspace (pl, false) && pl->ptr[pl->pos] != ']') { while (pl_skipspace (pl, false) && pl->ptr[pl->pos] != ']') {
if (!pl_parsevalue (pl, array, false)) { if (!pl_parsevalue (pl, array, parse_item, false)) {
PL_Release (array); PL_Release (array);
return nullptr; return nullptr;
} }
@ -1183,8 +1183,8 @@ pl_parsejson_element (pldata_t *pl)
return nullptr; return nullptr;
} }
switch (pl->ptr[pl->pos]) { switch (pl->ptr[pl->pos]) {
case '[': return pl_parsejson_array (pl); case '[': return pl_parsejson_array (pl, pl_parsejson_element);
case '{': return pl_parsedictionary (pl); case '{': return pl_parsedictionary (pl, pl_parsejson_element);
case ']': case ']':
case '}': case '}':
case ':': case ':':
@ -1252,7 +1252,7 @@ pl_getdictionary (pldata_t *pl)
dict->line = pl->line; dict->line = pl->line;
while (pl_skipspace (pl, true)) { while (pl_skipspace (pl, true)) {
if (!pl_parsekeyvalue (pl, dict, true)) { if (!pl_parsekeyvalue (pl, dict, pl_parsepropertylistitem, true)) {
PL_Release (dict); PL_Release (dict);
return nullptr; return nullptr;
} }
@ -1274,7 +1274,7 @@ pl_getarray (pldata_t *pl)
array->line = pl->line; array->line = pl->line;
while (pl_skipspace (pl, true)) { while (pl_skipspace (pl, true)) {
if (!pl_parsevalue (pl, array, true)) { if (!pl_parsevalue (pl, array, pl_parsepropertylistitem, true)) {
PL_Release (array); PL_Release (array);
return nullptr; return nullptr;
} }
@ -1404,7 +1404,6 @@ write_item (dstring_t *dstr, const plitem_t *item, int level, bool json)
plarray_t *array; plarray_t *array;
pldict_t *dict; pldict_t *dict;
plbinary_t *binary; plbinary_t *binary;
int i;
switch (item->type) { switch (item->type) {
case QFDictionary: case QFDictionary:
@ -1414,17 +1413,24 @@ write_item (dstring_t *dstr, const plitem_t *item, int level, bool json)
current = dict->keys.a[i]; current = dict->keys.a[i];
write_tabs (dstr, level + 1); write_tabs (dstr, level + 1);
write_string (dstr, current->key, json); write_string (dstr, current->key, json);
if (json) {
write_string_len (dstr, ": ", 2);
} else {
write_string_len (dstr, " = ", 3); write_string_len (dstr, " = ", 3);
write_item (dstr, current->value, level + 1, json);
write_string_len (dstr, ";\n", 2);
} }
write_item (dstr, current->value, level + 1, json);
if (i + 1 < dict->keys.size) {
write_string_len (dstr, json ? ",\n" : ";\n", 2);
}
}
write_string_len (dstr, "\n", 1);
write_tabs (dstr, level); write_tabs (dstr, level);
write_string_len (dstr, "}", 1); write_string_len (dstr, "}", 1);
break; break;
case QFArray: case QFArray:
write_string_len (dstr, "(\n", 2); write_string_len (dstr, json ? "[\n" : "(\n", 2);
array = (plarray_t *) item->data; array = (plarray_t *) item->data;
for (i = 0; i < array->numvals; i++) { for (int i = 0; i < array->numvals; i++) {
write_tabs (dstr, level + 1); write_tabs (dstr, level + 1);
write_item (dstr, array->values[i], level + 1, json); write_item (dstr, array->values[i], level + 1, json);
if (i < array->numvals - 1) if (i < array->numvals - 1)
@ -1432,7 +1438,7 @@ write_item (dstring_t *dstr, const plitem_t *item, int level, bool json)
} }
write_string_len (dstr, "\n", 1); write_string_len (dstr, "\n", 1);
write_tabs (dstr, level); write_tabs (dstr, level);
write_string_len (dstr, ")", 1); write_string_len (dstr, json ? "]\n" : ")", 1);
break; break;
case QFBinary: case QFBinary:
write_string_len (dstr, "<", 1); write_string_len (dstr, "<", 1);

View file

@ -2,6 +2,7 @@
# include "config.h" # include "config.h"
#endif #endif
#include <string.h> #include <string.h>
#include <ctype.h>
#include "QF/plist.h" #include "QF/plist.h"
static const char *test_strings[] = { static const char *test_strings[] = {
@ -24,7 +25,7 @@ static const char *test_strings[] = {
"[" "\n" "[" "\n"
" {" "\n" " {" "\n"
" \"precision\": \"zip\"," "\n" " \"precision\": \"zip\"," "\n"
" \"Latitude\": 37.7668," "\n" " \"Latitude\": 37.766800000000003," "\n"
" \"Longitude\": -122.3959," "\n" " \"Longitude\": -122.3959," "\n"
" \"Address\": \"\"," "\n" " \"Address\": \"\"," "\n"
" \"City\": \"SAN FRANCISCO\"," "\n" " \"City\": \"SAN FRANCISCO\"," "\n"
@ -34,8 +35,8 @@ static const char *test_strings[] = {
" }," "\n" " }," "\n"
" {" "\n" " {" "\n"
" \"precision\": \"zip\"," "\n" " \"precision\": \"zip\"," "\n"
" \"Latitude\": 37.371991," "\n" " \"Latitude\": 37.371991000000001," "\n"
" \"Longitude\": -122.026020," "\n" " \"Longitude\": -122.02602," "\n"
" \"Address\": \"\"," "\n" " \"Address\": \"\"," "\n"
" \"City\": \"SUNNYVALE\"," "\n" " \"City\": \"SUNNYVALE\"," "\n"
" \"State\": \"CA\"," "\n" " \"State\": \"CA\"," "\n"
@ -52,23 +53,35 @@ static const char *test_strings[] = {
}; };
#define num_string_tests (sizeof (test_strings) / sizeof (test_strings[0])) #define num_string_tests (sizeof (test_strings) / sizeof (test_strings[0]))
static int
wsstrcmp (const char *s1, const char *s2)
{
while (*s1 && *s1 == *s2) {
for (; *s1 && *s1 == *s2; s1++, s2++) continue;
for (; *s1 && isspace (*s1); s1++) continue;
for (; *s2 && isspace (*s2); s2++) continue;
}
return *s1 - *s2;
}
static int static int
test_string_io (const char *str) test_string_io (const char *str)
{ {
plitem_t *item; plitem_t *item;
const char *res;
char *saved; char *saved;
item = PL_NewString (str); item = PL_ParseJSON (str, 0);
if (!item) {
printf ("failed to parse\n");
return 0;
}
saved = PL_WriteJSON (item); saved = PL_WriteJSON (item);
PL_Release (item); PL_Release (item);
item = PL_ParseJSON (saved, 0); printf ("%p %p\n", str, saved);
res = PL_String (item); if (!wsstrcmp (str, saved))
if (!strcmp (str, res))
return 1; return 1;
printf ("expect: %s\n", str); printf ("expect: %s\n", str);
printf ("got : %s\n", res); printf ("got : %s\n", saved);
printf ("saved : %s\n", saved);
return 0; return 0;
} }