ref: 55ad332621a4761674ef84fc9a78e890b8cdc8dd
dir: /json.c/
#include "jsi.h"
#include "jslex.h"
#include "jsvalue.h"
#include "jsbuiltin.h"
#include "utf.h"
static inline void jsonnext(js_State *J)
{
J->lookahead = jsY_lexjson(J);
}
static inline int jsonaccept(js_State *J, int t)
{
if (J->lookahead == t) {
jsonnext(J);
return 1;
}
return 0;
}
static inline void jsonexpect(js_State *J, int t)
{
if (!jsonaccept(J, t))
js_syntaxerror(J, "JSON: unexpected token: %s (expected %s)",
jsY_tokenstring(J->lookahead), jsY_tokenstring(t));
}
static void jsonvalue(js_State *J)
{
int i;
const char *name;
switch (J->lookahead) {
case TK_STRING:
js_pushliteral(J, J->text);
jsonnext(J);
break;
case TK_NUMBER:
js_pushnumber(J, J->number);
jsonnext(J);
break;
case '{':
js_newobject(J);
jsonnext(J);
if (J->lookahead == '}')
return;
do {
if (J->lookahead != TK_STRING)
js_syntaxerror(J, "JSON: unexpected token: %s (expected string)", jsY_tokenstring(J->lookahead));
name = J->text;
jsonnext(J);
jsonexpect(J, ':');
jsonvalue(J);
js_setproperty(J, -2, name);
} while (jsonaccept(J, ','));
jsonexpect(J, '}');
break;
case '[':
js_newarray(J);
jsonnext(J);
i = 0;
if (J->lookahead == ']')
return;
do {
jsonvalue(J);
js_setindex(J, -2, i++);
} while (jsonaccept(J, ','));
jsonexpect(J, ']');
break;
case TK_TRUE:
js_pushboolean(J, 1);
jsonnext(J);
break;
case TK_FALSE:
js_pushboolean(J, 0);
jsonnext(J);
break;
case TK_NULL:
js_pushnull(J);
jsonnext(J);
break;
default:
js_syntaxerror(J, "JSON: unexpected token: %s", jsY_tokenstring(J->lookahead));
}
}
static int JSON_parse(js_State *J, int argc)
{
const char *source = js_tostring(J, 1);
jsY_initlex(J, "JSON", source);
jsonnext(J);
jsonvalue(J);
// TODO: reviver Walk()
return 1;
}
static void fmtnum(js_Buffer **sb, double n)
{
if (isnan(n)) sb_puts(sb, "NaN");
else if (isinf(n)) sb_puts(sb, n < 0 ? "-Infinity" : "Infinity");
else if (n == 0) sb_puts(sb, "0");
else {
char buf[40];
sprintf(buf, "%.17g", n);
sb_puts(sb, buf);
}
}
static void fmtstr(js_Buffer **sb, const char *s)
{
static const char *HEX = "0123456789ABCDEF";
Rune c;
sb_putc(sb, '"');
while (*s) {
s += chartorune(&c, s);
switch (c) {
case '"': sb_puts(sb, "\\\""); break;
case '\\': sb_puts(sb, "\\\\"); break;
case '\b': sb_puts(sb, "\\b"); break;
case '\f': sb_puts(sb, "\\f"); break;
case '\n': sb_puts(sb, "\\n"); break;
case '\r': sb_puts(sb, "\\r"); break;
case '\t': sb_puts(sb, "\\t"); break;
default:
if (c < ' ') {
sb_puts(sb, "\\u");
sb_putc(sb, HEX[(c>>12)&15]);
sb_putc(sb, HEX[(c>>8)&15]);
sb_putc(sb, HEX[(c>>4)&15]);
sb_putc(sb, HEX[c&15]);
} else {
sb_putc(sb, c); break;
}
}
}
sb_putc(sb, '"');
}
static int fmtvalue(js_State *J, js_Buffer **sb, const char *key);
static void fmtobject(js_State *J, js_Buffer **sb, js_Object *obj)
{
js_Property *ref;
int save;
int n = 0;
sb_putc(sb, '{');
for (ref = obj->head; ref; ref = ref->next) {
if (ref->atts & JS_DONTENUM)
continue;
save = (*sb)->n;
if (n) sb_putc(sb, ',');
fmtstr(sb, ref->name);
sb_putc(sb, ':');
js_pushvalue(J, ref->value);
if (!fmtvalue(J, sb, ref->name))
(*sb)->n = save;
else
++n;
js_pop(J, 1);
}
sb_putc(sb, '}');
}
static void fmtarray(js_State *J, js_Buffer **sb)
{
unsigned int len, k;
char buf[40];
js_getproperty(J, -1, "length");
len = js_touint32(J, -1);
js_pop(J, 1);
sb_putc(sb, '[');
for (k = 0; k < len; ++k) {
if (k) sb_putc(sb, ',');
sprintf(buf, "%u", k);
js_getproperty(J, -1, buf);
if (!fmtvalue(J, sb, buf))
sb_puts(sb, "null");
js_pop(J, 1);
}
sb_putc(sb, ']');
}
static int fmtvalue(js_State *J, js_Buffer **sb, const char *key)
{
if (js_try(J)) {
free(*sb);
js_throw(J);
}
if (js_isobject(J, -1)) {
if (js_hasproperty(J, -1, "toJSON")) {
if (js_iscallable(J, -1)) {
js_copy(J, -2);
js_pushliteral(J, key);
js_call(J, 1);
js_rot2pop1(J);
} else {
js_pop(J, 1);
}
}
}
js_endtry(J);
// TODO: replacer()
if (js_isobject(J, -1) && !js_iscallable(J, -1)) {
js_Object *obj = js_toobject(J, -1);
switch (obj->type) {
case JS_CNUMBER: fmtnum(sb, obj->u.number); break;
case JS_CSTRING: fmtstr(sb, obj->u.s.string); break;
case JS_CBOOLEAN: sb_puts(sb, obj->u.boolean ? "true" : "false"); break;
case JS_CARRAY: fmtarray(J, sb); break;
default: fmtobject(J, sb, obj); break;
}
}
else if (js_isboolean(J, -1))
sb_puts(sb, js_toboolean(J, -1) ? "true" : "false");
else if (js_isnumber(J, -1))
fmtnum(sb, js_tonumber(J, -1));
else if (js_isstring(J, -1))
fmtstr(sb, js_tostring(J, -1));
else if (js_isnull(J, -1))
sb_puts(sb, "null");
else
return 0;
return 1;
}
static int JSON_stringify(js_State *J, int argc)
{
js_Buffer *sb = NULL;
if (argc > 0) {
js_copy(J, 1);
if (fmtvalue(J, &sb, "")) {
sb_putc(&sb, 0);
if (js_try(J)) {
free(sb);
js_throw(J);
}
js_pushstring(J, sb ? sb->s : "");
js_endtry(J);
free(sb);
return 1;
}
}
return 0;
}
void jsB_initjson(js_State *J)
{
js_pushobject(J, jsV_newobject(J, JS_CJSON, J->Object_prototype));
{
jsB_propf(J, "parse", JSON_parse, 2);
jsB_propf(J, "stringify", JSON_stringify, 3);
}
js_defglobal(J, "JSON", JS_DONTENUM);
}