blob: 945845e6c5582ed3bb4e3b31be30be449b0994db [file] [log] [blame]
Elly Jones656a2af2011-11-07 23:24:441// Rust JSON serialization library
2// Copyright (c) 2011 Google Inc.
Elly Jonesbd726262011-11-07 19:01:283
4import float;
5import map;
Graydon Hoarefa9ad982011-12-14 00:25:516import core::option;
Elly Jonesbd726262011-11-07 19:01:287import option::{some, none};
8import str;
9import vec;
10
11export json;
Elly Jones656a2af2011-11-07 23:24:4412export to_str;
13export from_str;
Elly Jonesbd726262011-11-07 19:01:2814
Elly Jones656a2af2011-11-07 23:24:4415export num;
16export string;
17export boolean;
18export list;
19export dict;
20
21/*
22Tag: json
23
24Represents a json value.
25*/
Elly Jonesbd726262011-11-07 19:01:2826tag json {
Elly Jones656a2af2011-11-07 23:24:4427 /* Variant: num */
Elly Jonesbd726262011-11-07 19:01:2828 num(float);
Elly Jones656a2af2011-11-07 23:24:4429 /* Variant: string */
Elly Jonesbd726262011-11-07 19:01:2830 string(str);
Elly Jones656a2af2011-11-07 23:24:4431 /* Variant: boolean */
Elly Jonesbd726262011-11-07 19:01:2832 boolean(bool);
Elly Jones656a2af2011-11-07 23:24:4433 /* Variant: list */
Elly Jonesbd726262011-11-07 19:01:2834 list(@[json]);
Elly Jones656a2af2011-11-07 23:24:4435 /* Variant: dict */
Elly Jonesbd726262011-11-07 19:01:2836 dict(map::hashmap<str,json>);
37}
38
Elly Jones656a2af2011-11-07 23:24:4439/*
40Function: to_str
41
42Serializes a json value into a string.
43*/
44fn to_str(j: json) -> str {
Elly Jonesbd726262011-11-07 19:01:2845 alt j {
46 num(f) { float::to_str(f, 6u) }
47 string(s) { #fmt["\"%s\"", s] } // XXX: escape
48 boolean(true) { "true" }
49 boolean(false) { "false" }
50 list(@js) {
51 str::concat(["[",
52 str::connect(
Elly Jones656a2af2011-11-07 23:24:4453 vec::map::<json,str>({ |e| to_str(e) }, js),
Elly Jonesbd726262011-11-07 19:01:2854 ", "),
55 "]"])
56 }
57 dict(m) {
58 let parts = [];
59 m.items({ |k, v|
60 vec::grow(parts, 1u,
Elly Jones656a2af2011-11-07 23:24:4461 str::concat(["\"", k, "\": ", to_str(v)])
Elly Jonesbd726262011-11-07 19:01:2862 )
63 });
64 str::concat(["{ ", str::connect(parts, ", "), " }"])
65 }
66 }
67}
68
69fn rest(s: str) -> str {
Elly Jones656a2af2011-11-07 23:24:4470 assert(str::char_len(s) >= 1u);
Elly Jonesbd726262011-11-07 19:01:2871 str::char_slice(s, 1u, str::char_len(s))
72}
73
Elly Jones656a2af2011-11-07 23:24:4474fn from_str_str(s: str) -> (option::t<json>, str) {
Elly Jonesbd726262011-11-07 19:01:2875 let pos = 0u;
76 let len = str::byte_len(s);
77 let escape = false;
78 let res = "";
79
80 alt str::char_at(s, 0u) {
81 '"' { pos = 1u; }
82 _ { ret (none, s); }
83 }
84
85 while (pos < len) {
86 let chr = str::char_range_at(s, pos);
87 let c = chr.ch;
88 pos = chr.next;
89 if (escape) {
90 res = res + str::from_char(c);
91 escape = false;
92 cont;
93 }
94 if (c == '\\') {
95 escape = true;
96 cont;
97 } else if (c == '"') {
Brian Anderson06d14f32011-11-07 23:46:0098 ret (some(string(res)),
99 str::char_slice(s, pos, str::char_len(s)));
Elly Jonesbd726262011-11-07 19:01:28100 }
101 res = res + str::from_char(c);
102 }
103
104 ret (none, s);
105}
106
Elly Jones656a2af2011-11-07 23:24:44107fn from_str_list(s: str) -> (option::t<json>, str) {
Elly Jonesbd726262011-11-07 19:01:28108 if str::char_at(s, 0u) != '[' { ret (none, s); }
109 let s0 = str::trim_left(rest(s));
110 let vals = [];
111 if str::is_empty(s0) { ret (none, s0); }
112 if str::char_at(s0, 0u) == ']' { ret (some(list(@[])), rest(s0)); }
113 while str::is_not_empty(s0) {
114 s0 = str::trim_left(s0);
Elly Jones656a2af2011-11-07 23:24:44115 let (next, s1) = from_str_helper(s0);
Elly Jonesbd726262011-11-07 19:01:28116 s0 = s1;
117 alt next {
118 some(j) { vec::grow(vals, 1u, j); }
119 none { ret (none, s0); }
120 }
121 s0 = str::trim_left(s0);
122 if str::is_empty(s0) { ret (none, s0); }
123 alt str::char_at(s0, 0u) {
124 ',' { }
125 ']' { ret (some(list(@vals)), rest(s0)); }
126 _ { ret (none, s0); }
127 }
128 s0 = rest(s0);
129 }
130 ret (none, s0);
131}
132
Elly Jones656a2af2011-11-07 23:24:44133fn from_str_dict(s: str) -> (option::t<json>, str) {
Elly Jonesbd726262011-11-07 19:01:28134 if str::char_at(s, 0u) != '{' { ret (none, s); }
135 let s0 = str::trim_left(rest(s));
136 let vals = map::new_str_hash::<json>();
137 if str::is_empty(s0) { ret (none, s0); }
138 if str::char_at(s0, 0u) == '}' { ret (some(dict(vals)), rest(s0)); }
139 while str::is_not_empty(s0) {
140 s0 = str::trim_left(s0);
Elly Jones656a2af2011-11-07 23:24:44141 let (next, s1) = from_str_helper(s0); // key
Elly Jonesbd726262011-11-07 19:01:28142 let key = "";
143 s0 = s1;
144 alt next {
145 some(string(k)) { key = k; }
146 _ { ret (none, s0); }
147 }
148 s0 = str::trim_left(s0);
149 if str::is_empty(s0) { ret (none, s0); }
150 if str::char_at(s0, 0u) != ':' { ret (none, s0); }
151 s0 = str::trim_left(rest(s0));
Elly Jones656a2af2011-11-07 23:24:44152 let (next, s1) = from_str_helper(s0); // value
Elly Jonesbd726262011-11-07 19:01:28153 s0 = s1;
154 alt next {
155 some(j) { vals.insert(key, j); }
156 _ { ret (none, s0); }
157 }
158 s0 = str::trim_left(s0);
159 if str::is_empty(s0) { ret (none, s0); }
160 alt str::char_at(s0, 0u) {
161 ',' { }
162 '}' { ret (some(dict(vals)), rest(s0)); }
163 _ { ret (none, s0); }
164 }
165 s0 = str::trim_left(rest(s0));
166 }
167 (none, s)
168}
169
Elly Jones656a2af2011-11-07 23:24:44170fn from_str_float(s: str) -> (option::t<json>, str) {
Elly Jonesbd726262011-11-07 19:01:28171 let pos = 0u;
172 let len = str::byte_len(s);
173 let res = 0f;
Marijn Haverbeke4f826d82011-12-16 09:11:00174 let neg = 1f;
Elly Jonesbd726262011-11-07 19:01:28175
176 alt str::char_at(s, 0u) {
177 '-' {
Marijn Haverbeke4f826d82011-12-16 09:11:00178 neg = -1f;
Elly Jonesbd726262011-11-07 19:01:28179 pos = 1u;
180 }
181 '+' {
182 pos = 1u;
183 }
184 '0' to '9' | '.' { }
185 _ { ret (none, s); }
186 }
187
188 while (pos < len) {
189 let opos = pos;
190 let chr = str::char_range_at(s, pos);
191 let c = chr.ch;
192 pos = chr.next;
193 alt c {
194 '0' to '9' {
195 res = res * 10f;
196 res += ((c as int) - ('0' as int)) as float;
197 }
198 '.' { break; }
199 _ { ret (some(num(neg * res)),
200 str::char_slice(s, opos, str::char_len(s))); }
201 }
202 }
203
204 if pos == len {
205 ret (some(num(neg * res)), str::char_slice(s, pos, str::char_len(s)));
206 }
207
Marijn Haverbeke4f826d82011-12-16 09:11:00208 let dec = 1f;
Elly Jonesbd726262011-11-07 19:01:28209 while (pos < len) {
210 let opos = pos;
211 let chr = str::char_range_at(s, pos);
212 let c = chr.ch;
213 pos = chr.next;
214 alt c {
215 '0' to '9' {
Marijn Haverbeke4f826d82011-12-16 09:11:00216 dec /= 10f;
Elly Jonesbd726262011-11-07 19:01:28217 res += (((c as int) - ('0' as int)) as float) * dec;
218 }
219 _ { ret (some(num(neg * res)),
220 str::char_slice(s, opos, str::char_len(s))); }
221 }
222 }
223 ret (some(num(neg * res)), str::char_slice(s, pos, str::char_len(s)));
224}
225
Elly Jones656a2af2011-11-07 23:24:44226fn from_str_bool(s: str) -> (option::t<json>, str) {
Elly Jonesbd726262011-11-07 19:01:28227 if (str::starts_with(s, "true")) {
228 (some(boolean(true)), str::slice(s, 4u, str::byte_len(s)))
229 } else if (str::starts_with(s, "false")) {
230 (some(boolean(false)), str::slice(s, 5u, str::byte_len(s)))
231 } else {
232 (none, s)
233 }
234}
235
Elly Jones656a2af2011-11-07 23:24:44236fn from_str_helper(s: str) -> (option::t<json>, str) {
Elly Jonesbd726262011-11-07 19:01:28237 let s = str::trim_left(s);
238 if str::is_empty(s) { ret (none, s); }
239 let start = str::char_at(s, 0u);
240 alt start {
Elly Jones656a2af2011-11-07 23:24:44241 '"' { from_str_str(s) }
242 '[' { from_str_list(s) }
243 '{' { from_str_dict(s) }
244 '0' to '9' | '-' | '+' | '.' { from_str_float(s) }
245 't' | 'f' { from_str_bool(s) }
Elly Jonesbd726262011-11-07 19:01:28246 _ { ret (none, s); }
247 }
248}
249
Elly Jones656a2af2011-11-07 23:24:44250/*
251Function: from_str
252
253Deserializes a json value from a string.
254*/
255fn from_str(s: str) -> option::t<json> {
256 let (j, _) = from_str_helper(s);
Elly Jonesbd726262011-11-07 19:01:28257 j
258}