From 5a0f1a9eee5f5e88c52a2c0b64ad62c3b02b0968 Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Tue, 11 Oct 2022 20:47:27 -0700 Subject: json: support standard-style formatting. * stream.c (standard_k, print_json_format_s): New symbol variables. (stream_init): New variables initialized. * stream.h (enum json_fmt): New enum. (standard_k, print_json_format_s): Declared. * lib.c (out_json_rec): Take enum json_fmt param, and pass it recursively. Printing for vector and dictionaries reacts to argument value. (out_json, put_json): Examine value of special var *print-json-format* and calculate enum json_fmt value from this. Pass to out_json_rec. * txr.1: Documented. * stdlib/doc-syms.tl: Updated. --- lib.c | 127 ++++++++++++++++++++++++++++++++++++++--------------- stdlib/doc-syms.tl | 1 + stream.c | 4 ++ stream.h | 8 ++++ txr.1 | 23 ++++++++++ 5 files changed, 127 insertions(+), 36 deletions(-) diff --git a/lib.c b/lib.c index 5feb7901..9e8f9d55 100644 --- a/lib.c +++ b/lib.c @@ -13799,7 +13799,8 @@ static int check_emit_circle(val obj, val out, struct strm_ctx *ctx, val self) return 0; } -static void out_json_rec(val obj, val out, struct strm_ctx *ctx) +static void out_json_rec(val obj, val out, enum json_fmt jf, + struct strm_ctx *ctx) { val self = lit("print"); @@ -13827,55 +13828,78 @@ static void out_json_rec(val obj, val out, struct strm_ctx *ctx) val save_indent; int force_br = 0; val iter, next; - put_char(chr('{'), out); - save_indent = inc_indent(out, zero); + + if (jf == json_fmt_standard) { + put_string(lit("{\n"), out); + save_indent = inc_indent_abs(out, two); + } else { + put_char(chr('{'), out); + save_indent = inc_indent(out, zero); + } + for (iter = cddr(obj), next = nil; iter; iter = next) { val pair = car(iter); val k = car(pair), v = cadr(pair); - if (consp(k) || consp(v)) { - if (next) + if (jf == json_fmt_standard || consp(k) || consp(v)) { + if (jf != json_fmt_standard && next) put_char(chr(' '), out); - out_json_rec(k, out, ctx); + out_json_rec(k, out, jf, ctx); put_string(lit(" : "), out); } else { - out_json_rec(k, out, ctx); + out_json_rec(k, out, jf, ctx); put_char(chr(':'), out); } - out_json_rec(v, out, ctx); - if ((next = cdr(iter)) != 0) { + out_json_rec(v, out, jf, ctx); + + if (jf == json_fmt_standard) { + if ((next = cdr(iter)) != 0) + put_string(lit(",\n"), out); + else + put_char(chr('\n'), out); + } else if ((next = cdr(iter)) != 0) { put_char(chr(','), out); if (width_check(out, nil)) force_br = 1; } } + set_indent(out, save_indent); put_char(chr('}'), out); if (force_br) force_break(out); - if (save_indent) - set_indent(out, save_indent); return; } if (sym == vector_lit_s) { val save_indent; int force_br = 0; val iter, next; - put_char(chr('['), out); - save_indent = inc_indent(out, zero); + + if (jf == json_fmt_standard) { + put_string(lit("[\n"), out); + save_indent = inc_indent_abs(out, two); + } else { + put_char(chr('['), out); + save_indent = inc_indent(out, zero); + } + for (iter = cadr(obj), next = nil; iter; iter = next) { val elem = car(iter); next = cdr(iter); - out_json_rec(elem, out, ctx); - if (next) { + out_json_rec(elem, out, jf, ctx); + if (jf == json_fmt_standard) { + if (next) + put_string(lit(",\n"), out); + else + put_char(chr('\n'), out); + } else if (next) { put_char(chr(','), out); if (width_check(out, nil)) force_br = 1; } } + set_indent(out, save_indent); put_char(chr(']'), out); if (force_br) force_break(out); - if (save_indent) - set_indent(out, save_indent); return; } if (sym == sys_unquote_s) { @@ -13897,22 +13921,32 @@ static void out_json_rec(val obj, val out, struct strm_ctx *ctx) cnum len = c_num(length(obj), self); cnum i; - put_char(chr('['), out); - save_indent = inc_indent(out, zero); + if (jf == json_fmt_standard) { + put_string(lit("[\n"), out); + save_indent = inc_indent_abs(out, two); + } else { + put_char(chr('['), out); + save_indent = inc_indent(out, zero); + } + for (i = 0; i < len; i++) { val elem = obj->v.vec[i]; - out_json_rec(elem, out, ctx); - if (i < len - 1) { + out_json_rec(elem, out, jf, ctx); + if (jf == json_fmt_standard) { + if (i < len - 1) + put_string(lit(",\n"), out); + else + put_char(chr('\n'), out); + } else if (i < len - 1) { put_char(chr(','), out); if (width_check(out, nil)) force_br = 1; } } + set_indent(out, save_indent); put_char(chr(']'), out); if (force_br) force_break(out); - if (save_indent) - set_indent(out, save_indent); return; } break; @@ -13925,31 +13959,44 @@ static void out_json_rec(val obj, val out, struct strm_ctx *ctx) us_hash_iter_init(&hi, obj); - put_char(chr('{'), out); - save_indent = inc_indent(out, zero); + if (jf == json_fmt_standard) { + put_string(lit("{\n"), out); + save_indent = inc_indent_abs(out, two); + } else { + put_char(chr('{'), out); + save_indent = inc_indent(out, zero); + } + for (next = nil, cell = hash_iter_next(&hi); cell; cell = next) { val k = car(cell), v = cdr(cell); - if (consp(k) || consp(v)) { - if (next) + + if (jf == json_fmt_standard || consp(k) || consp(v)) { + if (jf != json_fmt_standard && next) put_char(chr(' '), out); - out_json_rec(k, out, ctx); + out_json_rec(k, out, jf, ctx); put_string(lit(" : "), out); } else { - out_json_rec(k, out, ctx); + out_json_rec(k, out, jf, ctx); put_char(chr(':'), out); } - out_json_rec(v, out, ctx); - if ((next = hash_iter_next(&hi)) != 0) { + out_json_rec(v, out, jf, ctx); + if (jf == json_fmt_standard) { + if ((next = hash_iter_next(&hi)) != 0) + put_string(lit(",\n"), out); + else + put_char(chr('\n'), out); + } else if ((next = hash_iter_next(&hi)) != 0) { put_char(chr(','), out); - if (width_check(out, nil)) + if (jf == json_fmt_standard) + put_char(chr('\n'), out); + else if (width_check(out, nil)) force_br = 1; } } + set_indent(out, save_indent); put_char(chr('}'), out); if (force_br) force_break(out); - if (save_indent) - set_indent(out, save_indent); return; } break; @@ -13973,9 +14020,13 @@ static void out_json(val op, val obj, val out, struct strm_ctx *ctx) { val save_mode = test_set_indent_mode(out, num_fast(indent_off), num_fast(indent_data)); + val jfsym = cdr(lookup_var(nil, print_json_format_s)); + enum json_fmt jf = if3(jfsym == standard_k, + json_fmt_standard, + json_fmt_default); if (op == sys_qquote_s) put_char(chr('^'), out); - out_json_rec(obj, out, ctx); + out_json_rec(obj, out, jf, ctx); set_indent_mode(out, save_mode); } @@ -14659,8 +14710,12 @@ val put_json(val obj, val stream_in, val flat) test_set_indent_mode(stream, num_fast(indent_off), num_fast(indent_data))); val isave = get_indent(stream); + val jfsym = cdr(lookup_var(nil, print_json_format_s)); + enum json_fmt jf = if3(jfsym == standard_k, + json_fmt_standard, + json_fmt_default); uw_simple_catch_begin; - out_json_rec(obj, stream, 0); + out_json_rec(obj, stream, jf, 0); uw_unwind { set_indent_mode(stream, imode); set_indent(stream, isave); diff --git a/stdlib/doc-syms.tl b/stdlib/doc-syms.tl index e214011c..5cc46e6b 100644 --- a/stdlib/doc-syms.tl +++ b/stdlib/doc-syms.tl @@ -36,6 +36,7 @@ ("*print-flo-digits*" "N-00F41F6C") ("*print-flo-format*" "N-02B252AA") ("*print-flo-precision*" "N-02E97D03") + ("*print-json-format*" "N-02338B4D") ("*random-state*" "N-033875AD") ("*random-warmup*" "N-010348CD") ("*read-bad-json*" "N-01C77B65") diff --git a/stream.c b/stream.c index a3a70545..3f835367 100644 --- a/stream.c +++ b/stream.c @@ -95,9 +95,11 @@ val get_error_s, get_error_str_s, clear_error_s, get_fd_s; val print_flo_precision_s, print_flo_digits_s, print_flo_format_s; val pprint_flo_format_s, print_base_s, print_circle_s; +val print_json_format_s; val from_start_k, from_current_k, from_end_k; val real_time_k, name_k, addr_k, fd_k, byte_oriented_k; +val standard_k; val format_s; val stdio_stream_s; @@ -5414,6 +5416,7 @@ void stream_init(void) addr_k = intern(lit("addr"), keyword_package); fd_k = intern(lit("fd"), keyword_package); byte_oriented_k = intern(lit("byte-oriented"), keyword_package); + standard_k = intern(lit("standard"), keyword_package); format_s = intern(lit("format"), user_package); stdio_stream_s = intern(lit("stdio-stream"), user_package); #if HAVE_SOCKETS @@ -5469,6 +5472,7 @@ void stream_init(void) reg_var(print_base_s = intern(lit("*print-base*"), user_package), num_fast(10)); reg_var(print_circle_s = intern(lit("*print-circle*"), user_package), nil); + reg_var(print_json_format_s = intern(lit("*print-json-format*"), user_package), nil); #if HAVE_ISATTY if (isatty(fileno(stdin)) == 1) { diff --git a/stream.h b/stream.h index 428fa47c..257a8bd7 100644 --- a/stream.h +++ b/stream.h @@ -131,10 +131,17 @@ struct stdio_mode { #define std_debug (deref(lookup_var_l(nil, stddebug_s))) #define std_error (deref(lookup_var_l(nil, stderr_s))) #define std_null (deref(lookup_var_l(nil, stdnull_s))) + +enum json_fmt { + json_fmt_default, + json_fmt_standard +}; + loc lookup_var_l(val env, val sym); extern val from_start_k, from_current_k, from_end_k; extern val real_time_k, name_k, addr_k, fd_k, byte_oriented_k; +extern val standard_k; extern val format_s; extern val stdio_stream_s; @@ -148,6 +155,7 @@ extern val get_error_s, get_error_str_s, clear_error_s, get_fd_s; extern val print_flo_precision_s, print_flo_digits_s, print_flo_format_s; extern val pprint_flo_format_s, print_base_s, print_circle_s; +extern val print_json_format_s; #if HAVE_SOCKETS extern val socket_error_s; diff --git a/txr.1 b/txr.1 index be539219..846c35d4 100644 --- a/txr.1 +++ b/txr.1 @@ -79981,6 +79981,29 @@ argument is passed to that function, defaulting to The value returned is that of .codn put-jsons . +.coNP Variable @ *print-json-format* +.desc +The +.code *print-json-format* +variable controls the formatting style exhibited by +.code put-json +and related functions. The initial value of this variable is +.codn nil . + +If the value is the keyword symbol +.codn :standard , +then a widely-used format is used, in which the opening and closing +braces and brackets of vectors and dictionaries are printed +on separate lines, as are the elements of those objects. + +If the variable +has any other value, including the initial value +.codn nil , +then a default format is used in which braces, brackets +and elements appear on the same line, subject to automatic +breaking and indentation, similar to the way Lisp nested +list structure is printed. + .coNP Variable @ *read-bad-json* .desc This dynamic variable, initialized to a value of -- cgit v1.2.3