summaryrefslogtreecommitdiff
path: root/vere/pkg/noun/jets/e/json_de.c
diff options
context:
space:
mode:
authorpolwex <polwex@sortug.com>2025-10-05 21:56:51 +0700
committerpolwex <polwex@sortug.com>2025-10-05 21:56:51 +0700
commitfcedfddf00b3f994e4f4e40332ac7fc192c63244 (patch)
tree51d38e62c7bdfcc5f9a5e9435fe820c93cfc9a3d /vere/pkg/noun/jets/e/json_de.c
claude is gud
Diffstat (limited to 'vere/pkg/noun/jets/e/json_de.c')
-rw-r--r--vere/pkg/noun/jets/e/json_de.c258
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)));
+}