rtjson: small fixes, add booleans and null
This commit is contained in:
parent
e65550b6e5
commit
c901af0a17
186
rtjson.h
186
rtjson.h
@ -37,6 +37,8 @@ typedef enum
|
||||
JSON_TYPE_STRING,
|
||||
JSON_TYPE_INTEGER,
|
||||
JSON_TYPE_FLOAT,
|
||||
JSON_TYPE_BOOL,
|
||||
JSON_TYPE_NULL,
|
||||
} json_type;
|
||||
|
||||
/* Represents a json value */
|
||||
@ -54,6 +56,7 @@ typedef struct json
|
||||
s8 s;
|
||||
i64 i;
|
||||
f64 f;
|
||||
b32 b;
|
||||
|
||||
/* The first subobject.
|
||||
* Element 0 of an array,
|
||||
@ -83,7 +86,7 @@ RTJ_API json *GetJSONArrayElement(json *a, isize i);
|
||||
RTJ_API isize GetJSONArrayLength(json *a);
|
||||
|
||||
/* Iterates over every member of an object or array */
|
||||
#define JsonForEach(_ChildVarName, _ParentPtr) \
|
||||
#define JSONForEach(_ChildVarName, _ParentPtr) \
|
||||
for (json *_ChildVarName = (_ParentPtr)->value.first_child; _ChildVarName != 0; _ChildVarName = _ChildVarName->next)
|
||||
|
||||
#endif
|
||||
@ -319,13 +322,97 @@ ConsumeWhitespace(s8 text, isize *_at, isize *current_line)
|
||||
} \
|
||||
(_At) += 1;
|
||||
|
||||
static json *ParseJSONImpl(s8 text, isize *_at, isize *current_line, s8 file, arena *a);
|
||||
|
||||
static json *
|
||||
ParseJSONImpl(s8 text, isize *_at, json *sibling, isize *current_line, s8 file, arena *a)
|
||||
ParseValue(s8 text, isize *_at, isize *current_line, s8 file, arena *a)
|
||||
{
|
||||
json *child = NULL;
|
||||
isize at = *_at;
|
||||
ConsumeWhitespaceNoEof(text, &at, current_line, file, 0);
|
||||
if (text.data[at] == '{' || text.data[at] == '[')
|
||||
{
|
||||
/* Object or array, recurse into it. */
|
||||
child = ParseJSONImpl(text, &at, current_line, file, a);
|
||||
if (!child)
|
||||
return NULL;
|
||||
}
|
||||
else if (text.data[at] == '\"')
|
||||
{
|
||||
child = alloc(a, json);
|
||||
child->value.s = ParseString(text, &at, current_line, file, a);
|
||||
if (!child->value.s.data)
|
||||
return NULL;
|
||||
child->type = JSON_TYPE_STRING;
|
||||
}
|
||||
else if ((text.data[at] >= '0' && text.data[at] <= '9') || text.data[at] == '-')
|
||||
{
|
||||
/* Number. */
|
||||
f64 f;
|
||||
i64 i;
|
||||
parse_number_result res = ParseNumber(text, &at, current_line, file, &i, &f);
|
||||
if (res == NOT_A_NUMBER)
|
||||
return NULL;
|
||||
child = alloc(a, json);
|
||||
if (res == INT)
|
||||
{
|
||||
child->value.i = i;
|
||||
child->type = JSON_TYPE_INTEGER;
|
||||
}
|
||||
else /* DOUBLE */
|
||||
{
|
||||
child->value.f = f;
|
||||
child->type = JSON_TYPE_FLOAT;
|
||||
}
|
||||
}
|
||||
else if (text.data[at] == 't' || text.data[at] == 'f' || text.data[at] == 'n')
|
||||
{
|
||||
/* true / false / null */
|
||||
const s8 true_str = S8("true");
|
||||
const s8 false_str = S8("false");
|
||||
const s8 null_str = S8("null");
|
||||
s8 value_str = {.data = &text.data[at], .length = 1};
|
||||
++at;
|
||||
while (at < text.length && text.data[at] >= 'a' && text.data[at] <= 'z')
|
||||
{
|
||||
/* small-caps character form these keywords */
|
||||
++at;
|
||||
++value_str.length;
|
||||
}
|
||||
child = alloc(a, json);
|
||||
if (S8Equals(value_str, true_str))
|
||||
{
|
||||
child->value.b = 1;
|
||||
child->type = JSON_TYPE_BOOL;
|
||||
}
|
||||
else if (S8Equals(value_str, false_str))
|
||||
{
|
||||
child->value.b = 0;
|
||||
child->type = JSON_TYPE_BOOL;
|
||||
}
|
||||
else if (S8Equals(value_str, null_str))
|
||||
{
|
||||
child->type = JSON_TYPE_NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (file.data)
|
||||
printf("%.*s:%zu unexpected keyword.\n", (int)file.length, file.data, *current_line);
|
||||
else
|
||||
printf("%zu: unexpected keyword\n", *current_line);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
*_at = at;
|
||||
return child;
|
||||
}
|
||||
|
||||
static json *
|
||||
ParseJSONImpl(s8 text, isize *_at, isize *current_line, s8 file, arena *a)
|
||||
{
|
||||
isize at = *_at;
|
||||
|
||||
json *j = alloc(a, json);
|
||||
j->next = sibling;
|
||||
|
||||
if (text.data[at] == '{')
|
||||
{
|
||||
@ -347,49 +434,12 @@ ParseJSONImpl(s8 text, isize *_at, json *sibling, isize *current_line, s8 file,
|
||||
ExpectCharacter(':', text, at, current_line, file, 0);
|
||||
ConsumeWhitespaceNoEof(text, &at, current_line, file, 0);
|
||||
|
||||
/* Value */
|
||||
if (text.data[at] == '{' || text.data[at] == '[')
|
||||
{
|
||||
/* Object or array, recurse into it.
|
||||
* JSON objects are unordered, so it does not matter that we
|
||||
* effectively reverse the order here */
|
||||
j->value.first_child = ParseJSONImpl(text, &at, j->value.first_child, current_line, file, a);
|
||||
if (!j->value.first_child)
|
||||
json *child = ParseValue(text, &at, current_line, file, a);
|
||||
if (!child)
|
||||
return 0;
|
||||
j->value.first_child->key = key;
|
||||
}
|
||||
else if (text.data[at] == '\"')
|
||||
{
|
||||
json *child = alloc(a, json);
|
||||
child->key = key;
|
||||
child->type = JSON_TYPE_STRING;
|
||||
child->value.s = ParseString(text, &at, current_line, file, a);
|
||||
child->next = j->value.first_child;
|
||||
j->value.first_child = child;
|
||||
}
|
||||
else if ((text.data[at] >= '0' && text.data[at] <= '9') || text.data[at] == '-')
|
||||
{
|
||||
/* Number. */
|
||||
f64 f;
|
||||
i64 i;
|
||||
parse_number_result res = ParseNumber(text, &at, current_line, file, &i, &f);
|
||||
if (res == NOT_A_NUMBER)
|
||||
return 0;
|
||||
json *child = alloc(a, json);
|
||||
child->key = key;
|
||||
if (res == INT)
|
||||
{
|
||||
child->type = JSON_TYPE_INTEGER;
|
||||
child->value.i = i;
|
||||
}
|
||||
else /* DOUBLE */
|
||||
{
|
||||
child->type = JSON_TYPE_FLOAT;
|
||||
child->value.f = f;
|
||||
}
|
||||
child->next = j->value.first_child;
|
||||
j->value.first_child = child;
|
||||
}
|
||||
|
||||
ConsumeWhitespaceNoEof(text, &at, current_line, file, 0);
|
||||
|
||||
@ -424,12 +474,7 @@ ParseJSONImpl(s8 text, isize *_at, json *sibling, isize *current_line, s8 file,
|
||||
json *last_child = NULL;
|
||||
while (text.data[at] != ']' && at < text.length)
|
||||
{
|
||||
/* value [,] ... */
|
||||
ConsumeWhitespaceNoEof(text, &at, current_line, file, 0);
|
||||
if (text.data[at] == '{' || text.data[at] == '[')
|
||||
{
|
||||
/* Object or array, recurse into it. */
|
||||
json *child = ParseJSONImpl(text, &at, NULL, current_line, file, a);
|
||||
json *child = ParseValue(text, &at, current_line, file, a);
|
||||
if (!child)
|
||||
return 0;
|
||||
if (!j->value.first_child)
|
||||
@ -437,43 +482,6 @@ ParseJSONImpl(s8 text, isize *_at, json *sibling, isize *current_line, s8 file,
|
||||
if (last_child)
|
||||
last_child->next = child;
|
||||
last_child = child;
|
||||
}
|
||||
else if (text.data[at] == '\"')
|
||||
{
|
||||
json *child = alloc(a, json);
|
||||
child->value.s = ParseString(text, &at, current_line, file, a);
|
||||
child->type = JSON_TYPE_STRING;
|
||||
if (!j->value.first_child)
|
||||
j->value.first_child = child;
|
||||
if (last_child)
|
||||
last_child->next = child;
|
||||
last_child = child;
|
||||
}
|
||||
else if ((text.data[at] >= '0' && text.data[at] <= '9') || text.data[at] == '-')
|
||||
{
|
||||
/* Number. */
|
||||
f64 f;
|
||||
i64 i;
|
||||
parse_number_result res = ParseNumber(text, &at, current_line, file, &i, &f);
|
||||
if (res == NOT_A_NUMBER)
|
||||
return 0;
|
||||
json *child = alloc(a, json);
|
||||
if (res == INT)
|
||||
{
|
||||
child->value.i = i;
|
||||
child->type = JSON_TYPE_INTEGER;
|
||||
}
|
||||
else /* DOUBLE */
|
||||
{
|
||||
child->value.f = f;
|
||||
child->type = JSON_TYPE_FLOAT;
|
||||
}
|
||||
if (!j->value.first_child)
|
||||
j->value.first_child = child;
|
||||
if (last_child)
|
||||
last_child->next = child;
|
||||
last_child = child;
|
||||
}
|
||||
|
||||
ConsumeWhitespaceNoEof(text, &at, current_line, file, 0);
|
||||
|
||||
@ -514,7 +522,7 @@ RTJ_API json *
|
||||
ParseJSON(s8 text, s8 file, arena *a)
|
||||
{
|
||||
isize line = 1, at = 0;
|
||||
return ParseJSONImpl(text, &at, NULL, &line, file, a);
|
||||
return ParseJSONImpl(text, &at, &line, file, a);
|
||||
}
|
||||
|
||||
RTJ_API json *
|
||||
@ -522,7 +530,7 @@ GetJSONMember(json *o, s8 key)
|
||||
{
|
||||
if (o->type != JSON_TYPE_OBJECT)
|
||||
return NULL;
|
||||
JsonForEach(child, o)
|
||||
JSONForEach(child, o)
|
||||
{
|
||||
if (S8Equals(child->key, key))
|
||||
return child;
|
||||
@ -536,7 +544,7 @@ GetJSONArrayElement(json *a, isize i)
|
||||
if (a->type != JSON_TYPE_ARRAY)
|
||||
return NULL;
|
||||
isize at = 0;
|
||||
JsonForEach(child, a)
|
||||
JSONForEach(child, a)
|
||||
{
|
||||
if (at == i)
|
||||
return child;
|
||||
@ -551,7 +559,7 @@ GetJSONArrayLength(json *a)
|
||||
if (a->type != JSON_TYPE_ARRAY)
|
||||
return -1;
|
||||
isize at = 0;
|
||||
JsonForEach(child, a) { ++at; }
|
||||
JSONForEach(child, a) { ++at; }
|
||||
return at;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user