Fix parsing nested tables in arrays

Closes #36
This commit is contained in:
Alex Crichton 2014-12-11 22:29:27 -08:00
parent 25e10a8256
commit f9408377c9
2 changed files with 69 additions and 12 deletions

View file

@ -1,5 +1,5 @@
use std::char; use std::char;
use std::collections::{TreeMap, HashSet}; use std::collections::TreeMap;
use std::error::Error; use std::error::Error;
use std::num::FromStrRadix; use std::num::FromStrRadix;
use std::str; use std::str;
@ -14,7 +14,6 @@ use Value::{mod, Array, Table, Float, Integer, Boolean, Datetime};
pub struct Parser<'a> { pub struct Parser<'a> {
input: &'a str, input: &'a str,
cur: str::CharOffsets<'a>, cur: str::CharOffsets<'a>,
tables_defined: HashSet<String>,
/// A list of all errors which have occurred during parsing. /// A list of all errors which have occurred during parsing.
/// ///
@ -64,7 +63,6 @@ impl<'a> Parser<'a> {
input: s, input: s,
cur: s.char_indices(), cur: s.char_indices(),
errors: Vec::new(), errors: Vec::new(),
tables_defined: HashSet::new(),
} }
} }
@ -690,25 +688,26 @@ impl<'a> Parser<'a> {
fn insert_table(&mut self, into: &mut TomlTable, key: String, value: TomlTable, fn insert_table(&mut self, into: &mut TomlTable, key: String, value: TomlTable,
key_lo: uint) { key_lo: uint) {
if !self.tables_defined.insert(key.clone()) {
self.errors.push(ParserError {
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) { let (into, key) = match self.recurse(into, key.as_slice(), key_lo) {
Some(pair) => pair, Some(pair) => pair,
None => return, None => return,
}; };
let key = key.to_string(); let key = key.to_string();
let mut added = false;
if !into.contains_key(&key) { if !into.contains_key(&key) {
into.insert(key.clone(), Table(TreeMap::new())); into.insert(key.clone(), Table(TreeMap::new()));
added = true;
} }
match into.get_mut(&key) { match into.get_mut(&key) {
Some(&Table(ref mut table)) => { Some(&Table(ref mut table)) => {
let any_tables = table.values().any(|v| v.as_table().is_some());
if !any_tables && !added {
self.errors.push(ParserError {
lo: key_lo,
hi: key_lo + key.len(),
desc: format!("redefinition of table `{}`", key),
});
}
for (k, v) in value.into_iter() { for (k, v) in value.into_iter() {
if table.insert(k.clone(), v).is_some() { if table.insert(k.clone(), v).is_some() {
self.errors.push(ParserError { self.errors.push(ParserError {
@ -875,4 +874,61 @@ trimmed in raw strings.
All other whitespace\n \ All other whitespace\n \
is preserved.\n")); is preserved.\n"));
} }
#[test]
fn tables_in_arrays() {
let mut p = Parser::new(r#"
[[foo]]
#
[foo.bar]
#
[[foo]]
#
[foo.bar]
#...
"#);
let table = Table(p.parse().unwrap());
table.lookup("foo.0.bar").unwrap().as_table().unwrap();
table.lookup("foo.1.bar").unwrap().as_table().unwrap();
}
#[test]
fn fruit() {
let mut p = Parser::new(r#"
[[fruit]]
name = "apple"
[fruit.physical]
color = "red"
shape = "round"
[[fruit.variety]]
name = "red delicious"
[[fruit.variety]]
name = "granny smith"
[[fruit]]
name = "banana"
[[fruit.variety]]
name = "plantain"
"#);
let table = Table(p.parse().unwrap());
assert_eq!(table.lookup("fruit.0.name").and_then(|k| k.as_str()),
Some("apple"));
assert_eq!(table.lookup("fruit.0.physical.color").and_then(|k| k.as_str()),
Some("red"));
assert_eq!(table.lookup("fruit.0.physical.shape").and_then(|k| k.as_str()),
Some("round"));
assert_eq!(table.lookup("fruit.0.variety.0.name").and_then(|k| k.as_str()),
Some("red delicious"));
assert_eq!(table.lookup("fruit.0.variety.1.name").and_then(|k| k.as_str()),
Some("granny smith"));
assert_eq!(table.lookup("fruit.1.name").and_then(|k| k.as_str()),
Some("banana"));
assert_eq!(table.lookup("fruit.1.variety.0.name").and_then(|k| k.as_str()),
Some("plantain"));
}
} }

View file

@ -64,6 +64,7 @@ pub struct Decoder {
/// Enumeration of errors which can occur while encoding a rust value into a /// Enumeration of errors which can occur while encoding a rust value into a
/// TOML value. /// TOML value.
#[allow(missing_copy_implementations)]
pub enum Error { pub enum Error {
/// Indication that a key was needed when a value was emitted, but no key /// Indication that a key was needed when a value was emitted, but no key
/// was previously emitted. /// was previously emitted.