summaryrefslogtreecommitdiff
path: root/vere/pkg/noun/jets/e/json_de.c
blob: e54393a3ee10168e821e2a84074112a9cd072351 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
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)));
}