summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2022-10-11 20:47:27 -0700
committerKaz Kylheku <kaz@kylheku.com>2022-10-11 20:47:27 -0700
commitaa02bc7f31edabb9e09f9756d7ae8fbc41995017 (patch)
tree60bd97475501b9c8484f3804a5ac5d409113d373
parent5df51b54116d4287df161c42ca0edb16fb1f79a5 (diff)
downloadtxr-aa02bc7f31edabb9e09f9756d7ae8fbc41995017.tar.gz
txr-aa02bc7f31edabb9e09f9756d7ae8fbc41995017.tar.bz2
txr-aa02bc7f31edabb9e09f9756d7ae8fbc41995017.zip
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.
-rw-r--r--lib.c127
-rw-r--r--stdlib/doc-syms.tl1
-rw-r--r--stream.c4
-rw-r--r--stream.h8
-rw-r--r--txr.123
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