/** parse a json object */
bool _json_parse_object();
- bool _json_parse_members();
- bool _json_parse_pair();
+bool _json_parse_members();
+bool _json_parse_pair();
bool _json_parse_array();
bool _json_parse_value();
- bool _json_parse_true();
- bool _json_parse_false();
- bool _json_parse_null();
+bool _json_parse_true();
+bool _json_parse_false();
+bool _json_parse_null();
bool _json_parse_string(bool add);
bool _json_parse_number();
- bool _json_parse_float();
- bool _json_parse_int();
+bool _json_parse_float();
+bool _json_parse_int();
#define JSON_BEGIN() int __i = STRING_ITERATOR_SAVE(_json)
#define JSON_FAIL(reason) goto fail
#define JSON_END() \
- return true; \
-LABEL(fail) \
- STRING_ITERATOR_LOAD(_json, __i); \
- return false;
+ return true; \
+ LABEL(fail) \
+ STRING_ITERATOR_LOAD(_json, __i); \
+ return false;
// Current namespace
string _json_ns;
// Current keys
int _json_keys;
ERASEABLE
-bool _json_parse_object() {
- JSON_BEGIN();
- if (STRING_ITERATOR_GET(_json) != '{') JSON_FAIL("expected '{'");
- WITH(int, _json_keys, bufstr_add(_json_buffer, "", 0), _json_parse_members());
- if (STRING_ITERATOR_GET(_json) != '}') JSON_FAIL("expected '}'");
- JSON_END();
+bool _json_parse_object()
+{
+ JSON_BEGIN();
+ if (STRING_ITERATOR_GET(_json) != '{') { JSON_FAIL("expected '{'"); }
+ WITH(int, _json_keys, bufstr_add(_json_buffer, "", 0), _json_parse_members());
+ if (STRING_ITERATOR_GET(_json) != '}') { JSON_FAIL("expected '}'"); }
+ JSON_END();
}
- ERASEABLE
- bool _json_parse_members() {
- JSON_BEGIN();
- for (;;) {
- if (!_json_parse_pair()) JSON_FAIL("expected pair");
- if (STRING_ITERATOR_PEEK(_json) == ',') {
- STRING_ITERATOR_NEXT(_json);
- continue;
- }
- break;
- }
- JSON_END();
- }
+ERASEABLE
+bool _json_parse_members()
+{
+ JSON_BEGIN();
+ for ( ; ; ) {
+ if (!_json_parse_pair()) { JSON_FAIL("expected pair"); }
+ if (STRING_ITERATOR_PEEK(_json) == ',') {
+ STRING_ITERATOR_NEXT(_json);
+ continue;
+ }
+ break;
+ }
+ JSON_END();
+}
- ERASEABLE
- bool _json_parse_pair() {
- JSON_BEGIN();
- if (!_json_parse_string(false)) JSON_FAIL("expected string");
- string key = _json_temp;
- bufstr_set(_json_buffer, _json_keys, cons(bufstr_get(_json_buffer, _json_keys), key));
- key = _json_ns ? strcat(_json_ns, ".", key) : key;
- bufstr_add(_json_buffer, key, 0);
- if (STRING_ITERATOR_GET(_json) != ':') JSON_FAIL("expected ':'");
- bool ret = false; WITH(string, _json_ns, key, ret = _json_parse_value());
- if (!ret) JSON_FAIL("expected value");
- JSON_END();
- }
+ERASEABLE
+bool _json_parse_pair()
+{
+ JSON_BEGIN();
+ if (!_json_parse_string(false)) { JSON_FAIL("expected string"); }
+ string key = _json_temp;
+ bufstr_set(_json_buffer, _json_keys, cons(bufstr_get(_json_buffer, _json_keys), key));
+ key = _json_ns ? strcat(_json_ns, ".", key) : key;
+ bufstr_add(_json_buffer, key, 0);
+ if (STRING_ITERATOR_GET(_json) != ':') { JSON_FAIL("expected ':'"); }
+ bool ret = false;
+ WITH(string, _json_ns, key, ret = _json_parse_value());
+ if (!ret) { JSON_FAIL("expected value"); }
+ JSON_END();
+}
ERASEABLE
-bool _json_parse_array() {
- JSON_BEGIN();
- if (STRING_ITERATOR_GET(_json) != '[') JSON_FAIL("expected '['");
- int len = bufstr_add(_json_buffer, "0", 0);
- if (len) bufstr_set(_json_buffer, len - 1, strcat(bufstr_get(_json_buffer, len - 1), ".length"));
- bool required = false;
- for (int n = 0; ; n++) {
- string key = ftos(n);
- key = _json_ns ? strcat(_json_ns, ".", key) : key;
- int it = bufstr_add(_json_buffer, key, 0);
- bool ret = false; WITH(string, _json_ns, key, ret = _json_parse_value());
- if (!ret) {
- bufstr_free(_json_buffer, it);
- if (required) JSON_FAIL("expected value"); else break;
- }
- bufstr_set(_json_buffer, len, ftos(n + 1));
- if (STRING_ITERATOR_PEEK(_json) == ',') {
- STRING_ITERATOR_NEXT(_json);
- required = true;
- continue;
- }
- break;
- }
- if (STRING_ITERATOR_GET(_json) != ']') JSON_FAIL("expected ']'");
- JSON_END();
+bool _json_parse_array()
+{
+ JSON_BEGIN();
+ if (STRING_ITERATOR_GET(_json) != '[') { JSON_FAIL("expected '['"); }
+ int len = bufstr_add(_json_buffer, "0", 0);
+ if (len) { bufstr_set(_json_buffer, len - 1, strcat(bufstr_get(_json_buffer, len - 1), ".length")); }
+ bool required = false;
+ for (int n = 0; ; n++) {
+ string key = ftos(n);
+ key = _json_ns ? strcat(_json_ns, ".", key) : key;
+ int it = bufstr_add(_json_buffer, key, 0);
+ bool ret = false;
+ WITH(string, _json_ns, key, ret = _json_parse_value());
+ if (!ret) {
+ bufstr_free(_json_buffer, it);
+ if (required) { JSON_FAIL("expected value"); } else { break; }
+ }
+ bufstr_set(_json_buffer, len, ftos(n + 1));
+ if (STRING_ITERATOR_PEEK(_json) == ',') {
+ STRING_ITERATOR_NEXT(_json);
+ required = true;
+ continue;
+ }
+ break;
+ }
+ if (STRING_ITERATOR_GET(_json) != ']') { JSON_FAIL("expected ']'"); }
+ JSON_END();
}
ERASEABLE
-bool _json_parse_value() {
- JSON_BEGIN();
- if (!(_json_parse_string(true)
- || _json_parse_number()
- || _json_parse_object()
- || _json_parse_array()
- || _json_parse_true()
- || _json_parse_false()
- || _json_parse_null())) JSON_FAIL("expected value");
- JSON_END();
+bool _json_parse_value()
+{
+ JSON_BEGIN();
+ if (!(_json_parse_string(true)
+ || _json_parse_number()
+ || _json_parse_object()
+ || _json_parse_array()
+ || _json_parse_true()
+ || _json_parse_false()
+ || _json_parse_null())) { JSON_FAIL("expected value"); }
+ JSON_END();
}
- ERASEABLE
- bool _json_parse_true() {
- JSON_BEGIN();
- if (!(STRING_ITERATOR_GET(_json) == 't'
- && STRING_ITERATOR_GET(_json) == 'r'
- && STRING_ITERATOR_GET(_json) == 'u'
- && STRING_ITERATOR_GET(_json) == 'e'))
- JSON_FAIL("expected 'true'");
- bufstr_add(_json_buffer, "1", 0);
- JSON_END();
- }
+ERASEABLE
+bool _json_parse_true()
+{
+ JSON_BEGIN();
+ if (!(STRING_ITERATOR_GET(_json) == 't'
+ && STRING_ITERATOR_GET(_json) == 'r'
+ && STRING_ITERATOR_GET(_json) == 'u'
+ && STRING_ITERATOR_GET(_json) == 'e')) {
+ JSON_FAIL("expected 'true'");
+ }
+ bufstr_add(_json_buffer, "1", 0);
+ JSON_END();
+}
- ERASEABLE
- bool _json_parse_false() {
- JSON_BEGIN();
- if (!(STRING_ITERATOR_GET(_json) == 'f'
- && STRING_ITERATOR_GET(_json) == 'a'
- && STRING_ITERATOR_GET(_json) == 'l'
- && STRING_ITERATOR_GET(_json) == 's'
- && STRING_ITERATOR_GET(_json) == 'e'))
- JSON_FAIL("expected 'false'");
- bufstr_add(_json_buffer, "0", 0);
- JSON_END();
- }
+ERASEABLE
+bool _json_parse_false()
+{
+ JSON_BEGIN();
+ if (!(STRING_ITERATOR_GET(_json) == 'f'
+ && STRING_ITERATOR_GET(_json) == 'a'
+ && STRING_ITERATOR_GET(_json) == 'l'
+ && STRING_ITERATOR_GET(_json) == 's'
+ && STRING_ITERATOR_GET(_json) == 'e')) {
+ JSON_FAIL("expected 'false'");
+ }
+ bufstr_add(_json_buffer, "0", 0);
+ JSON_END();
+}
- ERASEABLE
- bool _json_parse_null() {
- JSON_BEGIN();
- if (!(STRING_ITERATOR_GET(_json) == 'n'
- && STRING_ITERATOR_GET(_json) == 'u'
- && STRING_ITERATOR_GET(_json) == 'l'
- && STRING_ITERATOR_GET(_json) == 'l'))
- JSON_FAIL("expected 'null'");
- bufstr_add(_json_buffer, "", 0);
- JSON_END();
- }
+ERASEABLE
+bool _json_parse_null()
+{
+ JSON_BEGIN();
+ if (!(STRING_ITERATOR_GET(_json) == 'n'
+ && STRING_ITERATOR_GET(_json) == 'u'
+ && STRING_ITERATOR_GET(_json) == 'l'
+ && STRING_ITERATOR_GET(_json) == 'l')) {
+ JSON_FAIL("expected 'null'");
+ }
+ bufstr_add(_json_buffer, "", 0);
+ JSON_END();
+}
ERASEABLE
-bool _json_parse_string(bool add) {
- JSON_BEGIN();
- if (STRING_ITERATOR_GET(_json) != '"') JSON_FAIL("expected opening '\"'");
- string s = "";
- for (int c; (c = STRING_ITERATOR_GET(_json)); ) {
- if (c == '"') {
- STRING_ITERATOR_UNGET(_json);
- break;
- } else if (c == '\\') {
- string esc;
- switch (STRING_ITERATOR_GET(_json)) {
- default:
- JSON_FAIL("expected ( '\"' | '\\' | 'n' | 't' )");
- case '"': esc = "\""; break;
- case '\\': esc = "\\"; break;
- case 'n': esc = "\n"; break;
- case 't': esc = "\t"; break;
- case 'u': esc = "\\u"; break; // TODO
- case '/': esc = "/"; break;
- }
- s = strcat(s, esc);
- } else {
- s = strcat(s, chr2str(c));
- }
- }
- if (STRING_ITERATOR_GET(_json) != '"') JSON_FAIL("expected closing '\"'");
- if (add) bufstr_add(_json_buffer, s, 0);
- _json_temp = s;
- JSON_END();
+bool _json_parse_string(bool add)
+{
+ JSON_BEGIN();
+ if (STRING_ITERATOR_GET(_json) != '"') { JSON_FAIL("expected opening '\"'"); }
+ string s = "";
+ for (int c; (c = STRING_ITERATOR_GET(_json)); ) {
+ if (c == '"') {
+ STRING_ITERATOR_UNGET(_json);
+ break;
+ } else if (c == '\\') {
+ string esc;
+ switch (STRING_ITERATOR_GET(_json)) {
+ default:
+ JSON_FAIL("expected ( '\"' | '\\' | 'n' | 't' )");
+ case '"': esc = "\"";
+ break;
+ case '\\': esc = "\\";
+ break;
+ case 'n': esc = "\n";
+ break;
+ case 't': esc = "\t";
+ break;
+ case 'u': esc = "\\u";
+ break; // TODO
+ case '/': esc = "/";
+ break;
+ }
+ s = strcat(s, esc);
+ } else {
+ s = strcat(s, chr2str(c));
+ }
+ }
+ if (STRING_ITERATOR_GET(_json) != '"') { JSON_FAIL("expected closing '\"'"); }
+ if (add) { bufstr_add(_json_buffer, s, 0); }
+ _json_temp = s;
+ JSON_END();
}
ERASEABLE
-bool _json_parse_number() {
- JSON_BEGIN();
- if (!(_json_parse_float() || _json_parse_int())) JSON_FAIL("expected number");
- JSON_END();
+bool _json_parse_number()
+{
+ JSON_BEGIN();
+ if (!(_json_parse_float() || _json_parse_int())) { JSON_FAIL("expected number"); }
+ JSON_END();
}
- ERASEABLE
- bool _json_parse_float() {
- JSON_BEGIN();
- string s = "";
- bool needdot = true;
- for (int c; (c = STRING_ITERATOR_GET(_json)); ) {
- if (!(c >= '0' && c <= '9')) {
- if (c == '.' && needdot) {
- // fine
- needdot = false;
- } else {
- STRING_ITERATOR_UNGET(_json);
- break;
- }
- }
- s = strcat(s, chr2str(c));
- }
- if (s == "") JSON_FAIL("expected float");
- bufstr_add(_json_buffer, s, 0);
- JSON_END();
- }
+ERASEABLE
+bool _json_parse_float()
+{
+ JSON_BEGIN();
+ string s = "";
+ bool needdot = true;
+ for (int c; (c = STRING_ITERATOR_GET(_json)); ) {
+ if (!(c >= '0' && c <= '9')) {
+ if (c == '.' && needdot) {
+ // fine
+ needdot = false;
+ } else {
+ STRING_ITERATOR_UNGET(_json);
+ break;
+ }
+ }
+ s = strcat(s, chr2str(c));
+ }
+ if (s == "") { JSON_FAIL("expected float"); }
+ bufstr_add(_json_buffer, s, 0);
+ JSON_END();
+}
- ERASEABLE
- bool _json_parse_int() {
- JSON_BEGIN();
- string s = "";
- for (int c; (c = STRING_ITERATOR_GET(_json)); ) {
- if (!(c >= '0' && c <= '9')) {
- STRING_ITERATOR_UNGET(_json);
- break;
- }
- if (s == "" && c == '0') JSON_FAIL("expected [1-9]");
- s = strcat(s, chr2str(c));
- }
- if (s == "") JSON_FAIL("expected int");
- if (ftos(stof(s)) != s) JSON_FAIL("expected int");
- bufstr_add(_json_buffer, s, 0);
- JSON_END();
- }
+ERASEABLE
+bool _json_parse_int()
+{
+ JSON_BEGIN();
+ string s = "";
+ for (int c; (c = STRING_ITERATOR_GET(_json)); ) {
+ if (!(c >= '0' && c <= '9')) {
+ STRING_ITERATOR_UNGET(_json);
+ break;
+ }
+ if (s == "" && c == '0') { JSON_FAIL("expected [1-9]"); }
+ s = strcat(s, chr2str(c));
+ }
+ if (s == "") { JSON_FAIL("expected int"); }
+ if (ftos(stof(s)) != s) { JSON_FAIL("expected int"); }
+ bufstr_add(_json_buffer, s, 0);
+ JSON_END();
+}
ERASEABLE
-int json_parse(string in, bool() func) {
- string trimmed = "";
- LABEL(trim) {
- int o = strstrofs(in, "\"", 0);
- if (o >= 0) {
- string part = substring(in, 0, o + 1); in = substring(in, o + 1, -1);
- part = strreplace(" ", "", part);
- part = strreplace("\n", "", part);
- trimmed = strcat(trimmed, part);
- goto trim_str;
- } else {
- string part = in;
- part = strreplace(" ", "", part);
- part = strreplace("\n", "", part);
- trimmed = strcat(trimmed, part);
- goto done;
- }
- }
- LABEL(trim_str) {
- int o = strstrofs(in, "\"", 0);
- int esc = strstrofs(in, "\\\"", 0);
- if (o < esc || esc < 0) {
- // simple string
- string part = substring(in, 0, o + 1); in = substring(in, o + 1, -1);
- trimmed = strcat(trimmed, part);
- goto trim;
- } else {
- // has escape
- string part = substring(in, 0, esc + 2); in = substring(in, esc + 2, -1);
- trimmed = strcat(trimmed, part);
- goto trim_str;
- }
- }
- LABEL(done);
+int json_parse(string in, bool() func)
+{
+ string trimmed = "";
+ LABEL(trim)
+ {
+ int o = strstrofs(in, "\"", 0);
+ if (o >= 0) {
+ string part = substring(in, 0, o + 1);
+ in = substring(in, o + 1, -1);
+ part = strreplace(" ", "", part);
+ part = strreplace("\n", "", part);
+ trimmed = strcat(trimmed, part);
+ goto trim_str;
+ } else {
+ string part = in;
+ part = strreplace(" ", "", part);
+ part = strreplace("\n", "", part);
+ trimmed = strcat(trimmed, part);
+ goto done;
+ }
+ }
+ LABEL(trim_str)
+ {
+ int o = strstrofs(in, "\"", 0);
+ int esc = strstrofs(in, "\\\"", 0);
+ if (o < esc || esc < 0) {
+ // simple string
+ string part = substring(in, 0, o + 1);
+ in = substring(in, o + 1, -1);
+ trimmed = strcat(trimmed, part);
+ goto trim;
+ } else {
+ // has escape
+ string part = substring(in, 0, esc + 2);
+ in = substring(in, esc + 2, -1);
+ trimmed = strcat(trimmed, part);
+ goto trim_str;
+ }
+ }
+ LABEL(done);
- STRING_ITERATOR_SET(_json, trimmed, 0);
- _json_buffer = buf_create();
- bool ret = func();
- if (!ret) {
- buf_del(_json_buffer);
- _json_buffer = -1;
- }
- return _json_buffer;
+ STRING_ITERATOR_SET(_json, trimmed, 0);
+ _json_buffer = buf_create();
+ bool ret = func();
+ if (!ret) {
+ buf_del(_json_buffer);
+ _json_buffer = -1;
+ }
+ return _json_buffer;
}
ERASEABLE
string json_get(int buf, string key)
{
- for (int i = 1, n = buf_getsize(buf); i < n; i += 2) {
- if (bufstr_get(buf, i) == key) return bufstr_get(buf, i + 1);
- }
- return string_null;
+ for (int i = 1, n = buf_getsize(buf); i < n; i += 2) {
+ if (bufstr_get(buf, i) == key) { return bufstr_get(buf, i + 1); }
+ }
+ return string_null;
}
ERASEABLE
void json_del(int buf)
{
- buf_del(buf);
+ buf_del(buf);
}
ERASEABLE
void json_dump(int buf)
{
- for (int i = 0, n = buf_getsize(buf); i < n; ++i) {
- print(bufstr_get(buf, i), "\n");
- }
+ for (int i = 0, n = buf_getsize(buf); i < n; ++i) {
+ print(bufstr_get(buf, i), "\n");
+ }
}
#undef JSON_BEGIN
TEST(json, Parse)
{
- string s = "{\n\
+ string s = "{\n\
\"m_string\": \"\\\"string\\\"\",\n\
\"m_int\": 123,\n\
\"m_bool\": true,\n\
\"m_null\": null,\n\
\"m_obj\": { },\n\
\"m_arr\": [ ]\n}"; // "
- print(s, "\n");
- int buf = json_parse(s, _json_parse_object);
- EXPECT_NE(-1, buf);
- json_dump(buf);
- SUCCEED();
+ print(s, "\n");
+ int buf = json_parse(s, _json_parse_object);
+ EXPECT_NE(-1, buf);
+ json_dump(buf);
+ SUCCEED();
}