rust/src/libstd/json.rs

885 lines
24 KiB
Rust
Raw Normal View History

// Rust JSON serialization library
// Copyright (c) 2011 Google Inc.
//! json serialization
2012-03-07 20:17:30 -06:00
import result::{result, ok, err};
import io;
2012-08-14 15:38:35 -05:00
import io::WriterUtil;
import map;
import map::hashmap;
import map::map;
export json;
2012-03-05 10:47:21 -06:00
export error;
export to_writer;
export to_str;
export from_reader;
export from_str;
export eq;
export to_json;
export num;
export string;
export boolean;
export list;
export dict;
export null;
/// Represents a json value
2012-01-19 17:20:57 -06:00
enum json {
2012-01-19 19:55:34 -06:00
num(float),
string(@~str),
2012-01-19 19:55:34 -06:00
boolean(bool),
list(@~[json]),
dict(map::hashmap<~str, json>),
2012-01-19 19:55:34 -06:00
null,
}
type error = {
line: uint,
col: uint,
msg: @~str,
};
/// Serializes a json value into a io::writer
2012-08-14 15:38:35 -05:00
fn to_writer(wr: io::Writer, j: json) {
2012-08-06 14:34:08 -05:00
match j {
2012-08-03 21:59:04 -05:00
num(n) => wr.write_str(float::to_str(n, 6u)),
string(s) => wr.write_str(escape_str(*s)),
boolean(b) => wr.write_str(if b { ~"true" } else { ~"false" }),
list(v) => {
wr.write_char('[');
let mut first = true;
2012-06-30 18:19:07 -05:00
for (*v).each |item| {
if !first {
wr.write_str(~", ");
}
first = false;
to_writer(wr, item);
};
wr.write_char(']');
}
2012-08-03 21:59:04 -05:00
dict(d) => {
if d.size() == 0u {
wr.write_str(~"{}");
2012-08-01 19:30:05 -05:00
return;
}
wr.write_str(~"{ ");
let mut first = true;
2012-06-30 18:19:07 -05:00
for d.each |key, value| {
if !first {
wr.write_str(~", ");
}
first = false;
2012-06-12 19:20:51 -05:00
wr.write_str(escape_str(key));
wr.write_str(~": ");
to_writer(wr, value);
};
wr.write_str(~" }");
}
2012-08-03 21:59:04 -05:00
null => wr.write_str(~"null")
}
}
fn escape_str(s: ~str) -> ~str {
let mut escaped = ~"\"";
2012-06-30 18:19:07 -05:00
do str::chars_iter(s) |c| {
2012-08-06 14:34:08 -05:00
match c {
2012-08-03 21:59:04 -05:00
'"' => escaped += ~"\\\"",
'\\' => escaped += ~"\\\\",
'\x08' => escaped += ~"\\b",
'\x0c' => escaped += ~"\\f",
'\n' => escaped += ~"\\n",
'\r' => escaped += ~"\\r",
'\t' => escaped += ~"\\t",
_ => escaped += str::from_char(c)
2012-06-12 19:20:51 -05:00
}
};
escaped += ~"\"";
2012-06-12 19:20:51 -05:00
escaped
}
/// Serializes a json value into a string
fn to_str(j: json) -> ~str {
2012-06-30 18:19:07 -05:00
io::with_str_writer(|wr| to_writer(wr, j))
}
type parser_ = {
2012-08-14 15:38:35 -05:00
rdr: io::Reader,
2012-03-26 20:35:18 -05:00
mut ch: char,
mut line: uint,
mut col: uint,
};
enum parser {
parser_(parser_)
}
2012-08-07 20:10:06 -05:00
impl parser {
fn eof() -> bool { self.ch == -1 as char }
fn bump() {
self.ch = self.rdr.read_char();
if self.ch == '\n' {
self.line += 1u;
self.col = 1u;
} else {
self.col += 1u;
}
}
fn next_char() -> char {
self.bump();
self.ch
}
fn error<T>(+msg: ~str) -> result<T, error> {
2012-06-12 19:20:51 -05:00
err({ line: self.line, col: self.col, msg: @msg })
}
fn parse() -> result<json, error> {
2012-08-06 14:34:08 -05:00
match self.parse_value() {
2012-08-03 21:59:04 -05:00
ok(value) => {
// Skip trailing whitespaces.
self.parse_whitespace();
// Make sure there is no trailing characters.
if self.eof() {
ok(value)
} else {
self.error(~"trailing characters")
}
}
2012-08-03 21:59:04 -05:00
e => e
}
}
fn parse_value() -> result<json, error> {
self.parse_whitespace();
2012-08-01 19:30:05 -05:00
if self.eof() { return self.error(~"EOF while parsing value"); }
2012-08-06 14:34:08 -05:00
match self.ch {
2012-08-03 21:59:04 -05:00
'n' => self.parse_ident(~"ull", null),
't' => self.parse_ident(~"rue", boolean(true)),
'f' => self.parse_ident(~"alse", boolean(false)),
'0' to '9' | '-' => self.parse_number(),
2012-08-06 14:34:08 -05:00
'"' => match self.parse_str() {
2012-08-03 21:59:04 -05:00
ok(s) => ok(string(s)),
err(e) => err(e)
},
2012-08-03 21:59:04 -05:00
'[' => self.parse_list(),
'{' => self.parse_object(),
_ => self.error(~"invalid syntax")
}
}
fn parse_whitespace() {
while char::is_whitespace(self.ch) { self.bump(); }
}
fn parse_ident(ident: ~str, value: json) -> result<json, error> {
2012-06-30 18:19:07 -05:00
if str::all(ident, |c| c == self.next_char()) {
self.bump();
ok(value)
} else {
self.error(~"invalid syntax")
}
}
fn parse_number() -> result<json, error> {
let mut neg = 1f;
if self.ch == '-' {
self.bump();
neg = -1f;
}
2012-08-06 14:34:08 -05:00
let mut res = match self.parse_integer() {
2012-08-03 21:59:04 -05:00
ok(res) => res,
err(e) => return err(e)
};
if self.ch == '.' {
2012-08-06 14:34:08 -05:00
match self.parse_decimal(res) {
2012-08-03 21:59:04 -05:00
ok(r) => res = r,
err(e) => return err(e)
}
}
if self.ch == 'e' || self.ch == 'E' {
2012-08-06 14:34:08 -05:00
match self.parse_exponent(res) {
2012-08-03 21:59:04 -05:00
ok(r) => res = r,
err(e) => return err(e)
}
}
ok(num(neg * res))
}
fn parse_integer() -> result<float, error> {
let mut res = 0f;
2012-08-06 14:34:08 -05:00
match self.ch {
2012-08-03 21:59:04 -05:00
'0' => {
self.bump();
// There can be only one leading '0'.
2012-08-06 14:34:08 -05:00
match self.ch {
2012-08-03 21:59:04 -05:00
'0' to '9' => return self.error(~"invalid number"),
_ => ()
}
}
2012-08-03 21:59:04 -05:00
'1' to '9' => {
while !self.eof() {
2012-08-06 14:34:08 -05:00
match self.ch {
2012-08-03 21:59:04 -05:00
'0' to '9' => {
res *= 10f;
res += ((self.ch as int) - ('0' as int)) as float;
self.bump();
}
2012-08-03 21:59:04 -05:00
_ => break
}
}
}
2012-08-03 21:59:04 -05:00
_ => return self.error(~"invalid number")
}
ok(res)
}
fn parse_decimal(res: float) -> result<float, error> {
self.bump();
// Make sure a digit follows the decimal place.
2012-08-06 14:34:08 -05:00
match self.ch {
2012-08-03 21:59:04 -05:00
'0' to '9' => (),
_ => return self.error(~"invalid number")
}
let mut res = res;
let mut dec = 1f;
while !self.eof() {
2012-08-06 14:34:08 -05:00
match self.ch {
2012-08-03 21:59:04 -05:00
'0' to '9' => {
dec /= 10f;
res += (((self.ch as int) - ('0' as int)) as float) * dec;
self.bump();
}
2012-08-03 21:59:04 -05:00
_ => break
}
}
ok(res)
}
fn parse_exponent(res: float) -> result<float, error> {
self.bump();
let mut res = res;
let mut exp = 0u;
let mut neg_exp = false;
2012-08-06 14:34:08 -05:00
match self.ch {
2012-08-03 21:59:04 -05:00
'+' => self.bump(),
'-' => { self.bump(); neg_exp = true; }
_ => ()
}
// Make sure a digit follows the exponent place.
2012-08-06 14:34:08 -05:00
match self.ch {
2012-08-03 21:59:04 -05:00
'0' to '9' => (),
_ => return self.error(~"invalid number")
}
while !self.eof() {
2012-08-06 14:34:08 -05:00
match self.ch {
2012-08-03 21:59:04 -05:00
'0' to '9' => {
exp *= 10u;
exp += (self.ch as uint) - ('0' as uint);
self.bump();
}
2012-08-03 21:59:04 -05:00
_ => break
}
}
let exp = float::pow_with_uint(10u, exp);
if neg_exp {
res /= exp;
} else {
res *= exp;
}
ok(res)
}
fn parse_str() -> result<@~str, error> {
let mut escape = false;
let mut res = ~"";
while !self.eof() {
self.bump();
if (escape) {
2012-08-06 14:34:08 -05:00
match self.ch {
2012-08-03 21:59:04 -05:00
'"' => str::push_char(res, '"'),
'\\' => str::push_char(res, '\\'),
'/' => str::push_char(res, '/'),
'b' => str::push_char(res, '\x08'),
'f' => str::push_char(res, '\x0c'),
'n' => str::push_char(res, '\n'),
'r' => str::push_char(res, '\r'),
't' => str::push_char(res, '\t'),
'u' => {
// Parse \u1234.
let mut i = 0u;
let mut n = 0u;
while i < 4u {
2012-08-06 14:34:08 -05:00
match self.next_char() {
2012-08-03 21:59:04 -05:00
'0' to '9' => {
n = n * 10u +
(self.ch as uint) - ('0' as uint);
}
2012-08-03 21:59:04 -05:00
_ => return self.error(~"invalid \\u escape")
}
i += 1u;
}
// Error out if we didn't parse 4 digits.
if i != 4u {
2012-08-01 19:30:05 -05:00
return self.error(~"invalid \\u escape");
}
str::push_char(res, n as char);
}
2012-08-03 21:59:04 -05:00
_ => return self.error(~"invalid escape")
}
escape = false;
} else if self.ch == '\\' {
escape = true;
} else {
if self.ch == '"' {
self.bump();
2012-08-01 19:30:05 -05:00
return ok(@res);
}
str::push_char(res, self.ch);
}
}
self.error(~"EOF while parsing string")
}
fn parse_list() -> result<json, error> {
self.bump();
self.parse_whitespace();
let mut values = ~[];
if self.ch == ']' {
self.bump();
2012-08-01 19:30:05 -05:00
return ok(list(@values));
}
loop {
2012-08-06 14:34:08 -05:00
match self.parse_value() {
2012-08-03 21:59:04 -05:00
ok(v) => vec::push(values, v),
e => return e
}
self.parse_whitespace();
if self.eof() {
2012-08-01 19:30:05 -05:00
return self.error(~"EOF while parsing list");
}
2012-08-06 14:34:08 -05:00
match self.ch {
2012-08-03 21:59:04 -05:00
',' => self.bump(),
']' => { self.bump(); return ok(list(@values)); }
_ => return self.error(~"expected `,` or `]`")
}
};
}
fn parse_object() -> result<json, error> {
self.bump();
self.parse_whitespace();
let values = map::str_hash();
if self.ch == '}' {
self.bump();
2012-08-01 19:30:05 -05:00
return ok(dict(values));
}
while !self.eof() {
self.parse_whitespace();
if self.ch != '"' {
2012-08-01 19:30:05 -05:00
return self.error(~"key must be a string");
}
2012-08-06 14:34:08 -05:00
let key = match self.parse_str() {
2012-08-03 21:59:04 -05:00
ok(key) => key,
err(e) => return err(e)
};
self.parse_whitespace();
if self.ch != ':' {
if self.eof() { break; }
2012-08-01 19:30:05 -05:00
return self.error(~"expected `:`");
}
self.bump();
2012-08-06 14:34:08 -05:00
match self.parse_value() {
2012-08-03 21:59:04 -05:00
ok(value) => { values.insert(copy *key, value); }
e => return e
}
self.parse_whitespace();
2012-08-06 14:34:08 -05:00
match self.ch {
2012-08-03 21:59:04 -05:00
',' => self.bump(),
'}' => { self.bump(); return ok(dict(values)); }
_ => {
if self.eof() { break; }
2012-08-01 19:30:05 -05:00
return self.error(~"expected `,` or `}`");
}
}
}
2012-08-01 19:30:05 -05:00
return self.error(~"EOF while parsing object");
2011-12-21 14:36:43 -06:00
}
}
/// Deserializes a json value from an io::reader
2012-08-14 15:38:35 -05:00
fn from_reader(rdr: io::Reader) -> result<json, error> {
let parser = parser_({
rdr: rdr,
2012-03-26 20:35:18 -05:00
mut ch: rdr.read_char(),
mut line: 1u,
mut col: 1u,
});
parser.parse()
}
/// Deserializes a json value from a string
fn from_str(s: ~str) -> result<json, error> {
io::with_str_reader(s, from_reader)
}
/// Test if two json values are equal
fn eq(value0: json, value1: json) -> bool {
2012-08-06 14:34:08 -05:00
match (value0, value1) {
2012-08-03 21:59:04 -05:00
(num(f0), num(f1)) => f0 == f1,
(string(s0), string(s1)) => s0 == s1,
(boolean(b0), boolean(b1)) => b0 == b1,
(list(l0), list(l1)) => vec::all2(*l0, *l1, eq),
(dict(d0), dict(d1)) => {
if d0.size() == d1.size() {
let mut equal = true;
2012-06-30 18:19:07 -05:00
for d0.each |k, v0| {
2012-08-06 14:34:08 -05:00
match d1.find(k) {
2012-08-03 21:59:04 -05:00
some(v1) => if !eq(v0, v1) { equal = false },
none => equal = false
}
};
equal
} else {
false
}
}
2012-08-03 21:59:04 -05:00
(null, null) => true,
_ => false
}
}
2012-01-17 21:05:07 -06:00
trait to_json { fn to_json() -> json; }
2012-08-07 20:10:06 -05:00
impl json: to_json {
fn to_json() -> json { self }
}
2012-08-07 20:10:06 -05:00
impl @json: to_json {
2012-06-13 10:30:54 -05:00
fn to_json() -> json { *self }
}
2012-08-07 20:10:06 -05:00
impl int: to_json {
2012-06-13 10:30:54 -05:00
fn to_json() -> json { num(self as float) }
}
2012-08-07 20:10:06 -05:00
impl i8: to_json {
fn to_json() -> json { num(self as float) }
}
2012-08-07 20:10:06 -05:00
impl i16: to_json {
fn to_json() -> json { num(self as float) }
}
2012-08-07 20:10:06 -05:00
impl i32: to_json {
fn to_json() -> json { num(self as float) }
}
2012-08-07 20:10:06 -05:00
impl i64: to_json {
fn to_json() -> json { num(self as float) }
}
2012-08-07 20:10:06 -05:00
impl uint: to_json {
2012-06-13 10:30:54 -05:00
fn to_json() -> json { num(self as float) }
}
2012-08-07 20:10:06 -05:00
impl u8: to_json {
fn to_json() -> json { num(self as float) }
}
2012-08-07 20:10:06 -05:00
impl u16: to_json {
fn to_json() -> json { num(self as float) }
}
2012-08-07 20:10:06 -05:00
impl u32: to_json {
fn to_json() -> json { num(self as float) }
}
2012-08-07 20:10:06 -05:00
impl u64: to_json {
fn to_json() -> json { num(self as float) }
}
2012-08-07 20:10:06 -05:00
impl float: to_json {
fn to_json() -> json { num(self) }
}
2012-08-07 20:10:06 -05:00
impl f32: to_json {
fn to_json() -> json { num(self as float) }
}
2012-08-07 20:10:06 -05:00
impl f64: to_json {
fn to_json() -> json { num(self as float) }
}
2012-08-07 20:10:06 -05:00
impl (): to_json {
fn to_json() -> json { null }
}
2012-08-07 20:10:06 -05:00
impl bool: to_json {
fn to_json() -> json { boolean(self) }
}
2012-08-07 20:10:06 -05:00
impl ~str: to_json {
2012-06-12 19:20:51 -05:00
fn to_json() -> json { string(@copy self) }
}
2012-08-07 20:10:06 -05:00
impl @~str: to_json {
fn to_json() -> json { string(self) }
}
2012-08-07 20:10:06 -05:00
impl <A: to_json, B: to_json> (A, B): to_json {
fn to_json() -> json {
2012-08-06 14:34:08 -05:00
match self {
(a, b) => {
list(@~[a.to_json(), b.to_json()])
}
}
}
}
2012-08-07 20:10:06 -05:00
impl <A: to_json, B: to_json, C: to_json> (A, B, C): to_json {
fn to_json() -> json {
2012-08-06 14:34:08 -05:00
match self {
(a, b, c) => {
list(@~[a.to_json(), b.to_json(), c.to_json()])
}
}
}
}
2012-08-07 20:10:06 -05:00
impl <A: to_json> ~[A]: to_json {
2012-06-30 18:19:07 -05:00
fn to_json() -> json { list(@self.map(|elt| elt.to_json())) }
}
2012-08-07 20:10:06 -05:00
impl <A: to_json copy> hashmap<~str, A>: to_json {
fn to_json() -> json {
let d = map::str_hash();
2012-06-30 18:19:07 -05:00
for self.each() |key, value| {
2012-06-12 19:20:51 -05:00
d.insert(copy key, value.to_json());
}
dict(d)
}
}
2012-08-07 20:10:06 -05:00
impl <A: to_json> option<A>: to_json {
fn to_json() -> json {
2012-08-06 14:34:08 -05:00
match self {
2012-08-03 21:59:04 -05:00
none => null,
some(value) => value.to_json()
}
}
}
impl json: to_str::ToStr {
fn to_str() -> ~str { to_str(self) }
}
impl error: to_str::ToStr {
fn to_str() -> ~str {
fmt!{"%u:%u: %s", self.line, self.col, *self.msg}
}
}
2012-01-17 21:05:07 -06:00
#[cfg(test)]
mod tests {
fn mk_dict(items: ~[(~str, json)]) -> json {
let d = map::str_hash();
2012-06-30 18:19:07 -05:00
do vec::iter(items) |item| {
2012-06-12 19:20:51 -05:00
let (key, value) = copy item;
d.insert(key, value);
};
dict(d)
}
#[test]
fn test_write_null() {
assert to_str(null) == ~"null";
}
#[test]
fn test_write_num() {
assert to_str(num(3f)) == ~"3";
assert to_str(num(3.1f)) == ~"3.1";
assert to_str(num(-1.5f)) == ~"-1.5";
assert to_str(num(0.5f)) == ~"0.5";
}
#[test]
fn test_write_str() {
assert to_str(string(@~"")) == ~"\"\"";
assert to_str(string(@~"foo")) == ~"\"foo\"";
}
#[test]
fn test_write_bool() {
assert to_str(boolean(true)) == ~"true";
assert to_str(boolean(false)) == ~"false";
}
#[test]
fn test_write_list() {
assert to_str(list(@~[])) == ~"[]";
assert to_str(list(@~[boolean(true)])) == ~"[true]";
assert to_str(list(@~[
boolean(false),
null,
list(@~[string(@~"foo\nbar"), num(3.5f)])
])) == ~"[false, null, [\"foo\\nbar\", 3.5]]";
}
#[test]
fn test_write_dict() {
assert to_str(mk_dict(~[])) == ~"{}";
assert to_str(mk_dict(~[(~"a", boolean(true))]))
== ~"{ \"a\": true }";
assert to_str(mk_dict(~[
(~"a", boolean(true)),
(~"b", list(@~[
mk_dict(~[(~"c", string(@~"\x0c\r"))]),
mk_dict(~[(~"d", string(@~""))])
]))
])) ==
~"{ " +
~"\"a\": true, " +
~"\"b\": [" +
~"{ \"c\": \"\\f\\r\" }, " +
~"{ \"d\": \"\" }" +
~"]" +
~" }";
}
2012-01-17 21:05:07 -06:00
#[test]
fn test_trailing_characters() {
assert from_str(~"nulla") ==
err({line: 1u, col: 5u, msg: @~"trailing characters"});
assert from_str(~"truea") ==
err({line: 1u, col: 5u, msg: @~"trailing characters"});
assert from_str(~"falsea") ==
err({line: 1u, col: 6u, msg: @~"trailing characters"});
assert from_str(~"1a") ==
err({line: 1u, col: 2u, msg: @~"trailing characters"});
assert from_str(~"[]a") ==
err({line: 1u, col: 3u, msg: @~"trailing characters"});
assert from_str(~"{}a") ==
err({line: 1u, col: 3u, msg: @~"trailing characters"});
}
#[test]
fn test_read_identifiers() {
assert from_str(~"n") ==
err({line: 1u, col: 2u, msg: @~"invalid syntax"});
assert from_str(~"nul") ==
err({line: 1u, col: 4u, msg: @~"invalid syntax"});
assert from_str(~"t") ==
err({line: 1u, col: 2u, msg: @~"invalid syntax"});
assert from_str(~"truz") ==
err({line: 1u, col: 4u, msg: @~"invalid syntax"});
assert from_str(~"f") ==
err({line: 1u, col: 2u, msg: @~"invalid syntax"});
assert from_str(~"faz") ==
err({line: 1u, col: 3u, msg: @~"invalid syntax"});
assert from_str(~"null") == ok(null);
assert from_str(~"true") == ok(boolean(true));
assert from_str(~"false") == ok(boolean(false));
assert from_str(~" null ") == ok(null);
assert from_str(~" true ") == ok(boolean(true));
assert from_str(~" false ") == ok(boolean(false));
2012-01-17 21:05:07 -06:00
}
#[test]
fn test_read_num() {
assert from_str(~"+") ==
err({line: 1u, col: 1u, msg: @~"invalid syntax"});
assert from_str(~".") ==
err({line: 1u, col: 1u, msg: @~"invalid syntax"});
assert from_str(~"-") ==
err({line: 1u, col: 2u, msg: @~"invalid number"});
assert from_str(~"00") ==
err({line: 1u, col: 2u, msg: @~"invalid number"});
assert from_str(~"1.") ==
err({line: 1u, col: 3u, msg: @~"invalid number"});
assert from_str(~"1e") ==
err({line: 1u, col: 3u, msg: @~"invalid number"});
assert from_str(~"1e+") ==
err({line: 1u, col: 4u, msg: @~"invalid number"});
assert from_str(~"3") == ok(num(3f));
assert from_str(~"3.1") == ok(num(3.1f));
assert from_str(~"-1.2") == ok(num(-1.2f));
assert from_str(~"0.4") == ok(num(0.4f));
assert from_str(~"0.4e5") == ok(num(0.4e5f));
assert from_str(~"0.4e+15") == ok(num(0.4e15f));
assert from_str(~"0.4e-01") == ok(num(0.4e-01f));
assert from_str(~" 3 ") == ok(num(3f));
2012-01-17 21:05:07 -06:00
}
#[test]
fn test_read_str() {
assert from_str(~"\"") ==
err({line: 1u, col: 2u, msg: @~"EOF while parsing string"});
assert from_str(~"\"lol") ==
err({line: 1u, col: 5u, msg: @~"EOF while parsing string"});
assert from_str(~"\"\"") == ok(string(@~""));
assert from_str(~"\"foo\"") == ok(string(@~"foo"));
assert from_str(~"\"\\\"\"") == ok(string(@~"\""));
assert from_str(~"\"\\b\"") == ok(string(@~"\x08"));
assert from_str(~"\"\\n\"") == ok(string(@~"\n"));
assert from_str(~"\"\\r\"") == ok(string(@~"\r"));
assert from_str(~"\"\\t\"") == ok(string(@~"\t"));
assert from_str(~" \"foo\" ") == ok(string(@~"foo"));
2012-01-17 21:05:07 -06:00
}
#[test]
fn test_read_list() {
assert from_str(~"[") ==
err({line: 1u, col: 2u, msg: @~"EOF while parsing value"});
assert from_str(~"[1") ==
err({line: 1u, col: 3u, msg: @~"EOF while parsing list"});
assert from_str(~"[1,") ==
err({line: 1u, col: 4u, msg: @~"EOF while parsing value"});
assert from_str(~"[1,]") ==
err({line: 1u, col: 4u, msg: @~"invalid syntax"});
assert from_str(~"[6 7]") ==
err({line: 1u, col: 4u, msg: @~"expected `,` or `]`"});
assert from_str(~"[]") == ok(list(@~[]));
assert from_str(~"[ ]") == ok(list(@~[]));
assert from_str(~"[true]") == ok(list(@~[boolean(true)]));
assert from_str(~"[ false ]") == ok(list(@~[boolean(false)]));
assert from_str(~"[null]") == ok(list(@~[null]));
assert from_str(~"[3, 1]") == ok(list(@~[num(3f), num(1f)]));
assert from_str(~"\n[3, 2]\n") == ok(list(@~[num(3f), num(2f)]));
assert from_str(~"[2, [4, 1]]") ==
ok(list(@~[num(2f), list(@~[num(4f), num(1f)])]));
2012-01-17 21:05:07 -06:00
}
#[test]
fn test_read_dict() {
assert from_str(~"{") ==
err({line: 1u, col: 2u, msg: @~"EOF while parsing object"});
assert from_str(~"{ ") ==
err({line: 1u, col: 3u, msg: @~"EOF while parsing object"});
assert from_str(~"{1") ==
err({line: 1u, col: 2u, msg: @~"key must be a string"});
assert from_str(~"{ \"a\"") ==
err({line: 1u, col: 6u, msg: @~"EOF while parsing object"});
assert from_str(~"{\"a\"") ==
err({line: 1u, col: 5u, msg: @~"EOF while parsing object"});
assert from_str(~"{\"a\" ") ==
err({line: 1u, col: 6u, msg: @~"EOF while parsing object"});
assert from_str(~"{\"a\" 1") ==
err({line: 1u, col: 6u, msg: @~"expected `:`"});
assert from_str(~"{\"a\":") ==
err({line: 1u, col: 6u, msg: @~"EOF while parsing value"});
assert from_str(~"{\"a\":1") ==
err({line: 1u, col: 7u, msg: @~"EOF while parsing object"});
assert from_str(~"{\"a\":1 1") ==
err({line: 1u, col: 8u, msg: @~"expected `,` or `}`"});
assert from_str(~"{\"a\":1,") ==
err({line: 1u, col: 8u, msg: @~"EOF while parsing object"});
assert eq(result::get(from_str(~"{}")), mk_dict(~[]));
assert eq(result::get(from_str(~"{\"a\": 3}")),
mk_dict(~[(~"a", num(3.0f))]));
assert eq(result::get(from_str(~"{ \"a\": null, \"b\" : true }")),
mk_dict(~[
(~"a", null),
(~"b", boolean(true))]));
assert eq(result::get(from_str(~"\n{ \"a\": null, \"b\" : true }\n")),
mk_dict(~[
(~"a", null),
(~"b", boolean(true))]));
assert eq(result::get(from_str(~"{\"a\" : 1.0 ,\"b\": [ true ]}")),
mk_dict(~[
(~"a", num(1.0)),
(~"b", list(@~[boolean(true)]))
]));
assert eq(result::get(from_str(
~"{" +
~"\"a\": 1.0, " +
~"\"b\": [" +
~"true," +
~"\"foo\\nbar\", " +
~"{ \"c\": {\"d\": null} } " +
~"]" +
~"}")),
mk_dict(~[
(~"a", num(1.0f)),
(~"b", list(@~[
boolean(true),
string(@~"foo\nbar"),
mk_dict(~[
(~"c", mk_dict(~[(~"d", null)]))
])
]))
]));
2012-01-17 21:05:07 -06:00
}
#[test]
fn test_multiline_errors() {
assert from_str(~"{\n \"foo\":\n \"bar\"") ==
err({line: 3u, col: 8u, msg: @~"EOF while parsing object"});
2012-01-17 21:05:07 -06:00
}
}