Initial commit
This commit is contained in:
commit
11115f13a3
617
src/parser.rs
Normal file
617
src/parser.rs
Normal file
|
@ -0,0 +1,617 @@
|
|||
use std::char;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::num::FromStrRadix;
|
||||
use std::str;
|
||||
|
||||
use {Array, Table, Value, String, Float, Integer, Boolean, Datetime};
|
||||
|
||||
pub struct Parser<'a> {
|
||||
input: &'a str,
|
||||
cur: str::CharOffsets<'a>,
|
||||
tables_defined: HashSet<String>,
|
||||
pub errors: Vec<Error>,
|
||||
}
|
||||
|
||||
#[deriving(Show)]
|
||||
pub struct Error {
|
||||
pub lo: uint,
|
||||
pub hi: uint,
|
||||
pub desc: String,
|
||||
}
|
||||
|
||||
impl<'a> Parser<'a> {
|
||||
pub fn new(s: &'a str) -> Parser<'a> {
|
||||
Parser {
|
||||
input: s,
|
||||
cur: s.char_indices(),
|
||||
errors: Vec::new(),
|
||||
tables_defined: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn next_pos(&self) -> uint {
|
||||
self.cur.clone().next().map(|p| p.val0()).unwrap_or(self.input.len())
|
||||
}
|
||||
|
||||
fn eat(&mut self, ch: char) -> bool {
|
||||
match self.cur.clone().next() {
|
||||
Some((_, c)) if c == ch => { self.cur.next(); true }
|
||||
Some(_) | None => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn expect(&mut self, ch: char) -> bool {
|
||||
if self.eat(ch) { return true }
|
||||
let mut it = self.cur.clone();
|
||||
let lo = it.next().map(|p| p.val0()).unwrap_or(self.input.len());
|
||||
let hi = it.next().map(|p| p.val0()).unwrap_or(self.input.len());
|
||||
self.errors.push(Error {
|
||||
lo: lo,
|
||||
hi: hi,
|
||||
desc: match self.cur.clone().next() {
|
||||
Some((_, c)) => format!("expected `{}`, but found `{}`", ch, c),
|
||||
None => format!("expected `{}`, but found eof", ch)
|
||||
}
|
||||
});
|
||||
false
|
||||
}
|
||||
|
||||
fn ws(&mut self) {
|
||||
loop {
|
||||
match self.cur.clone().next() {
|
||||
Some((_, '\t')) |
|
||||
Some((_, ' ')) => { self.cur.next(); }
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn comment(&mut self) {
|
||||
match self.cur.clone().next() {
|
||||
Some((_, '#')) => {}
|
||||
_ => return,
|
||||
}
|
||||
for (_, ch) in self.cur {
|
||||
if ch == '\n' { break }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(&mut self) -> Option<Table> {
|
||||
let mut ret = HashMap::new();
|
||||
loop {
|
||||
self.ws();
|
||||
match self.cur.clone().next() {
|
||||
Some((_, '#')) => { self.comment(); }
|
||||
Some((_, '\n')) => { self.cur.next(); }
|
||||
Some((start, '[')) => {
|
||||
self.cur.next();
|
||||
let array = self.eat('[');
|
||||
let mut section = String::new();
|
||||
for (pos, ch) in self.cur {
|
||||
if ch == ']' { break }
|
||||
if ch == '[' {
|
||||
self.errors.push(Error {
|
||||
lo: pos,
|
||||
hi: pos + 1,
|
||||
desc: format!("section names cannot contain \
|
||||
a `[` character"),
|
||||
});
|
||||
continue
|
||||
}
|
||||
section.push_char(ch);
|
||||
}
|
||||
|
||||
if section.len() == 0 {
|
||||
self.errors.push(Error {
|
||||
lo: start,
|
||||
hi: start + if array {3} else {1},
|
||||
desc: format!("section name must not be empty"),
|
||||
});
|
||||
continue
|
||||
} else if array && !self.expect(']') {
|
||||
return None
|
||||
}
|
||||
|
||||
let mut table = HashMap::new();
|
||||
if !self.values(&mut table) { return None }
|
||||
if array {
|
||||
self.insert_array(&mut ret, section, Table(table), start)
|
||||
} else {
|
||||
self.insert_table(&mut ret, section, table, start)
|
||||
}
|
||||
}
|
||||
Some(_) => {
|
||||
if !self.values(&mut ret) { return None }
|
||||
}
|
||||
None if self.errors.len() == 0 => return Some(ret),
|
||||
None => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn values(&mut self, into: &mut Table) -> bool {
|
||||
loop {
|
||||
self.ws();
|
||||
match self.cur.clone().next() {
|
||||
Some((_, '#')) => self.comment(),
|
||||
Some((_, '\n')) => { self.cur.next(); }
|
||||
Some((_, '[')) => break,
|
||||
Some((start, _)) => {
|
||||
let mut key = String::new();
|
||||
let mut found_eq = false;
|
||||
for (pos, ch) in self.cur {
|
||||
match ch {
|
||||
' ' | '\t' => break,
|
||||
'=' => { found_eq = true; break }
|
||||
'\n' => {
|
||||
self.errors.push(Error {
|
||||
lo: start,
|
||||
hi: pos + 1,
|
||||
desc: format!("keys cannot be defined \
|
||||
across lines"),
|
||||
})
|
||||
}
|
||||
c => key.push_char(c),
|
||||
}
|
||||
}
|
||||
if !found_eq {
|
||||
self.ws();
|
||||
if !self.expect('=') { return false }
|
||||
}
|
||||
|
||||
let value = match self.value() {
|
||||
Some(value) => value,
|
||||
None => return false,
|
||||
};
|
||||
self.insert(into, key, value, start);
|
||||
self.ws();
|
||||
self.comment();
|
||||
self.eat('\n');
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fn value(&mut self) -> Option<Value> {
|
||||
self.ws();
|
||||
match self.cur.clone().next() {
|
||||
Some((pos, '"')) => self.string(pos),
|
||||
Some((pos, 't')) |
|
||||
Some((pos, 'f')) => self.boolean(pos),
|
||||
Some((pos, '[')) => self.array(pos),
|
||||
Some((pos, '-')) => self.number_or_datetime(pos),
|
||||
Some((pos, ch)) if ch.is_digit() => self.number_or_datetime(pos),
|
||||
_ => {
|
||||
let mut it = self.cur.clone();
|
||||
let lo = it.next().map(|p| p.val0()).unwrap_or(self.input.len());
|
||||
let hi = it.next().map(|p| p.val0()).unwrap_or(self.input.len());
|
||||
self.errors.push(Error {
|
||||
lo: lo,
|
||||
hi: hi,
|
||||
desc: format!("expected a value"),
|
||||
});
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn string(&mut self, start: uint) -> Option<Value> {
|
||||
if !self.expect('"') { return None }
|
||||
let mut ret = String::new();
|
||||
|
||||
loop {
|
||||
match self.cur.next() {
|
||||
Some((_, '"')) => break,
|
||||
Some((pos, '\\')) => {
|
||||
match escape(self, pos) {
|
||||
Some(c) => ret.push_char(c),
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
Some((pos, ch)) if ch < '\u001f' => {
|
||||
let mut escaped = String::new();
|
||||
ch.escape_default(|c| escaped.push_char(c));
|
||||
self.errors.push(Error {
|
||||
lo: pos,
|
||||
hi: pos + 1,
|
||||
desc: format!("control character `{}` must be escaped",
|
||||
escaped)
|
||||
});
|
||||
}
|
||||
Some((_, ch)) => ret.push_char(ch),
|
||||
None => {
|
||||
self.errors.push(Error {
|
||||
lo: start,
|
||||
hi: self.input.len(),
|
||||
desc: format!("unterminated string literal"),
|
||||
});
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Some(String(ret));
|
||||
|
||||
fn escape(me: &mut Parser, pos: uint) -> Option<char> {
|
||||
match me.cur.next() {
|
||||
Some((_, 'b')) => Some('\u0008'),
|
||||
Some((_, 't')) => Some('\u0009'),
|
||||
Some((_, 'n')) => Some('\u000a'),
|
||||
Some((_, 'f')) => Some('\u000c'),
|
||||
Some((_, 'r')) => Some('\u000d'),
|
||||
Some((_, '"')) => Some('\u0022'),
|
||||
Some((_, '/')) => Some('\u002f'),
|
||||
Some((_, '\\')) => Some('\u005c'),
|
||||
Some((pos, 'u')) => {
|
||||
let num = if me.input.is_char_boundary(pos + 5) {
|
||||
me.input.slice(pos + 1, pos + 5)
|
||||
} else {
|
||||
"invalid"
|
||||
};
|
||||
match FromStrRadix::from_str_radix(num, 16) {
|
||||
Some(n) => {
|
||||
match char::from_u32(n) {
|
||||
Some(c) => {
|
||||
me.cur.next();
|
||||
me.cur.next();
|
||||
me.cur.next();
|
||||
me.cur.next();
|
||||
return Some(c)
|
||||
}
|
||||
None => {
|
||||
me.errors.push(Error {
|
||||
lo: pos + 1,
|
||||
hi: pos + 5,
|
||||
desc: format!("codepoint `{:x}` is \
|
||||
not a valid unicode \
|
||||
codepoint", n),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
me.errors.push(Error {
|
||||
lo: pos,
|
||||
hi: pos + 1,
|
||||
desc: format!("expected four hex digits \
|
||||
after a `u` escape"),
|
||||
})
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
Some((pos, ch)) => {
|
||||
let mut escaped = String::new();
|
||||
ch.escape_default(|c| escaped.push_char(c));
|
||||
let next_pos = me.next_pos();
|
||||
me.errors.push(Error {
|
||||
lo: pos,
|
||||
hi: next_pos,
|
||||
desc: format!("unknown string escape: `{}`",
|
||||
escaped),
|
||||
});
|
||||
None
|
||||
}
|
||||
None => {
|
||||
me.errors.push(Error {
|
||||
lo: pos,
|
||||
hi: pos + 1,
|
||||
desc: format!("unterminated escape sequence"),
|
||||
});
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn number_or_datetime(&mut self, start: uint) -> Option<Value> {
|
||||
let negative = self.eat('-');
|
||||
let mut is_float = false;
|
||||
loop {
|
||||
match self.cur.clone().next() {
|
||||
Some((_, ch)) if ch.is_digit() => { self.cur.next(); }
|
||||
Some((_, '.')) if !is_float => {
|
||||
is_float = true;
|
||||
self.cur.next();
|
||||
}
|
||||
Some(_) | None => break,
|
||||
}
|
||||
}
|
||||
let end = self.next_pos();
|
||||
let ret = if is_float {
|
||||
if self.input.char_at_reverse(end) == '.' {
|
||||
None
|
||||
} else {
|
||||
from_str::<f64>(self.input.slice(start, end)).map(Float)
|
||||
}
|
||||
} else if !negative && self.eat('-') {
|
||||
self.datetime(start, end + 1)
|
||||
} else {
|
||||
from_str::<i64>(self.input.slice(start, end)).map(Integer)
|
||||
};
|
||||
if ret.is_none() {
|
||||
self.errors.push(Error {
|
||||
lo: start,
|
||||
hi: end,
|
||||
desc: format!("invalid numeric literal"),
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
fn boolean(&mut self, start: uint) -> Option<Value> {
|
||||
let rest = self.input.slice_from(start);
|
||||
if rest.starts_with("true") {
|
||||
for _ in range(0, 4) {
|
||||
self.cur.next();
|
||||
}
|
||||
Some(Boolean(true))
|
||||
} else if rest.starts_with("false") {
|
||||
for _ in range(0, 5) {
|
||||
self.cur.next();
|
||||
}
|
||||
Some(Boolean(false))
|
||||
} else {
|
||||
let next = self.next_pos();
|
||||
self.errors.push(Error {
|
||||
lo: start,
|
||||
hi: next,
|
||||
desc: format!("unexpected character: `{}`",
|
||||
rest.char_at(0)),
|
||||
});
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn datetime(&mut self, start: uint, end_so_far: uint) -> Option<Value> {
|
||||
let mut date = self.input.slice(start, end_so_far).to_string();
|
||||
for _ in range(0, 15) {
|
||||
match self.cur.next() {
|
||||
Some((_, ch)) => date.push_char(ch),
|
||||
None => {
|
||||
self.errors.push(Error {
|
||||
lo: start,
|
||||
hi: end_so_far,
|
||||
desc: format!("malformed date literal"),
|
||||
});
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut it = date.as_slice().chars();
|
||||
let mut valid = true;
|
||||
valid = valid && it.next().map(|c| c.is_digit()).unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c.is_digit()).unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c.is_digit()).unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c.is_digit()).unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c == '-').unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c.is_digit()).unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c.is_digit()).unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c == '-').unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c.is_digit()).unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c.is_digit()).unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c == 'T').unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c.is_digit()).unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c.is_digit()).unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c == ':').unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c.is_digit()).unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c.is_digit()).unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c == ':').unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c.is_digit()).unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c.is_digit()).unwrap_or(false);
|
||||
valid = valid && it.next().map(|c| c == 'Z').unwrap_or(false);
|
||||
if valid {
|
||||
Some(Datetime(date.clone()))
|
||||
} else {
|
||||
self.errors.push(Error {
|
||||
lo: start,
|
||||
hi: start + date.len(),
|
||||
desc: format!("malformed date literal"),
|
||||
});
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn array(&mut self, _start: uint) -> Option<Value> {
|
||||
if !self.expect('[') { return None }
|
||||
let mut ret = Vec::new();
|
||||
fn consume(me: &mut Parser) {
|
||||
loop {
|
||||
me.ws();
|
||||
match me.cur.clone().next() {
|
||||
Some((_, '#')) => { me.comment(); }
|
||||
Some((_, '\n')) => { me.cur.next(); }
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut type_str = None;
|
||||
loop {
|
||||
// Break out early if we see the closing bracket
|
||||
consume(self);
|
||||
if self.eat(']') { return Some(Array(ret)) }
|
||||
|
||||
// Attempt to parse a value, triggering an error if it's the wrong
|
||||
// type.
|
||||
let start = self.next_pos();
|
||||
let value = match self.value() {
|
||||
Some(v) => v,
|
||||
None => return None,
|
||||
};
|
||||
let end = self.next_pos();
|
||||
let expected = type_str.unwrap_or(value.type_str());
|
||||
if value.type_str() != expected {
|
||||
self.errors.push(Error {
|
||||
lo: start,
|
||||
hi: end,
|
||||
desc: format!("expected type `{}`, found type `{}`",
|
||||
expected, value.type_str()),
|
||||
});
|
||||
} else {
|
||||
type_str = Some(expected);
|
||||
ret.push(value);
|
||||
}
|
||||
|
||||
// Look for a comma. If we don't find one we're done
|
||||
consume(self);
|
||||
if !self.eat(',') { break }
|
||||
}
|
||||
consume(self);
|
||||
if !self.expect(']') { return None }
|
||||
return Some(Array(ret))
|
||||
}
|
||||
|
||||
fn insert(&mut self, into: &mut Table, key: String, value: Value,
|
||||
key_lo: uint) {
|
||||
if into.contains_key(&key) {
|
||||
self.errors.push(Error {
|
||||
lo: key_lo,
|
||||
hi: key_lo + key.len(),
|
||||
desc: format!("duplicate key: `{}`", key),
|
||||
})
|
||||
} else {
|
||||
into.insert(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
fn recurse<'a>(&mut self, mut cur: &'a mut Table, orig_key: &'a str,
|
||||
key_lo: uint) -> Option<(&'a mut Table, &'a str)> {
|
||||
if orig_key.starts_with(".") || orig_key.ends_with(".") ||
|
||||
orig_key.contains("..") {
|
||||
self.errors.push(Error {
|
||||
lo: key_lo,
|
||||
hi: key_lo + orig_key.len(),
|
||||
desc: format!("tables cannot have empty names"),
|
||||
});
|
||||
return None
|
||||
}
|
||||
let key = match orig_key.rfind('.') {
|
||||
Some(n) => orig_key.slice_to(n),
|
||||
None => return Some((cur, orig_key)),
|
||||
};
|
||||
for part in key.as_slice().split('.') {
|
||||
let part = part.to_string();
|
||||
let tmp = cur;
|
||||
|
||||
if tmp.contains_key(&part) {
|
||||
match *tmp.get_mut(&part) {
|
||||
Table(ref mut table) => {
|
||||
cur = table;
|
||||
continue
|
||||
}
|
||||
Array(ref mut array) => {
|
||||
match array.as_mut_slice().mut_last() {
|
||||
Some(&Table(ref mut table)) => cur = table,
|
||||
_ => {
|
||||
self.errors.push(Error {
|
||||
lo: key_lo,
|
||||
hi: key_lo + key.len(),
|
||||
desc: format!("array `{}` does not contain \
|
||||
tables", part)
|
||||
});
|
||||
return None
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
_ => {
|
||||
self.errors.push(Error {
|
||||
lo: key_lo,
|
||||
hi: key_lo + key.len(),
|
||||
desc: format!("key `{}` was not previously a table",
|
||||
part)
|
||||
});
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize an empty table as part of this sub-key
|
||||
tmp.insert(part.clone(), Table(HashMap::new()));
|
||||
match *tmp.get_mut(&part) {
|
||||
Table(ref mut inner) => cur = inner,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
return Some((cur, orig_key.slice_from(key.len() + 1)))
|
||||
}
|
||||
|
||||
fn insert_table(&mut self, into: &mut Table, key: String, value: Table,
|
||||
key_lo: uint) {
|
||||
if !self.tables_defined.insert(key.clone()) {
|
||||
self.errors.push(Error {
|
||||
lo: key_lo,
|
||||
hi: key_lo + key.len(),
|
||||
desc: format!("redefinition of table `{}`", key),
|
||||
});
|
||||
return
|
||||
}
|
||||
|
||||
let (into, key) = match self.recurse(into, key.as_slice(), key_lo) {
|
||||
Some(pair) => pair,
|
||||
None => return,
|
||||
};
|
||||
let key = key.to_string();
|
||||
if !into.contains_key(&key) {
|
||||
into.insert(key.clone(), Table(HashMap::new()));
|
||||
}
|
||||
match into.find_mut(&key) {
|
||||
Some(&Table(ref mut table)) => {
|
||||
for (k, v) in value.move_iter() {
|
||||
if !table.insert(k.clone(), v) {
|
||||
self.errors.push(Error {
|
||||
lo: key_lo,
|
||||
hi: key_lo + key.len(),
|
||||
desc: format!("duplicate key `{}` in table", k),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(_) => {
|
||||
self.errors.push(Error {
|
||||
lo: key_lo,
|
||||
hi: key_lo + key.len(),
|
||||
desc: format!("duplicate key `{}` in table", key),
|
||||
});
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_array(&mut self, into: &mut Table, key: String, value: Value,
|
||||
key_lo: uint) {
|
||||
let (into, key) = match self.recurse(into, key.as_slice(), key_lo) {
|
||||
Some(pair) => pair,
|
||||
None => return,
|
||||
};
|
||||
let key = key.to_string();
|
||||
if !into.contains_key(&key) {
|
||||
into.insert(key.clone(), Array(Vec::new()));
|
||||
}
|
||||
match *into.get_mut(&key) {
|
||||
Array(ref mut vec) => {
|
||||
match vec.as_slice().head() {
|
||||
Some(ref v) if !v.same_type(&value) => {
|
||||
self.errors.push(Error {
|
||||
lo: key_lo,
|
||||
hi: key_lo + key.len(),
|
||||
desc: format!("expected type `{}`, found type `{}`",
|
||||
v.type_str(), value.type_str()),
|
||||
})
|
||||
}
|
||||
Some(..) | None => {}
|
||||
}
|
||||
vec.push(value);
|
||||
}
|
||||
_ => {
|
||||
self.errors.push(Error {
|
||||
lo: key_lo,
|
||||
hi: key_lo + key.len(),
|
||||
desc: format!("key `{}` was previously not an array", key),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
src/test/README.md
Normal file
1
src/test/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
Tests are from https://github.com/BurntSushi/toml-test
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"ints-and-floats": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{
|
||||
"type": "integer",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"type": "float",
|
||||
"value": "1.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
76
src/test/invalid.rs
Normal file
76
src/test/invalid.rs
Normal file
|
@ -0,0 +1,76 @@
|
|||
use {Parser};
|
||||
|
||||
fn run(toml: &str) {
|
||||
let mut p = Parser::new(toml);
|
||||
let table = p.parse();
|
||||
assert!(p.errors.len() > 0);
|
||||
assert!(table.is_none());
|
||||
}
|
||||
|
||||
macro_rules! test( ($name:ident, $toml:expr) => (
|
||||
#[test]
|
||||
fn $name() { run($toml); }
|
||||
) )
|
||||
|
||||
test!(array_mixed_types_arrays_and_ints,
|
||||
include_str!("invalid/array-mixed-types-arrays-and-ints.toml"))
|
||||
test!(array_mixed_types_ints_and_floats,
|
||||
include_str!("invalid/array-mixed-types-ints-and-floats.toml"))
|
||||
test!(array_mixed_types_strings_and_ints,
|
||||
include_str!("invalid/array-mixed-types-strings-and-ints.toml"))
|
||||
test!(datetime_malformed_no_leads,
|
||||
include_str!("invalid/datetime-malformed-no-leads.toml"))
|
||||
test!(datetime_malformed_no_secs,
|
||||
include_str!("invalid/datetime-malformed-no-secs.toml"))
|
||||
test!(datetime_malformed_no_t,
|
||||
include_str!("invalid/datetime-malformed-no-t.toml"))
|
||||
test!(datetime_malformed_no_z,
|
||||
include_str!("invalid/datetime-malformed-no-z.toml"))
|
||||
test!(datetime_malformed_with_milli,
|
||||
include_str!("invalid/datetime-malformed-with-milli.toml"))
|
||||
test!(duplicate_keys,
|
||||
include_str!("invalid/duplicate-keys.toml"))
|
||||
test!(duplicate_key_table,
|
||||
include_str!("invalid/duplicate-key-table.toml"))
|
||||
test!(duplicate_tables,
|
||||
include_str!("invalid/duplicate-tables.toml"))
|
||||
test!(empty_implicit_table,
|
||||
include_str!("invalid/empty-implicit-table.toml"))
|
||||
test!(empty_table,
|
||||
include_str!("invalid/empty-table.toml"))
|
||||
test!(float_no_leading_zero,
|
||||
include_str!("invalid/float-no-leading-zero.toml"))
|
||||
test!(float_no_trailing_digits,
|
||||
include_str!("invalid/float-no-trailing-digits.toml"))
|
||||
test!(key_two_equals,
|
||||
include_str!("invalid/key-two-equals.toml"))
|
||||
test!(string_bad_byte_escape,
|
||||
include_str!("invalid/string-bad-byte-escape.toml"))
|
||||
test!(string_bad_escape,
|
||||
include_str!("invalid/string-bad-escape.toml"))
|
||||
test!(string_byte_escapes,
|
||||
include_str!("invalid/string-byte-escapes.toml"))
|
||||
test!(string_no_close,
|
||||
include_str!("invalid/string-no-close.toml"))
|
||||
test!(table_array_implicit,
|
||||
include_str!("invalid/table-array-implicit.toml"))
|
||||
test!(table_array_malformed_bracket,
|
||||
include_str!("invalid/table-array-malformed-bracket.toml"))
|
||||
test!(table_array_malformed_empty,
|
||||
include_str!("invalid/table-array-malformed-empty.toml"))
|
||||
test!(table_nested_brackets_close,
|
||||
include_str!("invalid/table-nested-brackets-close.toml"))
|
||||
test!(table_nested_brackets_open,
|
||||
include_str!("invalid/table-nested-brackets-open.toml"))
|
||||
test!(text_after_array_entries,
|
||||
include_str!("invalid/text-after-array-entries.toml"))
|
||||
test!(text_after_integer,
|
||||
include_str!("invalid/text-after-integer.toml"))
|
||||
test!(text_after_string,
|
||||
include_str!("invalid/text-after-string.toml"))
|
||||
test!(text_after_table,
|
||||
include_str!("invalid/text-after-table.toml"))
|
||||
test!(text_before_array_separator,
|
||||
include_str!("invalid/text-before-array-separator.toml"))
|
||||
test!(text_in_array,
|
||||
include_str!("invalid/text-in-array.toml"))
|
1
src/test/invalid/array-mixed-types-arrays-and-ints.toml
Normal file
1
src/test/invalid/array-mixed-types-arrays-and-ints.toml
Normal file
|
@ -0,0 +1 @@
|
|||
arrays-and-ints = [1, ["Arrays are not integers."]]
|
1
src/test/invalid/array-mixed-types-ints-and-floats.toml
Normal file
1
src/test/invalid/array-mixed-types-ints-and-floats.toml
Normal file
|
@ -0,0 +1 @@
|
|||
ints-and-floats = [1, 1.0]
|
1
src/test/invalid/array-mixed-types-strings-and-ints.toml
Normal file
1
src/test/invalid/array-mixed-types-strings-and-ints.toml
Normal file
|
@ -0,0 +1 @@
|
|||
strings-and-ints = ["hi", 42]
|
1
src/test/invalid/datetime-malformed-no-leads.toml
Normal file
1
src/test/invalid/datetime-malformed-no-leads.toml
Normal file
|
@ -0,0 +1 @@
|
|||
no-leads = 1987-7-05T17:45:00Z
|
1
src/test/invalid/datetime-malformed-no-secs.toml
Normal file
1
src/test/invalid/datetime-malformed-no-secs.toml
Normal file
|
@ -0,0 +1 @@
|
|||
no-secs = 1987-07-05T17:45Z
|
1
src/test/invalid/datetime-malformed-no-t.toml
Normal file
1
src/test/invalid/datetime-malformed-no-t.toml
Normal file
|
@ -0,0 +1 @@
|
|||
no-t = 1987-07-0517:45:00Z
|
1
src/test/invalid/datetime-malformed-no-z.toml
Normal file
1
src/test/invalid/datetime-malformed-no-z.toml
Normal file
|
@ -0,0 +1 @@
|
|||
no-z = 1987-07-05T17:45:00
|
1
src/test/invalid/datetime-malformed-with-milli.toml
Normal file
1
src/test/invalid/datetime-malformed-with-milli.toml
Normal file
|
@ -0,0 +1 @@
|
|||
with-milli = 1987-07-5T17:45:00.12Z
|
5
src/test/invalid/duplicate-key-table.toml
Normal file
5
src/test/invalid/duplicate-key-table.toml
Normal file
|
@ -0,0 +1,5 @@
|
|||
[fruit]
|
||||
type = "apple"
|
||||
|
||||
[fruit.type]
|
||||
apple = "yes"
|
2
src/test/invalid/duplicate-keys.toml
Normal file
2
src/test/invalid/duplicate-keys.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
dupe = false
|
||||
dupe = true
|
2
src/test/invalid/duplicate-tables.toml
Normal file
2
src/test/invalid/duplicate-tables.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[a]
|
||||
[a]
|
1
src/test/invalid/empty-implicit-table.toml
Normal file
1
src/test/invalid/empty-implicit-table.toml
Normal file
|
@ -0,0 +1 @@
|
|||
[naughty..naughty]
|
1
src/test/invalid/empty-table.toml
Normal file
1
src/test/invalid/empty-table.toml
Normal file
|
@ -0,0 +1 @@
|
|||
[]
|
2
src/test/invalid/float-no-leading-zero.toml
Normal file
2
src/test/invalid/float-no-leading-zero.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
answer = .12345
|
||||
neganswer = -.12345
|
2
src/test/invalid/float-no-trailing-digits.toml
Normal file
2
src/test/invalid/float-no-trailing-digits.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
answer = 1.
|
||||
neganswer = -1.
|
1
src/test/invalid/key-two-equals.toml
Normal file
1
src/test/invalid/key-two-equals.toml
Normal file
|
@ -0,0 +1 @@
|
|||
key= = 1
|
1
src/test/invalid/string-bad-byte-escape.toml
Normal file
1
src/test/invalid/string-bad-byte-escape.toml
Normal file
|
@ -0,0 +1 @@
|
|||
naughty = "\xAg"
|
1
src/test/invalid/string-bad-escape.toml
Normal file
1
src/test/invalid/string-bad-escape.toml
Normal file
|
@ -0,0 +1 @@
|
|||
invalid-escape = "This string has a bad \a escape character."
|
1
src/test/invalid/string-byte-escapes.toml
Normal file
1
src/test/invalid/string-byte-escapes.toml
Normal file
|
@ -0,0 +1 @@
|
|||
answer = "\x33"
|
1
src/test/invalid/string-no-close.toml
Normal file
1
src/test/invalid/string-no-close.toml
Normal file
|
@ -0,0 +1 @@
|
|||
no-ending-quote = "One time, at band camp
|
14
src/test/invalid/table-array-implicit.toml
Normal file
14
src/test/invalid/table-array-implicit.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
# This test is a bit tricky. It should fail because the first use of
|
||||
# `[[albums.songs]]` without first declaring `albums` implies that `albums`
|
||||
# must be a table. The alternative would be quite weird. Namely, it wouldn't
|
||||
# comply with the TOML spec: "Each double-bracketed sub-table will belong to
|
||||
# the most *recently* defined table element *above* it."
|
||||
#
|
||||
# This is in contrast to the *valid* test, table-array-implicit where
|
||||
# `[[albums.songs]]` works by itself, so long as `[[albums]]` isn't declared
|
||||
# later. (Although, `[albums]` could be.)
|
||||
[[albums.songs]]
|
||||
name = "Glory Days"
|
||||
|
||||
[[albums]]
|
||||
name = "Born in the USA"
|
2
src/test/invalid/table-array-malformed-bracket.toml
Normal file
2
src/test/invalid/table-array-malformed-bracket.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[[albums]
|
||||
name = "Born to Run"
|
2
src/test/invalid/table-array-malformed-empty.toml
Normal file
2
src/test/invalid/table-array-malformed-empty.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[[]]
|
||||
name = "Born to Run"
|
2
src/test/invalid/table-nested-brackets-close.toml
Normal file
2
src/test/invalid/table-nested-brackets-close.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[a]b]
|
||||
zyx = 42
|
2
src/test/invalid/table-nested-brackets-open.toml
Normal file
2
src/test/invalid/table-nested-brackets-open.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[a[b]
|
||||
zyx = 42
|
4
src/test/invalid/text-after-array-entries.toml
Normal file
4
src/test/invalid/text-after-array-entries.toml
Normal file
|
@ -0,0 +1,4 @@
|
|||
array = [
|
||||
"Is there life after an array separator?", No
|
||||
"Entry"
|
||||
]
|
1
src/test/invalid/text-after-integer.toml
Normal file
1
src/test/invalid/text-after-integer.toml
Normal file
|
@ -0,0 +1 @@
|
|||
answer = 42 the ultimate answer?
|
1
src/test/invalid/text-after-string.toml
Normal file
1
src/test/invalid/text-after-string.toml
Normal file
|
@ -0,0 +1 @@
|
|||
string = "Is there life after strings?" No.
|
1
src/test/invalid/text-after-table.toml
Normal file
1
src/test/invalid/text-after-table.toml
Normal file
|
@ -0,0 +1 @@
|
|||
[error] this shouldn't be here
|
4
src/test/invalid/text-before-array-separator.toml
Normal file
4
src/test/invalid/text-before-array-separator.toml
Normal file
|
@ -0,0 +1,4 @@
|
|||
array = [
|
||||
"Is there life before an array separator?" No,
|
||||
"Entry"
|
||||
]
|
5
src/test/invalid/text-in-array.toml
Normal file
5
src/test/invalid/text-in-array.toml
Normal file
|
@ -0,0 +1,5 @@
|
|||
array = [
|
||||
"Entry 1",
|
||||
I don't belong,
|
||||
"Entry 2",
|
||||
]
|
2
src/test/mod.rs
Normal file
2
src/test/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
mod valid;
|
||||
mod invalid;
|
163
src/test/valid.rs
Normal file
163
src/test/valid.rs
Normal file
|
@ -0,0 +1,163 @@
|
|||
extern crate serialize;
|
||||
|
||||
use std::num::strconv;
|
||||
use std::collections::TreeMap;
|
||||
use self::serialize::json;
|
||||
|
||||
use {Parser, Value, Table, String, Integer, Float, Boolean, Datetime, Array};
|
||||
|
||||
fn to_json(toml: Value) -> json::Json {
|
||||
fn doit(s: &str, json: json::Json) -> json::Json {
|
||||
let mut map = box TreeMap::new();
|
||||
map.insert("type".to_string(), json::String(s.to_string()));
|
||||
map.insert("value".to_string(), json);
|
||||
json::Object(map)
|
||||
}
|
||||
match toml {
|
||||
String(s) => doit("string", json::String(s)),
|
||||
Integer(i) => doit("integer", json::String(i.to_str())),
|
||||
Float(f) => doit("float", json::String({
|
||||
let (bytes, _) =
|
||||
strconv::float_to_str_bytes_common(f, 10, true,
|
||||
strconv::SignNeg,
|
||||
strconv::DigMax(15),
|
||||
strconv::ExpNone,
|
||||
false);
|
||||
let s = String::from_utf8(bytes).unwrap();
|
||||
if s.as_slice().contains(".") {s} else {format!("{}.0", s)}
|
||||
})),
|
||||
Boolean(b) => doit("bool", json::String(b.to_str())),
|
||||
Datetime(s) => doit("datetime", json::String(s)),
|
||||
Array(arr) => {
|
||||
let is_table = match arr.as_slice().head() {
|
||||
Some(&Table(..)) => true,
|
||||
_ => false,
|
||||
};
|
||||
let json = json::List(arr.move_iter().map(to_json).collect());
|
||||
if is_table {json} else {doit("array", json)}
|
||||
}
|
||||
Table(table) => json::Object(box table.move_iter().map(|(k, v)| {
|
||||
(k, to_json(v))
|
||||
}).collect()),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(toml: &str, json: &str) {
|
||||
let mut p = Parser::new(toml);
|
||||
let table = p.parse();
|
||||
assert!(p.errors.len() == 0, "had_errors: {}",
|
||||
p.errors.iter().map(|e| {
|
||||
(e.desc.clone(), toml.slice(e.lo - 5, e.hi + 5))
|
||||
}).collect::<Vec<(String, &str)>>());
|
||||
assert!(table.is_some());
|
||||
let table = table.unwrap();
|
||||
|
||||
let json = json::from_str(json).unwrap();
|
||||
let toml_json = to_json(Table(table));
|
||||
assert!(json == toml_json,
|
||||
"expected\n{}\ngot\n{}\n",
|
||||
json.to_pretty_str(),
|
||||
toml_json.to_pretty_str());
|
||||
}
|
||||
|
||||
macro_rules! test( ($name:ident, $toml:expr, $json:expr) => (
|
||||
#[test]
|
||||
fn $name() { run($toml, $json); }
|
||||
) )
|
||||
|
||||
test!(array_empty,
|
||||
include_str!("valid/array-empty.toml"),
|
||||
include_str!("valid/array-empty.json"))
|
||||
test!(array_nospaces,
|
||||
include_str!("valid/array-nospaces.toml"),
|
||||
include_str!("valid/array-nospaces.json"))
|
||||
test!(arrays_hetergeneous,
|
||||
include_str!("valid/arrays-hetergeneous.toml"),
|
||||
include_str!("valid/arrays-hetergeneous.json"))
|
||||
test!(arrays,
|
||||
include_str!("valid/arrays.toml"),
|
||||
include_str!("valid/arrays.json"))
|
||||
test!(arrays_nested,
|
||||
include_str!("valid/arrays-nested.toml"),
|
||||
include_str!("valid/arrays-nested.json"))
|
||||
test!(empty,
|
||||
include_str!("valid/empty.toml"),
|
||||
include_str!("valid/empty.json"))
|
||||
test!(bool,
|
||||
include_str!("valid/bool.toml"),
|
||||
include_str!("valid/bool.json"))
|
||||
test!(datetime,
|
||||
include_str!("valid/datetime.toml"),
|
||||
include_str!("valid/datetime.json"))
|
||||
test!(example,
|
||||
include_str!("valid/example.toml"),
|
||||
include_str!("valid/example.json"))
|
||||
test!(float,
|
||||
include_str!("valid/float.toml"),
|
||||
include_str!("valid/float.json"))
|
||||
test!(implicit_and_explicit_after,
|
||||
include_str!("valid/implicit-and-explicit-after.toml"),
|
||||
include_str!("valid/implicit-and-explicit-after.json"))
|
||||
test!(implicit_and_explicit_before,
|
||||
include_str!("valid/implicit-and-explicit-before.toml"),
|
||||
include_str!("valid/implicit-and-explicit-before.json"))
|
||||
test!(implicit_groups,
|
||||
include_str!("valid/implicit-groups.toml"),
|
||||
include_str!("valid/implicit-groups.json"))
|
||||
test!(integer,
|
||||
include_str!("valid/integer.toml"),
|
||||
include_str!("valid/integer.json"))
|
||||
test!(key_equals_nospace,
|
||||
include_str!("valid/key-equals-nospace.toml"),
|
||||
include_str!("valid/key-equals-nospace.json"))
|
||||
test!(key_special_chars,
|
||||
include_str!("valid/key-special-chars.toml"),
|
||||
include_str!("valid/key-special-chars.json"))
|
||||
test!(key_with_pound,
|
||||
include_str!("valid/key-with-pound.toml"),
|
||||
include_str!("valid/key-with-pound.json"))
|
||||
test!(long_float,
|
||||
include_str!("valid/long-float.toml"),
|
||||
include_str!("valid/long-float.json"))
|
||||
test!(long_integer,
|
||||
include_str!("valid/long-integer.toml"),
|
||||
include_str!("valid/long-integer.json"))
|
||||
test!(string_escapes,
|
||||
include_str!("valid/string-escapes.toml"),
|
||||
include_str!("valid/string-escapes.json"))
|
||||
test!(string_simple,
|
||||
include_str!("valid/string-simple.toml"),
|
||||
include_str!("valid/string-simple.json"))
|
||||
test!(string_with_pound,
|
||||
include_str!("valid/string-with-pound.toml"),
|
||||
include_str!("valid/string-with-pound.json"))
|
||||
test!(table_array_implicit,
|
||||
include_str!("valid/table-array-implicit.toml"),
|
||||
include_str!("valid/table-array-implicit.json"))
|
||||
test!(table_array_many,
|
||||
include_str!("valid/table-array-many.toml"),
|
||||
include_str!("valid/table-array-many.json"))
|
||||
test!(table_array_nest,
|
||||
include_str!("valid/table-array-nest.toml"),
|
||||
include_str!("valid/table-array-nest.json"))
|
||||
test!(table_array_one,
|
||||
include_str!("valid/table-array-one.toml"),
|
||||
include_str!("valid/table-array-one.json"))
|
||||
test!(table_empty,
|
||||
include_str!("valid/table-empty.toml"),
|
||||
include_str!("valid/table-empty.json"))
|
||||
test!(table_sub_empty,
|
||||
include_str!("valid/table-sub-empty.toml"),
|
||||
include_str!("valid/table-sub-empty.json"))
|
||||
test!(table_whitespace,
|
||||
include_str!("valid/table-whitespace.toml"),
|
||||
include_str!("valid/table-whitespace.json"))
|
||||
test!(table_with_pound,
|
||||
include_str!("valid/table-with-pound.toml"),
|
||||
include_str!("valid/table-with-pound.json"))
|
||||
test!(unicode_escape,
|
||||
include_str!("valid/unicode-escape.toml"),
|
||||
include_str!("valid/unicode-escape.json"))
|
||||
test!(unicode_literal,
|
||||
include_str!("valid/unicode-literal.toml"),
|
||||
include_str!("valid/unicode-literal.json"))
|
11
src/test/valid/array-empty.json
Normal file
11
src/test/valid/array-empty.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"thevoid": { "type": "array", "value": [
|
||||
{"type": "array", "value": [
|
||||
{"type": "array", "value": [
|
||||
{"type": "array", "value": [
|
||||
{"type": "array", "value": []}
|
||||
]}
|
||||
]}
|
||||
]}
|
||||
]}
|
||||
}
|
1
src/test/valid/array-empty.toml
Normal file
1
src/test/valid/array-empty.toml
Normal file
|
@ -0,0 +1 @@
|
|||
thevoid = [[[[[]]]]]
|
10
src/test/valid/array-nospaces.json
Normal file
10
src/test/valid/array-nospaces.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"ints": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "integer", "value": "1"},
|
||||
{"type": "integer", "value": "2"},
|
||||
{"type": "integer", "value": "3"}
|
||||
]
|
||||
}
|
||||
}
|
1
src/test/valid/array-nospaces.toml
Normal file
1
src/test/valid/array-nospaces.toml
Normal file
|
@ -0,0 +1 @@
|
|||
ints = [1,2,3]
|
19
src/test/valid/arrays-hetergeneous.json
Normal file
19
src/test/valid/arrays-hetergeneous.json
Normal file
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"mixed": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "array", "value": [
|
||||
{"type": "integer", "value": "1"},
|
||||
{"type": "integer", "value": "2"}
|
||||
]},
|
||||
{"type": "array", "value": [
|
||||
{"type": "string", "value": "a"},
|
||||
{"type": "string", "value": "b"}
|
||||
]},
|
||||
{"type": "array", "value": [
|
||||
{"type": "float", "value": "1.0"},
|
||||
{"type": "float", "value": "2.0"}
|
||||
]}
|
||||
]
|
||||
}
|
||||
}
|
1
src/test/valid/arrays-hetergeneous.toml
Normal file
1
src/test/valid/arrays-hetergeneous.toml
Normal file
|
@ -0,0 +1 @@
|
|||
mixed = [[1, 2], ["a", "b"], [1.0, 2.0]]
|
13
src/test/valid/arrays-nested.json
Normal file
13
src/test/valid/arrays-nested.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"nest": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "array", "value": [
|
||||
{"type": "string", "value": "a"}
|
||||
]},
|
||||
{"type": "array", "value": [
|
||||
{"type": "string", "value": "b"}
|
||||
]}
|
||||
]
|
||||
}
|
||||
}
|
1
src/test/valid/arrays-nested.toml
Normal file
1
src/test/valid/arrays-nested.toml
Normal file
|
@ -0,0 +1 @@
|
|||
nest = [["a"], ["b"]]
|
34
src/test/valid/arrays.json
Normal file
34
src/test/valid/arrays.json
Normal file
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"ints": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "integer", "value": "1"},
|
||||
{"type": "integer", "value": "2"},
|
||||
{"type": "integer", "value": "3"}
|
||||
]
|
||||
},
|
||||
"floats": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "float", "value": "1.0"},
|
||||
{"type": "float", "value": "2.0"},
|
||||
{"type": "float", "value": "3.0"}
|
||||
]
|
||||
},
|
||||
"strings": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "string", "value": "a"},
|
||||
{"type": "string", "value": "b"},
|
||||
{"type": "string", "value": "c"}
|
||||
]
|
||||
},
|
||||
"dates": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "datetime", "value": "1987-07-05T17:45:00Z"},
|
||||
{"type": "datetime", "value": "1979-05-27T07:32:00Z"},
|
||||
{"type": "datetime", "value": "2006-06-01T11:00:00Z"}
|
||||
]
|
||||
}
|
||||
}
|
9
src/test/valid/arrays.toml
Normal file
9
src/test/valid/arrays.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
ints = [1, 2, 3]
|
||||
floats = [1.0, 2.0, 3.0]
|
||||
strings = ["a", "b", "c"]
|
||||
dates = [
|
||||
1987-07-05T17:45:00Z,
|
||||
1979-05-27T07:32:00Z,
|
||||
2006-06-01T11:00:00Z,
|
||||
]
|
||||
|
4
src/test/valid/bool.json
Normal file
4
src/test/valid/bool.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"f": {"type": "bool", "value": "false"},
|
||||
"t": {"type": "bool", "value": "true"}
|
||||
}
|
2
src/test/valid/bool.toml
Normal file
2
src/test/valid/bool.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
t = true
|
||||
f = false
|
12
src/test/valid/comments-everywhere.json
Normal file
12
src/test/valid/comments-everywhere.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"group": {
|
||||
"answer": {"type": "integer", "value": "42"},
|
||||
"more": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "integer", "value": "42"},
|
||||
{"type": "integer", "value": "42"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
24
src/test/valid/comments-everywhere.toml
Normal file
24
src/test/valid/comments-everywhere.toml
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Top comment.
|
||||
# Top comment.
|
||||
# Top comment.
|
||||
|
||||
# [no-extraneous-groups-please]
|
||||
|
||||
[group] # Comment
|
||||
answer = 42 # Comment
|
||||
# no-extraneous-keys-please = 999
|
||||
# Inbetween comment.
|
||||
more = [ # Comment
|
||||
# What about multiple # comments?
|
||||
# Can you handle it?
|
||||
#
|
||||
# Evil.
|
||||
# Evil.
|
||||
42, 42, # Comments within arrays are fun.
|
||||
# What about multiple # comments?
|
||||
# Can you handle it?
|
||||
#
|
||||
# Evil.
|
||||
# Evil.
|
||||
# ] Did I fool you?
|
||||
] # Hopefully not.
|
3
src/test/valid/datetime.json
Normal file
3
src/test/valid/datetime.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"bestdayever": {"type": "datetime", "value": "1987-07-05T17:45:00Z"}
|
||||
}
|
1
src/test/valid/datetime.toml
Normal file
1
src/test/valid/datetime.toml
Normal file
|
@ -0,0 +1 @@
|
|||
bestdayever = 1987-07-05T17:45:00Z
|
1
src/test/valid/empty.json
Normal file
1
src/test/valid/empty.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
0
src/test/valid/empty.toml
Normal file
0
src/test/valid/empty.toml
Normal file
14
src/test/valid/example.json
Normal file
14
src/test/valid/example.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"best-day-ever": {"type": "datetime", "value": "1987-07-05T17:45:00Z"},
|
||||
"numtheory": {
|
||||
"boring": {"type": "bool", "value": "false"},
|
||||
"perfection": {
|
||||
"type": "array",
|
||||
"value": [
|
||||
{"type": "integer", "value": "6"},
|
||||
{"type": "integer", "value": "28"},
|
||||
{"type": "integer", "value": "496"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
5
src/test/valid/example.toml
Normal file
5
src/test/valid/example.toml
Normal file
|
@ -0,0 +1,5 @@
|
|||
best-day-ever = 1987-07-05T17:45:00Z
|
||||
|
||||
[numtheory]
|
||||
boring = false
|
||||
perfection = [6, 28, 496]
|
4
src/test/valid/float.json
Normal file
4
src/test/valid/float.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"pi": {"type": "float", "value": "3.14"},
|
||||
"negpi": {"type": "float", "value": "-3.14"}
|
||||
}
|
2
src/test/valid/float.toml
Normal file
2
src/test/valid/float.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
pi = 3.14
|
||||
negpi = -3.14
|
10
src/test/valid/implicit-and-explicit-after.json
Normal file
10
src/test/valid/implicit-and-explicit-after.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"a": {
|
||||
"better": {"type": "integer", "value": "43"},
|
||||
"b": {
|
||||
"c": {
|
||||
"answer": {"type": "integer", "value": "42"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
src/test/valid/implicit-and-explicit-after.toml
Normal file
5
src/test/valid/implicit-and-explicit-after.toml
Normal file
|
@ -0,0 +1,5 @@
|
|||
[a.b.c]
|
||||
answer = 42
|
||||
|
||||
[a]
|
||||
better = 43
|
10
src/test/valid/implicit-and-explicit-before.json
Normal file
10
src/test/valid/implicit-and-explicit-before.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"a": {
|
||||
"better": {"type": "integer", "value": "43"},
|
||||
"b": {
|
||||
"c": {
|
||||
"answer": {"type": "integer", "value": "42"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
src/test/valid/implicit-and-explicit-before.toml
Normal file
5
src/test/valid/implicit-and-explicit-before.toml
Normal file
|
@ -0,0 +1,5 @@
|
|||
[a]
|
||||
better = 43
|
||||
|
||||
[a.b.c]
|
||||
answer = 42
|
9
src/test/valid/implicit-groups.json
Normal file
9
src/test/valid/implicit-groups.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"a": {
|
||||
"b": {
|
||||
"c": {
|
||||
"answer": {"type": "integer", "value": "42"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
2
src/test/valid/implicit-groups.toml
Normal file
2
src/test/valid/implicit-groups.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[a.b.c]
|
||||
answer = 42
|
4
src/test/valid/integer.json
Normal file
4
src/test/valid/integer.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"answer": {"type": "integer", "value": "42"},
|
||||
"neganswer": {"type": "integer", "value": "-42"}
|
||||
}
|
2
src/test/valid/integer.toml
Normal file
2
src/test/valid/integer.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
answer = 42
|
||||
neganswer = -42
|
3
src/test/valid/key-equals-nospace.json
Normal file
3
src/test/valid/key-equals-nospace.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"answer": {"type": "integer", "value": "42"}
|
||||
}
|
1
src/test/valid/key-equals-nospace.toml
Normal file
1
src/test/valid/key-equals-nospace.toml
Normal file
|
@ -0,0 +1 @@
|
|||
answer=42
|
5
src/test/valid/key-special-chars.json
Normal file
5
src/test/valid/key-special-chars.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"~!@#$^&*()_+-`1234567890[]\\|/?><.,;:'": {
|
||||
"type": "integer", "value": "1"
|
||||
}
|
||||
}
|
1
src/test/valid/key-special-chars.toml
Normal file
1
src/test/valid/key-special-chars.toml
Normal file
|
@ -0,0 +1 @@
|
|||
~!@#$^&*()_+-`1234567890[]\|/?><.,;:' = 1
|
3
src/test/valid/key-with-pound.json
Normal file
3
src/test/valid/key-with-pound.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"key#name": {"type": "integer", "value": "5"}
|
||||
}
|
1
src/test/valid/key-with-pound.toml
Normal file
1
src/test/valid/key-with-pound.toml
Normal file
|
@ -0,0 +1 @@
|
|||
key#name = 5
|
4
src/test/valid/long-float.json
Normal file
4
src/test/valid/long-float.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"longpi": {"type": "float", "value": "3.141592653589793"},
|
||||
"neglongpi": {"type": "float", "value": "-3.141592653589793"}
|
||||
}
|
2
src/test/valid/long-float.toml
Normal file
2
src/test/valid/long-float.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
longpi = 3.141592653589793
|
||||
neglongpi = -3.141592653589793
|
4
src/test/valid/long-integer.json
Normal file
4
src/test/valid/long-integer.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"answer": {"type": "integer", "value": "9223372036854775807"},
|
||||
"neganswer": {"type": "integer", "value": "-9223372036854775808"}
|
||||
}
|
2
src/test/valid/long-integer.toml
Normal file
2
src/test/valid/long-integer.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
answer = 9223372036854775807
|
||||
neganswer = -9223372036854775808
|
34
src/test/valid/string-escapes.json
Normal file
34
src/test/valid/string-escapes.json
Normal file
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"backspace": {
|
||||
"type": "string",
|
||||
"value": "This string has a \u0008 backspace character."
|
||||
},
|
||||
"tab": {
|
||||
"type": "string",
|
||||
"value": "This string has a \u0009 tab character."
|
||||
},
|
||||
"newline": {
|
||||
"type": "string",
|
||||
"value": "This string has a \u000A new line character."
|
||||
},
|
||||
"formfeed": {
|
||||
"type": "string",
|
||||
"value": "This string has a \u000C form feed character."
|
||||
},
|
||||
"carriage": {
|
||||
"type": "string",
|
||||
"value": "This string has a \u000D carriage return character."
|
||||
},
|
||||
"quote": {
|
||||
"type": "string",
|
||||
"value": "This string has a \u0022 quote character."
|
||||
},
|
||||
"slash": {
|
||||
"type": "string",
|
||||
"value": "This string has a \u002F slash character."
|
||||
},
|
||||
"backslash": {
|
||||
"type": "string",
|
||||
"value": "This string has a \u005C backslash character."
|
||||
}
|
||||
}
|
8
src/test/valid/string-escapes.toml
Normal file
8
src/test/valid/string-escapes.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
backspace = "This string has a \b backspace character."
|
||||
tab = "This string has a \t tab character."
|
||||
newline = "This string has a \n new line character."
|
||||
formfeed = "This string has a \f form feed character."
|
||||
carriage = "This string has a \r carriage return character."
|
||||
quote = "This string has a \" quote character."
|
||||
slash = "This string has a \/ slash character."
|
||||
backslash = "This string has a \\ backslash character."
|
6
src/test/valid/string-simple.json
Normal file
6
src/test/valid/string-simple.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"answer": {
|
||||
"type": "string",
|
||||
"value": "You are not drinking enough whisky."
|
||||
}
|
||||
}
|
1
src/test/valid/string-simple.toml
Normal file
1
src/test/valid/string-simple.toml
Normal file
|
@ -0,0 +1 @@
|
|||
answer = "You are not drinking enough whisky."
|
7
src/test/valid/string-with-pound.json
Normal file
7
src/test/valid/string-with-pound.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"pound": {"type": "string", "value": "We see no # comments here."},
|
||||
"poundcomment": {
|
||||
"type": "string",
|
||||
"value": "But there are # some comments here."
|
||||
}
|
||||
}
|
2
src/test/valid/string-with-pound.toml
Normal file
2
src/test/valid/string-with-pound.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
pound = "We see no # comments here."
|
||||
poundcomment = "But there are # some comments here." # Did I # mess you up?
|
7
src/test/valid/table-array-implicit.json
Normal file
7
src/test/valid/table-array-implicit.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"albums": {
|
||||
"songs": [
|
||||
{"name": {"type": "string", "value": "Glory Days"}}
|
||||
]
|
||||
}
|
||||
}
|
2
src/test/valid/table-array-implicit.toml
Normal file
2
src/test/valid/table-array-implicit.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[[albums.songs]]
|
||||
name = "Glory Days"
|
16
src/test/valid/table-array-many.json
Normal file
16
src/test/valid/table-array-many.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"people": [
|
||||
{
|
||||
"first_name": {"type": "string", "value": "Bruce"},
|
||||
"last_name": {"type": "string", "value": "Springsteen"}
|
||||
},
|
||||
{
|
||||
"first_name": {"type": "string", "value": "Eric"},
|
||||
"last_name": {"type": "string", "value": "Clapton"}
|
||||
},
|
||||
{
|
||||
"first_name": {"type": "string", "value": "Bob"},
|
||||
"last_name": {"type": "string", "value": "Seger"}
|
||||
}
|
||||
]
|
||||
}
|
11
src/test/valid/table-array-many.toml
Normal file
11
src/test/valid/table-array-many.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[[people]]
|
||||
first_name = "Bruce"
|
||||
last_name = "Springsteen"
|
||||
|
||||
[[people]]
|
||||
first_name = "Eric"
|
||||
last_name = "Clapton"
|
||||
|
||||
[[people]]
|
||||
first_name = "Bob"
|
||||
last_name = "Seger"
|
18
src/test/valid/table-array-nest.json
Normal file
18
src/test/valid/table-array-nest.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"albums": [
|
||||
{
|
||||
"name": {"type": "string", "value": "Born to Run"},
|
||||
"songs": [
|
||||
{"name": {"type": "string", "value": "Jungleland"}},
|
||||
{"name": {"type": "string", "value": "Meeting Across the River"}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": {"type": "string", "value": "Born in the USA"},
|
||||
"songs": [
|
||||
{"name": {"type": "string", "value": "Glory Days"}},
|
||||
{"name": {"type": "string", "value": "Dancing in the Dark"}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
17
src/test/valid/table-array-nest.toml
Normal file
17
src/test/valid/table-array-nest.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[[albums]]
|
||||
name = "Born to Run"
|
||||
|
||||
[[albums.songs]]
|
||||
name = "Jungleland"
|
||||
|
||||
[[albums.songs]]
|
||||
name = "Meeting Across the River"
|
||||
|
||||
[[albums]]
|
||||
name = "Born in the USA"
|
||||
|
||||
[[albums.songs]]
|
||||
name = "Glory Days"
|
||||
|
||||
[[albums.songs]]
|
||||
name = "Dancing in the Dark"
|
8
src/test/valid/table-array-one.json
Normal file
8
src/test/valid/table-array-one.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"people": [
|
||||
{
|
||||
"first_name": {"type": "string", "value": "Bruce"},
|
||||
"last_name": {"type": "string", "value": "Springsteen"}
|
||||
}
|
||||
]
|
||||
}
|
3
src/test/valid/table-array-one.toml
Normal file
3
src/test/valid/table-array-one.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
[[people]]
|
||||
first_name = "Bruce"
|
||||
last_name = "Springsteen"
|
3
src/test/valid/table-empty.json
Normal file
3
src/test/valid/table-empty.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"a": {}
|
||||
}
|
1
src/test/valid/table-empty.toml
Normal file
1
src/test/valid/table-empty.toml
Normal file
|
@ -0,0 +1 @@
|
|||
[a]
|
3
src/test/valid/table-sub-empty.json
Normal file
3
src/test/valid/table-sub-empty.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"a": { "b": {} }
|
||||
}
|
2
src/test/valid/table-sub-empty.toml
Normal file
2
src/test/valid/table-sub-empty.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[a]
|
||||
[a.b]
|
3
src/test/valid/table-whitespace.json
Normal file
3
src/test/valid/table-whitespace.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"valid key": {}
|
||||
}
|
1
src/test/valid/table-whitespace.toml
Normal file
1
src/test/valid/table-whitespace.toml
Normal file
|
@ -0,0 +1 @@
|
|||
[valid key]
|
5
src/test/valid/table-with-pound.json
Normal file
5
src/test/valid/table-with-pound.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"key#group": {
|
||||
"answer": {"type": "integer", "value": "42"}
|
||||
}
|
||||
}
|
2
src/test/valid/table-with-pound.toml
Normal file
2
src/test/valid/table-with-pound.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[key#group]
|
||||
answer = 42
|
3
src/test/valid/unicode-escape.json
Normal file
3
src/test/valid/unicode-escape.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"answer": {"type": "string", "value": "\u03B4"}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue