diff options
Diffstat (limited to 'vere/pkg/noun/jets/e/json_de.c')
-rw-r--r-- | vere/pkg/noun/jets/e/json_de.c | 258 |
1 files changed, 258 insertions, 0 deletions
diff --git a/vere/pkg/noun/jets/e/json_de.c b/vere/pkg/noun/jets/e/json_de.c new file mode 100644 index 0000000..e54393a --- /dev/null +++ b/vere/pkg/noun/jets/e/json_de.c @@ -0,0 +1,258 @@ +/// @file + +#include "jets/k.h" +#include "jets/q.h" +#include "jets/w.h" + +#include "noun.h" + +#include "pdjson.h" + +/* +** custom code for interfacing with external parser: +** https://github.com/skeeto/pdjson +*/ + +typedef struct _u3qedj_coll { + u3_noun col; // collection (list for array, map for object) + union { // store context for recursive arrays/objects: + u3_noun* tel; // - pointer to tail for array + u3_atom key; // - key for object + }; +} u3qedj_coll; + +static u3qedj_coll* +_push_stack(const u3a_pile *pil_u) +{ + u3qedj_coll *res_u = u3a_push(pil_u); + u3a_pile_done(pil_u); + + res_u->col = u3_nul; + res_u->key = u3_none; + + return res_u; +} + +static u3qedj_coll* +_pop_stack(const u3a_pile *pil_u) +{ + return u3a_pop(pil_u); +} + +static void +_close_stack(const u3a_pile *pil_u) +{ + while ( c3n == u3a_pile_done(pil_u) ) { + u3qedj_coll *tak_u = u3a_peek(pil_u); + + u3z(tak_u->col); + if ( u3_none != tak_u->key ) { + u3z(tak_u->key); + } + + u3a_drop(pil_u); + } +} + +static void +_close_on_error(json_stream *sam_u, const u3a_pile *pil_u) +{ + _close_stack(pil_u); + json_close(sam_u); +} + +static u3_atom +_json_get_string_as_atom(json_stream *sam_u) { + // length returned by json_get_string includes the trailing null byte + // it's possible for json_get_string to return a length of 0, but only if: + // - it's called directly after init + // - it's called directly after init_string + size_t len_i; + const c3_c *str_c = json_get_string(sam_u, &len_i); + return (len_i <= 1) ? + u3_nul : + u3i_bytes(len_i - 1, (const c3_y *)str_c); +} + +static u3_noun +_parse(u3_atom txt) +{ + // + // vars + // + + u3qedj_coll *tak_u; + + json_allocator loc_u = {u3a_malloc, u3a_realloc, u3a_free}; + json_stream sem_u; + json_stream* sam_u = &sem_u; + + u3a_pile pel_u; + u3a_pile *pil_u = &pel_u; + + u3_noun res = u3_none; + u3_noun val; + + const c3_y *byt_y; + c3_z cnt_z; + c3_w len_w = u3r_met(3, txt); + + // + // initialization + // + + // XX assumes little-endian + // + if ( c3y == u3a_is_cat(txt) ) { + byt_y = (c3_y*)&txt; + } + else { + u3a_atom* vat_u = u3a_to_ptr(txt); + byt_y = (c3_y*)vat_u->buf_w; + } + json_open_buffer(sam_u, byt_y, len_w); + json_set_allocator(sam_u, &loc_u); + u3a_pile_prep(pil_u, sizeof(u3qedj_coll)); + + // + // core logic + // + + while ( json_peek(sam_u) != JSON_DONE ) { + switch ( json_next(sam_u) ) { + // unreachable barring programming error + default: u3_assert(0); + + case JSON_ARRAY: + case JSON_OBJECT: { + tak_u = _push_stack(pil_u); + } continue; + + case JSON_ARRAY_END: { + val = u3nc(c3__a, tak_u->col); + tak_u = _pop_stack(pil_u); + } break; + + case JSON_OBJECT_END: { + val = u3nc(c3__o, tak_u->col); + tak_u = _pop_stack(pil_u); + } break; + + case JSON_STRING: { + if ( (json_get_context(sam_u, &cnt_z) == JSON_OBJECT) && (cnt_z & 1) ) { + // since object key must be followed by value, skip ahead + tak_u->key = _json_get_string_as_atom(sam_u); + continue; + } + else { + val = u3nc(c3__s, _json_get_string_as_atom(sam_u)); + break; + } + } + + case JSON_NUMBER: { + // read number from string in the JSON reparser + val = u3nc(c3__n, _json_get_string_as_atom(sam_u)); + } break; + + case JSON_TRUE: { + val = u3nc(c3__b, c3y); + } break; + + case JSON_FALSE: { + val = u3nc(c3__b, c3n); + } break; + + case JSON_NULL: { + val = u3_nul; + } break; + + case JSON_ERROR: { + _close_on_error(sam_u, pil_u); + return u3_nul; + } break; + } + + switch ( json_get_context(sam_u, &cnt_z) ) { + // unreachable barring programming error + default: u3_assert(0); + + case JSON_DONE: { + res = val; + } break; + + case JSON_ARRAY: { + u3_noun* nex; + u3_noun* hed; + + if ( tak_u->col == u3_nul ) { + nex = &(tak_u->col); + } + else { + nex = tak_u->tel; + } + + *nex = u3i_defcons(&hed, &(tak_u->tel)); + *hed = val; + *(tak_u->tel) = u3_nul; + } break; + + case JSON_OBJECT: { + // odd cnt_z and unset key weeded out by continue command on key + u3_assert(!(cnt_z & 1)); + u3_assert(tak_u->key != u3_none); + // cnt_z == 0 weeded out by continue command on array/object open + u3_assert(cnt_z); + + tak_u->col = u3kdb_put(tak_u->col, tak_u->key, val); + tak_u->key = u3_none; + } break; + } + } + + // + // clean up + // + + u3_assert(c3y == u3a_pile_done(pil_u)); + + // skip over whitespce + while ( json_isspace(json_source_peek(sam_u)) ) { + json_source_get(sam_u); + } + + json_close(sam_u); + + // return null if trailing trash/multiple JSON objects + if ( json_get_position(sam_u) != len_w ) { + u3z(res); + return u3_nul; + } + else { + return u3nc(u3_nul, res); + } +} + +/* +** jet interface functions +*/ + +u3_noun +u3qe_json_de(u3_atom a) +{ + return _parse(a); +} + +u3_noun +u3ke_json_de(u3_atom a) +{ + u3_noun res = u3qe_json_de(a); + u3z(a); + return res; +} + +u3_noun +u3we_json_de(u3_noun cor) +{ + return u3qe_json_de(u3x_atom(u3x_at(u3x_sam, cor))); +} |