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_STRING,
|
||||||
JSON_TYPE_INTEGER,
|
JSON_TYPE_INTEGER,
|
||||||
JSON_TYPE_FLOAT,
|
JSON_TYPE_FLOAT,
|
||||||
|
JSON_TYPE_BOOL,
|
||||||
|
JSON_TYPE_NULL,
|
||||||
} json_type;
|
} json_type;
|
||||||
|
|
||||||
/* Represents a json value */
|
/* Represents a json value */
|
||||||
@ -54,6 +56,7 @@ typedef struct json
|
|||||||
s8 s;
|
s8 s;
|
||||||
i64 i;
|
i64 i;
|
||||||
f64 f;
|
f64 f;
|
||||||
|
b32 b;
|
||||||
|
|
||||||
/* The first subobject.
|
/* The first subobject.
|
||||||
* Element 0 of an array,
|
* Element 0 of an array,
|
||||||
@ -83,7 +86,7 @@ RTJ_API json *GetJSONArrayElement(json *a, isize i);
|
|||||||
RTJ_API isize GetJSONArrayLength(json *a);
|
RTJ_API isize GetJSONArrayLength(json *a);
|
||||||
|
|
||||||
/* Iterates over every member of an object or array */
|
/* 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)
|
for (json *_ChildVarName = (_ParentPtr)->value.first_child; _ChildVarName != 0; _ChildVarName = _ChildVarName->next)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -319,13 +322,97 @@ ConsumeWhitespace(s8 text, isize *_at, isize *current_line)
|
|||||||
} \
|
} \
|
||||||
(_At) += 1;
|
(_At) += 1;
|
||||||
|
|
||||||
|
static json *ParseJSONImpl(s8 text, isize *_at, isize *current_line, s8 file, arena *a);
|
||||||
|
|
||||||
static json *
|
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;
|
isize at = *_at;
|
||||||
|
|
||||||
json *j = alloc(a, json);
|
json *j = alloc(a, json);
|
||||||
j->next = sibling;
|
|
||||||
|
|
||||||
if (text.data[at] == '{')
|
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);
|
ExpectCharacter(':', text, at, current_line, file, 0);
|
||||||
ConsumeWhitespaceNoEof(text, &at, current_line, file, 0);
|
ConsumeWhitespaceNoEof(text, &at, current_line, file, 0);
|
||||||
|
|
||||||
/* Value */
|
json *child = ParseValue(text, &at, current_line, file, a);
|
||||||
if (text.data[at] == '{' || text.data[at] == '[')
|
if (!child)
|
||||||
{
|
|
||||||
/* 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)
|
|
||||||
return 0;
|
return 0;
|
||||||
j->value.first_child->key = key;
|
|
||||||
}
|
|
||||||
else if (text.data[at] == '\"')
|
|
||||||
{
|
|
||||||
json *child = alloc(a, json);
|
|
||||||
child->key = key;
|
child->key = key;
|
||||||
child->type = JSON_TYPE_STRING;
|
|
||||||
child->value.s = ParseString(text, &at, current_line, file, a);
|
|
||||||
child->next = j->value.first_child;
|
child->next = j->value.first_child;
|
||||||
j->value.first_child = 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);
|
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;
|
json *last_child = NULL;
|
||||||
while (text.data[at] != ']' && at < text.length)
|
while (text.data[at] != ']' && at < text.length)
|
||||||
{
|
{
|
||||||
/* value [,] ... */
|
json *child = ParseValue(text, &at, current_line, file, a);
|
||||||
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);
|
|
||||||
if (!child)
|
if (!child)
|
||||||
return 0;
|
return 0;
|
||||||
if (!j->value.first_child)
|
if (!j->value.first_child)
|
||||||
@ -437,43 +482,6 @@ ParseJSONImpl(s8 text, isize *_at, json *sibling, isize *current_line, s8 file,
|
|||||||
if (last_child)
|
if (last_child)
|
||||||
last_child->next = child;
|
last_child->next = child;
|
||||||
last_child = 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);
|
ConsumeWhitespaceNoEof(text, &at, current_line, file, 0);
|
||||||
|
|
||||||
@ -514,7 +522,7 @@ RTJ_API json *
|
|||||||
ParseJSON(s8 text, s8 file, arena *a)
|
ParseJSON(s8 text, s8 file, arena *a)
|
||||||
{
|
{
|
||||||
isize line = 1, at = 0;
|
isize line = 1, at = 0;
|
||||||
return ParseJSONImpl(text, &at, NULL, &line, file, a);
|
return ParseJSONImpl(text, &at, &line, file, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
RTJ_API json *
|
RTJ_API json *
|
||||||
@ -522,7 +530,7 @@ GetJSONMember(json *o, s8 key)
|
|||||||
{
|
{
|
||||||
if (o->type != JSON_TYPE_OBJECT)
|
if (o->type != JSON_TYPE_OBJECT)
|
||||||
return NULL;
|
return NULL;
|
||||||
JsonForEach(child, o)
|
JSONForEach(child, o)
|
||||||
{
|
{
|
||||||
if (S8Equals(child->key, key))
|
if (S8Equals(child->key, key))
|
||||||
return child;
|
return child;
|
||||||
@ -536,7 +544,7 @@ GetJSONArrayElement(json *a, isize i)
|
|||||||
if (a->type != JSON_TYPE_ARRAY)
|
if (a->type != JSON_TYPE_ARRAY)
|
||||||
return NULL;
|
return NULL;
|
||||||
isize at = 0;
|
isize at = 0;
|
||||||
JsonForEach(child, a)
|
JSONForEach(child, a)
|
||||||
{
|
{
|
||||||
if (at == i)
|
if (at == i)
|
||||||
return child;
|
return child;
|
||||||
@ -551,7 +559,7 @@ GetJSONArrayLength(json *a)
|
|||||||
if (a->type != JSON_TYPE_ARRAY)
|
if (a->type != JSON_TYPE_ARRAY)
|
||||||
return -1;
|
return -1;
|
||||||
isize at = 0;
|
isize at = 0;
|
||||||
JsonForEach(child, a) { ++at; }
|
JSONForEach(child, a) { ++at; }
|
||||||
return at;
|
return at;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user