2011-11-07 17:24:44 -06:00
|
|
|
// Rust JSON serialization library
|
|
|
|
// Copyright (c) 2011 Google Inc.
|
2011-11-07 13:01:28 -06:00
|
|
|
|
|
|
|
import float;
|
|
|
|
import map;
|
2011-12-13 18:25:51 -06:00
|
|
|
import core::option;
|
2011-11-07 13:01:28 -06:00
|
|
|
import option::{some, none};
|
|
|
|
import str;
|
|
|
|
import vec;
|
|
|
|
|
|
|
|
export json;
|
2011-11-07 17:24:44 -06:00
|
|
|
export to_str;
|
|
|
|
export from_str;
|
2011-11-07 13:01:28 -06:00
|
|
|
|
2011-11-07 17:24:44 -06:00
|
|
|
export num;
|
|
|
|
export string;
|
|
|
|
export boolean;
|
|
|
|
export list;
|
|
|
|
export dict;
|
|
|
|
|
|
|
|
/*
|
|
|
|
Tag: json
|
|
|
|
|
|
|
|
Represents a json value.
|
|
|
|
*/
|
2011-11-07 13:01:28 -06:00
|
|
|
tag json {
|
2011-11-07 17:24:44 -06:00
|
|
|
/* Variant: num */
|
2011-11-07 13:01:28 -06:00
|
|
|
num(float);
|
2011-11-07 17:24:44 -06:00
|
|
|
/* Variant: string */
|
2011-11-07 13:01:28 -06:00
|
|
|
string(str);
|
2011-11-07 17:24:44 -06:00
|
|
|
/* Variant: boolean */
|
2011-11-07 13:01:28 -06:00
|
|
|
boolean(bool);
|
2011-11-07 17:24:44 -06:00
|
|
|
/* Variant: list */
|
2011-11-07 13:01:28 -06:00
|
|
|
list(@[json]);
|
2011-11-07 17:24:44 -06:00
|
|
|
/* Variant: dict */
|
2011-11-07 13:01:28 -06:00
|
|
|
dict(map::hashmap<str,json>);
|
|
|
|
}
|
|
|
|
|
2011-11-07 17:24:44 -06:00
|
|
|
/*
|
|
|
|
Function: to_str
|
|
|
|
|
|
|
|
Serializes a json value into a string.
|
|
|
|
*/
|
|
|
|
fn to_str(j: json) -> str {
|
2011-11-07 13:01:28 -06:00
|
|
|
alt j {
|
|
|
|
num(f) { float::to_str(f, 6u) }
|
|
|
|
string(s) { #fmt["\"%s\"", s] } // XXX: escape
|
|
|
|
boolean(true) { "true" }
|
|
|
|
boolean(false) { "false" }
|
|
|
|
list(@js) {
|
|
|
|
str::concat(["[",
|
|
|
|
str::connect(
|
2011-12-16 08:27:50 -06:00
|
|
|
vec::map::<json,str>(js, { |e| to_str(e) }),
|
2011-11-07 13:01:28 -06:00
|
|
|
", "),
|
|
|
|
"]"])
|
|
|
|
}
|
|
|
|
dict(m) {
|
|
|
|
let parts = [];
|
|
|
|
m.items({ |k, v|
|
|
|
|
vec::grow(parts, 1u,
|
2011-11-07 17:24:44 -06:00
|
|
|
str::concat(["\"", k, "\": ", to_str(v)])
|
2011-11-07 13:01:28 -06:00
|
|
|
)
|
|
|
|
});
|
|
|
|
str::concat(["{ ", str::connect(parts, ", "), " }"])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn rest(s: str) -> str {
|
2011-11-07 17:24:44 -06:00
|
|
|
assert(str::char_len(s) >= 1u);
|
2011-11-07 13:01:28 -06:00
|
|
|
str::char_slice(s, 1u, str::char_len(s))
|
|
|
|
}
|
|
|
|
|
2011-11-07 17:24:44 -06:00
|
|
|
fn from_str_str(s: str) -> (option::t<json>, str) {
|
2011-11-07 13:01:28 -06:00
|
|
|
let pos = 0u;
|
|
|
|
let len = str::byte_len(s);
|
|
|
|
let escape = false;
|
|
|
|
let res = "";
|
|
|
|
|
|
|
|
alt str::char_at(s, 0u) {
|
|
|
|
'"' { pos = 1u; }
|
|
|
|
_ { ret (none, s); }
|
|
|
|
}
|
|
|
|
|
|
|
|
while (pos < len) {
|
|
|
|
let chr = str::char_range_at(s, pos);
|
|
|
|
let c = chr.ch;
|
|
|
|
pos = chr.next;
|
|
|
|
if (escape) {
|
|
|
|
res = res + str::from_char(c);
|
|
|
|
escape = false;
|
|
|
|
cont;
|
|
|
|
}
|
|
|
|
if (c == '\\') {
|
|
|
|
escape = true;
|
|
|
|
cont;
|
|
|
|
} else if (c == '"') {
|
2011-11-07 17:46:00 -06:00
|
|
|
ret (some(string(res)),
|
|
|
|
str::char_slice(s, pos, str::char_len(s)));
|
2011-11-07 13:01:28 -06:00
|
|
|
}
|
|
|
|
res = res + str::from_char(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret (none, s);
|
|
|
|
}
|
|
|
|
|
2011-11-07 17:24:44 -06:00
|
|
|
fn from_str_list(s: str) -> (option::t<json>, str) {
|
2011-11-07 13:01:28 -06:00
|
|
|
if str::char_at(s, 0u) != '[' { ret (none, s); }
|
|
|
|
let s0 = str::trim_left(rest(s));
|
|
|
|
let vals = [];
|
|
|
|
if str::is_empty(s0) { ret (none, s0); }
|
|
|
|
if str::char_at(s0, 0u) == ']' { ret (some(list(@[])), rest(s0)); }
|
|
|
|
while str::is_not_empty(s0) {
|
|
|
|
s0 = str::trim_left(s0);
|
2011-11-07 17:24:44 -06:00
|
|
|
let (next, s1) = from_str_helper(s0);
|
2011-11-07 13:01:28 -06:00
|
|
|
s0 = s1;
|
|
|
|
alt next {
|
|
|
|
some(j) { vec::grow(vals, 1u, j); }
|
|
|
|
none { ret (none, s0); }
|
|
|
|
}
|
|
|
|
s0 = str::trim_left(s0);
|
|
|
|
if str::is_empty(s0) { ret (none, s0); }
|
|
|
|
alt str::char_at(s0, 0u) {
|
|
|
|
',' { }
|
|
|
|
']' { ret (some(list(@vals)), rest(s0)); }
|
|
|
|
_ { ret (none, s0); }
|
|
|
|
}
|
|
|
|
s0 = rest(s0);
|
|
|
|
}
|
|
|
|
ret (none, s0);
|
|
|
|
}
|
|
|
|
|
2011-11-07 17:24:44 -06:00
|
|
|
fn from_str_dict(s: str) -> (option::t<json>, str) {
|
2011-11-07 13:01:28 -06:00
|
|
|
if str::char_at(s, 0u) != '{' { ret (none, s); }
|
|
|
|
let s0 = str::trim_left(rest(s));
|
|
|
|
let vals = map::new_str_hash::<json>();
|
|
|
|
if str::is_empty(s0) { ret (none, s0); }
|
|
|
|
if str::char_at(s0, 0u) == '}' { ret (some(dict(vals)), rest(s0)); }
|
|
|
|
while str::is_not_empty(s0) {
|
|
|
|
s0 = str::trim_left(s0);
|
2011-11-07 17:24:44 -06:00
|
|
|
let (next, s1) = from_str_helper(s0); // key
|
2011-11-07 13:01:28 -06:00
|
|
|
let key = "";
|
|
|
|
s0 = s1;
|
|
|
|
alt next {
|
|
|
|
some(string(k)) { key = k; }
|
|
|
|
_ { ret (none, s0); }
|
|
|
|
}
|
|
|
|
s0 = str::trim_left(s0);
|
|
|
|
if str::is_empty(s0) { ret (none, s0); }
|
|
|
|
if str::char_at(s0, 0u) != ':' { ret (none, s0); }
|
|
|
|
s0 = str::trim_left(rest(s0));
|
2011-11-07 17:24:44 -06:00
|
|
|
let (next, s1) = from_str_helper(s0); // value
|
2011-11-07 13:01:28 -06:00
|
|
|
s0 = s1;
|
|
|
|
alt next {
|
|
|
|
some(j) { vals.insert(key, j); }
|
|
|
|
_ { ret (none, s0); }
|
|
|
|
}
|
|
|
|
s0 = str::trim_left(s0);
|
|
|
|
if str::is_empty(s0) { ret (none, s0); }
|
|
|
|
alt str::char_at(s0, 0u) {
|
|
|
|
',' { }
|
|
|
|
'}' { ret (some(dict(vals)), rest(s0)); }
|
|
|
|
_ { ret (none, s0); }
|
|
|
|
}
|
|
|
|
s0 = str::trim_left(rest(s0));
|
|
|
|
}
|
|
|
|
(none, s)
|
|
|
|
}
|
|
|
|
|
2011-11-07 17:24:44 -06:00
|
|
|
fn from_str_float(s: str) -> (option::t<json>, str) {
|
2011-11-07 13:01:28 -06:00
|
|
|
let pos = 0u;
|
|
|
|
let len = str::byte_len(s);
|
|
|
|
let res = 0f;
|
2011-12-16 03:11:00 -06:00
|
|
|
let neg = 1f;
|
2011-11-07 13:01:28 -06:00
|
|
|
|
|
|
|
alt str::char_at(s, 0u) {
|
|
|
|
'-' {
|
2011-12-16 03:11:00 -06:00
|
|
|
neg = -1f;
|
2011-11-07 13:01:28 -06:00
|
|
|
pos = 1u;
|
|
|
|
}
|
|
|
|
'+' {
|
|
|
|
pos = 1u;
|
|
|
|
}
|
|
|
|
'0' to '9' | '.' { }
|
|
|
|
_ { ret (none, s); }
|
|
|
|
}
|
|
|
|
|
|
|
|
while (pos < len) {
|
|
|
|
let opos = pos;
|
|
|
|
let chr = str::char_range_at(s, pos);
|
|
|
|
let c = chr.ch;
|
|
|
|
pos = chr.next;
|
|
|
|
alt c {
|
|
|
|
'0' to '9' {
|
|
|
|
res = res * 10f;
|
|
|
|
res += ((c as int) - ('0' as int)) as float;
|
|
|
|
}
|
|
|
|
'.' { break; }
|
|
|
|
_ { ret (some(num(neg * res)),
|
|
|
|
str::char_slice(s, opos, str::char_len(s))); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if pos == len {
|
|
|
|
ret (some(num(neg * res)), str::char_slice(s, pos, str::char_len(s)));
|
|
|
|
}
|
|
|
|
|
2011-12-16 03:11:00 -06:00
|
|
|
let dec = 1f;
|
2011-11-07 13:01:28 -06:00
|
|
|
while (pos < len) {
|
|
|
|
let opos = pos;
|
|
|
|
let chr = str::char_range_at(s, pos);
|
|
|
|
let c = chr.ch;
|
|
|
|
pos = chr.next;
|
|
|
|
alt c {
|
|
|
|
'0' to '9' {
|
2011-12-16 03:11:00 -06:00
|
|
|
dec /= 10f;
|
2011-11-07 13:01:28 -06:00
|
|
|
res += (((c as int) - ('0' as int)) as float) * dec;
|
|
|
|
}
|
|
|
|
_ { ret (some(num(neg * res)),
|
|
|
|
str::char_slice(s, opos, str::char_len(s))); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ret (some(num(neg * res)), str::char_slice(s, pos, str::char_len(s)));
|
|
|
|
}
|
|
|
|
|
2011-11-07 17:24:44 -06:00
|
|
|
fn from_str_bool(s: str) -> (option::t<json>, str) {
|
2011-11-07 13:01:28 -06:00
|
|
|
if (str::starts_with(s, "true")) {
|
|
|
|
(some(boolean(true)), str::slice(s, 4u, str::byte_len(s)))
|
|
|
|
} else if (str::starts_with(s, "false")) {
|
|
|
|
(some(boolean(false)), str::slice(s, 5u, str::byte_len(s)))
|
|
|
|
} else {
|
|
|
|
(none, s)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-07 17:24:44 -06:00
|
|
|
fn from_str_helper(s: str) -> (option::t<json>, str) {
|
2011-11-07 13:01:28 -06:00
|
|
|
let s = str::trim_left(s);
|
|
|
|
if str::is_empty(s) { ret (none, s); }
|
|
|
|
let start = str::char_at(s, 0u);
|
|
|
|
alt start {
|
2011-11-07 17:24:44 -06:00
|
|
|
'"' { from_str_str(s) }
|
|
|
|
'[' { from_str_list(s) }
|
|
|
|
'{' { from_str_dict(s) }
|
|
|
|
'0' to '9' | '-' | '+' | '.' { from_str_float(s) }
|
|
|
|
't' | 'f' { from_str_bool(s) }
|
2011-11-07 13:01:28 -06:00
|
|
|
_ { ret (none, s); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-07 17:24:44 -06:00
|
|
|
/*
|
|
|
|
Function: from_str
|
2011-11-07 13:01:28 -06:00
|
|
|
|
2011-11-07 17:24:44 -06:00
|
|
|
Deserializes a json value from a string.
|
|
|
|
*/
|
|
|
|
fn from_str(s: str) -> option::t<json> {
|
|
|
|
let (j, _) = from_str_helper(s);
|
|
|
|
j
|
2011-11-07 13:01:28 -06:00
|
|
|
}
|