@@ -29,23 +29,29 @@ Notes on pretty printing:
2929define thread variable *indent* :: false-or (<string> ) = #f ;
3030define thread variable *sort-keys?* :: <boolean> = #f ;
3131
32- // Call this to print an object on `stream` in json format. If `indent` is
33- // false `object` is printed with minimal whitespace. If `indent` is an integer
34- // then use pretty printing and output `indent` spaces for each indent level.
35- // If `sort-keys?` is true then output object keys in lexicographical order.
36- define function print
32+ // Print an object in json format.
33+ //
34+ // Parameters:
35+ // object: The object to print.
36+ // stream: Stream on which to do output.
37+ // indent: If false, `object` is printed with minimal whitespace. If an integer,
38+ // then use pretty printing and output `indent` spaces for each indent level.
39+ // sort-keys?: If true, output object keys in lexicographical order.
40+ define function print-json
3741 (object :: <object> , stream :: <stream>,
38- #key indent :: false-or (<integer> ), sort-keys? :: <boolean> );
39- if (indent)
40- dynamic-bind (*indent* = make (<string> , size: indent, fill: ' ' ),
41- *sort-keys?* = sort-keys?,
42- *print-pretty?* = #t ) // bug: shouldn't be required.
43- io/printing-logical-block(stream)
44- print-json(object, stream);
45- end ;
46- end
47- else
48- print-json(object, stream);
42+ #key indent :: false-or (<integer> ),
43+ sort-keys? :: <boolean> )
44+ dynamic-bind (*sort-keys?* = sort-keys?)
45+ if (indent)
46+ dynamic-bind (*indent* = make (<string> , size: indent, fill: ' ' ),
47+ *print-pretty?* = #t ) // bug: shouldn't be required.
48+ io/printing-logical-block(stream)
49+ do-print-json(object, stream);
50+ end ;
51+ end
52+ else
53+ do-print-json(object, stream);
54+ end ;
4955 end ;
5056end function ;
5157
@@ -56,30 +62,72 @@ end function;
5662//
5763// If `indent:` was passed to `print` then `stream` will be a pretty printing
5864// stream and the io:pprint module may be used to implement pretty printing.
59- define open generic print-json (object :: <object> , stream :: <stream>);
65+ define open generic do- print-json (object :: <object> , stream :: <stream>);
6066
61- define method print-json (object == $null, stream :: <stream>)
67+ define method do- print-json (object == $null, stream :: <stream>)
6268 write(stream, "null" );
6369end method ;
6470
65- define method print-json (object :: <integer> , stream :: <stream>)
71+ define method do- print-json (object :: <integer> , stream :: <stream>)
6672 write(stream, integer-to-string(object));
6773end method ;
6874
69- define method print-json (object :: <float> , stream :: <stream>)
75+ define method do- print-json (object :: <float> , stream :: <stream>)
7076 write(stream, float-to-string(object));
7177end method ;
7278
73- define method print-json (object :: <boolean> , stream :: <stream>)
79+ define method do- print-json (object :: <boolean> , stream :: <stream>)
7480 write(stream, if (object) "true" else "false" end );
7581end method ;
7682
77- define method print-json (object :: <string> , stream :: <stream>)
78- // TODO: check whether Dylan escaped string printing is compatible with json.
79- format(stream, "%=" , object);
83+ define method do-print-json (object :: <string> , stream :: <stream>)
84+ write-element(stream, '"' );
85+ let zero :: <integer> = as (<integer> , '0' );
86+ let a :: <integer> = as (<integer> , 'a' ) - 10 ;
87+ local
88+ method write-hex-digit (code :: <integer> )
89+ write-element(stream, as (<character> ,
90+ if (code < 10 ) zero + code else a + code end ));
91+ end ,
92+ method write-unicode-escape (code :: <integer> )
93+ write(stream, "\\ u" );
94+ write-hex-digit(ash (logand (code, #xf000), -12 ));
95+ write-hex-digit(ash (logand (code, #x0f00), -8 ));
96+ write-hex-digit(ash (logand (code, #x00f0), -4 ));
97+ write-hex-digit(logand (code, #x000f));
98+ end ;
99+ for (char in object)
100+ let code = as (<integer> , char);
101+ case
102+ code <= #x1f =>
103+ let escape-char = select (char)
104+ '\b' => 'b' ;
105+ '\f' => 'f' ;
106+ '\n' => 'n' ;
107+ '\r' => 'r' ;
108+ '\t' => 't' ;
109+ otherwise => #f ;
110+ end ;
111+ if (escape-char)
112+ write-element(stream, '\\' );
113+ write-element(stream, escape-char);
114+ else
115+ write-unicode-escape(code);
116+ end ;
117+ char == '"' =>
118+ write(stream, "\\\" " );
119+ char == '\\' =>
120+ write(stream, "\\\\ " );
121+ code < 127 => // omits DEL
122+ write-element(stream, char);
123+ otherwise =>
124+ write-unicode-escape(code);
125+ end case ;
126+ end for ;
127+ write-element(stream, '"' );
80128end method ;
81129
82- define method print-json (object :: <collection> , stream :: <stream>)
130+ define method do- print-json (object :: <collection> , stream :: <stream>)
83131 io/printing-logical-block (stream, prefix: "[" , suffix: "]" )
84132 for (o in object,
85133 i from 0 )
@@ -93,7 +141,7 @@ define method print-json (object :: <collection>, stream :: <stream>)
93141 io/pprint-newline(# "fill" , stream);
94142 end ;
95143 end if ;
96- print-json(o, stream);
144+ do- print-json(o, stream);
97145 end for ;
98146 end ;
99147end method ;
@@ -102,17 +150,17 @@ end method;
102150// one element per line. Not sure if the pretty printer can be coaxed into
103151// doing that. Might be easier to do it (even just the current functionality)
104152// by hand.
105- define method print-json (object :: <table> , stream :: <stream>)
153+ define method do- print-json (object :: <table> , stream :: <stream>)
106154 local
107155 method print-key-value-pairs-body (stream, i, key, value)
108156 if (i > 0 )
109157 write(stream, "," );
110158 *indent* & io/pprint-newline(# "mandatory" , stream);
111159 end if ;
112- print-json(key, stream);
160+ do- print-json(key, stream);
113161 write(stream, ":" );
114162 *indent* & write(stream, " " );
115- print-json(value, stream);
163+ do- print-json(value, stream);
116164 end method ,
117165 method print-key-value-pairs (stream :: <stream>)
118166 if (*sort-keys?*)
0 commit comments