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:
Eric Fischer
2014-02-06 18:56:22 -08:00
parent 55510cac04
commit 979b9243d8
3 changed files with 109 additions and 112 deletions
+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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);