mirror of
https://github.com/felt/tippecanoe.git
synced 2026-10-05 09:55:41 +02:00
A container object makes the state transitions more straightforward
And should make it possible to free an object that has just been read and still know what the next state will be, importantly.
This commit is contained in:
@@ -76,10 +76,10 @@ void json_print(json_object *j, int depth) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void process(FILE *f) {
|
void process(FILE *f) {
|
||||||
|
json_pull *jp = json_begin(f);
|
||||||
json_object *j = NULL;
|
json_object *j = NULL;
|
||||||
char *error = NULL;
|
|
||||||
|
|
||||||
while ((j = json_parse(f, j, &error)) != NULL) {
|
while ((j = json_parse(jp)) != NULL) {
|
||||||
if (j->parent == NULL) {
|
if (j->parent == NULL) {
|
||||||
json_print(j, 0);
|
json_print(j, 0);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@@ -87,8 +87,8 @@ void process(FILE *f) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error != NULL) {
|
if (jp->error != NULL) {
|
||||||
fprintf(stderr, "%s\n", error);
|
fprintf(stderr, "%s\n", jp->error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+93
-107
@@ -5,51 +5,57 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include "jsonpull.h"
|
#include "jsonpull.h"
|
||||||
|
|
||||||
|
typedef enum json_expect {
|
||||||
|
JSON_ITEM, JSON_COMMA, JSON_COLON, JSON_KEY, JSON_VALUE,
|
||||||
|
} json_expect;
|
||||||
|
|
||||||
|
json_pull *json_begin(FILE *f) {
|
||||||
|
json_pull *j = malloc(sizeof(json_pull));
|
||||||
|
j->container = NULL;
|
||||||
|
j->f = f;
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
#define SIZE_FOR(i) (((i) + 31) & ~31)
|
#define SIZE_FOR(i) (((i) + 31) & ~31)
|
||||||
|
|
||||||
static json_object *add_object(json_type type, json_object *parent, char **error) {
|
static json_object *add_object(json_pull *j, json_type type) {
|
||||||
json_object *o = malloc(sizeof(struct json_object));
|
json_object *o = malloc(sizeof(struct json_object));
|
||||||
o->type = type;
|
o->type = type;
|
||||||
o->parent = parent;
|
o->parent = j->container;
|
||||||
o->array = NULL;
|
o->array = NULL;
|
||||||
o->keys = NULL;
|
o->keys = NULL;
|
||||||
o->values = NULL;
|
o->values = NULL;
|
||||||
o->length = 0;
|
o->length = 0;
|
||||||
|
|
||||||
if (parent != NULL) {
|
json_object *c = j->container;
|
||||||
if (parent->type == JSON_ARRAY) {
|
|
||||||
if (SIZE_FOR(parent->length + 1) != SIZE_FOR(parent->length)) {
|
if (c != NULL) {
|
||||||
parent->array = realloc(parent->array, SIZE_FOR(parent->length + 1) * sizeof(json_object *));
|
if (c->type == JSON_ARRAY) {
|
||||||
|
if (SIZE_FOR(c->length + 1) != SIZE_FOR(c->length)) {
|
||||||
|
c->array = realloc(c->array, SIZE_FOR(c->length + 1) * sizeof(json_object *));
|
||||||
}
|
}
|
||||||
|
|
||||||
parent->array[parent->length++] = o;
|
c->array[c->length++] = o;
|
||||||
} else if (parent->type == JSON_HASH) {
|
c->expect = JSON_COMMA;
|
||||||
if (parent->length > 0 && parent->values[parent->length - 1] == NULL) {
|
} else if (c->type == JSON_HASH) {
|
||||||
// Hash has key but no value, so this is the value
|
if (c->expect == JSON_VALUE) {
|
||||||
|
c->values[c->length - 1] = o;
|
||||||
parent->values[parent->length - 1] = o;
|
c->expect = JSON_COMMA;
|
||||||
} else {
|
} else {
|
||||||
// No current hash, so this is a key
|
|
||||||
|
|
||||||
if (type != JSON_STRING) {
|
if (type != JSON_STRING) {
|
||||||
*error = "Hash key is not a string";
|
j->error = "Hash key is not a string";
|
||||||
free(o);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SIZE_FOR(parent->length + 1) != SIZE_FOR(parent->length)) {
|
if (SIZE_FOR(c->length + 1) != SIZE_FOR(c->length)) {
|
||||||
parent->keys = realloc(parent->keys, SIZE_FOR(parent->length + 1) * sizeof(json_object *));
|
c->keys = realloc(c->keys, SIZE_FOR(c->length + 1) * sizeof(json_object *));
|
||||||
parent->values = realloc(parent->values, SIZE_FOR(parent->length + 1) * sizeof(json_object *));
|
c->values = realloc(c->values, SIZE_FOR(c->length + 1) * sizeof(json_object *));
|
||||||
}
|
}
|
||||||
|
|
||||||
parent->keys[parent->length] = o;
|
c->keys[c->length] = o;
|
||||||
parent->values[parent->length] = NULL;
|
c->values[c->length] = NULL;
|
||||||
parent->length++;
|
c->length++;
|
||||||
|
c->expect = JSON_COLON;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
*error = "Parent is not a container";
|
|
||||||
free(o);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,17 +112,17 @@ static void string_free(struct string *s) {
|
|||||||
free(s->buf);
|
free(s->buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
json_object *json_parse(FILE *f, json_object *current, char **error) {
|
json_object *json_parse(json_pull *j) {
|
||||||
int current_is = 0;
|
|
||||||
int c;
|
int c;
|
||||||
|
FILE *f = j->f;
|
||||||
again:
|
again:
|
||||||
/////////////////////////// Whitespace
|
/////////////////////////// Whitespace
|
||||||
|
|
||||||
do {
|
do {
|
||||||
c = getc(f);
|
c = getc(f);
|
||||||
if (c == EOF) {
|
if (c == EOF) {
|
||||||
if (current != NULL) {
|
if (j->container != NULL) {
|
||||||
*error = "Reached EOF without all containers being closed";
|
j->error = "Reached EOF without all containers being closed";
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -126,144 +132,130 @@ again:
|
|||||||
/////////////////////////// Arrays
|
/////////////////////////// Arrays
|
||||||
|
|
||||||
if (c == '[') {
|
if (c == '[') {
|
||||||
current = add_object(JSON_ARRAY, current, error);
|
j->container = add_object(j, JSON_ARRAY);
|
||||||
if (current == NULL) {
|
j->container->expect = JSON_ITEM;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
current_is = '[';
|
|
||||||
goto again;
|
goto again;
|
||||||
} else if (c == ']') {
|
} else if (c == ']') {
|
||||||
if (current == NULL) {
|
if (j->container == NULL) {
|
||||||
*error = "Found ] at top level";
|
j->error = "Found ] at top level";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_is == '[') { // Empty array
|
if (j->container->type != JSON_ARRAY) {
|
||||||
return current;
|
j->error = "Found ] not in an array";
|
||||||
} else if (current_is) {
|
|
||||||
*error = "Found ] without final element";
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current->parent == NULL || current->parent->type != JSON_ARRAY) {
|
if (j->container->expect != JSON_COMMA) {
|
||||||
*error = "Found ] without matching [";
|
if (! (j->container->expect == JSON_ITEM && j->container->length == 0)) {
|
||||||
return NULL;
|
j->error = "Found ] without final element";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return current->parent;
|
json_object *ret = j->container;
|
||||||
|
j->container = ret->parent;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////// Hashes
|
/////////////////////////// Hashes
|
||||||
|
|
||||||
if (c == '{') {
|
if (c == '{') {
|
||||||
current = add_object(JSON_HASH, current, error);
|
j->container = add_object(j, JSON_HASH);
|
||||||
if (current == NULL) {
|
j->container->expect = JSON_KEY;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
current_is = '{';
|
|
||||||
goto again;
|
goto again;
|
||||||
} else if (c == '}') {
|
} else if (c == '}') {
|
||||||
if (current == NULL) {
|
if (j->container == NULL) {
|
||||||
*error = "Found } at top level";
|
j->error = "Found } at top level";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_is == '{') { // Empty hash
|
if (j->container->type != JSON_HASH) {
|
||||||
return current;
|
j->error = "Found } not in a hash";
|
||||||
} else if (current_is) {
|
|
||||||
*error = "Found } without final element";
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current->parent == NULL || current->parent->type != JSON_HASH) {
|
if (j->container->expect != JSON_COMMA) {
|
||||||
*error = "Found } without matching {";
|
if (! (j->container->expect == JSON_KEY && j->container->length == 0)) {
|
||||||
return NULL;
|
j->error = "Found } without final element";
|
||||||
}
|
return NULL;
|
||||||
if (current->parent->length != 0 && current->parent->values[current->parent->length - 1] == NULL) {
|
}
|
||||||
*error = "Found hash key without value";
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return current->parent;
|
json_object *ret = j->container;
|
||||||
|
j->container = ret->parent;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////// Null
|
/////////////////////////// Null
|
||||||
|
|
||||||
if (c == 'n') {
|
if (c == 'n') {
|
||||||
if (getc(f) != 'u' || getc(f) != 'l' || getc(f) != 'l') {
|
if (getc(f) != 'u' || getc(f) != 'l' || getc(f) != 'l') {
|
||||||
*error = "Found misspelling of null";
|
j->error = "Found misspelling of null";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return add_object(JSON_NULL, current, error);
|
return add_object(j, JSON_NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////// True
|
/////////////////////////// True
|
||||||
|
|
||||||
if (c == 't') {
|
if (c == 't') {
|
||||||
if (getc(f) != 'r' || getc(f) != 'u' || getc(f) != 'e') {
|
if (getc(f) != 'r' || getc(f) != 'u' || getc(f) != 'e') {
|
||||||
*error = "Found misspelling of true";
|
j->error = "Found misspelling of true";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return add_object(JSON_TRUE, current, error);
|
return add_object(j, JSON_TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////// False
|
/////////////////////////// False
|
||||||
|
|
||||||
if (c == 'f') {
|
if (c == 'f') {
|
||||||
if (getc(f) != 'a' || getc(f) != 'l' || getc(f) != 's' || getc(f) != 'e') {
|
if (getc(f) != 'a' || getc(f) != 'l' || getc(f) != 's' || getc(f) != 'e') {
|
||||||
*error = "Found misspelling of false";
|
j->error = "Found misspelling of false";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return add_object(JSON_FALSE, current, error);
|
return add_object(j, JSON_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////// Comma
|
/////////////////////////// Comma
|
||||||
|
|
||||||
if (c == ',') {
|
if (c == ',') {
|
||||||
if (current == NULL) {
|
if (j->container == NULL) {
|
||||||
*error = "Found comma at top level";
|
j->error = "Found comma at top level";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current->parent == NULL || (current->parent->type != JSON_ARRAY && current->parent->type != JSON_HASH)) {
|
if (j->container->expect != JSON_COMMA) {
|
||||||
*error = "Found comma outside of array or hash";
|
j->error = "Found unexpected comma";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current->parent->type == JSON_HASH) {
|
if (j->container->type == JSON_HASH) {
|
||||||
if (current->parent->length == 0 || current->parent->values[current->parent->length - 1] == NULL) {
|
j->container->expect = JSON_KEY;
|
||||||
*error = "Found comma in hash without a hash value";
|
} else {
|
||||||
return NULL;
|
j->container->expect = JSON_ITEM;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
current = current->parent;
|
|
||||||
current_is = ',';
|
|
||||||
goto again;
|
goto again;
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////// Colon
|
/////////////////////////// Colon
|
||||||
|
|
||||||
if (c == ':') {
|
if (c == ':') {
|
||||||
if (current == NULL) {
|
if (j->container == NULL) {
|
||||||
*error = "Found colon at top level";
|
j->error = "Found colon at top level";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current->parent == NULL || current->parent->type != JSON_HASH) {
|
if (j->container->expect != JSON_COLON) {
|
||||||
*error = "Found colon outside of hash";
|
j->error = "Found unexpected colon";
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (current->parent->length == 0 || current->parent->keys[current->parent->length - 1] == NULL) {
|
|
||||||
*error = "Found colon without a hash key";
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
current = current->parent;
|
j->container->expect = JSON_VALUE;
|
||||||
current_is = ':';
|
|
||||||
goto again;
|
goto again;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,7 +303,7 @@ again:
|
|||||||
|
|
||||||
c = peek(f);
|
c = peek(f);
|
||||||
if (c < '0' || c > '9') {
|
if (c < '0' || c > '9') {
|
||||||
*error = "Exponent without digits";
|
j->error = "Exponent without digits";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
while (c >= '0' && c <= '9') {
|
while (c >= '0' && c <= '9') {
|
||||||
@@ -320,12 +312,9 @@ again:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
json_object *n = add_object(JSON_NUMBER, current, error);
|
json_object *n = add_object(j, JSON_NUMBER);
|
||||||
if (n != NULL) {
|
n->number = atof(val.buf);
|
||||||
n->number = atof(val.buf);
|
string_free(&val);
|
||||||
string_free(&val);
|
|
||||||
}
|
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +364,7 @@ again:
|
|||||||
string_append(&val, 0x80 | (ch & 0x3F));
|
string_append(&val, 0x80 | (ch & 0x3F));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
*error = "Found backslash followed by unknown character";
|
j->error = "Found backslash followed by unknown character";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -383,16 +372,13 @@ again:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
json_object *s = add_object(JSON_STRING, current, error);
|
json_object *s = add_object(j, JSON_STRING);
|
||||||
if (s != NULL) {
|
s->string = val.buf;
|
||||||
s->string = val.buf;
|
s->length = val.n;
|
||||||
s->length = val.n;
|
|
||||||
}
|
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
*error = "Found unexpected character";
|
j->error = "Found unexpected character";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-1
@@ -13,7 +13,18 @@ typedef struct json_object {
|
|||||||
struct json_object **keys;
|
struct json_object **keys;
|
||||||
struct json_object **values;
|
struct json_object **values;
|
||||||
int length;
|
int length;
|
||||||
|
|
||||||
|
int expect;
|
||||||
} json_object;
|
} json_object;
|
||||||
|
|
||||||
json_object *json_parse(FILE *f, json_object *current, char **error);
|
struct json_pull {
|
||||||
|
json_object *container;
|
||||||
|
char *error;
|
||||||
|
FILE *f;
|
||||||
|
};
|
||||||
|
typedef struct json_pull json_pull;
|
||||||
|
|
||||||
|
json_pull *json_begin(FILE *f);
|
||||||
|
json_object *json_parse(json_pull *j);
|
||||||
|
|
||||||
json_object *json_hash_get(json_object *o, char *s);
|
json_object *json_hash_get(json_object *o, char *s);
|
||||||
|
|||||||
Reference in New Issue
Block a user