Skip to content

Commit 89e4657

Browse files
committed
extmod: Add loads to ujson module.
1 parent c95359e commit 89e4657

2 files changed

Lines changed: 179 additions & 0 deletions

File tree

extmod/modujson.c

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@
3030

3131
#include "mpconfig.h"
3232
#include "misc.h"
33+
#include "nlr.h"
3334
#include "qstr.h"
3435
#include "obj.h"
36+
#include "objlist.h"
37+
#include "parsenum.h"
3538
#include "runtime.h"
3639

3740
#if MICROPY_PY_UJSON
@@ -46,9 +49,184 @@ STATIC mp_obj_t mod_ujson_dumps(mp_obj_t obj) {
4649
}
4750
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_dumps_obj, mod_ujson_dumps);
4851

52+
STATIC mp_obj_t mod_ujson_loads(mp_obj_t obj) {
53+
mp_uint_t len;
54+
const char *s = mp_obj_str_get_data(obj, &len);
55+
const char *top = s + len;
56+
vstr_t vstr;
57+
vstr_init(&vstr, 8);
58+
mp_obj_list_t *stack = NULL;
59+
mp_obj_t stack_top = MP_OBJ_NULL;
60+
mp_obj_type_t *stack_top_type= NULL;
61+
mp_obj_t stack_key = MP_OBJ_NULL;
62+
for (;;) {
63+
cont:
64+
if (s == top) {
65+
break;
66+
}
67+
mp_obj_t next = MP_OBJ_NULL;
68+
bool enter = false;
69+
switch (*s) {
70+
case ',':
71+
case ':':
72+
case ' ':
73+
s += 1;
74+
goto cont;
75+
case 'n':
76+
if (s + 3 < top && s[1] == 'u' && s[2] == 'l' && s[3] == 'l') {
77+
s += 4;
78+
next = mp_const_none;
79+
} else {
80+
goto fail;
81+
}
82+
break;
83+
case 'f':
84+
if (s + 4 < top && s[1] == 'a' && s[2] == 'l' && s[3] == 's' && s[4] == 'e') {
85+
s += 5;
86+
next = mp_const_false;
87+
} else {
88+
goto fail;
89+
}
90+
break;
91+
case 't':
92+
if (s + 3 < top && s[1] == 'r' && s[2] == 'u' && s[3] == 'e') {
93+
s += 4;
94+
next = mp_const_true;
95+
} else {
96+
goto fail;
97+
}
98+
break;
99+
case '"':
100+
vstr_reset(&vstr);
101+
for (s++; s < top && *s != '"'; s++) {
102+
byte c = *s;
103+
if (c == '\\') {
104+
s++;
105+
c = *s;
106+
switch (c) {
107+
case 'b': c = '\b'; break;
108+
case 'f': c = '\f'; break;
109+
case 'n': c = '\n'; break;
110+
case 'r': c = '\r'; break;
111+
case 't': c = '\t'; break;
112+
case 'u': if (s + 4 >= top) { goto fail; } else { assert(0); } //vstr_add_char(&vstr, s[0]
113+
}
114+
}
115+
vstr_add_byte(&vstr, c);
116+
}
117+
if (s == top) {
118+
goto fail;
119+
}
120+
s++;
121+
next = mp_obj_new_str(vstr.buf, vstr.len, false);
122+
break;
123+
case '-':
124+
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': {
125+
bool flt = false;
126+
vstr_reset(&vstr);
127+
for (; s < top; s++) {
128+
if (*s == '.' || *s == 'E' || *s == 'e') {
129+
flt = true;
130+
} else if (*s == '-' || unichar_isdigit(*s)) {
131+
// pass
132+
} else {
133+
break;
134+
}
135+
vstr_add_byte(&vstr, *s);
136+
}
137+
if (flt) {
138+
next = mp_parse_num_decimal(vstr.buf, vstr.len, false, false);
139+
} else {
140+
next = mp_parse_num_integer(vstr.buf, vstr.len, 10);
141+
}
142+
break;
143+
}
144+
case '[':
145+
next = mp_obj_new_list(0, NULL);
146+
enter = true;
147+
s += 1;
148+
break;
149+
case '{':
150+
next = mp_obj_new_dict(0);
151+
enter = true;
152+
s += 1;
153+
break;
154+
case '}':
155+
case ']': {
156+
s += 1;
157+
if (stack_top == MP_OBJ_NULL) {
158+
// no object at all
159+
goto fail;
160+
}
161+
if (stack == NULL || stack->len == 0) {
162+
// finished; compound object
163+
goto success;
164+
}
165+
stack->len -= 1;
166+
stack_top = stack->items[stack->len];
167+
stack_top_type = mp_obj_get_type(stack_top);
168+
goto cont;
169+
}
170+
default:
171+
goto fail;
172+
}
173+
if (stack_top == MP_OBJ_NULL) {
174+
stack_top = next;
175+
stack_top_type = mp_obj_get_type(stack_top);
176+
if (!enter) {
177+
// finished; single primitive only
178+
goto success;
179+
}
180+
} else {
181+
// append to list or dict
182+
if (stack_top_type == &mp_type_list) {
183+
mp_obj_list_append(stack_top, next);
184+
} else {
185+
if (stack_key == MP_OBJ_NULL) {
186+
stack_key = next;
187+
if (enter) {
188+
goto fail;
189+
}
190+
} else {
191+
mp_obj_dict_store(stack_top, stack_key, next);
192+
stack_key = MP_OBJ_NULL;
193+
}
194+
}
195+
if (enter) {
196+
if (stack == NULL) {
197+
stack = mp_obj_new_list(1, &stack_top);
198+
} else {
199+
mp_obj_list_append(stack, stack_top);
200+
}
201+
stack_top = next;
202+
stack_top_type = mp_obj_get_type(stack_top);
203+
}
204+
}
205+
}
206+
success:
207+
// eat trailing whitespace
208+
while (s < top && unichar_isspace(*s)) {
209+
s++;
210+
}
211+
if (s < top) {
212+
// unexpected chars
213+
goto fail;
214+
}
215+
if (stack != NULL && stack->len != 0) {
216+
goto fail;
217+
}
218+
vstr_clear(&vstr);
219+
return stack_top;
220+
221+
fail:
222+
nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "syntax error in JSON"));
223+
}
224+
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_ujson_loads_obj, mod_ujson_loads);
225+
49226
STATIC const mp_map_elem_t mp_module_ujson_globals_table[] = {
50227
{ MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_ujson) },
51228
{ MP_OBJ_NEW_QSTR(MP_QSTR_dumps), (mp_obj_t)&mod_ujson_dumps_obj },
229+
{ MP_OBJ_NEW_QSTR(MP_QSTR_loads), (mp_obj_t)&mod_ujson_loads_obj },
52230
};
53231

54232
STATIC const mp_obj_dict_t mp_module_ujson_globals = {

py/qstrdefs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,4 +469,5 @@ Q(decompress)
469469
#if MICROPY_PY_UJSON
470470
Q(ujson)
471471
Q(dumps)
472+
Q(loads)
472473
#endif

0 commit comments

Comments
 (0)